feat: add chat outline navigation strip#1917
feat: add chat outline navigation strip#1917Abhijitam01 wants to merge 8 commits intopingdotgg:mainfrom
Conversation
Minimap-style strip beside chat content showing user message bars. Hover to expand popover with previews, click to scroll to message. Active messages highlighted via IntersectionObserver.
|
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 |
ApprovabilityVerdict: Needs human review This PR adds a new user-facing feature (chat outline navigation minimap) with a new component, observer-based tracking, and scroll functionality. New features introducing user-facing behavior warrant human review to validate the implementation approach and UX. You can customize Macroscope's approvability policy. Learn more. |
Use functional setState updaters to eliminate shared mutable Set between IntersectionObserver and MutationObserver, preventing race conditions. Batch removal updates into a single state update. Fix theme colors (bg-foreground instead of bg-white) and add max-h-40 scroll cap.
- Traverse nested children in removedNodes handler to find data-message-id on wrapper divs (mirrors the addedNodes traversal pattern) - Move popover outside the overflow-y-auto scrollable strip container to prevent CSS clipping
|
nothin happens when i click here: CleanShot.2026-04-11.at.11.07.05.mp4 |
|
its happening only when the messages passes the number 20 or from starting @juliusmarminge |
|
idk. just tested on a large thread as that's where i thought i could find some bugs but i tried clicking many of the "links" and none worked on this thread |
Wire scrollToIndex through a shared ref so clicking any outline bar scrolls to that message even when it's virtualized off-screen. Pre-compute message-id→row-index Map for O(1) lookups.
Messages in the last ~8 rows are rendered directly in the DOM (not tracked by the virtualizer). scrollToIndex with an out-of-bounds index silently fails for these. Fall back to scrollIntoView when the target index >= virtualizedRowCount.
issue - When you click message #25 in the outline panel, our code looks up its index (25) and tells the virtualizer “hey, scroll to row 25.” But the virtualizer only knows about rows 0–22. It has no idea what row 25 is, so it just… does nothing. No error, no scroll, nothing happens. Fix - |
- Remove behavior: "smooth" from scrollToIndex — conflicts with virtualizer's re-measure correction for distant items, causing silent scroll failures. Matches existing codebase pattern. - Return prev Set when IntersectionObserver fires without actual visibility changes, avoiding unnecessary re-renders during scroll.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit c24a29f. Configure here.
| interface OutlineEntry { | ||
| readonly id: string; | ||
| readonly preview: string; | ||
| } |
There was a problem hiding this comment.
Unused OutlineEntry interface is dead code
Low Severity
The OutlineEntry interface is defined but never referenced anywhere in the codebase. The outlineEntries array gets its type inferred from the useMemo return value, so this interface serves no purpose and is dead code.
Reviewed by Cursor Bugbot for commit c24a29f. Configure here.
| for (const id of removedIds) next.delete(id); | ||
| return next; | ||
| }); | ||
| } |
There was a problem hiding this comment.
Removed-node handler triggers unnecessary re-renders for non-user messages
Low Severity
The MutationObserver removal handler collects IDs from all [data-message-id] elements (including assistant messages), but activeMessageIds only ever contains user message IDs (since the IntersectionObserver only observes [data-message-role="user"] elements). When the virtualizer unmounts assistant message rows, the handler creates a new Set and calls setActiveMessageIds, returning a new reference even though no entries were actually deleted. This causes unnecessary re-renders each time the virtualizer recycles assistant message rows.
Reviewed by Cursor Bugbot for commit c24a29f. Configure here.




Minimap-style strip beside chat content showing user message bars. Hover to expand popover with previews, click to scroll to message. Active messages highlighted via IntersectionObserver.
What Changed
Add a minimap-style outline strip on the right side of the chat, beside the message content area:
Small horizontal bars representing each user message
Currently visible messages highlighted with brighter bars (IntersectionObserver)
Hover the strip to reveal a popover with message text previews
Click any bar or popover entry to smooth-scroll to that message
Only shows user messages, hidden when there are none
Why
Its a simple ui change to make the ux better
UI Changes
Before :
After :
Checklist
Note
Medium Risk
Adds new scroll/navigation UI that integrates with the virtualized timeline via observers and an imperative scroll ref, which could impact scrolling behavior and performance in long threads.
Overview
Adds a minimap-style chat outline strip beside the message list that renders one marker per user message, highlights currently visible messages, and shows a hover popover with truncated previews.
Wires the outline to message navigation by exposing an imperative
scrollToMessageAPI fromMessagesTimeline(viaonScrollToMessageRef) and connecting it inChatView, using the virtualizer when possible and falling back to DOMscrollIntoViewfor non-virtualized tail rows.Reviewed by Cursor Bugbot for commit c24a29f. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Add chat outline navigation strip with click-to-scroll support
ChatOutlinePanelcomponent that renders a minimap-style strip on the right side of the chat, with one bar per user message.IntersectionObserverand aMutationObserverto handle virtualized DOM mount/unmount; the active message is highlighted.MessagesTimelineexposes a newonScrollToMessageRefcallback that scrolls to a message by ID usingrowVirtualizer.scrollToIndex, with a DOM fallback for non-virtualized tail rows.Macroscope summarized c24a29f.