feat(techdemo): add opaque island bridge

Add a canvas leaf-widget island to the techdemo that receives server snapshots through Effect::event/CustomEvent while keeping slhx core and runtime semantics unchanged.

req: interop/001

req: examples/001

req: examples/005
This commit is contained in:
slhx agent
2026-05-26 07:29:51 +02:00
parent 28ee1b5111
commit 9d3f70e9fe
10 changed files with 179 additions and 7 deletions
+27
View File
@@ -133,6 +133,7 @@ struct ControlCenter {
board: SafeHtml,
inspector: SafeHtml,
activity: SafeHtml,
island_snapshot: String,
}
#[derive(Hemplate)]
@@ -207,6 +208,7 @@ async fn main() {
.route("/slhx.js", get(runtime))
.route("/app.css", get(app_css))
.route("/control_center.css", get(control_center_css))
.route("/island.js", get(island_js))
.with_state(state);
let addr = std::env::var("SLHX_TECHDEMO_ADDR")
@@ -234,6 +236,7 @@ async fn architecture(request: PageRequest) -> impl IntoResponse {
board: architecture_board(),
inspector: architecture_inspector(),
activity: architecture_activity(),
island_snapshot: "2|3|21|architecture route · same opaque island bridge".to_owned(),
});
request
.page(body, shell)
@@ -253,6 +256,10 @@ async fn control_center_css() -> impl IntoResponse {
([("content-type", "text/css; charset=utf-8")], include_str!("../templates/control_center.css"))
}
async fn island_js() -> impl IntoResponse {
([("content-type", "text/javascript; charset=utf-8")], include_str!("../templates/island.js"))
}
async fn interact(
State(state): State<Arc<Shared>>,
form: InteractionForm,
@@ -383,6 +390,7 @@ fn registry(shared: Arc<Shared>) -> HandlerRegistry {
}),
ui::control_center::slots::activity.render(&activity_view(&demo)),
ui::control_center::slots::notice.text("Push simulated · no client app code"),
slhx::event("slhx:island-orbit", island_snapshot(&demo)),
)
}
})
@@ -413,6 +421,7 @@ fn demo_effects(demo: &DemoState, notice: &'static str) -> impl IntoEffect {
ui::control_center::slots::inspector.render(&inspector_view(demo)),
ui::control_center::slots::notice.text(notice),
ui::control_center::forms::launch_work.clear("title"),
slhx::event("slhx:island-orbit", island_snapshot(demo)),
)
}
@@ -421,6 +430,23 @@ fn parse_lane(value: Option<&str>) -> usize {
LANES.iter().position(|(id, _, _)| *id == value).unwrap_or(0)
}
fn island_snapshot(demo: &DemoState) -> String {
// Opaque leaf-widget bridge: compact server snapshot in, native CustomEvent out.
// req: interop/001 req: examples/001
let active = demo.work.iter().filter(|item| item.stage == Stage::Active).count();
let shipped = demo.work.iter().filter(|item| item.stage == Stage::Shipped).count();
let impact: u64 = demo.work.iter().map(|item| item.impact as u64).sum();
format!(
"{}|{}|{}|{} active · {} shipped · {} activity rows",
active + shipped,
demo.work.len(),
impact,
active,
shipped,
demo.activity.len()
)
}
fn page_html(demo: &DemoState) -> String {
// Explicit full-page composition boundary for already-rendered hemplate fragments.
// req: html_safety/002 req: view/001
@@ -429,6 +455,7 @@ fn page_html(demo: &DemoState) -> String {
board: render_board(demo),
inspector: render_inspector(demo),
activity: render_activity(demo),
island_snapshot: island_snapshot(demo),
})
}