Skip to content

fix(settings): unfreeze the desktop settings panel and keep its rail reachable - #1641

Merged
BigSimmo merged 21 commits into
mainfrom
claude/settings-nav-freeze-desktop-tdzh7z
Aug 7, 2026
Merged

fix(settings): unfreeze the desktop settings panel and keep its rail reachable#1641
BigSimmo merged 21 commits into
mainfrom
claude/settings-nav-freeze-desktop-tdzh7z

Conversation

@BigSimmo

@BigSimmoBigSimmo commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Summary

Reported: on desktop, opening settings freezes the settings navigation and leaves no way to click back. Reproduced at 1440x900 — the panel does not scroll, and clicking any section in the rail scrolls the rail, the title bar and the close control out of the dialog with no scrollbar to bring them back.

Three compounding defects, all at lg and up:

  • The panel could not scroll at all. The two-column grid used lg:h-auto + lg:max-h-[min(88dvh,840px)]. An auto-height grid sizes its single row to max-content — the full ~2800px of settings — which overflows the 792px-capped container and gets clipped by overflow-hidden. The scroll column inside therefore never overflowed its own box, so its overflow-y-auto never engaged (measured at 1440x900: scrollHeight === clientHeight === 2800, no scrollbar, wheel does nothing). Fixed with a definite lg:h-[min(88dvh,840px)], which bounds the row and so bounds the column. Same visual cap as before, since the content always reached it.

  • A rail click scrolled the whole panel away.scrollToSection called target.scrollIntoView(), which walks every scrollable ancestor — and an overflow: hidden ancestor is still programmatically scrollable. With the real scroller inert it scrolled the clipped grid instead (after clicking Privacy: grid.scrollTop 2008, rail at top: -1954). It now scrolls the settings scroll port explicitly via container.scrollTo, offset by the sticky header, and asks for "instant" under reduced motion — "auto" would have deferred to the container's own scroll-smooth.

  • The desktop title bar was lg:static. The close control lives in that bar, so reaching a later section scrolled the only pointer-driven way out of settings off the top. It stays sticky at lg now, with an opaque panel-surface fill so content passes behind it rather than through it. Scroll-hide stays phone-only via lg:translate-y-0.

  • Scroll-spy rewritten from geometry instead of IntersectionObserver. An observer callback receives only the entries whose intersection changed in that batch, so "topmost visible entry" was the topmost of a partial set — which is why selecting the last rail item highlighted its neighbour. A rail click now pins its own selection until the reader scrolls for themselves, and the spy is gated behind the lg media query so phone scrolling pays nothing for its geometry reads.

  • Regression coverage added to tests/ui-smoke.spec.ts: asserts the settings column owns the overflow and the panel never clips, then walks the last three rail sections checking the rail and close control stay in the viewport.

Mobile/phone behaviour is untouched — every layout change is lg:-scoped, and the phone scroll path takes the same work it did before.

Verification

  • npm run typecheck — clean

  • npm run lint — clean

  • npm run testTest Files 509 passed (509), Tests 5374 passed | 4 skipped (5378)

  • npm run build — passed, Client bundle secret surface check passed.

  • npm run check:rag:fixturesOffline RAG fixture and manifest validation passed (36 golden cases, 23 suites).

  • New journey via npm run test:e2e -- tests/ui-smoke.spec.ts --project=chromium -g "desktop settings scrolls its own column"1 passed (5.1s) against a production build. Confirmed it fails when lg:h-auto is temporarily restored, so it pins the defect rather than the fix.

  • Direct Chromium proof of the reported surface at 1440x900, 1280x720, 1024x640 and under reduced motion: grid never clips, content column scrollable, and all eight rail items land their heading below the sticky bar with the rail and close button in-panel and hit-testable throughout.

  • npm run test:e2e:pr (the verify:ui journey set) — 347 passed, 4 failed. The new settings journey passed. Each of the four failures was checked against the pre-fix tree in the same container and is not attributable to this diff:

    • ui-pwa.spec.ts:143 (browser-valid manifest / installable icons / root worker) — fails identically at baseline.
    • ui-smoke.spec.ts "document viewer puts the PDF preview first with pinned evidence after it on mobile" — fails identically at baseline, same assertion (expect(pdfScroller.locator("canvas")).toBeVisible()element(s) not found).
    • ui-smoke.spec.ts "document frame stretches canvas and native owners at phone and desktop" — passes in isolation with this diff applied; same PDF.js-canvas family as the above, failing only under full-sweep parallel load.
    • ui-formulation.spec.ts:234 — passes in isolation both at baseline and with this diff; unreachable from a settings-dialog change.

    The PDF.js canvas failures are very likely this container's Chromium substitution (see below) rather than a product regression — CI's own Production UI shards are the authority on that, not this local run.

