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