Agent chat: terminal theme tokens + in-conversation search#6006
Agent chat: terminal theme tokens + in-conversation search#6006lawrencecchen wants to merge 6 commits into
Conversation
…heme seam Reuses AgentSessionWebTheme.resolve + applyAgentTheme end to end: the panel appearance resolves to the shared token set, rides the chat.init reply for the initial paint, and pushes via cmuxAgentChatBridge.applyTheme on change. styles.css gains scheme-aware token defaults so dev/mock mode keeps the system fallback.
… mode) Pure search module (bounded matcher + highlighter, reducer-shaped UI state) + a search bar wired into the surface: Cmd+F or the header button opens it, Enter/Shift+Enter step with wraparound, Escape closes and clears, a toggle filters the timeline to matching items only (no turn separators in the filtered view). Matching rows get an accent ring, the current match an outline and scrollIntoView, plain-text user bubbles get inline marks (markdown bodies keep row-level highlight only). An active search pauses auto-follow; clearing restores it. All scanning is bounded so multi-MB tool outputs cannot freeze rendering. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryAdds terminal-derived theme tokens and Cmd/Ctrl+F in-conversation search to the Agent Chat webview. The theme seam reuses
Confidence Score: 5/5Safe to merge. The search and theme paths are well-isolated, bounded against large inputs, and covered by 88 tests; no correctness issues were found in the Swift or TypeScript changes. Both features make additive, well-scoped changes. The search reducer is pure and thoroughly tested; the WeakMap cache correctly handles streaming item replacement; theme validation rejects partial payloads rather than half-applying them. Swift changes mirror the established agent-session coordinator pattern with proper idempotency guards. No correctness or data-integrity issues were found. No files require special attention. The one style note is in Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[macOS: AgentChatPanelView] -->|AgentSessionWebTheme.resolve| B[AgentChatWebViewRepresentable]
B -->|apply theme on make/update| C[AgentChatWebViewController]
C -->|evaluateJavaScript applyTheme JSON| D[WKWebView Page]
C -->|chat.init reply with theme dict| D
D -->|applyAgentChatTheme validates + applies| E[CSS --agent-* custom props]
F[AgentChatApp] -->|useReducer reduceSearchUI| G[SearchUIState]
G -->|open/query/cursor/filterMode| H[SearchBar UI]
F -->|computeMatches + WeakMap cache| I[matchedIndexes array]
I -->|matchedIndexSet + filterMode| J[Timeline rows]
J -->|data-search-match/current attrs| K[CSS match ring / outline]
J -->|highlightSegments bounded| L[mark elements in UserMessageRow]
H -->|stepSearch + scrollIntoView| J
Reviews (5): Last reviewed commit: "Search: match argv commands, edit pairs,..." | Re-trigger Greptile |
| const openSearch = () => dispatchSearch({ type: "open" }); | ||
| useSearchHotkey(openSearch); |
There was a problem hiding this comment.
openSearch is recreated as a new arrow function on every render, so useSearchHotkey's [onOpen] dependency changes every render and the effect runs cleanup+setup on every AgentChatApp render. This directly contradicts the hook's documented invariant ("attaches exactly one keydown listener for the component's lifetime"). During streaming (frequent renders) the listener is re-attached on every incoming event. Wrapping in useCallback with an empty dependency array is safe because dispatchSearch from useReducer is guaranteed stable.
| const openSearch = () => dispatchSearch({ type: "open" }); | |
| useSearchHotkey(openSearch); | |
| const openSearch = useCallback(() => dispatchSearch({ type: "open" }), []); | |
| useSearchHotkey(openSearch); |
Two autoreview findings. computeMatches rescanned every item (up to 100k chars per field) on every render while streaming; items are immutable snapshots and updates replace only the changed object, so a WeakMap identity cache makes the scan incremental (cache-hit for unchanged items, one rescan per changed item or query change; test seam proves the scan counts). The matcher also missed input strings the tool rows visibly render (file paths, web queries/URLs, apply_patch text); those keys are now indexed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The inline callback ref was recreated per render, so every search-bar update (next/prev, filter toggle, typing) refocused the field, stealing focus from the controls and risking IME composition issues. The ref is now a stable module-level function guarded by a WeakSet, focusing each re-keyed input node exactly once. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…puts Codex shell commands arrive as argv arrays, and file-change rows render old_string/new_string, MultiEdit edits entries, and Write content; the matcher skipped all of those, so visible diff/command text returned no match and filter mode hid the row. Bounded one-pass descent (depth 3, 64 array entries per field) covers the rendered shapes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 41a140c. Configure here.
| "content", | ||
| "new_source", | ||
| "edits", | ||
| ] as const; |
There was a problem hiding this comment.
Search skips visible input keys
Medium Severity
In-conversation search walks tool input objects via SEARCHED_INPUT_KEYS, but that list omits field names the rich tool rows already use for display— notably cmd (Codex exec_command arguments in fixtures), q (web search), and script (command execution). Text visible in a row can fail to match even though the query appears in the UI.
Reviewed by Cursor Bugbot for commit 41a140c. Configure here.


Stacked on #5967 (rich tool rendering), in the #5736 lane.
Terminal theme tokens. Reuses the agent-session theme seam end to end (AgentSessionWebTheme.resolve + applyAgentTheme): the panel appearance resolves to the shared token set, rides the chat.init reply for the first paint, and pushes via cmuxAgentChatBridge.applyTheme on appearance change. styles.css gains scheme-aware token defaults so dev/mock mode keeps the system fallback. User bubbles, tool rows, diff colors, and banners all read the variables.
In-conversation search. Cmd+F (page-level, not a global cmux shortcut) or a header button opens a search bar: case-insensitive substring match over message text, tool titles/names, output, and command input; match counter; Enter/Shift+Enter step with wraparound and scroll the current match into view; Escape closes and clears; a toggle filters the timeline to matches only. Matching rows get an accent ring (current match an outline); plain-text user bubbles get inline marks, markdown bodies keep row-level highlight only (no marks inside sanitized HTML). An active search pauses auto-follow; clearing restores it. All matching/highlighting is bounded (100k chars scanned per field, 20k chars / 100 marks decorated per text) so multi-MB tool outputs cannot freeze the render path. No raw useEffect: the one document-level Cmd+F listener lives in a narrow-contract hook, focus is a re-keyed callback ref, everything else is derived render state.
Worker note: the theme commit and the bounded search module were built by one worker session; the UI wiring, styles, and tests were completed by the manager after that session died on a rate limit.
Verification: bun test 88 pass (search reducer/matcher/highlight bounds suite added), typecheck, lint:ci, verify:tanstack-router, build-webviews-app.sh assets rebuilt and committed.
🤖 Generated with Claude Code
Need help on this PR? Tag
/codesmithwith what you need. Autofix is disabled.Note
Low Risk
UI-only webview bundle changes (theming + client-side search) with bounded scanning and no auth or data-path changes; main risk is render/scroll edge cases on very large transcripts.
Overview
Adds terminal-derived theme tokens and in-conversation search to the Agent Chat webview (
agentChatSurface.mjsrebuild).Theme:
chat.initnow applies a validated theme on first paint (st(r.theme)); CSS defines--agent-*defaults withprefers-color-schemeanddata-themeoverrides so bubbles, diffs, and banners stay consistent before/after native theme pushes viacmuxAgentChatBridge.applyTheme.Search: Cmd/Ctrl+F or a header button opens a search bar with match counter, prev/next (Enter/Shift+Enter), optional “matches only” filter, row highlights and inline marks on plain user text, and scroll-into-view for the current match. Matching scans message/tool fields with caps (100k per field, bounded highlight segments) and a WeakMap cache per item/query; an active query pauses auto-follow to latest. Search input focuses once per open via re-keyed input + WeakSet; IME composition is ignored for Enter/Escape.
Reviewed by Cursor Bugbot for commit 41a140c. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by cubic
Adds terminal-derived theme tokens to Agent Chat and in-conversation search (Cmd/Ctrl+F) with next/prev, match counter, and optional filtering. First paint is themed via
chat.init; search stays fast on large, streaming outputs with an incremental cache and bounded scanning.New Features
chat.initand update viacmuxAgentChatBridge.applyTheme; scheme-aware CSS fallbacks cover dev/mock; webview background is transparent; rows, diffs, banners, and bubbles read--agent-*vars.old_string/new_string), file contents, andeditsentries (bounded one-pass descent, depth 3, 64 array entries). Counter and Enter/Shift+Enter with wrap + scroll-into-view; Escape closes; toggle to show matches only (filtered view omits turn separators); row-level highlight with current-match outline, inline marks for plain user text; active search pauses auto-follow. Incremental identity cache rescans only changed items; bounds: 100k chars per field scan, 20k chars / 100 marks per text. Tests cover the reducer, matcher/highlighter (incl. argv/edit/content coverage), and theme parsing; webview assets rebuilt.Bug Fixes
Written for commit 41a140c. Summary will update on new commits.