Verification not run: npm run verify:pr-local — it fails closed at check:installed-lock-parity on pre-existing container drift (playwright: installed 1.62.0 does not match locked 1.62.1), unrelated to this diff. Its remaining steps (check:runtime, format, lint, typecheck, test, build, check:rag:fixtures) were each run directly and are listed above. For the same drift, Playwright was driven with PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH pointed at the preinstalled Chromium 1194 instead of the locked build.

No provider-backed gate was run: no OpenAI, Supabase, or live eval calls.

Risk and rollout

  • Risk: Low, and confined to the desktop settings dialog. The behavioural surface is one component's layout and scroll handling; no shared primitive is touched — Sheet is unchanged. The main thing to watch is the lg title bar now being sticky, which changes what a reader sees behind it when scrolled.
  • Rollback: single-commit revert. Nothing is persisted, migrated, or cached by this change.
  • Provider or production effects: None.

Notes

classifyPullRequestFiles returns clinicalRisk: false, operationalRisk: false, ragRanking: false, ui: true for this diff, so no Clinical Governance Preflight and no RAG impact: line are required. No retrieval, ranking, ingestion, source-rendering, document-access, or privacy-behaviour code is touched — the Privacy section of the settings dialog is rendered by this file, but its clear/reset handlers are unmodified.

Summary by CodeRabbit

  • Bug Fixes

    • Improved desktop settings navigation and scrolling.
    • Kept the settings title bar visible while scrolling.
    • Ensured section links, close controls, and panels remain reachable and correctly positioned.
    • Improved behavior when reopening the settings dialog, including resetting the selected section.
  • Tests

    • Added regression coverage for desktop settings scrolling, navigation, layout, and reset behavior.

…reachable
On desktop, opening settings left the panel unscrollable, and clicking any
section in the rail scrolled the rail, the title bar and the close control out
of the dialog with no way to bring them back.
Three compounding defects, all at `lg` and up:
- The two-column grid used `lg:h-auto` + `lg:max-h-`. An auto-height grid sizes
its single row to max-content — the full ~2800px of settings — which overflows
the 792px-capped container and is clipped by `overflow-hidden`. The scroll
column inside therefore never overflowed its own box, so `overflow-y-auto`
never engaged (measured at 1440x900: scrollHeight === clientHeight === 2800).
A definite `lg:h-[min(88dvh,840px)]` bounds the row, which bounds the column.
Same visual cap as before, since the content always reached it.
- `scrollToSection` called `target.scrollIntoView()`, which walks every
scrollable ancestor — and an `overflow: hidden` ancestor is still
programmatically scrollable. With the real scroller inert it scrolled the
clipped grid instead (after clicking Privacy: grid.scrollTop 2008, rail at
top -1954). It now scrolls the settings scroll port explicitly, offset by the
sticky header, and asks for "instant" under reduced motion — "auto" would
have deferred to the container's own `scroll-smooth`.
- The desktop title bar was `lg:static`, so reaching a later section scrolled
the only pointer-driven way out of settings off the top. It stays sticky at
`lg` now, with an opaque panel-surface fill so content passes behind it.
Also replaces the IntersectionObserver scroll-spy with a geometry read on
scroll. An observer callback receives only the entries whose intersection
changed in that batch, so "topmost visible entry" was the topmost of a partial
set — which is why selecting the last rail item highlighted its neighbour. A
rail click now pins its own selection until the reader scrolls, and the spy is
gated behind the `lg` media query so phone scrolling pays nothing for it.
Verified in Chromium at 1440x900, 1280x720, 1024x640 and under reduced motion:
the grid never clips, the content column scrolls, and all eight rail items land
their heading below the sticky bar with the rail and close control in-panel and
hit-testable throughout. The new ui-smoke journey fails against the previous
`lg:h-auto` layout and passes on this one.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013vs5TgqziktquHWaseoRp2
@supabase

