Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 48 additions & 15 deletions apps/mapgen-studio/src/ui/components/GameConsole.tsx
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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 */
Expand All @@ -72,6 +77,7 @@ export const GameConsole: React.FC<GameConsoleProps> = ({
onSyncFromLiveGame,
isAutoplayActionRunning = false,
onToggleAutoplay,
onExplore,
operationControlsDisabled,
isRunInGameRunning,
runInGameStatus,
Expand Down Expand Up @@ -127,14 +133,16 @@ export const GameConsole: React.FC<GameConsoleProps> = ({
? "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
? [
Expand All @@ -144,8 +152,11 @@ export const GameConsole: React.FC<GameConsoleProps> = ({
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,
Expand Down Expand Up @@ -196,20 +207,42 @@ export const GameConsole: React.FC<GameConsoleProps> = ({
<TooltipTrigger asChild>
<Button
variant="outline"
size="sm"
size="icon"
onClick={onToggleAutoplay}
disabled={autoplayControlDisabled}
aria-label={autoplayTitle}
title={autoplayTitle}
className={`h-7 px-2 shrink-0 ${liveRuntime?.autoplayActive ? "border-warning/60 text-warning" : ""}`}>
className={`shrink-0 ${liveRuntime?.autoplayActive ? "border-warning/60 text-warning" : ""}`}>

{liveRuntime?.autoplayActive ? <Square className="w-3.5 h-3.5" /> : <Bot className="w-3.5 h-3.5" />}
<span>{autoplayButtonText}</span>
{isAutoplayActionRunning ? (
<LoaderCircle className="w-3.5 h-3.5 animate-spin" />
) : liveRuntime?.autoplayActive ? (
<Square className="w-3.5 h-3.5" />
) : (
<FastForward className="w-3.5 h-3.5" />
)}
</Button>
</TooltipTrigger>
<TooltipContent>{autoplayTitle}</TooltipContent>
</Tooltip>

<Tooltip>
<TooltipTrigger asChild>
<Button
variant="outline"
size="icon"
onClick={onExplore}
disabled={operationControlsDisabled || liveRuntime?.status !== "ok" || !onExplore}
aria-label={exploreTitle}
title={exploreTitle}
className="shrink-0">

<Binoculars className="w-3.5 h-3.5" />
</Button>
</TooltipTrigger>
<TooltipContent>{exploreTitle}</TooltipContent>
</Tooltip>

{saveDeployStatus && saveDeployStatus.status !== "complete" ? (
<Tooltip>
<TooltipTrigger asChild>
Expand Down Expand Up @@ -285,12 +318,12 @@ export const GameConsole: React.FC<GameConsoleProps> = ({
onClick={onRunInGame}
disabled={operationControlsDisabled}
variant="outline"
size="icon"
aria-label={runInGameTitle}
title={runInGameTitle}
className={isRunInGameRunning ? 'shrink-0 opacity-70 cursor-wait' : 'shrink-0'}>

<MonitorPlay className="w-3.5 h-3.5" />
<span>{runInGameButtonText}</span>
<SquareArrowOutUpRight className="w-3.5 h-3.5" />
</Button>
</TooltipTrigger>
<TooltipContent className="whitespace-pre-line">{runInGameTitle}</TooltipContent>
Expand Down
6 changes: 6 additions & 0 deletions apps/mapgen-studio/test/runInGame/GameConsole.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
Expand Down
42 changes: 42 additions & 0 deletions openspec/changes/mapgen-studio-game-console-icons/proposal.md
Original file line number Diff line number Diff line change
@@ -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`
Original file line number Diff line number Diff line change
@@ -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
17 changes: 17 additions & 0 deletions openspec/changes/mapgen-studio-game-console-icons/tasks.md
Original file line number Diff line number Diff line change
@@ -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.