perf(tui): paint banner on first frame, don't wait on session.create

Previously `historyItems` was seeded empty and the intro (with Banner +
SessionPanel) was only pushed after Python's `session.create` returned —
~1.8s of agent + tools + MCP init with nothing on screen. Base CLI feels
instant because it prints the banner as its first action.

Seed `historyItems` with an info-less intro on mount. `appLayout` now
renders the Banner unconditionally for `kind === 'intro'` and gates only
the SessionPanel on `info` being present. Gateway.ready swaps the skin
(~200ms) and session.info fills in the panel when the agent is ready.

Net: first usable frame drops from ~2s to ~300ms (node + module graph +
React mount). No behavior change — intro message is replaced in place
by `introMsg(info)` when `newSession()` / `resumeById()` resolve.
This commit is contained in:
Brooklyn Nicholson 2026-04-16 14:58:12 -05:00
parent cb2a737bc8
commit c6ed61430a
2 changed files with 6 additions and 3 deletions

View file

@ -93,7 +93,10 @@ export function useMainApp(gw: GatewayClient) {
}
}, [stdout])
const [historyItems, setHistoryItems] = useState<Msg[]>([])
// Seed with an info-less intro so the banner paints on the first frame,
// before gateway.ready / session.create resolve. Replaced by
// `introMsg(info)` as soon as session.info arrives.
const [historyItems, setHistoryItems] = useState<Msg[]>(() => [{ kind: 'intro', role: 'system', text: '' }])
const [lastUserMsg, setLastUserMsg] = useState('')
const [stickyPrompt, setStickyPrompt] = useState('')
const [catalog, setCatalog] = useState<null | SlashCatalog>(null)