Skip to content

feat: add chat outline navigation strip#1917

Open
Abhijitam01 wants to merge 8 commits intopingdotgg:mainfrom
Abhijitam01:Abhijitam01/chat-panel-ui
Open

feat: add chat outline navigation strip#1917
Abhijitam01 wants to merge 8 commits intopingdotgg:mainfrom
Abhijitam01:Abhijitam01/chat-panel-ui

Conversation

@Abhijitam01
Copy link
Copy Markdown

@Abhijitam01 Abhijitam01 commented Apr 11, 2026

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 :

Screenshot 2026-04-11 at 8 10 57 AM

After :

Screenshot 2026-04-11 at 8 14 09 AM Screenshot 2026-04-11 at 8 11 57 AM

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

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 scrollToMessage API from MessagesTimeline (via onScrollToMessageRef) and connecting it in ChatView, using the virtualizer when possible and falling back to DOM scrollIntoView for 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

  • Adds a new ChatOutlinePanel component that renders a minimap-style strip on the right side of the chat, with one bar per user message.
  • Tracks message visibility via IntersectionObserver and a MutationObserver to handle virtualized DOM mount/unmount; the active message is highlighted.
  • Clicking an entry scrolls to that message; hovering expands a popover showing a message preview.
  • MessagesTimeline exposes a new onScrollToMessageRef callback that scrolls to a message by ID using rowVirtualizer.scrollToIndex, with a DOM fallback for non-virtualized tail rows.

Macroscope summarized c24a29f.

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.
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 11, 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: 63878b30-bb8b-41ba-9efe-3e4cad86b1bb

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:L 100-499 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list. labels Apr 11, 2026
@juliusmarminge
Copy link
Copy Markdown
Member

juliusmarminge commented Apr 11, 2026

how does it handle very long threads? this happened to me in conductor a while back xd
image

@macroscopeapp
Copy link
Copy Markdown
Contributor

macroscopeapp bot commented Apr 11, 2026

Approvability

Verdict: 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.

@Abhijitam01
Copy link
Copy Markdown
Author

how does it handle very long threads? this happened to me in conductor a while back xd image

this will only show upto 20 messages and then after that a scroll functionality

Abhijitam01 and others added 2 commits April 11, 2026 16:16
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
@juliusmarminge
Copy link
Copy Markdown
Member

nothin happens when i click here:

CleanShot.2026-04-11.at.11.07.05.mp4

@Abhijitam01
Copy link
Copy Markdown
Author

its happening only when the messages passes the number 20 or from starting @juliusmarminge

@juliusmarminge
Copy link
Copy Markdown
Member

juliusmarminge commented Apr 11, 2026

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.
@Abhijitam01
Copy link
Copy Markdown
Author

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

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 -
Before calling scrollToIndex, check if the message index is within the virtualizer’s range. If it’s a tail row (index >= virtualizedRowCount), skip the virtualizer entirely and use plain scrollIntoView — because those elements are already in the DOM, always.

- 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.
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 2 potential issues.

Fix All in Cursor

❌ 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;
}
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.

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.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit c24a29f. Configure here.

for (const id of removedIds) next.delete(id);
return next;
});
}
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.

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.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit c24a29f. Configure here.

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

Labels

size:L 100-499 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.

2 participants