refactor!: rename slhx to hemx

Rename the tracked product identity, crate/package names, Rust paths/macros, generated artifacts, runtime files, public attributes, examples, docs, requirements, and tests from slhx to hemx without compatibility shims.

Verified with cargo run -p hemx-xtask -- test, cargo test -p hemx-derive --test compile_fail, cargo test -p hemx-js, cargo test -p hemx-axum, cargo test -p hemx-v0-examples, cargo check --workspace, redgate list, redgate refs, redgate health --strict, git diff --check, and git grep/ls-files legacy-name audits.

req: misc/001

req: codegen/001

req: component/004

req: runtime/001
This commit is contained in:
slhx agent
2026-06-05 06:52:37 +02:00
parent d4e865ef92
commit c33500440e
69 changed files with 1415 additions and 1415 deletions
+35 -35
View File
@@ -4,18 +4,18 @@ A board with drag-and-drop cards, 60fps pointer-follow, optimistic updates,
offline queue, conflict reconciliation, live presence, and SSR-first rendering —
all without React/Vue/VDOM, in a single typed Rust codebase.
This is an explicitly advanced/low-level north-star boundary sketch for slhx + hemplate + slhx-sync, not the beginner-facing authoring path. Raw sync/effect/wire vocabulary below is excluded from beginner-facing examples by design.
This is an explicitly advanced/low-level north-star boundary sketch for hemx + hemplate + hemx-sync, not the beginner-facing authoring path. Raw sync/effect/wire vocabulary below is excluded from beginner-facing examples by design.
---
## 1. Template: `board.heml`
```html
<section data-slhx-root="board" data-slhx-slot="board" data-slhx-atom="board">
<section data-hemx-root="board" data-hemx-slot="board" data-hemx-atom="board">
<header>
<h1>{+ self.title +}</h1>
<form data-slhx-handle="create_card" data-slhx-form="create_card">
<form data-hemx-handle="create_card" data-hemx-form="create_card">
<input name="title" type="text" required>
<select name="column">
<template h-for="column in &self.columns" h-key="column.id">
@@ -26,14 +26,14 @@ This is an explicitly advanced/low-level north-star boundary sketch for slhx + h
</form>
</header>
<div class="columns" data-slhx-slot="columns">
<div class="columns" data-hemx-slot="columns">
<template h-for="column in &self.columns" h-key="column.id">
<section class="column" data-slhx-slot="column" +data-column-id="column.id">
<section class="column" data-hemx-slot="column" +data-column-id="column.id">
<h2>{+ column.title +}</h2>
<div class="cards" +data-column-id="column.id">
<template h-for="card in &column.cards" h-key="card.id">
<article class="card" data-slhx-slot="card" data-slhx-handle="drag_card" +data-card-id="card.id" draggable="true">
<article class="card" data-hemx-slot="card" data-hemx-handle="drag_card" +data-card-id="card.id" draggable="true">
<strong>{+ card.title +}</strong>
<small>{+ card.assignee +}</small>
</article>
@@ -43,7 +43,7 @@ This is an explicitly advanced/low-level north-star boundary sketch for slhx + h
</template>
</div>
<aside data-slhx-slot="presence">
<aside data-hemx-slot="presence">
<template h-for="user in &self.online_users" h-key="user.id">
<span>{+ user.name +}</span>
</template>
@@ -53,16 +53,16 @@ This is an explicitly advanced/low-level north-star boundary sketch for slhx + h
Notes on keyed scopes:
- `h-for="column in &self.columns" h-key="column.id"`**required** for slhx-addressable nodes inside
- `h-for="column in &self.columns" h-key="column.id"`**required** for hemx-addressable nodes inside
- `h-for="card in &column.cards" h-key="card.id"`**required**
- A slot inside a keyed loop is addressed as a generated keyed resource, never by selector strings or positional DOM targeting
- Without `h-key`, slhx rejects the build — no runtime selector fallback
- Without `h-key`, hemx rejects the build — no runtime selector fallback
---
## 2. What hemplate exports
hemplate does **not** interpret `data-slhx-*`. It records raw facts:
hemplate does **not** interpret `data-hemx-*`. It records raw facts:
```rust
Node {
@@ -70,8 +70,8 @@ Node {
element: "article",
attrs: [
("class", "card"),
("data-slhx-slot", "card"),
("data-slhx-handle", "drag_card"),
("data-hemx-slot", "card"),
("data-hemx-handle", "drag_card"),
("data-card-id", "{card.id}"),
("draggable", "true"),
],
@@ -87,7 +87,7 @@ FormSurface {
}
```
slhx reads this from hemplate Surface facts and generates scoped typed resources:
hemx reads this from hemplate Surface facts and generates scoped typed resources:
```rust
use ui::board::{forms, targets};
@@ -104,7 +104,7 @@ No string desync. No manual ids. The generated module owns the names.
## 3. App State
```rust
#[slhx::app]
#[hemx::app]
pub struct BoardApp {
pub board: Atom<BoardState>,
pub drag: Atom<Option<DragState>>,
@@ -119,13 +119,13 @@ The same struct runs on server (SSR) and in WASM (client-local effects).
## 4. Normal Form: Server-first
```rust
#[derive(SlhxForm)]
#[derive(HemxForm)]
pub struct CreateCardForm {
pub title: String,
pub column: ColumnId,
}
#[slhx::handler]
#[hemx::handler]
pub fn create_card(
form: Form<CreateCardForm>,
app: &mut BoardApp,
@@ -137,7 +137,7 @@ pub fn create_card(
(
targets::card.append(card.id, &CardView::from(card)),
forms::create_card.clear("title"),
// slhx-sync: queue atomic board state diff for sync
// hemx-sync: queue atomic board state diff for sync
SyncEffect::send_patch(atoms::board, Patch::insert_card(form.column, card)),
)
}
@@ -150,7 +150,7 @@ HTML submits as usual. Server returns a typed update batch. Browser applies DOM
## 5. Drag: 60fps client-local WASM
```rust
#[slhx::handler(client)]
#[hemx::handler(client)]
pub fn drag_card(
event: DragEvent,
app: &mut BoardApp,
@@ -163,7 +163,7 @@ pub fn drag_card(
}));
// Client-local extension APIs stay typed by generated resources;
// names below are illustrative until slhx-sync lands.
// names below are illustrative until hemx-sync lands.
Effect::batch((
Effect::class_keyed(slots::CARD, event.card_id, "dragging", true),
Effect::transform_keyed(
@@ -182,7 +182,7 @@ Zero round-trip. Zero custom JS. Pure Rust → typed updates → DOM.
## 6. Drop: optimistic update + sync
```rust
#[slhx::handler(client)]
#[hemx::handler(client)]
pub fn drop_card(
event: DropEvent,
app: &mut BoardApp,
@@ -194,7 +194,7 @@ pub fn drop_card(
app.drag.set(None);
// Client-local extension APIs stay typed by generated resources;
// names below are illustrative until slhx-sync lands.
// names below are illustrative until hemx-sync lands.
Effect::batch((
Effect::move_keyed(
slots::CARD,
@@ -204,7 +204,7 @@ pub fn drop_card(
InsertBefore(event.before_card),
),
Effect::class_keyed(slots::CARD, event.card_id, "dragging", false),
// slhx-sync: queue patch, send when online
// hemx-sync: queue patch, send when online
SyncEffect::send_patch(atoms::BOARD, patch),
))
}
@@ -212,14 +212,14 @@ pub fn drop_card(
A pure htmx+SSR app cannot model this: 60fps pointer → local transient drag → optimistic update → offline queue → reconciliation. You'd need custom JS or a parallel React/Vue layer.
slhx models it in one type graph.
hemx models it in one type graph.
---
## 7. Server reconciliation
```rust
#[slhx_sync::handler]
#[hemx_sync::handler]
pub fn apply_board_patch(
patch: BoardPatch,
app: &mut BoardApp,
@@ -253,7 +253,7 @@ Server-authoritative on conflict. No Redux sagas. No React Query cache fades.
## 8. Presence
```rust
#[slhx_sync::presence]
#[hemx_sync::presence]
pub fn user_joined(user: UserPresence) -> impl IntoEffect {
targets::presence_user.append(user.id, &PresenceBadge::from(user))
}
@@ -272,22 +272,22 @@ The runtime does not know "presence". It executes generated DOM updates.
## 9. What app authors write; what the browser receives
Initial SSR stays an ordinary rendered template with symbolic slhx attributes at
Initial SSR stays an ordinary rendered template with symbolic hemx attributes at
the authoring boundary:
```html
<section data-slhx-root="board" data-slhx-slot="board" data-slhx-atom="board">
<section data-hemx-root="board" data-hemx-slot="board" data-hemx-atom="board">
...
<article data-slhx-slot="card" data-slhx-handle="select_card" +data-card-id="card.id">
<article data-hemx-slot="card" data-hemx-handle="select_card" +data-card-id="card.id">
{+ card.title +}
</article>
...
</section>
<!-- the app shell loads /slhx.js and any bootstrap state -->
<!-- the app shell loads /hemx.js and any bootstrap state -->
```
The compiler lowers those symbols to compact runtime metadata, but that metadata
is not an app-authoring contract. Runtime attachment: `/slhx.js` installs
is not an app-authoring contract. Runtime attachment: `/hemx.js` installs
delegated root listeners for forms, clicks, and pointer/drag events. App authors
keep composing generated resources; they do not attach per-node listeners, copy
numeric ids, or write selector glue.
@@ -298,18 +298,18 @@ No framework download. No VDOM. No hydration. No game loop.
## 10. Why this is not a React/Vue/htmx app
| Concern | React/Vue | htmx+SSR | slhx |
| Concern | React/Vue | htmx+SSR | hemx |
|---|---|---|---|
| SSR | RSC/Vue SSR | native | native (hemplate) |
| 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` |
| Offline support | Service Worker + custom | impossible | patch queue in `hemx-sync` |
| Conflict resolution | manual / Yjs CRDT | impossible | server-authoritative patch |
| Presence | WebSocket + custom state | SSE possible | `Effect::broadcast` over channel |
| Keyed DOM | React key | not a concern | `KeyedSlot<K, T>` compile-time |
| Forms | React Hook Form | HTML native, but no validation bridge | `Form<T>` derived from `.heml` surface |
| Routing | React Router / Vue Router | HTML links, but no state routing | `Effect::navigate` with scroll/title |
| Total JS shipped | ~300KB+ | ~20KB htmx + custom | ~3KB slhx.js interpreter |
| Total JS shipped | ~300KB+ | ~20KB htmx + custom | ~3KB hemx.js interpreter |
---
@@ -317,7 +317,7 @@ No framework download. No VDOM. No hydration. No game loop.
```text
A local-first multiplayer board where all high-frequency UI runs as Rust/WASM effects,
all durable state syncs through slhx-sync,
all durable state syncs through hemx-sync,
all HTML is hemplate-rendered,
and the browser runtime only executes typed postcard DOM ops.
```
@@ -338,6 +338,6 @@ But:
```text
Rust owns types.
hemplate owns structure.
slhx owns interaction.
hemx owns interaction.
browser executes ops.
```