Capture the direction change from M3-b: UI screens are authored editor-first as .tscn node trees (previewable/arrangeable in the Godot editor), scripts do on-load work only — never build the tree in _ready(). Add ADR 0001, a "UI scene conventions" how-to in client/docs, a §16 mandate in the charter, and mark M3-a/M3-b done in the roadmap. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
75 lines
4.3 KiB
Markdown
75 lines
4.3 KiB
Markdown
# /client/docs
|
||
|
||
Client-specific documentation. Scoped to the Godot project.
|
||
|
||
Put here:
|
||
|
||
- Scene tree / autoload structure
|
||
- Input model — menu vs free-text boundary, which must be visible (§15)
|
||
- Combat wiring — deterministic damage, per-encounter seeding (§10)
|
||
- Canon log data structure and fact-harvesting (§11)
|
||
- HTTP client + fallback-degradation handling (§13)
|
||
|
||
Cross-cutting design (anything touching both client and api) goes in the root [`/docs`](../../docs), not here.
|
||
|
||
## UI scene conventions (shell + all screens)
|
||
|
||
Screens are **authored editor-first** — the layout lives in the `.tscn`, not in
|
||
code. See ADR [0001](../../docs/adr/0001-editor-first-ui-scenes.md) for the why;
|
||
this is the how. Reference: `scenes/shell/{MainWindowShell,SystemDock,NarrationBook}.tscn`
|
||
+ `scripts/ui/shell/*.gd`.
|
||
|
||
- **The `.tscn` owns the tree.** Author every node in the editor: containers,
|
||
panels, labels, buttons, art-slot placeholders. Assign the shared theme on the
|
||
scene root (`theme = game_theme.tres`) and set each node's `theme_type_variation`
|
||
(from `ThemeKeys`) in the editor, so the scene previews themed. Instance
|
||
sub-screens (`PackedScene` ext_resource) rather than rebuilding them.
|
||
- **The script does on-load work only.** `@onready var x := $Path/To/Node` refs,
|
||
signal/service wiring, animations, and binding state into the authored nodes
|
||
(label text, colours from `Palette`). Do **not** create nodes in `_ready()`.
|
||
- **Data-driven repeats = fixed authored slots, bound at runtime.** Author enough
|
||
turn-order tokens / consumable slots / etc. in the editor; the script fills the
|
||
first N from state and hides the rest. Previewable now; systems write them later.
|
||
- **Scene-root sizing.** Give the root a fixed size via offsets (e.g. 1920×1080)
|
||
for editor preview, but **no full-rect anchors on the root** — a root Control run
|
||
via F6 collapses to (0,0) under full-rect anchors. Keep `size = get_viewport_rect().size`
|
||
on `_ready` + a `size_changed` connection (the M3-a collapse lesson).
|
||
- **Button styling → Button-base variations only.** Never put a PanelContainer
|
||
variation (`DarkPanel`, `ParchmentCard`) on a `Button`; it has no `normal/hover/
|
||
pressed` styleboxes so the button renders unskinned. Use the Button-base
|
||
variations (`PrimaryCTA`, `Tab`, `TabActive`, `Chip`, `DockButton`, `ParchmentButton`).
|
||
- **`RichTextLabel` prose** reads its dark serif ink from the theme's base
|
||
`RichTextLabel` style (it does *not* inherit `default_font`/`default_color`).
|
||
Just add the `RichTextLabel` — don't override its colour per-node.
|
||
- **Theme is generated.** All colours live in `palette.gd`; `game_theme.tres` is
|
||
built by `scripts/theme/build_game_theme.gd` (source of truth). To add/adjust a
|
||
variation, edit the builder + `theme_keys.gd`, then regenerate:
|
||
`godot --headless -s res://scripts/theme/build_game_theme.gd`. Never hand-edit
|
||
the `.tres` or put a hex literal in a script or `.tscn`.
|
||
- **Wiring the DM loop / injectable services.** Follow the shell: a `service`
|
||
var settable *between* `instantiate()` and `add_child()` lets tests inject a
|
||
`FakeTransport`-backed `DmService` so on-load calls stay hermetic; `_ready`
|
||
builds the real `HttpTransport`-backed one when none was injected.
|
||
- **Visual gate.** Headless GUT proves structure (nodes mount, sizes non-zero,
|
||
bindings run); it cannot prove rendering. Every screen needs a **human F6
|
||
eyeball** before it's done. Note shader surfaces (`DarkBay`, vignette) render
|
||
their texture only on an F6 run, not in the editor.
|
||
|
||
## Narrate live smoke (M2 client HTTP loop)
|
||
|
||
The `narrate_harness` scene is a throwaway proof of the client → `/dm/narrate`
|
||
loop. It is not the game UI.
|
||
|
||
**Prereqs:** the proxy running and Ollama reachable.
|
||
|
||
1. Start the proxy: from `api/`, `uvicorn app.main:app --port 8000` (with the
|
||
`.env` from `.env.example`; Ollama up at `OLLAMA_BASE_URL`).
|
||
2. In the Godot editor, open `res://scenes/narrate_harness.tscn` and run it (F6).
|
||
3. Click **Narrate this scene**. Expect grounded prose in the Narrator's voice
|
||
(referencing Greywater / Brannoc / the washed-out bridge), tags stripped, and
|
||
`facts harvested: N` with N ≥ 0.
|
||
4. **Degraded path:** stop the proxy and click again. Expect the authored
|
||
fallback line and `(degraded)` in the status — never an error.
|
||
|
||
Override the proxy URL with the `coc_rpg/proxy_base_url` project setting.
|