@duetso/agent 0.1.138 → 0.1.140

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/dist/package.json +1 -1
  2. package/dist/src/cli/help.d.ts.map +1 -1
  3. package/dist/src/cli/help.js +20 -7
  4. package/dist/src/cli/help.js.map +1 -1
  5. package/dist/src/cli/memory-reflect.d.ts.map +1 -1
  6. package/dist/src/cli/memory-reflect.js +3 -2
  7. package/dist/src/cli/memory-reflect.js.map +1 -1
  8. package/dist/src/memory/observation-groups.d.ts +11 -1
  9. package/dist/src/memory/observation-groups.d.ts.map +1 -1
  10. package/dist/src/memory/observation-groups.js +15 -5
  11. package/dist/src/memory/observation-groups.js.map +1 -1
  12. package/dist/src/memory/observational-prompts.d.ts +21 -2
  13. package/dist/src/memory/observational-prompts.d.ts.map +1 -1
  14. package/dist/src/memory/observational-prompts.js +89 -12
  15. package/dist/src/memory/observational-prompts.js.map +1 -1
  16. package/dist/src/memory/observational.d.ts +36 -6
  17. package/dist/src/memory/observational.d.ts.map +1 -1
  18. package/dist/src/memory/observational.js +118 -53
  19. package/dist/src/memory/observational.js.map +1 -1
  20. package/dist/src/tui/app.d.ts.map +1 -1
  21. package/dist/src/tui/app.js +19 -0
  22. package/dist/src/tui/app.js.map +1 -1
  23. package/dist/src/tui/dino/index.d.ts +31 -0
  24. package/dist/src/tui/dino/index.d.ts.map +1 -0
  25. package/dist/src/tui/dino/index.js +313 -0
  26. package/dist/src/tui/dino/index.js.map +1 -0
  27. package/dist/src/tui/dino/input.d.ts +12 -0
  28. package/dist/src/tui/dino/input.d.ts.map +1 -0
  29. package/dist/src/tui/dino/input.js +44 -0
  30. package/dist/src/tui/dino/input.js.map +1 -0
  31. package/dist/src/tui/dino/persistence.d.ts +3 -0
  32. package/dist/src/tui/dino/persistence.d.ts.map +1 -0
  33. package/dist/src/tui/dino/persistence.js +36 -0
  34. package/dist/src/tui/dino/persistence.js.map +1 -0
  35. package/dist/src/tui/dino/render.d.ts +19 -0
  36. package/dist/src/tui/dino/render.d.ts.map +1 -0
  37. package/dist/src/tui/dino/render.js +164 -0
  38. package/dist/src/tui/dino/render.js.map +1 -0
  39. package/dist/src/tui/dino/state.d.ts +141 -0
  40. package/dist/src/tui/dino/state.d.ts.map +1 -0
  41. package/dist/src/tui/dino/state.js +156 -0
  42. package/dist/src/tui/dino/state.js.map +1 -0
  43. package/dist/src/tui/dino/tick.d.ts +10 -0
  44. package/dist/src/tui/dino/tick.d.ts.map +1 -0
  45. package/dist/src/tui/dino/tick.js +117 -0
  46. package/dist/src/tui/dino/tick.js.map +1 -0
  47. package/dist/src/tui/dino/visibility.d.ts +2 -0
  48. package/dist/src/tui/dino/visibility.d.ts.map +1 -0
  49. package/dist/src/tui/dino/visibility.js +18 -0
  50. package/dist/src/tui/dino/visibility.js.map +1 -0
  51. package/dist/src/tui/key-handlers.d.ts +7 -0
  52. package/dist/src/tui/key-handlers.d.ts.map +1 -1
  53. package/dist/src/tui/key-handlers.js +25 -1
  54. package/dist/src/tui/key-handlers.js.map +1 -1
  55. package/dist/src/tui/layout.d.ts +7 -0
  56. package/dist/src/tui/layout.d.ts.map +1 -1
  57. package/dist/src/tui/layout.js +10 -0
  58. package/dist/src/tui/layout.js.map +1 -1
  59. package/dist/src/tui/status-controller.d.ts +9 -0
  60. package/dist/src/tui/status-controller.d.ts.map +1 -1
  61. package/dist/src/tui/status-controller.js +28 -0
  62. package/dist/src/tui/status-controller.js.map +1 -1
  63. package/dist/src/turn-runner/prompts.js +1 -1
  64. package/dist/src/turn-runner/prompts.js.map +1 -1
  65. package/dist/src/turn-runner/tools.js +17 -21
  66. package/dist/src/turn-runner/tools.js.map +1 -1
  67. package/dist/src/turn-runner/turn-runner.d.ts.map +1 -1
  68. package/dist/src/turn-runner/turn-runner.js +1 -0
  69. package/dist/src/turn-runner/turn-runner.js.map +1 -1
  70. package/package.json +1 -1
