Skip to content

chat: add typed scrollbar markers to the Chat View transcript#322312

Closed
matthewcorven wants to merge 52 commits into
microsoft:mainfrom
matthewcorven:agents/chat-scrollbar-markers-impl
Closed

chat: add typed scrollbar markers to the Chat View transcript#322312
matthewcorven wants to merge 52 commits into
microsoft:mainfrom
matthewcorven:agents/chat-scrollbar-markers-impl

Conversation

@matthewcorven

@matthewcorven matthewcorven commented Jun 21, 2026

Copy link
Copy Markdown

Summary

Adds a typed scrollbar marker overlay to the Chat View transcript, letting users see and jump to significant conversation moments (prompts, questions, file changes, compactions, errors) directly from the vertical scrollbar — analogous to Monaco's overview ruler.

screenshot:
image

  • Typed marker taxonomy with five types (prompt, askQuestion, fileChange, compaction, error) mapped to three lanes (right, left, full), produced from chat view-model signals including a new editedFileEvents field on IChatRequestViewModel.
  • Minimal read-only list/tree API (getOverviewRulerLayoutInfo, getElementTop, getElementHeight) threaded through ListViewListAbstractTree so the chat widget can anchor the overlay without DOM-query hacks.
  • Marker controller (ChatScrollbarPromptMarkerController) that computes pixel-accurate geometry, resolves overlaps deterministically (error > compaction/fileChange > askQuestion > prompt), reuses DOM nodes for smooth transitions, and uses full-width hit-testing so narrow lane markers stay clickable.
  • Host scoping + config: markers enabled only in Chat View transcripts (excluded from Quick Chat and Inline Chat). The feature is gated by a new chat.scrollbarPromptMarkers.enabled setting (default false, tagged experimental) and a companion chat.scrollbarPromptMarkers.clickBehavior setting (revealAndFocus default, reveal alternate). Both settings react live — toggling enabled while a chat is open immediately shows/hides the marker overlay without requiring a reload.
  • Styling + tests: per-type/per-lane CSS using --vscode-charts-* theme tokens (purple=prompt, yellow=askQuestion/compaction, green=fileChange, red=error), plus focused unit tests covering descriptor production, lane assignment, overlap/click resolution, controller lifecycle, and setEnabled enable/disable behavior.

Lane mapping

The final lane mapping is right, left, and full:

Marker type Lane
prompt right
askQuestion left
fileChange full
compaction full
error full

Center-lane taxonomy mapping was intentionally excluded — left/right/full mirrors Monaco's overview ruler lane layout and keeps the interaction model simple.

Files changed

  • src/vs/base/browser/ui/list/listView.ts — expose getOverviewRulerLayoutInfo() on IListView.
  • src/vs/base/browser/ui/list/listWidget.ts — pass-through getOverviewRulerLayoutInfo() and getElementHeight().
  • src/vs/base/browser/ui/tree/abstractTree.ts — pass-through getOverviewRulerLayoutInfo(), getElementTop(), getElementHeight().
  • src/vs/workbench/contrib/chat/browser/actions/chatPromptNavigationActions.ts — typed marker taxonomy, descriptor producers, lane/priority mapping.
  • src/vs/workbench/contrib/chat/browser/widget/chatScrollbarPromptMarkerController.ts — new marker overlay controller with setEnabled for live enable/disable.
  • src/vs/workbench/contrib/chat/browser/widget/chatListWidget.ts — wire controller, expose geometry helpers, setScrollbarPromptMarkersEnabled passthrough.
  • src/vs/workbench/contrib/chat/browser/widget/chatWidget.ts — host scoping flag, gated on chat.scrollbarPromptMarkers.enabled, reacts to live setting changes.
  • src/vs/workbench/contrib/chat/browser/widget/media/chat.css — marker container, lane, and per-type styling.
  • src/vs/workbench/contrib/chat/browser/chat.shared.contribution.ts — register chat.scrollbarPromptMarkers.enabled and chat.scrollbarPromptMarkers.clickBehavior settings.
  • src/vs/workbench/contrib/chat/common/constants.tsChatConfiguration keys + ChatScrollbarPromptMarkerClickBehavior enum.
  • src/vs/workbench/contrib/chat/common/model/chatViewModel.ts — expose editedFileEvents on IChatRequestViewModel.
  • src/vs/workbench/contrib/chat/test/browser/actions/chatPromptNavigationActions.test.ts — descriptor production tests.
  • src/vs/workbench/contrib/chat/test/browser/widget/chatScrollbarPromptMarkerController.test.ts — controller lifecycle/geometry/interaction tests + setEnabled enable/disable tests.

