mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-02 02:01:47 +00:00
fix(tui): hydrate lazy startup panel and use animated loaders
The lazy startup panel could remain stuck on the placeholder when no first prompt was submitted because agent construction only started from _sess(). Keep session.create cheap, but schedule _start_agent_build shortly after returning the placeholder so tools/skills hydrate automatically. Also replace the ugly placeholder bar rows with compact unicode-animations braille loaders for the tools and skills sections. Tests: - python -m py_compile tui_gateway/server.py - cd ui-tui && npm run type-check && npm run build - cd ui-tui && npm test -- --run src/__tests__/useSessionLifecycle.test.ts src/__tests__/useConfigSync.test.ts - scripts/run_tests.sh tests/tui_gateway/test_protocol.py::test_sess_found tests/tools/test_code_execution_modes.py tests/tools/test_code_execution.py
This commit is contained in:
parent
b66cbb7b4c
commit
0a6ecea676
2 changed files with 43 additions and 14 deletions
|
|
@ -1,10 +1,32 @@
|
|||
import { Box, Text, useStdout } from '@hermes/ink'
|
||||
import { useEffect, useState } from 'react'
|
||||
import unicodeSpinners from 'unicode-animations'
|
||||
|
||||
import { artWidth, caduceus, CADUCEUS_WIDTH, logo, LOGO_WIDTH } from '../banner.js'
|
||||
import { flat } from '../lib/text.js'
|
||||
import type { Theme } from '../theme.js'
|
||||
import type { PanelSection, SessionInfo } from '../types.js'
|
||||
|
||||
const LOADER_TICK_MS = 120
|
||||
|
||||
function InlineLoader({ label, t }: { label: string; t: Theme }) {
|
||||
const [tick, setTick] = useState(0)
|
||||
const spinner = unicodeSpinners.braille
|
||||
const frame = spinner.frames[tick % spinner.frames.length] ?? '⠋'
|
||||
|
||||
useEffect(() => {
|
||||
const id = setInterval(() => setTick(n => n + 1), Math.max(LOADER_TICK_MS, spinner.interval))
|
||||
|
||||
return () => clearInterval(id)
|
||||
}, [spinner.interval])
|
||||
|
||||
return (
|
||||
<Text color={t.color.muted} wrap="truncate">
|
||||
<Text color={t.color.accent}>{frame}</Text> {label}
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
|
||||
export function ArtLines({ lines }: { lines: [string, string][] }) {
|
||||
return (
|
||||
<>
|
||||
|
|
@ -64,7 +86,6 @@ export function SessionPanel({ info, sid, t }: SessionPanelProps) {
|
|||
}
|
||||
|
||||
const section = (title: string, data: Record<string, string[]>, max = 8, overflowLabel = 'more…') => {
|
||||
const skeletonRows = title === 'Tools' ? ['browser', 'terminal', 'file'] : ['apple', 'creative', 'software-development']
|
||||
const entries = Object.entries(data).sort()
|
||||
const shown = entries.slice(0, max)
|
||||
const overflow = entries.length - max
|
||||
|
|
@ -76,19 +97,16 @@ export function SessionPanel({ info, sid, t }: SessionPanelProps) {
|
|||
Available {title}
|
||||
</Text>
|
||||
|
||||
{skeleton
|
||||
? skeletonRows.map(k => (
|
||||
<Text dimColor key={k} wrap="truncate">
|
||||
<Text color={t.color.muted}>{k}: </Text>
|
||||
<Text color={t.color.text}>━━━━━━━━━━━━━━</Text>
|
||||
</Text>
|
||||
))
|
||||
: shown.map(([k, vs]) => (
|
||||
<Text key={k} wrap="truncate">
|
||||
<Text color={t.color.muted}>{strip(k)}: </Text>
|
||||
<Text color={t.color.text}>{truncLine(strip(k) + ': ', vs)}</Text>
|
||||
</Text>
|
||||
))}
|
||||
{skeleton ? (
|
||||
<InlineLoader label={title === 'Tools' ? 'discovering tools' : 'scanning skills'} t={t} />
|
||||
) : (
|
||||
shown.map(([k, vs]) => (
|
||||
<Text key={k} wrap="truncate">
|
||||
<Text color={t.color.muted}>{strip(k)}: </Text>
|
||||
<Text color={t.color.text}>{truncLine(strip(k) + ': ', vs)}</Text>
|
||||
</Text>
|
||||
))
|
||||
)}
|
||||
|
||||
{overflow > 0 && (
|
||||
<Text color={t.color.muted}>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue