Skip to content

Commit e143db0

Browse files
Merge overlay fixture removal
2 parents 11b773a + f193c1f commit e143db0

9 files changed

Lines changed: 198 additions & 346 deletions

File tree

src/tui-opentui/command-surfaces.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,7 @@ function renderSettingsMenu(
504504
export function openSettingsSurface(shell: AppShell, deps: CommandSurfaceDeps): void {
505505
const settings = deps.settings
506506
if (settings === undefined) {
507-
openSettingsOverlay(shell)
507+
deps.notify("Settings are not available in this session.")
508508
return
509509
}
510510
if (deps.permissions === undefined) {

src/tui-opentui/demo.ts

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {
2020
attachSessionBridge,
2121
createRecordingPort,
2222
} from "./runtime-bridge.js"
23-
import { makeObserveFixture } from "./residuals.js"
23+
import type { ObserveSession } from "./residuals.js"
2424
import {
2525
openModelPickerOverlay,
2626
openOperatorOverlay,
@@ -32,15 +32,55 @@ import {
3232
createAppShell,
3333
enterSubagentObserve,
3434
openHelpOverlay,
35+
openListOverlay,
3536
openMentionsOverlay,
36-
openPluginsOverlay,
37-
openResumeOverlay,
3837
openSettingsOverlay,
3938
paintChrome,
4039
setChromeZones,
4140
setShellRunState,
4241
} from "./shell.js"
4342

43+
/** Demo-only rows: never shipped, just something to look at in `s`/`l`/`e`/`n`. */
44+
const DEMO_SETTINGS_ITEMS: readonly string[] = [
45+
"Permissions — revoke remembered approvals",
46+
"Compaction — summarize vs drop",
47+
"Session mode — auto / ask / plan",
48+
"Close settings",
49+
]
50+
51+
const DEMO_PLUGINS_ITEMS: readonly string[] = [
52+
"plugin:linear — enabled",
53+
"plugin:github — needs trust",
54+
"Close plugins",
55+
]
56+
57+
const DEMO_RESUME_ITEMS: readonly string[] = [
58+
"Fix permissions overflow · 2h ago · idle",
59+
"Wave 6 palette work · yesterday · done",
60+
"Close resume",
61+
]
62+
63+
const DEMO_MENTION_ITEMS: readonly string[] = [
64+
"@src/tui-opentui/shell.ts",
65+
"@AGENTS.md",
66+
"Close mentions",
67+
]
68+
69+
function demoObserveSession(): ObserveSession {
70+
return {
71+
sessionId: "child-1",
72+
agentId: "explore",
73+
description: "map callers of openListOverlay",
74+
lines: [
75+
{ role: "system", text: "— child session explore —" },
76+
{ role: "user", text: "find every openListOverlay caller" },
77+
{ role: "assistant", text: "Searching src/tui-opentui…" },
78+
{ role: "tool", text: "grep openListOverlay → 6 hits", meta: "tool.done" },
79+
{ role: "assistant", text: "Report ready for parent." },
80+
],
81+
}
82+
}
83+
4484
if (!process.stdout.isTTY) {
4585
console.error("demo requires a TTY (stdout is not a terminal)")
4686
process.exit(1)
@@ -163,7 +203,7 @@ renderer.keyInput.on("keypress", (key: KeyEvent) => {
163203
!key.meta &&
164204
shell.prompt.value.length === 0
165205
) {
166-
openSettingsOverlay(shell)
206+
openSettingsOverlay(shell, { items: DEMO_SETTINGS_ITEMS })
167207
return
168208
}
169209

@@ -183,7 +223,12 @@ renderer.keyInput.on("keypress", (key: KeyEvent) => {
183223
!key.meta &&
184224
shell.prompt.value.length === 0
185225
) {
186-
openPluginsOverlay(shell)
226+
openListOverlay(shell, {
227+
kind: "plugins",
228+
title: "plugins",
229+
items: DEMO_PLUGINS_ITEMS,
230+
frameId: "overlay-plugins",
231+
})
187232
return
188233
}
189234

@@ -193,7 +238,12 @@ renderer.keyInput.on("keypress", (key: KeyEvent) => {
193238
!key.meta &&
194239
shell.prompt.value.length === 0
195240
) {
196-
openResumeOverlay(shell)
241+
openListOverlay(shell, {
242+
kind: "resume",
243+
title: "resume session",
244+
items: DEMO_RESUME_ITEMS,
245+
frameId: "overlay-resume",
246+
})
197247
return
198248
}
199249

@@ -203,7 +253,7 @@ renderer.keyInput.on("keypress", (key: KeyEvent) => {
203253
!key.meta &&
204254
shell.prompt.value.length === 0
205255
) {
206-
openMentionsOverlay(shell)
256+
openMentionsOverlay(shell, { items: DEMO_MENTION_ITEMS })
207257
return
208258
}
209259

@@ -213,7 +263,7 @@ renderer.keyInput.on("keypress", (key: KeyEvent) => {
213263
!key.meta &&
214264
shell.prompt.value.length === 0
215265
) {
216-
enterSubagentObserve(shell, makeObserveFixture())
266+
enterSubagentObserve(shell, demoObserveSession())
217267
return
218268
}
219269

src/tui-opentui/observe-live.test.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ describe("palette observe action asks the host for a live session", () => {
358358
)
359359
})
360360

361-
test("falls back to the fixture when no host handler is set", async () => {
361+
test("stays out of observe with an honest message when no host handler is set", async () => {
362362
await withTestRenderer(
363363
async (h) => {
364364
const shell = createAppShell(h.renderer, {
@@ -367,7 +367,10 @@ describe("palette observe action asks the host for a live session", () => {
367367
})
368368
try {
369369
runPaletteAction(shell, "observe")
370-
expect(shell.observe?.sessionId).toBe("child-1")
370+
expect(shell.observe).toBeNull()
371+
expect(
372+
shell.streamLog.some((r) => r.text === "no subagent session to observe"),
373+
).toBe(true)
371374
} finally {
372375
shell.dispose()
373376
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/**
2+
* CL-5596: a missing surface dependency must produce an honest empty state,
3+
* never the hardcoded rows from residuals.ts rendered as if they were real.
4+
*/
5+
import { describe, expect, test } from "bun:test"
6+
7+
import { openSettingsSurface, type CommandSurfaceDeps } from "./command-surfaces.js"
8+
import { withTestRenderer } from "./harness.js"
9+
import { createAppShell } from "./shell.js"
10+
11+
describe("overlay dependency gaps never render fixture content", () => {
12+
test("settings surface without a settings dependency shows no fabricated rows", async () => {
13+
await withTestRenderer(
14+
async (h) => {
15+
const shell = createAppShell(h.renderer, {
16+
terminal: { columns: 80, rows: 24 },
17+
run: "idle",
18+
})
19+
try {
20+
const notified: string[] = []
21+
const deps: CommandSurfaceDeps = { notify: (text) => notified.push(text) }
22+
23+
openSettingsSurface(shell, deps)
24+
25+
expect(shell.overlayItems).not.toContain(
26+
"Permissions — revoke remembered approvals",
27+
)
28+
expect(shell.overlayItems).not.toContain("Compaction — summarize vs drop")
29+
expect(notified.length).toBeGreaterThan(0)
30+
} finally {
31+
shell.dispose()
32+
}
33+
},
34+
{ width: 80, height: 24 },
35+
)
36+
})
37+
})

src/tui-opentui/overlay-paint.test.ts

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ import {
1616
openListOverlay,
1717
openMentionsOverlay,
1818
openPalette,
19-
openPluginsOverlay,
20-
openResumeOverlay,
2119
openSettingsOverlay,
2220
setPromptModelLabel,
2321
type AppShell,
@@ -153,7 +151,10 @@ describe("overlay host never shares cells with the prompt border", () => {
153151
{ width: 60, height: 24 },
154152
] as const) {
155153
test(`mention popup with matches clears the prompt border at ${size.width}x${size.height}`, async () => {
156-
const { frame } = await paintOverlay((shell) => openMentionsOverlay(shell), size)
154+
const { frame } = await paintOverlay(
155+
(shell) => openMentionsOverlay(shell, { items: ["@src/file.ts", "@AGENTS.md"] }),
156+
size,
157+
)
157158

158159
// Every border rule stays a border rule: no list text glued onto it, and
159160
// the overlay host's own rules never share a row with the prompt box's.
@@ -170,11 +171,27 @@ describe("overlay host never shares cells with the prompt border", () => {
170171

171172
describe("every overlay kind paints clean rows", () => {
172173
const openers: readonly [string, (shell: AppShell) => void][] = [
173-
["settings", (s) => openSettingsOverlay(s)],
174+
["settings", (s) => openSettingsOverlay(s, { items: ["Compaction", "Close settings"] })],
174175
["help", (s) => openHelpOverlay(s)],
175-
["plugins", (s) => openPluginsOverlay(s)],
176-
["resume", (s) => openResumeOverlay(s)],
177-
["mentions", (s) => openMentionsOverlay(s)],
176+
[
177+
"plugins",
178+
(s) =>
179+
openListOverlay(s, {
180+
kind: "plugins",
181+
title: "plugins",
182+
items: ["plugin:linear — enabled", "Close plugins"],
183+
}),
184+
],
185+
[
186+
"resume",
187+
(s) =>
188+
openListOverlay(s, {
189+
kind: "resume",
190+
title: "resume session",
191+
items: ["Fix permissions overflow · 2h ago · idle", "Close resume"],
192+
}),
193+
],
194+
["mentions", (s) => openMentionsOverlay(s, { items: ["@src/file.ts", "Close mentions"] })],
178195
["palette", (s) => openPalette(s)],
179196
[
180197
"permissions",

src/tui-opentui/palette.ts

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,6 @@ export type PaletteActionId =
2121
| "copy_active"
2222
| "toggle_mouse"
2323
| "help"
24-
| "settings"
25-
| "plugins"
26-
| "resume"
2724
| "mentions"
2825
| "observe"
2926

@@ -37,9 +34,6 @@ const RESIDUAL_ACTION_IDS = new Set<string>([
3734
"copy_active",
3835
"toggle_mouse",
3936
"help",
40-
"settings",
41-
"plugins",
42-
"resume",
4337
"mentions",
4438
"observe",
4539
])
@@ -130,24 +124,6 @@ export const DEFAULT_PALETTE_COMMANDS: readonly PaletteCommand[] = [
130124
keywords: ["keys", "bindings", "help"],
131125
dispatch: "residual",
132126
},
133-
{
134-
id: "settings",
135-
label: "Open settings",
136-
keywords: ["config", "preferences", "options"],
137-
dispatch: "residual",
138-
},
139-
{
140-
id: "plugins",
141-
label: "Manage plugins",
142-
keywords: ["mcp", "extension", "plugin"],
143-
dispatch: "residual",
144-
},
145-
{
146-
id: "resume",
147-
label: "Resume prior session",
148-
keywords: ["history", "session", "picker"],
149-
dispatch: "residual",
150-
},
151127
{
152128
id: "mentions",
153129
label: "Insert file mention",

src/tui-opentui/residuals.ts

Lines changed: 5 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
/**
2-
* Wave 7 residual surface fixtures + observe session types (pure).
3-
* Shell openers inject host catalogs via OpenResidualListOpts; fixtures apply
4-
* only when the host omits `items`.
5-
*
6-
* Hosts can also build rows with {@link residualListFromCatalog} and resolve
7-
* accept callbacks via {@link residualIdFromSelection}.
2+
* Residual list-overlay helpers + observe session types (pure, production).
3+
* Hosts build rows with {@link residualListFromCatalog} and resolve accept
4+
* callbacks via {@link residualIdFromSelection}; overlay openers require
5+
* `items` from the caller that owns the data. Demo/fixture data lives in
6+
* demo.ts, not here.
87
*/
98

10-
import { SHELL_SHORTCUTS } from "./keybindings.js"
119
import type { StreamRow } from "./stream.js"
1210

1311
/** Host-owned residual row: stable id + display label. */
@@ -44,81 +42,9 @@ export function residualIdFromSelection(
4442
return itemIds[selection.index]
4543
}
4644

47-
export function makeSettingsItems(): readonly string[] {
48-
return [
49-
"Permissions — revoke remembered approvals",
50-
"Compaction — summarize vs drop",
51-
"Session mode — auto / ask / plan",
52-
"Sub-agents — max concurrent",
53-
"Tools — wait-for-approval budget",
54-
"Telemetry — usage opt-in",
55-
"Close settings",
56-
]
57-
}
58-
59-
/** Help overlay rows derived from the OpenTUI shell's own keybinding
60-
* catalog, so they cannot drift from what the shell actually implements. */
61-
export function makeHelpItems(): readonly string[] {
62-
return [
63-
...SHELL_SHORTCUTS.map((s) => `${s.keys}${s.description}`),
64-
"Close help",
65-
]
66-
}
67-
68-
export function makePluginsItems(): readonly string[] {
69-
return [
70-
"plugin:linear — enabled",
71-
"plugin:github — needs trust",
72-
"plugin:exa — enabled",
73-
"Add plugin from path…",
74-
"Web override: none",
75-
"Close plugins",
76-
]
77-
}
78-
79-
export function makeResumeItems(): readonly string[] {
80-
return [
81-
"Fix permissions overflow · 2h ago · idle",
82-
"Wave 6 palette work · yesterday · done",
83-
"Spike OpenTUI sticky scroll · 3d · done",
84-
"Untitled session · 1w · canceled",
85-
"Close resume",
86-
]
87-
}
88-
89-
export function makeMentionItems(): readonly string[] {
90-
return [
91-
"@src/tui-opentui/shell.ts",
92-
"@src/tui-opentui/residuals.ts",
93-
"@docs/plans/tui-layout-scroll-platform.md",
94-
"@AGENTS.md",
95-
"Close mentions",
96-
]
97-
}
98-
9945
export type ObserveSession = {
10046
readonly sessionId: string
10147
readonly agentId: string
10248
readonly description: string
10349
readonly lines: readonly StreamRow[]
10450
}
105-
106-
/** Fixture child session for tests/demo. */
107-
export function makeObserveFixture(): ObserveSession {
108-
return {
109-
sessionId: "child-1",
110-
agentId: "explore",
111-
description: "map callers of openListOverlay",
112-
lines: [
113-
{ role: "system", text: "— child session explore —" },
114-
{ role: "user", text: "find every openListOverlay caller" },
115-
{ role: "assistant", text: "Searching src/tui-opentui…" },
116-
{
117-
role: "tool",
118-
text: "grep openListOverlay → 6 hits",
119-
meta: "tool.done",
120-
},
121-
{ role: "assistant", text: "Report ready for parent." },
122-
],
123-
}
124-
}

0 commit comments

Comments
 (0)