Skip to main content

Module runtime

Module runtime 

Source
Expand description

Host-generic ECS driver: dispatchers, derivation fixed-point, mirror sync, timers, HTTP fetch, and runtime::ScriptPlugin. Formerly the lumen-script-runtime crate; folded in with its flat surface re-exported at the crate root for source compatibility. Host-generic script runtime.

Everything in the scripting contract that is not compilation, value conversion, or invocation lives here, parameterized over H: ScriptHost: the 18-event dispatch surface with per-id handler routing, the derivation fixed-point driver, the store->mirror sync policy driver, timers, HTTP fetch plumbing, the load-failure banner protocol, and the ScriptPlugin that wires it all into the tick.

Concrete hosts, each a runtime module (lumen-candela and the others under std/), provide engine + builtins + conversion and hand a built host to ScriptPlugin::new.

§Ordering contract (7bfc0f2 - do not regress)

Systems:
  1. control pushes (push_toggle/slider/textinput/scroll_to_signal)
  2. sync_signals_into_host .after(all pushes)   - type-preserving parse-back
  3. tick_script .after(sync) + all event dispatchers
  4. embedder's apply_script_commands .after(dispatchers) .before(derivations, readers)
  5. apply_derivations .after(sync)              - fixed point, <= 32 passes
  6. dirty-gated binding readers
A11ySync: clear_property_store_dirty

Dirty flags live exactly one tick; every consumer of “what changed” must be ordered inside that window.

§Several hosts in one app

An app can run more than one host at a time (one per script language it ships). Each host is its own Resource, so every ::<H> system above is monomorphised once per active host. Embedders must therefore order against ScriptSet rather than against a concrete system::<H>: a .before( apply_derivations::<Rhai>) edge says nothing about the Lua host’s derivation pass, and the dirty-window guarantees collapse for every host it does not name. The sets below hold the same systems for every active host, so one edge covers all of them.

Hosts share the world’s PropertyStore, so a signal written by one is read by the others on the same tick. Lifecycle and event callbacks (on_start, on_ready, on_click, on_timer, …) are delivered to every active host; a host that does not define the handler ignores the call.

Structs§

DueFrame
The step handed to on_frame this tick, or None when no frame is due. Rewritten every tick by retire_frame_request and read by fire_frame_callbacks on each active host.
DueTimers
Names of the timers due this tick, in sorted order. Rewritten every tick by retire_due_timers and read by fire_due_timers on each active host.
FetchRegistry
HTTP plumbing: start requests away from the world thread, marshal the completed reply back onto the ECS/UI thread, and surface it to the script.
FrameHook
The state behind request_frame() / on_frame(dt).
HttpHooks
The HttpHooks every script request passes through, in the order they were added.
OnReadyFired
Latch guarding fire_on_ready: holds the ScriptHost::lang of every host that has already dispatched on_ready, so each active host fires once per mount. Hot reload clears the set after respawning the tree, so a script that builds DOM in on_ready rebuilds it on the fresh mount.
PendingFetchReplies
HTTP replies that finished this tick, waiting to be delivered to every active host. Filled by collect_fetch_replies and emptied by clear_fetch_replies.
PendingPluginEvents
Handler calls pushed from outside a script call and delivered this tick, mirroring PendingFetchReplies: filled by collect_plugin_events, offered to every active host by fire_plugin_events, and emptied by clear_plugin_events.
ScriptCommandEvent
One ScriptCommand flowing through the ECS message bus so app systems can read it via MessageReader<ScriptCommandEvent>.
ScriptLoadFailure
Inserted by ScriptPlugin::build when the initial script failed to compile or evaluate. Embedders (lumenc’s run) surface it prominently - e.g. through the in-window error banner - so a dead script never masquerades as a healthy app that just ignores clicks. Absent when the script loaded cleanly.
ScriptPlugin
Generic script plugin: loads source into the supplied host, fires on_start (re-stashing its commands into the sink so they flow through the first tick’s normal drain), installs the host as a Resource, and registers the full dispatcher / derivation / timer / fetch system set.
ScriptStartedAt
Wall-clock moment the script plugin was installed. Kept for embedders that want elapsed-since-install (the FFI surface reads it); the runtime itself is reactive-only and no longer computes a per-frame t.
TimerRegistry
Active timers keyed by name. Names are unique - setting a timer with the same name replaces the previous one (matches set_interval / set_timeout semantics in browsers).

Enums§

ScriptSet
Ordering anchors for the host-generic script systems.

Constants§

MAX_FRAME_DT
The longest step on_frame is ever handed, in seconds.

Functions§

