feat(api): streamline generated app authoring

Move the canonical examples toward generated component-root helpers, typed form decoding, async/state handler registration, and derive-driven app/component registry wiring. Tighten requirements and diagnostics for the server-first, selectorless authoring path.

Verified with cargo run -p slhx-xtask -- test, cargo check --workspace, redgate list, redgate refs, redgate health --strict, and git diff --check.

req: canonical/001

req: canonical/003

req: canonical/004

req: dx/002

req: derive_app/001

req: component/003

req: form/004

req: axum_integration/003
This commit is contained in:
slhx agent
2026-06-05 06:33:41 +02:00
parent eb6086616c
commit d4e865ef92
34 changed files with 4573 additions and 1156 deletions
@@ -17,7 +17,7 @@
<aside class="command-card">
<h2>Create an issue</h2>
<form id="launch-work" method="post" data-slhx-handle="launch_work" data-slhx-form="launch_work" data-slhx-disable-while-pending>
<label>Title <input name="title" required="required" value="Ship typed effects"></label>
<label>Title <input name="title" required="required" value="Ship typed updates"></label>
<label>Lane
<select name="lane" required="required">
<option value="compiler">Compiler</option>
@@ -32,7 +32,7 @@
<button type="button" data-slhx-handle="simulate_push">Simulate server push</button>
<button type="button" data-slhx-handle="reset_demo">Reset demo</button>
</div>
<p data-slhx-slot="notice" class="notice">Every control posts a numeric handle id and receives typed effects.</p>
<p data-slhx-slot="notice" class="notice">Every control posts through a generated handle and receives typed updates.</p>
</aside>
<section class="board-card">
@@ -46,8 +46,8 @@
<section class="insight-grid">
<article class="glass-card">
<h2>Effect inspector</h2>
<div data-slhx-slot="inspector">{+= self.inspector =+}</div>
<h2>Update inspector</h2>
<div data-slhx-slot="inspector" class="inspector-slot">{+= self.inspector =+}</div>
</article>
<article class="glass-card island-card" data-slhx-island="orbit" +data-island-snapshot="self.island_snapshot">
<h2>Opaque island bridge</h2>
@@ -60,7 +60,7 @@
</article>
<article class="glass-card glow">
<h2>Server push</h2>
<div data-slhx-slot="live_feed">Waiting for SSE heartbeat…</div>
<div data-slhx-slot="live_feed" class="live-feed">Waiting for SSE heartbeat…</div>
</article>
</section>
@@ -1,4 +1,4 @@
{+= self.selected =+}
<div class="inspector-row"><b>What happened</b><br>{+ self.spotlight +}</div>
<div class="inspector-row"><b>Update path</b><br><code>targets::* → tuple effects → DOM</code></div>
<div class="inspector-row"><b>Runtime</b><br>Root-scoped delegated listeners; numeric targets only.</div>
<div class="inspector-row"><b>Update path</b><br><code>generated targets → tuple updates → DOM</code></div>
<div class="inspector-row"><b>Runtime</b><br>Root-scoped delegated listeners; generated targets only.</div>
@@ -2,8 +2,8 @@
<header><strong>{+ self.title +}</strong><span class="pill">{+ self.stage +}</span></header>
<div class="impact"><i +style="self.impact_style"></i></div>
<div class="card-actions">
<button type="button" data-slhx-handle="spotlight_work" +data-work-id="self.id">Inspect</button>
<button type="button" data-slhx-handle="advance_work" +data-work-id="self.id">Advance</button>
<button type="button" data-slhx-handle="delete_work" +data-work-id="self.id">Delete</button>
<button type="button" data-slhx-handle="spotlight_work" +data-work-id="self.id" name="work_id" +value="self.id">Inspect</button>
<button type="button" data-slhx-handle="advance_work" +data-work-id="self.id" name="work_id" +value="self.id">Advance</button>
<button type="button" data-slhx-handle="delete_work" +data-work-id="self.id" name="work_id" +value="self.id">Delete</button>
</div>
</article>
@@ -1 +1 @@
<div class="live-row"><strong>SSE tick #{+ self.tick +}</strong><br>Server streamed a generated update into <code>slots::live_feed</code>.</div>
<div class="live-row"><strong>SSE tick #{+ self.tick +}</strong><br>Server streamed a generated update into the live feed target.</div>