fix(web): prevent shelf rows from overlapping headers#25412

closed9
main
fix/issue-25383
opened9d agobyJJulius Marminge
View on GitHubTwo-way sync enabledPR last imported 5d ago

Expanding or collapsing a bottom-anchored sidebar shelf (Working, Snoozed, Settled) slid thread rows across the shelf headers below it. This PR turns row motion off for the layout pass in which shelf expansion changes. The original description is kept unchanged below; its evidence was replaced by this round's captures because it came from a modified "before" tree and a source-edited Working predicate.

Fixes pingdotgg/t3code#25383

Takeover

No code changes; evidence and verification added below. The earlier evidence was rebuilt on complete before/after trees (merge-base 1316d48b0c, head f4bbe57480), with real running Codex turns for Working and no source edits. That rebuild surfaced the Loading→Settled overlap described at the top of Evidence.

Evidence

Not covered by the title: the fix introduces a Loading→Settled text overlap. On every Settled expansion (cold and warm; Settled re-fetches each time), the toggle pass now suppresses motion, but the next pass, where rows replace the Loading placeholder, keeps the same shelf key and animates. The Loading row exits as a fading clone (Sidebar.motion.ts:173) on top of the incoming first row (:194) for about 0.1–0.15 s. Before has no equivalent overlap in the same expansion (header and rows slide together with a 1 px gap). Reproduced independently on both complete builds (R08, R11).

Reported overlap, R01 1440 dark, toggle +60 ms: Before | After | Diff0 | Diff16Introduced overlap, R11 cold, rows replace Loading +60 ms: Before | After | Diff0 | Diff16
R01 D-K sheetR11 cold replacement sheet
Before 1316d48b0c · After f4bbe57480Before 1316d48b0c · After f4bbe57480

Before 1316d48b0c → after f4bbe57480. Complete clean trees; full index, recordings, action timestamps and provenance. Passed round-1 rows retained; R08/R11/R13/R18 recaptured. No PR edit performed. Linux x86_64 fs-39; headless Chromium 148.0.7778.96 / Playwright-core 1.60.0; DPR 1; font scaling 100%; locale en-US; timezone UTC. All round-2 captures: native 1440×900 CSS pixels, sidebar 256, dark, scroll 0, frozen Date 2026-10-27T15:13:00Z. Original mobile-web captures retain 390×900/drawer 354 and their stated theme; no device emulation. Synthetic data via app/RPC; real running Codex turns; T3CODE_DEV_SEED=0; no source edits/live data. Native whole-surface captures; crops supplemental. WAAPI pause/seek60ms for motion; R18 actual reduce before reload. Cold/warm older-page requests and both checkpoint milestones are explicit in the index. Δ0 = absolute RGB delta ×16; Δ16 = max-channel >16/255. B/A/Δ0/Δ16 below link every full-viewport original/diff.

R01 — complete 40-pair proof. D=1440×900, M=390×900 mobile web; K=dark, L=light. Initial/final endpoints exactly equal. Four neighbor-bit states × expand/collapse, all four variants.

PhaseD-K B/A/Δ0/Δ16D-L B/A/Δ0/Δ16M-K B/A/Δ0/Δ16M-L B/A/Δ0/Δ16
initialB A Δ0 Δ16B A Δ0 Δ16B A Δ0 Δ16B A Δ0 Δ16
00-expand-60msB A Δ0 Δ16B A Δ0 Δ16B A Δ0 Δ16B A Δ0 Δ16
00-collapse-60msB A Δ0 Δ16B A Δ0 Δ16B A Δ0 Δ16B A Δ0 Δ16
01-expand-60msB A Δ0 Δ16B A Δ0 Δ16B A Δ0 Δ16B A Δ0 Δ16
01-collapse-60msB A Δ0 Δ16B A Δ0 Δ16B A Δ0 Δ16B A Δ0 Δ16
10-expand-60msB A Δ0 Δ16B A Δ0 Δ16B A Δ0 Δ16B A Δ0 Δ16
10-collapse-60msB A Δ0 Δ16B A Δ0 Δ16B A Δ0 Δ16B A Δ0 Δ16
11-expand-60msB A Δ0 Δ16B A Δ0 Δ16B A Δ0 Δ16B A Δ0 Δ16
11-collapse-60msB A Δ0 Δ16B A Δ0 Δ16B A Δ0 Δ16B A Δ0 Δ16
finalB A Δ0 Δ16B A Δ0 Δ16B A Δ0 Δ16B A Δ0 Δ16

