From c596a8f11dec2fddcfe6c0c4b216c706be042bae Mon Sep 17 00:00:00 2001 From: David Hill <1879069+iamdavidhill@users.noreply.github.com> Date: Tue, 1 Sep 2026 09:06:25 -0600 Subject: [PATCH] fix(tui): simplify narrow interrupt footer (#46533) Co-authored-by: jlongster <17031+jlongster@users.noreply.github.com> --- packages/plugin/src/tui/context.ts | 6 +- packages/tui/src/component/prompt/index.tsx | 6 +- .../tui/src/feature-plugins/prompt/footer.tsx | 24 +++-- .../feature-plugins/prompt-footer.test.tsx | 91 ++++++++++++++++++- 4 files changed, 115 insertions(+), 12 deletions(-) diff --git a/packages/plugin/src/tui/context.ts b/packages/plugin/src/tui/context.ts index e80921b5a11..6a68b9ce8a2 100644 --- a/packages/plugin/src/tui/context.ts +++ b/packages/plugin/src/tui/context.ts @@ -156,7 +156,11 @@ export interface Page { readonly render: (input: { readonly data?: Record }) => JSX.Element } -type PromptFooterInput = { readonly sessionID?: string; readonly mode: "normal" | "shell" } +type PromptFooterInput = { + readonly sessionID?: string + readonly mode: "normal" | "shell" + readonly showDetails: boolean +} /** * The host UI's slot tree. Every path is one slot: a named boundary a plugin diff --git a/packages/tui/src/component/prompt/index.tsx b/packages/tui/src/component/prompt/index.tsx index 6e4b5212113..7560d496536 100644 --- a/packages/tui/src/component/prompt/index.tsx +++ b/packages/tui/src/component/prompt/index.tsx @@ -1595,7 +1595,11 @@ export function Prompt(props: PromptProps) { if (agentLabel()) revealedPromptMetadata.add(local) }) const borderHighlight = createMemo(() => tint(theme.border.default, highlight(), agentMetaAlpha())) - const footerInput = () => ({ sessionID: props.sessionID, mode: store.mode }) + const footerInput = () => ({ + sessionID: props.sessionID, + mode: store.mode, + showDetails: store.interrupt === 0 || dimensions().width >= 80, + }) const placeholderText = createMemo(() => { if (props.showPlaceholder === false) return undefined diff --git a/packages/tui/src/feature-plugins/prompt/footer.tsx b/packages/tui/src/feature-plugins/prompt/footer.tsx index 8d818e8a95e..8f397d84ecd 100644 --- a/packages/tui/src/feature-plugins/prompt/footer.tsx +++ b/packages/tui/src/feature-plugins/prompt/footer.tsx @@ -8,7 +8,12 @@ const money = new Intl.NumberFormat("en-US", { currency: "USD", }) -export function PromptFooter(props: { context: Plugin.Context; sessionID?: string; mode: "normal" | "shell" }) { +export function PromptFooter(props: { + context: Plugin.Context + sessionID?: string + mode: "normal" | "shell" + showDetails: boolean +}) { const dimensions = useTerminalDimensions() const [liveHovered, setLiveHovered] = createSignal(false) const subagents = createMemo(() => { @@ -69,7 +74,7 @@ export function PromptFooter(props: { context: Plugin.Context; sessionID?: strin - 0}> + 0}> · {status().join(" · ")} @@ -77,14 +82,14 @@ export function PromptFooter(props: { context: Plugin.Context; sessionID?: strin - = 44}> + = 44}> {shortcut("agent.cycle")} agents - = 44}> - + + {shortcut("command.palette.show")} commands @@ -106,7 +111,14 @@ export default Plugin.define({ setup(context) { context.ui.slot({ append: "prompt.footer", - render: (props) => , + render: (props) => ( + + ), }) }, }) diff --git a/packages/tui/test/feature-plugins/prompt-footer.test.tsx b/packages/tui/test/feature-plugins/prompt-footer.test.tsx index eb9bb233589..869c8e808de 100644 --- a/packages/tui/test/feature-plugins/prompt-footer.test.tsx +++ b/packages/tui/test/feature-plugins/prompt-footer.test.tsx @@ -3,6 +3,7 @@ import { expect, test } from "bun:test" import { RGBA, TextRenderable } from "@opentui/core" import { testRender } from "@opentui/solid" import type { Context } from "@opencode-ai/plugin/tui/context" +import { createSignal } from "solid-js" import { PromptFooter } from "../../src/feature-plugins/prompt/footer" test("prompt footer separates simultaneous subagent, shell, and usage status", async () => { @@ -38,10 +39,13 @@ test("prompt footer separates simultaneous subagent, shell, and usage status", a }, }, } as unknown as Context - const app = await testRender(() => , { - width: 80, - height: 2, - }) + const app = await testRender( + () => , + { + width: 80, + height: 2, + }, + ) try { await app.renderOnce() @@ -59,3 +63,82 @@ test("prompt footer separates simultaneous subagent, shell, and usage status", a app.renderer.destroy() } }) + +test("prompt footer can hide details", async () => { + const color = RGBA.fromInts(200, 200, 200) + const context = { + location: { directory: "/workspace" }, + theme: { + text: { + default: color, + subdued: color, + }, + }, + keymap: { + shortcuts: (id: string) => { + if (id === "command.palette.show") return ["ctrl+p"] + if (id === "agent.cycle") return ["shift+tab"] + return [] + }, + }, + data: { + session: { + family: () => ["session"], + status: () => "running", + get: () => ({ id: "session", location: { directory: "/workspace" } }), + cost: () => 1, + message: { + list: () => [ + { + id: "message", + type: "assistant", + model: { providerID: "provider", id: "model" }, + tokens: { input: 1_000, output: 0, reasoning: 0, cache: { read: 0, write: 0 } }, + }, + ], + }, + }, + shell: { list: () => [] }, + location: { + model: { list: () => [{ providerID: "provider", id: "model", limit: { context: 10_000 } }] }, + }, + }, + } as unknown as Context + const [showDetails, setShowDetails] = createSignal(true) + const [sessionID, setSessionID] = createSignal("session") + const app = await testRender( + () => ( + + + + ), + { + width: 80, + height: 1, + }, + ) + + try { + await app.renderOnce() + expect(app.captureCharFrame()).toContain("1.0K (10%) · $1.00") + expect(app.captureCharFrame()).toContain("ctrl+p commands") + + setShowDetails(false) + await app.renderOnce() + const frame = app.captureCharFrame() + expect(frame).not.toContain("1.0K (10%)") + expect(frame).not.toContain("$1.00") + expect(frame).not.toContain("ctrl+p commands") + + setSessionID(undefined) + await app.renderOnce() + expect(app.captureCharFrame()).not.toContain("shift+tab agents") + } finally { + app.renderer.destroy() + } +})