Skip to content

feat: Add transcript text search to AI chat - #628

Merged
bdjulbic merged 24 commits into
mainfrom
codex/add-text-search-to-ai
May 19, 2026
Merged

bdjulbic merged 24 commits into
mainfrom
codex/add-text-search-to-ai

Conversation

@ilyabo

@ilyabo ilyabo commented May 15, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • Adds a chat-local text search surface as Chat.Search in @sqlrooms/ai-core
  • Searches the current session transcript for prompts, assistant text, reasoning, and lightweight tool labels
  • Highlights matches in rendered chat content and supports next/previous navigation plus clear
  • Updates the AI example UIs to show the new search control in the chat header

Testing

  • Added unit and component coverage for search matching, highlighting, navigation, and session reset behavior
  • Validated the touched packages with TypeScript typechecks and the @sqlrooms/ai-core Jest suite

Summary by CodeRabbit

  • New Features

    • Added chat search functionality to search across messages and analysis content.
    • Real-time match highlighting with visual markers throughout conversations.
    • Navigate results with prev/next controls and keyboard shortcuts (Enter, Shift+Enter, Escape).
    • Match counter displays total search results.
  • UI/UX Improvements

    • Layout refinements for improved component organization and responsiveness.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 15, 2026 •

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 484cd0dd-7c31-4546-83de-c1c8b4b28f0d

📥 Commits

Reviewing files that changed from the base of the PR and between c2becab and 65f70c3.

📒 Files selected for processing (6)
  • packages/ai-core/__tests__/ChatSearch.test.tsx
  • packages/ai-core/src/components/AnalysisAnswer.tsx
  • packages/ai-core/src/components/AnalysisResult.tsx
  • packages/ai-core/src/components/Chat.tsx
  • packages/ai-core/src/components/ChatSearch.tsx
  • packages/ai-core/src/components/FlatAgentRenderer.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/ai-core/src/components/Chat.tsx
  • packages/ai-core/src/components/AnalysisResult.tsx

📝 Walkthrough

Walkthrough

This PR implements an end-to-end chat search feature. It adds core search utilities (markdown plaintext conversion, match finding, query normalization), a ChatSearchProvider for session-aware state management and active match navigation, a search UI with input and navigation controls, and text highlighting via rehype plugin injection. The feature is wired into the Chat component, integrated with analysis result rendering, tool logging, and reflected in example app layouts. Tests and package dependencies support the implementation.

Changes

Chat Search Feature

Layer / File(s) Summary
Core search utilities, provider, UI, and transformers
packages/ai-core/src/components/ChatSearch.tsx
Markdown-to-plain-text pipeline via unified/remark/rehype, query normalization, findChatSearchMatches for case-insensitive substring matching with stable ids and offsets, ChatSearchProvider context (session-aware block registration, active-match navigation with wrap-around, scroll into view on change), ChatSearch UI component (input, match counter, prev/next navigation, keyboard shortcuts), HighlightedChatSearchText for inline highlighting, and createChatSearchRehypePlugin for markdown rehype tree transformation with <mark> injection.
Provider integration into Chat
packages/ai-core/src/components/Chat.tsx
Imports ChatSearch and ChatSearchProvider, mounts provider in Chat.Root around SessionChatManager and children, extends exported Chat compound component with Search property, and updates ChatComponent type to include search member.
AnalysisAnswer search support
packages/ai-core/src/components/AnalysisAnswer.tsx
Exports processAnalysisAnswerContent helper (renamed from private processContent), accepts optional searchBlockId prop, derives search matches from context, builds memoized rehypePlugins array that conditionally appends createChatSearchRehypePlugin when matches exist, and passes computed plugins to react-markdown rendering.
AnalysisResult block registration and highlighting
packages/ai-core/src/components/AnalysisResult.tsx
Generates and registers session-scoped ChatSearchBlock entries for analysis prompt, text parts, reasoning parts, and tool parts; wraps prompt display in HighlightedChatSearchText; passes searchBlockId into AnalysisAnswer and tool log rendering; refactors ReasoningBox to render provided children instead of text prop; includes plain-text extraction for text blocks and empty-block filtering.
Tool log search support
packages/ai-core/src/components/FlatAgentRenderer.tsx
Extends OrchestratorToolLogLine and OrchestratorLogLineInner to accept optional searchBlockId prop, enabling tool log segments to link to corresponding search blocks for highlighting.
Example MainView layout updates
examples/ai-core/src/components/MainView.tsx, examples/ai/src/components/MainView.tsx
ai-core example: replaces single-row layout with flex-column wrapper for Chat.Sessions and Chat.Search with gap and right padding. ai example: updates header row to use flex-wrap, justify-between, and applies responsive sizing to Chat.Search via min-w-64 and flex-1.
Tests and package dependencies
packages/ai-core/__tests__/ChatSearch.test.tsx, packages/ai-core/package.json
Adds Jest/jsdom test file covering findChatSearchMatches (case-insensitive matching, multiple matches, empty-query handling, exclusion of tool labels), createChatSearchRehypePlugin (HAST tree transformation, <mark> injection, preservation of existing wrapper tags), and ChatSearch UI (match counter rendering, next/previous navigation with wrap-around). Adds dependencies: remark-parse, remark-rehype, unified (^11.0.0) to dependencies; jsdom (^29.1.1) to devDependencies.
Minor cleanup
packages/ai-core/src/hooks/useScrollToBottom.ts
Adds eslint-disable comment for unused endRef parameter and updates backward-compatibility comment.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • sqlrooms/sqlrooms#632: Both PRs directly modify packages/ai-core/src/components/AnalysisResult.tsx's reasoning rendering and ReasoningBox component (children wiring and content display), with overlapping changes.