Verification

  • npm run typecheck-client passes.
  • npm run valid-layers-check passes (list/tree API surface extended).
  • Targeted chat tests pass.

Repro steps for new feature set:

  • Set "chat.scrollbarPromptMarkers.enabled": true in settings.
  • Manual validation in a long chat transcript:
    • Prompt markers render in the right lane.
    • Ask-question markers render in the left lane.
    • File-change, compaction, and error markers render full lane.
    • Error remains visually dominant under overlap.
    • Click behavior matches the chat.scrollbarPromptMarkers.clickBehavior setting.
    • Markers do not appear in Quick Chat or Inline Chat.
    • Markers do not appear when chat.scrollbarPromptMarkers.enabled is false (default).
    • Markers remain visible in high-contrast themes.
  • Live setting toggle (no reload):
    • Open a chat transcript with markers visible (enabled: true).
    • Toggle chat.scrollbarPromptMarkers.enabled to false in Settings — markers disappear immediately.
    • Toggle back to true — markers reappear immediately without scrolling or reloading.
    • Toggle the setting off then on in Quick Chat / Inline Chat — markers never appear (host scoping still applies).

Copilot AI review requested due to automatic review settings June 21, 2026 22:36

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 typed “overview ruler”-style scrollbar marker overlay to the Chat View transcript so users can visually locate and click-jump to key moments in a conversation (prompts, ask-questions, file changes, compactions, errors), backed by a small read-only geometry API threaded through the base list/tree stack.

Changes:

  • Extend list/tree widget APIs to expose overview-ruler layout info and per-element geometry (top/height) for anchoring overlays without DOM querying.
  • Introduce a chat-local marker taxonomy + descriptor pipeline and a dedicated controller to render/update markers, resolve overlaps, and handle pointer interaction.
  • Add configuration, styling, and unit tests for marker production and controller behavior; scope the feature to the main Chat View transcript (excluding Quick/Inline Chat).

Reviewed changes

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

Show a summary per file
File Description
src/vs/base/browser/ui/list/listView.ts Exposes overview-ruler layout info from the list view.
src/vs/base/browser/ui/list/listWidget.ts Threads overview-ruler layout info and element-height access through List.
src/vs/base/browser/ui/tree/abstractTree.ts Threads overview-ruler layout info and element top/height through AbstractTree.
src/vs/workbench/contrib/chat/browser/actions/chatPromptNavigationActions.ts Adds marker taxonomy enums + descriptor computation (lanes, priorities, clustering).
src/vs/workbench/contrib/chat/browser/widget/chatScrollbarPromptMarkerController.ts New controller for marker rendering, overlap handling, and click navigation.
src/vs/workbench/contrib/chat/browser/widget/chatListWidget.ts Wires controller into chat transcript list and refresh triggers; exposes geometry helpers.
src/vs/workbench/contrib/chat/browser/widget/chatWidget.ts Scopes marker enablement to Chat View (not Quick Chat / Inline Chat).
src/vs/workbench/contrib/chat/browser/widget/media/chat.css Adds marker overlay and per-type/per-lane styling; includes some related CSS adjustments.
src/vs/workbench/contrib/chat/browser/chat.shared.contribution.ts Registers chat.scrollbarPromptMarkers.clickBehavior setting.
src/vs/workbench/contrib/chat/common/constants.ts Adds ChatConfiguration key and click-behavior enum.
src/vs/workbench/contrib/chat/common/model/chatViewModel.ts Exposes editedFileEvents on IChatRequestViewModel.
src/vs/workbench/contrib/chat/test/browser/actions/chatPromptNavigationActions.test.ts Unit tests for descriptor production, lane/priority mapping, and click behavior helper.
src/vs/workbench/contrib/chat/test/browser/widget/chatScrollbarPromptMarkerController.test.ts Unit tests for controller layout, geometry, interaction, and lifecycle behavior.
.github/prompts/chat-scrollbar-markers-test-hardening.prompt.md Agent prompt/documentation for test hardening work.
.github/prompts/chat-scrollbar-markers-stability-validation.prompt.md Agent prompt/documentation for stability/perf validation.
.github/prompts/chat-scrollbar-markers-scoping.prompt.md Agent prompt/documentation for feature scoping and implementation plan.
.github/prompts/chat-scrollbar-markers-implementation.prompt.md Agent prompt/documentation for implementation plan and verification checklist.

