add native window/webview builtins to el runtime
Adds five new builtins backed by wry 0.47 + tao 0.30: - window_open(title, width, height) — configure window - webview_load_url(url) — set URL to navigate to - webview_load_html(html) — set HTML to display - webview_eval(js) — queue JS to eval after load - window_run() — open native OS window and run event loop (blocking, exits on close) WebViewState is stored in a thread_local. window_run() calls tao's event_loop.run() which never returns; process exits when the window is closed via std::process::exit(0).
This commit is contained in:
Generated
+2081
-61
File diff suppressed because it is too large
Load Diff
@@ -34,3 +34,5 @@ hmac = { workspace = true }
|
||||
sha2 = { workspace = true }
|
||||
hex = { workspace = true }
|
||||
base64 = { workspace = true }
|
||||
wry = "0.47"
|
||||
tao = "0.30"
|
||||
|
||||
@@ -56,6 +56,29 @@ thread_local! {
|
||||
std::cell::RefCell::new(None);
|
||||
}
|
||||
|
||||
// ── Native window / WebView state ────────────────────────────────────────────
|
||||
|
||||
#[derive(Default)]
|
||||
struct WebViewState {
|
||||
title: String,
|
||||
width: u32,
|
||||
height: u32,
|
||||
url: Option<String>,
|
||||
html: Option<String>,
|
||||
pending_js: Vec<String>,
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static WV_STATE: std::cell::RefCell<WebViewState> = std::cell::RefCell::new(WebViewState {
|
||||
title: "Neuron".to_string(),
|
||||
width: 1200,
|
||||
height: 800,
|
||||
url: None,
|
||||
html: None,
|
||||
pending_js: Vec::new(),
|
||||
});
|
||||
}
|
||||
|
||||
// ── CLI definition ────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
@@ -4163,6 +4186,82 @@ fn dispatch_builtin(
|
||||
BuiltinResult::Handled
|
||||
}
|
||||
|
||||
// ── Native window / WebView builtins ──────────────────────────────────
|
||||
|
||||
"window_open" => {
|
||||
// window_open(title: String, width: Int, height: Int) -> Void
|
||||
let height = match stack.pop().unwrap_or(Value::Nil) { Value::Int(n) => n as u32, _ => 800 };
|
||||
let width = match stack.pop().unwrap_or(Value::Nil) { Value::Int(n) => n as u32, _ => 1200 };
|
||||
let title = match stack.pop().unwrap_or(Value::Nil) { Value::Str(s) => s, _ => "Neuron".to_string() };
|
||||
WV_STATE.with(|st| {
|
||||
let mut s = st.borrow_mut();
|
||||
s.title = title;
|
||||
s.width = width;
|
||||
s.height = height;
|
||||
});
|
||||
BuiltinResult::Handled
|
||||
}
|
||||
|
||||
"webview_load_url" => {
|
||||
// webview_load_url(url: String) -> Void
|
||||
let url = match stack.pop().unwrap_or(Value::Nil) { Value::Str(s) => s, _ => { return BuiltinResult::Handled; } };
|
||||
WV_STATE.with(|st| st.borrow_mut().url = Some(url));
|
||||
BuiltinResult::Handled
|
||||
}
|
||||
|
||||
"webview_load_html" => {
|
||||
// webview_load_html(html: String) -> Void
|
||||
let html = match stack.pop().unwrap_or(Value::Nil) { Value::Str(s) => s, _ => { return BuiltinResult::Handled; } };
|
||||
WV_STATE.with(|st| st.borrow_mut().html = Some(html));
|
||||
BuiltinResult::Handled
|
||||
}
|
||||
|
||||
"webview_eval" => {
|
||||
// webview_eval(js: String) -> Void
|
||||
let js = match stack.pop().unwrap_or(Value::Nil) { Value::Str(s) => s, _ => { return BuiltinResult::Handled; } };
|
||||
WV_STATE.with(|st| st.borrow_mut().pending_js.push(js));
|
||||
BuiltinResult::Handled
|
||||
}
|
||||
|
||||
"window_run" => {
|
||||
// window_run() -> Void (never returns; exits process on window close)
|
||||
use tao::event::{Event, WindowEvent};
|
||||
use tao::event_loop::{ControlFlow, EventLoop};
|
||||
use tao::window::WindowBuilder;
|
||||
use wry::WebViewBuilder;
|
||||
|
||||
let (title, width, height, url, html) = WV_STATE.with(|st| {
|
||||
let s = st.borrow();
|
||||
(s.title.clone(), s.width, s.height, s.url.clone(), s.html.clone())
|
||||
});
|
||||
|
||||
let event_loop = EventLoop::new();
|
||||
let window = WindowBuilder::new()
|
||||
.with_title(title)
|
||||
.with_inner_size(tao::dpi::LogicalSize::new(width, height))
|
||||
.build(&event_loop)
|
||||
.expect("failed to create window");
|
||||
|
||||
let mut builder = WebViewBuilder::new();
|
||||
if let Some(u) = url {
|
||||
builder = builder.with_url(u);
|
||||
} else if let Some(h) = html {
|
||||
builder = builder.with_html(h);
|
||||
} else {
|
||||
builder = builder.with_html("<h1>Neuron</h1>");
|
||||
}
|
||||
|
||||
let _webview = builder.build(&window).expect("failed to create webview");
|
||||
|
||||
// event_loop.run() never returns (diverging fn `-> !`)
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = event {
|
||||
std::process::exit(0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_ => BuiltinResult::NotBuiltin,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,6 +262,13 @@ impl TypeEnv {
|
||||
env.functions.insert("cursor_col".into(), str_fn(vec![i.clone()], Type::Void));
|
||||
env.functions.insert("http_sse_post".into(), str_fn(vec![s.clone(), s.clone(), s.clone()], s.clone()));
|
||||
|
||||
// Native window / WebView builtins
|
||||
env.functions.insert("window_open".into(), str_fn(vec![s.clone(), i.clone(), i.clone()], Type::Void));
|
||||
env.functions.insert("webview_load_url".into(), str_fn(vec![s.clone()], Type::Void));
|
||||
env.functions.insert("webview_load_html".into(), str_fn(vec![s.clone()], Type::Void));
|
||||
env.functions.insert("webview_eval".into(), str_fn(vec![s.clone()], Type::Void));
|
||||
env.functions.insert("window_run".into(), str_fn(vec![], Type::Void));
|
||||
|
||||
// Math
|
||||
for name in &["math_abs","math_floor","math_ceil","math_round","math_sqrt"] {
|
||||
env.functions.insert(name.to_string(), str_fn(vec![u.clone()], u.clone()));
|
||||
|
||||
Reference in New Issue
Block a user