Suggested reviewers

  • lixun910
  • dmitriy-kostianetskiy
  • mihajlo-stojkovic

Poem

🔍 A search through thoughts, both fast and true,
Markdown flows to plaintext new,
Highlights dance on every mark,
Sessions bright in reasoning's arc—
ChatSearch blooms! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.88% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main feature added: transcript text search functionality in the AI chat component.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/add-text-search-to-ai

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.

@ilyabo ilyabo changed the title Add transcript text search to AI chat feat: Add transcript text search to AI chat May 15, 2026
@bdjulbic
bdjulbic marked this pull request as ready for review May 18, 2026 11:37
@bdjulbic
bdjulbic requested a review from Copilot May 18, 2026 11:37

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/ai-core/__tests__/ChatSearch.test.tsx`:
- Around line 66-71: The test mutates a module-scoped variable (latestSearch)
during render in SearchController which causes the lint error; instead stop
reassigning latestSearch during render by making latestSearch a React ref or
state held by the test and have SearchController assign into it inside a
useEffect (or expose the hook result via a callback prop) after mount; update
the test to declare latestSearch as a MutableRef (or provide a setter function)
and in SearchController call useChatSearch() then set ref.current (or call the
setter) inside useEffect so no module-scoped mutation happens during render
(references: latestSearch, SearchController, useChatSearch).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 72eadddd-e1a9-4814-a22e-46143b726e1d

📥 Commits

Reviewing files that changed from the base of the PR and between e1584eb and 2072206.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (9)
  • examples/ai-core/src/components/MainView.tsx
  • examples/ai/src/components/MainView.tsx
  • packages/ai-core/__tests__/ChatSearch.test.tsx
  • packages/ai-core/package.json
  • packages/ai-core/src/components/AnalysisAnswer.tsx
  • packages/ai-core/src/components/AnalysisResult.tsx
  • packages/ai-core/src/components/Chat.tsx
  • packages/ai-core/src/components/ChatSearch.tsx
  • packages/ai-core/src/components/FlatAgentRenderer.tsx

Comment thread packages/ai-core/__tests__/ChatSearch.test.tsx Outdated

Copilot AI left a comment

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.

Pull request overview

Adds a chat-local transcript search surface to @sqlrooms/ai-core via Chat.Search, enabling query-based navigation through the current session transcript with match highlighting across prompts, assistant text, reasoning, and lightweight tool labels.

Changes:

  • Introduces ChatSearchProvider, search UI controls, match-finding, and highlighting utilities.
  • Integrates search block registration + highlighting into AnalysisResult, AnalysisAnswer, and orchestrator tool log labeling.
  • Updates example AI UIs to display the new search control in the chat header.

Reviewed changes

Copilot reviewed 9 out of 10 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
pnpm-lock.yaml Locks new markdown parsing + jsdom-related dependencies.
packages/ai-core/src/components/FlatAgentRenderer.tsx Adds optional search highlighting for orchestrator tool activity labels.
packages/ai-core/src/components/ChatSearch.tsx Implements search state, match indexing, highlighting components, and a rehype plugin for markdown highlighting.
packages/ai-core/src/components/Chat.tsx Wraps Chat.Root with ChatSearchProvider and exposes Chat.Search.
packages/ai-core/src/components/AnalysisResult.tsx Registers searchable transcript blocks and highlights prompt/reasoning/tool labels.
packages/ai-core/src/components/AnalysisAnswer.tsx Adds optional rehype highlighting plugin per message block.
packages/ai-core/package.json Adds dependencies for markdown-to-plain-text and jsdom for tests.
packages/ai-core/tests/ChatSearch.test.tsx Adds unit/component tests for matching, highlighting plugin behavior, navigation, and session reset.
examples/ai/src/components/MainView.tsx Adds Chat.Search to the example header layout.
examples/ai-core/src/components/MainView.tsx Adds Chat.Search to the example header layout.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +94 to +105
const normalizedQuery = normalizeChatSearchQuery(query);
if (!normalizedQuery) return [];

const matches: ChatSearchMatch[] = [];
for (const block of blocks) {
if (!block.text) continue;

const normalizedText = block.text.toLocaleLowerCase();
let start = normalizedText.indexOf(normalizedQuery);

while (start !== -1) {
const end = start + normalizedQuery.length;
Comment on lines 212 to 215
<Markdown
remarkPlugins={[remarkGfm]}
rehypePlugins={[rehypeRaw]}
rehypePlugins={rehypePlugins as never}
components={
Comment thread packages/ai-core/package.json

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/ai-core/src/components/AnalysisResult.tsx`:
- Around line 375-385: The JSX for ReasoningBox is malformed and passing an
undeclared prop: remove the className prop from the ReasoningBox invocation
(since ReasoningBox props don’t include className) and fix the tag nesting so
HighlightedChatSearchText is a child of ReasoningBox and ReasoningBox is
properly closed (use <ReasoningBox key=... text={part.text} isRunning={...}>
...</ReasoningBox> with HighlightedChatSearchText as the inner element). Ensure
you keep the existing props (key, text, isRunning) and the
HighlightedChatSearchText props (blockId, text) unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 782f1f5c-8112-4f2f-825c-db2ea8644845