@@ -0,0 +1,141 @@
1
+ /** Default logical width of the playfield in character cells. Used as the
2
+ * initial value of `GameState.fieldWidth`; the panel updates the live
3
+ * field width from the renderer on construction and on every resize so
4
+ * the game fills the available column. */
5
+ export declare const DEFAULT_FIELD_WIDTH = 60;
6
+ /** Minimum field width we will render at. Below this the playfield gets
7
+ * uncomfortably tight for the dino + grace window + spawned obstacles. */
8
+ export declare const MIN_FIELD_WIDTH = 40;
9
+ /** Maximum field width we will render at. Above this the obstacles get so
10
+ * far apart that the game stops feeling like Chrome's dino. The cap is
11
+ * generous so ultra-wide terminals still look full. */
12
+ export declare const MAX_FIELD_WIDTH = 240;
13
+ /** @deprecated Use `state.fieldWidth`. Kept as a re-export of the default
14
+ * so older call sites and tests continue to compile against a single
15
+ * width number. */
16
+ export declare const FIELD_WIDTH = 60;
17
+ /** Row index of the ground line within the rendered panel (0-indexed from
18
+ * the top of the 12-row panel). The dino's `y` is measured upward from
19
+ * here so a positive `y` means "in the air". */
20
+ export declare const GROUND_ROW = 8;
21
+ /** Horizontal cell the dino occupies; obstacles scroll past this column. */
22
+ export declare const DINO_X = 6;
23
+ /** Gravity applied per tick to `dinoVy`, in cells/tick². Tuned with
24
+ * `JUMP_VELOCITY` so a single press clears a small cactus and the dino
25
+ * lands in ~0.45s — matching the snappy feel of Chrome's offline dino
26
+ * rather than a floaty Mario jump. Peak height ≈ v²/(2g) ≈ 4.9 cells. */
27
+ export declare const GRAVITY = 0.75;
28
+ /** Upward velocity applied on a jump press while grounded, in cells/tick.
29
+ * Combined with `GRAVITY` this gives a tight ~7.2-tick airtime so the
30
+ * rhythm matches Chrome's game. */
31
+ export declare const JUMP_VELOCITY = 2.7;
32
+ /** Base horizontal scroll speed in cells/tick. */
33
+ export declare const BASE_SPEED = 0.6;
34
+ /** Cap so the game stays playable on a single jump arc no matter how long
35
+ * the run lasts. */
36
+ export declare const MAX_SPEED = 1.6;
37
+ /** Score gained per advanced tick at base speed. Scaled with current speed
38
+ * so faster runs score faster, matching Chrome's behavior. */
39
+ export declare const SCORE_PER_TICK = 0.1;
40
+ /** Minimum horizontal gap between consecutive obstacles, expressed in
41
+ * cells, before the spawner is allowed to drop another one. */
42
+ export declare const MIN_OBSTACLE_GAP = 14;
43
+ /** Maximum extra gap added on top of the minimum, chosen with a per-spawn
44
+ * random roll so the cadence does not feel metronomic. */
45
+ export declare const MAX_EXTRA_GAP = 18;
46
+ /** Cells of obstacle-free runway granted ahead of the dino on resume after
47
+ * an automatic freeze. Set to ~1.5s worth of cells at base speed. */
48
+ export declare const GRACE_DISTANCE_CELLS = 14;
49
+ /** Maximum distance ahead of the dino at which the FIRST obstacle of a run
50
+ * is allowed to spawn, in cells. Without this cap the first obstacle
51
+ * always spawns at the right edge of the playfield, so on a 200-cell-wide
52
+ * terminal the user waits ~23 seconds before anything reaches them. With
53
+ * the cap, wide screens still see the obstacle enter from the right but
54
+ * no further out than ~4 seconds of runway. Narrow screens (where the
55
+ * edge is already closer than this) are unaffected. */
56
+ export declare const FIRST_OBSTACLE_MAX_DISTANCE = 36;
57
+ export interface Obstacle {
58
+ /** Sub-cell horizontal position. Decreases as the world scrolls left. */
59
+ x: number;
60
+ /** Cactus height in cells. 1 = small, 2 = tall. v1 ships with one size
61
+ * but the field is in place so a future variant can differ. */
62
+ height: number;
63
+ }
64
+ export type Phase = {
65
+ kind: "idle";
66
+ } | {
67
+ kind: "running";
68
+ } | {
69
+ kind: "countdown";
70
+ ticksRemaining: number;
71
+ } | {
72
+ kind: "grace";
73
+ } | {
74
+ kind: "frozen";
75
+ } | {
76
+ kind: "gameover";
77
+ };
78
+ export interface GameState {
79
+ phase: Phase;
80
+ /** Score accumulates only during `running`. Frozen as an integer for the
81
+ * UI but kept as a float here so sub-tick scaling is preserved. */
82
+ score: number;
83
+ /** Best score observed this process. Hydrated from `~/.duet` at panel
84
+ * construction; persisted whenever a run ends with `score > highScore`. */
85
+ highScore: number;
86
+ /** Current scroll speed in cells/tick. Ramps from BASE_SPEED toward
87
+ * MAX_SPEED as score grows. */
88
+ speed: number;
89
+ /** Dino vertical position above the ground in cells. `0` = grounded. */
90
+ dinoY: number;
91
+ /** Dino vertical velocity in cells/tick. Positive = ascending. */
92
+ dinoVy: number;
93
+ /** Obstacles ordered left-to-right by `x`. The spawner appends to the
94
+ * tail; the tick removes from the head once an obstacle scrolls past
95
+ * `x < -2`. */
96
+ obstacles: Obstacle[];
97
+ /** Cells of runway still owed to the dino during the post-countdown
98
+ * grace period. Decremented each tick; transitions back to `running`
99
+ * when it reaches zero. */
100
+ graceCellsLeft: number;
101
+ /** Cells of horizontal distance until the spawner is allowed to emit
102
+ * the next obstacle. Decremented each tick the world advances. */
103
+ cellsUntilNextSpawn: number;
104
+ /** Live width of the playfield in cells. The panel keeps this in sync
105
+ * with the renderer's terminal width on construction and on resize so
106
+ * the game stretches to fill the available column. Obstacles spawn at
107
+ * the right edge of this window. */
108
+ fieldWidth: number;
109
+ }
110
+ export declare function initialState(highScore: number, fieldWidth?: number): GameState;
111
+ /** Resize reducer. Called by the panel when the terminal resizes; clamps
112
+ * to the supported range and trims any obstacles that fell off the new
113
+ * right edge so a shrink doesn't leave hazards floating in space. */
114
+ export declare function setFieldWidth(state: GameState, fieldWidth: number): GameState;
115
+ export declare function clampFieldWidth(width: number): number;
116
+ /** Start a fresh run from `idle` or `gameover`. Preserves `highScore` and
117
+ * the current `fieldWidth` so resizes during gameover persist into the
118
+ * next run. */
119
+ export declare function startRun(state: GameState): GameState;
120
+ /** Snap to the ground and stop the world. Called from the panel's
121
+ * `freeze()` so a long agent turn does not leave the dino hanging
122
+ * mid-jump. */
123
+ export declare function freezeRun(state: GameState): GameState;
124
+ /** Decide what should happen when the user re-expands the dino panel
125
+ * while a run is in progress. Three outcomes:
126
+ * - "countdown": agent-induced freeze → run the 3-2-1 + grace gap.
127
+ * - "run": user-induced collapse → instant resume.
128
+ * - "noop": nothing in flight (idle/gameover/countdown/grace/running).
129
+ * Extracted as a pure helper so the panel's toggle flow is unit-testable
130
+ * without spinning up a CliRenderer. */
131
+ export declare function expandResumeKind(phase: GameState["phase"], frozenByAgent: boolean): "countdown" | "run" | "noop";
132
+ /** Begin the 3-2-1 countdown after an automatic freeze ends. The countdown
133
+ * itself counts down by tick in `tick.ts` so this just seeds the phase. */
134
+ export declare function beginCountdown(state: GameState, ticksPerSecond: number): GameState;
135
+ /** Transition from countdown into the grace gap. The grace gap pushes any
136
+ * obstacle inside `GRACE_DISTANCE_CELLS` of the dino's column out to the
137
+ * far edge of the grace window so the run resumes without an unfair hit. */
138
+ export declare function beginGrace(state: GameState): GameState;
139
+ export declare function endRunOnHit(state: GameState): GameState;
140
+ export declare function isWorldAdvancing(phase: Phase): boolean;
141
+ //# sourceMappingURL=state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../../src/tui/dino/state.ts"],"names":[],"mappings":"AAGA;;;2CAG2C;AAC3C,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC;2EAC2E;AAC3E,eAAO,MAAM,eAAe,KAAK,CAAC;AAElC;;wDAEwD;AACxD,eAAO,MAAM,eAAe,MAAM,CAAC;AAEnC;;oBAEoB;AACpB,eAAO,MAAM,WAAW,KAAsB,CAAC;AAE/C;;iDAEiD;AACjD,eAAO,MAAM,UAAU,IAAI,CAAC;AAE5B,4EAA4E;AAC5E,eAAO,MAAM,MAAM,IAAI,CAAC;AAExB;;;0EAG0E;AAC1E,eAAO,MAAM,OAAO,OAAO,CAAC;AAE5B;;oCAEoC;AACpC,eAAO,MAAM,aAAa,MAAM,CAAC;AAEjC,kDAAkD;AAClD,eAAO,MAAM,UAAU,MAAM,CAAC;AAE9B;qBACqB;AACrB,eAAO,MAAM,SAAS,MAAM,CAAC;AAE7B;+DAC+D;AAC/D,eAAO,MAAM,cAAc,MAAM,CAAC;AAElC;gEACgE;AAChE,eAAO,MAAM,gBAAgB,KAAK,CAAC;AAEnC;2DAC2D;AAC3D,eAAO,MAAM,aAAa,KAAK,CAAC;AAEhC;sEACsE;AACtE,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC;;;;;;wDAMwD;AACxD,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAE9C,MAAM,WAAW,QAAQ;IACvB,yEAAyE;IACzE,CAAC,EAAE,MAAM,CAAC;IACV;oEACgE;IAChE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,KAAK,GACb;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,GAC7C;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC;AAEzB,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,KAAK,CAAC;IACb;wEACoE;IACpE,KAAK,EAAE,MAAM,CAAC;IACd;gFAC4E;IAC5E,SAAS,EAAE,MAAM,CAAC;IAClB;oCACgC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAC;IACd,kEAAkE;IAClE,MAAM,EAAE,MAAM,CAAC;IACf;;oBAEgB;IAChB,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB;;gCAE4B;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB;uEACmE;IACnE,mBAAmB,EAAE,MAAM,CAAC;IAC5B;;;yCAGqC;IACrC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,YAAY,CAC1B,SAAS,EAAE,MAAM,EACjB,UAAU,GAAE,MAA4B,GACvC,SAAS,CAaX;AAED;;sEAEsE;AACtE,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,GAAG,SAAS,CAO7E;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGrD;AAED;;gBAEgB;AAChB,wBAAgB,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,CAKpD;AAED;;gBAEgB;AAChB,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,CAQrD;AAED;;;;;;yCAMyC;AACzC,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,SAAS,CAAC,OAAO,CAAC,EACzB,aAAa,EAAE,OAAO,GACrB,WAAW,GAAG,KAAK,GAAG,MAAM,CAG9B;AAED;4EAC4E;AAC5E,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,GAAG,SAAS,CAKlF;AAED;;6EAE6E;AAC7E,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,CAStD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,CAOvD;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAEtD"}
@@ -0,0 +1,156 @@
1
+ // Pure GameState for the dino panel. No timers, no I/O, no OpenTUI imports.
2
+ // All transitions are reducer-style: state in, state out.
3
+ /** Default logical width of the playfield in character cells. Used as the
4
+ * initial value of `GameState.fieldWidth`; the panel updates the live
5
+ * field width from the renderer on construction and on every resize so
6
+ * the game fills the available column. */
7
+ export const DEFAULT_FIELD_WIDTH = 60;
8
+ /** Minimum field width we will render at. Below this the playfield gets
9
+ * uncomfortably tight for the dino + grace window + spawned obstacles. */
10
+ export const MIN_FIELD_WIDTH = 40;
11
+ /** Maximum field width we will render at. Above this the obstacles get so
12
+ * far apart that the game stops feeling like Chrome's dino. The cap is
13
+ * generous so ultra-wide terminals still look full. */
14
+ export const MAX_FIELD_WIDTH = 240;
15
+ /** @deprecated Use `state.fieldWidth`. Kept as a re-export of the default
16
+ * so older call sites and tests continue to compile against a single
17
+ * width number. */
18
+ export const FIELD_WIDTH = DEFAULT_FIELD_WIDTH;
19
+ /** Row index of the ground line within the rendered panel (0-indexed from
20
+ * the top of the 12-row panel). The dino's `y` is measured upward from
21
+ * here so a positive `y` means "in the air". */
22
+ export const GROUND_ROW = 8;
23
+ /** Horizontal cell the dino occupies; obstacles scroll past this column. */
24
+ export const DINO_X = 6;
25
+ /** Gravity applied per tick to `dinoVy`, in cells/tick². Tuned with
26
+ * `JUMP_VELOCITY` so a single press clears a small cactus and the dino
27
+ * lands in ~0.45s — matching the snappy feel of Chrome's offline dino
28
+ * rather than a floaty Mario jump. Peak height ≈ v²/(2g) ≈ 4.9 cells. */
29
+ export const GRAVITY = 0.75;
30
+ /** Upward velocity applied on a jump press while grounded, in cells/tick.
31
+ * Combined with `GRAVITY` this gives a tight ~7.2-tick airtime so the
32
+ * rhythm matches Chrome's game. */
33
+ export const JUMP_VELOCITY = 2.7;
34
+ /** Base horizontal scroll speed in cells/tick. */
35
+ export const BASE_SPEED = 0.6;
36
+ /** Cap so the game stays playable on a single jump arc no matter how long
37
+ * the run lasts. */
38
+ export const MAX_SPEED = 1.6;
39
+ /** Score gained per advanced tick at base speed. Scaled with current speed
40
+ * so faster runs score faster, matching Chrome's behavior. */
41
+ export const SCORE_PER_TICK = 0.1;
42
+ /** Minimum horizontal gap between consecutive obstacles, expressed in
43
+ * cells, before the spawner is allowed to drop another one. */
44
+ export const MIN_OBSTACLE_GAP = 14;
45
+ /** Maximum extra gap added on top of the minimum, chosen with a per-spawn
46
+ * random roll so the cadence does not feel metronomic. */
47
+ export const MAX_EXTRA_GAP = 18;
48
+ /** Cells of obstacle-free runway granted ahead of the dino on resume after
49
+ * an automatic freeze. Set to ~1.5s worth of cells at base speed. */
50
+ export const GRACE_DISTANCE_CELLS = 14;
51
+ /** Maximum distance ahead of the dino at which the FIRST obstacle of a run
52
+ * is allowed to spawn, in cells. Without this cap the first obstacle
53
+ * always spawns at the right edge of the playfield, so on a 200-cell-wide
54
+ * terminal the user waits ~23 seconds before anything reaches them. With
55
+ * the cap, wide screens still see the obstacle enter from the right but
56
+ * no further out than ~4 seconds of runway. Narrow screens (where the
57
+ * edge is already closer than this) are unaffected. */
58
+ export const FIRST_OBSTACLE_MAX_DISTANCE = 36;
59
+ export function initialState(highScore, fieldWidth = DEFAULT_FIELD_WIDTH) {
60
+ return {
61
+ phase: { kind: "idle" },
62
+ score: 0,
63
+ highScore,
64
+ speed: BASE_SPEED,
65
+ dinoY: 0,
66
+ dinoVy: 0,
67
+ obstacles: [],
68
+ graceCellsLeft: 0,
69
+ cellsUntilNextSpawn: MIN_OBSTACLE_GAP,
70
+ fieldWidth: clampFieldWidth(fieldWidth),
71
+ };
72
+ }
73
+ /** Resize reducer. Called by the panel when the terminal resizes; clamps
74
+ * to the supported range and trims any obstacles that fell off the new
75
+ * right edge so a shrink doesn't leave hazards floating in space. */
76
+ export function setFieldWidth(state, fieldWidth) {
77
+ const next = clampFieldWidth(fieldWidth);
78
+ if (next === state.fieldWidth)
79
+ return state;
80
+ // Drop obstacles past the new right edge; the spawner will refill from
81
+ // the new edge on the next tick.
82
+ const obstacles = state.obstacles.filter((o) => o.x < next);
83
+ return { ...state, fieldWidth: next, obstacles };
84
+ }
85
+ export function clampFieldWidth(width) {
86
+ if (!Number.isFinite(width))
87
+ return DEFAULT_FIELD_WIDTH;
88
+ return Math.max(MIN_FIELD_WIDTH, Math.min(MAX_FIELD_WIDTH, Math.floor(width)));
89
+ }
90
+ /** Start a fresh run from `idle` or `gameover`. Preserves `highScore` and
91
+ * the current `fieldWidth` so resizes during gameover persist into the
92
+ * next run. */
93
+ export function startRun(state) {
94
+ return {
95
+ ...initialState(state.highScore, state.fieldWidth),
96
+ phase: { kind: "running" },
97
+ };
98
+ }
99
+ /** Snap to the ground and stop the world. Called from the panel's
100
+ * `freeze()` so a long agent turn does not leave the dino hanging
101
+ * mid-jump. */
102
+ export function freezeRun(state) {
103
+ if (state.phase.kind === "frozen" || state.phase.kind === "idle")
104
+ return state;
105
+ return {
106
+ ...state,
107
+ phase: { kind: "frozen" },
108
+ dinoY: 0,
109
+ dinoVy: 0,
110
+ };
111
+ }
112
+ /** Decide what should happen when the user re-expands the dino panel
113
+ * while a run is in progress. Three outcomes:
114
+ * - "countdown": agent-induced freeze → run the 3-2-1 + grace gap.
115
+ * - "run": user-induced collapse → instant resume.
116
+ * - "noop": nothing in flight (idle/gameover/countdown/grace/running).
117
+ * Extracted as a pure helper so the panel's toggle flow is unit-testable
118
+ * without spinning up a CliRenderer. */
119
+ export function expandResumeKind(phase, frozenByAgent) {
120
+ if (phase.kind !== "frozen")
121
+ return "noop";
122
+ return frozenByAgent ? "countdown" : "run";
123
+ }
124
+ /** Begin the 3-2-1 countdown after an automatic freeze ends. The countdown
125
+ * itself counts down by tick in `tick.ts` so this just seeds the phase. */
126
+ export function beginCountdown(state, ticksPerSecond) {
127
+ return {
128
+ ...state,
129
+ phase: { kind: "countdown", ticksRemaining: ticksPerSecond * 3 },
130
+ };
131
+ }
132
+ /** Transition from countdown into the grace gap. The grace gap pushes any
133
+ * obstacle inside `GRACE_DISTANCE_CELLS` of the dino's column out to the
134
+ * far edge of the grace window so the run resumes without an unfair hit. */
135
+ export function beginGrace(state) {
136
+ const graceEdge = DINO_X + GRACE_DISTANCE_CELLS;
137
+ const shifted = state.obstacles.map((o) => (o.x < graceEdge ? { ...o, x: graceEdge + 2 } : o));
138
+ return {
139
+ ...state,
140
+ obstacles: shifted,
141
+ phase: { kind: "grace" },
142
+ graceCellsLeft: GRACE_DISTANCE_CELLS,
143
+ };
144
+ }
145
+ export function endRunOnHit(state) {
146
+ const finalScore = Math.floor(state.score);
147
+ return {
148
+ ...state,
149
+ phase: { kind: "gameover" },
150
+ highScore: Math.max(state.highScore, finalScore),
151
+ };
152
+ }
153
+ export function isWorldAdvancing(phase) {
154
+ return phase.kind === "running" || phase.kind === "grace";
155
+ }
156
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../../../../src/tui/dino/state.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { type GameState } from "./state.js";
2
+ export interface TickDeps {
3
+ /** Returns a float in [0, 1). Injected so tests can seed the spawner. */
4
+ random: () => number;
5
+ /** Ticks per second the panel is calling us at; used to step the
6
+ * countdown phase down in wall-clock-aligned increments. */
7
+ ticksPerSecond: number;
8
+ }
9
+ export declare function tick(state: GameState, deps: TickDeps): GameState;
10
+ //# sourceMappingURL=tick.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tick.d.ts","sourceRoot":"","sources":["../../../../src/tui/dino/tick.ts"],"names":[],"mappings":"AAIA,OAAO,EAYL,KAAK,SAAS,EAEf,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,QAAQ;IACvB,yEAAyE;IACzE,MAAM,EAAE,MAAM,MAAM,CAAC;IACrB;iEAC6D;IAC7D,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,GAAG,SAAS,CA4HhE"}
@@ -0,0 +1,117 @@
1
+ // One physics tick. Pure: takes current state + a random source and the
2
+ // per-tick cadence, returns the next state. The panel owns the timer that
3
+ // calls this; tests call it directly with a seeded RNG.
4
+ import { BASE_SPEED, DINO_X, FIRST_OBSTACLE_MAX_DISTANCE, GRAVITY, MAX_EXTRA_GAP, MAX_SPEED, MIN_OBSTACLE_GAP, SCORE_PER_TICK, beginGrace, endRunOnHit, isWorldAdvancing, } from "./state.js";
5
+ export function tick(state, deps) {
6
+ if (state.phase.kind === "countdown") {
7
+ const ticksRemaining = state.phase.ticksRemaining - 1;
8
+ if (ticksRemaining <= 0) {
9
+ // Countdown finished; enter the grace gap. `beginGrace` shifts any
10
+ // nearby obstacles to the edge of the grace window so the dino does
11
+ // not wake up onto a cactus.
12
+ return beginGrace(state);
13
+ }
14
+ return { ...state, phase: { kind: "countdown", ticksRemaining } };
15
+ }
16
+ if (state.phase.kind === "idle" ||
17
+ state.phase.kind === "gameover" ||
18
+ state.phase.kind === "frozen") {
19
+ return state;
20
+ }
21
+ // Physics for running / grace. The dino integrates regardless so a jump
22
+ // initiated mid-grace still arcs correctly.
23
+ const nextDinoVy = state.dinoVy - GRAVITY;
24
+ let nextDinoY = state.dinoY + state.dinoVy;
25
+ let landedVy = nextDinoVy;
26
+ if (nextDinoY <= 0) {
27
+ nextDinoY = 0;
28
+ landedVy = 0;
29
+ }
30
+ // World scroll. Running ticks accumulate score and ramp speed; grace
31
+ // ticks scroll obstacles by but score stays pinned (preventing pause
32
+ // farming) and the speed ramp is paused too.
33
+ const advancingWorld = isWorldAdvancing(state.phase);
34
+ const nextSpeed = state.phase.kind === "running"
35
+ ? Math.min(MAX_SPEED, BASE_SPEED + state.score * 0.0008)
36
+ : state.speed;
37
+ const scroll = advancingWorld ? nextSpeed : 0;
38
+ let nextObstacles = state.obstacles
39
+ .map((o) => ({ ...o, x: o.x - scroll }))
40
+ .filter((o) => o.x > -2);
41
+ // Spawner. Decrements the countdown to the next spawn by the scroll
42
+ // distance, then drops an obstacle at the right edge when it hits zero.
43
+ let cellsUntilNextSpawn = state.cellsUntilNextSpawn - scroll;
44
+ if (advancingWorld && cellsUntilNextSpawn <= 0) {
45
+ // Spawn at the live right edge for every obstacle except the first
46
+ // of a run. For the very first obstacle (no existing obstacles) we
47
+ // clamp the spawn to a sensible distance ahead of the dino so wide
48
+ // terminals don't make the player wait 20+ seconds for anything to
49
+ // appear. Narrow screens fall back to the right edge because the
50
+ // cap is already past it. After the first spawn the obstacle list
51
+ // is never empty during a run (the next spawn fires long before the
52
+ // previous obstacle scrolls off), so this naturally only fires once.
53
+ const isFirstSpawnOfRun = nextObstacles.length === 0;
54
+ const spawnX = isFirstSpawnOfRun
55
+ ? Math.min(state.fieldWidth, DINO_X + FIRST_OBSTACLE_MAX_DISTANCE)
56
+ : state.fieldWidth;
57
+ nextObstacles = [...nextObstacles, { x: spawnX, height: 1 }];
58
+ cellsUntilNextSpawn = MIN_OBSTACLE_GAP + deps.random() * MAX_EXTRA_GAP;
59
+ }
60
+ // Collision: only during `running`. The grace phase explicitly ignores
61
+ // collisions for `graceCellsLeft` cells so the resume is fair. The
62
+ // hitbox is narrower than the rendered sprite (which is 4 cells wide
63
+ // with a blank leading column) so collisions feel like Chrome's dino:
64
+ // forgiving on the edges, honest in the middle. dx covers the body
65
+ // columns DINO_X+1..DINO_X+2 where the actual ink lives.
66
+ const collided = state.phase.kind === "running" &&
67
+ nextObstacles.some((o) => {
68
+ const dx = o.x - DINO_X;
69
+ return dx > 0 && dx < 3 && nextDinoY < o.height;
70
+ });
71
+ if (collided) {
72
+ return endRunOnHit({
73
+ ...state,
74
+ obstacles: nextObstacles,
75
+ dinoY: nextDinoY,
76
+ dinoVy: landedVy,
77
+ speed: nextSpeed,
78
+ cellsUntilNextSpawn,
79
+ });
80
+ }
81
+ // Grace transition back to running once enough cells have scrolled.
82
+ if (state.phase.kind === "grace") {
83
+ const graceCellsLeft = state.graceCellsLeft - scroll;
84
+ if (graceCellsLeft <= 0) {
85
+ return {
86
+ ...state,
87
+ phase: { kind: "running" },
88
+ obstacles: nextObstacles,
89
+ dinoY: nextDinoY,
90
+ dinoVy: landedVy,
91
+ speed: nextSpeed,
92
+ graceCellsLeft: 0,
93
+ cellsUntilNextSpawn,
94
+ };
95
+ }
96
+ return {
97
+ ...state,
98
+ obstacles: nextObstacles,
99
+ dinoY: nextDinoY,
100
+ dinoVy: landedVy,
101
+ speed: nextSpeed,
102
+ graceCellsLeft,
103
+ cellsUntilNextSpawn,
104
+ };
105
+ }
106
+ // Plain running tick.
107
+ return {
108
+ ...state,
109
+ obstacles: nextObstacles,
110
+ dinoY: nextDinoY,
111
+ dinoVy: landedVy,
112
+ speed: nextSpeed,
113
+ score: state.score + SCORE_PER_TICK * (nextSpeed / BASE_SPEED),
114
+ cellsUntilNextSpawn,
115
+ };
116
+ }
117
+ //# sourceMappingURL=tick.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tick.js","sourceRoot":"","sources":["../../../../src/tui/dino/tick.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,0EAA0E;AAC1E,wDAAwD;AAExD,OAAO,EACL,UAAU,EACV,MAAM,EACN,2BAA2B,EAC3B,OAAO,EACP,aAAa,EACb,SAAS,EACT,gBAAgB,EAChB,cAAc,EACd,UAAU,EACV,WAAW,EACX,gBAAgB,GAGjB,MAAM,YAAY,CAAC;AAUpB,MAAM,UAAU,IAAI,CAAC,KAAgB,EAAE,IAAc;IACnD,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;QACrC,MAAM,cAAc,GAAG,KAAK,CAAC,KAAK,CAAC,cAAc,GAAG,CAAC,CAAC;QACtD,IAAI,cAAc,IAAI,CAAC,EAAE,CAAC;YACxB,mEAAmE;YACnE,oEAAoE;YACpE,6BAA6B;YAC7B,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC;QAC3B,CAAC;QACD,OAAO,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,cAAc,EAAE,EAAE,CAAC;IACpE,CAAC;IAED,IACE,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,MAAM;QAC3B,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,UAAU;QAC/B,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,QAAQ,EAC7B,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,wEAAwE;IACxE,4CAA4C;IAC5C,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,GAAG,OAAO,CAAC;IAC1C,IAAI,SAAS,GAAG,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC;IAC3C,IAAI,QAAQ,GAAG,UAAU,CAAC;IAC1B,IAAI,SAAS,IAAI,CAAC,EAAE,CAAC;QACnB,SAAS,GAAG,CAAC,CAAC;QACd,QAAQ,GAAG,CAAC,CAAC;IACf,CAAC;IAED,qEAAqE;IACrE,qEAAqE;IACrE,6CAA6C;IAC7C,MAAM,cAAc,GAAG,gBAAgB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACrD,MAAM,SAAS,GACb,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS;QAC5B,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,UAAU,GAAG,KAAK,CAAC,KAAK,GAAG,MAAM,CAAC;QACxD,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC;IAClB,MAAM,MAAM,GAAG,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;IAE9C,IAAI,aAAa,GAAe,KAAK,CAAC,SAAS;SAC5C,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,MAAM,EAAE,CAAC,CAAC;SACvC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAE3B,oEAAoE;IACpE,wEAAwE;IACxE,IAAI,mBAAmB,GAAG,KAAK,CAAC,mBAAmB,GAAG,MAAM,CAAC;IAC7D,IAAI,cAAc,IAAI,mBAAmB,IAAI,CAAC,EAAE,CAAC;QAC/C,mEAAmE;QACnE,mEAAmE;QACnE,mEAAmE;QACnE,mEAAmE;QACnE,iEAAiE;QACjE,kEAAkE;QAClE,oEAAoE;QACpE,qEAAqE;QACrE,MAAM,iBAAiB,GAAG,aAAa,CAAC,MAAM,KAAK,CAAC,CAAC;QACrD,MAAM,MAAM,GAAG,iBAAiB;YAC9B,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,EAAE,MAAM,GAAG,2BAA2B,CAAC;YAClE,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC;QACrB,aAAa,GAAG,CAAC,GAAG,aAAa,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;QAC7D,mBAAmB,GAAG,gBAAgB,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,aAAa,CAAC;IACzE,CAAC;IAED,uEAAuE;IACvE,mEAAmE;IACnE,qEAAqE;IACrE,sEAAsE;IACtE,mEAAmE;IACnE,yDAAyD;IACzD,MAAM,QAAQ,GACZ,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS;QAC9B,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;YACvB,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC;YACxB,OAAO,EAAE,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,SAAS,GAAG,CAAC,CAAC,MAAM,CAAC;QAClD,CAAC,CAAC,CAAC;IAEL,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,WAAW,CAAC;YACjB,GAAG,KAAK;YACR,SAAS,EAAE,aAAa;YACxB,KAAK,EAAE,SAAS;YAChB,MAAM,EAAE,QAAQ;YAChB,KAAK,EAAE,SAAS;YAChB,mBAAmB;SACpB,CAAC,CAAC;IACL,CAAC;IAED,oEAAoE;IACpE,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QACjC,MAAM,cAAc,GAAG,KAAK,CAAC,cAAc,GAAG,MAAM,CAAC;QACrD,IAAI,cAAc,IAAI,CAAC,EAAE,CAAC;YACxB,OAAO;gBACL,GAAG,KAAK;gBACR,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;gBAC1B,SAAS,EAAE,aAAa;gBACxB,KAAK,EAAE,SAAS;gBAChB,MAAM,EAAE,QAAQ;gBAChB,KAAK,EAAE,SAAS;gBAChB,cAAc,EAAE,CAAC;gBACjB,mBAAmB;aACpB,CAAC;QACJ,CAAC;QACD,OAAO;YACL,GAAG,KAAK;YACR,SAAS,EAAE,aAAa;YACxB,KAAK,EAAE,SAAS;YAChB,MAAM,EAAE,QAAQ;YAChB,KAAK,EAAE,SAAS;YAChB,cAAc;YACd,mBAAmB;SACpB,CAAC;IACJ,CAAC;IAED,sBAAsB;IACtB,OAAO;QACL,GAAG,KAAK;QACR,SAAS,EAAE,aAAa;QACxB,KAAK,EAAE,SAAS;QAChB,MAAM,EAAE,QAAQ;QAChB,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,KAAK,CAAC,KAAK,GAAG,cAAc,GAAG,CAAC,SAAS,GAAG,UAAU,CAAC;QAC9D,mBAAmB;KACpB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function panelVisibleRowCount(expanded: boolean, agentBusy: boolean): number;
2
+ //# sourceMappingURL=visibility.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visibility.d.ts","sourceRoot":"","sources":["../../../../src/tui/dino/visibility.ts"],"names":[],"mappings":"AAcA,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,GAAG,MAAM,CAGlF"}
@@ -0,0 +1,18 @@
1
+ // Pure visibility rule for the dino panel. Lives in its own file so tests
2
+ // can import it without dragging in OpenTUI (which the panel module
3
+ // pulls in at the top of `./index.ts`). Keeping this rule centralized
4
+ // means a regression that quietly makes the panel visible at rest fails
5
+ // a unit test instead of a manual dogfood pass.
6
+ //
7
+ // Contract:
8
+ // - agent idle → 0 rows (panel is invisible, reserves no
9
+ // vertical space).
10
+ // - agent busy + collapsed → COLLAPSED_ROWS rows (the Ctrl-G hint).
11
+ // - agent busy + expanded → EXPANDED_ROWS rows (the full game).
12
+ import { COLLAPSED_ROWS, EXPANDED_ROWS } from "./render.js";
13
+ export function panelVisibleRowCount(expanded, agentBusy) {
14
+ if (!agentBusy)
15
+ return 0;
16
+ return expanded ? EXPANDED_ROWS : COLLAPSED_ROWS;
17
+ }
18
+ //# sourceMappingURL=visibility.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visibility.js","sourceRoot":"","sources":["../../../../src/tui/dino/visibility.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,oEAAoE;AACpE,sEAAsE;AACtE,wEAAwE;AACxE,gDAAgD;AAChD,EAAE;AACF,YAAY;AACZ,yEAAyE;AACzE,kDAAkD;AAClD,wEAAwE;AACxE,qEAAqE;AAErE,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5D,MAAM,UAAU,oBAAoB,CAAC,QAAiB,EAAE,SAAkB;IACxE,IAAI,CAAC,SAAS;QAAE,OAAO,CAAC,CAAC;IACzB,OAAO,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,cAAc,CAAC;AACnD,CAAC"}
@@ -1,6 +1,7 @@
1
1
  import type { CliRenderer, ScrollBoxRenderable, TextareaRenderable } from "@opentui/core";
