From 39878aff00bd147408f132642c14d5b990b6747b Mon Sep 17 00:00:00 2001 From: Brooklyn Nicholson Date: Mon, 6 Apr 2026 18:40:21 -0500 Subject: [PATCH] chore: uptick --- ui-tui/src/app.tsx | 49 ++++++++++++-------- ui-tui/src/components/messageLine.tsx | 5 +- ui-tui/src/components/thinking.tsx | 67 ++++++++++++++++----------- 3 files changed, 72 insertions(+), 49 deletions(-) diff --git a/ui-tui/src/app.tsx b/ui-tui/src/app.tsx index b52a305080..51369fc737 100644 --- a/ui-tui/src/app.tsx +++ b/ui-tui/src/app.tsx @@ -185,29 +185,38 @@ export function App({ gw }: { gw: GatewayClient }) { const sys = useCallback((text: string) => setMessages(prev => [...prev, { role: 'system' as const, text }]), []) - const rpc = (method: string, params: Record = {}) => - gw.request(method, params).catch((e: Error) => { - sys(`error: ${e.message}`) - }) + const colsRef = useRef(cols) + colsRef.current = cols - const newSession = (msg?: string) => - rpc('session.create', { cols }).then((r: any) => { - if (!r) { - return - } + const rpc = useCallback( + (method: string, params: Record = {}) => + gw.request(method, params).catch((e: Error) => { + sys(`error: ${e.message}`) + }), + [gw, sys] + ) - setSid(r.session_id) - setMessages([]) - setUsage(ZERO) - setStatus('ready') - lastStatusNoteRef.current = '' - protocolWarnedRef.current = false - stderrWarnedRef.current = false + const newSession = useCallback( + (msg?: string) => + rpc('session.create', { cols: colsRef.current }).then((r: any) => { + if (!r) { + return + } - if (msg) { - sys(msg) - } - }) + setSid(r.session_id) + setMessages([]) + setUsage(ZERO) + setStatus('ready') + lastStatusNoteRef.current = '' + protocolWarnedRef.current = false + stderrWarnedRef.current = false + + if (msg) { + sys(msg) + } + }), + [rpc, sys] + ) const idle = () => { setThinking(false) diff --git a/ui-tui/src/components/messageLine.tsx b/ui-tui/src/components/messageLine.tsx index b2e8c914e6..5b9f4659a5 100644 --- a/ui-tui/src/components/messageLine.tsx +++ b/ui-tui/src/components/messageLine.tsx @@ -1,4 +1,5 @@ import { Box, Text } from 'ink' +import { memo } from 'react' import { LONG_MSG, ROLE } from '../constants.js' import { hasAnsi, userDisplay } from '../lib/text.js' @@ -7,7 +8,7 @@ import type { Msg } from '../types.js' import { Md } from './markdown.js' -export function MessageLine({ compact, msg, t }: { compact?: boolean; msg: Msg; t: Theme }) { +export const MessageLine = memo(function MessageLine({ compact, msg, t }: { compact?: boolean; msg: Msg; t: Theme }) { const { body, glyph, prefix } = ROLE[msg.role](t) const content = (() => { @@ -47,4 +48,4 @@ export function MessageLine({ compact, msg, t }: { compact?: boolean; msg: Msg; {content} ) -} +}) diff --git a/ui-tui/src/components/thinking.tsx b/ui-tui/src/components/thinking.tsx index e789ee7435..71e3ebf4f3 100644 --- a/ui-tui/src/components/thinking.tsx +++ b/ui-tui/src/components/thinking.tsx @@ -1,12 +1,26 @@ -import { Box, Text } from 'ink' -import { useEffect, useState } from 'react' +import { Text } from 'ink' +import { memo, useEffect, useRef, useState } from 'react' import { FACES, SPINNER, TOOL_VERBS, VERBS } from '../constants.js' import { pick } from '../lib/text.js' import type { Theme } from '../theme.js' import type { ActiveTool } from '../types.js' -export function Thinking({ +function SpinnerChar({ color }: { color: string }) { + const ref = useRef(0) + + useEffect(() => { + const id = setInterval(() => { + ref.current = (ref.current + 1) % SPINNER.length + }, 80) + + return () => clearInterval(id) + }, []) + + return {SPINNER[ref.current]} +} + +export const Thinking = memo(function Thinking({ reasoning, t, thinking, @@ -17,35 +31,34 @@ export function Thinking({ thinking?: string tools: ActiveTool[] }) { - const [frame, setFrame] = useState(0) const [verb] = useState(() => pick(VERBS)) const [face] = useState(() => pick(FACES)) - useEffect(() => { - const id = setInterval(() => setFrame(f => (f + 1) % SPINNER.length), 80) - - return () => clearInterval(id) - }, []) - const tail = (reasoning || thinking || '').slice(-120).replace(/\n/g, ' ') - return ( - - {tools.length ? ( - tools.map(tool => ( + if (tools.length) { + return ( + <> + {tools.map(tool => ( - {SPINNER[frame]} {TOOL_VERBS[tool.name] ?? '⚡ ' + tool.name}… + ⚡ {TOOL_VERBS[tool.name] ?? tool.name}… - )) - ) : tail ? ( - - {SPINNER[frame]} 💭 {tail} - - ) : ( - - {SPINNER[frame]} {face} {verb}… - - )} - + ))} + + ) + } + + if (tail) { + return ( + + 💭 {tail} + + ) + } + + return ( + + {face} {verb}… + ) -} +})