# star
A **native Lightspeed** LiveView app on [Mist](https://hexdocs.pm/mist) (port **8080**).
Server-owned state, typed events, verified routes, and the Lightspeed wire protocol — no Datastar.
## What you get
| Piece | Role |
|-------|------|
| `star/live.gleam` | `stateful.LifecycleComponent` — model, msgs, routes, render |
| `star/ws.gleam` | Live session host — protocol Event → handle → Diff patch |
| `star.gleam` | Mist HTTP + `/live` WebSocket + `endpoint` |
| `/assets/lightspeed.js` | Minimal browser runtime (`data-ls-click` → Event frames) |
### UI (all server-driven)
- Counter (`inc` / `dec` / `reset`)
- Toggle panel
- Greeting form
- Notes (save / delete)
## Run
```sh
# enter the flake dev shell, then run
nix develop
gleam run
# or one-shot from the repo root
nix run
```
Open **http://localhost:8080/**
## Routes
| Path | What |
|------|------|
| `GET /` | LiveView shell (`data-ls-*` + initial HTML) |
| `WS /live` | Lightspeed protocol (`hello` / `event` / `diff` / `ack`) |
| `GET /health` | Controller route (`ok`) |
| `GET /assets/app.css` | Styles |
| `GET /assets/lightspeed.js` | Client runtime |
## Idioms used
```gleam
// verified route + live controller
endpoint.new(contract.allow_all("owner-1"), "/live")
|> endpoint.get_live(index, "star_view", fn(_conn) { initial_body() })
// stateful component (mount / handle / render / routes)
stateful.LifecycleComponent(
mount: mount,
update: update_assigns,
handle: handle,
render: render,
routes: routes(),
)
// typed event decoding
stateful.route("greet", fn(e) {
event.decode_form(e, "greet", fn(data) { ... })
})
```
Client hooks are LiveView-style attributes:
```html
```
## Development
```sh
gleam test
```