# Web client — cbd-web `cbd-web` is a browser client with the same functionality as the [terminal UI](./tui.md): library browsing, search terms, queue manipulation, playback control, saves (`w`), captures (`W`, with progress), and the live update stream. It is a Leptos application compiled to WebAssembly and rendered client-side in the browser — no terminal required, so phones, tablets, and guests can drive the server. Every TUI binding has a clickable equivalent, and the familiar keyboard bindings (`j`/`k`/`h`/`l`, `Tab`, `%`, `e`, `d`, `w`, `W`, playback and queue keys, `?` for help) also work on desktop browsers. That includes marks (`s`), visual mode (`v`/`V`) in both panes, and the register — `y` yanks, `d`/`c`/`C` fill it as they remove, and `p`/`P` paste it after or before the cursor (see [the TUI's register section](./tui.md#the-register-y-d-and-pp), which behaves identically here; the queue toolbar shows how many entries are waiting). Seek is there too — `,` and `.` move 15 seconds back and forward (`<` and `>` skip a whole track), as do the `⏪`/`⏩` buttons in the transport bar, and the progress bar is clickable — a click seeks to that point in the track. The `/` live filter is TUI-only for now. A pair of `library` / `queue` tabs in the top bar switches panes and shows which one the keys go to — the same thing `Tab` does, reachable without a keyboard. It matters on a phone: below 700px the two panes cannot sit side by side, so the unfocused one collapses to a title strip (tapping it also switches) and the tabs are how you get back. The volume slider spans the server's whole accepted range, ending at 110% — the server clamps there, so a slider that went further would have a dead stretch at its right end. Its tooltip reports the current level as a percentage; the TUI shows the same figure in its now-playing pane. ## How it is served The browser talks **gRPC-web** to the same service the TUI uses. There is no second API surface: the server wraps its existing gRPC service in a gRPC-web layer, so the browser calls the same `/crabidy.v1.CrabidyService/…` paths, including the streaming `GetUpdateStream`, from the same generated clients. ```d2 direction: right browser: Browser { app: cbd-web (Leptos CSR wasm) } server: "crabidy-server :50051" { static: embedded cbd-web bundle grpcweb: gRPC-web layer rpc: CrabidyService grpcweb -> rpc } browser.app -> server.static: GET / and assets browser.app -> server.grpcweb: gRPC-web (fetch) ``` Everything is served on the server's own address — gRPC for the TUI, gRPC-web for the browser, and the static app assets all share one port. Opening `http://:50051/` in a browser is the whole install story; there is no separate host to run. ## Embedded in the server `cbd-web` is not run directly. It is built to a WASM bundle and embedded into `crabidy-server` behind the `web-ui` cargo feature, which is **on by default** (and thus present in `cbd` too). A plain `cargo build` needs no WASM toolchain: until you build the bundle, the server embeds a "web UI not built" placeholder page. Building the bundle re-embeds it automatically on the next server build. ```sh devenv shell -- build-web # writes the cbd-web bundle cargo build -p crabidy-server # embeds it ``` Building the server with `--no-default-features` drops the feature for a headless, gRPC-only binary. ## Auth and theme The static app shell is public; every RPC behind it stays gated. When the server has [auth](../auth.md) configured, the client shows a login form on the first unauthorized RPC and sends the role name and password as basic-auth credentials — the same `[auth]` roles the TUI honors, enforced identically. Credentials and the theme choice are kept in the browser. The interface offers a light and a dark theme with a manual toggle, following the browser's preference by default.