summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorKit Langton <[email protected]>2026-04-17 12:45:29 -0400
committerGitHub <[email protected]>2026-04-17 12:45:29 -0400
commit13dfe569efda341cb7b6d4d163e4aee471e65043 (patch)
treea1ae18fca354ffefc061d9a5e6a85934617f6c46
parentc491161c0c6b69fe95672bba395a13b506940512 (diff)
downloadopencode-13dfe569efda341cb7b6d4d163e4aee471e65043.tar.gz
opencode-13dfe569efda341cb7b6d4d163e4aee471e65043.zip
tui: fix agent cycling and prompt metadata polish (#23115)
-rw-r--r--packages/opencode/src/cli/cmd/tui/component/dialog-command.tsx1
-rw-r--r--packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx24
-rw-r--r--packages/opencode/src/cli/cmd/tui/context/local.tsx4
-rw-r--r--packages/opencode/src/cli/cmd/tui/util/signal.ts7
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(() => {