diff --git a/tui_gateway/server.py b/tui_gateway/server.py index 1239ea7197..39def65eb5 100644 --- a/tui_gateway/server.py +++ b/tui_gateway/server.py @@ -3743,7 +3743,13 @@ def _details_completions(text: str) -> list[dict] | None: return [ _details_completion_item( candidate, - "section override" if candidate in sections else "global mode", + ( + "section override" + if candidate in sections + else "cycle global mode" + if candidate == "cycle" + else "global mode" + ), ) for candidate in candidates if candidate.startswith(prefix) and candidate != prefix diff --git a/ui-tui/src/app/slash/commands/session.ts b/ui-tui/src/app/slash/commands/session.ts index 7cb7fcf835..e91dd421f5 100644 --- a/ui-tui/src/app/slash/commands/session.ts +++ b/ui-tui/src/app/slash/commands/session.ts @@ -21,7 +21,7 @@ const GLOBAL_MODEL_FLAG_RE = /(?:^|\s)--global(?:\s|$)/ const persistedModelArg = (arg: string) => { const trimmed = arg.trim() - return GLOBAL_MODEL_FLAG_RE.test(trimmed) ? trimmed : `${trimmed} --global` + return !trimmed || GLOBAL_MODEL_FLAG_RE.test(trimmed) ? trimmed : `${trimmed} --global` } export const sessionCommands: SlashCommand[] = [ @@ -73,7 +73,7 @@ export const sessionCommands: SlashCommand[] = [ return } - if (!arg) { + if (!arg.trim()) { return patchOverlayState({ modelPicker: true }) } diff --git a/ui-tui/src/app/useSubmission.ts b/ui-tui/src/app/useSubmission.ts index b499bfd8f7..70a3faf329 100644 --- a/ui-tui/src/app/useSubmission.ts +++ b/ui-tui/src/app/useSubmission.ts @@ -51,24 +51,29 @@ export function useSubmission(opts: UseSubmissionOptions) { const typingIdleTimer = useRef | null>(null) useEffect(() => { - if (composerState.input || composerState.inputBuf.length) { - if (getUiState().busy) { - turnController.boostStreamingForTyping() - } - - if (typingIdleTimer.current) { - clearTimeout(typingIdleTimer.current) - } - - typingIdleTimer.current = setTimeout(() => { - typingIdleTimer.current = null - turnController.relaxStreaming() - }, TYPING_IDLE_MS) + if (typingIdleTimer.current) { + clearTimeout(typingIdleTimer.current) + typingIdleTimer.current = null } + if (!composerState.input && !composerState.inputBuf.length) { + turnController.relaxStreaming() + return + } + + if (getUiState().busy) { + turnController.boostStreamingForTyping() + } + + typingIdleTimer.current = setTimeout(() => { + typingIdleTimer.current = null + turnController.relaxStreaming() + }, TYPING_IDLE_MS) + return () => { if (typingIdleTimer.current) { clearTimeout(typingIdleTimer.current) + typingIdleTimer.current = null } } }, [composerState.input, composerState.inputBuf]) diff --git a/ui-tui/src/components/appLayout.tsx b/ui-tui/src/components/appLayout.tsx index b9e9fece76..170d0649ac 100644 --- a/ui-tui/src/components/appLayout.tsx +++ b/ui-tui/src/components/appLayout.tsx @@ -110,16 +110,6 @@ const TranscriptPane = memo(function TranscriptPane({ <> - - {transcript.virtualHistory.topSpacer > 0 ? : null} {transcript.virtualRows.slice(transcript.virtualHistory.start, transcript.virtualHistory.end).map(row => ( @@ -147,6 +137,15 @@ const TranscriptPane = memo(function TranscriptPane({ {transcript.virtualHistory.bottomSpacer > 0 ? : null} + diff --git a/ui-tui/src/components/textInput.tsx b/ui-tui/src/components/textInput.tsx index 9b916c4623..9f8b299424 100644 --- a/ui-tui/src/components/textInput.tsx +++ b/ui-tui/src/components/textInput.tsx @@ -431,13 +431,11 @@ export function TextInput({ parentChangeTimer.current = setTimeout(flushParentChange, 16) } - const flushLocalRender = () => { + const cancelLocalRender = () => { if (localRenderTimer.current) { clearTimeout(localRenderTimer.current) localRenderTimer.current = null } - - setCur(curRef.current) } const scheduleLocalRender = () => { @@ -445,7 +443,10 @@ export function TextInput({ return } - localRenderTimer.current = setTimeout(flushLocalRender, 16) + localRenderTimer.current = setTimeout(() => { + localRenderTimer.current = null + setCur(curRef.current) + }, 16) } const canFastEchoBase = () => focus && termFocus && !selected && !mask && !!stdout?.isTTY @@ -468,9 +469,7 @@ export function TextInput({ return false } - const prev = current[cursor - 1] - - return !!prev && stringWidth(prev) === 1 + return stringWidth(current.slice(prevPos(current, cursor), cursor)) === 1 } const commit = (next: string, nextCur: number, track = true, syncParent = true, syncLocal = true) => { @@ -494,7 +493,7 @@ export function TextInput({ } if (syncLocal) { - flushLocalRender() + cancelLocalRender() setCur(c) } else { scheduleLocalRender() diff --git a/ui-tui/src/hooks/useVirtualHistory.ts b/ui-tui/src/hooks/useVirtualHistory.ts index e8565e8cb0..17c93a7565 100644 --- a/ui-tui/src/hooks/useVirtualHistory.ts +++ b/ui-tui/src/hooks/useVirtualHistory.ts @@ -235,7 +235,7 @@ export function useVirtualHistory( if (dirty) { setVer(v => v + 1) } - }, [end, hasScrollRef, items, n, offsets, scrollRef, start, total, vp]) + }, [end, hasScrollRef, items, n, offsets, scrollRef, start, sticky, total, vp]) return { bottomSpacer: Math.max(0, total - (offsets[end] ?? total)),