mirror of
https://github.com/anomalyco/opencode.git
synced 2026-05-22 03:45:23 +00:00
fix(tui): separate question checkmark labels (#28558)
This commit is contained in:
@@ -416,7 +416,7 @@ export function RunQuestionBody(props: {
|
||||
</text>
|
||||
</box>
|
||||
<Show when={!info()?.multiple}>
|
||||
<text fg={props.theme.success}>{hit() ? "✓" : ""}</text>
|
||||
<text fg={props.theme.success}>{hit() ? " ✓" : ""}</text>
|
||||
</Show>
|
||||
</box>
|
||||
<box paddingLeft={3}>
|
||||
@@ -466,7 +466,7 @@ export function RunQuestionBody(props: {
|
||||
</text>
|
||||
</box>
|
||||
<Show when={!info()?.multiple}>
|
||||
<text fg={props.theme.success}>{picked() ? "✓" : ""}</text>
|
||||
<text fg={props.theme.success}>{picked() ? " ✓" : ""}</text>
|
||||
</Show>
|
||||
</box>
|
||||
<Show
|
||||
|
||||
@@ -375,7 +375,7 @@ export function QuestionPrompt(props: { request: QuestionRequest }) {
|
||||
</text>
|
||||
</box>
|
||||
<Show when={!multi()}>
|
||||
<text fg={theme.success}>{picked() ? "✓" : ""}</text>
|
||||
<text fg={theme.success}>{picked() ? " ✓" : ""}</text>
|
||||
</Show>
|
||||
</box>
|
||||
|
||||
@@ -408,7 +408,7 @@ export function QuestionPrompt(props: { request: QuestionRequest }) {
|
||||
</box>
|
||||
|
||||
<Show when={!multi()}>
|
||||
<text fg={theme.success}>{customPicked() ? "✓" : ""}</text>
|
||||
<text fg={theme.success}>{customPicked() ? " ✓" : ""}</text>
|
||||
</Show>
|
||||
</box>
|
||||
<Show when={store.editing}>
|
||||
|
||||
@@ -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(
|
||||
() => (
|
||||
<box width={100} height={12}>
|
||||
<RunQuestionBody
|
||||
request={request}
|
||||
theme={RUN_THEME_FALLBACK.footer}
|
||||
onReply={(input) => {
|
||||
replies.push(input)
|
||||
}}
|
||||
onReject={() => {}}
|
||||
/>
|
||||
</box>
|
||||
),
|
||||
{
|
||||
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<RunProvider[] | undefined>([provider()])
|
||||
const [current] = createSignal<RunInput["model"]>({ providerID: "opencode", modelID: "gpt-5" })
|
||||
|
||||
@@ -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"],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<string | undefined> = []
|
||||
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<string | undefined>
|
||||
onReady: (ctx: { project: ReturnType<typeof useProject> }) => 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 })
|
||||
|
||||
Reference in New Issue
Block a user