docs(examples): hide batch internals from UI copy
Replace beginner-facing EffectBatch/slot-effect wording with generated update language while keeping wire-batch types in tests and low-level APIs. req: dx/006 req: examples/003
This commit is contained in:
+7
-7
@@ -143,7 +143,7 @@ pub fn create_card(
|
||||
}
|
||||
```
|
||||
|
||||
HTML submits as usual. Server returns `EffectBatch`. Browser applies DOM ops.
|
||||
HTML submits as usual. Server returns a typed update batch. Browser applies DOM ops.
|
||||
|
||||
---
|
||||
|
||||
@@ -175,7 +175,7 @@ pub fn drag_card(
|
||||
}
|
||||
```
|
||||
|
||||
Zero round-trip. Zero custom JS. Pure Rust → EffectBatch → DOM.
|
||||
Zero round-trip. Zero custom JS. Pure Rust → typed updates → DOM.
|
||||
|
||||
---
|
||||
|
||||
@@ -259,14 +259,14 @@ pub fn user_joined(user: UserPresence) -> impl IntoEffect {
|
||||
}
|
||||
```
|
||||
|
||||
Browser receives raw `EffectBatch` over WebSocket/SSE:
|
||||
Browser receives typed update bytes over WebSocket/SSE:
|
||||
|
||||
```text
|
||||
Op::AppendKeyed(slot=PRESENCE_USER, key=user_id, html=...)
|
||||
Op::RemoveKeyed(slot=PRESENCE_USER, key=user_id)
|
||||
append keyed presence user
|
||||
remove keyed presence user
|
||||
```
|
||||
|
||||
The runtime does not know "presence". It executes ops.
|
||||
The runtime does not know "presence". It executes generated DOM updates.
|
||||
|
||||
---
|
||||
|
||||
@@ -298,7 +298,7 @@ No framework download. No VDOM. No hydration. No game loop.
|
||||
| Concern | React/Vue | htmx+SSR | slhx |
|
||||
|---|---|---|---|
|
||||
| SSR | RSC/Vue SSR | native | native (hemplate) |
|
||||
| 60fps drag | 100ms re-render + React-DnD | custom JS | WASM handler, EffectBatch |
|
||||
| 60fps drag | 100ms re-render + React-DnD | custom JS | WASM handler, typed update |
|
||||
| Optimistic update | useOptimistic | impossible | `board.update` → `SyncEffect::send_patch` |
|
||||
| Offline support | Service Worker + custom | impossible | patch queue in `slhx-sync` |
|
||||
| Conflict resolution | manual / Yjs CRDT | impossible | server-authoritative patch |
|
||||
|
||||
@@ -21,7 +21,7 @@ use std::time::Duration;
|
||||
|
||||
const LANES: [(&str, &str, &str); 3] = [
|
||||
("compiler", "Compiler", "Surface → generated API"),
|
||||
("runtime", "Runtime", "EffectBatch → DOM"),
|
||||
("runtime", "Runtime", "typed updates → DOM"),
|
||||
("product", "Product", "Native UX, zero app JS"),
|
||||
];
|
||||
|
||||
@@ -315,7 +315,7 @@ fn registry(shared: Arc<Shared>) -> impl DispatchRegistry {
|
||||
}
|
||||
});
|
||||
if let Some(title) = title {
|
||||
demo.spotlight = format!("{title} advanced without a selector: the server returned slot effects.");
|
||||
demo.spotlight = format!("{title} advanced without a selector: the server returned generated slot updates.");
|
||||
demo.log(format!("Advanced {title}"));
|
||||
}
|
||||
demo_effects(&demo, "Pipeline advanced")
|
||||
@@ -382,7 +382,7 @@ fn registry(shared: Arc<Shared>) -> impl DispatchRegistry {
|
||||
move |_| {
|
||||
// req: push/003 req: examples/001
|
||||
let mut demo = shared.demo.lock().unwrap();
|
||||
demo.log("Simulated push event produced the same EffectBatch shape");
|
||||
demo.log("Simulated push event produced the same generated update shape");
|
||||
(
|
||||
ui::put(slots::live_feed, &LiveFeed {
|
||||
tick: demo.activity.len() as u64,
|
||||
@@ -691,7 +691,7 @@ mod tests {
|
||||
assert_eq!(document.select(&selector(".inspector-row")).count(), 3);
|
||||
assert!(document
|
||||
.select(&selector("code"))
|
||||
.any(|code| code.text().collect::<String>().contains("EffectBatch")));
|
||||
.any(|code| code.text().collect::<String>().contains("typed update batch")));
|
||||
}
|
||||
|
||||
// req: html_safety/002 req: view/001 req: test/005
|
||||
@@ -806,7 +806,7 @@ mod tests {
|
||||
lane.select(&selector("p"))
|
||||
.next()
|
||||
.map(|paragraph| paragraph.text().collect::<String>())
|
||||
.is_some_and(|text| text.contains("EffectBatch ops"))
|
||||
.is_some_and(|text| text.contains("typed DOM ops"))
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ main { width:min(1180px, calc(100vw - 32px)); margin:0 auto; padding:38px 0 56px
|
||||
.hero-shell { display:grid; grid-template-columns:1.35fr .85fr; gap:22px; align-items:stretch; }
|
||||
.hero-copy, .hero-panel, .command-card, .board-card, .glass-card, .topology { border:1px solid var(--line); background:linear-gradient(145deg, rgba(255,255,255,.14), rgba(255,255,255,.055)); box-shadow:0 24px 90px rgba(0,0,0,.36), inset 0 1px 0 rgba(255,255,255,.12); backdrop-filter: blur(22px) saturate(145%); border-radius:28px; }
|
||||
.hero-copy { padding:34px; overflow:hidden; position:relative; }
|
||||
.hero-copy::after { content:"EffectBatch"; position:absolute; right:-18px; bottom:8px; font-size:86px; font-weight:900; color:rgba(255,255,255,.045); }
|
||||
.hero-copy::after { content:"slhx"; position:absolute; right:-18px; bottom:8px; font-size:86px; font-weight:900; color:rgba(255,255,255,.045); }
|
||||
.eyebrow { color:var(--cyan); text-transform:uppercase; letter-spacing:.2em; font-weight:800; font-size:12px; }
|
||||
h1 { font-size:clamp(42px, 7vw, 84px); line-height:.88; letter-spacing:-.075em; margin:12px 0 18px; max-width:900px; text-wrap:balance; overflow-wrap:anywhere; }
|
||||
h2 { margin:0 0 16px; letter-spacing:-.035em; }
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<div class="hero-copy">
|
||||
<p class="eyebrow">slhx Control Plane</p>
|
||||
<h1>A Linear-class work system without a frontend framework.</h1>
|
||||
<p class="lede">Create, inspect, advance, and stream work through native HTML, generated typed resources, and compact EffectBatches. The UI feels app-grade; the model stays server-owned and boring.</p>
|
||||
<p class="lede">Create, inspect, advance, and stream work through native HTML, generated typed resources, and compact update batches. The UI feels app-grade; the model stays server-owned and boring.</p>
|
||||
</div>
|
||||
<div class="hero-panel" data-slhx-slot="hero_metrics">{+= self.hero =+}</div>
|
||||
</section>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div class="lanes">
|
||||
<section class="lane"><h3>hemplate</h3><p>Owns syntax and Surface facts.</p></section>
|
||||
<section class="lane"><h3>slhx-build</h3><p>Generates resources and lowering tables.</p></section>
|
||||
<section class="lane"><h3>runtime</h3><p>Executes compact EffectBatch ops.</p></section>
|
||||
<section class="lane"><h3>runtime</h3><p>Executes compact typed DOM ops.</p></section>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{+= self.selected =+}
|
||||
<div class="inspector-row"><b>What happened</b><br>{+ self.spotlight +}</div>
|
||||
<div class="inspector-row"><b>Wire contract</b><br><code>POST __h → application/slhx → EffectBatch</code></div>
|
||||
<div class="inspector-row"><b>Wire contract</b><br><code>POST __h → application/slhx → typed update batch</code></div>
|
||||
<div class="inspector-row"><b>Runtime</b><br>Root-scoped delegated listeners; numeric targets only.</div>
|
||||
|
||||
@@ -1 +1 @@
|
||||
<div class="live-row"><strong>SSE tick #{+ self.tick +}</strong><br>Server streamed a typed EffectBatch into <code>slots::live_feed</code>.</div>
|
||||
<div class="live-row"><strong>SSE tick #{+ self.tick +}</strong><br>Server streamed a generated update into <code>slots::live_feed</code>.</div>
|
||||
|
||||
@@ -223,7 +223,7 @@ fn product_is_e2e_working_over_http() {
|
||||
let push_batch = simulated_push.batch();
|
||||
assert_payload_contains(&push_batch, "SSE tick");
|
||||
assert_payload_contains(&push_batch, "Push simulated · no client app code");
|
||||
assert_payload_contains(&push_batch, "Simulated push event produced the same EffectBatch shape");
|
||||
assert_payload_contains(&push_batch, "Simulated push event produced the same generated update shape");
|
||||
assert_emit(&push_batch, "slhx:island-orbit", "activity rows");
|
||||
|
||||
let delete_missing = post(
|
||||
|
||||
@@ -173,7 +173,7 @@ fn registry(state: Arc<ExampleState>) -> impl DispatchRegistry {
|
||||
// req: page_swap/002, req: examples/001
|
||||
(
|
||||
page_swap::put(page_slots::content, &DocsContent {
|
||||
message: "This content came from an EffectBatch.",
|
||||
message: "This content came from a generated update response.",
|
||||
}),
|
||||
page_slots::title.text("Docs"),
|
||||
push("/docs"),
|
||||
@@ -257,7 +257,7 @@ mod tests {
|
||||
// req: html_safety/002 req: view/001 req: test/005
|
||||
#[test]
|
||||
fn docs_content_payload_is_rendered_by_a_hemplate_view() {
|
||||
let html = render_docs_content("This content came from an EffectBatch.");
|
||||
let html = render_docs_content("This content came from a generated update response.");
|
||||
let document = Html::parse_fragment(html.as_str());
|
||||
assert_eq!(
|
||||
document
|
||||
@@ -271,7 +271,7 @@ mod tests {
|
||||
.select(&selector("p"))
|
||||
.next()
|
||||
.map(|paragraph| paragraph.text().collect::<String>()),
|
||||
Some("This content came from an EffectBatch.".to_owned())
|
||||
Some("This content came from a generated update response.".to_owned())
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user