fix(tui): separate question checkmark labels (#28558)
This commit is contained in:
@@ -416,7 +416,7 @@ export function RunQuestionBody(props: {
|
|||||||
</text>
|
</text>
|
||||||
</box>
|
</box>
|
||||||
<Show when={!info()?.multiple}>
|
<Show when={!info()?.multiple}>
|
||||||
<text fg={props.theme.success}>{hit() ? "✓" : ""}</text>
|
<text fg={props.theme.success}>{hit() ? " ✓" : ""}</text>
|
||||||
</Show>
|
</Show>
|
||||||
</box>
|
</box>
|
||||||
<box paddingLeft={3}>
|
<box paddingLeft={3}>
|
||||||
@@ -466,7 +466,7 @@ export function RunQuestionBody(props: {
|
|||||||
</text>
|
</text>
|
||||||
</box>
|
</box>
|
||||||
<Show when={!info()?.multiple}>
|
<Show when={!info()?.multiple}>
|
||||||
<text fg={props.theme.success}>{picked() ? "✓" : ""}</text>
|
<text fg={props.theme.success}>{picked() ? " ✓" : ""}</text>
|
||||||
</Show>
|
</Show>
|
||||||
</box>
|
</box>
|
||||||
<Show
|
<Show
|
||||||
|
|||||||
@@ -375,7 +375,7 @@ export function QuestionPrompt(props: { request: QuestionRequest }) {
|
|||||||
</text>
|
</text>
|
||||||
</box>
|
</box>
|
||||||
<Show when={!multi()}>
|
<Show when={!multi()}>
|
||||||
<text fg={theme.success}>{picked() ? "✓" : ""}</text>
|
<text fg={theme.success}>{picked() ? " ✓" : ""}</text>
|
||||||
</Show>
|
</Show>
|
||||||
</box>
|
</box>
|
||||||
|
|
||||||
@@ -408,7 +408,7 @@ export function QuestionPrompt(props: { request: QuestionRequest }) {
|
|||||||
</box>
|
</box>
|
||||||
|
|
||||||
<Show when={!multi()}>
|
<Show when={!multi()}>
|
||||||
<text fg={theme.success}>{customPicked() ? "✓" : ""}</text>
|
<text fg={theme.success}>{customPicked() ? " ✓" : ""}</text>
|
||||||
</Show>
|
</Show>
|
||||||
</box>
|
</box>
|
||||||
<Show when={store.editing}>
|
<Show when={store.editing}>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import { expect, test } from "bun:test"
|
import { expect, test } from "bun:test"
|
||||||
import { testRender } from "@opentui/solid"
|
import { testRender } from "@opentui/solid"
|
||||||
import { createSignal } from "solid-js"
|
import { createSignal } from "solid-js"
|
||||||
|
import type { QuestionRequest } from "@opencode-ai/sdk/v2"
|
||||||
import {
|
import {
|
||||||
RUN_COMMAND_PANEL_ROWS,
|
RUN_COMMAND_PANEL_ROWS,
|
||||||
RUN_SUBAGENT_PANEL_ROWS,
|
RUN_SUBAGENT_PANEL_ROWS,
|
||||||
@@ -24,6 +25,7 @@ import type {
|
|||||||
RunProvider,
|
RunProvider,
|
||||||
StreamCommit,
|
StreamCommit,
|
||||||
} from "@/cli/cmd/run/types"
|
} from "@/cli/cmd/run/types"
|
||||||
|
import { RunQuestionBody } from "@/cli/cmd/run/footer.question"
|
||||||
|
|
||||||
function bindings(...keys: string[]) {
|
function bindings(...keys: string[]) {
|
||||||
return keys.map((key) => ({ key }))
|
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 () => {
|
test("direct model panel renders current model selector", async () => {
|
||||||
const [providers] = createSignal<RunProvider[] | undefined>([provider()])
|
const [providers] = createSignal<RunProvider[] | undefined>([provider()])
|
||||||
const [current] = createSignal<RunInput["model"]>({ providerID: "opencode", modelID: "gpt-5" })
|
const [current] = createSignal<RunInput["model"]>({ providerID: "opencode", modelID: "gpt-5" })
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ function globalSse(stream: GlobalEventStream) {
|
|||||||
function wrapGlobalStream(stream: EventStream): GlobalEventStream {
|
function wrapGlobalStream(stream: EventStream): GlobalEventStream {
|
||||||
return (async function* (): GlobalEventStream {
|
return (async function* (): GlobalEventStream {
|
||||||
for await (const event of stream) {
|
for await (const event of stream) {
|
||||||
yield globalEvent(event)
|
yield globalEvent(event as GlobalEvent["payload"])
|
||||||
}
|
}
|
||||||
return StreamClosed
|
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 {
|
return {
|
||||||
directory: "/tmp",
|
directory: "/tmp",
|
||||||
project: "project-1",
|
project: "project-1",
|
||||||
payload,
|
payload: payload as GlobalEvent["payload"],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/** @jsxImportSource @opentui/solid */
|
/** @jsxImportSource @opentui/solid */
|
||||||
import { describe, expect, test } from "bun:test"
|
import { describe, expect, test } from "bun:test"
|
||||||
import { testRender } from "@opentui/solid"
|
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 { onMount } from "solid-js"
|
||||||
import { ProjectProvider, useProject } from "../../../src/cli/cmd/tui/context/project"
|
import { ProjectProvider, useProject } from "../../../src/cli/cmd/tui/context/project"
|
||||||
import { SDKProvider } from "../../../src/cli/cmd/tui/context/sdk"
|
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 {
|
return {
|
||||||
directory: input.directory,
|
directory: input.directory,
|
||||||
project: input.project,
|
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 {
|
return {
|
||||||
id: `evt_vcs_${branch}`,
|
id: `evt_vcs_${branch}`,
|
||||||
type: "vcs.branch.updated",
|
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 {
|
return {
|
||||||
id: `evt_update_${version}`,
|
id: `evt_update_${version}`,
|
||||||
type: "installation.update-available",
|
type: "installation.update-available",
|
||||||
@@ -67,7 +70,7 @@ function createSource() {
|
|||||||
|
|
||||||
async function mount() {
|
async function mount() {
|
||||||
const source = createSource()
|
const source = createSource()
|
||||||
const seen: Event[] = []
|
const seen: GlobalEvent["payload"][] = []
|
||||||
const workspaces: Array<string | undefined> = []
|
const workspaces: Array<string | undefined> = []
|
||||||
const fetch = (async (input: RequestInfo | URL) => {
|
const fetch = (async (input: RequestInfo | URL) => {
|
||||||
const url = new URL(input instanceof Request ? input.url : String(input))
|
const url = new URL(input instanceof Request ? input.url : String(input))
|
||||||
@@ -102,7 +105,7 @@ async function mount() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Probe(props: {
|
function Probe(props: {
|
||||||
seen: Event[]
|
seen: GlobalEvent["payload"][]
|
||||||
workspaces: Array<string | undefined>
|
workspaces: Array<string | undefined>
|
||||||
onReady: (ctx: { project: ReturnType<typeof useProject> }) => void
|
onReady: (ctx: { project: ReturnType<typeof useProject> }) => void
|
||||||
}) {
|
}) {
|
||||||
@@ -111,7 +114,7 @@ function Probe(props: {
|
|||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
event.subscribe((evt, { workspace }) => {
|
event.subscribe((evt, { workspace }) => {
|
||||||
props.seen.push(evt)
|
props.seen.push(evt as GlobalEvent["payload"])
|
||||||
props.workspaces.push(workspace)
|
props.workspaces.push(workspace)
|
||||||
})
|
})
|
||||||
props.onReady({ project })
|
props.onReady({ project })
|
||||||
|
|||||||
Reference in New Issue
Block a user