Skip to content

apps/web: add in-thread find/search for chat conversations#1501

Open
leonardoxr wants to merge 11 commits intopingdotgg:mainfrom
leonardoxr:feat/thread-search-1486
Open

apps/web: add in-thread find/search for chat conversations#1501
leonardoxr wants to merge 11 commits intopingdotgg:mainfrom
leonardoxr:feat/thread-search-1486

Conversation

@leonardoxr
Copy link
Copy Markdown

@leonardoxr leonardoxr commented Mar 28, 2026

Summary

Adds a Codex-style in-thread search flow to the chat view in apps/web.

  • opens with Cmd+F / Ctrl+F
  • searches the current thread across user messages, assistant messages, plans, and work log rows
  • cycles matches with Enter, Shift+Enter, and the next/previous buttons
  • scrolls virtualized matches into view
  • highlights the matched text inline instead of applying a whole-row emphasis treatment
  • clears all highlighting when the search UI is closed
  • keeps the search bar overlaid so opening it does not shift the thread layout
  • narrows search incrementally as the query gets longer to reduce repeated scan work on large threads

Why

Closes #1486.

The issue asks for a Cmd+F / Ctrl+F style 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:

Before thread search

After:

After thread search

Validation

  • bun fmt
  • bun lint
  • bun typecheck
  • bun run test src/components/chat/threadSearch.test.ts
  • bun run test src/components/ChatMarkdown.test.tsx src/components/chat/MessagesTimeline.test.tsx src/components/chat/threadSearchHighlight.test.tsx src/components/chat/ProposedPlanCard.test.tsx
  • bun run test:browser src/components/ChatView.threadSearch.browser.tsx

Notes

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 overlaid ThreadSearchBar, 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 MessagesTimeline and 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 buildTimelineRows and adds markdownToPlainText to 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

  • Adds a ThreadSearchBar component that opens with Cmd/Ctrl+F, displays a result counter, and supports keyboard navigation (Enter/Shift+Enter/Escape) with focus restoration on close.
  • Builds a search index (threadSearch.ts) from precomputed TimelineRow items, supporting case-insensitive matching across user messages, assistant markdown, work entries, and proposed plans.
  • Introduces buildTimelineRows in MessagesTimeline.logic.ts to replace in-component row derivation; ChatView and MessagesTimeline now receive precomputed rows and search state as props.
  • Adds createThreadSearchHighlightRehypePlugin and renderHighlightedText to inject <mark> elements into markdown, inline chips, work entry headings/previews, and code blocks; code blocks with matches bypass async syntax highlighting.
  • Auto-expands work-log groups and proposed plan cards during an active search, hiding expand/collapse controls, and scrolls the active matched row into view with an animation-frame retry loop.
  • Adds markdownToPlainText in markdownPlainText.ts for indexing rendered markdown content as plain text.
  • Risk: MessagesTimeline API is breaking — callers must now pass rows (via buildTimelineRows) instead of individual timelineEntries, isWorking, and completionDividerBeforeEntryId props.

Macroscope summarized e8e4642.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 28, 2026

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 0f142552-e236-41d8-92e0-315d85eb394a

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions bot added size:XXL 1,000+ changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list. labels Mar 28, 2026
@leonardoxr leonardoxr marked this pull request as ready for review March 28, 2026 21:26
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment on lines +18 to +25
const HTML_ENTITY_BY_NAME = {
amp: "&",
apos: "'",
gt: ">",
lt: "<",
nbsp: " ",
quot: '"',
} as const;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 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.

Suggested change
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`).

Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

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],
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
Fix in Cursor Fix in Web

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment on lines +31 to +32
const codePoint = Number.parseInt(normalizedValue.slice(2), 16);
return Number.isFinite(codePoint) ? String.fromCodePoint(codePoint) : entity;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 &#99999999; or &#x110000;). 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 👍 / 👎.

Comment on lines +18 to +21
const HTML_ENTITY_BY_NAME = {
amp: "&",
apos: "'",
gt: ">",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 &copy;, &ndash;, or &eacute;. 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 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL 1,000+ changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Add search within the thread/chat.

2 participants