From 87a6f63be317f7a5f2cbf19d5af0f440e2d935cc Mon Sep 17 00:00:00 2001 From: Matei Date: Thu, 11 Jun 2026 21:14:26 -0400 Subject: [PATCH] feat(mapgen-studio): game console commands go icon-only (FastForward/SquareArrowOutUpRight) + Explore placeholder Co-Authored-By: Claude Fable 5 --- .../src/ui/components/GameConsole.tsx | 63 ++++++++++++++----- .../test/runInGame/GameConsole.test.tsx | 6 ++ .../proposal.md | 42 +++++++++++++ .../specs/mapgen-studio/spec.md | 32 ++++++++++ .../mapgen-studio-game-console-icons/tasks.md | 17 +++++ 5 files changed, 145 insertions(+), 15 deletions(-) create mode 100644 openspec/changes/mapgen-studio-game-console-icons/proposal.md create mode 100644 openspec/changes/mapgen-studio-game-console-icons/specs/mapgen-studio/spec.md create mode 100644 openspec/changes/mapgen-studio-game-console-icons/tasks.md diff --git a/apps/mapgen-studio/src/ui/components/GameConsole.tsx b/apps/mapgen-studio/src/ui/components/GameConsole.tsx index 6ff5b214a1..b0b3a266cf 100644 --- a/apps/mapgen-studio/src/ui/components/GameConsole.tsx +++ b/apps/mapgen-studio/src/ui/components/GameConsole.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Bot, Clipboard, MonitorPlay, Radio, RotateCw, Square } from 'lucide-react'; +import { Binoculars, Clipboard, FastForward, LoaderCircle, Radio, RotateCw, Square, SquareArrowOutUpRight } from 'lucide-react'; import { Button, Tooltip, TooltipContent, TooltipTrigger } from '../../components/ui'; import { formatRunInGamePhaseLabel, @@ -48,6 +48,11 @@ export interface GameConsoleProps { isAutoplayActionRunning?: boolean; /** Callback to start or stop Civ7 native autoplay */ onToggleAutoplay?: () => void; + /** + * Callback for the Explore command (tile visibility/exploration in the + * live game). The button is a disabled placeholder until this is wired. + */ + onExplore?: () => void; /** Whether any studio/game operation is in flight (shared control gating) */ operationControlsDisabled: boolean; /** Whether Civ7 Run in Game is currently running */ @@ -72,6 +77,7 @@ export const GameConsole: React.FC = ({ onSyncFromLiveGame, isAutoplayActionRunning = false, onToggleAutoplay, + onExplore, operationControlsDisabled, isRunInGameRunning, runInGameStatus, @@ -127,14 +133,16 @@ export const GameConsole: React.FC = ({ ? "Apply live game suggestion to Studio" : liveRuntime?.readiness ?? liveRuntime?.error ?? "Civ7 live runtime status"; const autoplayControlDisabled = operationControlsDisabled || isAutoplayActionRunning || liveRuntime?.status !== "ok" || !onToggleAutoplay; - const autoplayButtonText = isAutoplayActionRunning - ? "Autoplay..." + // Icon-only contract (Pass-4): the start/stop/in-flight wording the label + // used to carry lives entirely in the accessible name + tooltip. + const autoplayTitle = isAutoplayActionRunning + ? "Autoplay request in flight" : liveRuntime?.autoplayActive - ? "Stop Auto" - : "Start Auto"; - const autoplayTitle = liveRuntime?.autoplayActive - ? `Stop Civ7 autoplay${liveRuntime.autoplayPaused ? " (paused)" : ""}` - : "Start Civ7 autoplay"; + ? `Stop Civ7 autoplay${liveRuntime.autoplayPaused ? " (paused)" : ""}` + : "Start Civ7 autoplay"; + const exploreTitle = onExplore + ? "Explore: toggle tile visibility in the live game" + : "Explore: tile visibility control is not yet available"; const saveDeployLabel = saveDeployStatus ? formatMapConfigSaveDeployPhaseLabel(saveDeployStatus.phase) : null; const saveDeployTitle = saveDeployStatus ? [ @@ -144,8 +152,11 @@ export const GameConsole: React.FC = ({ saveDeployStatus.error ? `Error: ${saveDeployStatus.error}` : null, ].filter(Boolean).join("\n") : "Config save/deploy status"; + // Icon-only contract (Pass-4): the dynamic action label ("Run in Game", + // "Retry Run", "Restart Civ & Run") leads the accessible name + tooltip. const runInGameTitle = [ - runInGameStatus ? `Run in Game: ${runInGamePhaseLabel}` : "Run in Game: launch current config in Civ7", + runInGameButtonText, + runInGameStatus ? `Run in Game: ${runInGamePhaseLabel}` : "Launches the current config in Civ7", runInGameStatus?.requestId ? `Request: ${runInGameStatus.requestId}` : null, runInGameStatus?.materialization?.mapScript ? `Map: ${runInGameStatus.materialization.mapScript}` : null, runInGameStateLabel ? `Studio state: ${runInGameStateLabel}` : null, @@ -196,20 +207,42 @@ export const GameConsole: React.FC = ({ {autoplayTitle} + + + + + {exploreTitle} + + {saveDeployStatus && saveDeployStatus.status !== "complete" ? ( @@ -285,12 +318,12 @@ export const GameConsole: React.FC = ({ onClick={onRunInGame} disabled={operationControlsDisabled} variant="outline" + size="icon" aria-label={runInGameTitle} title={runInGameTitle} className={isRunInGameRunning ? 'shrink-0 opacity-70 cursor-wait' : 'shrink-0'}> - - {runInGameButtonText} + {runInGameTitle} diff --git a/apps/mapgen-studio/test/runInGame/GameConsole.test.tsx b/apps/mapgen-studio/test/runInGame/GameConsole.test.tsx index 84cfcae55f..54bca1c7c0 100644 --- a/apps/mapgen-studio/test/runInGame/GameConsole.test.tsx +++ b/apps/mapgen-studio/test/runInGame/GameConsole.test.tsx @@ -175,6 +175,12 @@ describe("GameConsole live runtime and save/deploy", () => { expect(html).toContain("Auto"); }); + it("renders the Explore placeholder disabled until a handler is wired", () => { + const html = renderConsole(); + + expect(html).toContain("Explore: tile visibility control is not yet available"); + }); + it("highlights live seed status when a proved live game is out of sync with Studio", () => { const html = renderConsole({ liveRuntime: { status: "ok", turn: 12, seed: 123 }, diff --git a/openspec/changes/mapgen-studio-game-console-icons/proposal.md b/openspec/changes/mapgen-studio-game-console-icons/proposal.md new file mode 100644 index 0000000000..3716c1e492 --- /dev/null +++ b/openspec/changes/mapgen-studio-game-console-icons/proposal.md @@ -0,0 +1,42 @@ +# Game console commands go icon-only + Explore button + +## Why + +With the console docked beneath the world bar, its labeled buttons ("Start +Auto", "Run in Game") are the widest things in the header zone, and a third +command (Explore — tile visibility) is arriving. Labels also communicated +poorly: `Bot` read as a chatbot, `MonitorPlay` undersold that the action +launches the external Civ7 app. The Pass-4 icon contract (frame E2): secondary +repeated-use actions in dense consoles are icon-only with full accessible +names; primary CTAs keep labels. + +## Target Authority Refs + +- `docs/projects/mapgen-studio-redesign/pass-4-design-fixes.md` (E2 table) +- `apps/mapgen-studio/.interface-design/system.md` (§Pass-4 amendment: + icon-only contract + console glyphs) + +## What Changes + +All in `src/ui/components/GameConsole.tsx`: + +- **Autoplay** becomes icon-only: `FastForward` (start) / `Square` (stop), + `LoaderCircle` spinner while the start/stop request is in flight; the + start/stop/in-flight wording moves into `aria-label`/`title`/Tooltip. +- **Run in Game** becomes icon-only: `SquareArrowOutUpRight` (launches the + external app). The dynamic action label ("Run in Game" / "Retry Run" / + "Restart Civ & Run") is preserved as the FIRST line of the button's + `aria-label`/`title`/Tooltip. +- **Explore** (NEW): icon-only `Binoculars` between autoplay and the + run-in-game group (both autoplay and explore command the live game; Run in + Game launches it). Behavior is wired later behind an optional `onExplore` + prop; the button renders disabled until a handler exists, with an + accessible name saying so. +- Tests: GameConsole scenarios gain assertions that the action labels survive + in accessible names and that Explore renders. + +## Impact + +- Affected specs: `mapgen-studio` +- Affected code: `apps/mapgen-studio/src/ui/components/GameConsole.tsx`, + `apps/mapgen-studio/test/runInGame/GameConsole.test.tsx` diff --git a/openspec/changes/mapgen-studio-game-console-icons/specs/mapgen-studio/spec.md b/openspec/changes/mapgen-studio-game-console-icons/specs/mapgen-studio/spec.md new file mode 100644 index 0000000000..81892a85bb --- /dev/null +++ b/openspec/changes/mapgen-studio-game-console-icons/specs/mapgen-studio/spec.md @@ -0,0 +1,32 @@ +## ADDED Requirements + +### Requirement: Game Console Commands Are Icon-Only With Accessible Names + +Game console command buttons (autoplay, Explore, Run in Game) SHALL render +icon-only, and every state the removed labels carried (start/stop/in-flight +autoplay wording; the dynamic Run in Game action label including recovery +variants) SHALL remain available via `aria-label`, `title`, and Tooltip. +Glyphs: autoplay start `FastForward`, stop `Square`, in-flight spinner; Run in +Game `SquareArrowOutUpRight`. + +#### Scenario: Autoplay states survive as accessible names +- **WHEN** the autoplay button renders in start, stop, or in-flight state +- **THEN** it shows only the state's glyph and its accessible name states the action (start/stop Civ7 autoplay, or that a request is in flight) + +#### Scenario: Run in Game recovery actions survive as accessible names +- **WHEN** a failed or blocked operation makes the primary action "Retry Run" or "Restart Civ & Run" +- **THEN** the icon-only button's accessible name leads with that action label + +### Requirement: The Game Console Offers An Explore Button + +The game console SHALL render an icon-only Explore button (`Binoculars`, +tile visibility) between autoplay and the Run in Game group, disabled with an +accessible name announcing unavailability until a handler is wired. + +#### Scenario: Explore renders with the command set +- **WHEN** the game console renders +- **THEN** an Explore button with the `Binoculars` glyph renders after the autoplay button and before the Run in Game status/action group + +#### Scenario: Explore without a handler is announced unavailable +- **WHEN** no Explore handler is provided +- **THEN** the button renders disabled and its accessible name says tile-visibility control is not yet available diff --git a/openspec/changes/mapgen-studio-game-console-icons/tasks.md b/openspec/changes/mapgen-studio-game-console-icons/tasks.md new file mode 100644 index 0000000000..4935aeb537 --- /dev/null +++ b/openspec/changes/mapgen-studio-game-console-icons/tasks.md @@ -0,0 +1,17 @@ +## 1. Implementation + +- [x] 1.1 Autoplay button icon-only: `FastForward`/`Square`/in-flight + spinner; state wording moves to aria-label/title/Tooltip. +- [x] 1.2 Run in Game button icon-only: `SquareArrowOutUpRight`; dynamic + action label leads the aria-label/title/Tooltip. +- [x] 1.3 Add icon-only Explore button (`Binoculars`, optional `onExplore` + prop, disabled placeholder) between autoplay and the run-in-game group. +- [x] 1.4 Extend GameConsole tests: action labels survive in accessible + names; Explore renders disabled without a handler. + +## 2. Verification + +- [x] 2.1 `bun run openspec -- validate mapgen-studio-game-console-icons --strict` +- [x] 2.2 tsc + mapgen-studio vitest green +- [x] 2.3 Visual on :5173: three icon-only commands read as a set under the + world bar; tooltips carry the full action names. Screenshot.