📥 Commits

Reviewing files that changed from the base of the PR and between 2072206 and f994799.

📒 Files selected for processing (5)
  • packages/ai-core/__tests__/ChatSearch.test.tsx
  • packages/ai-core/package.json
  • packages/ai-core/src/components/AnalysisAnswer.tsx
  • packages/ai-core/src/components/AnalysisResult.tsx
  • packages/ai-core/src/components/ChatSearch.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/ai-core/src/components/AnalysisAnswer.tsx
  • packages/ai-core/package.json
  • packages/ai-core/tests/ChatSearch.test.tsx
  • packages/ai-core/src/components/ChatSearch.tsx

Comment thread packages/ai-core/src/components/AnalysisResult.tsx Outdated

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/ai-core/src/components/AnalysisResult.tsx`:
- Around line 220-254: Generate search blocks only for the same parts that are
actually rendered: before the forEach over uiMessageParts, filter uiMessageParts
using the same predicate the render path uses (exclude any part whose index
appears in the suppressed indices collection used by rendering and exclude parts
with ids that start with "step-"), then map that filtered array through the
existing isTextPart/isReasoningPart/isToolPart/isDynamicToolPart logic (using
getToolName and processAnalysisAnswerContent as before) so searchBlockPrefix and
analysisResult.id remain, ensuring returned blocks match visible highlights.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7e4efbdb-b04f-4e13-93e2-8807a6b77508

📥 Commits

Reviewing files that changed from the base of the PR and between f994799 and fcd7cfc.

📒 Files selected for processing (3)
  • packages/ai-core/src/components/AnalysisResult.tsx
  • packages/ai-core/src/components/ChatSearch.tsx
  • packages/ai-core/src/hooks/useScrollToBottom.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/ai-core/src/hooks/useScrollToBottom.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/ai-core/src/components/ChatSearch.tsx

Comment thread packages/ai-core/src/components/AnalysisResult.tsx
bdjulbic added 2 commits May 19, 2026 10:04
…-to-ai

# Conflicts:
#	packages/ai-core/src/components/Chat.tsx

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated 8 comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

Comment thread packages/ai-core/src/components/ChatSearch.tsx
Comment thread packages/ai-core/src/components/ChatSearch.tsx
Comment thread packages/ai-core/src/components/ChatSearch.tsx
Comment thread packages/ai-core/src/components/ChatSearch.tsx Outdated
Comment thread packages/ai-core/src/components/AnalysisResult.tsx Outdated
Comment thread packages/ai-core/src/components/Chat.tsx Outdated
Comment on lines 41 to 45
Item: typeof PromptSuggestions.Item;
VisibilityToggle: typeof PromptSuggestions.VisibilityToggle;
};
Search: typeof ChatSearch;
ModelSelector: typeof ModelSelector;
Comment on lines +663 to +670
reasoning && 'italic',
)}
>
{label}
{searchBlockId ? (
<HighlightedChatSearchText blockId={searchBlockId} text={label} />
) : (
label
)}
bdjulbic and others added 2 commits May 19, 2026 12:01
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
Co-authored-by: Copilot Autofix powered by AI <[email protected]>

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated 5 comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

Comment on lines +124 to +130
useEffect(() => {
setActiveMatchIndex(0);
}, [currentSessionId, query]);

const blocks = useMemo(() => {
const allBlocks = Object.values(blockGroups).flat();
if (!currentSessionId) return allBlocks;
Comment thread packages/ai-core/src/components/ChatSearch.tsx
Comment thread packages/ai-core/src/components/FlatAgentRenderer.tsx Outdated
Comment thread packages/ai-core/src/components/AnalysisResult.tsx Outdated
Comment thread packages/ai-core/src/components/Chat.tsx Outdated
bdjulbic and others added 3 commits May 19, 2026 12:09
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
Co-authored-by: Copilot Autofix powered by AI <[email protected]>

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 10 out of 11 changed files in this pull request and generated 3 comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported
Comments suppressed due to low confidence (2)

packages/ai-core/src/components/FlatAgentRenderer.tsx:623

  • searchBlockId was added to the OrchestratorLogLineInner props/destructuring but is not used anywhere in the component. That means matches for the tool-label blocks registered in AnalysisResult cannot render a corresponding <mark id=...> for highlighting/scrolling, so navigating to those matches won’t scroll to the tool line. Either use searchBlockId to wrap the rendered label with HighlightedChatSearchText, or stop registering tool-label blocks until highlighting is implemented.
  isSuccess: boolean;
  isError: boolean;
  toolCall: AgentToolCall;
  searchBlockId?: string;
}> = ({toolCallId, isPending, isSuccess, isError, toolCall, searchBlockId}) => {
  const showDetails = useShowToolCallDetails();
  const {getActivityLabel} = useToolRenderBehavior();
  const timing = useStoreWithAi((s) => s.ai.toolTimings[toolCallId]);

packages/ai-core/src/components/Chat.tsx:76

  • Chat.Search is exported as part of the Chat compound component, but only Chat.Root is wrapped in ChatSearchProvider. If a consumer renders <Chat.Search /> inside <Chat.LocalAgentRoot>, useChatSearch() will throw at runtime ("Chat.Search must be rendered inside Chat.Root."). Consider also wrapping LocalAgentRoot in ChatSearchProvider (if search should work there), or adjust the API/error message to make the session-only constraint explicit and prevent accidental misuse.
const Root: FC<RootProps> = ({ children, toolRenderBehavior }) => (
  <ToolRenderBehaviorProvider value={toolRenderBehavior ?? EMPTY_BEHAVIOR}>
    <SessionChatRuntimeProvider>
      <ChatSearchProvider>
        <SessionChatManager />
        {children}
      </ChatSearchProvider>
    </SessionChatRuntimeProvider>
  </ToolRenderBehaviorProvider>
);

const LocalAgentRoot: FC<LocalAgentChatRootProps> = ({
  children,
  toolRenderBehavior,
  ...props
}) => (
  <ToolRenderBehaviorProvider value={toolRenderBehavior ?? EMPTY_BEHAVIOR}>
    <LocalAgentChatRuntimeProvider {...props}>
      {children}
    </LocalAgentChatRuntimeProvider>
  </ToolRenderBehaviorProvider>
);

Comment thread packages/ai-core/src/components/FlatAgentRenderer.tsx Outdated
Comment thread packages/ai-core/src/components/ChatSearch.tsx
Comment on lines +216 to +224
uiMessageParts.forEach((part, index) => {
if (isTextPart(part)) {
blocks.push({
id: `${searchBlockPrefix}:text:${index}`,
resultId: analysisResult.id,
text: markdownToPlainText(
processAnalysisAnswerContent(part.text).processedContent,
),
});

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I added a guard for empty queries

@bdjulbic
bdjulbic merged commit 75dfde2 into main May 19, 2026
4 checks passed
@bdjulbic
bdjulbic deleted the codex/add-text-search-to-ai branch May 19, 2026 11:53
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.

3 participants