Skip to content

Make the document outline collapsible by subtree - #322

Open
ThePumpingLemma wants to merge 5 commits into
block:mainfrom
ThePumpingLemma:dgrochowski/outline-collapse
Open

ThePumpingLemma wants to merge 5 commits into
block:mainfrom
ThePumpingLemma:dgrochowski/outline-collapse

Conversation

@ThePumpingLemma

@ThePumpingLemma ThePumpingLemma commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

What changed and why

Long plans, such as an STPA report with ten numbered sections (## 5. Correctness › ### 5.1 …) and many sub-sections each, gave an outline far taller than the screen. The left-hand outline can now fold by subtree.

  • Nesting. Entries nest by heading level: each h2 holds the h3s below it, and each h1 holds its h2s.
  • Chevrons. When the outline has more than 12 entries, every parent gets a small chevron before its label. The chevron is a button with aria-expanded and aria-controls. Only the chevron toggles; clicking the label still navigates.
    • A lone root (usually the document title) never folds, because folding it would leave a single line.
  • Defaults. Long outlines start folded, except the subtree holding the active section and the one holding a URL hash target. The hash target's own sub-sections also open.
    • Short outlines render exactly as before: flat, fully expanded, no chevrons and no controls.
  • Auto-expand. Active-section tracking unfolds the ancestors of the active entry as you scroll, once per section reached. It never folds anything. A #section hash change unfolds the target.
  • Expand all / Collapse all. Two small icon buttons sit in the outline's header beside ✕, shown only when something can fold.
  • Keyboard. On a focused entry, Right unfolds it, then moves into its children. Left folds it, then moves to its parent.
  • Persistence. Explicit toggles (chevron, keys, expand/collapse all) are stored in sessionStorage per plan id and keyed by heading id. Keys for headings that no longer exist are ignored. Scroll and link auto-expansion are not stored.
  • Rebuilds. Live content updates and the editor's outline keep each subtree's state across rebuilds. The match is by heading id, then the stored toggle, then heading text, then outline position (typing in an editor heading changes both its id and its text). A section that first becomes foldable during a rebuild, for example when the outline grows past 12 entries, stays open if it holds the reader's position.
  • Comment counts. A folded parent's badge adds the open-discussion counts of its hidden sub-sections, with a tooltip saying so. When the parent is unfolded, each entry shows only its own count, as on main.
  • Live edits. A change inside a folded subtree pulses the parent that hides it.

Everything is in the engine. The behavior is in content_nav_controller.js, with plain CSS in document-outline.css and a small server-rendered partial for the header controls; the same markup is used on the version page.

Overlap with #314: that PR (dgrochowski/summary-block) also touches content_nav_controller.js, to add a pinned "Summary" entry. This branch builds on main and keeps its change contained to the outline's build/assemble path. Whichever lands second will need a small rebase: the Summary entry should be placed outside the nested tree.

Evidence

Screenshots to be attached. Files, at 1400×900, from the nested-outline demo:

  • /tmp/coplan-shots/outline/outline-light-collapsed.png: initial state; sections folded; 4. Correctness shows a total of 3, 7. Security shows 1.
  • /tmp/coplan-shots/outline/outline-light-partly-expanded.png: 4. Correctness opened by its chevron; 7. Security unfolded by scrolling to 7.2.
  • /tmp/coplan-shots/outline/outline-dark-collapsed.png
  • /tmp/coplan-shots/outline/outline-dark-partly-expanded.png
  • Sidebar-only crops of each: outline-{light,dark}-{collapsed,partly-expanded}-sidebar.png

How to try it

  1. bin/rails db:seed, then bin/rails server -p 3107.
  2. Sign in at http://localhost:3107/sign_in as hampton@example.com.
  3. Open http://localhost:3107/hampton/demo-content/rich-text/nested-outline-a-ten-part-service-review. It is also linked from the demo library as Nested outline: a ten-part service review (nested-outline).

What to check:

  • Initial state. The outline shows the title plus ten folded sections. 4. Correctness shows 3 and 7. Security shows 1: those are the discussions inside their folded sub-sections.
  • Chevrons and labels. Click the chevron beside 4. Correctness: its sub-sections appear and each shows its own count. Click a label: the page scrolls there and nothing folds or unfolds.
  • Scrolling. Scroll down into 7.2: 7. Security unfolds and 7.2 is highlighted. 4. Correctness stays open.
  • Expand all / Collapse all. Use the two icons beside ✕ in the outline header.
  • Keyboard. Tab to an entry, then press Right and Left.
  • Persistence. Reload the page: your toggles stay.
  • Hash links. Copy a sub-section's link with the link icon beside its heading. Open the link in a new tab: its section starts unfolded.
  • Editor. Choose Edit: the editor's outline folds the same way and keeps its state as you change headings.
  • Short plans. Open a short plan, such as Rich text: headings, tasks, code, quotes, and footnotes. Its outline looks exactly as before.

Testing

  • bundle exec rspec spec/system/outline_collapse_spec.rb (new, 11 browser examples):

    • A long plan starts folded except the active subtree.
    • The chevron toggles while the label navigates; Left/Right keys work.
    • Scrolling into a folded section's sub-heading unfolds it and leaves the others alone.
    • A #section URL unfolds the target, for both h2 and h3 targets.
    • Expand all and Collapse all work.
    • State survives a reload and Turbo back, with a stale key present, and a live-update rebuild.
    • A folded parent sums its sub-sections' discussion counts.
    • The editor outline folds and keeps its state as headings change, including typing in an open parent's heading.
    • A short plan, and a long flat plan, stay as before with no toggles, gutters or controls.
  • spec/lib/development_seed_spec.rb: new nested-outline example covering initial state, re-seed idempotency, and preservation of local edits and resolved discussions.

  • Touched specs: outline_collapse, development_seed, human_editing, inline_editor, deck_ux, changed_sections_ux, demo_library and requests/plans. 316 examples, 1 failure: the known Chrome "failed to close window in 20 seconds" teardown flake, which hits whichever example is running about 20s after launch. The failing example (outline_collapse_spec.rb:125) passes on its own.

  • Full suite, bundle exec rspec: 3412 examples, 1 failure: the known flaky spec/system/agent_connections_spec.rb:16, with the same teardown error. This run predates a final fix to the outline JS (no gutter on long flat outlines; rebuild defaults and position fallback); the touched specs above were rerun after it.

  • Light and dark mode were checked in a real browser against the seeded demo (screenshots above).

  • Added or updated real JavaScript-enabled browser specs in spec/system/ for changed UI behavior, covering the user interaction and visible result; or explained why no browser UI is involved.

  • Added model, service, or request specs where needed for domain behavior, authorization, or API cases.

  • Ran bundle exec rspec, or explained why it could not run and listed the specs that did run.

  • Tested affected UI behavior in both light and dark mode, or explained why mode testing does not apply.

  • Attached a video or images demonstrating the change, if this PR adds or changes a feature or fixes a UI bug. (Screenshots to be attached.)

outline-dark-collapsed-sidebar outline-dark-collapsed outline-dark-partly-expanded-sidebar outline-dark-partly-expanded outline-light-collapsed-sidebar outline-light-collapsed outline-light-partly-expanded-sidebar outline-light-partly-expanded

Long outlines (more than 12 entries) nest by heading level and fold each
section behind a chevron, except where the reader is. Scrolling and
#section links unfold; expand/collapse all, Left/Right keys, and
per-plan sessionStorage persistence; state survives live updates and the
editor. Folded sections total their sub-sections' discussion counts.
Short outlines are unchanged. Adds the nested-outline demo.
@ThePumpingLemma
ThePumpingLemma marked this pull request as ready for review November 9, 2026 01:57
@HamptonMakes

Copy link
Copy Markdown
Collaborator

🤖 @codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 9, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-09T02:09:01.875933Z b8f1b20 Manual request
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

Signed-off-by: Hampton Lintorn-Catlin <hampton@squareup.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

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

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

Signed-off-by: Hampton Lintorn-Catlin <hampton@squareup.com>
@HamptonMakes

HamptonMakes commented Nov 9, 2026 •

Copy link
Copy Markdown
Collaborator

🤖 Cleanup complete; local tryout ready for @HamptonMakes. Hampton requested independent correctness/product review, routine repairs, and an isolated local tryout; he has not approved or authorized merging this PR.

Pushed two bounded corrections:

  • b3f2c40b: retain keyboard focus when a live update rebuilds the outline; translate live-edit text keys to outline IDs so edits inside numbered, collapsed sections pulse their visible parent. Both added browser regressions failed before the fix and passed afterward.
  • 2b078dfb: shared blue hover and keyboard-focus treatment for tree controls/disclosures; addressed and resolved Codex's finding.

Verification: initial outline + development-seed run 46 examples, 0 failures; follow-up outline, selected inline-editor, and changed-section browser run 31 examples, 0 failures; RuboCop and diff checks pass. All 15 current-head checks are green on 2b078dfb (CI attempt 2). Codex reviewed b8f1b208; its one finding is fixed and resolved. The subsequent changes are bounded corrections with focused regression/browser verification, not a major behavior refactor. GitHub reports MERGEABLE / BLOCKED: no merge conflict; human approval remains outstanding.

CI caveat preserved: attempt 1 failed editor_toolbar_spec.rb[1:1:4] because the sticky-header Edit link was absent after opening a long discussion. This was not the author's teardown failure. The exact local shard and seed 60530 passed 255/0; the toolbar file passed 18/0 on both final head and base 2f0e7ded. Eight older failed CI runs had no matching signature. One diagnostic retry of only the failed shard passed on the unchanged head. The cause remains unproven; this is not a verified flake. No test was weakened and no full local suite was duplicated.

The preview uses a dedicated checkout and development/test databases on port 3322. Sign in as hampton@example.com, no password; open /hampton/demo-content/rich-text/nested-outline-a-ten-part-service-review (also linked from /hampton/demo-library). Try Correctness's chevron: its collapsed 3 becomes child counts 2 and 1. Try Left/Right, expand/collapse all, reload, a subsection link, and editing a heading. Light/dark, keyboard navigation, editor rebuilds, and a second-tab live edit were exercised in a real browser.

Product judgment: useful navigation for long plans, engine-contained with no new dependency. Two separate acceptance choices: (1) the >12-entry threshold, which seems a reasonable starting point for reducing clutter; (2) automatic expansion after deliberate collapse, which can undermine the reader's organization. My recommendation is to respect explicit collapses during ordinary scrolling while allowing intentional section-link navigation to reveal its target. That policy change has not been applied without Hampton's decision. ID/text/position matching remains heuristic for wholesale heading replacement/reordering; avoid adding more matching machinery without a concrete need. #314 still requires integration: its pinned Summary must remain outside this tree and its separate markup must work with the new row-scoped badge/pulse selectors. Combined behavior has not been claimed as tested.

Fresh local evidence (synthetic shared demo):

Light mode, expanded Correctness and keyboard focus on Collapse all

Dark mode, expanded Correctness and keyboard focus on Collapse all

@HamptonMakes

HamptonMakes commented Nov 9, 2026 •

Copy link
Copy Markdown
Collaborator

🤖 Merged current main (af594b7b, including #318) into this PR in c577426e, as part of Hampton's requested PR maintenance. Both demo entries and their tests are retained; math-aware outline text and the existing renderer cache version 28 are preserved. The resolution adds no renderer changes requiring another cache increment. Contributor history was preserved with a normal merge and fast-forward push.

Focused integration: 65 examples, 0 failures across outline collapse, development seeding, math rendering, and math editing; diff checks pass. Resolution review found no new issues. The existing Codex review remains applicable to this bounded additive conflict resolution; no unresolved review findings were returned. All 15 current-head checks passed on c577426e: https://github.com/block/coplan/actions/runs/42669849195. GitHub reports MERGEABLE / BLOCKED, not conflicting; approval remains outstanding.

The isolated port-3322 preview was restarted and exercised: Correctness's folded count of 3 becomes child counts 2 and 1. Its dedicated database still has revision 6, the original text, and four discussions. No product policy changed; the previously documented threshold/scroll-expansion choices remain for Hampton. No approval or merge was authorized or performed.

@HamptonMakes

HamptonMakes commented Nov 12, 2026 •

Copy link
Copy Markdown
Collaborator

🤖 Reconciled current main (1d720fc4, including #313) in 17b26a20 under Hampton's routine PR-maintenance instructions. The only manual conflicts were adjacent demo entries: both the callout gallery and nested outline are retained in the library and documentation. Contributor history and prior feedback fixes are preserved by a normal merge/fast-forward push. No cache design changes or independent cache fixes were added; that work remains with #327.

Focused checks: 60 examples, 0 failures across outline collapse, callouts, and development seeds, including re-seeding preservation. Diff checks and resolution review pass. This additive resolution does not rebuild behavior, so the existing automated review remains applicable; there are no new unresolved review findings. GitHub reports MERGEABLE / BLOCKED on the new head; all 15 current-head CI checks passed on 17b26a20. Approval remains required.

The existing isolated preview on port 3322 is running with current code; availability was rechecked after CI completed (HTTP 200). Main's migrations were applied only to its dedicated development database; revision 6, original nested-outline content, and all four discussions remain intact. Browser specs ran headlessly in isolation; no normal Chrome session was used. PR attachment verified. Prior product choices remain unchanged. No approval or merge was authorized or performed.

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