Skip to content

Commit 13dfe56

Browse files
authored
tui: fix agent cycling and prompt metadata polish (#23115)
1 parent c491161 commit 13dfe56

4 files changed

Lines changed: 24 additions & 12 deletions

File tree

packages/opencode/src/cli/cmd/tui/component/dialog-command.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ function init() {
6363
useKeyboard((evt) => {
6464
if (suspended()) return
6565
if (dialog.stack.length > 0) return
66+
if (evt.defaultPrevented) return
6667
for (const option of entries()) {
6768
if (!isEnabled(option)) continue
6869
if (option.keybind && keybind.match(option.keybind, evt)) {

packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import path from "path"
55
import { fileURLToPath } from "url"
66
import { Filesystem } from "@/util"
77
import { useLocal } from "@tui/context/local"
8-
import { useTheme } from "@tui/context/theme"
8+
import { tint, useTheme } from "@tui/context/theme"
99
import { EmptyBorder, SplitBorder } from "@tui/component/border"
1010
import { useSDK } from "@tui/context/sdk"
1111
import { useRoute } from "@tui/context/route"
@@ -463,19 +463,25 @@ export function Prompt(props: PromptProps) {
463463
createEffect(() => {
464464
if (!input || input.isDestroyed) return
465465
if (props.visible === false || dialog.stack.length > 0) {
466-
input.blur()
466+
if (input.focused) input.blur()
467467
return
468468
}
469469

470470
// Slot/plugin updates can remount the background prompt while a dialog is open.
471471
// Keep focus with the dialog and let the prompt reclaim it after the dialog closes.
472-
input.focus()
472+
if (!input.focused) input.focus()
473473
})
474474

475475
createEffect(() => {
476476
if (!input || input.isDestroyed) return
477+
const capture =
478+
store.mode === "normal"
479+
? auto()?.visible
480+
? (["escape", "navigate", "submit", "tab"] as const)
481+
: (["tab"] as const)
482+
: undefined
477483
input.traits = {
478-
capture: auto()?.visible ? ["escape", "navigate", "submit", "tab"] : undefined,
484+
capture,
479485
suspend: !!props.disabled || store.mode === "shell",
480486
status: store.mode === "shell" ? "SHELL" : undefined,
481487
}
@@ -870,6 +876,7 @@ export function Prompt(props: PromptProps) {
870876
() => !!local.agent.current() && store.mode === "normal" && showVariant(),
871877
animationsEnabled,
872878
)
879+
const borderHighlight = createMemo(() => tint(theme.border, highlight(), agentMetaAlpha()))
873880

874881
const placeholderText = createMemo(() => {
875882
if (props.showPlaceholder === false) return undefined
@@ -931,7 +938,7 @@ export function Prompt(props: PromptProps) {
931938
<box ref={(r) => (anchor = r)} visible={props.visible !== false}>
932939
<box
933940
border={["left"]}
934-
borderColor={highlight()}
941+
borderColor={borderHighlight()}
935942
customBorderChars={{
936943
...SplitBorder.customBorderChars,
937944
bottomLeft: "╹",
@@ -1146,11 +1153,10 @@ export function Prompt(props: PromptProps) {
11461153
<Show when={local.agent.current()} fallback={<box height={1} />}>
11471154
{(agent) => (
11481155
<>
1149-
<text fg={fadeColor(highlight(), agentMetaAlpha())}>
1150-
{store.mode === "shell" ? "Shell" : Locale.titlecase(agent().name)}{" "}
1151-
</text>
1156+
<text fg={fadeColor(highlight(), agentMetaAlpha())}>{store.mode === "shell" ? "Shell" : Locale.titlecase(agent().name)}</text>
11521157
<Show when={store.mode === "normal"}>
11531158
<box flexDirection="row" gap={1}>
1159+
<text fg={fadeColor(theme.textMuted, modelMetaAlpha())}>·</text>
11541160
<text
11551161
flexShrink={0}
11561162
fg={fadeColor(keybind.leader ? theme.textMuted : theme.text, modelMetaAlpha())}
@@ -1183,7 +1189,7 @@ export function Prompt(props: PromptProps) {
11831189
<box
11841190
height={1}
11851191
border={["left"]}
1186-
borderColor={highlight()}
1192+
borderColor={borderHighlight()}
11871193
customBorderChars={{
11881194
...EmptyBorder,
11891195
vertical: theme.backgroundElement.a !== 0 ? "╹" : " ",

packages/opencode/src/cli/cmd/tui/context/local.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,9 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
7575
},
7676
move(direction: 1 | -1) {
7777
batch(() => {
78-
let next = agents().findIndex((x) => x.name === agentStore.current) + direction
78+
const current = this.current()
79+
if (!current) return
80+
let next = agents().findIndex((x) => x.name === current.name) + direction
7981
if (next < 0) next = agents().length - 1
8082
if (next >= agents().length) next = 0
8183
const value = agents()[next]

packages/opencode/src/cli/cmd/tui/util/signal.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,23 @@ export function createDebouncedSignal<T>(value: T, ms: number): [Accessor<T>, Sc
88

99
export function createFadeIn(show: Accessor<boolean>, enabled: Accessor<boolean>) {
1010
const [alpha, setAlpha] = createSignal(show() ? 1 : 0)
11+
let revealed = show()
1112

1213
createEffect(
13-
on([show, enabled], ([visible, animate], previous) => {
14+
on([show, enabled], ([visible, animate]) => {
1415
if (!visible) {
1516
setAlpha(0)
1617
return
1718
}
1819

19-
if (!animate || !previous) {
20+
if (!animate || revealed) {
21+
revealed = true
2022
setAlpha(1)
2123
return
2224
}
2325

2426
const start = performance.now()
27+
revealed = true
2528
setAlpha(0)
2629

2730
const timer = setInterval(() => {

0 commit comments

Comments
 (0)