Skip to content
Closed
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
5 changes: 5 additions & 0 deletions .changeset/fix-question-multiselect-space-toggle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@kilocode/cli": patch
---

Bind `Space` to toggle and `Enter` to continue in multi-select questions from the `kilo run` footer and the interactive TUI, matching the convention used by other CLI tools (`fzf`, `lazygit`, `btop`, ...).
26 changes: 24 additions & 2 deletions packages/opencode/src/cli/cmd/run/footer.question.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
questionSubmit,
questionSync,
questionTabs,
questionToggleAction, // kilocode_change
questionTotal,
} from "./question.shared"
import type { RunFooterTheme } from "./theme"
Expand Down Expand Up @@ -65,7 +66,7 @@ export function RunQuestionBody(props: {
}

if (info()?.multiple) {
return "toggle"
return "continue" // kilocode_change
}

if (single()) {
Expand Down Expand Up @@ -125,7 +126,10 @@ export function RunQuestionBody(props: {
const choose = (selected: number) => {
const base = state()
const cur = questionSetSelected(base, selected)
const next = questionSelect(cur, props.request)
// kilocode_change start - digit shortcuts toggle in multi-select
const info = questionInfo(props.request, cur)
const next = info?.multiple ? questionToggleAction(cur, props.request) : questionSelect(cur, props.request)
// kilocode_change end
if (next.state !== base) {
setState(next.state)
}
Expand Down Expand Up @@ -217,6 +221,17 @@ export function RunQuestionBody(props: {
return
}

// kilocode_change start - space toggles in multi-select
if (event.name === "space") {
const next = questionToggleAction(cur, props.request)
if (next.state !== cur) {
setState(next.state)
}
event.preventDefault()
return
}
// kilocode_change end

const total = questionTotal(props.request, cur)
const max = Math.min(total, 9)
const digit = Number(event.name)
Expand Down Expand Up @@ -562,6 +577,13 @@ export function RunQuestionBody(props: {
{"⇆"} <span style={{ fg: props.theme.muted }}>tab</span>
</text>
</Show>
{/* kilocode_change start - multi-select space toggle hint */}
<Show when={!confirm() && info()?.multiple}>
<text fg={props.theme.text}>
space <span style={{ fg: props.theme.muted }}>toggle</span>
</text>
</Show>
{/* kilocode_change end */}
<Show when={!confirm()}>
<text fg={props.theme.text}>
{"↑↓"} <span style={{ fg: props.theme.muted }}>select</span>
Expand Down
52 changes: 43 additions & 9 deletions packages/opencode/src/cli/cmd/run/question.shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ export function questionMove(state: QuestionBodyState, request: QuestionRequest,
}
}

// kilocode_change start - space toggles, enter advances in multi-select
export function questionSelect(state: QuestionBodyState, request: QuestionRequest): QuestionStep {
const info = questionInfo(request, state)
if (!info) {
Expand All @@ -229,8 +230,38 @@ export function questionSelect(state: QuestionBodyState, request: QuestionReques
}
}

const last = request.questions.length - 1
const next = state.tab >= last ? last + 1 : state.tab + 1
return {
Comment thread
IamCoder18 marked this conversation as resolved.
state: questionSetTab(state, next),
}
}

const option = info.options[state.selected]
if (!option) {
return { state }
}

if (info.multiple) {
const last = request.questions.length - 1
const next = state.tab >= last ? last + 1 : state.tab + 1
return {
state: questionSetTab(state, next),
}
}

return questionPick(state, request, option.label)
}

export function questionToggleAction(state: QuestionBodyState, request: QuestionRequest): QuestionStep {
const info = questionInfo(request, state)
if (!info || !info.multiple) {
return { state }
}

if (questionOther(request, state)) {
const value = questionInput(state)
if (value && questionPicked(state)) {
if (value) {
return {
state: questionToggle(state, value),
}
Expand All @@ -246,14 +277,11 @@ export function questionSelect(state: QuestionBodyState, request: QuestionReques
return { state }
}

if (info.multiple) {
return {
state: questionToggle(state, option.label),
}
return {
state: questionToggle(state, option.label),
}

return questionPick(state, request, option.label)
}
// kilocode_change end

export function questionSave(state: QuestionBodyState, request: QuestionRequest): QuestionStep {
const info = questionInfo(request, state)
Expand Down Expand Up @@ -332,9 +360,15 @@ export function questionHint(request: QuestionRequest, state: QuestionBodyState)
}

const info = questionInfo(request, state)
// kilocode_change start - multi-select hint uses space/continue
if (questionSingle(request)) {
return `↑↓ select enter ${info?.multiple ? "toggle" : "submit"} esc dismiss`
return `↑↓ select enter submit esc dismiss`
}

if (info?.multiple) {
return `⇆ tab ↑↓ select space toggle enter continue esc dismiss`
}

return `⇆ tab ↑↓ select enter ${info?.multiple ? "toggle" : "confirm"} esc dismiss`
return `⇆ tab ↑↓ select enter confirm esc dismiss`
// kilocode_change end
}
91 changes: 86 additions & 5 deletions packages/opencode/src/cli/cmd/tui/routes/session/question.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,26 @@ export function QuestionPrompt(props: {
pick(opt.label)
}

// kilocode_change start - space toggles, enter advances in multi-select
function continueOption() {
if (other()) {
Comment thread
IamCoder18 marked this conversation as resolved.
if (multi()) {
selectTab(Math.min(store.tab + 1, tabs() - 1))
return
}
setStore("editing", true)
return
}
if (multi()) {
selectTab(Math.min(store.tab + 1, tabs() - 1))
return
}
const opt = options()[store.selected]
if (!opt) return
pick(opt.label)
}
// kilocode_change end

onMount(() => {
const popMode = modeStack.push(QUESTION_MODE)
onCleanup(popMode)
Expand Down Expand Up @@ -259,15 +279,36 @@ export function QuestionPrompt(props: {
...tuiConfig.keybinds.get("app.exit"),
]
: [
// kilocode_change start - space toggles, enter advances in multi-select
...Array.from({ length: max }, (_, index) => ({
key: String(index + 1),
desc: `Select answer ${index + 1}`,
desc: question()?.multiple ? `Toggle answer ${index + 1}` : `Select answer ${index + 1}`,
group: "Question",
cmd: () => {
moveTo(index)
selectOption()
if (index === options().length) {
if (question()?.multiple) {
const value = input()
if (value) {
toggle(value)
} else {
setStore("editing", true)
}
} else {
setStore("editing", true)
}
return
}
const opt = options()[index]
if (!opt) return
Comment thread
IamCoder18 marked this conversation as resolved.
if (question()?.multiple) {
toggle(opt.label)
} else {
pick(opt.label)
}
},
})),
// kilocode_change end
{
key: "up",
desc: "Previous answer",
Expand All @@ -282,7 +323,39 @@ export function QuestionPrompt(props: {
},
{ key: "down", desc: "Next answer", group: "Question", cmd: () => moveTo((store.selected + 1) % total) },
{ key: "j", desc: "Next answer", group: "Question", cmd: () => moveTo((store.selected + 1) % total) },
{ key: "return", desc: "Select answer", group: "Question", cmd: () => selectOption() },
// kilocode_change start - multi-select space toggle binding
...(question()?.multiple
? [
{
key: "space",
desc: "Toggle answer",
group: "Question",
cmd: () => {
if (other()) {
const value = input()
if (value) {
toggle(value)
} else {
setStore("editing", true)
}
return
}
const opt = options()[store.selected]
if (!opt) return
Comment thread
IamCoder18 marked this conversation as resolved.
toggle(opt.label)
},
},
]
: []),
// kilocode_change end
// kilocode_change start - enter advances in multi-select
{
key: "return",
desc: question()?.multiple ? "Continue" : "Select answer",
group: "Question",
cmd: () => continueOption(),
},
// kilocode_change end
{ key: "escape", desc: "Reject question", group: "Question", cmd: () => reject() },
...tuiConfig.keybinds.get("app.exit"),
]),
Expand Down Expand Up @@ -497,16 +570,24 @@ export function QuestionPrompt(props: {
{"⇆"} <span style={{ fg: theme.textMuted }}>tab</span>
</text>
</Show>
{/* kilocode_change start - multi-select space toggle hint */}
<Show when={!confirm() && multi()}>
<text fg={theme.text}>
space <span style={{ fg: theme.textMuted }}>toggle</span>
</text>
</Show>
{/* kilocode_change end */}
<Show when={!confirm()}>
<text fg={theme.text}>
{"↑↓"} <span style={{ fg: theme.textMuted }}>select</span>
</text>
</Show>
<text fg={theme.text}>
enter{" "}
enter {/* kilocode_change start - multi-select uses continue verb */}
<span style={{ fg: theme.textMuted }}>
{confirm() ? "submit" : multi() ? "toggle" : single() ? "submit" : "confirm"}
{confirm() ? "submit" : multi() ? "continue" : single() ? "submit" : "confirm"}
</span>
{/* kilocode_change end */}
</text>

<text fg={theme.text}>
Expand Down
Loading
Loading