# 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 only the focused one is rendered and the tabs are the way between them. 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.