R14 — ordinary moves still animate after a toggle, D-K. 15 phase pairs (pin/unpin, settle/unsettle, snooze/unsnooze, archive/unarchive, delete, real run start/stop), each with zero differing pixels at both thresholds between before and after; every B/A/Δ0/Δ16 link is in the index.

Other rows — audited passed sheets retained; replacements shown explicitly.

RowEvidence / result
R02R02-D-L-classic-T-sheet-1 · R02-D-L-classic-W-sheet-1 · R02-D-L-classic-Z-sheet-1
R03R03-D-K-sheet-1 · R03-D-K-sheet-2 · R03-D-K-sheet-3
R04R04-D-K-sheet-3 · R04-D-K-sheet-1 · R04-D-K-sheet-2
R05R05-D-K-sheet-1
R06R06-D-K-sheet-1
R07R07-D-K-sheet-1
R08cold Loading · cold rows · warm Loading · warm rows · R08-D-K-sheet-1 · R08-D-K-sheet-2 · R08-D-K-sheet-3 · R08-D-K-sheet-4 · R08-D-K-sheet-5 · R08-D-K-sheet-2 · R08-D-K-sheet-3 · R08-D-K-sheet-1 · R08-D-K-sheet-4. After Loading clone overlaps rows in both preconditions; before has moving/fading rows without that clone. Normal and replay clips in index.
R09R09-D-K-sheet-1 · R09-D-K-sheet-2
R10R10-D-K-sheet-1 · R10-D-K-sheet-2
R11cold Loading · cold rows · warm Loading · warm rows · T0 and T2 collapse retained in full index. After Loading clone overlaps rows in both preconditions; before has moving/fading rows without that clone. Normal and replay clips in index.
R12R12-D-K-sheet-1 · R12-D-K-sheet-2
R13R13-D-K-sheet-1 · R13-D-K-sheet-2 · R13-D-K-sheet-3 · new before video · new after video. Both native1440×900, same nine actions.
R17R17-M-K-sheet-1 · R17-M-K-sheet-2 · R17-D-K-sheet-1 · R17-D-K-sheet-2
R18working-expand · snoozed-expand · settled-expand · ordinary-pin · before video · after video. Actual reduce, warm Settled rows; all seven endpoint pairs Δ0=Δ16=0.

What else changed

  • Intended: R01 retains all four neighbor-bit states, both directions, desktop/mobile web and light/dark; before shelf-toggle transforms/fades/exit clones disappear after, and settled endpoints match. Chevron transitions remain.
  • R13 also cancels an ordinary in-flight move when a shelf toggle happens. Later ordinary moves still animate in R14; its full control proof is retained.
  • All 29 new paired comparisons have zero outside-sidebar differences at Diff0 and Diff16. The inherited audit identified two pairs with eight outside-sidebar antialias pixels at Diff0 and zero at Diff16. R16 remains uncredited because of scroll/raster/hover mismatch.

Unverified inventory: R15 not captured; R16 partial; R19–R22 and R24 inventory only, not captured; R23 cannot-verify macos. Rebuild drivers, raw captures and request/geometry logs: round-1 drivers · recapture drivers and raw captures · fixture matrix.

