Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 39 additions & 33 deletions packages/opencode/src/cli/cmd/tui/routes/session/question.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ export function QuestionPrompt(props: { request: QuestionRequest }) {
const question = createMemo(() => questions()[store.tab])
const confirm = createMemo(() => !single() && store.tab === questions().length)
const options = createMemo(() => question()?.options ?? [])
const customInput = createMemo(() => question()?.customInput !== false)
const hasDescriptions = createMemo(() => options().some((opt) => opt.description))
const other = createMemo(() => store.selected === options().length)
const input = createMemo(() => store.custom[store.tab] ?? "")
const multi = createMemo(() => question()?.multiple === true)
Expand Down Expand Up @@ -172,7 +174,7 @@ export function QuestionPrompt(props: { request: QuestionRequest }) {
}
} else {
const opts = options()
const total = opts.length + 1 // options + "Other"
const total = opts.length + (customInput() ? 1 : 0) // options + "Other" (if allowed)

if (evt.name === "up" || evt.name === "k") {
evt.preventDefault()
Expand Down Expand Up @@ -251,9 +253,9 @@ export function QuestionPrompt(props: { request: QuestionRequest }) {
</Show>

<Show when={!confirm()}>
<box paddingLeft={1} gap={1}>
<box paddingLeft={1} gap={hasDescriptions() ? 1 : 0}>
<box>
<text fg={theme.text}>
<text fg={theme.text} marginBottom={hasDescriptions() ? 0 : 1}>
{question()?.question}
{multi() ? " (select all that apply)" : ""}
</text>
Expand All @@ -273,42 +275,46 @@ export function QuestionPrompt(props: { request: QuestionRequest }) {
</box>
<text fg={theme.success}>{picked() ? "✓" : ""}</text>
</box>
<box paddingLeft={3}>
<text fg={theme.textMuted}>{opt.description}</text>
</box>
<Show when={opt.description}>
<box paddingLeft={3}>
<text fg={theme.textMuted}>{opt.description}</text>
</box>
</Show>
</box>
)
}}
</For>
<box>
<box flexDirection="row" gap={1}>
<box backgroundColor={other() ? theme.backgroundElement : undefined}>
<text fg={other() ? theme.secondary : customPicked() ? theme.success : theme.text}>
{options().length + 1}. Type your own answer
</text>
<Show when={customInput()}>
<box>
<box flexDirection="row" gap={1}>
<box backgroundColor={other() ? theme.backgroundElement : undefined}>
<text fg={other() ? theme.secondary : customPicked() ? theme.success : theme.text}>
{options().length + 1}. Type your own answer
</text>
</box>
<text fg={theme.success}>{customPicked() ? "✓" : ""}</text>
</box>
<text fg={theme.success}>{customPicked() ? "✓" : ""}</text>
<Show when={store.editing}>
<box paddingLeft={3}>
<textarea
ref={(val: TextareaRenderable) => (textarea = val)}
focused
initialValue={input()}
placeholder="Type your own answer"
textColor={theme.text}
focusedTextColor={theme.text}
cursorColor={theme.primary}
keyBindings={bindings()}
/>
</box>
</Show>
<Show when={!store.editing && input()}>
<box paddingLeft={3}>
<text fg={theme.textMuted}>{input()}</text>
</box>
</Show>
</box>
<Show when={store.editing}>
<box paddingLeft={3}>
<textarea
ref={(val: TextareaRenderable) => (textarea = val)}
focused
initialValue={input()}
placeholder="Type your own answer"
textColor={theme.text}
focusedTextColor={theme.text}
cursorColor={theme.primary}
keyBindings={bindings()}
/>
</box>
</Show>
<Show when={!store.editing && input()}>
<box paddingLeft={3}>
<text fg={theme.textMuted}>{input()}</text>
</box>
</Show>
</box>
</Show>
</box>
</box>
</Show>
Expand Down
3 changes: 2 additions & 1 deletion packages/opencode/src/question/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export namespace Question {
export const Option = z
.object({
label: z.string().describe("Display text (1-5 words, concise)"),
description: z.string().describe("Explanation of choice"),
description: z.string().optional().describe("Explanation of choice"),
})
.meta({
ref: "QuestionOption",
Expand All @@ -24,6 +24,7 @@ export namespace Question {
header: z.string().max(12).describe("Very short label (max 12 chars)"),
options: z.array(Option).describe("Available choices"),
multiple: z.boolean().optional().describe("Allow selecting multiple choices"),
customInput: z.boolean().optional().describe("Allow users to type their own answer (default: true)"),
})
.meta({
ref: "QuestionInfo",
Expand Down
6 changes: 5 additions & 1 deletion packages/sdk/js/src/v2/gen/types.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,7 @@ export type QuestionOption = {
/**
* Explanation of choice
*/
description: string
description?: string
}

export type QuestionInfo = {
Expand All @@ -545,6 +545,10 @@ export type QuestionInfo = {
* Allow selecting multiple choices
*/
multiple?: boolean
/**
* Allow users to type their own answer (default: true)
*/
customInput?: boolean
}

export type QuestionRequest = {
Expand Down