Skip to content

🤖 feat: sticky table of contents next to plans in chat transcript#3380

Merged
ammario merged 32 commits into
mainfrom
chat-layout-qmqf
May 25, 2026
Merged

🤖 feat: sticky table of contents next to plans in chat transcript#3380
ammario merged 32 commits into
mainfrom
chat-layout-qmqf

Conversation

@ammar-agent
Copy link
Copy Markdown
Collaborator

Summary

Renders a sticky "Contents" navigation alongside propose_plan tool cards in the chat transcript when the chat pane has enough horizontal room. Clicking an entry jumps to the matching plan heading; the TOC follows the user while the plan is on screen, then scrolls away once the plan exits the viewport. Visibility is gated by a CSS container query on the transcript scrollport, so opening/closing sidebars and expanding/collapsing the plan tool never produce layout flash.

Background

Long propose_plan outputs (multi-section refactor plans, rollout designs, etc.) can be hundreds of lines tall. Users had no way to jump between sections short of scrolling, and the centered max-w-4xl transcript column wastes a large strip of horizontal space on wide monitors. This PR puts that unused right gutter to work as a navigation aid for the plan currently in view, without touching the markdown rendering of any other transcript content.

Implementation

Heading source of truth: plan markdown, not the rendered DOM. A new extractPlanHeadings helper walks the plan source and emits one entry per heading (ATX, setext, or block-level raw HTML), skipping fenced code blocks so example markdown inside code samples never shows up in the TOC. Each entry stores a renderIndex — its position across all rendered <h1>..<h6> elements — so click handlers can locate the matching DOM node via container.querySelectorAll("h1..h6")[renderIndex].scrollIntoView({ block: "start" }) without forcing the shared Streamdown rehype pipeline to assign IDs to every heading in the chat.

Layout: absolute aside + sticky inner nav. The plan card is wrapped in a plan-toc-layout positioning context. The TOC <aside> is absolutely positioned to the right of that wrapper (inset: 0 auto 0 100%), so it breaks out of the centered max-w-4xl column into the transcript's right gutter without changing the plan's own bounding box. Inside the aside, the <nav> is position: sticky and bounded to the aside's vertical extent — that's what lets the TOC follow the user while the plan is on screen and then scroll away naturally with the plan.

Visibility: CSS container query, no JS. The transcript scroll container becomes a named CSS container (@container/transcript). The visibility rule fires only above 78.5rem of content-box width, which works out to roughly a 1440px viewport with mux's standard sidebars open. Container queries (rather than viewport min-width) let the gate track actual chat-pane width, so opening or closing the project sidebar or right sidebar correctly hides or reveals the TOC. The plan-toc-aware opt-in class is only set on the centered transcript column — full-width transcript mode is excluded so the TOC never gets clipped by the scroller's overflow-x-hidden.

Self-containment. The TOC only renders when the plan is in normal markdown mode (not annotate, raw-text, or error). Plans with fewer than two visible (h1–h4) headings render nothing. Deep h5/h6 headings are filtered from display but still consume renderIndex, so DOM lookups stay aligned regardless of which levels we show.

Validation

  • make static-check — clean (ESLint, both TypeScript projects, Prettier, docs links).
  • 33 tests covering the new code:
    • 12 unit tests in extractPlanHeadings.test.ts — ATX / setext / raw HTML headings, fenced-code-block skipping, thematic-break disambiguation, inline-formatting stripping, mixed-content renderIndex alignment.
    • 4 unit tests in PlanTableOfContents.test.tsx — short-list hiding, deep-heading filtering, click → scrollIntoView wiring against a hand-rolled DOM (the dedicated component test is needed because sibling test files mock MarkdownCore at file scope), and level normalization.
    • 3 integration tests added to ProposePlanToolCall.test.tsx — TOC content presence, hidden for short plans, hidden during annotate mode.
  • Visual verification via headless Chromium against the new ProposePlanWithTableOfContents Storybook story:
    • 1600×900 wide viewport: TOC visible, sticky in the right gutter, indented by heading level, with ellipsis truncation + tooltip on overflow.
    • 1280×800 desktop viewport: TOC display: none, plan renders identically to before.

Risks

Scoped to plan rendering and a single CSS container declaration on the chat scrollport. Risk surface:

  • Plan tool only: All TOC code is gated by showToc (markdown mode + ≥2 headings + non-error) and only renders inside ProposePlanToolCall. Other tool calls and message types are unaffected.
  • Transcript container: Adding @container/transcript to the scrollport is the only change visible outside the plan tool. There are no existing @container transcript (...) queries in the codebase that would unexpectedly match. The new visibility gate only matches the .plan-toc-aware .plan-toc-aside selector pair, which exists nowhere else.
  • Full-width transcript mode: TOC is explicitly excluded because left: 100% would be clipped by the scroller's overflow-x-hidden and could create invisible focusable controls.
  • Heading-extractor drift: The regex-based extractor could disagree with Streamdown's parser on exotic markdown. If it does, the worst case is a TOC entry pointing at the wrong heading; nothing breaks. The test suite covers the common cases (ATX/setext/HTML, code fences, thematic breaks) and the extractor degrades gracefully on empty input.