supabaseBot commented Aug 6, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project sjrfecxgysukkwxsowpy because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@coderabbitai

coderabbitaiBot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in:59 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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 58f4edcf-9c8f-473f-ba9a-0994c6cf023c

📥 Commits

Reviewing files that changed from the base of the PR and between 91dbf2c and eb51d49.

📒 Files selected for processing (3)
  • docs/branch-review-ledger.md
  • src/components/clinical-dashboard/settings-dialog.tsx
  • tests/ui-smoke.spec.ts
📝 Walkthrough

Walkthrough

The settings dialog now uses a definite desktop content height, sticky title bar, geometry-based scroll tracking, direct container navigation, reduced-motion handling, and rail-click pinning. A desktop smoke test covers scrolling, navigation, controls, final-section selection, and reopening behavior.

Changes

Desktop settings scrolling

Layer / File(s)Summary
Desktop scroll container and sticky header
src/components/clinical-dashboard/settings-dialog.tsx
The desktop layout now provides a bounded scrolling column. The title bar remains sticky with an opaque background.
Scroll-spy and rail navigation
src/components/clinical-dashboard/settings-dialog.tsx
Section tracking uses geometry and sticky-header offsets. Rail navigation scrolls the container with clamped offsets, reduced-motion handling, and temporary pinning.
Desktop scrolling regression coverage
tests/ui-smoke.spec.ts
The smoke test verifies overflow ownership, reachable controls, scroll-driven selection, final-section selection, and reset behavior after reopening.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
participant User
participant SettingsDialog
participant SettingsContainer
User->>SettingsDialog: click navigation rail section
SettingsDialog->>SettingsContainer: scroll to compensated offset
SettingsContainer->>SettingsDialog: report scroll position
SettingsDialog->>User: update active section
Loading

Possibly related PRs

  • BigSimmo/Database#958: Both changes modify SettingsDialog scroll handling and sticky-header behavior.
  • BigSimmo/Database#1174: Both changes modify settings navigation behavior and related UI smoke coverage.
  • BigSimmo/Database#846: Both changes modify tests/ui-smoke.spec.ts, although they cover different dashboard behaviors.

Suggested labels:codex

Suggested reviewers:claude, cursoragent, copilot

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly identifies the desktop settings scrolling and rail accessibility fix.
Description check✅ PassedThe description covers the change, verification results, risks, rollback, production effects, and explains why the required local gate was not run.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands.

@BigSimmo
BigSimmo marked this pull request as ready for review August 6, 2026 13:01
devin-ai-integration[bot]

This comment was marked as resolved.

