Feature Map
Canonical index of every screen, panel, overlay & sub-view · feature_map() in the dev console
feature-map.md; the styling source of truth is style-guide.md + /style-guide/.
00 · Overview
Top-level routing happens in src/App.tsx and is driven by state.screen
(one of 'title' | 'map' | 'docked' | 'denied' | 'transit' | 'gameover', defined in src/state/types.ts).
Persistent chrome stacks on every non-title screen; overlays and modals stack on top.
Quick reference
| Resource | Path |
|---|---|
| Top-level routing | src/App.tsx |
| Game state shape | src/state/types.ts |
| Reducer | src/state/reducer.ts |
| Static game data | src/data/ |
| Pure game logic | src/engine/ |
| Component CSS | co-located: src/components/Foo.tsx + Foo.css |
| Style spec | style-guide.md, style-guide/index.html (/style-guide/) |
01 · Window Taxonomy
Every visible UI surface, grouped by its window class. One row per pane — tabs inside a tabbed window are listed individually, not folded into the parent.
| Class | Definition |
|---|---|
| Screen | Full-canvas window. Mutually exclusive with other screens. Driven by state.screen. |
| Pane | One tab inside a tabbed screen. |
| Sub-surface | Internal region of a screen with its own chrome (rails, strips, rosters). |
| Overlay | Floats above the active screen, persists across screens, dismissed by the user. |
| Modal | Floats above, blocks input, decision-driven dismissal. |
| Persistent chrome | Always rendered, not a window. |
1a · Screens
| Pane | File | state.screen | Game systems housed |
|---|---|---|---|
TitleScreen | src/components/TitleScreen.tsx | title | Main menu — new game, load, options. Bypasses TopBar / overlays. |
StarMap (map mode) | src/components/StarMap.tsx | map | Travel / pathfinding, scan / survey, plot routing. Sub-surfaces in §1c. |
StarMap (transit mode) | src/components/StarMap.tsx | transit | Same SVG canvas as map, but the TransitConsole bottom strip replaces FlightPlanStrip. Ship sprite animates the edge. |
DockPanel | src/components/DockPanel.tsx | docked | Two-column docked view — wraps StationPanel (left) + ShipPanel (right). Tabs in §1b. |
DeniedPanel | src/components/DeniedPanel.tsx | denied | Hostile / story-locked station rejection. Faction system surfaces error here. |
GameOverPanel | src/components/GameOverPanel.tsx | gameover | End-of-run summary. |
state.screen === 'transit' is served by StarMap in transit mode — the live transit UI is the TransitConsole bottom HUD (a StarMap sub-surface, §1c), not a separate component. The old TransitScreen.tsx was dead / orphaned code and has been deleted; don't recreate it.
1b · Panes (tabs)
StationPanel — left column of DockPanel
File: src/components/StationPanel.tsx. A .station-hero header (station art + faction emblem + standing bar) sits above a two-level tab nav: four primary groups (Trade / Services / People / Info) and, when a group has more than one tab, a secondary .station-subtabs row. Each tab's visibility depends on station.shipyard, station.outfitting, etc.
| Group | Pane | Tab key | Game systems housed |
|---|---|---|---|
| Trade | StationPanel.market | market | Buy / sell goods. Row click opens GoodInfoModal. |
| Trade | StationPanel.jobs | jobs | Contract board — accept/deliver generated, time-limited delivery contracts (outward = frontier, homeward = premium runs to the start region). Cargo-consuming hand-in; deadline countdowns; "days since home" + Agri-Guild standing-decay readout. Owned by contracts.ts. |
| Services | StationPanel.shipyard | shipyard | Dock services + browse / buy ships (tab labelled Dock). |
| Services | StationPanel.loadout | loadout | Loadout — installed modules and the station locker as plain lists (slot badge + name + stats + Stow / Sell / Install). Single home for inspecting & swapping the current fit; no ship-image overlay. |
| Services | StationPanel.outfitting | outfitting | Outfitting — buy new modules from the station catalogue (install / trade-in). |
| Services | StationPanel.gather | gather | Mining tab (shown only when station.gatherables is non-empty) — single rig-driven MINE button per deposit; yield and ore-tier access scale with the installed mining module. |
| People | StationPanel.comms | comms | Mission board — accept / hand-in story steps. Faction rep gating. The Log (L) tracks all in-progress missions. |
| People | StationPanel.crew | crew | Hire / dismiss crew. |
| Info | StationPanel.info | info | Station details, description, environment (atmosphere / temp / gravity). Faction identity + standing live in the hero header. |
beacon / derelict / anomaly categories skip the normal tab strip entirely and render a single lore view (.lore-only-view) — history blurb + tag pills — instead.
ShipPanel — right column of DockPanel
File: src/components/ShipPanel.tsx. A fixed-width sidebar (flex: 0 0 clamp(380px, 36%, 480px)) with no tabs — an always-on summary that stacks the readouts, crew roster, and cargo list.
| Section | Game systems housed |
|---|---|
| Readouts | Cargo / fuel / crew / engine / scanner / hull readouts, mass & speed. |
| Crew | Hired crew roster + upkeep. |
| Cargo | Inventory list with quantities + value. |
ShipPanel. Install / inspect / swap lives on the station's Services → Loadout sub-tab (StationPanel.loadout). The old ShipPanel schematic tab and its drag-drop ShipSchematic overlay were retired in the declutter to give modules a single, list-based home.
StoryPanel — Star Map .starmap-nav-dock tabs (L key)
File: src/components/StoryPanel.tsx. Not a standalone window — it renders in the lower section of the Star Map's navigation sidebar (.starmap-nav-dock, §1c) as the Missions / Factions / Codex tabs alongside the Roster. [L] jumps to the Missions tab. Accepts a controlled tab prop + showTabs={false} so the sidebar's tab strip drives it.
| Pane | Tab key | Game systems housed |
|---|---|---|
StoryPanel.missions | missions | Active steps, ready-to-hand-in, chain progress, story-flag unlocks. |
StoryPanel.factions | factions | All four factions at a glance — current rep, rank, trade modifiers, progress to next rank, and each faction's rivalry/ally relations (drives reputation spillover). |
StoryPanel.codex | codex | Public FIELD RECORDS (visit-unlocked lore dossiers) + story-gated CLASSIFIED intel (secrets, sealed until their reveal flag fires). |
ShipPanel, then a full-screen overlay, then a toggled right-rail panel. Now they're the lower tabs of the persistent .starmap-nav sidebar (Roster · Missions · Factions · Codex) — the single home. Don't add them back elsewhere.
1c · Sub-surfaces (Star Map zones)
StarMap.tsx is a two-column shell (.starmap-layout), mirroring the docked layout: a map canvas (.starmap-root, left, flexible) + a persistent navigation sidebar (.starmap-nav, right, clamp(340px,32%,420px)). The old floating Intel / roster / bottom-strip windows were folded into the sidebar.
| Pane | Sub-component | Class root | Game systems housed |
|---|---|---|---|
StarMap.FilterChips | FilterChips | .starmap-corner-hud, .starmap-filterbar, .starmap-filters | (On canvas.) Interactive faction-filter chips (All / per-faction / Neutral / Hostile). Dims non-matching nodes. Collapsed by default behind a .starmap-filter-toggle ("FILTERS") that reveals the chip row; a cyan .starmap-filter-badge shows the active-filter count while collapsed. |
StarMap.ZoomControls | inline | .starmap-zoom | (On canvas.) Zoom in/out, percentage, reset (RST). |
StarMap.StationNode (×N) | StationNode | .starmap-node | (On canvas.) Per-station glyph: ring (category vs. faction), pulse on current, dashed ring on plotted, hostile / dimmed / unknown variants. Lane-locked variant (.is-lane-locked): a discovered node reachable only through a hard-locked lane renders desaturated with a dashed alert ring. Labels gate by zoom (LABELS_MIN_ZOOM, 120%) + priority states (current / selected / hovered / plotted / on-route); survey badges (L1–L7) show only on hover/selection. |
StarMap lanes (edges) | inline EDGES.map | .starmap-edge | (On canvas.) Connection lines. Lane-lock variants: .is-toll (amber dashes) and .is-locked (alert dashes), with a midpoint .starmap-edge-lock tag (e.g. TOLL 120c, BLOCKADE) and a <title> reason. Lock state comes from engine/lanes.ts; a paid-off one-time toll reverts to a plain lane. |
StarMap.NavContext | inline | .starmap-nav-context | Sidebar top. Stacked & scrollable context: TransitConsole in transit, else IntelPanel (selected or current station) + FlightPlanStrip (when a route is plotted) + IdleHint fallback. |
StarMap.IntelPanel | IntelPanel | .starmap-intel | Station survey readout (scan-gated rows, survey-progress bar, SCAN / CENTER / DOCK). Inline in the sidebar; close (×) only for an explicitly-selected station. Body shows core facts; trade rows + description collapse behind a .intel-more-toggle (MORE/LESS, default collapsed). |
StarMap.FlightPlanStrip | FlightPlanStrip | .starmap-flight | Waypoint trail, 5-stat totals (+ a TOLL stat when the route crosses a paid lane), next-leg row, JUMP button. A .flight-block "LANE LOCKED" banner with the reason replaces the route when no open path exists (button → NO ROUTE); insufficient toll credits → NO CREDITS. Reflowed to a column in the sidebar. |
StarMap.TransitConsole | TransitConsole | .starmap-transit-console | Origin → destination, progress bar with ship marker, ETA / elapsed, risk pips, drive status. |
StarMap.IdleHint | IdleHint | .starmap-idle-hint | Hint shown in the context area when there's no current/selected station. |
StarMap.NavDock | inline + SectorRoster / StoryPanel | .starmap-nav-dock | Sidebar bottom — always visible, tabbed. A .starmap-dock-tabs strip switches between Roster (searchable station list; click selects + plots + focuses) and Missions / Factions / Codex (renders StoryPanel, §1b). [L] jumps to Missions. Roster tab only with a current station, not in transit. The "Sector Roster" heading is a .starmap-dock-toggle that collapses/expands the list (open by default); collapsed (.is-collapsed) the dock shrinks to its heading strip and the freed space goes to the nav-context above. |
TransitConsole shows. Otherwise Intel (selected-or-current) and the plotted-route FlightPlanStrip stack together in the scrollable context area, so SCAN/DOCK and JUMP stay reachable.
1d · Overlays
| Pane | Component | Trigger | Game systems housed |
|---|---|---|---|
| Controls / help | ControlsOverlay | ? or / key | Keyboard + mouse reference. |
.starmap-nav sidebar (§1c).
StarMap.tsx); the controls overlay handles its own Esc in App.tsx.
1e · Modals
| Pane | Component | Trigger | Game systems housed |
|---|---|---|---|
| Event | EventModal | Reducer sets state.activeEvent | Random encounters. Blocks input; 1 / 2 / 3 to pick a response. |
| Comms | CommsModal | Reducer sets state.activeComms — opening (START_GAME→intro.ts), accept→briefing, complete→debrief, dock-tab Talk (OPEN_COMMS→character.idle) | Interactive story transmissions — complete, stationary, left-aligned lines with space/click to advance and number keys for replies. Choices display consequences, persist flags/effects, and enter the captain log. Standard dialogue uses an avatar panel; pivotal evidence cutscenes promote a wide still with an evidence caption and omit the redundant portrait. Speakers resolve via resolveSpeaker (data/staff.ts): named characters/*.json or synthetic generic:… clerks. |
| Arrival | ArrivalModal | After transit → map with currentStationId set | Arrival flow — DOCK / MINE / EXPLORE / STAY IN ORBIT. Faction-rep gating. |
| Good info | GoodInfoModal | Click a market row in StationPanel.market | Good details + lore tip. |
1f · Persistent Chrome
Rendered on top of every non-title screen. Not windows — never call them "panels".
| Element | Component | File | Game systems housed |
|---|---|---|---|
| Top bar | TopBar | src/components/TopBar.tsx | Day · credits · fuel (segmented bar) · hull (segmented bar) · location · [M] DOCK (map only) · SAVE · MENU. |
| Error boundary | ErrorBoundary | src/components/ErrorBoundary.tsx | Wraps the shell — renders a fallback on thrown error. |
| Scanlines + small-viewport warning | inline | src/App.tsx | Pure visual overlay; no logic. |
1g · Proposed — new homes for orphaned systems
These game systems exist in the engine / data but have no canonical window today — they're either scattered inline across unrelated panels or invisible after the moment they fire. Each row below is a proposed new pane; none of these are implemented yet. Documenting them as the agreed home prevents the next ad-hoc inline render from deepening the scatter.
| Proposed pane | Status | What it would show | Source |
|---|---|---|---|
StoryPanel.log |
proposed | Reverse-chronological event history — events fired, choices taken, outcomes. Currently events vanish after RESOLVE_EVENT. |
Needs reducer to keep a bounded state.eventHistory array on each RESOLVE_EVENT. |
StoryPanel.gathering (optional, lower priority) |
proposed | Resources gathered, cooldowns, eligible nodes. | state.gatheringHistory (new) + station.gatherables lookups. |
StoryPanel.factions shipped (see §1b), implemented with the reputation-spillover system. StoryPanel.log is medium. StoryPanel.gathering is optional.
02 · Shared Components
Canonical implementations of UI patterns that appear in multiple panes.
Located at src/components/shared/. Do not duplicate these inline —
import from shared/. If you find a pattern rendered in 2+ panes with cosmetic drift,
promote it here.
| Component | Class prefix | Consumers | Renders |
|---|---|---|---|
ObjectiveList | .obj-* | StoryPanel · StationPanel.comms · StationPanel.jobs · ShipPanel | Mission objective checklist with progress + done states. |
CodexList | .codex-* | StoryPanel.codex | Public FIELD RECORDS — discovered lore dossiers + undiscovered titles list. |
ClassifiedList | .classified-* | StoryPanel.codex | Story-gated CLASSIFIED intel — revealed secret bodies + sealed teaser rows. Gated by secrets.ts reveal flags; in-game only, never on public pages. |
MissionStepCard | .mission-step-* | StoryPanel.missions | Mission step card — chain badge, title, description, objectives, reward. Variants: default / ready-handin / auto-complete / new-mission. |
ChainRow | .mission-chain-* | StoryPanel.missions | Chain progress row with status pill (LOCKED / IN PROGRESS / COMPLETE), faction, step count, optional prereq. |
StoryFlagRow | .mission-flag-* | StoryPanel.missions | Single unlock row in the UNLOCKS section. |
StatBar | .stat-bar-* | TopBar (fuel + hull) · ShipPanel (cargo / fuel / crew / speed / scanner / hull) | Segmented stat indicator. Supports fixed colour, tier mode (auto low/mid/ok colour by pct), and blinkOnCritical (≤ 10%). Replaces the previous FuelBar / HullBar / Bar. |
.modal-overlay / .modal-panel classes live in src/styles/global.css (not a shared/ component) and are the single backdrop + panel pattern consumed by every modal: EventModal, GoodInfoModal, ArrivalModal, ControlsOverlay. Don't hand-roll a new overlay backdrop; reuse these.
shared/Foo.tsx + Foo.css with a single foo-* class prefix, replace the duplicates, and add a row to this table.
03 · Engine Modules
Pure modules under src/engine/. No React imports. Each pane consumes zero or more of these to compute display values.
| Module | Owns | Consumers |
|---|---|---|
travel.ts | Path-finding (incl. findOpenPath — lane-lock-aware BFS), distance, fuel cost, station lookup | StarMap, FlightPlan, Transit |
lanes.ts | Per-lane locks (pass / blockade / toll / rep) keyed by sorted endpoint pair. isLaneOpen / getLaneToll / lanePassToSet / laneLockReason / routeTollTotal. Toll lanes stay passable; one-time tolls buy a passFlag. | StarMap (lane render + reachability), reducer (START_TRAVEL gate + toll charge) |
economy.ts | Pricing, stock, supply/demand modifiers | StationPanel (market) |
shipStats.ts | Engine speed, fuel efficiency, cargo capacity, scanner depth, cargo risk | TopBar, StarMap, ShipPanel |
events.ts | Event rolls + risk evaluation | StarMap (transit), EventModal |
environment.ts | Thermal / radiation hazards | reducer.applyArrivalProcessing |
story.ts | Story step graph, station accessibility, story-gated stations/goods/modules/ships | StarMap, StationPanel.comms, StoryPanel |
contracts.ts | Dynamic job board — seeded contract generation/refresh/expiry, cargo-consuming hand-in, home-region rep decay | StationPanel.jobs, reducer (day-advance) |
reputation.ts | Central reputation writer — applies a rep change plus rival/ally spillover (matrix in balance.json), returns the change-list for the multi-faction toast | reducer (trade/events), contracts, story |
objectives.ts | Derives per-step objective lists | shared/ObjectiveList |
hexGrid.ts | Hex maths legacy | — |
04 · Static Data
All under src/data/. Loaded at build time. Every entity follows
the same convention: one JSON file per entity, aggregated
into an array by a thin .ts wrapper using
import.meta.glob. No data is inline in TypeScript.
| Wrapper | JSON glob | Drives |
|---|---|---|
stations.ts | stations/*.json | Every node on the star map; market exports/imports; gatherables. |
lanes.ts | lanes/*.json | Per-lane lock overrides. Only locked lanes get a file (endpoints + lock); every other connection (derived from station connections) defaults open. Consumed by engine/lanes.ts. |
ships.ts | ships/*/info.json | Shipyard inventory + archetypes + gameplay stats. |
modules.ts | modules/*.json | Outfitting catalogue (engines, fuel tanks, scanners, etc.). |
goods.ts | goods/*.json | Tradeable items + categories. |
factions.ts | factions/*.json | Faction list, colour, reputation thresholds. Also exports rep-tier logic. |
crew.ts | crew/*.json | Hireable crew templates. |
characters.ts | characters/*.json | Story speakers (name, role, station, faction, glyph, avatar, idle Talk dialogue) for CommsModal. Exports CHARACTERS + CHARACTERS_BY_ID + getCharacter. |
staff.ts | — (resolver) | Station + dock tab → NPC. getTabCharacter(station, post) returns the named station.staff character or a generic faction clerk; resolveSpeaker resolves generic:… ids. Drives NPCHeader. |
story.ts | story/*.json | Mission chains, objectives, dialogue. |
lore.ts | lore/*.json | Public codex dossiers (FIELD RECORDS) unlocked by visits / events. Exports both LORE_ENTRIES (array) and LORE_BY_ID (Record). |
secrets.ts | secrets/*.json | Story-gated CLASSIFIED codex entries. Each has a revealFlag; isSecretRevealed() checks it against storyFlags. Bodies lifted from STARWIND_LORE.md §10. |
stationInventory.ts | — | Pure filter logic. Resolves station services against storyFlags + factionRep. |
balance.ts | balance.json (single file) | Tunable gameplay constants — tier margins, stock curves, price drift, base event chance, planet bias, engine multipliers, travel scale, starting credits. economy/events/shipStats/travel/initialState import BALANCE instead of hardcoding magic numbers. |
*.json. If you find inline data in a .ts file under src/data/, split it into per-entity JSON files and reduce the .ts to an import.meta.glob wrapper. The existing wrappers are the template. balance.json is the one deliberate single-file exception (a flat config, not entities) — add new tunables there.
4a · Data editor (/editor/ — dev-only)
A password-gated editor for tweaking the static-data JSON (and
balance.json) against the running game, for balancing.
Dev-only: it lives under npm run dev and is
deliberately excluded from vite build (not in
rollupOptions.input), so it 404s on a deployed site.
| Piece | Where | Role |
|---|---|---|
| Editor SPA | editor/index.html + src/editor/ | Login gate → seven modes (Files / Map / Flags / Story / Economy / Doctor / Reputation). Files = sidebar + GUI form editor (Raw-JSON toggle) + Save (⌘S); the rest are read-only analytical views. |
| Map mode | src/editor/MapView.tsx | Interactive SVG star map: drag nodes (writes x/y), Connect mode toggles links, add/delete stations. Click a lane (edge) to set/clear its lock (pass / blockade / toll / rep) — writes/deletes a lanes/*.json file (keyed by sorted endpoints). Locked/toll edges render in alert/amber with a midpoint tag. |
| Flags mode | src/editor/FlagsView.tsx | Read-only index of every story flag (from STORY_CHAINS + LANES + the STORY_GATED_* maps): where it's set, required, and what it gates. Flags orphans ("no story source" / "set, never read"); click a source to open the chain. |
| Story mode | src/editor/StoryGraphView.tsx | Mission graph: each chain's steps in flow order with trigger / completion / rewards, the flag gate that unlocks the chain, and the flags + content each step unlocks. |
| Economy mode | src/editor/EconomyView.tsx | Goods × stations trade matrix (Export/Import per cell) with tier / price / min-rep; flags one-sided goods (no exporter or no importer). |
| Doctor mode | src/editor/DoctorView.tsx | Cross-file integrity sweep: broken refs, orphan goods/ships/modules, story trigger/goods/station/faction refs, gate & secret flags with no source, speaker↔character mismatches, stations unreachable from Verdant. |
| Reputation mode | src/editor/ReputationView.tsx | Faction relationship matrix (rival/ally spillover from balance.json) + station ownership + every story step that grants rep, per faction. |
| Schemas | src/editor/schemas/*.schema.json | One strict JSON Schema (draft-07, additionalProperties:false) per data type — the validation "standard". schema.ts maps each file path to its schema. |
| Form generator | src/editor/SchemaForm.tsx | Recursive, schema-driven widgets: objects, arrays, enums→selects, numbers/booleans, optional add/remove, $ref, free-form params as raw-JSON islands, "unknown field" drift rows. |
| Validation | src/editor/schema.ts (Ajv) | Validates on every edit; Save blocked while invalid. Files without a schema fall back to the raw editor. |
| Related panel | src/editor/refs.ts (right sidebar) | Cross-reference graph: References → (live from the edited value) and Referenced by ← (static index over all files via import.meta.glob), grouped by category, click to open, broken refs flagged. Driven by x-ref schema annotations. |
| Backend | editorPlugin() in vite.config.ts (apply: 'serve') | Auth + read/write API at /api/editor/*, path-allowlisted to src/data/**.json. |
| Auth | EDITOR_PASSWORD in .env.local (gitignored, no VITE_ prefix → server-side only) | Password mints an in-memory session token; every read/write requires it. |
Schemas cover all 10 entity types plus balance.json; every existing data file validates clean. Saving writes the real source JSON on disk (canonical 2-space); Vite HMR then reloads any open /game/ tab. See .env.local.example to enable.
/editors. The dev-only data editor
above (Vite, localhost) is unrelated to the deployed admin
console at starwind.darkvision.dev/editor (§4b). Different runtimes,
different jobs — the dev one edits source JSON, the prod one shows
players/saves.
4b · Telemetry server + admin console (deployed)
A standalone Node service (server/, Fastify + node:sqlite)
on the OVH box. Not part of the game build or static rsync —
it runs as its own systemd service; nginx serves the static game and
reverse-proxies /api/* + /editor to it.
| Piece | Where | Role |
|---|---|---|
| Telemetry ingest | server/src/routes/telemetry.mjs | POST /api/sync (save + version + last-played mirror), POST /api/heartbeat (presence). Validated, rate-limited, size-capped. |
| Admin API | server/src/routes/admin.mjs | Login (signed httpOnly cookie) + players list / save view / delete. |
| Admin console | server/public/editor.html → /editor | Player table: callsign · online dot · version · last played · save size; save viewer + download. |
| Data store | server/src/db.mjs | One SQLite file (WAL), one row per player, latest save inline. Repository pattern → Postgres-swappable. |
| Game client | src/net/telemetry.ts | Player ID + callsign (captured on TitleScreen first New Game), throttled save push + 30s heartbeat. Fire-and-forget. |
| Deploy | server/deploy/ + server/README.md | systemd unit, nginx config, updated webhook deploy script. |
Saves are a mirror: localStorage stays the source of truth for playing; the server copy exists for the console. No cross-device restore.
4c · Public guide (generated)
The player-safe documentation portal at /guide/ is generated
from docs/public/*.md and docs/catalog.json.
scripts/generate-docs.mjs emits a landing page, clean chapter
routes, and a static search index during predev and
prebuild.
| Piece | Where | Role |
|---|---|---|
| Catalog | docs/catalog.json | Ordered documentation lookup and public/maintainer audience allowlist. |
| Public source | docs/public/*.md | Canonical spoiler-free player guide. |
| Generator | scripts/generate-docs.mjs | Safe Markdown rendering, navigation, table of contents, pagination, and search records. |
| Audit | scripts/docs-audit.mjs | IDs/slugs, sources, links, audience boundary, generated coverage, and search coverage. |
| Shared client | public/guide.css + public/guide.js | Reference-page layout, responsive chapter rail, and in-browser search. |
Generated guide HTML and public/guide/search-index.json are
ignored build output. Maintainer runbooks never enter dist/.
05 · Keyboard Map
Handled in App.tsx (handleKey) unless noted. ? / / always toggles ControlsOverlay.
| Key | Screen(s) | Action |
|---|---|---|
| M | map | Dock at current station |
| M | docked | Undock back to map |
| K · S · U | map | Alias for dock |
| J | map | Jump to plotted destination (handled in StarMap.tsx) |
| R | docked | Refuel |
| L | map / transit | Jump the nav sidebar to the Missions tab (handled in StarMap.tsx) |
| 1 · 2 · 3 | EventModal | Pick response option |
| Esc | EventModal | Decline (encounters & opportunities only) |
| Esc | map | Deselect station → clear plotted route (cascading, in StarMap.tsx) |
| ? · / | anywhere | Toggle ControlsOverlay |
| ` | non-title | DEBUG_CHEAT action dev |
The canonical list rendered to the player lives in src/components/ControlsOverlay.tsx. Keep this table and that array in sync.
06 · Screen Transitions
┌──────────┐
│ title │
└────┬─────┘
│ start / load
▼
┌──────────┐ ┌─────────────┐
undock ◀────┤ map ├─── jump ─────▶ │ transit │
│ │ │ │
│ ▲ │ complete │ (StarMap │
│ └──────┼────────────────┤ in │
│ │ │ transit │
│ dock │ │ mode) │
└────┬─────┘ └─────────────┘
│
▼
┌──────────┐
│ docked │
└────┬─────┘
│
hostile / locked
▼
┌──────────┐
│ denied │
└──────────┘
hull == 0 / catastrophic event from any state ▶ gameover
07 · When to Update This File
You must update this file (and its markdown twin feature-map.md) when you:
- Add a new top-level screen (new value of
state.screen). - Add a new tab to
StationPanel,ShipPanel, orStoryPanel. - Add a new persistent chrome element rendered above every screen.
- Add a new overlay or modal.
- Add a new sub-surface to
StarMap. - Add a new engine module under
src/engine/. - Add a new key binding handled by
App.tsxor a screen-level component. - Implement one of the §1g proposed homes — move its row from §1g into §1b in the same change.
You do not need to update this file for:
- Internal refactors that don't change the surface area.
- Visual-only tweaks (those go in
style-guide.md). - Data-only additions (new stations, ships, goods, missions) — those flow through existing tables.