Generated with mux • Model: anthropic:claude-opus-4-7 • Thinking: max • Cost: $29.54

Adds a sticky 'Contents' navigation alongside propose_plan tool cards in the
chat transcript when the chat pane has enough horizontal room. Click any
entry to jump to that section in the plan; the TOC follows the user while
the plan is on screen and scrolls away naturally once the plan exits the
viewport.

Visibility is CSS-only (a container query on the transcript scrollport plus
an opt-in class), so toggling the plan tool expanded/collapsed never produces
a layout flash. The transcript scroll container is a named CSS container, so
the gate tracks actual chat-pane width and stays correct when sidebars open
or close.
@ammar-agent
Copy link
Copy Markdown
Collaborator Author

@codex review

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: 2d410d17b2

ℹ️ 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 thread src/browser/features/Tools/ProposePlan/extractPlanHeadings.ts Outdated
- Promote the plan title to be the TOC's heading instead of a static
  "CONTENTS" label plus a separate h1 list entry. Saves vertical space and
  tightens the visual hierarchy: the title sits at column 0, h2 entries
  align flush with it, and h3+ get a small indent step per level.
- Filter h1 entries out of the navigation list since they're represented by
  the heading. h1 still consumes a renderIndex so DOM lookups stay aligned.
- When the plan markdown contains an h1, the TOC heading becomes a button
  that scrolls to that h1 (the natural "top of plan" target). With no h1,
  the heading is a static label.
- Reduce indent steps from 10/20/30px to 6/12/18px to keep the TOC usable
  in the narrow left-gutter column.
- Drop the uppercase/letter-spaced eyebrow styling on the heading since
  real plan titles are sentences, not labels.
@ammar-agent
Copy link
Copy Markdown
Collaborator Author

@codex review

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: 9b70cd43d2

ℹ️ 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 thread src/browser/features/Tools/ProposePlan/extractPlanHeadings.ts Outdated
Comment thread src/browser/features/Tools/ProposePlan/extractPlanHeadings.ts Outdated
@ammar-agent
Copy link
Copy Markdown
Collaborator Author

@codex review

Updated the plan TOC layout and fixed the indented ATX heading renderIndex drift from the previous review.

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: 42205bce7c

ℹ️ 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 thread src/browser/features/Tools/ProposePlan/extractPlanHeadings.ts Outdated
@ammar-agent
Copy link
Copy Markdown
Collaborator Author

@codex review

Updated the TOC to stay in the existing gutter without shifting the plan, widened/wrapped the nav to avoid unnecessary ellipses, and addressed the extractor comments for indented ATX headings plus ordered-list/setext drift.

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: 0817d55127

ℹ️ 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 thread src/browser/features/Tools/ProposePlan/extractPlanHeadings.ts Outdated
Comment thread src/browser/features/Tools/ProposePlan/extractPlanHeadings.ts Outdated
@ammar-agent
Copy link
Copy Markdown
Collaborator Author

@codex review

Updated TOC gutter layout per latest design feedback (no plan displacement, centered gutter nav, thinner hint) and fixed container-prefixed heading index alignment.

@ammar-agent
Copy link
Copy Markdown
Collaborator Author

@codex review

Addressed the latest extractor feedback: setext underlines now preserve indentation so four-space code lines do not create phantom headings, and container-prefixed/blockquoted headings are counted for renderIndex alignment.

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: f54f01bb48

ℹ️ 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 thread src/browser/features/Tools/ProposePlan/extractPlanHeadings.ts Outdated
Comment thread src/browser/features/Tools/ProposePlan/extractPlanHeadings.ts Outdated
@ammar-agent
Copy link
Copy Markdown
Collaborator Author

@codex review

Addressed the HTML edge cases: raw HTML block lines are excluded from setext detection, and empty raw HTML headings now still consume renderIndex for navigation alignment.

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: ebfda4820d

ℹ️ 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 thread src/browser/features/Tools/ProposePlan/extractPlanHeadings.ts Outdated
@ammar-agent
Copy link
Copy Markdown
Collaborator Author

@codex review

Pushed the final visual update: the full TOC stays centered in the gutter and the constrained-width indicator is now plain informational text instead of button-like chrome.

@ammar-agent
Copy link
Copy Markdown
Collaborator Author

@codex review