sentry[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

@BigSimmo

Copy link
Copy Markdown
OwnerAuthor

@codex resolve actionable Codex review findings for this pull request and current head using the repository instructions. This is the pull request's single automatic repair pass: do not perform a fresh review, create new standalone findings, or request another review. Work only the existing unresolved Codex threads on the current head. The only repository destination is BigSimmo/Database, and the only branch destination is the pull request head branch claude/settings-nav-freeze-desktop-tdzh7z at starting commit 9e6255f; never publish fixes to a detached or synthetic work branch and never create a stacked pull request. Use the authenticated GitHub connector to commit each approved fix to BigSimmo/Database:claude/settings-nav-freeze-desktop-tdzh7z, then verify that the pull request head contains the published commit before reporting success. Always fix P0 and P1 findings. For P2 and lower findings, fix only clear, scoped, low-risk issues; otherwise disposition them with a concise reason. For a fixed thread, reply with as the first line and as the second line. For a no-code disposition, use followed by . These result markers authorize the workflow to close that exact thread only after it verifies a fixed commit is the pull request head; a local-only commit is not a fix. If publication or verification fails, do not use either result marker, do not claim success, and leave the thread open with the blocker. If human input or new authorization is required, do the same. Finish only after every actionable thread is fixed or dispositioned and closed, or explicitly left open for a human decision. Do not update the branch from main, address unrelated reviews, broaden scope, or create more than one scoped fix commit. Do not use external APIs, paid services, credentials, dependency changes, or broad refactors unless explicitly authorized. Add targeted tests where behavior changes and run the narrowest relevant validation.

BigSimmoand others added 3 commits August 6, 2026 21:17
A rail click pinned its section so the geometry scroll-spy could not
override it — the last sections are shorter than the scroll port and can
never reach the marker line. But the pin was only released by wheel,
touchmove and keydown on the scroll port.
Dragging the native scrollbar changes scrollTop and emits `scroll` alone,
so the pin survived and the rail kept pointing at the clicked section
while the reader was somewhere else. That is the one interaction this
branch just gave the dialog back.
Release on position instead: hold while the scroll closes the gap to the
clicked section, hold once it arrives, and let go as soon as the position
moves again. Input-agnostic, so scrollbar drags, wheel, touch, keyboard
and an interrupted animation all behave the same, and the over-broad
keydown release (which fired on Tab mid-animation) is gone with it.
Also clear the pin when the dialog opens: the Sheet unmounts its children
while closed, so the port returns at offset 0, but this component stays
mounted and a stale pin would hold the spy inert on the fresh surface.
Covers a scrollbar-drag journey and a close/reopen journey in ui-smoke.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013vs5TgqziktquHWaseoRp2
devin-ai-integration[bot]

This comment was marked as resolved.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🎉

Reviewed commit:9e6255f803

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

@BigSimmo
BigSimmo enabled auto-merge (squash) August 6, 2026 13:58

@coderabbitaicoderabbitaiBot 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.

🧹 Nitpick comments (2)
src/components/clinical-dashboard/settings-dialog.tsx (1)

378-385: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider moving the panel height to a @theme token.

The definite height fixes the overflow chain correctly. The value min(88dvh,840px) is a hardcoded design constant in a component class. If another dialog needs the same bounded panel height, define the value once as a @theme token in src/app/globals.css and reference it here.

♻️ Sketch
/* src/app/globals.css */
`@theme` {
--size-dialog-panel:min(88dvh,840px);
}
- <div className="relative grid h-full max-h-full min-h-0 overflow-hidden lg:h-[min(88dvh,840px)] lg:grid-cols-[248px_minmax(0,1fr)]">+ <div className="relative grid h-full max-h-full min-h-0 overflow-hidden lg:h-(--size-dialog-panel) lg:grid-cols-[248px_minmax(0,1fr)]">

As per coding guidelines: "Use Tailwind 4 @theme tokens in src/app/globals.css and the repository's intentionally unlayered component CSS rather than introducing hardcoded design values."

🤖 Prompt for 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.
In `@src/components/clinical-dashboard/settings-dialog.tsx` around lines 378 -
385, Define the shared bounded dialog height as a Tailwind 4 `@theme` token in
globals.css, then replace the hardcoded lg:h-[min(88dvh,840px)] value on the
settings dialog panel with the corresponding theme-token utility. Preserve the
existing responsive height behavior and overflow layout.

Source: Coding guidelines

tests/ui-smoke.spec.ts (1)

1389-1400: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Replace the Tailwind class selector with a stable test hook.

closest("[class*='overflow-y-auto']") couples the test to a utility class string. If the class list changes, closest returns null, portScrollable becomes false, and the failure points at scrolling instead of at the selector. The same expression is repeated three times.

Add a data-testid to the scroll container in settings-dialog.tsx and resolve the port once.

♻️ Proposed change
 <div
ref={scrollRef}
onScroll={handleScroll}
+ data-testid="settings-scroll-port"
className="relative min-h-0 w-full overflow-y-auto scroll-smooth bg-[color:var(--background)] polished-scroll lg:bg-transparent lg:px-7"
>
+ const port = settings.getByTestId("settings-scroll-port");+
const scrollState = async () =>
- settings- .locator("[data-settings-section]")- .first()- .evaluate((section) => {- const port = section.closest<HTMLElement>("[class*='overflow-y-auto']");- const panel = port?.parentElement;+ port.evaluate((element) => {+ const panel = element.parentElement;
return {
- portScrollable: port ? port.scrollHeight > port.clientHeight : false,+ portScrollable: element.scrollHeight > element.clientHeight,
panelClipped: panel ? panel.scrollHeight > panel.clientHeight : true,
};
});
- await settings- .locator("[data-settings-section]")- .first()- .evaluate((section) => {- const port = section.closest<HTMLElement>("[class*='overflow-y-auto']");- if (port) port.scrollTop = 0;- });+ await port.evaluate((element) => {+ element.scrollTop = 0;+ });

Also applies to: 1427-1433, 1446-1452

🤖 Prompt for 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.
In `@tests/ui-smoke.spec.ts` around lines 1389 - 1400, Replace the repeated
Tailwind-based closest("[class*='overflow-y-auto']") lookups in scrollState and
the related checks with a stable data-testid on the scroll container rendered by
settings-dialog.tsx. Resolve the container once and reuse it for portScrollable,
panelClipped, and the corresponding assertions.
🤖 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.
Nitpick comments:
In `@src/components/clinical-dashboard/settings-dialog.tsx`:
- Around line 378-385: Define the shared bounded dialog height as a Tailwind 4
`@theme` token in globals.css, then replace the hardcoded lg:h-[min(88dvh,840px)]
value on the settings dialog panel with the corresponding theme-token utility.
Preserve the existing responsive height behavior and overflow layout.
In `@tests/ui-smoke.spec.ts`:
- Around line 1389-1400: Replace the repeated Tailwind-based
closest("[class*='overflow-y-auto']") lookups in scrollState and the related
checks with a stable data-testid on the scroll container rendered by
settings-dialog.tsx. Resolve the container once and reuse it for portScrollable,
panelClipped, and the corresponding assertions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a0d101e0-b302-4e49-9d75-c0d32825d5b7

📥 Commits

Reviewing files that changed from the base of the PR and between d289abf and 91dbf2c.

📒 Files selected for processing (2)
  • src/components/clinical-dashboard/settings-dialog.tsx
  • tests/ui-smoke.spec.ts

@github-actions

Copy link
Copy Markdown
Contributor

CI triage

CI failed on this PR. Automated classification of the 2 failed job(s):

  • Unit coverageneeds investigation: inspect the failing step and uploaded diagnostics; rerun only after classifying the cause.
  • PR requiredneeds investigation: inspect the failing step and uploaded diagnostics; rerun only after classifying the cause.

Compared with main CI run #8427 (cancelled).

Classification is evidence routing, not permission to ignore a failure. Exact quarantined Playwright identities remain governed by the flake ledger.

cursoragentand others added 4 commits August 6, 2026 15:23
…g-review-fixes-db59
Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
Re-run the geometry spy on resize/content growth, align marker slack with
settle tolerance, and drop important utilities that overrode a11y header
fallbacks. Also stabilize the smoke journey with a scroll-port test id and
instant scrollTop writes under scroll-smooth.
Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
…op-tdzh7z' into claude/settings-nav-freeze-desktop-tdzh7z
Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
@BigSimmo

Copy link
Copy Markdown
OwnerAuthor

Review feedback disposition (PR #1641)

Pushed on a5d574fc23f08cab4cfaab548733d11be5812ff5 after merging latest main.

Fixed

FindingDisposition
Scroll-spy stale after resize / section growth (Devin)ResizeObserver + resize + rail MQL change while open
Sticky header ! overrides a11y fallbacks + wrong “unlayered” commentRemoved !; corrected comment (.edge-glass-header is @layer components)
Focus scroll under sticky bar / dead scroll-mt-4Raised section + action-row scroll-mt to clear the sticky title bar
Marker slack (1px) vs settle tolerance (2px)Marker uses scrollSettleTolerance
End-of-runway used SETTINGS_SECTIONS last idDerived from last rendered [data-settings-section]
matchMedia on every scrollMemoised MediaQueryList
Unthrottled spy geometry readsCoalesced into requestAnimationFrame (same pattern as useHideOnScroll)
behavior: \"instant\" reduced-motion riskWrite scrollTop under inline scroll-behavior: auto
E2E [class*='overflow-y-auto'] selectordata-testid=\"settings-scroll-port\"
E2E scrollTop under scroll-smoothTemporary scroll-behavior: auto around the assignment

Not changed (with reason)

FindingReason
Media query 1024px vs Tailwind 64remRepo desktop seam is already 1024px in globals.css / JS helpers; 64rem would diverge from that CSS
Definite lg:h-[min(88dvh,840px)] empty space if content shrinksDeliberate: h-auto+max-h was the original unscrollable defect; keep the definite height
Theme-token for panel height (CodeRabbit)One call site; extracting a shared token is premature
Pin can stick if smooth scroll interrupted mid-flightTransient/recoverable by any distance-increasing scroll; clamp-on-resize already covers unreachable targets
Sheet-cap height noteInformational confirmation, no code change

CI

Latest Unit coverage failure on this PR was npmECONNRESET during install (network flake), not a product assertion. This push re-triggers CI.

Bugbot

No cursor[bot] Bugbot threads were present on this PR to triage.

BigSimmoand others added 2 commits August 6, 2026 23:52
A coalesced rAF can run after a rail click arms the pin but before
smooth-scroll moves scrollTop. Strict distance < pin.distance treated
that no-progress frame as reader takeover and dropped the pin, so a
rapid second click (or an in-flight scroll listener) lost the hold
before the animation started. Use <= so equal distance still counts
as the click's own scroll.
Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
@BigSimmo

Copy link
Copy Markdown
OwnerAuthor

Review + CI follow-up (e7f380f3)

Review threads

All prior Devin / Sentry / Codex review threads were already fixed and resolved on this head (position-driven pin release, open/close pin clear, resize/ResizeObserver re-spy). No unresolved threads remain.

Bugbot (cursor[bot])

No Cursor Bugbot comments on this PR. An independent pass of the scroll/pin path did find one remaining P2:

  • Rail pin dropped before smooth-scroll starts — a coalesced requestAnimationFrame can run after a rail click arms the pin but before scrollTop moves. Strict distance < pin.distance treated that equal-distance frame as reader takeover. Fixed in e7f380f3 by using distance <= pin.distance.

CodeRabbit nits

  • data-testid="settings-scroll-port" — already landed in an earlier commit; smoke journey uses it.
  • @theme token for min(88dvh, 840px) — declining for now. The height is settings-dialog-specific (not shared), and extracting a one-off token adds globals surface without a second consumer. Happy to revisit if another dialog needs the same bound.

CI failures on the listed checks

Inspected the failing jobs on a5d574fc / 91dbf2cb. They are not product regressions:

CheckCause
Static PR checks / Safety and config / Production UI (1–3) / Visual baselinesGitHub Actions Failed to resolve action download infoService Unavailable / Bad Gateway before the job body ran
Unit coverage (91dbf2cb)npm error network read ECONNRESET during install

Earlier CI on 5d664b1 was green for the same settings surface. This push re-triggers CI on e7f380f3.

@BigSimmo
BigSimmo disabled auto-merge August 6, 2026 16:02
When content or viewport growth made a pinned section offset unreachable,
the ResizeObserver path clamped the offset but left the pre-clamp distance
in place. The next scroll frame then saw a larger gap than pin.distance and
treated it as reader takeover, dropping the highlight mid-animation. Reset
distance (and settled) from the live scrollTop whenever the offset is
clamped.
Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
@BigSimmo

Copy link
Copy Markdown
OwnerAuthor

Babysit follow-up (d4ecd2b5)

Review threads

All prior Devin / Sentry / Codex threads remain resolved on this head. No unresolved threads; no Bugbot comments.

Code fix this pass

ResizeObserver pin clamp dropped the rail hold mid-animation. When content/viewport growth made a pinned section offset unreachable, the observer clamped pin.offset but left the pre-clamp pin.distance. The next scroll frame then saw a larger gap than pin.distance and treated it as reader takeover. Fixed in d4ecd2b5 by resetting distance and settled from the live scrollTop whenever the offset is clamped.

CI

Prior reds on this PR were infra, not product:

  • Unit coverage (91dbf2cb): npm error network read ECONNRESET during install
  • Latest head before this push (e7f380f3): jobs cancelled at “Set up job” / stuck in a repo-wide Actions queue

This push re-triggers required checks on d4ecd2b5. Branch is current with main (0 behind), merge tree clean.

Local: typecheck + lint on the changed file — clean. No provider-backed gates run.

cursoragentand others added 2 commits August 6, 2026 16:52
Capture the babysit outcome at d4ecd2b: review threads clear, pin-clamp
fix pushed, hosted CI blocked on the GitHub Actions outage rather than
a product failure.
Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
Point the sweep record at c31827e so ledger:lookup matches HEAD.
Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>

@devin-ai-integrationdevin-ai-integrationBot 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.

Devin Review found 1 new potential issue.

View 1 additional finding in Devin Review.

Open in Devin Review

Comment threaddocs/branch-review-ledger.md
Cancel the coalesced spy rAF when the dialog closes, and ignore a frame
whose port is no longer the live scroll ref. Sheet unmounts children on
close while SettingsDialog stays mounted, so a frame armed mid-scroll
could overwrite the reopen Account reset. Smoke journey now asserts
Account is current immediately after reopen.
Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
@cursor

cursorBot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Babysit / review-and-fix summary (PR #1641)

Tip:6adc0f0e386fb29f6531ed86bb481cb6b0bea103 on claude/settings-nav-freeze-desktop-tdzh7z
Base:origin/main @ c839b97d0 behind / 17 ahead
Merge-tree vs origin/main: clean (git merge-tree --write-tree exit 0) — behind-but-clean N/A; no sync needed
Mergeable:MERGEABLE / BLOCKED (required checks not green yet)
Auto-merge: not armed
Unresolved actionable threads: 0

Fixed

FindingDisposition
Bugbot — stale scroll-spy requestAnimationFrame after close can overwrite reopen activeSection reset (settings-dialog.tsx spy path)Fixed in 6adc0f0e: cancel coalesced spy frame on open flip; ignore frames whose port is not the live scrollRef / not connected. Smoke journey asserts Account is current immediately after reopen.
Devin — ledger-only tip + stacked Run PR sweep rows without (supersedes …) scopeDispositioned + resolved: cannot rewind c31827e6/8418b488 without force-push. Product tip 6adc0f0e ends the ledger-only tip. Local (unpushed) ledger rows record PR #1641 review-and-fix and Run PR sweep… (supersedes 2026-08-06) — left unpushed to avoid another ledger-only tip.

Review notes

  • Deep review + Bugbot on the settings scroll/pin/spy delta. No protected RAG/ranking surfaces touched.
  • Prior Devin/Sentry/Codex threads were already resolved on earlier tips; no new P0/P1 beyond the Bugbot rAF issue.
  • CodeRabbit theme-token nit ignored (not a defect).

Required CI

GitHub Actions = major_outage. Checks on this tip are queued/pending (PR policy, mergeability, Semgrep, Gitleaks, CI) — not green, and missing checks while dirty/outaged are not treated as green. No product CI failure to fix locally. No re-run attempted during outage (would not clear the blocker).

Local gates (decisive lines)

  • verify:cheapTest Files 515 passed (515) / Tests 5446 passed | 1 skipped (5447)
  • npx prettier --check (touched files) → All matched files use Prettier code style!
  • Chromium journey → 1 passed (3.9s)desktop settings scrolls its own column…
  • ALLOW_BUILD_WITH_DEV_SERVER=1 npm run buildClient bundle secret surface check passed.
  • npm run check:rag:fixturesOffline RAG fixture and manifest validation passed (36 golden cases, 23 suites).
  • verify:pr-local first pass hit unrelated design-system-adoption 30s timeout; retry npx vitest run tests/design-system-adoption.test.tsTests 51 passed (51)

Residual risks

  • Hosted required CI still blocked on Actions recovery; re-check when Actions is operational.
  • Local review-ledger appends for this tip remain unpushed (policy: no ledger-only tip).
  • Phone/desktop settings path under reduced-motion / forced-colors not re-swept beyond prior Chromium proof on this PR.

Merge left to you.

@BigSimmo
BigSimmo enabled auto-merge (squash) August 6, 2026 17:25
@BigSimmo
BigSimmo merged commit 7d3e626 into mainAug 7, 2026
25 checks passed
@BigSimmo
BigSimmo deleted the claude/settings-nav-freeze-desktop-tdzh7z branch August 7, 2026 01:09
cursorBot pushed a commit that referenced this pull request Aug 7, 2026
Record squash-merge content verification after the settings
nav freeze PR landed on main.
Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
Sign up for freeto 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.

3 participants

@BigSimmo@claude@cursoragent