Files
hemx/README.md
T
slhx agent b3d72e8d82 feat(examples): add hemx html pattern gallery
Add examples/html_examples as a copy-pasteable gallery for the first htmx-style HTML UX patterns using exact htmx URL slugs in the page and README. The slice covers click-to-edit, edit-row, delete-row, inline-validation, click-to-load, and active-search with .heml templates, generated resources, server-owned Rust state, and runtime-free selector targeting.

req: htmx_equivalents/001

req: htmx_equivalents/002

req: examples/001
2026-06-23 07:22:40 +02:00

192 lines
11 KiB
Markdown

# hemx
hemx is checked hypermedia for Rust: write hemplate templates, write typed Rust
handlers, and return generated UI commands. The browser receives checked UI
commands; ordinary server-first apps do not need a frontend framework,
handwritten UI JavaScript, selector targeting, or raw runtime primitives. req: pitch/001 req: canonical_authoring/001
Status: the repository currently has a strong v0 example/API path, a compile-tested
SaaS tutorial app, an advanced Kanban milestone sketch, and a full techdemo.
The v1 story now has tutorial, recipe, diagnostics, and stability docs; see
`docs/v1-readiness.md` for the remaining close-gap audit.
Template authoring: `.heml` is HTML plus a small hemplate overlay for escaped
text, trusted HTML, dynamic attributes, Rust-shaped control directives, generated
slots/forms/handles, and keyed partial targets. See `docs/hemplate-syntax.md`.
Editor setup for VS Code, Cursor, and Neovim lives in `docs/editor-support.md`;
VS Code/Cursor share the repo extension in `editors/vscode-hemx`, while all
editors keep normal HTML/tree-sitter highlighting and layer `hemx-build`
diagnostics on top.
Local checkout note: until the hemplate crates are published, this repository
expects `hemplate` checked out next to `hemx` as `../hemplate/hemplate`. The app
scaffolder fails with that exact path if the prerequisite is missing, instead of
creating an app that fails later with a vague Cargo path-dependency error.
## The normal path
For beginner and production-shaped app code, stay on this path. req: public_api/001 req: public_api/005
1. **Templates declare the surface.** `.heml` files declare roots, slots,
forms, handles, keys, page targets, optional pending states, and explicit
leaf islands with `data-hemx-*` attributes. The stable syntax surface lives in
`docs/hemplate-syntax.md`.
2. **Build generates typed helpers.** `hemx_build::app().run()` consumes the
hemplate Surface and emits generated Rust helpers for slots, forms, handles,
page targets, classes, and events. req: ceremony/003 req: build/001
3. **Handlers are plain Rust.** App code uses `#[hemx::handler]` functions with
ordinary domain types, framework extractors, generated `Form<T>` inputs, and
`impl IntoEffect` or `Result<impl IntoEffect, E>` returns. req: derive_handler/004
4. **Handlers return generated commands.** Common handlers return helpers such
as `todos.append(todo)`, `todo_row.replace(row)`, `new_todo.error("title",
"Required")`, `new_todo.clear()`, `page.replace(view)`, or tuples of those
commands. req: dx/006 req: dx/007
5. **The runtime only applies effects.** The JavaScript runtime is a small,
root-scoped effect interpreter: no VDOM, no hydration framework, no client
expression engine, no selector retargeting, and no app state store. req: runtime/003 req: invariant/002
## Mental model: render → slot/key → effect → runtime
- **Render:** hemplate renders Rust view structs into checked HTML. App code
normally reaches rendering through generated `ui::page(...)` helpers at a
server page boundary, not raw HTML construction. req: html_safety/002 req: view/001
- **Slot/key:** a generated slot names the target, and a generated keyed slot
also carries the stable row key. A list target inside `h-for` must have a
stable `h-key`, so row updates are addressable without CSS selectors. req: list/001
- **Effect:** handlers return typed commands that become a checked effect
response. Tuple composition is the normal fixed batch syntax; arrays and
`Vec<T: IntoEffect>` cover fixed or dynamic repeated partial updates.
- **Reuse:** the hemx answer to framework components is reusable hemplate
partials plus generated helpers, app-owned state, `IntoEffect` composition,
and explicit leaf islands when browser-owned behavior is necessary. See
`docs/recipes/reusable-partials.md`.
- **Runtime:** the browser checks the build fingerprint, resolves targets within
the current `data-hemx-root`, and applies compatible batches. Mismatched
server/runtime builds fail closed instead of silently mutating the wrong DOM.
req: abi/002 req: check/004 req: failure/005
## Forms and errors
Forms remain HTML forms. hemx checks the generated form contract against a
user-authored Rust form type, so domain newtypes such as `Email`, `TodoId`, and
`Title` parse through ordinary Rust traits rather than generated DTOs. req: form/001
Use validation effects for expected user mistakes, and `Result<impl IntoEffect,
E>` for fallible domain, database, or infrastructure work. Integration crates map
`E` to generated UI effects, redirects, events, or HTTP responses; the canonical
app code still uses the same handler shape for plain and fallible handlers. See
`docs/diagnostics.md` for common compile/build/runtime mistakes and fixes.
req: failure/004 req: derive_handler/004
## Pages, push, CSS, and islands
- Page navigation is a specialized generated page/slot effect around real
anchors and ordinary HTTP routes. hemx does not own routing. req: modes/002 req: axum_integration/002
- Server push streams send checked effect responses over framework-managed
transports such as SSE; auth and connection policy stay in the server
integration. req: wire/004 req: push/002
- Appearance is plain CSS. Generated class tokens can make dynamic classes
checked, but hemx does not introduce a styling runtime.
- Custom JavaScript belongs at explicit opaque leaf boundaries: charts, maps,
editors, Web Components, or similar widgets. Islands communicate through
generated handles/events and do not create a second UI model. req: canonical_authoring/007 req: interop/003
## Production boundary
hemx is not a SaaS platform. Production concerns stay in normal Rust/web crates
and integrate at explicit boundaries. req: laws/002 req: auth/001
- **Persistence:** use SQLx or another storage adapter in your application
state/handlers. hemx should see ordinary domain values and generated UI
commands, not own the database layer. See `docs/recipes/sqlx-persistence.md`.
- **Auth/session:** use Axum/Tower extractors and middleware. Handlers may accept
typed auth/session context and return ordinary HTTP failures or generated UI
failures. See `docs/recipes/auth-session-csrf.md`. req: auth/002
- **CSRF:** keep CSRF policy in middleware/extractors with hidden form fields,
cookies, and normal SameSite/browser semantics. hemx preserves submitted form
fields and credentials semantics. See `docs/recipes/auth-session-csrf.md`. req: auth/004 req: auth/005
- **Observability, feature flags, killswitches, deploy:** use explicit platform
integrations around handlers, routes, runtime assets, and mobile shells. Core
hemx must not vendor providers or add framework-specific magic. See
`docs/recipes/observability-flags.md`, `docs/recipes/deploy-versioning.md`,
and `docs/recipes/mobile-release.md`. req: examples/006
- **Mobile starter:** create the phone-first path with `cargo run -p
hemx-xtask -- app new --mobile PATH`. The starter carries a real app flow,
typed host capabilities, command/event/projection recovery truth, and
inspectable mobile release-kit commands without adding a native UI framework.
Use it for Rust-owned hypermedia apps; use explicit native shells/islands for
heavy native UI, games, camera-heavy flows, deep OS integration, or complex
offline sync. req: ceremony/006 req: host/002
- **PWA/offline/sync:** optional adapters may reuse generated targets/effects,
but core hemx must not gain a mandatory client state graph or local app
runtime. Local truth is commands/events/projections, not stored DOM patches or
stored `EffectBatch` payloads. See `docs/recipes/pwa-offline.md` and
`docs/recipes/local-command-log.md`. req: canonical_authoring/008 req: local/001 req: local/002
- **Host capabilities:** browser, PWA, WebView, and native-shell capabilities use
`hemx-host` manifests/calls/events. Adapters return host facts to app code;
UI still changes through normal hemx effects. See
`docs/recipes/host-capabilities.md`. req: host/001 req: host/002 req: host/005
## Escape hatches
Advanced APIs are named and isolated. Raw effects, low-level ids, manual
registries, raw HTML/render/target construction, runtime hooks, SSE internals,
and island internals are for integration crates, tests, migrations, or explicit
leaf boundaries. They should not appear in beginner examples or ordinary handler
docs. req: public_api/002 req: public_api/005
## Versioning and deploy compatibility
The generated API, symbols, effect wire schema, and JavaScript runtime carry
schema/ABI versions. Deploy a matching server, generated output, and runtime
asset together. Build fingerprints are derived from the generated surface and ABI
parts; the runtime refuses incompatible effect responses and integrations should
fall back to a full page reload when possible. See `docs/recipes/deploy-versioning.md`. req: abi/001 req: abi/002 req: failure/005
Before a v1 release, the semver policy and upgrade notes should explicitly state
which surfaces are stable: beginner generated helpers and handler shapes; the
wire/runtime ABI; and advanced escape hatches that may remain integration-level.
See `docs/versioning.md`.
## Examples
- `examples/v0`: canonical beginner path covering counter, typed todo CRUD,
form wizard, auth action, page swaps, SSE notifications, and keyed list
updates. Create the generic starter with `cargo run -p hemx-xtask -- app new
PATH`; it includes a page, form, keyed row partial, notice slot, handlers,
tests, and generated append/replace/remove/dynamic-batch updates. Start here.
req: ceremony/005
- `examples/html_examples`: copy-paste HTML pattern gallery for htmx-style
CRUD/form/search/load UX patterns. It proves the hemx idiom for click-to-edit,
edit row, delete row, inline validation, click-to-load, and active search with
`.heml`, generated resources, server-owned Rust state, and tiny runtime
behavior. req: htmx_equivalents/001 req: htmx_equivalents/002 req: examples/001
- `examples/saas`: compile-tested v1 tutorial app covering auth/session,
CSRF-safe mutation, local persistence, generated swaps, page/push shape, plain
CSS, and one explicit island without provider-heavy platform scope. Read the
walkthrough in `docs/tutorial-saas.md`; the SQLx persistence recipe in
`docs/recipes/sqlx-persistence.md` shows the provider boundary without moving
SQL into core.
- `examples/workout`: phone-first local-first product exemplar. Create a starter
with `cargo run -p hemx-xtask -- workout new PATH`; run the exemplar with
`cargo run -p hemx-xtask -- workout dev` and open `http://127.0.0.1:3028`.
It keeps workout truth as commands/events/projections and routes export
through the host capability boundary; `examples/workout/README.md` documents
the canonical dev, test, production build, mobile release, verification, and
failure-mode paths. req: examples/001 req: examples/006 req: local/001 req: host/005
- `examples/kanban`: advanced / north-star milestone boundary sketch. It may
expose manual registry or render escape hatches while exploring product limits.
- `examples/techdemo`: advanced integration demo with a leaf island and broader
product interactions.
## Local checks
```sh
cargo run -p hemx-xtask -- test
cargo check --workspace
redgate health --strict
```
Use `cargo run -p hemx-xtask -- test` for the full local verification path so
jobs stay capped for local CPU and memory. req: test/004