Skip to content

Commit ddd6eb4

Browse files
authored
fix(tui): separate question checkmark labels (anomalyco#28558)
1 parent 12bbe84 commit ddd6eb4

5 files changed

Lines changed: 66 additions & 14 deletions

File tree

‎packages/opencode/src/cli/cmd/run/footer.question.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ export function RunQuestionBody(props: {
416416
</text>
417417
</box>
418418
<Show when={!info()?.multiple}>
419-
<text fg={props.theme.success}>{hit() ? "✓" : ""}</text>
419+
<text fg={props.theme.success}>{hit() ? " ✓" : ""}</text>
420420
</Show>
421421
</box>
422422
<box paddingLeft={3}>
@@ -466,7 +466,7 @@ export function RunQuestionBody(props: {
466466
</text>
467467
</box>
468468
<Show when={!info()?.multiple}>
469-
<text fg={props.theme.success}>{picked() ? "✓" : ""}</text>
469+
<text fg={props.theme.success}>{picked() ? " ✓" : ""}</text>
470470
</Show>
471471
</box>
472472
<Show

‎packages/opencode/src/cli/cmd/tui/routes/session/question.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,7 @@ export function QuestionPrompt(props: { request: QuestionRequest }) {
375375
</text>
376376
</box>
377377
<Show when={!multi()}>
378-
<text fg={theme.success}>{picked() ? "✓" : ""}</text>
378+
<text fg={theme.success}>{picked() ? " ✓" : ""}</text>
379379
</Show>
380380
</box>
381381

@@ -408,7 +408,7 @@ export function QuestionPrompt(props: { request: QuestionRequest }) {
408408
</box>
409409

410410
<Show when={!multi()}>
411-
<text fg={theme.success}>{customPicked() ? "✓" : ""}</text>
411+
<text fg={theme.success}>{customPicked() ? " ✓" : ""}</text>
412412
</Show>
413413
</box>
414414
<Show when={store.editing}>

‎packages/opencode/test/cli/run/footer.view.test.tsx‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import { expect, test } from "bun:test"
33
import { testRender } from "@opentui/solid"
44
import { createSignal } from "solid-js"
5+
import type { QuestionRequest } from "@opencode-ai/sdk/v2"
56
import {
67
RUN_COMMAND_PANEL_ROWS,
78
RUN_SUBAGENT_PANEL_ROWS,
@@ -24,6 +25,7 @@ import type {
2425
RunProvider,
2526
StreamCommit,
2627
} from "@/cli/cmd/run/types"
28+
import { RunQuestionBody } from "@/cli/cmd/run/footer.question"
2729

2830
function bindings(...keys: string[]) {
2931
return keys.map((key) => ({ key }))
@@ -401,6 +403,53 @@ test("direct footer shows subagent indicator while prompt is running", async ()
401403
}
402404
})
403405

406+
test("direct question body separates single-select checkmark from label", async () => {
407+
const request = {
408+
id: "question-1",
409+
sessionID: "session-1",
410+
questions: [
411+
{
412+
question: "Which categorical concept is often described as a universal way to combine two objects?",
413+
header: "Universal Product",
414+
options: [
415+
{ label: "Product", description: "A product comes with projections." },
416+
{ label: "Equalizer", description: "An equalizer selects morphisms where arrows agree." },
417+
],
418+
},
419+
],
420+
} satisfies QuestionRequest
421+
const replies: unknown[] = []
422+
423+
const app = await testRender(
424+
() => (
425+
<box width={100} height={12}>
426+
<RunQuestionBody
427+
request={request}
428+
theme={RUN_THEME_FALLBACK.footer}
429+
onReply={(input) => {
430+
replies.push(input)
431+
}}
432+
onReject={() => {}}
433+
/>
434+
</box>
435+
),
436+
{
437+
width: 100,
438+
height: 12,
439+
},
440+
)
441+
442+
try {
443+
app.mockInput.pressEnter()
444+
await app.renderOnce()
445+
446+
expect(replies).toHaveLength(1)
447+
expect(app.captureCharFrame()).toContain("Product ✓")
448+
} finally {
449+
app.renderer.destroy()
450+
}
451+
})
452+
404453
test("direct model panel renders current model selector", async () => {
405454
const [providers] = createSignal<RunProvider[] | undefined>([provider()])
406455
const [current] = createSignal<RunInput["model"]>({ providerID: "opencode", modelID: "gpt-5" })

‎packages/opencode/test/cli/run/stream.transport.test.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ function globalSse(stream: GlobalEventStream) {
171171
function wrapGlobalStream(stream: EventStream): GlobalEventStream {
172172
return (async function* (): GlobalEventStream {
173173
for await (const event of stream) {
174-
yield globalEvent(event)
174+
yield globalEvent(event as GlobalEvent["payload"])
175175
}
176176
return StreamClosed
177177
})()
@@ -339,11 +339,11 @@ function child(id: string): SessionChild {
339339
}
340340
}
341341

342-
function globalEvent(payload: GlobalEvent["payload"]): GlobalEvent {
342+
function globalEvent(payload: SdkEvent | GlobalEvent["payload"]): GlobalEvent {
343343
return {
344344
directory: "/tmp",
345345
project: "project-1",
346-
payload,
346+
payload: payload as GlobalEvent["payload"],
347347
}
348348
}
349349

‎packages/opencode/test/cli/tui/use-event.test.tsx‎

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/** @jsxImportSource @opentui/solid */
22
import { describe, expect, test } from "bun:test"
33
import { testRender } from "@opentui/solid"
4-
import type { Event, GlobalEvent } from "@opencode-ai/sdk/v2"
4+
import type { GlobalEvent } from "@opencode-ai/sdk/v2"
55
import { onMount } from "solid-js"
66
import { ProjectProvider, useProject } from "../../../src/cli/cmd/tui/context/project"
77
import { SDKProvider } from "../../../src/cli/cmd/tui/context/sdk"
@@ -17,7 +17,10 @@ async function wait(fn: () => boolean, timeout = 2000) {
1717
}
1818
}
1919

20-
function event(payload: Event, input: { directory: string; project?: string; workspace?: string }): GlobalEvent {
20+
function event(
21+
payload: GlobalEvent["payload"],
22+
input: { directory: string; project?: string; workspace?: string },
23+
): GlobalEvent {
2124
return {
2225
directory: input.directory,
2326
project: input.project,
@@ -26,7 +29,7 @@ function event(payload: Event, input: { directory: string; project?: string; wor
2629
}
2730
}
2831

29-
function vcs(branch: string): Event {
32+
function vcs(branch: string): GlobalEvent["payload"] {
3033
return {
3134
id: `evt_vcs_${branch}`,
3235
type: "vcs.branch.updated",
@@ -36,7 +39,7 @@ function vcs(branch: string): Event {
3639
}
3740
}
3841

39-
function update(version: string): Event {
42+
function update(version: string): GlobalEvent["payload"] {
4043
return {
4144
id: `evt_update_${version}`,
4245
type: "installation.update-available",
@@ -67,7 +70,7 @@ function createSource() {
6770

6871
async function mount() {
6972
const source = createSource()
70-
const seen: Event[] = []
73+
const seen: GlobalEvent["payload"][] = []
7174
const workspaces: Array<string | undefined> = []
7275
const fetch = (async (input: RequestInfo | URL) => {
7376
const url = new URL(input instanceof Request ? input.url : String(input))
@@ -102,7 +105,7 @@ async function mount() {
102105
}
103106

104107
function Probe(props: {
105-
seen: Event[]
108+
seen: GlobalEvent["payload"][]
106109
workspaces: Array<string | undefined>
107110
onReady: (ctx: { project: ReturnType<typeof useProject> }) => void
108111
}) {
@@ -111,7 +114,7 @@ function Probe(props: {
111114

112115
onMount(() => {
113116
event.subscribe((evt, { workspace }) => {
114-
props.seen.push(evt)
117+
props.seen.push(evt as GlobalEvent["payload"])
115118
props.workspaces.push(workspace)
116119
})
117120
props.onReady({ project })

0 commit comments

Comments
 (0)