fix(tui): keep interrupt confirmation visible (#42096)

This commit is contained in:
Kit Langton 2026-08-12 14:33:53 -04:00 committed by GitHub
parent c80472b6d5
commit f713d39c61
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 48 additions and 15 deletions

View file

@ -5,6 +5,7 @@ import {
MouseEvent,
PasteEvent,
decodePasteBytes,
type ColorInput,
type KeyEvent,
} from "@opentui/core"
import { createEffect, createMemo, onMount, createSignal, onCleanup, on, Show, Switch, Match, For } from "solid-js"
@ -107,6 +108,22 @@ function fadeColor(color: RGBA, alpha: number) {
return RGBA.fromValues(color.r, color.g, color.b, color.a * alpha)
}
export function PromptInterruptStatus(props: {
armed: boolean
text: ColorInput
subdued: ColorInput
warning: ColorInput
}) {
return (
<text fg={props.armed ? props.warning : props.text} wrapMode="none" truncate flexShrink={1}>
esc{" "}
<span style={{ fg: props.armed ? props.warning : props.subdued }}>
{props.armed ? "again to interrupt" : "interrupt"}
</span>
</text>
)
}
function hasEditorRangeSelection(selection: EditorSelection["ranges"][number]) {
return (
selection.selection.start.line !== selection.selection.end.line ||
@ -1804,21 +1821,12 @@ export function Prompt(props: PromptProps) {
<spinner color={spinnerDef().color} frames={spinnerDef().frames} interval={40} />
</Show>
</box>
<text
fg={store.interrupt > 0 ? theme.background.action.primary.default : theme.text.default}
wrapMode="none"
truncate
flexShrink={1}
>
esc{" "}
<span
style={{
fg: store.interrupt > 0 ? theme.background.action.primary.default : theme.text.subdued,
}}
>
{store.interrupt > 0 ? "again to interrupt" : "interrupt"}
</span>
</text>
<PromptInterruptStatus
armed={store.interrupt > 0}
text={theme.text.default}
subdued={theme.text.subdued}
warning={theme.text.feedback.warning.default}
/>
</box>
</Match>
<Match when={move.progress()}>

View file

@ -0,0 +1,25 @@
/** @jsxImportSource @opentui/solid */
import { RGBA } from "@opentui/core"
import { testRender } from "@opentui/solid"
import { expect, test } from "bun:test"
import { PromptInterruptStatus } from "../../../src/component/prompt"
test("armed interrupt status renders visible warning text", async () => {
const text = RGBA.fromHex("#ffffff")
const subdued = RGBA.fromHex("#808080")
const warning = RGBA.fromHex("#fbbf24")
const app = await testRender(
() => <PromptInterruptStatus armed text={text} subdued={subdued} warning={warning} />,
{ width: 30, height: 1 },
)
try {
await app.renderOnce()
const line = app.renderer.currentRenderBuffer.getSpanLines()[0]!
expect(line.spans.map((span) => span.text).join("")).toContain("esc again to interrupt")
expect(line.spans.filter((span) => span.text.trim()).every((span) => span.fg.equals(warning))).toBeTrue()
} finally {
app.renderer.destroy()
}
})