Verification log
  • Mode: evidence-only (no code changes). Head f4bbe57480, merge-base 1316d48b0c.
  • Proof audit pass 1: 24-row fixture matrix, plan SUFFICIENT.
  • Capture round 1: 15/24 rows full, R08/R16 partial. Proof audit pass 2: INSUFFICIENT on R08, R11 (did not reproduce; independent rebuild showed the Loading clone), R13 (downscaled recording), R18 (Settled stuck on Loading), wrong Chromium version.
  • Capture round 2: R08, R11 (cold and warm), R13 (native 1440×900), R18 recaptured; provenance corrected. Proof audit pass 2b: R08, R11, R13 SUFFICIENT; R18 caption mislabel ("None collapse" → pin Active planning) corrected in the index without recapture. Pass 2b independently confirmed the Loading→Settled overlap is introduced relative to before.
  • Unverified: R15, R16 (partial), R19–R22, R24 not captured (inventory paths the diff does not change); R23 cannot-verify macos.
  • Tests: none added or changed (visual motion fix; before/after captures are the proof).
  • CI at f4bbe57480: Build and Typecheck/Check suite green; no failing checks.
  • Audit reports: pass 2 · pass 2b.
  • Lead: Claude Opus 5.5 through Claude Code in T3 Code; audits and captures: GPT-6.1-Sol (high) through Codex.
Original description by @juliusmarminge

Problem

Expanding or collapsing a bottom-anchored shelf moves thread rows across the Snoozed and Settled headers. With two Working threads, toggling Working briefly superimposes thread text and section labels.

Change

Reset list motion when the Working, Snoozed, or Settled expansion state changes. The layout effect cancels active row motion and records the new positions before paint. Chevron transitions and ordinary thread-move animations remain available. This applies the focused shelf-toggle reset proposed in #25384.

Scope and approval

Fixes https://github.com/pingdotgg/t3code/issues/25383

Triaged bug via the automated issue flow; GitCafe issue #25383. Shared web/desktop sidebar only; native mobile uses a separate implementation. No contracts, provider behavior, or stored settings change.

Verification

  • vp run check:changed: format, lint, both knip passes, affected typechecks, and 108 focused tests passed. No new test for this purely visual defect.
  • Integrated Chromium web pass, isolated --home-dir: Working, Snoozed, Settled, and repeated Working reversals produced zero row animations and zero exit clones after the toggles.
  • Two independent source reviews found no concrete code defect in the final diff. Final proof recheck: SUFFICIENT; corrected keyboard recordings and manifest verified.
  • CI at the current head: 27 successful checks, zero pending or failing.
  • Evidence index and all 11 recording pairs, fixture matrix, and artifact manifest including fixture/driver scripts.
  • Recordings cover all three shelf toggles, rapid reversals, selected retained rows, ordinary thread moves before/after interruption, missing Snoozed anchor, Working disabled, keyboard Enter/Space, both designs, light/dark, and 1440/390 CSS widths.
  • Linux Chromium, DPR 2, 1440×900 / 390×900, frozen fixture clock. Desktop screenshots are downsampled to 1280×800 by the capture tool; portrait images are 780×1800. Synthetic projection fixture used equally before/after; temporary fixture source was restored and no evidence scripts are committed.
  • Reduced-motion recordings exercise the JavaScript preference branch only; native CSS media emulation was unavailable and remains unverified. Older Settled pagination was not separately exercised.

Evidence

Before 1316d48b0c; after f4bbe57480. Before interaction recording / After interaction recording. Videos include timestamped interaction captions; the index contains every matrix row and the complete comparison table.

Four of five settled pairs are pixel-identical; the 1440-light pair has 60 differing pixels. Five hover-contaminated motion checkpoint pairs were excluded from the delivered proof; uninterrupted interaction recordings establish the transition behavior. The corrected desktop keyboard clips visibly cover Enter and Space on all three shelves. Relative-time labels differ in those qualitative clips after hot reload; no recording pixel-equality claim is made.

  • macOS Electron has not been run separately; it uses this shared web sidebar. Native mobile is unaffected.

GPT-6.1-Sol through the Codex harness; Claude Opus 5.5 through T3 Code assisted reproduction and independent review.

Checks

24 successful · 15 skipped
All checks passed
f4bbe57

Added 5 commits

  • Merge remote-tracking branch 'origin/main' into fix/issue-2538306afea0
  • fix(web): shelf rows are clipped at section boundaries while they animate5fc1013
  • fix(web): settle interrupted shelf animations before clipping goes stale6035a33
  • fix(web): retarget active shelf clips onto the new layout instead of settling themb83739e
  • fix(web): revealed shelf rows no longer slide across the headers belowce73664
Synced from GitHub

Approvability

Verdict: Approved at f4bbe57

Macroscope's review found this PR approvable — This is a focused visual bug fix in the sidebar that resets row motion when shelf expansion changes, preventing rows from crossing stationary headers while preserving normal list animations. It is limited to one existing component and introduces no schema, security, billing, deployment, default, or static-analysis changes.

You can add or adjust custom eligibility rules. Learn more.

Synced from GitHub

Review in Change Stack →

No actionable comments were generated in the recent review.

External image Recent review info
External image Run configuration
External image Commits

Reviewing files that changed from the base of the PR and between b83739eb0bf9665de4bba8da26d18b6d236c5f4f and f4bbe57480e4498d279fce1dcf99b1aece1100b5.

External image Files selected for processing: 1
  • apps/web/src/components/Sidebar.tsx

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 6 remain after this review.

Walkthrough

The sidebar layout effect now checks whether the Working, Snoozed, or Settled shelf expansion state changed. It enables list-motion animation only when the state is unchanged and animation is allowed.

Changes: Sidebar shelf motion

Layer / File(s)Summary
Shelf expansion motion update
apps/web/src/components/Sidebar.tsx
The layout effect compares the combined shelf expansion state with its previous value. It enables list-motion animation only when that state is unchanged and listMotionPass.animate is true.

Priority: External image Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix · Severity of issue fixed: Low

Possibly related PRs

Suggested reviewers: macroscopeapp, maria-rcks

Merge Risk: External image Minimal · up to f4bbe

The shelf transition fix has no remaining merge-blocking risk identified.

Pre-merge checks: Passed 4
Check nameStatusExplanation
Title checkPassedThe title is concise, specific, and accurately describes the main change: preventing shelf rows from overlapping headers during sidebar shelf transitions.
Linked Issues checkPassedIssue #25383 requires thread rows and the Snoozed and Settled headers to remain separate during shelf expansion and collapse. Sidebar.tsx now tracks all three shelf states and calls `listMotionRef.c…
Out of Scope Changes checkPassedThe whole-PR diff changes only apps/web/src/components/Sidebar.tsx. The change supports issue #25383 by resetting shared sidebar row motion when Working, Snoozed, or Settled expansion changes. No un…
Description checkPassedThe description is complete and follows the required template. It documents the problem, change, linked issue and scope, verification results, screenshots, recordings, known limitations, and the agent…
Full details: Linked Issues check

Explanation

Issue #25383 requires thread rows and the Snoozed and Settled headers to remain separate during shelf expansion and collapse. Sidebar.tsx now tracks all three shelf states and calls listMotionRef.current?.update(false) when a shelf state changes. This cancels row motion and records positions before paint. The reported verification covers Working, Snoozed, Settled, rapid reversals, and selected rows. The change preserves ordinary thread-move animation when shelf states are unchanged.

Full details: Out of Scope Changes check

Explanation

The whole-PR diff changes only apps/web/src/components/Sidebar.tsx. The change supports issue #25383 by resetting shared sidebar row motion when Working, Snoozed, or Settled expansion changes. No unrelated product behavior, contracts, provider behavior, stored settings, or native mobile implementation changes.

Full details: Description check

Explanation

The description is complete and follows the required template. It documents the problem, change, linked issue and scope, verification results, screenshots, recordings, known limitations, and the agents used. It also clearly reports a newly identified Loading-to-Settled overlap instead of omitting the regression.

Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

Added 4 commits

  • fix(web): retain exit clipping until clones are removed77c8e0a
  • Merge remote-tracking branch 'origin/main' into fix/issue-253839b5484d
  • fix(web): discard fades when clipping neighbors disappearb633238
  • fix(web): reset row motion when shelves togglef4bbe57