diff --git a/packages/opencode/src/cli/cmd/run/footer.question.tsx b/packages/opencode/src/cli/cmd/run/footer.question.tsx index c48be37d41..5bea73a919 100644 --- a/packages/opencode/src/cli/cmd/run/footer.question.tsx +++ b/packages/opencode/src/cli/cmd/run/footer.question.tsx @@ -416,7 +416,7 @@ export function RunQuestionBody(props: { - {hit() ? "✓" : ""} + {hit() ? " ✓" : ""} @@ -466,7 +466,7 @@ export function RunQuestionBody(props: { - {picked() ? "✓" : ""} + {picked() ? " ✓" : ""} - {picked() ? "✓" : ""} + {picked() ? " ✓" : ""} @@ -408,7 +408,7 @@ export function QuestionPrompt(props: { request: QuestionRequest }) { - {customPicked() ? "✓" : ""} + {customPicked() ? " ✓" : ""} diff --git a/packages/opencode/test/cli/run/footer.view.test.tsx b/packages/opencode/test/cli/run/footer.view.test.tsx index 716554be49..697e64efc8 100644 --- a/packages/opencode/test/cli/run/footer.view.test.tsx +++ b/packages/opencode/test/cli/run/footer.view.test.tsx @@ -2,6 +2,7 @@ import { expect, test } from "bun:test" import { testRender } from "@opentui/solid" import { createSignal } from "solid-js" +import type { QuestionRequest } from "@opencode-ai/sdk/v2" import { RUN_COMMAND_PANEL_ROWS, RUN_SUBAGENT_PANEL_ROWS, @@ -24,6 +25,7 @@ import type { RunProvider, StreamCommit, } from "@/cli/cmd/run/types" +import { RunQuestionBody } from "@/cli/cmd/run/footer.question" function bindings(...keys: string[]) { return keys.map((key) => ({ key })) @@ -401,6 +403,53 @@ test("direct footer shows subagent indicator while prompt is running", async () } }) +test("direct question body separates single-select checkmark from label", async () => { + const request = { + id: "question-1", + sessionID: "session-1", + questions: [ + { + question: "Which categorical concept is often described as a universal way to combine two objects?", + header: "Universal Product", + options: [ + { label: "Product", description: "A product comes with projections." }, + { label: "Equalizer", description: "An equalizer selects morphisms where arrows agree." }, + ], + }, + ], + } satisfies QuestionRequest + const replies: unknown[] = [] + + const app = await testRender( + () => ( + + { + replies.push(input) + }} + onReject={() => {}} + /> + + ), + { + width: 100, + height: 12, + }, + ) + + try { + app.mockInput.pressEnter() + await app.renderOnce() + + expect(replies).toHaveLength(1) + expect(app.captureCharFrame()).toContain("Product ✓") + } finally { + app.renderer.destroy() + } +}) + test("direct model panel renders current model selector", async () => { const [providers] = createSignal([provider()]) const [current] = createSignal({ providerID: "opencode", modelID: "gpt-5" }) diff --git a/packages/opencode/test/cli/run/stream.transport.test.ts b/packages/opencode/test/cli/run/stream.transport.test.ts index d1b145db24..f01519fe7d 100644 --- a/packages/opencode/test/cli/run/stream.transport.test.ts +++ b/packages/opencode/test/cli/run/stream.transport.test.ts @@ -171,7 +171,7 @@ function globalSse(stream: GlobalEventStream) { function wrapGlobalStream(stream: EventStream): GlobalEventStream { return (async function* (): GlobalEventStream { for await (const event of stream) { - yield globalEvent(event) + yield globalEvent(event as GlobalEvent["payload"]) } return StreamClosed })() @@ -339,11 +339,11 @@ function child(id: string): SessionChild { } } -function globalEvent(payload: GlobalEvent["payload"]): GlobalEvent { +function globalEvent(payload: SdkEvent | GlobalEvent["payload"]): GlobalEvent { return { directory: "/tmp", project: "project-1", - payload, + payload: payload as GlobalEvent["payload"], } } diff --git a/packages/opencode/test/cli/tui/use-event.test.tsx b/packages/opencode/test/cli/tui/use-event.test.tsx index d690cfd6ce..5889eaa7c4 100644 --- a/packages/opencode/test/cli/tui/use-event.test.tsx +++ b/packages/opencode/test/cli/tui/use-event.test.tsx @@ -1,7 +1,7 @@ /** @jsxImportSource @opentui/solid */ import { describe, expect, test } from "bun:test" import { testRender } from "@opentui/solid" -import type { Event, GlobalEvent } from "@opencode-ai/sdk/v2" +import type { GlobalEvent } from "@opencode-ai/sdk/v2" import { onMount } from "solid-js" import { ProjectProvider, useProject } from "../../../src/cli/cmd/tui/context/project" import { SDKProvider } from "../../../src/cli/cmd/tui/context/sdk" @@ -17,7 +17,10 @@ async function wait(fn: () => boolean, timeout = 2000) { } } -function event(payload: Event, input: { directory: string; project?: string; workspace?: string }): GlobalEvent { +function event( + payload: GlobalEvent["payload"], + input: { directory: string; project?: string; workspace?: string }, +): GlobalEvent { return { directory: input.directory, project: input.project, @@ -26,7 +29,7 @@ function event(payload: Event, input: { directory: string; project?: string; wor } } -function vcs(branch: string): Event { +function vcs(branch: string): GlobalEvent["payload"] { return { id: `evt_vcs_${branch}`, type: "vcs.branch.updated", @@ -36,7 +39,7 @@ function vcs(branch: string): Event { } } -function update(version: string): Event { +function update(version: string): GlobalEvent["payload"] { return { id: `evt_update_${version}`, type: "installation.update-available", @@ -67,7 +70,7 @@ function createSource() { async function mount() { const source = createSource() - const seen: Event[] = [] + const seen: GlobalEvent["payload"][] = [] const workspaces: Array = [] const fetch = (async (input: RequestInfo | URL) => { const url = new URL(input instanceof Request ? input.url : String(input)) @@ -102,7 +105,7 @@ async function mount() { } function Probe(props: { - seen: Event[] + seen: GlobalEvent["payload"][] workspaces: Array onReady: (ctx: { project: ReturnType }) => void }) { @@ -111,7 +114,7 @@ function Probe(props: { onMount(() => { event.subscribe((evt, { workspace }) => { - props.seen.push(evt) + props.seen.push(evt as GlobalEvent["payload"]) props.workspaces.push(workspace) }) props.onReady({ project })