Skip to content

UI pass: terminal color/clear, responsive headers, flat-document chat#263

Open
ANonABento wants to merge 13 commits into
mainfrom
ui-terminal-color-and-responsive-headers
Open

UI pass: terminal color/clear, responsive headers, flat-document chat#263
ANonABento wants to merge 13 commits into
mainfrom
ui-terminal-color-and-responsive-headers

Conversation

@ANonABento

Copy link
Copy Markdown
Owner

What

Three things from the UI pass:

1. Terminal color + clear (fix(terminal))

The embedded tmux terminal had no color and clear/TUI redraws left artifacts. Root cause: the tmux attach client (whose output feeds xterm.js) was spawned with no TERM — on a GUI launch (TERM unset) tmux downgrades to a colorless dumb terminal and drops clear / alternate-screen sequences. Fixes in tmux_transport.rs:

  • attach_in_pty: TERM=xterm-256color + COLORTERM=truecolor + -2
  • ensure_tmux_server: explicit default-terminal tmux-256color + truecolor terminal-overrides
  • create_session: COLORTERM in the session env

Takes effect on every attach, so existing sessions get color on reopen too.

2. Two-stage responsive headers (feat(panels))

Header controls overlapped at narrow panel widths. New useElementWidth + PanelDensityContext drive three tiers:

  • regular (≥470px): tab labels + labelled runtime/Hold
  • compact (360–470px): tabs → icons, runtime/Hold → icon buttons
  • mini (<360px): tabs → icons; runtime + Hold + Kill fold into a generalized PanelOverflowMenu

PanelTabs gains a responsive prop; RuntimeModeToggle gains button|compact|menu variants; HoldButton extracted. Chef header gets the lighter pass (responsive tabs + Cmd+J hint hidden when tight).

3. Docs refresh (docs)

  • ROADMAP.md — authoritative current state ranked for daily-driving
  • specs/CHECKLIST_AS_ROADMAP.md — planned checklist /api + MCP tools
  • CLAUDE.md known-gaps refreshed; STATUS.md MCP count 19 → 25

Verification

  • tsc --noEmit, eslint, panel-tabs unit test, cargo check --workspace all green (precommit hooks enforce check + lint).
  • Built the release bundle and relaunched; terminal color confirmed via the attach path.

Not included

  • Chat rendering consolidation (agent transcript + chef bubbles → flat document) — next up.
  • Perf pass (list virtualization, chunk splitting).

- ROADMAP.md: authoritative current state, ranked for daily-driving (supersedes
  the stale STATUS tiers + session-scoped REMAINING_WORK index)
- specs/CHECKLIST_AS_ROADMAP.md: planned checklist /api routes + MCP tools so the
  per-workspace checklist can serve as an agent-maintainable roadmap
- CLAUDE.md: drop already-fixed MCP 'known gaps' (source attribution + recursion
  guard shipped; mark_complete/deps route through /api), list the real remaining
  gaps (no checklist tools; UI-only ops without routes)
- STATUS.md: MCP tool count 19 -> 25
The tmux attach client (whose output feeds xterm.js) was spawned with no TERM,
so on a GUI launch (TERM unset) tmux downgraded to a colorless dumb terminal and
dropped clear / alternate-screen sequences. Set TERM=xterm-256color +
COLORTERM=truecolor + -2 on the attach client, default-terminal=tmux-256color +
truecolor terminal-overrides on the server, and COLORTERM in the session env.
Takes effect on every attach, so existing sessions get color on reopen too.
Headers overlapped at narrow panel widths. New useElementWidth + PanelDensityContext
drive three tiers (regular >=470 / compact / mini <360): tabs go label->icon
(PanelTabs gains 'responsive'); agent right-side controls fold regular(labelled)
-> compact(icons) -> mini(runtime+hold into a generalized PanelOverflowMenu).
RuntimeModeToggle gains button|compact|menu variants; HoldButton extracted. Chef
header gets the lighter pass (responsive tabs + Cmd+J hint hidden when tight).
…erer