2
2
  import type { AutocompleteController } from "./autocomplete-controller.js";
3
3
  import type { CopyController } from "./copy-controller.js";
4
+ import type { DinoPanel } from "./dino/index.js";
4
5
  import type { PasteController } from "./paste-controller.js";
5
6
  import type { QuestionPicker } from "./question-picker.js";
6
7
  import type { StarterSection } from "./starter-section.js";
@@ -31,6 +32,12 @@ export interface KeyHandlerDeps {
31
32
  onEscape(): void;
32
33
  /** Dispatch the composer text with behavior:"steer" (Ctrl+Enter). */
33
34
  onSteer(): void;
35
+ /** Dino "while-you-wait" panel. The panel always owns Ctrl-G, which
36
+ * cycles collapsed → expanded+game-focused → expanded+composer-focused
37
+ * → collapsed. The only game keystroke is ArrowUp; the spacebar is
38
+ * always the composer's so a half-typed follow-up can coexist with
39
+ * the running game. Ctrl-G hands the up-arrow back and forth. */
40
+ dinoPanel: DinoPanel;
34
41
  }
35
42
  /**
36
43
  * Wires up the two keyboard event surfaces the TUI listens on:
@@ -1 +1 @@
1
- {"version":3,"file":"key-handlers.d.ts","sourceRoot":"","sources":["../../../src/tui/key-handlers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAY,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACpG,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAC3E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE/D;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,OAAO,CAAC;CACnB;AAMD,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,WAAW,CAAC;IACtB,UAAU,EAAE,kBAAkB,CAAC;IAC/B,UAAU,EAAE,mBAAmB,CAAC;IAChC,YAAY,EAAE,sBAAsB,CAAC;IACrC,cAAc,EAAE,cAAc,CAAC;IAC/B,eAAe,EAAE,eAAe,CAAC;IACjC,cAAc,EAAE,cAAc,CAAC;IAC/B,QAAQ,EAAE,cAAc,GAAG,SAAS,CAAC;IACrC,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,WAAW,EAAE,qBAAqB,CAAC;IACnC,wEAAwE;IACxE,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,uDAAuD;IACvD,QAAQ,IAAI,IAAI,CAAC;IACjB,qEAAqE;IACrE,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI,CAkM7D"}
1
+ {"version":3,"file":"key-handlers.d.ts","sourceRoot":"","sources":["../../../src/tui/key-handlers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAY,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACpG,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAC3E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE/D;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,OAAO,CAAC;CACnB;AAMD,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,WAAW,CAAC;IACtB,UAAU,EAAE,kBAAkB,CAAC;IAC/B,UAAU,EAAE,mBAAmB,CAAC;IAChC,YAAY,EAAE,sBAAsB,CAAC;IACrC,cAAc,EAAE,cAAc,CAAC;IAC/B,eAAe,EAAE,eAAe,CAAC;IACjC,cAAc,EAAE,cAAc,CAAC;IAC/B,QAAQ,EAAE,cAAc,GAAG,SAAS,CAAC;IACrC,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,WAAW,EAAE,qBAAqB,CAAC;IACnC,wEAAwE;IACxE,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,uDAAuD;IACvD,QAAQ,IAAI,IAAI,CAAC;IACjB,qEAAqE;IACrE,OAAO,IAAI,IAAI,CAAC;IAChB;;;;sEAIkE;IAClE,SAAS,EAAE,SAAS,CAAC;CACtB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI,CA6N7D"}
@@ -12,7 +12,7 @@
12
12
  * sketch and key handling can be reasoned about in one place.
13
13
  */