Comment thread src/vs/workbench/contrib/chat/browser/widget/media/chat.css
Comment thread src/vs/workbench/contrib/chat/browser/widget/chatListWidget.ts
@matthewcorven

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

…r to editor scrollbar styling, and stabilize clicks on pre-scrolled-virtualized areas
…tion plan

- §0: explicitly build both Electron apps (npm run electron + transpile-client)
  and verify .build/electron paths exist before §1/§2 reference them
- §0: add npm install for main worktree (required before transpile-client)
- §1: document where --heap-snapshots artifacts land and that §5 consumes them
- §1: add independence note clarifying perf:chat and perf:chat-leak don't
  share state or need port coordination
- §3: clarify it launches its own instance and doesn't need the mock server;
  note its snapshots are consumed by §5
- §4: add mock LLM server setup step before scenarios that need chat responses
  (C, D, E, I, J, K); specify custom scenario registration for I and J
- §4: add heap snapshot capture instructions for §5 consumption
- §4 Scenario L: add explicit dependency note that it runs concurrently with
  Scenario D on the same instance
- §5: add prerequisites block enumerating snapshot sources from §1, §3, §4
  and what to do if sources are missing
- §6: add prerequisites block requiring a fresh Code OSS instance with CDP
  and mock server (do not rely on §4's instance which may be torn down)
- Add execution order summary at top of Methodology listing all section
  dependencies and produced artifacts
- §1: specify exact output locations for ci-summary.md, run-summary JSON,
  and heap snapshots (all under .chat-simulation-data/)
- §1: document that --ci enables --cleanup-diagnostics which deletes heap
  snapshots for non-regressed scenarios; add second-pass command without
  --ci to obtain snapshots for §5 analysis
- §2: specify that perf:chat-leak writes to stdout + JSON file
  (.chat-simulation-data/chat-simulation-leak-results.json); add tee/cp
  commands to capture both; note JSON overwrite between branch/main runs
- §3: fix misconception that chat-session-switch-smoke.mts uses --build
  flag; it launches via scripts/code.sh from current worktree sources
- §4: document mock server standalone startup command and [scenario:<id>]
  tag-based scenario selection mechanism
- §4: document that registerScenario()/ScenarioBuilder cannot produce error
  responses or errorDetails; provide alternative approaches for Scenario I
  (kill server mid-stream, dead port) with skip-and-document fallback
- §4: document that /compact is extension-handled, not mock-server-handled;
  provide fallback if compaction marker doesn't appear
- §4: add failure handling guidance (max 2 retries, then mark BLOCKED)
- §6: correct claim that chat-memory-smoke.mts handles mock server setup;
  it does not — mock server must be started separately
- Scenario L: instruct agent to verify debug log path from source via grep
  rather than hardcoding /Users/core/out.txt
- Deliverables: add missing report sections (session switch from §3,
  open/close cycle from §6, blocked scenarios list); add blocked status
  to summary table
- Add Cleanup section: remove /tmp/vscode-main-baseline worktree
- Add Launch failure handling section: missing electron, port conflicts,
  CDP timeout, missing sqlite3
- Update execution order summary with §1 two-pass note, §2 output location,
  and §9 cleanup step
- Update constraints recap with worktree cleanup reminder
- §0: add reminder to remove baseline worktree (cross-ref Cleanup section)
…s — only editor close disposes ChatWidget, not panel hide
@matthewcorven

Copy link
Copy Markdown
Author

#284142, #293106

@matthewcorven matthewcorven marked this pull request as ready for review June 22, 2026 00:29
@vs-code-engineering

vs-code-engineering Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

📬 CODENOTIFY

The following users are being notified based on files changed in this PR:

@benibenj

Matched files:

  • src/vs/base/browser/ui/list/listView.ts
  • src/vs/base/browser/ui/list/listWidget.ts
  • src/vs/base/browser/ui/tree/abstractTree.ts

@matthewcorven matthewcorven requested a review from Copilot June 23, 2026 01:30

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 14 out of 14 changed files in this pull request and generated 2 comments.

Comment thread src/vs/workbench/contrib/chat/browser/widget/media/chat.css
Comment thread src/vs/workbench/contrib/chat/browser/widget/media/chat.css
matthewcorven and others added 2 commits June 25, 2026 23:15
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

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 14 out of 14 changed files in this pull request and generated 2 comments.

Comment thread src/vs/workbench/contrib/chat/browser/widget/media/chat.css
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

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 14 out of 14 changed files in this pull request and generated 1 comment.

Gate marker activation on the primary mouse button so right/middle click
no longer navigates or suppresses the scrollbar. Add a pointercancel
listener that resets gesture state when the OS interrupts the pointer
sequence, preventing a later unrelated pointerup from arming click
suppression. Centralize gesture-state cleanup in resetGestureState and
call it from setVisible/setEnabled so markerActivated cannot survive a
mid-gesture hide or disable.

Addresses review findings 1-3 from the edge-case audit.
Add tests for the three edge-case findings:
- Non-primary mouse button (right click) does not activate markers
  or call preventDefault/stopPropagation
- pointercancel resets gesture state so a later unrelated pointerup
  does not arm click suppression
- setVisible(false) and setEnabled(false) mid-gesture reset
  markerActivated so it cannot survive into the next interaction
Keep both our scrollbar prompt marker CSS rules and upstream's
.monaco-reduce-motion checkpoint restore rule — they are independent
additions that landed at the same location.
- Remove unused onDidChangeFocus public event from ChatListWidget (no consumers)
- Wire ChatScrollbarPromptMarkerController.revealItem to the shared
  applyScrollbarPromptMarkerClickBehavior helper for a single source of
  truth on click behavior, preserving the deferred focus-retry logic
- Add explicit return type annotation to getOverviewRulerLayoutInfo to
  match the IChatScrollbarPromptMarkerHost interface contract
- Add a comment explaining why refresh() (not refreshIfDimensionsChanged())
  is required on item-height changes

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 14 out of 14 changed files in this pull request and generated 1 comment.

…alse)