Addressed the raw HTML block feedback: extraction now tracks CommonMark-style raw HTML block context and skips markdown-looking headings inside those blocks while still counting standalone raw h1-h6 headings.

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: 7286841a5d

ℹ️ 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 thread src/browser/features/Tools/ProposePlan/extractPlanHeadings.ts Outdated
@ammar-agent
Copy link
Copy Markdown
Collaborator Author

@codex review

Addressed nested raw HTML headings as well: standalone h1-h6 tags are counted even while inside raw HTML block context, so later renderIndex values stay aligned.

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: a9206c1f5b

ℹ️ 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 thread src/browser/features/Tools/ProposePlan/extractPlanHeadings.ts Outdated
@ammar-agent
Copy link
Copy Markdown
Collaborator Author

@codex review

Addressed script/style raw HTML handling: nested h1-h6 tags are counted only for raw HTML blocks whose rendered content can contribute headings, while script/style/pre/comment-like blocks continue to suppress heading counting.

@ammar-agent
Copy link
Copy Markdown
Collaborator Author

@codex review

No unresolved Codex threads remain; pushed an empty commit to rerun the failing Codex Comments check after resolving the stale parser-review threads.

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: 0d97834972

ℹ️ 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 thread src/browser/features/Tools/ProposePlan/extractPlanHeadings.ts
@ammar-agent
Copy link
Copy Markdown
Collaborator Author

@codex review

Addressed raw HTML allowlist parity: raw HTML heading extraction now uses the same rawHtmlUsesOnlyAllowedTags guard as MarkdownCore, so unsupported raw blocks are treated as text and do not affect TOC entries or renderIndex.

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: d803b2ef94

ℹ️ 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 thread src/browser/features/Tools/ProposePlan/extractPlanHeadings.ts
@ammar-agent
Copy link
Copy Markdown
Collaborator Author

@codex review

Addressed inline-token raw HTML headings: inline token children are reconstructed when they contain raw HTML so paragraph/list inline <hN>...</hN> tags are counted for TOC renderIndex alignment.

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: 1ab11202cc

ℹ️ 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 thread src/browser/features/Tools/ProposePlan/extractPlanHeadings.ts Outdated
Comment thread src/browser/styles/globals.css
@ammar-agent
Copy link
Copy Markdown
Collaborator Author

@codex review

Addressed both findings: inline reconstruction now ignores code_inline/non-HTML content while preserving text inside actual HTML tags, and the full TOC breakpoint now waits until the gutter can contain the 13rem rail without clipping.

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: a04e419d36

ℹ️ 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 thread src/browser/styles/globals.css Outdated
Comment thread src/browser/features/Tools/ProposePlan/extractPlanHeadings.ts Outdated
@ammar-agent
Copy link
Copy Markdown
Collaborator Author

@codex review

Addressed both latest findings: the full TOC now reveals at 82rem so the 13rem gutter rail fits, and inline HTML reconstruction tracks nested tags so text after inner tags remains in the TOC label.

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Delightful!

ℹ️ 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".

@ammar-agent
Copy link
Copy Markdown
Collaborator Author

@codex review

Polished the TOC reveal threshold, compact hint spacing, and title tint per design feedback.

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep it up!

ℹ️ 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".

@ammar-agent
Copy link
Copy Markdown
Collaborator Author

@codex review

Adjusted the ToC breakpoint/gutter so the full ToC is visible in the 1600px wide Storybook story, and fixed the story copy to say left gutter.

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: f8a2acccea

ℹ️ 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 thread src/browser/features/Tools/ProposePlan/extractPlanHeadings.ts Outdated
@ammar-agent
Copy link
Copy Markdown
Collaborator Author

@codex review

Fixed the Storybook/Chromatic mismatch: the ToC story now pins an explicit wide Chromatic viewport, asserts the full ToC is visible under Chromatic/wide runtime, and has a unit contract so future responsive snapshot stories do not silently capture the wrong UI.

@ammar-agent
Copy link
Copy Markdown
Collaborator Author

@codex review

Addressed the raw HTML <pre> heading-index comment by counting h1-h6 tags inside allowed <pre> raw HTML instead of stripping the whole block, with a regression test for renderIndex alignment.

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: ae88d5e31e

ℹ️ 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 thread src/browser/features/Tools/ProposePlan/extractPlanHeadings.ts Outdated
@ammar-agent
Copy link
Copy Markdown
Collaborator Author

@codex review

Addressed the implicit-close raw HTML heading comment by scanning opening h1-h6 tags and stopping at explicit closes, block boundaries, or the next heading tag. Added regression coverage for unclosed headings and adjacent raw HTML headings.

@ammario ammario merged commit cbdbf53 into main May 25, 2026
24 checks passed
@ammario ammario deleted the chat-layout-qmqf branch May 25, 2026 16:31
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.

2 participants