tui: fix agent cycling and prompt metadata polish (#23115)
This commit is contained in:
@@ -63,6 +63,7 @@ function init() {
|
|||||||
useKeyboard((evt) => {
|
useKeyboard((evt) => {
|
||||||
if (suspended()) return
|
if (suspended()) return
|
||||||
if (dialog.stack.length > 0) return
|
if (dialog.stack.length > 0) return
|
||||||
|
if (evt.defaultPrevented) return
|
||||||
for (const option of entries()) {
|
for (const option of entries()) {
|
||||||
if (!isEnabled(option)) continue
|
if (!isEnabled(option)) continue
|
||||||
if (option.keybind && keybind.match(option.keybind, evt)) {
|
if (option.keybind && keybind.match(option.keybind, evt)) {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import path from "path"
|
|||||||
import { fileURLToPath } from "url"
|
import { fileURLToPath } from "url"
|
||||||
import { Filesystem } from "@/util"
|
import { Filesystem } from "@/util"
|
||||||
import { useLocal } from "@tui/context/local"
|
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 { EmptyBorder, SplitBorder } from "@tui/component/border"
|
||||||
import { useSDK } from "@tui/context/sdk"
|
import { useSDK } from "@tui/context/sdk"
|
||||||
import { useRoute } from "@tui/context/route"
|
import { useRoute } from "@tui/context/route"
|
||||||
@@ -463,19 +463,25 @@ export function Prompt(props: PromptProps) {
|
|||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
if (!input || input.isDestroyed) return
|
if (!input || input.isDestroyed) return
|
||||||
if (props.visible === false || dialog.stack.length > 0) {
|
if (props.visible === false || dialog.stack.length > 0) {
|
||||||
input.blur()
|
if (input.focused) input.blur()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Slot/plugin updates can remount the background prompt while a dialog is open.
|
// 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.
|
// Keep focus with the dialog and let the prompt reclaim it after the dialog closes.
|
||||||
input.focus()
|
if (!input.focused) input.focus()
|
||||||
})
|
})
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
if (!input || input.isDestroyed) return
|
if (!input || input.isDestroyed) return
|
||||||
|
const capture =
|
||||||
|
store.mode === "normal"
|
||||||
|
? auto()?.visible
|
||||||
|
? (["escape", "navigate", "submit", "tab"] as const)
|
||||||
|
: (["tab"] as const)
|
||||||
|
: undefined
|
||||||
input.traits = {
|
input.traits = {
|
||||||
capture: auto()?.visible ? ["escape", "navigate", "submit", "tab"] : undefined,
|
capture,
|
||||||
suspend: !!props.disabled || store.mode === "shell",
|
suspend: !!props.disabled || store.mode === "shell",
|
||||||
status: store.mode === "shell" ? "SHELL" : undefined,
|
status: store.mode === "shell" ? "SHELL" : undefined,
|
||||||
}
|
}
|
||||||
@@ -870,6 +876,7 @@ export function Prompt(props: PromptProps) {
|
|||||||
() => !!local.agent.current() && store.mode === "normal" && showVariant(),
|
() => !!local.agent.current() && store.mode === "normal" && showVariant(),
|
||||||
animationsEnabled,
|
animationsEnabled,
|
||||||
)
|
)
|
||||||
|
const borderHighlight = createMemo(() => tint(theme.border, highlight(), agentMetaAlpha()))
|
||||||
|
|
||||||
const placeholderText = createMemo(() => {
|
const placeholderText = createMemo(() => {
|
||||||
if (props.showPlaceholder === false) return undefined
|
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 ref={(r) => (anchor = r)} visible={props.visible !== false}>
|
||||||
<box
|
<box
|
||||||
border={["left"]}
|
border={["left"]}
|
||||||
borderColor={highlight()}
|
borderColor={borderHighlight()}
|
||||||
customBorderChars={{
|
customBorderChars={{
|
||||||
...SplitBorder.customBorderChars,
|
...SplitBorder.customBorderChars,
|
||||||
bottomLeft: "╹",
|
bottomLeft: "╹",
|
||||||
@@ -1146,11 +1153,10 @@ export function Prompt(props: PromptProps) {
|
|||||||
<Show when={local.agent.current()} fallback={<box height={1} />}>
|
<Show when={local.agent.current()} fallback={<box height={1} />}>
|
||||||
{(agent) => (
|
{(agent) => (
|
||||||
<>
|
<>
|
||||||
<text fg={fadeColor(highlight(), agentMetaAlpha())}>
|
<text fg={fadeColor(highlight(), agentMetaAlpha())}>{store.mode === "shell" ? "Shell" : Locale.titlecase(agent().name)}</text>
|
||||||
{store.mode === "shell" ? "Shell" : Locale.titlecase(agent().name)}{" "}
|
|
||||||
</text>
|
|
||||||
<Show when={store.mode === "normal"}>
|
<Show when={store.mode === "normal"}>
|
||||||
<box flexDirection="row" gap={1}>
|
<box flexDirection="row" gap={1}>
|
||||||
|
<text fg={fadeColor(theme.textMuted, modelMetaAlpha())}>·</text>
|
||||||
<text
|
<text
|
||||||
flexShrink={0}
|
flexShrink={0}
|
||||||
fg={fadeColor(keybind.leader ? theme.textMuted : theme.text, modelMetaAlpha())}
|
fg={fadeColor(keybind.leader ? theme.textMuted : theme.text, modelMetaAlpha())}
|
||||||
@@ -1183,7 +1189,7 @@ export function Prompt(props: PromptProps) {
|
|||||||
<box
|
<box
|
||||||
height={1}
|
height={1}
|
||||||
border={["left"]}
|
border={["left"]}
|
||||||
borderColor={highlight()}
|
borderColor={borderHighlight()}
|
||||||
customBorderChars={{
|
customBorderChars={{
|
||||||
...EmptyBorder,
|
...EmptyBorder,
|
||||||
vertical: theme.backgroundElement.a !== 0 ? "╹" : " ",
|
vertical: theme.backgroundElement.a !== 0 ? "╹" : " ",
|
||||||
|
|||||||
@@ -75,7 +75,9 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
|||||||
},
|
},
|
||||||
move(direction: 1 | -1) {
|
move(direction: 1 | -1) {
|
||||||
batch(() => {
|
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 < 0) next = agents().length - 1
|
||||||
if (next >= agents().length) next = 0
|
if (next >= agents().length) next = 0
|
||||||
const value = agents()[next]
|
const value = agents()[next]
|
||||||
|
|||||||
@@ -8,20 +8,23 @@ export function createDebouncedSignal<T>(value: T, ms: number): [Accessor<T>, Sc
|
|||||||
|
|
||||||
export function createFadeIn(show: Accessor<boolean>, enabled: Accessor<boolean>) {
|
export function createFadeIn(show: Accessor<boolean>, enabled: Accessor<boolean>) {
|
||||||
const [alpha, setAlpha] = createSignal(show() ? 1 : 0)
|
const [alpha, setAlpha] = createSignal(show() ? 1 : 0)
|
||||||
|
let revealed = show()
|
||||||
|
|
||||||
createEffect(
|
createEffect(
|
||||||
on([show, enabled], ([visible, animate], previous) => {
|
on([show, enabled], ([visible, animate]) => {
|
||||||
if (!visible) {
|
if (!visible) {
|
||||||
setAlpha(0)
|
setAlpha(0)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!animate || !previous) {
|
if (!animate || revealed) {
|
||||||
|
revealed = true
|
||||||
setAlpha(1)
|
setAlpha(1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const start = performance.now()
|
const start = performance.now()
|
||||||
|
revealed = true
|
||||||
setAlpha(0)
|
setAlpha(0)
|
||||||
|
|
||||||
const timer = setInterval(() => {
|
const timer = setInterval(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user