add engine and rpc client, archive old code
This commit is contained in:
BIN
_archive/talk/.DS_Store
vendored
Normal file
BIN
_archive/talk/.DS_Store
vendored
Normal file
Binary file not shown.
2146
_archive/talk/Cargo.lock
generated
Normal file
2146
_archive/talk/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
14
_archive/talk/Cargo.toml
Normal file
14
_archive/talk/Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "talk"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
actix-web = "4.4.0"
|
||||
actix-files = "0.6.2"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
rhai = "1.18.0"
|
||||
heromodels = { path = "../../db/heromodels" }
|
||||
rhai_wrapper = { path = "../rhai_wrapper" }
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
331
_archive/talk/src/main.rs
Normal file
331
_archive/talk/src/main.rs
Normal file
@@ -0,0 +1,331 @@
|
||||
use actix_files::Files;
|
||||
use actix_web::{web, App, HttpResponse, HttpServer, Responder};
|
||||
use heromodels::db::hero::OurDB;
|
||||
use heromodels::models::calendar::Calendar;
|
||||
use heromodels::models::governance::{Proposal, Ballot, VoteOption, ProposalStatus, VoteEventStatus};
|
||||
use rhai::Engine;
|
||||
use rhai_wrapper::wrap_vec_return;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use chrono::{Utc, Duration};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct ScriptRequest {
|
||||
script: String,
|
||||
model_type: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct ScriptResponse {
|
||||
output: String,
|
||||
success: bool,
|
||||
}
|
||||
|
||||
// Function to set up the calendar model in the Rhai engine
|
||||
fn setup_calendar_engine(engine: &mut Engine, db: Arc<OurDB>) {
|
||||
// Register the Calendar type with Rhai
|
||||
Calendar::register_rhai_bindings_for_calendar(engine, db.clone());
|
||||
|
||||
// Register a function to get the database instance
|
||||
engine.register_fn("get_db", move || db.clone());
|
||||
|
||||
// Register a calendar builder function
|
||||
engine.register_fn("calendar__builder", |id: i64| {
|
||||
Calendar::new(id as u32)
|
||||
});
|
||||
|
||||
// Register setter methods for Calendar properties
|
||||
engine.register_fn("set_description", |calendar: &mut Calendar, desc: String| {
|
||||
calendar.description = Some(desc);
|
||||
});
|
||||
|
||||
// Register getter methods for Calendar properties
|
||||
engine.register_fn("get_description", |calendar: Calendar| -> String {
|
||||
calendar.description.clone().unwrap_or_default()
|
||||
});
|
||||
|
||||
// Register getter for base_data.id
|
||||
engine.register_fn("get_id", |calendar: Calendar| -> i64 {
|
||||
calendar.base_data.id as i64
|
||||
});
|
||||
|
||||
// Register additional functions needed by the script
|
||||
engine.register_fn("set_calendar", |_db: Arc<OurDB>, _calendar: Calendar| {
|
||||
// In a real implementation, this would save the calendar to the database
|
||||
println!("Calendar saved: {}", _calendar.name);
|
||||
});
|
||||
|
||||
engine.register_fn("get_calendar_by_id", |_db: Arc<OurDB>, id: i64| -> Calendar {
|
||||
// In a real implementation, this would retrieve the calendar from the database
|
||||
Calendar::new(id as u32)
|
||||
});
|
||||
|
||||
// Register a function to check if a calendar exists
|
||||
engine.register_fn("calendar_exists", |_db: Arc<OurDB>, id: i64| -> bool {
|
||||
// In a real implementation, this would check if the calendar exists in the database
|
||||
id == 1 || id == 2
|
||||
});
|
||||
|
||||
// Define the function separately to use with the wrap_vec_return macro
|
||||
fn get_all_calendars(_db: Arc<OurDB>) -> Vec<Calendar> {
|
||||
// In a real implementation, this would retrieve all calendars from the database
|
||||
vec![Calendar::new(1), Calendar::new(2)]
|
||||
}
|
||||
|
||||
// Register the function with the wrap_vec_return macro
|
||||
engine.register_fn("get_all_calendars", wrap_vec_return!(get_all_calendars, Arc<OurDB> => Calendar));
|
||||
|
||||
engine.register_fn("delete_calendar_by_id", |_db: Arc<OurDB>, _id: i64| {
|
||||
// In a real implementation, this would delete the calendar from the database
|
||||
println!("Calendar deleted with ID: {}", _id);
|
||||
});
|
||||
}
|
||||
|
||||
// Function to set up the governance model in the Rhai engine
|
||||
fn setup_governance_engine(engine: &mut Engine, db: Arc<OurDB>) {
|
||||
// Register the Proposal type with Rhai
|
||||
Proposal::register_rhai_bindings_for_proposal(engine, db.clone());
|
||||
|
||||
// Register the Ballot type with Rhai
|
||||
Ballot::register_rhai_bindings_for_ballot(engine, db.clone());
|
||||
|
||||
// Register a function to get the database instance
|
||||
engine.register_fn("get_db", move || db.clone());
|
||||
|
||||
// Register builder functions for Proposal and related types
|
||||
engine.register_fn("create_proposal", |id: i64, creator_id: String, title: String, description: String| {
|
||||
let start_date = Utc::now();
|
||||
let end_date = start_date + Duration::days(14);
|
||||
Proposal::new(id as u32, creator_id, title, description, start_date, end_date)
|
||||
});
|
||||
|
||||
engine.register_fn("create_vote_option", |id: i64, text: String| {
|
||||
VoteOption::new(id as u8, text)
|
||||
});
|
||||
|
||||
engine.register_fn("create_ballot", |id: i64, user_id: i64, vote_option_id: i64, shares_count: i64| {
|
||||
Ballot::new(id as u32, user_id as u32, vote_option_id as u8, shares_count)
|
||||
});
|
||||
|
||||
// Register getter and setter methods for Proposal properties
|
||||
engine.register_fn("get_title", |proposal: Proposal| -> String {
|
||||
proposal.title.clone()
|
||||
});
|
||||
|
||||
engine.register_fn("get_description", |proposal: Proposal| -> String {
|
||||
proposal.description.clone()
|
||||
});
|
||||
|
||||
engine.register_fn("get_creator_id", |proposal: Proposal| -> String {
|
||||
proposal.creator_id.clone()
|
||||
});
|
||||
|
||||
engine.register_fn("get_id", |proposal: Proposal| -> i64 {
|
||||
proposal.base_data.id as i64
|
||||
});
|
||||
|
||||
engine.register_fn("get_status", |proposal: Proposal| -> String {
|
||||
format!("{:?}", proposal.status)
|
||||
});
|
||||
|
||||
engine.register_fn("get_vote_status", |proposal: Proposal| -> String {
|
||||
format!("{:?}", proposal.vote_status)
|
||||
});
|
||||
|
||||
// Register methods for proposal operations
|
||||
engine.register_fn("add_option_to_proposal", |proposal: Proposal, option_id: i64, option_text: String| -> Proposal {
|
||||
proposal.add_option(option_id as u8, option_text)
|
||||
});
|
||||
|
||||
engine.register_fn("cast_vote_on_proposal", |proposal: Proposal, ballot_id: i64, user_id: i64, option_id: i64, shares: i64| -> Proposal {
|
||||
proposal.cast_vote(ballot_id as u32, user_id as u32, option_id as u8, shares)
|
||||
});
|
||||
|
||||
engine.register_fn("change_proposal_status", |proposal: Proposal, status_str: String| -> Proposal {
|
||||
let new_status = match status_str.as_str() {
|
||||
"Draft" => ProposalStatus::Draft,
|
||||
"Active" => ProposalStatus::Active,
|
||||
"Approved" => ProposalStatus::Approved,
|
||||
"Rejected" => ProposalStatus::Rejected,
|
||||
"Cancelled" => ProposalStatus::Cancelled,
|
||||
_ => ProposalStatus::Draft,
|
||||
};
|
||||
proposal.change_proposal_status(new_status)
|
||||
});
|
||||
|
||||
engine.register_fn("change_vote_event_status", |proposal: Proposal, status_str: String| -> Proposal {
|
||||
let new_status = match status_str.as_str() {
|
||||
"Open" => VoteEventStatus::Open,
|
||||
"Closed" => VoteEventStatus::Closed,
|
||||
"Cancelled" => VoteEventStatus::Cancelled,
|
||||
_ => VoteEventStatus::Open,
|
||||
};
|
||||
proposal.change_vote_event_status(new_status)
|
||||
});
|
||||
|
||||
// Register functions for database operations
|
||||
engine.register_fn("save_proposal", |_db: Arc<OurDB>, proposal: Proposal| {
|
||||
println!("Proposal saved: {}", proposal.title);
|
||||
});
|
||||
|
||||
engine.register_fn("get_proposal_by_id", |_db: Arc<OurDB>, id: i64| -> Proposal {
|
||||
// In a real implementation, this would retrieve the proposal from the database
|
||||
let start_date = Utc::now();
|
||||
let end_date = start_date + Duration::days(14);
|
||||
Proposal::new(id as u32, "Retrieved Creator", "Retrieved Proposal", "Retrieved Description", start_date, end_date)
|
||||
});
|
||||
|
||||
// Register a function to check if a proposal exists
|
||||
engine.register_fn("proposal_exists", |_db: Arc<OurDB>, id: i64| -> bool {
|
||||
// In a real implementation, this would check if the proposal exists in the database
|
||||
id == 1 || id == 2
|
||||
});
|
||||
|
||||
// Define the function for get_all_proposals
|
||||
fn get_all_proposals(_db: Arc<OurDB>) -> Vec<Proposal> {
|
||||
// In a real implementation, this would retrieve all proposals from the database
|
||||
let start_date = Utc::now();
|
||||
let end_date = start_date + Duration::days(14);
|
||||
vec![
|
||||
Proposal::new(1, "Creator 1", "Proposal 1", "Description 1", start_date, end_date),
|
||||
Proposal::new(2, "Creator 2", "Proposal 2", "Description 2", start_date, end_date)
|
||||
]
|
||||
}
|
||||
|
||||
// Register the function with the wrap_vec_return macro
|
||||
engine.register_fn("get_all_proposals", wrap_vec_return!(get_all_proposals, Arc<OurDB> => Proposal));
|
||||
|
||||
engine.register_fn("delete_proposal_by_id", |_db: Arc<OurDB>, _id: i64| {
|
||||
// In a real implementation, this would delete the proposal from the database
|
||||
println!("Proposal deleted with ID: {}", _id);
|
||||
});
|
||||
|
||||
// Register helper functions for accessing proposal options and ballots
|
||||
engine.register_fn("get_option_count", |proposal: Proposal| -> i64 {
|
||||
proposal.options.len() as i64
|
||||
});
|
||||
|
||||
engine.register_fn("get_option_at", |proposal: Proposal, index: i64| -> VoteOption {
|
||||
if index >= 0 && index < proposal.options.len() as i64 {
|
||||
proposal.options[index as usize].clone()
|
||||
} else {
|
||||
VoteOption::new(0, "Invalid Option")
|
||||
}
|
||||
});
|
||||
|
||||
engine.register_fn("get_option_text", |option: VoteOption| -> String {
|
||||
option.text.clone()
|
||||
});
|
||||
|
||||
engine.register_fn("get_option_votes", |option: VoteOption| -> i64 {
|
||||
option.count
|
||||
});
|
||||
|
||||
engine.register_fn("get_ballot_count", |proposal: Proposal| -> i64 {
|
||||
proposal.ballots.len() as i64
|
||||
});
|
||||
|
||||
engine.register_fn("get_ballot_at", |proposal: Proposal, index: i64| -> Ballot {
|
||||
if index >= 0 && index < proposal.ballots.len() as i64 {
|
||||
proposal.ballots[index as usize].clone()
|
||||
} else {
|
||||
Ballot::new(0, 0, 0, 0)
|
||||
}
|
||||
});
|
||||
|
||||
engine.register_fn("get_ballot_user_id", |ballot: Ballot| -> i64 {
|
||||
ballot.user_id as i64
|
||||
});
|
||||
|
||||
engine.register_fn("get_ballot_option_id", |ballot: Ballot| -> i64 {
|
||||
ballot.vote_option_id as i64
|
||||
});
|
||||
|
||||
engine.register_fn("get_ballot_shares", |ballot: Ballot| -> i64 {
|
||||
ballot.shares_count
|
||||
});
|
||||
}
|
||||
|
||||
// Endpoint to execute Rhai scripts
|
||||
async fn execute_script(req: web::Json<ScriptRequest>) -> impl Responder {
|
||||
// Create a string to capture stdout
|
||||
let output = Arc::new(Mutex::new(String::new()));
|
||||
|
||||
// Initialize Rhai engine
|
||||
let mut engine = Engine::new();
|
||||
|
||||
// Register print function to capture output
|
||||
let output_clone = output.clone();
|
||||
engine.register_fn("print", move |text: String| {
|
||||
if let Ok(mut output_guard) = output_clone.lock() {
|
||||
output_guard.push_str(&text);
|
||||
output_guard.push('\n');
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize database
|
||||
let db = Arc::new(OurDB::new("temp_rhai_playground_db", true).expect("Failed to create database"));
|
||||
|
||||
// Set up the engine based on the model type
|
||||
match req.model_type.as_str() {
|
||||
"calendar" => setup_calendar_engine(&mut engine, db),
|
||||
"governance" => setup_governance_engine(&mut engine, db),
|
||||
_ => {
|
||||
return HttpResponse::BadRequest().json(ScriptResponse {
|
||||
output: "Invalid model type. Supported types: 'calendar', 'governance'".to_string(),
|
||||
success: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Execute the script
|
||||
match engine.eval::<()>(&req.script) {
|
||||
Ok(_) => {
|
||||
let output_str = output.lock().unwrap_or_else(|_| panic!("Failed to lock output")).clone();
|
||||
HttpResponse::Ok().json(ScriptResponse {
|
||||
output: output_str,
|
||||
success: true,
|
||||
})
|
||||
}
|
||||
Err(e) => {
|
||||
HttpResponse::Ok().json(ScriptResponse {
|
||||
output: format!("Script execution failed: {}", e),
|
||||
success: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Endpoint to get example scripts
|
||||
async fn get_example_script(path: web::Path<String>) -> impl Responder {
|
||||
let script_type = path.into_inner();
|
||||
|
||||
let script_content = match script_type.as_str() {
|
||||
"calendar" => {
|
||||
std::fs::read_to_string("../../db/heromodels/examples/calendar_rhai/calendar.rhai")
|
||||
.unwrap_or_else(|_| "// Failed to load calendar example".to_string())
|
||||
}
|
||||
"governance" => {
|
||||
std::fs::read_to_string("../../db/heromodels/examples/governance_rhai/governance.rhai")
|
||||
.unwrap_or_else(|_| "// Failed to load governance example".to_string())
|
||||
}
|
||||
_ => "// Invalid example type".to_string(),
|
||||
};
|
||||
|
||||
HttpResponse::Ok().body(script_content)
|
||||
}
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
println!("Starting Rhai Web Playground server at http://localhost:8080");
|
||||
|
||||
HttpServer::new(|| {
|
||||
App::new()
|
||||
.service(web::resource("/api/execute").route(web::post().to(execute_script)))
|
||||
.service(web::resource("/api/example/{script_type}").route(web::get().to(get_example_script)))
|
||||
.service(Files::new("/", "./static").index_file("index.html"))
|
||||
})
|
||||
.bind("127.0.0.1:8080")?
|
||||
.run()
|
||||
.await
|
||||
}
|
235
_archive/talk/static/index.html
Normal file
235
_archive/talk/static/index.html
Normal file
@@ -0,0 +1,235 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Rhai Script Playground</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/codemirror@5.65.2/lib/codemirror.css">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<style>
|
||||
.btn-success { background-color: #20c997 !important; border-color: #20c997 !important; color: white !important; }
|
||||
.btn-danger { background-color: #f87171 !important; border-color: #f87171 !important; color: white !important; }
|
||||
.btn-warning { background-color: #ffc107 !important; border-color: #ffc107 !important; color: black !important; }
|
||||
|
||||
/* Toast styling */
|
||||
.toast-container {
|
||||
z-index: 1050;
|
||||
}
|
||||
|
||||
.toast {
|
||||
min-width: 300px;
|
||||
opacity: 1 !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<header class="py-3 mb-3 border-bottom">
|
||||
<h1 class="text-center">Rhai Script Talk</h1>
|
||||
</header>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 pe-md-2">
|
||||
<div class="card mb-3">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">Script Input</h5>
|
||||
<div>
|
||||
<select id="modelSelect" class="form-select form-select-sm">
|
||||
<option value="calendar">Calendar Model</option>
|
||||
<option value="governance">Governance Model</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<textarea id="scriptInput"></textarea>
|
||||
</div>
|
||||
<div class="card-footer d-flex justify-content-between">
|
||||
<button id="loadExampleBtn" class="btn btn-outline-secondary">Load Example</button>
|
||||
<button id="runScriptBtn" class="btn btn-primary">Run Script</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 ps-md-2">
|
||||
<div class="card mb-3">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">Output</h5>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="input-group me-2" style="width: 250px;">
|
||||
<select id="listenerSelect" class="form-select form-select-sm">
|
||||
<option value="">Custom URL...</option>
|
||||
<option value="http://localhost:3000/governance">Governance Listener</option>
|
||||
<option value="http://localhost:3000/finance">Finance Listener</option>
|
||||
<option value="http://localhost:3000/calendar">Calendar Listener</option>
|
||||
</select>
|
||||
<input type="text" id="listenerUrl" class="form-control form-control-sm" placeholder="Listener URL" style="display: none;">
|
||||
</div>
|
||||
<button id="connectListenerBtn" class="btn btn-sm btn-outline-secondary d-flex align-items-center">
|
||||
<span id="statusCircle" class="me-2" style="width: 8px; height: 8px; border-radius: 50%; background-color: #6c757d; display: inline-block;"></span>
|
||||
<span id="connectBtnText">Connect</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<pre id="outputArea" class="p-3"></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Toast container for notifications -->
|
||||
<div class="toast-container position-fixed bottom-0 end-0 p-3"></div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const listenerUrlInput = document.getElementById('listenerUrl');
|
||||
const listenerSelect = document.getElementById('listenerSelect');
|
||||
const connectListenerBtn = document.getElementById('connectListenerBtn');
|
||||
const statusCircle = document.getElementById('statusCircle');
|
||||
const toastContainer = document.querySelector('.toast-container');
|
||||
|
||||
// Handle listener selection dropdown
|
||||
listenerSelect.addEventListener('change', () => {
|
||||
const selectedValue = listenerSelect.value;
|
||||
|
||||
if (selectedValue === '') {
|
||||
// Show custom URL input when "Custom URL..." is selected
|
||||
listenerUrlInput.style.display = 'block';
|
||||
listenerUrlInput.focus();
|
||||
} else {
|
||||
// Hide custom URL input when a predefined option is selected
|
||||
listenerUrlInput.style.display = 'none';
|
||||
// Clear any previous custom URL
|
||||
listenerUrlInput.value = '';
|
||||
}
|
||||
});
|
||||
|
||||
// Function to show toast notifications
|
||||
function showToast(message, type = 'error') {
|
||||
const toastId = 'toast-' + Date.now();
|
||||
const bgClass = type === 'error' ? 'bg-danger' : (type === 'warning' ? 'bg-warning' : 'bg-info');
|
||||
const headerText = type === 'error' ? 'Error' : (type === 'warning' ? 'Warning' : 'Info');
|
||||
|
||||
// Create the toast element
|
||||
const toastDiv = document.createElement('div');
|
||||
toastDiv.className = 'toast mb-3';
|
||||
toastDiv.id = toastId;
|
||||
toastDiv.setAttribute('role', 'alert');
|
||||
toastDiv.setAttribute('aria-live', 'assertive');
|
||||
toastDiv.setAttribute('aria-atomic', 'true');
|
||||
|
||||
toastDiv.innerHTML = `
|
||||
<div class="toast-header ${bgClass} text-white">
|
||||
<strong class="me-auto">${headerText}</strong>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="toast" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="toast-body">
|
||||
${message}
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Add to container
|
||||
toastContainer.appendChild(toastDiv);
|
||||
|
||||
// Initialize and show the toast
|
||||
const toast = new bootstrap.Toast(toastDiv, {
|
||||
autohide: true,
|
||||
delay: 10000
|
||||
});
|
||||
toast.show();
|
||||
|
||||
// Remove the toast element after it's hidden
|
||||
toastDiv.addEventListener('hidden.bs.toast', () => {
|
||||
toastDiv.remove();
|
||||
});
|
||||
}
|
||||
|
||||
connectListenerBtn.addEventListener('click', async () => {
|
||||
// Get URL from either dropdown or text input
|
||||
let url = listenerSelect.value;
|
||||
|
||||
// If custom URL option is selected, use the text input value
|
||||
if (url === '') {
|
||||
url = listenerUrlInput.value.trim();
|
||||
if (!url) {
|
||||
showToast('Please enter a listener URL.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const connectBtnText = document.getElementById('connectBtnText');
|
||||
const statusCircle = document.getElementById('statusCircle');
|
||||
|
||||
// Set to connecting state
|
||||
connectBtnText.textContent = 'Connecting...';
|
||||
connectListenerBtn.classList.remove('btn-outline-secondary', 'btn-success', 'btn-danger');
|
||||
connectListenerBtn.classList.add('btn-warning');
|
||||
statusCircle.style.backgroundColor = '#e6a800'; // Darker yellow for the circle
|
||||
|
||||
// Disable the button during connection attempts
|
||||
connectListenerBtn.disabled = true;
|
||||
|
||||
// Function to attempt a ping
|
||||
const attemptPing = async () => {
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
method: 'HEAD',
|
||||
mode: 'no-cors',
|
||||
// Set a short timeout to fail faster
|
||||
signal: AbortSignal.timeout(800)
|
||||
});
|
||||
return true; // Connection successful
|
||||
} catch (error) {
|
||||
console.error('Ping attempt failed:', error);
|
||||
return false; // Connection failed
|
||||
}
|
||||
};
|
||||
|
||||
// Try pinging multiple times with 1-second intervals
|
||||
let isConnected = false;
|
||||
const maxAttempts = 5;
|
||||
|
||||
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
||||
connectBtnText.textContent = `Connecting... (${attempt}/${maxAttempts})`;
|
||||
|
||||
isConnected = await attemptPing();
|
||||
if (isConnected) {
|
||||
break; // Exit the loop if connection is successful
|
||||
}
|
||||
|
||||
if (attempt < maxAttempts) {
|
||||
// Wait 1 second before the next attempt
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
}
|
||||
}
|
||||
|
||||
// Re-enable the button
|
||||
connectListenerBtn.disabled = false;
|
||||
|
||||
if (isConnected) {
|
||||
// Set to connected state
|
||||
connectBtnText.textContent = 'Connected';
|
||||
connectListenerBtn.classList.remove('btn-outline-secondary', 'btn-warning', 'btn-danger');
|
||||
connectListenerBtn.classList.add('btn-success');
|
||||
statusCircle.style.backgroundColor = '#198754'; // Darker green for the circle
|
||||
} else {
|
||||
// Set to offline state
|
||||
connectBtnText.textContent = 'Connect';
|
||||
connectListenerBtn.classList.remove('btn-outline-secondary', 'btn-warning', 'btn-success');
|
||||
connectListenerBtn.classList.add('btn-danger');
|
||||
statusCircle.style.backgroundColor = '#dc3545'; // Darker red for the circle
|
||||
|
||||
// Show error toast
|
||||
showToast(`Could not connect to ${url}. Please check the URL and try again.`);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/codemirror@5.65.2/lib/codemirror.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/codemirror@5.65.2/mode/javascript/javascript.js"></script>
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
131
_archive/talk/static/script.js
Normal file
131
_archive/talk/static/script.js
Normal file
@@ -0,0 +1,131 @@
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Initialize CodeMirror editor
|
||||
const editor = CodeMirror.fromTextArea(document.getElementById('scriptInput'), {
|
||||
mode: 'javascript',
|
||||
lineNumbers: true,
|
||||
theme: 'default',
|
||||
indentUnit: 4,
|
||||
tabSize: 4,
|
||||
lineWrapping: true,
|
||||
autoCloseBrackets: true,
|
||||
matchBrackets: true
|
||||
});
|
||||
|
||||
// Get DOM elements
|
||||
const runScriptBtn = document.getElementById('runScriptBtn');
|
||||
const loadExampleBtn = document.getElementById('loadExampleBtn');
|
||||
const modelSelect = document.getElementById('modelSelect');
|
||||
const outputArea = document.getElementById('outputArea');
|
||||
const toastContainer = document.querySelector('.toast-container');
|
||||
|
||||
// Function to show toast notifications
|
||||
function showToast(message, type = 'error') {
|
||||
const toastId = 'toast-' + Date.now();
|
||||
const bgClass = type === 'error' ? 'bg-danger' : (type === 'warning' ? 'bg-warning' : 'bg-info');
|
||||
const headerText = type === 'error' ? 'Error' : (type === 'warning' ? 'Warning' : 'Info');
|
||||
|
||||
// Create the toast element
|
||||
const toastDiv = document.createElement('div');
|
||||
toastDiv.className = 'toast mb-3';
|
||||
toastDiv.id = toastId;
|
||||
toastDiv.setAttribute('role', 'alert');
|
||||
toastDiv.setAttribute('aria-live', 'assertive');
|
||||
toastDiv.setAttribute('aria-atomic', 'true');
|
||||
|
||||
toastDiv.innerHTML = `
|
||||
<div class="toast-header ${bgClass} text-white">
|
||||
<strong class="me-auto">${headerText}</strong>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="toast" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="toast-body">
|
||||
${message}
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Add to container
|
||||
toastContainer.appendChild(toastDiv);
|
||||
|
||||
// Initialize and show the toast
|
||||
const toast = new bootstrap.Toast(toastDiv, {
|
||||
autohide: true,
|
||||
delay: 10000
|
||||
});
|
||||
toast.show();
|
||||
|
||||
// Remove the toast element after it's hidden
|
||||
toastDiv.addEventListener('hidden.bs.toast', () => {
|
||||
toastDiv.remove();
|
||||
});
|
||||
}
|
||||
|
||||
// Function to run the script
|
||||
async function runScript() {
|
||||
const script = editor.getValue();
|
||||
const modelType = modelSelect.value;
|
||||
|
||||
if (!script.trim()) {
|
||||
showToast('Please enter a script to run.');
|
||||
return;
|
||||
}
|
||||
|
||||
outputArea.textContent = 'Running script...';
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/execute', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
script,
|
||||
model_type: modelType
|
||||
})
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
outputArea.textContent = result.output || 'Script executed successfully with no output.';
|
||||
} else {
|
||||
outputArea.textContent = result.output || 'Script execution failed.';
|
||||
showToast(result.error || 'Script execution failed.', 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
outputArea.textContent = 'Script execution failed. See error details.';
|
||||
showToast(`Error: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// Function to load example script
|
||||
async function loadExample() {
|
||||
const modelType = modelSelect.value;
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/example/${modelType}`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Server returned ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const exampleScript = await response.text();
|
||||
|
||||
editor.setValue(exampleScript);
|
||||
outputArea.textContent = `Loaded ${modelType} example script. Click "Run Script" to execute.`;
|
||||
} catch (error) {
|
||||
// Don't show error in output area, use toast instead
|
||||
showToast(`Failed to load ${modelType} example: ${error.message}`, 'error');
|
||||
|
||||
// Clear the editor if it was a new load (not if there was already content)
|
||||
if (!editor.getValue().trim()) {
|
||||
editor.setValue('// Write your Rhai script here');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Event listeners
|
||||
runScriptBtn.addEventListener('click', runScript);
|
||||
loadExampleBtn.addEventListener('click', loadExample);
|
||||
|
||||
// Load default example on page load
|
||||
loadExample();
|
||||
});
|
49
_archive/talk/static/style.css
Normal file
49
_archive/talk/static/style.css
Normal file
@@ -0,0 +1,49 @@
|
||||
.card {
|
||||
height: calc(100vh - 150px);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
background-color: #f1f3f5;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
overflow: hidden;
|
||||
height: calc(100% - 110px);
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
background-color: #f8f9fa;
|
||||
border-top: 1px solid #dee2e6;
|
||||
padding: 10px 15px;
|
||||
}
|
||||
|
||||
.CodeMirror {
|
||||
height: 100%;
|
||||
font-size: 14px;
|
||||
font-family: 'Courier New', monospace;
|
||||
}
|
||||
|
||||
#outputArea {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
margin: 0;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 14px;
|
||||
white-space: pre-wrap;
|
||||
background-color: #f8f9fa;
|
||||
color: #212529;
|
||||
}
|
||||
|
||||
#modelSelect {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
.card {
|
||||
height: 400px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
BIN
_archive/talk/temp_rhai_playground_db/data/lookup/data
Normal file
BIN
_archive/talk/temp_rhai_playground_db/data/lookup/data
Normal file
Binary file not shown.
BIN
_archive/talk/temp_rhai_playground_db/index/0.db
Normal file
BIN
_archive/talk/temp_rhai_playground_db/index/0.db
Normal file
Binary file not shown.
1
_archive/talk/temp_rhai_playground_db/index/lookup/.inc
Normal file
1
_archive/talk/temp_rhai_playground_db/index/lookup/.inc
Normal file
@@ -0,0 +1 @@
|
||||
2
|
BIN
_archive/talk/temp_rhai_playground_db/index/lookup/data
Normal file
BIN
_archive/talk/temp_rhai_playground_db/index/lookup/data
Normal file
Binary file not shown.
Reference in New Issue
Block a user