Two bugs from the register work, both reported from actual use. **Paste landed one row too far.** `QueueManager::insert_tracks` spliced at `position + 1` — it inserted *after* the given index, while its own CLI help says "insert tracks/subtrees at a position". So `p` (which sent cursor + 1) landed two below the cursor and `P` (cursor) landed one below, exactly as reported. The clients were already computing the right indices for an insert-*at* API. Fixed at the primitive rather than in the clients, because "after" cannot express the front of the queue: the earliest reachable index was 1, so pasting before the first row — and therefore undoing a delete of it — was impossible. `insert_tracks` now inserts **at** `position`, pushing that row down, with 0 the front and past-the-end an append. The two callers that genuinely mean "after" pass `N + 1`: `ResolveKind::InsertAfter` (which keeps its name and its streaming-chunk arithmetic) and `queue_tracks` (play-next, `L`). All existing behaviour is preserved — the whole server suite passes untouched — and three tests pin the new front/interior/play-next cases. `queue insert <POS>` on the CLI shifts by one accordingly, which brings it in line with what its help always claimed. Documented in the proto, the CLI help, and the book. **Queue marks were invisible.** The TUI rendered no mark indicator, so `s` and visual mode had no feedback. Marked rows now carry the library's `*` prefix and the same green bold; the playing row keeps `>` and its red, and a row that is both shows `> * title`. The web client already rendered marks (its `.marked .title` rule), but neither client showed visual mode outside the TUI's pane title — both panes there now get a VISUAL badge in the toolbar. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| src | ||
| .gitignore | ||
| Cargo.toml | ||
| README.md | ||
| Trunk.toml | ||
| index.html | ||
| style.css | ||
README.md
cbd-web — the browser client
A Leptos client-side WASM app with the same
functionality as cbd-tui, served by crabidy-server itself.
How it works
- Transport: gRPC-web (
tonic-web-wasm-client) over the same generated client and proto types the TUI uses (crabidy-core). No second API surface — feature parity is structural. The server wraps its existing gRPC service intonic-web, so the browser and the TUI hit identical/crabidy.v1.CrabidyService/…paths, and the same role auth layer gates both. - Serving: the built bundle (
cbd-web/dist) is embedded intocrabidy-serverat compile time behind the default-onweb-uifeature and served as the fallback route on port 50051. gRPC and static assets share one origin, so there is no CORS story. - Local-first: pure client-side rendering, every asset in the
bundle (no CDN, no external fonts), library listings cached in memory
like the TUI, credentials and theme in
localStorage, and the update stream reconnects with backoff when the server disappears. There is no CRDT layer — this is a remote control for one live server state, not an offline-editing app (a deliberate departure from theweb_client_example_workspacetemplate that informed the toolchain).
Functionality
Everything the TUI does: browse the library (j/k/h/l, click),
marks, create/rename/delete nodes (%/e/d), bookmark and capture
(w/W, with live progress lines and skipped-track marking), the full queue
and playback controls, volume, shuffle/repeat, and a ? help overlay listing
the keys. Keys mirror the TUI; every key also has a clickable control. A
light/dark theme follows the OS and can be toggled (persisted). The accent
color is the crab orange-red.
When the server requires credentials, a login form collects the role
(owner / queue-owner / queue-appender) and password; they are
stored in localStorage and sent as the gRPC-web authorization
header on every request.
Building
The WASM toolchain (trunk, wasm-bindgen, the wasm32-unknown-unknown
target) is provided by devenv. From the repo root:
devenv shell -- build-web # release bundle → cbd-web/dist
cargo build -p crabidy-server # embeds cbd-web/dist
build-web clears RUSTFLAGS first: the native toolchain sets the
mold linker, which rust-lld (the wasm linker) cannot parse.
Building crabidy-server without a cbd-web/dist present is fine — it
embeds a placeholder page telling you to run build-web. To drop the web
client (and the tonic-web layer) entirely, build the server without its
web-ui cargo feature — e.g. --no-default-features --features all-providers,opus,spectrum; see docs/src/build-features.md.
Dev loop
Run a server, then a live-reloading trunk server that proxies gRPC-web to it:
cargo run -p crabidy-server # or `cbd`
devenv shell -- serve-web # trunk serve on http://127.0.0.1:8080
Trunk.toml proxies /crabidy.v1.CrabidyService to 127.0.0.1:50051,
so the app behaves as if served from the server.
Tests
The DOM-free logic (pane/selection state machines, the keymap, capture
progress formatting) lives in src/state.rs and src/keymap.rs and is
unit-tested on the native target:
cargo test -p cbd-web
Components in src/app.rs stay thin over that logic. The server-side
serving and the gRPC-web + auth routing are tested in crabidy-server
(src/web.rs, tests/web_server.rs).