mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-08-30 17:14:40 +08:00
tui(cli): redesign inline suggest bar to match VS Code single-row layout
Replaces the number-prefixed question-picker block with a full-width tinted row: icon + suggestion text on the left, clickable action buttons on the right. Removes keyboard shortcuts and the esc-dismiss hint -- dismissal already happens automatically server-side when the user sends a new prompt.
This commit is contained in:
@@ -2,4 +2,4 @@
|
||||
"@kilocode/cli": patch
|
||||
---
|
||||
|
||||
CLI suggestions now render inline in the conversation at the position of the suggest tool call, instead of as a separate bar above the prompt input. This matches the VS Code extension and leaves more room for reading while scrolling. Blocking suggestions still use the above-prompt overlay.
|
||||
CLI suggestions now render inline in the conversation at the position of the suggest tool call, instead of as a separate bar above the prompt input. The inline bar renders as a single full-width row with a subtle background and clickable action buttons, matching the VS Code extension. Dismissal happens automatically when you send a new prompt. Blocking suggestions still use the above-prompt overlay.
|
||||
|
||||
@@ -23,15 +23,7 @@ import { Spinner } from "@tui/component/spinner"
|
||||
import { selectedForeground, useTheme } from "@tui/context/theme"
|
||||
import { BoxRenderable, ScrollBoxRenderable, addDefaultParsers, TextAttributes, RGBA } from "@opentui/core"
|
||||
import { Prompt, type PromptRef } from "@tui/component/prompt"
|
||||
import type {
|
||||
AssistantMessage,
|
||||
Part,
|
||||
Provider,
|
||||
ToolPart,
|
||||
UserMessage,
|
||||
TextPart,
|
||||
ReasoningPart,
|
||||
} from "@kilocode/sdk/v2"
|
||||
import type { AssistantMessage, Part, Provider, ToolPart, UserMessage, TextPart, ReasoningPart } from "@kilocode/sdk/v2"
|
||||
import { useLocal } from "@tui/context/local"
|
||||
import { Locale } from "@/util/locale"
|
||||
import type { Tool } from "@/tool/tool"
|
||||
@@ -1635,7 +1627,6 @@ function TextPart(props: { last: boolean; part: TextPart; message: AssistantMess
|
||||
function ToolPart(props: { last: boolean; part: ToolPart; message: AssistantMessage }) {
|
||||
const ctx = use()
|
||||
const sync = useSync()
|
||||
const promptRef = usePromptRef() // kilocode_change - for suggest tool inline bar
|
||||
|
||||
// Hide tool if showDetails is false and tool completed successfully
|
||||
const shouldHide = createMemo(() => {
|
||||
@@ -1724,15 +1715,7 @@ function ToolPart(props: { last: boolean; part: ToolPart; message: AssistantMess
|
||||
r.tool?.messageID === props.part.messageID,
|
||||
)
|
||||
})
|
||||
return (
|
||||
<Suggest
|
||||
{...toolprops}
|
||||
InlineTool={InlineTool}
|
||||
BlockTool={BlockTool}
|
||||
pendingRequest={pending()}
|
||||
inputFocused={() => promptRef.current?.focused ?? false}
|
||||
/>
|
||||
)
|
||||
return <Suggest {...toolprops} InlineTool={InlineTool} BlockTool={BlockTool} pendingRequest={pending()} />
|
||||
})()}
|
||||
</Match>
|
||||
{/* kilocode_change end */}
|
||||
|
||||
@@ -1,22 +1,16 @@
|
||||
/** @jsxImportSource @opentui/solid */
|
||||
// kilocode_change - new file
|
||||
|
||||
import { useKeyboard } from "@opentui/solid"
|
||||
import type { SuggestionRequest } from "@kilocode/sdk/v2"
|
||||
import { createMemo, createSignal, For, Show } from "solid-js"
|
||||
import { useKeybind } from "../../../cli/cmd/tui/context/keybind"
|
||||
import { createMemo, createSignal, For } from "solid-js"
|
||||
import { useSDK } from "../../../cli/cmd/tui/context/sdk"
|
||||
import { tint, useTheme } from "../../../cli/cmd/tui/context/theme"
|
||||
import { useDialog } from "../../../cli/cmd/tui/ui/dialog"
|
||||
import { selectedForeground, useTheme } from "../../../cli/cmd/tui/context/theme"
|
||||
|
||||
export function SuggestBar(props: { request: SuggestionRequest; inputFocused?: () => boolean }) {
|
||||
export function SuggestBar(props: { request: SuggestionRequest }) {
|
||||
const sdk = useSDK()
|
||||
const { theme } = useTheme()
|
||||
const keybind = useKeybind()
|
||||
const dialog = useDialog()
|
||||
|
||||
const options = createMemo(() => props.request.actions)
|
||||
const [selected, setSelected] = createSignal(0)
|
||||
const [busy, setBusy] = createSignal(false)
|
||||
|
||||
function accept(index: number) {
|
||||
@@ -30,80 +24,53 @@ export function SuggestBar(props: { request: SuggestionRequest; inputFocused?: (
|
||||
.catch(() => setBusy(false))
|
||||
}
|
||||
|
||||
function reject() {
|
||||
if (busy()) return
|
||||
setBusy(true)
|
||||
sdk.client.suggestion
|
||||
.dismiss({
|
||||
requestID: props.request.id,
|
||||
})
|
||||
.catch(() => setBusy(false))
|
||||
}
|
||||
|
||||
useKeyboard((evt) => {
|
||||
if (dialog.stack.length > 0) return
|
||||
if (evt.defaultPrevented) return
|
||||
|
||||
if (evt.name === "escape" || keybind.match("app_exit", evt)) {
|
||||
evt.preventDefault()
|
||||
reject()
|
||||
return
|
||||
}
|
||||
|
||||
// Skip digit shortcuts when the prompt has focus so typed digits go into
|
||||
// the prompt rather than triggering the bar. Matches the non-blocking
|
||||
// suppression pattern from the old footer overlay.
|
||||
if (props.inputFocused?.()) return
|
||||
|
||||
const max = Math.min(options().length, 2)
|
||||
const digit = Number(evt.name)
|
||||
if (!Number.isNaN(digit) && digit >= 1 && digit <= max) {
|
||||
evt.preventDefault()
|
||||
accept(digit - 1)
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<box
|
||||
marginTop={1}
|
||||
backgroundColor={theme.backgroundElement}
|
||||
flexDirection="row"
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
paddingLeft={2}
|
||||
paddingTop={0}
|
||||
paddingBottom={0}
|
||||
gap={1}
|
||||
border={["left"]}
|
||||
borderColor={theme.secondary}
|
||||
paddingRight={2}
|
||||
paddingTop={1}
|
||||
paddingBottom={1}
|
||||
flexShrink={0}
|
||||
>
|
||||
<box paddingLeft={1}>
|
||||
<box flexDirection="row" gap={1} flexShrink={1}>
|
||||
<text fg={theme.secondary}>→</text>
|
||||
<text fg={theme.text}>{props.request.text}</text>
|
||||
</box>
|
||||
<box flexDirection="row" gap={2} paddingLeft={1}>
|
||||
<box flexDirection="row" gap={1} flexShrink={0}>
|
||||
<For each={options()}>
|
||||
{(opt, i) => {
|
||||
const active = () => i() === selected()
|
||||
const [hover, setHover] = createSignal(false)
|
||||
const primary = () => i() === 0
|
||||
const bg = () => {
|
||||
if (busy()) return undefined
|
||||
if (hover()) return theme.accent
|
||||
if (primary()) return theme.backgroundPanel
|
||||
return undefined
|
||||
}
|
||||
const fg = () => {
|
||||
if (busy()) return theme.textMuted
|
||||
if (hover()) return selectedForeground(theme, theme.accent)
|
||||
return theme.text
|
||||
}
|
||||
return (
|
||||
<box
|
||||
flexDirection="row"
|
||||
onMouseOver={() => setSelected(i())}
|
||||
onMouseDown={() => setSelected(i())}
|
||||
paddingLeft={2}
|
||||
paddingRight={2}
|
||||
backgroundColor={bg()}
|
||||
onMouseOver={() => setHover(true)}
|
||||
onMouseOut={() => setHover(false)}
|
||||
onMouseUp={() => accept(i())}
|
||||
>
|
||||
<box backgroundColor={active() ? theme.backgroundElement : undefined} paddingRight={1}>
|
||||
<text fg={active() ? tint(theme.textMuted, theme.secondary, 0.6) : theme.textMuted}>
|
||||
{`${i() + 1}.`}
|
||||
</text>
|
||||
</box>
|
||||
<box backgroundColor={active() ? theme.backgroundElement : undefined} paddingRight={1}>
|
||||
<text fg={active() ? theme.secondary : theme.text}>{opt.label}</text>
|
||||
</box>
|
||||
<text fg={fg()}>{opt.label}</text>
|
||||
</box>
|
||||
)
|
||||
}}
|
||||
</For>
|
||||
<text fg={theme.textMuted}>
|
||||
<Show when={busy()} fallback={<>esc dismiss</>}>
|
||||
Waiting...
|
||||
</Show>
|
||||
</text>
|
||||
</box>
|
||||
</box>
|
||||
)
|
||||
|
||||
@@ -33,7 +33,6 @@ export function Suggest(props: {
|
||||
InlineTool: (props: InlineProps) => JSX.Element
|
||||
BlockTool: (props: BlockProps) => JSX.Element
|
||||
pendingRequest?: SuggestionRequest
|
||||
inputFocused?: () => boolean
|
||||
}) {
|
||||
const { theme } = useTheme()
|
||||
const accepted = createMemo(() => props.metadata.accepted)
|
||||
@@ -62,7 +61,7 @@ export function Suggest(props: {
|
||||
}
|
||||
</Match>
|
||||
<Match when={props.pendingRequest} keyed>
|
||||
{(request) => <SuggestBar request={request} inputFocused={props.inputFocused} />}
|
||||
{(request) => <SuggestBar request={request} />}
|
||||
</Match>
|
||||
<Match when={true}>
|
||||
{(_) =>
|
||||
|
||||
Reference in New Issue
Block a user