Feat/chat UI improvements - #237
Closed
dave-apmic wants to merge 122 commits into
Closed
Conversation
The vt10x emulator advances the cursor one column per rune, but TUI agents lay out CJK and other double-width runes as two columns and reposition with absolute escapes, leaving spurious gaps mid-line. Inject a one-column padding rune after each wide rune printed in ground state and strip it from ReadScreen output. Co-Authored-By: Claude <noreply@anthropic.com>
TUI agents sometimes re-render already-finalized transcript lines (e.g. Codex redraws previous cells when starting a new turn), which pushes the screen diff boundary above the previous turn's output and leaks its tail into the head of the new agent message. Detect the line-level overlap between the previous turn's message tail and the new message head and remove it. Co-Authored-By: Claude <noreply@anthropic.com>
Tail the agent's session JSONL log (Claude Code, Codex) as a sidecar alongside the PTY. Agent-specific resolvers locate the session file from the agent PID; line parsers assemble rich structured messages (text, thinking, tool calls, tool results, model and usage data) and normalized session events. The watcher keeps no state: completed messages and raw lines are handed to callbacks for downstream storage and deduplication. Co-Authored-By: Claude <noreply@anthropic.com>
…SSE heartbeat - POST /message now queues user messages while the agent is busy (queued: true) instead of returning an error. A polling dispatch loop sends the queue head when the agent is stable, retries transient validation failures, and drops poison messages after 5 consecutive failures with an agent_error event. New GET/PUT/DELETE /queue endpoints manage pending messages in FIFO order. - Wire the JSONL watcher into the server: GET /rich-messages and the rich_message_update SSE event expose structured content blocks, model info, and token usage. The event emitter is the single store (upsert by message id and role, late-subscriber replay). - GET /session/export downloads all normalized session events for the current run with stable event ids. - /events sends a heartbeat every 15s so clients can detect connections that died without a FIN (e.g. after system sleep). Co-Authored-By: Claude <noreply@anthropic.com>
Add a header menu action that fetches /session/export and saves the normalized session events as a timestamped .jsonl file. Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
- Add --status-success, --status-warning, --status-error tokens (light+dark) - Add --code-block-bg/border/text tokens replacing hardcoded zinc-950 - Add --search-highlight/text tokens replacing hardcoded amber-300 - Add --hl-* syntax highlight tokens (8 colors, light+dark) - Add color-scheme: light/dark for native form controls - Remove 26 unused --sidebar-* and --chart-* token declarations Co-Authored-By: Claude <noreply@anthropic.com>
…kens Replace all instances of text-amber-600 dark:text-amber-400, text-emerald-600 dark:text-emerald-400, bg-zinc-950, and related color variants with the new semantic tokens (text-status-success, text-status-warning, bg-code-block-bg, etc.) across chat.tsx, explorer.tsx, and message-input.tsx. Also surfaces the attach-files button as a first-class Paperclip icon next to send (previously buried in overflow menu). Co-Authored-By: Claude <noreply@anthropic.com>
- Add Usage interface and usage/model/stop_reason fields to RichMessage - Add customTitle support via ?title= query parameter - Extract formatTokenCount, computeTokenTotals, getStatusMeta into lib/session-status.ts for reuse by header and embed status bar Co-Authored-By: Claude <noreply@anthropic.com>
…om title - Display session token count in header bar (compact pill) and session details dropdown (input/output breakdown) - Replace hardcoded amber/emerald with semantic status tokens - Add Keyboard shortcuts menu item opening shortcuts dialog - Show customTitle from ?title= query param instead of 'AgentAPI' Co-Authored-By: Claude <noreply@anthropic.com>
- Add CodeBlockCopy component with clipboard button on hover - Add rehype-highlight with selective language imports (~50KB) - Create custom hljs-theme.css using --hl-* design tokens - Fix dark mode leak: replace hardcoded zinc-950 with code-block tokens - Fix search highlight: replace hardcoded amber-300 with semantic tokens - Update test assertions for new token class names Co-Authored-By: Claude <noreply@anthropic.com>
Split 1755-line monolith into focused modules: lib/ format-time.ts — formatMessageTime, formatDateLabel, formatElapsedTime tool-format.ts — formatToolInput, getToolSummary content-fingerprint.ts — contentFingerprint (FNV-1a) scroll-anchors.ts — getPreviousUserMessageTop, getNextUserMessageTop task-timeline.ts — types (ToolCall, TaskSection, etc.) + 6 functions components/message-list/ tool-call.tsx — ToolCallCard, ToolCallGroup, ToolDetail (w/ duration) message-item.tsx — MessageItem, CopyButton, LoadingDots (w/ Tooltip) empty-state.tsx — EmptyState with AgentType displayName + 4 prompts task-group.tsx — TaskGroup with Markdown preview dialog message-list.tsx reduced from 1755 to 616 lines (↓65%). Also includes: - Tool call argument preview in collapsed summary - Tool call duration display (resultTimestamp) - User bubble visual weight rebalanced (bg-primary/10) - Agent output wrapped in subtle card container - Global render mode toggle moved to toolbar - Multi-day date separators between tasks - Visible tasks threshold lowered from 8 to 5 - Focus-visible rings on all hand-rolled buttons - Search highlight uses semantic tokens Co-Authored-By: Claude <noreply@anthropic.com>
- Create ui/tooltip.tsx with Radix @radix-ui/react-tooltip - Mount TooltipProvider (delayDuration=300) in root layout - Import hljs-theme.css in layout Co-Authored-By: Claude <noreply@anthropic.com>
Shows General, Message Input, and Terminal Mode shortcut sections. Triggered by pressing ? (when not in an input), or via the header session details dropdown menu. Co-Authored-By: Claude <noreply@anthropic.com>
Compact 32px bar showing status icon, agent display name (or custom title from ?title=), and session token count. Mounted above Chat in embed/page.tsx. Uses shared session-status.ts helpers. Co-Authored-By: Claude <noreply@anthropic.com>
Replace plain text 'Loading chat interface...' with an animated skeleton matching the real layout (header + content + composer). Co-Authored-By: Claude <noreply@anthropic.com>
Add ?title= URL parameter support for distinguishing multiple agent tabs. Affects browser tab title, header brand text, and embed status bar. Also adds rehype-highlight, highlight.js, lowlight, and @radix-ui/react-tooltip dependencies. Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
When JSONL rich activity provides real text+tool interleaving, tools are already at their correct positions in the conversation flow. Only apply groupConsecutiveTools() in the fallback PTY-only path where tools lack positional context. This fixes the '4 consecutive tool calls' container appearing at the top instead of tools showing inline where they were called. Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Claude stamps stop_reason on every JSONL line of a multi-block turn, so FlushCompleted (called every 200ms) can fire between blocks of the same message.id. This caused the pending entry to be deleted, and when the next block arrived, a new empty RichMessage was created — losing all previously accumulated content blocks (thinking, text). The frontend then saw only the last block (usually tool_use) per turn, with all text blocks missing. This prevented rich interleaving from working, causing tools to cluster into 'N consecutive tool calls' instead of appearing inline in the conversation flow. Fix: - Add a 'flushed' map to ClaudeParser that retains emitted messages - When a new block arrives for a flushed message.id, re-attach it to pending with its accumulated content intact - Clean up flushed entries when a genuinely new message.id appears - Make EventEmitter.EmitRichMessage keep the longer content slice when upserting, as a defensive guard against partial re-emissions Co-Authored-By: Claude <noreply@anthropic.com>
Claude Code v2.1.261+ uses session parking: the initial interactive process may park its work to a background process with a different PID and sessionId. The JSONL file is written under the background process's sessionId, not the initial one. The resolver now: 1. Tries the direct sessionId path first 2. If the file doesn't exist and parkedJobId is set, scans other session files to find the background process with matching jobId 3. Falls back to the original path if nothing matches This fixes rich messages (thinking, tool calls, usage) being empty when Claude Code uses session parking. Co-Authored-By: Claude <noreply@anthropic.com>
EmitRichMessage now unions content blocks by key instead of keeping whichever slice was longer. This prevents block loss when Claude streams partial deltas that contain only the newest block. The merged snapshot is always broadcast so clients never replace a complete message with a single-block delta. Co-Authored-By: Claude Code <noreply@anthropic.com>
The watcher now periodically re-resolves the session path and hot-swaps the tailed file when Claude Code parks a session to a new JSONL. The resolver priority is reordered so the ParkedJobID scan runs before the direct os.Stat check, preventing permanent selection of a stale file. Co-Authored-By: Claude Code <noreply@anthropic.com>
Add thinking block support to the task timeline: activity-groups and task-timeline now carry a 'thinking' variant, and task-group renders a collapsible ThinkingBlock with a Brain icon. Move the markdown/raw render toggle from the global toolbar to each individual message, giving users per-message control. The global state and localStorage persistence are removed in favor of local component state. Co-Authored-By: Claude Code <noreply@anthropic.com>
- README: add thinking block rendering, per-message markdown toggle, rich message merging fix, and session parking hot-swap to the Changes in this fork section - CHANGELOG: add v0.13.0 section with features and fixes Co-Authored-By: Claude Code <noreply@anthropic.com>
Detect when the agent's output contains an interactive TUI prompt (plan approval, trust confirmation, etc.) that cannot be operated from the chat Text mode. Show a warning banner instructing the user to switch to Terminal mode to respond. Detects patterns like 'Would you like to proceed?' + '❯' for plan approval, and 'Enter to confirm' for trust/other confirmations. Co-Authored-By: Claude <noreply@anthropic.com>
Replace hardcoded pattern matching with broader detection of TUI interactive elements: selection menus (❯ + numbered options), confirmation prompts (Enter to confirm / Esc to cancel), permission prompts (Allow/Deny), auth flows (/login, authorize), and question prompts (AskUserQuestion). Shows a single unified banner: 'This prompt requires terminal input. Switch to the Terminal tab below to respond.' Co-Authored-By: Claude <noreply@anthropic.com>
… Codex Rewrite terminalActionNeeded to cover all known TUI interactive prompts from both Claude Code and Codex: Claude Code: - Selection menus (❯ cursor + numbered options) - Permission/tool approval (Allow/Deny, Do you want to proceed) - Plan mode approval (Would you like to proceed, Ready to code) - Auth flows (authorize/authentication/sign in, /login, /mcp) - MCP server trust and elicitation prompts - Press Enter to continue/connect/install/retry/open Codex: - Selection menus (> cursor + numbered options + Press Enter) - Edit/run approval (wants to edit, wants to run) - Network access approval - ChatGPT device-code sign-in Removed previous false-positive-prone patterns (bare 'Login', 'once', question-mark heuristic) in favor of more targeted checks. Co-Authored-By: Claude <noreply@anthropic.com>
When the agent shows a selection prompt (plan approval, AskUserQuestion, permission dialogs, etc.), the chat UI now: 1. Detects numbered options from the terminal output 2. Renders clickable buttons for each option in the warning banner 3. Clicking a button sends the option number + Enter as raw keystrokes This lets users respond to interactive prompts directly from the chat UI without switching to Terminal mode. The 'Switch to Terminal tab' fallback message still shows for prompts without numbered options. Plumbing: onSendRaw callback threaded from Chat → MessageList → TaskGroup → MessageItem. Co-Authored-By: Claude <noreply@anthropic.com>
Parse AskUserQuestion and ExitPlanMode tool_use inputs from rich messages and render their options as interactive buttons directly in the ToolCallCard. Users can now: - See the question text and all options clearly formatted - Click an option button to send the selection as raw keystrokes - See option descriptions below the buttons AskUserQuestion cards auto-open when pending (waiting for input). onSendRaw callback threaded through ToolCallGroup → ToolCallCard. Co-Authored-By: Claude <noreply@anthropic.com>
Expand selectableOptions parsing to cover all prompt variants: 1. Numbered options (❯ 1. Label) — sends digit + Enter 2. Non-numbered cursor options (❯ Yes, I trust) — sends Enter for current selection, arrow-down + Enter for siblings 3. Enter-only confirmations (Press Enter to continue) — sends Enter Buttons no longer show a number badge for non-numbered options. Covers: trust dialogs, bypass permissions, ExitPlanMode approval, permission prompts (Allow/Deny), and Enter-gated flows. Co-Authored-By: Claude <noreply@anthropic.com>
The rich interleaving path (hasRichText && hasRichTools) was discarding task.responses entirely, showing only rich text blocks. This lost PTY-specific content like progress indicators, tool execution summaries, and intermediate status lines. Now PTY responses are included first, followed by rich activity for structured text/tool interleaving. Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.