14
14
  export function installKeyHandlers(deps) {
15
- const { renderer, inputField, transcript, autocomplete, questionPicker, pasteController, copyController, starters, transcriptWriter, escapeState, onSubmit, onEscape, onSteer, } = deps;
15
+ const { renderer, inputField, transcript, autocomplete, questionPicker, pasteController, copyController, starters, transcriptWriter, escapeState, onSubmit, onEscape, onSteer, dinoPanel, } = deps;
16
16
  const keyHandler = renderer._keyHandler;
17
17
  keyHandler.onInternal("keypress", (key) => {
18
18
  transcriptWriter.logKey("global", key);
@@ -61,6 +61,30 @@ export function installKeyHandlers(deps) {
61
61
  // fires, so we intercept at the Renderable's onKeyDown hook which runs first.
62
62
  inputField.onKeyDown = (key) => {
63
63
  transcriptWriter.logKey("keydown", key);
64
+ // Ctrl-G: the dino panel always owns this keystroke regardless of
65
+ // focus, the agent's running state, or whether the composer has
66
+ // typed text. Toggling the panel must never be eaten by autocomplete
67
+ // or starter chrome below.
68
+ if (key.ctrl && (key.name === "g" || key.sequence === "\u0007")) {
69
+ key.preventDefault();
70
+ dinoPanel.toggle();
71
+ return;
72
+ }
73
+ // Forward ArrowUp to the dino panel whenever it is expanded AND
74
+ // game-focused. Spacebar is deliberately not a game key: users
75
+ // frequently type a follow-up while the game keeps running, and
76
+ // letting the composer always own space avoids the awkward case of
77
+ // a stray jump while typing. We deliberately do not gate on
78
+ // `statusController.isRunning()` so the game is fully testable at
79
+ // rest.
80
+ if (dinoPanel.isGameFocused()) {
81
+ if (key.name === "up" && !key.ctrl && !key.meta && !key.super && !key.shift) {
82
+ if (dinoPanel.handleKey(key.name)) {
83
+ key.preventDefault();
84
+ return;
85
+ }
86
+ }
87
+ }
64
88
  if (key.name === "pageup") {
65
89
  scrollByPage(-1);
66
90
  key.preventDefault();
@@ -1 +1 @@
1
- {"version":3,"file":"key-handlers.js","sourceRoot":"","sources":["../../../src/tui/key-handlers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"key-handlers.js","sourceRoot":"","sources":["../../../src/tui/key-handlers.ts"],"names":[],"mappings":"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"}
@@ -31,6 +31,13 @@ export interface LayoutRefs {
31
31
  status: TextRenderable;
32
32
  /** Single-line hint row showing the active keystrokes (Enter / Esc / copy). */
33
33
  hint: TextRenderable;
34
+ /** Mount point for the dino "while-you-wait" game panel. Sits between
35
+ * the hint row and the autocomplete pickers so it never overlaps the
36
+ * input box. The dino factory adds its own rows into this container;
37
+ * layout doesn't reserve a height because the panel sizes itself from
38
+ * its children (12 rows when expanded, 1 when collapsed, 0 when
39
+ * never opened). */
40
+ dinoPanel: BoxRenderable;
34
41
  /** Slash + skill autocomplete panel; toggled via `visible` from the autocomplete controller. */
35
42
  skillAutocompletePanel: BoxRenderable;
36
43
  /** Header row for the "commands" section of the slash/skill picker. */
@@ -1 +1 @@
1
- {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../src/tui/layout.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,KAAK,WAAW,EAChB,mBAAmB,EACnB,cAAc,EACd,kBAAkB,EACnB,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAO7C;;;;GAIG;AACH,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,mEAAmE;IACnE,IAAI,EAAE,aAAa,CAAC;IACpB,6DAA6D;IAC7D,MAAM,EAAE,aAAa,CAAC;IACtB,qEAAqE;IACrE,OAAO,EAAE,aAAa,CAAC;IACvB;6EACyE;IACzE,gBAAgB,EAAE,aAAa,CAAC;IAChC;oCACgC;IAChC,oBAAoB,EAAE,cAAc,CAAC;IACrC,8FAA8F;IAC9F,UAAU,EAAE,mBAAmB,CAAC;IAChC,0FAA0F;IAC1F,MAAM,EAAE,cAAc,CAAC;IACvB,+EAA+E;IAC/E,IAAI,EAAE,cAAc,CAAC;IACrB,gGAAgG;IAChG,sBAAsB,EAAE,aAAa,CAAC;IACtC,uEAAuE;IACvE,aAAa,EAAE,cAAc,CAAC;IAC9B,iEAAiE;IACjE,WAAW,EAAE,cAAc,EAAE,CAAC;IAC9B,qEAAqE;IACrE,WAAW,EAAE,cAAc,CAAC;IAC5B,wDAAwD;IACxD,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,kEAAkE;IAClE,qBAAqB,EAAE,aAAa,CAAC;IACrC,oDAAoD;IACpD,qBAAqB,EAAE,cAAc,CAAC;IACtC,uDAAuD;IACvD,oBAAoB,EAAE,cAAc,EAAE,CAAC;IACvC,qFAAqF;IACrF,aAAa,EAAE,aAAa,CAAC;IAC7B,2EAA2E;IAC3E,aAAa,EAAE,cAAc,CAAC;IAC9B,mEAAmE;IACnE,cAAc,EAAE,cAAc,CAAC;IAC/B,uEAAuE;IACvE,YAAY,EAAE,cAAc,EAAE,CAAC;IAC/B;;;iEAG6D;IAC7D,aAAa,EAAE,aAAa,CAAC;IAC7B,yEAAyE;IACzE,iBAAiB,EAAE,cAAc,CAAC;IAClC,uEAAuE;IACvE,QAAQ,EAAE,aAAa,CAAC;IACxB,kFAAkF;IAClF,MAAM,EAAE,cAAc,CAAC;IACvB,6EAA6E;IAC7E,UAAU,EAAE,kBAAkB,CAAC;CAChC;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,WAAW,GAAG,UAAU,CA6U7D"}
1
+ {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../src/tui/layout.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,KAAK,WAAW,EAChB,mBAAmB,EACnB,cAAc,EACd,kBAAkB,EACnB,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAO7C;;;;GAIG;AACH,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,mEAAmE;IACnE,IAAI,EAAE,aAAa,CAAC;IACpB,6DAA6D;IAC7D,MAAM,EAAE,aAAa,CAAC;IACtB,qEAAqE;IACrE,OAAO,EAAE,aAAa,CAAC;IACvB;6EACyE;IACzE,gBAAgB,EAAE,aAAa,CAAC;IAChC;oCACgC;IAChC,oBAAoB,EAAE,cAAc,CAAC;IACrC,8FAA8F;IAC9F,UAAU,EAAE,mBAAmB,CAAC;IAChC,0FAA0F;IAC1F,MAAM,EAAE,cAAc,CAAC;IACvB,+EAA+E;IAC/E,IAAI,EAAE,cAAc,CAAC;IACrB;;;;;yBAKqB;IACrB,SAAS,EAAE,aAAa,CAAC;IACzB,gGAAgG;IAChG,sBAAsB,EAAE,aAAa,CAAC;IACtC,uEAAuE;IACvE,aAAa,EAAE,cAAc,CAAC;IAC9B,iEAAiE;IACjE,WAAW,EAAE,cAAc,EAAE,CAAC;IAC9B,qEAAqE;IACrE,WAAW,EAAE,cAAc,CAAC;IAC5B,wDAAwD;IACxD,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,kEAAkE;IAClE,qBAAqB,EAAE,aAAa,CAAC;IACrC,oDAAoD;IACpD,qBAAqB,EAAE,cAAc,CAAC;IACtC,uDAAuD;IACvD,oBAAoB,EAAE,cAAc,EAAE,CAAC;IACvC,qFAAqF;IACrF,aAAa,EAAE,aAAa,CAAC;IAC7B,2EAA2E;IAC3E,aAAa,EAAE,cAAc,CAAC;IAC9B,mEAAmE;IACnE,cAAc,EAAE,cAAc,CAAC;IAC/B,uEAAuE;IACvE,YAAY,EAAE,cAAc,EAAE,CAAC;IAC/B;;;iEAG6D;IAC7D,aAAa,EAAE,aAAa,CAAC;IAC7B,yEAAyE;IACzE,iBAAiB,EAAE,cAAc,CAAC;IAClC,uEAAuE;IACvE,QAAQ,EAAE,aAAa,CAAC;IACxB,kFAAkF;IAClF,MAAM,EAAE,cAAc,CAAC;IACvB,6EAA6E;IAC7E,UAAU,EAAE,kBAAkB,CAAC;CAChC;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,WAAW,GAAG,UAAU,CAwV7D"}
@@ -107,6 +107,14 @@ export function buildLayout(renderer) {
107
107
  flexShrink: 0,
108
108
  selectable: false,
109
109
  });
110
+ // Container for the dino game panel. The dino factory mounts its own
111
+ // row pool into this container; we keep it as a bare BoxRenderable so
112
+ // the dino module can own its sizing without layout having to know
113
+ // anything about the game.
114
+ const dinoPanel = new BoxRenderable(renderer, {
115
+ flexDirection: "column",
116
+ flexShrink: 0,
117
+ });
110
118
  const skillAutocompletePanel = new BoxRenderable(renderer, {
111
119
  flexDirection: "column",
112
120
  border: true,
@@ -295,6 +303,7 @@ export function buildLayout(renderer) {
295
303
  layout.add(transcriptFrame);
296
304
  layout.add(status);
297
305
  layout.add(hint);
306
+ layout.add(dinoPanel);
298
307
  layout.add(skillAutocompletePanel);
299
308
  layout.add(fileAutocompletePanel);
300
309
  layout.add(questionPanel);
@@ -313,6 +322,7 @@ export function buildLayout(renderer) {
313
322
  transcript,
314
323
  status,
315
324
  hint,
325
+ dinoPanel,
316
326
  skillAutocompletePanel,
317
327
  commandHeader,
318
328
  commandRows,