Chef chat used bubbles; the agent transcript used a flat left-border + role-label
style. Unify on the flat style (no bubbles) and share the renderer:
- New chat-flat.tsx: ChatEntry (left accent border + role label + markdown body),
  ChatDivider (system notices), and ChatMarkdown — one GFM markdown renderer for
  every chat surface.
- chat-history.tsx (chef) rewritten off bubbles to flat ChatEntry rows; user='you',
  assistant='chef', streaming/thinking/tools/queued preserved.
- agent-transcript.tsx drops its local MarkdownBlock and uses the shared ChatMarkdown
  (now GFM-enabled).
- useElementWidth guards a missing ResizeObserver (old webview / jsdom); test setup
  mocks ResizeObserver (configurable so terminal-view can re-stub).
@ANonABento ANonABento changed the title Terminal color/clear fix + two-stage responsive panel headers UI pass: terminal color/clear, responsive headers, flat-document chat Jun 13, 2026
@ANonABento

Copy link
Copy Markdown
Owner Author

Added a third piece: flat-document chat consolidation (feat(chat)).

Chef chat used bubbles; the agent transcript used a flat left-border + role-label style. Unified both on the flat style (no bubbles) and shared the renderer:

  • New chat-flat.tsx: ChatEntry (left accent border + role label + markdown body), ChatDivider, and ChatMarkdown — one GFM markdown renderer for every chat surface.
  • chat-history.tsx (chef) rewritten off bubbles to flat entries (user='you', assistant='chef'); streaming/thinking/tools/queued preserved.
  • agent-transcript.tsx drops its local markdown renderer for the shared ChatMarkdown (now GFM-enabled).

All 419 frontend tests pass (added a ResizeObserver mock + guard for jsdom/old webviews).

Still pending from the UI pass: agent-transcript visual polish (fully sharing ChatEntry) and the perf pass (list virtualization, chunk splitting).

Replace the transcript's local EntryShell/EntryMeta/MetaEntry with the shared
ChatEntry/ChatDivider (added a 'warning' tone for cancelled runs) so the agent
and chef chat are now literally the same components. Readability: drop the
blanket font-mono from the transcript container so agent prose reads like the
chef chat; keep monospace only on the raw tmux-tail AgentOutputBlock where
terminal alignment matters.
…filer

- vite manualChunks: split react / xterm / markdown / motion / dnd out of the
  single app chunk. Main chunk 1532 kB -> 652 kB (gzip 429 -> 172). shiki left
  alone (stays a lazy per-language async chunk).
- chat-history: memoize MessageEntry so a streaming response no longer re-renders
  the settled history on every token.
- react-scan as an opt-in dev profiler ('npm run profile' / VITE_REACT_SCAN=1);
  dev-only, dead-code-eliminated from production (verified absent from prod bundle).
- eslint: allowDefaultProject for *.config.ts so vite.config.ts lints under the
  project service.
@ANonABento

Copy link
Copy Markdown
Owner Author

Added the perf pass (perf: commit f7e445a) — completes the UI pass:

  • Bundle split via vite manualChunks: react / xterm / markdown / motion / dnd pulled out of the single app chunk. Main chunk 1532 kB → 652 kB (gzip 429 → 172). shiki stays a lazy per-language async chunk.
  • Memoized chef chat history so a streaming response no longer re-renders settled messages on every token.
  • react-scan opt-in dev profiler (npm run profile or VITE_REACT_SCAN=1) — dev-only, dead-code-eliminated from production (verified absent from the prod bundle).
  • eslint allowDefaultProject so vite.config.ts lints under the project service.

419 frontend tests green, tsc + lint clean.

