feat(tui): put the kawaii face+verb ticker in the status bar, not the thinking panel
The status bar was showing stale lifecycle text ("running…") while the
face+verb stream flickered through the thinking panel as Python pushed
thinking.delta events. That's backwards — the face ticker is the
primary "I'm alive" signal, it belongs in the status bar; the thinking
panel is for substantive reasoning and tool activity.
Status bar now reads `ui.busy`: when true, renders a local `<FaceTicker>`
cycling FACES × VERBS on a 2.5s interval, unaffected by server events.
When false, the bar shows the actual status string (ready, starting
agent…, interrupted, etc.).
Side effect: `scheduleThinkingStatus` still patches `ui.status` with
Python's face text, but while busy the bar ignores that string and uses
the ticker instead. No server-side changes needed — Python keeps
emitting thinking.delta as a liveness heartbeat, the TUI just doesn't
let it fight the status bar.
This commit is contained in:
@@ -1,12 +1,32 @@
|
||||
import { Box, type ScrollBoxHandle, Text } from '@hermes/ink'
|
||||
import { type ReactNode, type RefObject, useCallback, useEffect, useState, useSyncExternalStore } from 'react'
|
||||
|
||||
import { FACES } from '../content/faces.js'
|
||||
import { VERBS } from '../content/verbs.js'
|
||||
import { fmtDuration } from '../domain/messages.js'
|
||||
import { stickyPromptFromViewport } from '../domain/viewport.js'
|
||||
import { fmtK } from '../lib/text.js'
|
||||
import type { Theme } from '../theme.js'
|
||||
import type { Msg, Usage } from '../types.js'
|
||||
|
||||
const FACE_TICK_MS = 2500
|
||||
|
||||
function FaceTicker({ color }: { color: string }) {
|
||||
const [tick, setTick] = useState(() => Math.floor(Math.random() * 1000))
|
||||
|
||||
useEffect(() => {
|
||||
const id = setInterval(() => setTick(n => n + 1), FACE_TICK_MS)
|
||||
|
||||
return () => clearInterval(id)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Text color={color}>
|
||||
{FACES[tick % FACES.length]} {VERBS[tick % VERBS.length]}…
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
|
||||
function ctxBarColor(pct: number | undefined, t: Theme) {
|
||||
if (pct == null) {
|
||||
return t.color.dim
|
||||
@@ -73,6 +93,7 @@ export function GoodVibesHeart({ tick, t }: { tick: number; t: Theme }) {
|
||||
export function StatusRule({
|
||||
cwdLabel,
|
||||
cols,
|
||||
busy,
|
||||
status,
|
||||
statusColor,
|
||||
model,
|
||||
@@ -84,6 +105,7 @@ export function StatusRule({
|
||||
}: {
|
||||
cwdLabel: string
|
||||
cols: number
|
||||
busy: boolean
|
||||
status: string
|
||||
statusColor: string
|
||||
model: string
|
||||
@@ -111,7 +133,7 @@ export function StatusRule({
|
||||
<Box flexShrink={1} width={leftWidth}>
|
||||
<Text color={t.color.bronze} wrap="truncate-end">
|
||||
{'─ '}
|
||||
<Text color={statusColor}>{status}</Text>
|
||||
{busy ? <FaceTicker color={statusColor} /> : <Text color={statusColor}>{status}</Text>}
|
||||
<Text color={t.color.dim}> │ {model}</Text>
|
||||
{ctxLabel ? <Text color={t.color.dim}> │ {ctxLabel}</Text> : null}
|
||||
{bar ? (
|
||||
|
||||
@@ -138,6 +138,7 @@ const ComposerPane = memo(function ComposerPane({
|
||||
{ui.statusBar && (
|
||||
<StatusRule
|
||||
bgCount={ui.bgTasks.size}
|
||||
busy={ui.busy}
|
||||
cols={composer.cols}
|
||||
cwdLabel={status.cwdLabel}
|
||||
model={ui.info?.model?.split('/').pop() ?? ''}
|
||||
@@ -181,7 +182,7 @@ const ComposerPane = memo(function ComposerPane({
|
||||
{sh ? (
|
||||
<Text color={ui.theme.color.shellDollar}>$ </Text>
|
||||
) : (
|
||||
<Text bold color={ui.theme.color.gold}>
|
||||
<Text bold color={ui.theme.color.prompt}>
|
||||
{composer.inputBuf.length ? ' ' : `${ui.theme.brand.prompt} `}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
@@ -106,7 +106,12 @@ export function SessionPanel({ info, sid, t }: { info: SessionInfo; sid?: string
|
||||
<Text color={t.color.dim} wrap="truncate-end">
|
||||
{cwd}
|
||||
</Text>
|
||||
{sid && <Text color={t.color.dim}>Session: {sid}</Text>}
|
||||
{sid && (
|
||||
<Text>
|
||||
<Text color={t.color.sessionLabel}>Session: </Text>
|
||||
<Text color={t.color.sessionBorder}>{sid}</Text>
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
<Box flexDirection="column" width={w}>
|
||||
|
||||
Reference in New Issue
Block a user