Remote views
A process extension draws Dioxus views in its own process; the app shows them in workspace panes over vox, and a placeholder with the reason when the process dies.
What it is and when to use it#
A process extension is a program of its own. For each view the app opens, it
runs a Dioxus VirtualDom on its own thread and streams the DOM edits, as
architect’s versioned wire edits, over vox on a local socket. The app mounts
each view as a sub-document inside a workspace pane and sends the person’s
clicks, keys and form input back. Every edit is checked before it is
applied, so a misbehaving process cannot crash the app. If the process dies,
the pane shows a placeholder with the reason and a Start again button; the
supervisor restarts it with backoff, and the view comes back.
Use it for a tool that may crash, runs another runtime, or should be developed and restarted apart from the app, but needs real UI. Design: Extensions, sections on processes over vox and on views rendered remotely; the manifest side is Extensions.
Example: examples/extension, a registry with a Luau extension and a process extension loaded by a small host, hot-reloaded on save; walked through in Write an extension, end to end.
A minimal example#
The extension, architect-extensions with guest, from
apps/sandbox/clicker/src/main.rs:
use architect_extensions::guest::{Guest, GuestError};
fn main() -> Result<(), GuestError> {
Guest::new()
.view("counter", Counter) // fn() -> Element
.action("LAB_CLICKER_CLICKS", |_| async { Ok(serde_json::json!(clicks())) })
.serve() // the socket the host passes
}
Its manifest, apps/sandbox/clicker/extension.toml, has kind = "process", an
[entry] with the program’s path, its [[actions]] and a [[views]] entry
with id = "counter".
The app, with ui and remote (desk only), after apps/ui/src/extensions.rs
and apps/ui/src/vault.rs:
use architect_extensions::process::{ProcessRunner, Processes};
static PROCESSES: LazyLock<Processes> = LazyLock::new(Processes::default);
let host = Host::new(list, sections)
.with_runner(ProcessRunner::new()?.with_processes(PROCESSES.clone()));
// Extensions::new(host, ..), then install or dev the extension
View::new("clicker", "Clicker", |_| rsx! {
architect_extensions::remote::RemoteView {
processes: PROCESSES.clone(), extension: "lab.clicker", view: "counter",
}
})
Code tour#
features/extensions/src/proto.rsProcessExtension: the vox service a process serves (hello,run,event,close, and theviewsubscription), andSOCKET.features/extensions/src/wire.rsWIRE,Hello,Open,Frame,Edit,ViewEvent: the versioned edits and events.features/extensions/src/process.rsProcessRunner(with_restart,with_processes): starts the program and waits for its hello, which must cover what the manifest declares.features/extensions/src/process.rsProcess(state,open,restart,stop),State,Restart(three tries with backoff by default),Processes.features/extensions/src/guest.rsGuest(view,action,serve) andWireWriter: Dioxus mutations to wire edits, on the extension’s side.features/extensions/src/remote.rsRemoteDocument: a Blitz document that checks and applies each edit within its caps and forwards events.features/extensions/src/remote.rsRemoteView: the pane’s component; follows the process and shows the placeholder.
When the pane mounts, RemoteView waits for the process to be running, then
Process::open(view) subscribes to the view over vox. It installs a
RemoteDocument as the pane node’s sub-document and applies each Frame as
it arrives. On the extension’s side, Guest starts a VirtualDom on a
thread for that view, writes its renders through WireWriter, and
dispatches the events that come back. When the frames stop (the process
died or was unloaded), the view removes the sub-document, shows the
placeholder, and follows the process when it restarts.
Tests to read#
features/extensions/tests/remote_view.rs(the test binary starts itself again as the extension): clicks go through the sub-document and the count comes back; the extension’s action runs over vox;kill -9leaves the app running and the placeholder shows why; Start again brings it back; unloading shows a placeholder; a declared view the process does not draw, or a program that never says hello, fails to load; nonsense frames (unknown ids, cycles, a pop past the root, a child that is not there, raw HTML) are refused without a crash.
Status#
Done in architect on Unix desks; the sandbox runs the clicker. Not yet: a stdio transport, starting lazily and stopping when idle, handing state over on a reload, a host service extensions call back into, Windows; pointer, touch, wheel and scroll events, IME, and an element and CSS allowlist (only raw HTML is refused today).
- Task and Studio do not use it yet.
Linked from
- FeaturesOne page per major architect feature: what it is, the least code to use it, a tour of its code, the tests to read, and its status.
- Extensionsarchitect-extensions: packages that add actions, settings, views and document data at run time; data, Luau and process kinds; git, folder and ReaPack sources; hot reload; a project's required extensions under an org's policy.