diff options
| author | Kit Langton <[email protected]> | 2026-04-17 12:45:29 -0400 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-04-17 12:45:29 -0400 |
| commit | 13dfe569efda341cb7b6d4d163e4aee471e65043 (patch) | |
| tree | a1ae18fca354ffefc061d9a5e6a85934617f6c46 | |
| parent | c491161c0c6b69fe95672bba395a13b506940512 (diff) | |
| download | opencode-13dfe569efda341cb7b6d4d163e4aee471e65043.tar.gz opencode-13dfe569efda341cb7b6d4d163e4aee471e65043.zip | |
tui: fix agent cycling and prompt metadata polish (#23115)
4 files changed, 24 insertions, 12 deletions
diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-command.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-command.tsx index f42ba15ec..49bf42c63 100644 --- a/packages/opencode/src/cli/cmd/tui/component/dialog-command.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/dialog-command.tsx @@ -63,6 +63,7 @@ function init() { useKeyboard((evt) => { if (suspended()) return if (dialog.stack.length > 0) return + if (evt.defaultPrevented) return for (const option of entries()) { if (!isEnabled(option)) continue if (option.keybind && keybind.match(option.keybind, evt)) { diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx index 06e5a0884..98527bf91 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx @@ -5,7 +5,7 @@ import path from "path" import { fileURLToPath } from "url" import { Filesystem } from "@/util" import { useLocal } from "@tui/context/local" -import { useTheme } from "@tui/context/theme" +import { tint, useTheme } from "@tui/context/theme" import { EmptyBorder, SplitBorder } from "@tui/component/border" import { useSDK } from "@tui/context/sdk" import { useRoute } from "@tui/context/route" @@ -463,19 +463,25 @@ export function Prompt(props: PromptProps) { createEffect(() => { if (!input || input.isDestroyed) return if (props.visible === false || dialog.stack.length > 0) { - input.blur() + if (input.focused) input.blur() return } // Slot/plugin updates can remount the background prompt while a dialog is open. // Keep focus with the dialog and let the prompt reclaim it after the dialog closes. - input.focus() + if (!input.focused) input.focus() }) createEffect(() => { if (!input || input.isDestroyed) return + const capture = + store.mode === "normal" + ? auto()?.visible + ? (["escape", "navigate", "submit", "tab"] as const) + : (["tab"] as const) + : undefined input.traits = { - capture: auto()?.visible ? ["escape", "navigate", "submit", "tab"] : undefined, + capture, suspend: !!props.disabled || store.mode === "shell", status: store.mode === "shell" ? "SHELL" : undefined, } @@ -870,6 +876,7 @@ export function Prompt(props: PromptProps) { () => !!local.agent.current() && store.mode === "normal" && showVariant(), animationsEnabled, ) + const borderHighlight = createMemo(() => tint(theme.border, highlight(), agentMetaAlpha())) const placeholderText = createMemo(() => { if (props.showPlaceholder === false) return undefined @@ -931,7 +938,7 @@ export function Prompt(props: PromptProps) { <box ref={(r) => (anchor = r)} visible={props.visible !== false}> <box border={["left"]} - borderColor={highlight()} + borderColor={borderHighlight()} customBorderChars={{ ...SplitBorder.customBorderChars, bottomLeft: "╹", @@ -1146,11 +1153,10 @@ export function Prompt(props: PromptProps) { <Show when={local.agent.current()} fallback={<box height={1} />}> {(agent) => ( <> - <text fg={fadeColor(highlight(), agentMetaAlpha())}> - {store.mode === "shell" ? "Shell" : Locale.titlecase(agent().name)}{" "} - </text> + <text fg={fadeColor(highlight(), agentMetaAlpha())}>{store.mode === "shell" ? "Shell" : Locale.titlecase(agent().name)}</text> <Show when={store.mode === "normal"}> <box flexDirection="row" gap={1}> + <text fg={fadeColor(theme.textMuted, modelMetaAlpha())}>·</text> <text flexShrink={0} fg={fadeColor(keybind.leader ? theme.textMuted : theme.text, modelMetaAlpha())} @@ -1183,7 +1189,7 @@ export function Prompt(props: PromptProps) { <box height={1} border={["left"]} - borderColor={highlight()} + borderColor={borderHighlight()} customBorderChars={{ ...EmptyBorder, vertical: theme.backgroundElement.a !== 0 ? "╹" : " ", diff --git a/packages/opencode/src/cli/cmd/tui/context/local.tsx b/packages/opencode/src/cli/cmd/tui/context/local.tsx index bb73c6537..910483764 100644 --- a/packages/opencode/src/cli/cmd/tui/context/local.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/local.tsx @@ -75,7 +75,9 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ }, move(direction: 1 | -1) { batch(() => { - let next = agents().findIndex((x) => x.name === agentStore.current) + direction + const current = this.current() + if (!current) return + let next = agents().findIndex((x) => x.name === current.name) + direction if (next < 0) next = agents().length - 1 if (next >= agents().length) next = 0 const value = agents()[next] diff --git a/packages/opencode/src/cli/cmd/tui/util/signal.ts b/packages/opencode/src/cli/cmd/tui/util/signal.ts index 1c7cc0008..7d20ae04b 100644 --- a/packages/opencode/src/cli/cmd/tui/util/signal.ts +++ b/packages/opencode/src/cli/cmd/tui/util/signal.ts @@ -8,20 +8,23 @@ export function createDebouncedSignal<T>(value: T, ms: number): [Accessor<T>, Sc export function createFadeIn(show: Accessor<boolean>, enabled: Accessor<boolean>) { const [alpha, setAlpha] = createSignal(show() ? 1 : 0) + let revealed = show() createEffect( - on([show, enabled], ([visible, animate], previous) => { + on([show, enabled], ([visible, animate]) => { if (!visible) { setAlpha(0) return } - if (!animate || !previous) { + if (!animate || revealed) { + revealed = true setAlpha(1) return } const start = performance.now() + revealed = true setAlpha(0) const timer = setInterval(() => { |
