# GUI plan Decisions and rationale live in `docs/adr/0001-gui-frontend-architecture.md` and `0002-version-lockstep.md`. This file is the order of work. ## Non-goals Audio, 3D, macOS, auto-update, a browser client, and pixel-golden tests in CI. ## Phase 0 — version handshake (v4.0.0) Breaking wire change, released for client and server together. - `HELLO ` is the first line after TLS. The server answers `HELLO latest= url=` and rejects every other message with `ERROR HELLO required` until it has seen one. - MAJOR.MINOR must match; PATCH may differ. A mismatch is refused before any credentials are sent. - The client measures its connect latency with the `HELLO` round trip and prints a notice when the server advertises a newer version. - `DOWNLOAD_URL` is read from the environment by the server, with the public site as the fallback. **Gate:** mismatch refused in both directions, `HELLO` required before auth, existing game flows unchanged, new tests for version parsing and gating. ## Phase 1 — core boundary and roulette (done) Complete in v4.0.7. Every game plus `Lobby` and `History` are structured views; the core builds the view and frontends send `Command`. The client transport (TCP, rustls, TOFU, `HELLO`, auto-login) lives in `client::transport` behind a trust callback. The command line moved out of the core in two pieces: `client::input` owns the editor, history and key mapping, and `client::shell` parses a finished line into a core `Command` or an adapter action (connect, login, register, set-credential, host maintenance). `ClientCore` no longer holds a `Key`, a prompt buffer, or any of the old TUI-shaped effects (`Interactive`, `SetPrompt`, `ListHosts`, `ForgetHost`, `AnimateRoulette`); the roulette wheel animation is derived from the `Result` view by the adapter. "Log out" (`logout`) and "forget saved login" (`forget-login`) are distinct: the latter drops the credential for the pinned key and keeps trust. The `Connect` and `Trust` screens are adapter-owned for now: the terminal asks in the transcript, and the GUI will draw them as its own screens over the same `client::transport` callback. They are not core state because the core never sees a certificate. - Introduce `Command` and `View`; move terminal-only concerns (prompt editing, wheel animation, interactive prompts) into the TUI adapter. - App screens: `Connect`, `Trust`, `TrustMismatch`, `Login`, `Lobby`, `History`, `Game`. - Port roulette first: `PlaceBet(BetTarget)`, `SetChip`, `ClearBets`, `Spin`, and a `FeltView` carrying chips, cursor, and the last result. - Add `forget-login` so "log out" and "forget saved login" are distinct in both frontends. - Auto-login stays: a stored credential for the pinned fingerprint logs in automatically after the trust check. **Gate:** met. The TUI behaves as before (same boards, keys and prompts), its tests drive the real adapter path (`shell::parse`, `Editor`, `game_input`), and `ClientCore` contains no terminal types. ## Phase 2 — the other three games Slots, blackjack, crash ports with their own `GameView` variants and commands. Server push (`CRASH_BUSTED`) feeds the same `View`. The TUI keeps animating the crash board from its local clock. **Gate:** all four games playable in the TUI through `Command`/`View`. ## Phase 3 — `gui/` skeleton and the style spike (done) - `gui/` is a workspace member (`zagan-bet-gui`). It depends on the core with `default-features = false`, so neither egui nor native-tls enters the TUI or server build graph. The build uses the workspace's `gui-release` profile (opt-level `z`, LTO, stripped). - `gui/src/theme.rs` holds the tokens: the terminal-derived dark palette, felt green kept for the table, a 4/8/12/16/24 spacing scale, radii, and the type scale. Two embedded fonts: Open Sans (OFL) for text, Hack (MIT) for numbers, chips and labels. Licences sit next to the files in `gui/assets/fonts/`. - `gui/src/roulette.rs` draws the felt, chips and wheel from a `RouletteView`; it owns the geometry, not the rules. The felt is pointer-first: the hover snaps to the fine lattice, so the inner half of a cell is a straight, an edge is a split and a crossing is a corner. Left click adds the selected chip, right or middle click takes one back, and a translucent chip previews the target under the pointer. - `gui/src/app.rs` composes the persistent header (connection, account, balance) with the table and the betting sidebar. The demo state animates a 2.4 s wheel spin, highlights the winning pocket and cell, and reports the amount paid and the net for the round. SPIN and CLEAR sit side by side so clearing is never below the fold. - `gui/tests/layout.rs` covers the pure maths (money grouping, wheel angle, table aspect, bet names). UI wiring tests arrive with `egui_kittest` in Phase 4. Measured on Linux at 1080p with llvmpipe software GL: | Check | Result | | --- | --- | | Linux binary | 6.0 MB | | Windows x86_64 binary | 4.7 MB | | Cold start to window | 52 ms | | Idle RSS | 137 MB | | Idle CPU | 0% | The Phase 4 screenshots in `docs/screenshots/` supersede the spike captures; they show the same layout with the real session and the pinned action bars. **Gate:** met. The style was approved and the spike grew into Phase 4. ## Phase 4 — GUI wiring (code complete, v4.2.0) - `gui/src/net.rs` is the adapter's network worker. One blocking thread owns the TLS stream; `Command` goes in, `Event` comes out (connected, failed, trust, line, closed), and every event calls `Context::request_repaint`. There is no async runtime. - First contact is a modal that answers the transport's trust callback through a channel. A changed key is a hard stop with an explicit forget-and-retry; a version mismatch links to the download page; a saved credential auto-logs in before the login screen is ever shown. - Screens: connect (remembered hosts, forget trust), login/register with a "remember this login" choice, lobby with the update notice, history table, and the four games. "Log out" and "forget saved login" are separate. Leaving a live hand or round asks first and forfeits the reservation. - Games are painted from `View`: slots with a 0.9 s reel animation and payline highlights, roulette with the wheel spin, the hover lattice, chip placement and payouts, blackjack with vector-drawn suits and legal actions, crash with a sampled curve, cashout and a server-drawn crash point. A refused spin shows the error and never runs the animation: the reels and the wheel wait for the server's answer, not for the click. - Sidebars pin their action block below the content, so SPIN, CLEAR, DEAL and START never move as the bet slip or a hand grows. The embedded fonts have no suit or arrow glyphs, so suits are shapes and navigation uses plain words. - The lobby and the game screens speak plain language: one how-to sentence per game, no RTP, table limits, deck rules or server-timing jargon. The account and balance are fetched as soon as a session signs in. - The header balance is held at the staked amount while the reels or wheel turn, then jumps to the settled value: the GUI never reveals a payout before the animation does. Refused spins cancel the animation and show the error. The crash multiplier keeps full precision in the game state so the chart is a smooth line; the terminal still formats it to two decimals. - `gui/tests/layout.rs` covers money, wheel angles, lattice snapping, table aspect and bet names. Verified live against a v4.2.0 server: register, login, auto-login, trust, all four games settling, history, logout, disconnect, the leave-forfeit confirmation, and the version-mismatch screen (new client against a 4.1.1 server). Screenshots: `docs/screenshots/gui-{lobby,slots,roulette,blackjack,crash, history}-1080p.png`, plus the roulette result state. Measured on Linux at 1080p with llvmpipe software GL: | Check | Result | | --- | --- | | Linux binary | 8.3 MB | | Windows x86_64 binary | 6.8 MB | | Cold start to window | 72 ms | | Idle RSS | 130 MB | | Idle CPU | 0% | **Gate:** the screenshot set is ready for review. Remaining for the gate: frame rate on integrated graphics and the Windows interactive pass. ## Phase 5 — packaging (code complete, v4.3.0) - `publish.sh` builds the GUI with the `gui-release` profile for Linux and Windows and names the app `zagan-bet-gui---[.exe]`. - It renders a bash installer and a PowerShell installer next to each app. Both embed the app's SHA-256, verify the file (or download it), install per-user and add a launcher: `.desktop` plus an SVG icon on Linux, a Start-menu shortcut on Windows. Templates live in `scripts/installers/`; `render-installer.py` fills them in. - The download page scans `bin/gui`, `bin/client` and `bin/server`, lists apps, installers, clients and servers, and includes every file in the `sha256sum` block. A Source section links the public git repository () for the GPL corresponding source. `deploy.sh` verifies every artifact kind over the public URL. - `publish.sh` refuses to run from a dirty tree so the linked history matches the binaries. **Gate:** deploy verification passes for apps, installers, clients and servers; the Linux installer is exercised in a clean HOME. The Windows installer needs a Windows machine before the gate closes. ## Testing split ## Testing split - `View`/`Command` tests in the core: flows, gating, state. Always. - The TUI adapter remains the regression net through Phases 1–2. - `egui_kittest` for wiring that `View` cannot see; no pixel goldens in CI. - Manual for feel, animations, and layout at different window sizes.