apply_derivations
Re-evaluate every computed signal whose deps changed this tick.
clear_fetch_replies
Drop this tick’s delivered HTTP replies. Runs after every host’s fire_fetched_responses.
clear_plugin_events
Drop this tick’s delivered plugin events. Runs after every host’s fire_plugin_events.
collect_fetch_replies
Move finished HTTP replies (marshalled back from the worker threads) off the channel into PendingFetchReplies, and record each for the devtools network pane. Host-neutral and registered once, so a reply is offered to every active host rather than taken by whichever ran first.
collect_plugin_events
Move the events plugins and capabilities pushed into PendingPluginEvents. Host-neutral and registered once, so a handler call is offered to every active host rather than taken by whichever ran first; a PluginEvent::Commands batch goes straight onto the command bus here instead, so it applies once however many hosts run.
dispatch_clicks_and_doubles
Forward this tick’s ClickEvent / DoubleClickEvent messages to the script’s on_click(id) / on_double_click(id) handlers.
dispatch_close_to_script
Forward the window backend’s lumen_core::input::CloseRequest to the script’s on_close() lifecycle hook - before the backend tears anything down, so scripts get a last chance to persist state.
dispatch_dialog_closes_to_script
Forward lumen_core::input::DialogClosed (W5 dialog contract) to the script: accepted = true routes as on_dialog_accepted(id) / on("dialog_accepted", id, fn), accepted = false as on_dialog_rejected(id) / on("dialog_rejected", id, fn). The emitter guarantees exactly one message per open->close cycle.
dispatch_file_drops_to_script
Forward FileDropped as on_file_dropped(id, path).
dispatch_long_press_to_script
Forward LongPressEvent as on_long_press(id).
dispatch_menu_clicks_to_script
Forward lumen_core::input::MenuClicked to the script as on_menu(id), with the per-id on("menu", id, fn) router applying first.
dispatch_slider_to_script
Forward lumen_primitives::SliderChanged as on_slider(id, value) (f64), with per-id handler routing via on("slider", "<id>", "<fn>").
dispatch_text_input_to_script
Forward text changes as on_text_input(id, text).
dispatch_toggle_to_script
Forward lumen_primitives::ToggleChanged as on_toggle(id, checked) (bool), with per-id handler routing via on("toggle", "<id>", "<fn>").
drain_fetch_commands
Read Fetch / Http commands the script emitted this tick and hand each to the registry’s dispatcher. Other variants are no-ops here (they flow to apply_script_commands and timer drains separately).
drain_frame_requests
Read the RequestFrame commands emitted this tick and arm the hook.
drain_timer_commands
Read SetTimer / CancelTimer commands emitted by the script’s builtins this tick and update the TimerRegistry accordingly, then ask the loop to wake at the earliest pending deadline. Without that request an idle loop parks on events alone and a timer armed from a click fires only when some unrelated input ticks the app.
fill_components
Fill every use site the build could not finish by calling the function it names and putting the node that call returns in the marker’s place.
fire_due_timers
Fire on_timer(name) for every timer retire_due_timers collected this tick. A host that defines no matching handler ignores the call, so in an app running several languages the timer reaches whichever host declared it.
fire_fetched_responses
Invoke the script’s completion handler once for each reply collect_fetch_replies gathered. This is the only place a network reply crosses into script/signal land - the worker never touches the world, mirroring Slint’s invoke_from_event_loop.
fire_frame_callbacks
Call on_frame(dt) on this host when retire_frame_request found a frame due. A host that declares no on_frame is silent, so in an app running several languages the callback reaches whichever host declared it.
fire_on_ready
Dispatch the script’s optional on_ready() once per mount, on the first tick after the DOM index is published.
fire_plugin_events
Invoke the script’s handler once for each call collect_plugin_events gathered. Routing matches the fetch pipeline exactly: a per-key on(event, key, fn) registration wins, else the event’s fallback, and the key rides as the handler’s first argument ahead of the event’s own.
panic_payload_text
Best-effort text for a caught panic payload.
register_plugin_event_message
Register PluginEvent as a message, so a main-thread system can write one through MessageWriter<PluginEvent>. Idempotent: ScriptPlugin calls it, and so does a capability that writes events, whichever installs first.
register_script_commands
Make sure the ScriptCommandEvent stream exists in world.
reload_script
Hot-reload orchestration entry: swap the loaded program on the live host resource via ScriptHost::replace (compile-first, atomic, full rollback on eval failure - the host owns the atomicity). Returns None when no host is installed (script-less app).
retire_due_timers
Collect the timers whose deadline has passed, reschedule the repeating ones, and drop the one-shots. Host-neutral and registered once, so each active host is offered the same due list instead of the first one to run taking it.
retire_frame_request
Decide whether a frame callback is due this tick and what step it gets.
sync_signals_into_host
Mirror the ECS-side global lumen_core::property_store::PropertyStore cells into the host’s rich-typed local mirror at the start of every tick, walking only the keys dirtied this tick. Without this, the script’s signal reads see stale values when another system (a <for> reconciler, a two-way bind push, …) wrote to the property store between the previous tick and this one.
tick_script
Per-tick driver that drains the host’s command sink and forwards every ScriptCommand onto the ScriptCommandEvent message bus. On the first tick this also flushes the commands re-stashed by on_start (see ScriptPlugin::build).