Addresses Copilot review comment: setEnabled(false) previously left the
overlay container in the DOM and the capture listeners on the overview-
ruler parent attached, so every pointerdown/click/pointerup still went
through the controller (early-returning) and the overlay remained until
dispose. Now setEnabled(false) calls a new detachOverlay() helper that
removes the container from the DOM and clears all parent listeners,
making the feature a true no-op when disabled. layout() re-attaches
both on re-enable.

Also fixes a regression from the earlier revealItem refactor: the
applyScrollbarPromptMarkerClickBehavior helper calls focusItem
synchronously for RevealAndFocus, but the controller must defer
focusItem across animation frames. Now the helper is only used for the
Reveal behavior; RevealAndFocus calls reveal directly then defers
focusItem via the retry loop.

Adds a test verifying setEnabled(false) detaches the container and
disposes all parent listeners.

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 14 out of 14 changed files in this pull request and generated 1 comment.

Comment on lines +544 to +546
// Prefer right-lane (prompt) > left-lane > full-lane
const lanePriority: Record<string, number> = { right: 0, left: 1, full: 2 };
candidates.sort((a, b) => (lanePriority[a.lane] ?? 3) - (lanePriority[b.lane] ?? 3));
@matthewcorven

Copy link
Copy Markdown
Author

Closing this out, will return with a more simplified approach taking inspiration from GitHub Copilot Desktop

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants