apps/web: add in-thread find/search for chat conversations#1501
apps/web: add in-thread find/search for chat conversations#1501leonardoxr wants to merge 11 commits intopingdotgg:mainfrom
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL 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 |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 72d67ca4fb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7828e85e7f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2fd077b092
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 340789c4b9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 776dfcfe8d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 50affe963c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
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.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a652fd5ee6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
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.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f9bfd72303
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
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.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cd11e1f59d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const HTML_ENTITY_BY_NAME = { | ||
| amp: "&", | ||
| apos: "'", | ||
| gt: ">", | ||
| lt: "<", | ||
| nbsp: " ", | ||
| quot: '"', | ||
| } as const; |
There was a problem hiding this comment.
🟢 Low lib/markdownPlainText.ts:18
HTML_ENTITY_BY_NAME.nbsp is mapped to a regular ASCII space (" ", U+0020) instead of the actual non-breaking space character (\u00A0). This causes entities in markdown to be decoded to regular spaces, losing their intended non-breaking semantics.
| const HTML_ENTITY_BY_NAME = { | |
| amp: "&", | |
| apos: "'", | |
| gt: ">", | |
| lt: "<", | |
| nbsp: " ", | |
| quot: '"', | |
| } as const; | |
| const HTML_ENTITY_BY_NAME = { | |
| amp: "&", | |
| apos: "'", | |
| gt: ">", | |
| lt: "<", | |
| nbsp: "\u00A0", | |
| quot: '"', | |
| } as const; |
🤖 Copy this AI Prompt to have your agent fix this:
In file apps/web/src/lib/markdownPlainText.ts around lines 18-25:
`HTML_ENTITY_BY_NAME.nbsp` is mapped to a regular ASCII space (`" "`, U+0020) instead of the actual non-breaking space character (`\u00A0`). This causes ` ` entities in markdown to be decoded to regular spaces, losing their intended non-breaking semantics.
Evidence trail:
apps/web/src/lib/markdownPlainText.ts lines 18-24 at REVIEWED_COMMIT showing `HTML_ENTITY_BY_NAME` object with `nbsp: " "` (regular space U+0020, not `\u00A0`).
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.
| visibleThreadSearchResults.length > 0 | ||
| ? new Set(visibleThreadSearchResults.map((result) => result.rowId)) | ||
| : EMPTY_MATCHED_THREAD_SEARCH_ROW_IDS, | ||
| [visibleThreadSearchResults], |
There was a problem hiding this comment.
Redundant memo duplicates already-empty search results
Low Severity
visibleThreadSearchResults is always identical to threadSearchResults and adds an unnecessary memoization layer. When threadSearchOpen is false, threadSearchLookupState already receives "" as the query (line 1077), causing threadSearchResults to be empty. The visibleThreadSearchResults memo then redundantly gates on threadSearchOpen again, but the value is already empty. Both downstream consumers (matchedThreadSearchRowIds, activeThreadSearchRowId) could use threadSearchResults directly.
Additional Locations (1)
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e8e4642e0b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const codePoint = Number.parseInt(normalizedValue.slice(2), 16); | ||
| return Number.isFinite(codePoint) ? String.fromCodePoint(codePoint) : entity; |
There was a problem hiding this comment.
Guard numeric entity decoding against invalid code points
Calling String.fromCodePoint on any parsed numeric entity without range checks can throw RangeError for values above 0x10FFFF (for example � or �). markdownToPlainText is executed while building the thread-search index, so opening search on a thread containing one of these entities can crash rendering instead of just skipping that entity. Clamp/validate code points before decoding (and fall back to the original entity when invalid).
Useful? React with 👍 / 👎.
| const HTML_ENTITY_BY_NAME = { | ||
| amp: "&", | ||
| apos: "'", | ||
| gt: ">", |
There was a problem hiding this comment.
Decode all named HTML entities for markdown indexing
The entity decoder only recognizes a tiny hardcoded subset of named entities, so rendered text and indexed text diverge for common entities like ©, –, or é. In those cases users can see the decoded character in assistant markdown but find-in-thread will miss it because the index still stores the raw entity token. Use a complete named-entity decoder (or parser-backed decode) to keep search aligned with rendered output.
Useful? React with 👍 / 👎.


Summary
Adds a Codex-style in-thread search flow to the chat view in
apps/web.Cmd+F/Ctrl+FEnter,Shift+Enter, and the next/previous buttonsWhy
Closes #1486.
The issue asks for a
Cmd+F/Ctrl+Fstyle find-in-thread flow because it is hard to locate earlier content in long conversations by scrolling, especially during active generation.Scope
This PR is intentionally limited to the web thread-search experience.
It does not include unrelated server/runtime compatibility changes or local dev seeding helpers.
Before / After
Before:
After:
Validation
bun fmtbun lintbun typecheckbun run test src/components/chat/threadSearch.test.tsbun run test src/components/ChatMarkdown.test.tsx src/components/chat/MessagesTimeline.test.tsx src/components/chat/threadSearchHighlight.test.tsx src/components/chat/ProposedPlanCard.test.tsxbun run test:browser src/components/ChatView.threadSearch.browser.tsxNotes
This is still a fairly broad UI change for a single feature, but all included edits are directly tied to making thread search usable across the actual thread surfaces that can render searchable content.
Note
Medium Risk
Adds new keyboard shortcut handling, virtualized scrolling behavior, and cross-surface text highlighting in the core chat view; regressions could affect focus management, timeline rendering, or performance on large threads.
Overview
Adds an in-thread “find” experience to
ChatView(Cmd/Ctrl+F) with an overlaidThreadSearchBar, match counting, Enter/Shift+Enter navigation, and focus restore on close.Introduces a reusable search index (
threadSearch.ts) built from visible timeline content (assistant markdown/plain text, user message + terminal-chip labels, attachment names, work log, and proposed plans) with incremental narrowing as the query extends.Updates
MessagesTimelineand related components (ChatMarkdown,ProposedPlanCard,TerminalContextInlineChip) to accept search state, render inline<mark>highlights (including via a rehype plugin for markdown), expand collapsed/overflow content while searching, and auto-scroll active matches into view even when rows are virtualized.Refactors timeline row building into
buildTimelineRowsand addsmarkdownToPlainTextto index rendered markdown text instead of raw markdown syntax; includes unit and browser tests covering shortcut behavior, navigation, virtualization, and highlighting across surfaces.Written by Cursor Bugbot for commit e8e4642. This will update automatically on new commits. Configure here.
Note
Add in-thread find/search for chat conversations
ThreadSearchBarcomponent that opens with Cmd/Ctrl+F, displays a result counter, and supports keyboard navigation (Enter/Shift+Enter/Escape) with focus restoration on close.threadSearch.ts) from precomputedTimelineRowitems, supporting case-insensitive matching across user messages, assistant markdown, work entries, and proposed plans.buildTimelineRowsinMessagesTimeline.logic.tsto replace in-component row derivation;ChatViewandMessagesTimelinenow receive precomputed rows and search state as props.createThreadSearchHighlightRehypePluginandrenderHighlightedTextto inject<mark>elements into markdown, inline chips, work entry headings/previews, and code blocks; code blocks with matches bypass async syntax highlighting.markdownToPlainTextinmarkdownPlainText.tsfor indexing rendered markdown content as plain text.MessagesTimelineAPI is breaking — callers must now passrows(viabuildTimelineRows) instead of individualtimelineEntries,isWorking, andcompletionDividerBeforeEntryIdprops.Macroscope summarized e8e4642.