Noted follow-ups (not in this PR): further code-splitting via React.lazy on heavy panels; the agent transcript rebuilds all items from events per streaming token (incremental build would help but is intricate); true list virtualization deferred (variable-height + collapsibles + auto-scroll make it high-risk).

The regular->compact breakpoint (470px) was below the labelled row's natural
width (~640px for the agent header), so between ~470-640 the row stayed 'regular'
and flexbox truncated the tabs / clipped the right-side controls before snapping
to icons. Make panelDensity thresholds per-header (agent 640, chef 480) so icons
kick in before labels overflow. Also lock the agent controls row to a fixed h-10
so the header never changes vertical size between label and icon states.
…nner

Chef/managed pipe turns passed the system prompt + the full message (which for
chef includes the whole prior conversation when there's no resume id) on argv.
Once a conversation/board grew past Linux's 128 KiB per-arg limit, exec() failed
with 'Argument list too long' (E2BIG). Claude now receives the message on stdin
(claude -p reads stdin), kept off argv: new SpawnConfig.stdin_data, written by the
pipe transport on a task to avoid pipe-buffer deadlock; ClaudeCliAdapter gains
managed_turn_args_stdin. Codex unchanged (keeps message on argv). Regression test
builds a 512 KiB prompt and asserts it rides stdin with every argv < 128 KiB.

Also collapse the orchestrator's two error UIs into one: drop the retry/dismiss
FailedMessageBanner; the single ErrorBanner now surfaces stream errors AND
send/spawn failures, and its dismiss clears every error source.
…ndicator

- Queued orchestrator turns were added to BOTH messages (optimistic) and the
  queue, so they rendered twice (a normal 'you' message + a QueuedEntry). Only
  queue them now; the optimistic message is added on immediate send / agent
  delivery / drain.
- Remove the in-bubble Cancel from the streaming entry — the composer's Stop is
  the single cancel control.
- QueuedEntry restyled into a distinct muted dashed wrap with a 'Queued' pill so
  pending turns read as waiting, not delivered.
The chef embedded the full board snapshot in BOTH --system-prompt and (prepended
to) every message. Keep the system-prompt copy (rebuilt fresh each turn) and only
prepend the board to the message on --resume turns, where Claude reuses the
original session's stale system prompt. Fresh turns now send the board once,
roughly halving the prompt and easing the argv/E2BIG pressure. Real fix (drop the
embedded board for an on-demand get_board tool) spec'd in
specs/CHEF_TOOL_BASED_CONTEXT.md.
…HEF_TOOLS)

Opt-in: when KAITENCODE_CHEF_TOOLS=1 and the kaitencode-mcp binary is locatable,
the CLI chef reads the board via the get_board MCP tool instead of embedding the
whole board in the prompt. The system prompt drops the task snapshot (keeps
columns + workspace id + the write action-protocol); claude is spawned with
--strict-mcp-config --mcp-config <isolated json> --allowedTools <read tools>; the
message no longer carries the board. Falls back to the embedded board when off or
the binary isn't found. Also fixes the real (non-dead) board double-send in
stream_cli.rs (the earlier dedup was on the unused chef.send_message_with_context).

SessionConfig gains mcp_config_path + allowed_tools (Default-derived); session
arg builder appends the MCP flags for claude. Verified end-to-end: chef calls
get_board with the workspace id and answers from the live board.
Spec + follow-ups (bundle the mcp binary) in specs/CHEF_TOOL_BASED_CONTEXT.md.
Bounded in-memory recorder (chat/debug.rs, last 20 pipe-transport turns) capturing
the exact outgoing invocation — command, args (incl. --system-prompt + MCP flags),
and the stdin prompt — plus the raw stream-json received. Exposed via
get_chat_debug/clear_chat_debug. New Settings → Debug tab renders each turn with
collapsible sent/received sections: parses tool_use/tool_result/text/result out of
the stream-json (with a raw toggle), so you can verify the prompt and tool-calling
behaviour at a glance. Covers chef + managed agents.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant