Record DocumentViewer nav non-adoption and merge the duplicated anchor predicate - #1776
Conversation
…r predicate /issues #288 asked whether DocumentViewer should converge onto the shared InPageNavHeader template. Evaluated and declined on the merits; the decision and its blocking reasons are now recorded in docs/search-chrome-behaviour.md so it reads as closed rather than as a migration waiting for an owner. The premise that four modules are duplicated does not hold. Only the ~70-line header row and its sheet-state plumbing are duplicated: the scroll spy, segment track, section list, jump, and anchor measurement are already single implementations that both paths import. Adoption would instead require InPageNavHeader to grow escape hatches for its one non-conforming consumer — controlled sheet state (DocumentViewer observes it for chrome hold, opens it from a second trigger in the composer dock, and blurs the composer first), sheet content slots (density toggle, portal/headerLeading), and an opt-out of its own chrome metrics (different scope, extra property, headerHidden consumed by the desktop rail) — on a component seven routes already mount. It would also break the DocumentViewer literals pinned by header-scroll-hide-contract and document-section-nav-contract, which are the contract this change exists to preserve. Converged what genuinely was duplicated: the visible-element predicate existed twice, character for character, in use-section-spy.ts and use-page-section-weights.ts. It is now one exported resolveVisibleElement(ids), with resolveSectionElement(id) as the alias-aware wrapper over it. Semantics are unchanged. useResolvedPageSections keeps its third, deliberately different predicate; converging that one would change resolution behaviour on seven live routes with no test covering it. Adds tests/in-page-nav-document-viewer-convergence.dom.test.tsx: behavioural cover for the merged predicate (alias fallback, zero-rect skip, absent section) plus static guards that the in-page-nav path keeps importing the shared spy, track, list and metrics rather than forking them, so the divergence cannot widen from two header rows to two of everything. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q4VxLcvHim6btP5Zu2hFAo
This pull request has been ignored for the connected project Preview Branches by Supabase. |
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in:11 minutes Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe PR extracts shared visible-element resolution for DocumentViewer and in-page navigation. It adds convergence tests and documents the decision to retain a separate DocumentViewer header while sharing navigation infrastructure. ChangesDocumentViewer navigation convergence
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q4VxLcvHim6btP5Zu2hFAo
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/branch-review-ledger.md`:
- Line 840: Update the ledger entry’s component reference from DocumentReviewer
to DocumentViewer, leaving the documented decision and all other entry details
unchanged.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: d9203690-2823-4175-af5b-925069a822dc
📒 Files selected for processing (5)
docs/branch-review-ledger.mddocs/search-chrome-behaviour.mdsrc/components/document-viewer/use-section-spy.tssrc/components/in-page-nav/use-page-section-weights.tstests/in-page-nav-document-viewer-convergence.dom.test.tsx
Uh oh!
There was an error while loading. Please reload this page.
…resolve GitHub DIRTY/staleness) Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Documents the decision to retain DocumentViewer’s specialized header while consolidating its duplicated visibility predicate.
Changes:
- Extracts shared
resolveVisibleElementlogic without changing behavior. - Adds behavioral and architectural regression tests.
- Records the header non-adoption rationale and review outcome.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
src/components/document-viewer/use-section-spy.ts | Exports the shared visibility resolver. |
src/components/in-page-nav/use-page-section-weights.ts | Reuses the shared resolver. |
tests/in-page-nav-document-viewer-convergence.dom.test.tsx | Covers resolution behavior and sharing contracts. |
docs/search-chrome-behaviour.md | Documents the non-adoption decision. |
docs/branch-review-ledger.md | Records the review and verification. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
CI triageCI failed on this PR. Automated classification of the 2 failed job(s):
Compared with main CI run #9477 (cancelled). Classification is evidence routing, not permission to ignore a failure. Exact quarantined Playwright identities remain governed by the flake ledger. |
Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
Uh oh!
There was an error while loading. Please reload this page.
Summary
/issues #288asked whetherDocumentViewershould converge onto the sharedInPageNavHeadertemplate. This PR answers it: declined on the merits, recorded, plus the one piece of duplication that genuinely was worth merging.docs/search-chrome-behaviour.md, replacing the previous "converging it is a separate change" note — which read as a migration waiting for an owner — with a closed decision, its four blocking reasons, and what would have to change to re-open it.getElementById+ rect-size loop existed character for character indocument-viewer/use-section-spy.tsandin-page-nav/use-page-section-weights.ts. It is now one exportedresolveVisibleElement(ids), withresolveSectionElement(id)as the alias-aware wrapper over it. Semantics unchanged.tests/in-page-nav-document-viewer-convergence.dom.test.tsx— behavioural cover for the merged predicate (alias fallback, zero-rect skip, absent section) plus static guards that the in-page-nav path keeps importing the shared spy, track, list and metrics rather than forking them.Why not converge the header
The issue's premise — that
DocumentViewerruns its ownuse-section-spy.ts,use-document-chrome-metrics.ts,section-index.tsandsection-nav.tsx— does not hold. Three of those four are already the shared implementation, imported byin-page-navtoday:document-viewer/section-nav.tsxin-page-nav-header.tsx,use-in-page-section-nav.tsdocument-viewer/use-section-spy.tsuse-in-page-section-nav.ts,use-page-section-weights.tssticky-chrome-metrics.tsSo "fix a bug in one and the other keeps it" already does not apply to the spy, the track, the list, the jump or the anchor measurement. The real duplication is the ~70-line header row and its sheet-state plumbing. Converging that would require
InPageNavHeaderto grow escape hatches for its one non-conforming consumer, on a component seven routes already mount:DocumentViewerfeedsmobileActionsOpen || sectionSheetOpentouseDocumentViewerChromeScroll; a second actions trigger lives in the phone composer dock; andopenSectionSheetblurs the source-search input first.InPageNavHeaderowns its sheet state privately and deliberately (pathname-keyed), so its four Server Component adopters need no client state.DocumentViewDensityTogglein the section sheet,portal/contentClassName/headerLeadingon the actions sheet. The shared sheets take no content slot.viewerRootRefvs document), property set (--document-collapse-height) and return value (headerHiddendrives the desktop rail).InPageNavHeadercalls its metrics hook itself, with no opt-out.header-scroll-hide-contractrequires<PhoneHeaderCollapsePortal>anddata-document-sticky-headerinDocumentViewer.tsx;document-section-nav-contractrequiresdata-testid="document-section-trigger"there. All three move into the shared header on adoption, and keeping them green would mean threading literal strings through props purely to satisfy source-text greps.The anchor-alias question is settled rather than merged: the viewer keeps
sectionAnchorAliases(its sections are derived from the indexed payload at render time, so there is no declaration site fortargetIds), pages keepPageSection.targetIds(resolved before the spy, which is what keeps the spy generic).Verification
npm run verify:pr-local— completedcheck:runtime,check:installed-lock-parity,format:changed,sitemap:check,docs:check-index,docs:check-inventory,docs:check-scripts,docs:check-links,check:branch-review-ledger,check:outstanding-issues,lint,typecheck. Unit suite:Test Files 1 failed | 546 passed (547),Tests 1 failed | 5883 passed | 4 skipped (5888).tests/pr-handoff-stop.test.ts > emits handoff context only when the marker file exists, which is pre-existing and unrelated to this diff — it fails identically on a pristineorigin/mainworktree in the same container (Test Files 1 failed (1),Tests 1 failed | 10 passed (11)). This diff touches nothing under.claude/.npm run verify:phone-chrome— all four stages green. Contracts:Test Files 9 passed (9),Tests 123 passed (123). Focused browser journeys in real Chromium:7 passed (24.5s), including bothdocument detail header overlay and footer follow …cases. Its selector reported "Focused ownership and journey coverage is sufficient for this page-local or test-infrastructure scope" and did not escalate to full UI.Test Files 12 passed (12),Tests 151 passed (151)— coveringheader-scroll-hide-contract,document-section-nav-contract,document-section-nav.dom,in-page-nav-header.dom,in-page-nav-route-sections.dom,document-viewer-shell.dom,document-view-density.dom,differential-section-nav.dom,mode-nav-contract.▲ Next.js 16.2.12 … ✓ Ready).verify:uifor this scope, and no rendering, class, or chrome behaviour changed — the focused Chromium journeys above are the browser proof.verify:release,eval:*,check:supabase-projectandtest:livewere not run — provider-backed and out of scope.Environment note: this container ships Chromium
1194while Playwright 1.62.1 pins1234(/issues #255). Rather than force a mismatched binary, the matching revision 1234 was installed, so the browser evidence above is genuine and not a soft-skip.Risk and rollout
resolveVisibleElement(ids)contains the identical loop, andresolveSectionElement(id)calls it with the same candidate list it built inline before. It is exercised by the document viewer and by all sevenInPageNavHeaderroutes, so it is broadly covered by the suite above and now has direct behavioural tests of its own.git revertthe single commit. No data, schema, config, or provider state is touched.Notes
Deliberately left for a follow-up rather than folded in here (not written to
docs/outstanding-issues.md— another session owns that file this cycle):useResolvedPageSectionscarries a third visibility predicate that is genuinely different from the two merged here: it testsgetClientRects().length > 0 && computedStyle.display !== "none"rather than rect size. Converging it would change resolution behaviour on seven live routes and no current test covers the difference, so it was left alone and documented instead./issues #288when that file is next writable: the issue names four duplicated modules, but three are already shared.Generated by Claude Code
Summary by CodeRabbit
Improvements
Documentation