fix(app): right-panel titlebar seam — alignment, stable toggle, drag-resize sync#887
Conversation
…macOS PR #880 declared the seam fixed ("panel border-l flush with titlebar slot border-l, seam continuous") but a post-release regression report showed the two border-l's are offset by 8px on macOS only. ## Root cause `[data-component="titlebar-shell"][data-shell-os="macos"]` in `packages/app/src/index.css` carried a symmetric `padding-inline: 8px`, which pushed the titlebar's right rail (and therefore the tabs slot pinned to its right edge) 8px inboard of the viewport. The right-panel `<aside>` directly below sits in a sibling layout tree without that padding, so its body's `border-left` reached the actual viewport edge — the two `border-l`s ended up 8px apart, breaking the seam. The titlebar comment in PR #880 explicitly assumed the right rail's outer edge = viewport.right ("`pr-2` lives on `#pawwork-titlebar-right` … so it reads as 'toggle inset from viewport edge' when closed and 'gap between toggle and tabs border-l' when open"). That assumption was true on Windows (no equivalent padding rule) but false on macOS. Snap target `right-panel-titlebar` was supposed to catch this — its own comment names the seam as the contract — but the 8px x-axis offset slipped through visual review. ## Fix `padding-inline: 8px` → `padding-inline-start: 8px`. The toggle's existing `pr-2` provides the closed-state inset from the viewport edge (8px instead of 16px, which matches macOS native toolbar conventions for the rightmost control). ## Regression test Added `tabs slot border-l aligns horizontally with the right-panel body border-l` to `titlebar-right-rail-contract.spec.ts`. Asserts `|tabs.left - body.left| < 1px` (sub-pixel tolerance for Retina rounding). Confirmed red→green: with the old `padding-inline: 8px` the test fails with `Received: 8`; with the fix it passes. ## Verified - `bun run test:e2e:local titlebar-right-rail-contract.spec.ts` — 4/4 pass (including the new x-axis seam assertion) - `bun run snap right-panel-titlebar` — passes, grid PNG shows the light + dark seam continuous from titlebar top to viewport bottom - `bun run lint` — clean - `bun --cwd packages/app run typecheck` — clean Follow-up to PR #880, addresses the post-release regression report.
|
Warning Review limit reached
Your plan includes 1 review of capacity. Refill in 20 minutes and 23 seconds. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more review capacity refills, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than trial, open-source, and free plans. In all cases, review capacity refills continuously over time. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughAdjusts titlebar right-rail layout and macOS titlebar padding, mirrors right-panel drag-resize state to the desktop shell to disable transitions during resizing, and adds an E2E contract test that asserts the titlebar tabs and right-panel body left borders align within 1px. ChangesTitlebar-right-rail seam alignment
Sequence DiagramsequenceDiagram
participant Session as Session (E2E / User)
participant SessionSidePanel as SessionSidePanel
participant DesktopShell as Desktop Shell (DOM)
participant Titlebar as Titlebar component
participant CSS as Stylesheet
Session->>SessionSidePanel: start drag-resize right panel
SessionSidePanel->>DesktopShell: set data-resizing-right-panel attribute
DesktopShell->>CSS: transition disabled via attribute
SessionSidePanel->>Titlebar: layout unchanged during resize (no seam shift)
Titlebar->>Session: visual seam shows aligned left borders
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related issues
Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request fixes a visual alignment issue on macOS where the titlebar tabs slot was offset from the right-panel body due to symmetric padding. The fix involves changing the titlebar's padding to be start-only and adding a regression test to ensure horizontal alignment. Feedback was provided regarding the new E2E test, specifically recommending the use of expect.poll to prevent flakiness caused by CSS transitions when the panel opens.
Perf delta summaryComparator: pass
|
Two follow-ups to the macOS seam fix earlier in this PR, both surfaced once the alignment seam was visibly continuous and motion against it became readable: ## 1. Stable toggle position (replaces #880's slide) PR #880 made the right utility toggle an in-flow flex sibling of the titlebar tabs slot, so opening the right panel pushed the toggle left by `--right-panel-width` over the 240ms transition. The slide read as visually jarring once the seam was aligned and the motion became visible against the now-continuous border-l. Fix: `#pawwork-titlebar-right` is now absolute-positioned to the rail's top-right corner (`right-2` on macOS, `right-0` on Windows), so its x-coordinate stays at `viewport.right - 8px` regardless of panel state. The tabs slot is the only flex sibling and pins to the rail's right edge as before; the toggle visually overlaps the tabs slot's rightmost area but in practice doesn't collide — Kobalte's `Tabs.List` sizes to content (~150px for 1-4 chips), leaving the trailing `+` button in the middle of the panel column with empty space at the corner for the toggle to float in. Regression test `right utility toggle keeps the same x-position across open/close` in `titlebar-right-rail-contract.spec.ts`: asserts `|toggle.right(open) - toggle.right(closed)| < 12px`. Pre-fix this came back `Received: 229.25` (the full panel-width displacement); post-fix it's within sub-pixel rounding. ## 2. Drag-resize seam sync While dragging the panel resize handle, the body's `<aside>` inline `width` snapped to each pointermove (its transition is gated on the session-scope `props.size.active()`), but `--right-panel-width` on the desktop shell kept its 240ms cubic-bezier transition because the shell's gate uses `state.sizing` — a layout.tsx-local flag scoped to the sidebar resize handler only. The two sides of the seam moved at different speeds; one led, one trailed, visibly drifting apart. Fix: a small effect in `SessionSidePanel` mirrors `props.size.active()` to a `data-resizing-right-panel` attribute on `<desktop-shell>`. A CSS rule in `index.css` matches the attribute and disables the shell's transition while the user is dragging. This avoids plumbing session-scope sizing state into the global layout context — the bridge stays one attribute wide and one CSS rule shallow. The 120ms timeout in `createSizing` re-enables the transition shortly after the user lets go, so the open/close animation still uses the full 240ms cubic-bezier curve. ## Verified - `bun run test:e2e:local titlebar-right-rail-contract.spec.ts` — 5/5 pass (incl. the new x-position-stability assertion) - `bun run snap right-panel-titlebar` — passes; grid PNG shows toggle at corner in both states, seam continuous - `bun run lint` — clean - `bun --cwd packages/app run typecheck` — clean - Manual Electron verification (mac): toggle stable across open/close, drag-resize seam moves as one continuous line.
Three review items from the post-#887 review thread, all verified and fixed. ## P1 — tabs slot reserves explicit right-edge for absolute toggle Reviewer flagged: the right utility toggle is absolute-positioned over the rightmost area of the tabs slot, while the `+` add-tab button lives in the portalled Tabs.List. If `+` ever reaches the slot's right edge, clicks meant for it get intercepted by the toggle's hit-target. Verified: today the collision doesn't happen — Kobalte's Tabs.List renders at content-width (~150px for 1-4 chips, measured 192.5px in DEBUG dump) instead of the `width: 100%` declared in tabs.css, so the `+` sits ~180px clear of the toggle. But that's a CSS coincidence, not a contract. Any future change (consumer `w-full`, tabs.css refactor, Kobalte upgrade) makes the list fill the slot and collapses the safety margin. Fix: `#pawwork-titlebar-tabs` slot now declares `padding-right: 44px` (toggle 30 + viewport inset 8 + gap 4 + 2px buffer for Tabs.List internal `px-1`) when `tabsRailActive()`. Physical `padding-right` matches the physical `right-2`/`right-0` on the toggle so RTL stays consistent. The reserve makes the no-collision guarantee independent of Tabs.List's intrinsic sizing. Title comment block on the slot rewritten to explain the reserve instead of the CSS-coincidence it replaces. ## P2 — sizing effect no longer leaks data-resizing-right-panel Reviewer flagged: the effect that mirrors `props.size.active()` to `data-resizing-right-panel` early-returns on `!isDesktop()` without removing the attribute, and has no `onCleanup` to remove it on unmount. Because the CSS rule that reads the attribute uses `transition: none !important`, a leak would silently kill the shell's open/close transitions until the next page load. Three leak paths closed: 1. `isDesktop()` flips false mid-drag — effect now always `removeAttribute` first, then optionally re-sets only when desktop AND active. Early-return after the cleanup is safe. 2. Component unmounts with active=true (navigation away from session) — `onCleanup` always removes the attribute regardless of state. 3. Stale attribute from previous instance — same "always clear first" pattern handles this too. Did NOT take reviewer's "more optimal: use layout state instead of querySelector global DOM" because lifting session-scope sizing into the layout context expands this hotfix PR into a refactor; tracked separately (see future follow-up). ## P3 — tests now cover the user-visible "+" contract Reviewer flagged: the existing toggle-position test asserted only that the toggle exists, not that `+` is clickable; the test comment claimed "open/closed are two DOM nodes" but the actual implementation keeps one node and absolute-positions its container; `waitForTimeout(300)` is brittle. Fixed: - Toggle-position test comment rewritten — explicitly notes one DOM node, absolute-positioned, so pre/post toggle.right are pixel- identical (tolerance tightened from 12px to 1px). - `waitForTimeout(300)` replaced with `expect.poll` for toggle-right stability (two consecutive reads within sub-pixel). - New test `+ add-tab button stays clickable and does not close the panel`: clicks `+`, asserts panel stays open AND dropdown menu opens. Uses raw `page.locator` instead of `getByRole` for the toggle check because Kobalte's DropdownMenu marks the rest of the page `inert` while open and `getByRole` filters it. - New test `+ stays clickable even if Tabs.List is forced to fill the slot (collision guard)`: runtime-injects `[data-slot="tabs-list"] { width: 100% !important }` via `page.addStyleTag`, then asserts (a) plus.right < toggle.left geometrically and (b) the click still opens dropdown without closing panel. This is the fortification test that proves the P1 reserve actually protects against the future-CSS scenario. - New test `data-resizing-right-panel attribute does not leak when viewport drops below the desktop breakpoint mid-resize state`: manually sets the attribute, flips viewport below 768px, asserts attribute is removed. Pins the P2 cleanup contract. ## Verified - `bun run test:e2e:local titlebar-right-rail-contract.spec.ts` — 8/8 pass (3 original + 1 from earlier #887 work + 1 toggle-position + 2 "+" overlap + 1 P2 leak) - `bun run snap right-panel-titlebar` — grid PNG unchanged (padding-end reserve is invisible because content fits well within the available space) - `bun run lint` — clean - `bun --cwd packages/app run typecheck` — clean
|
Addressed all three review items; verification passes. P1 — accepted direction, hardened with codex-refined numberFact correction: the reviewer's premise that "Tabs.List is But the reviewer's "this is a fragile dependency" call is correct: today's no-collision is a CSS coincidence, and the moment Tabs.List becomes 100% it collides. Going with option A, but not the reviewer's 36px — after consulting codex, used 44px (toggle 30 + Added 2 e2e tests:
Not accepted: "drop the toggle stability fix, keep only the macOS padding fix" — that would walk back a product contract the user explicitly chose. P2 — real bug, fixed as reviewer describedAll three leak paths closed:
Added e2e: Not accepted: "better to use layout state than to write the DOM globally via querySelector" — that would lift session-scope P3 — all accepted
VerificationNew commit: 281ed42 |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
packages/app/e2e/session/titlebar-right-rail-contract.spec.ts (1)
302-305: ⚡ Quick winUse a data-attribute selector in injected CSS instead of an ID selector.
Switching this injected selector to a stable data attribute keeps the test aligned with the suite’s selector policy.
Selector update
- `#pawwork-titlebar-tabs` [data-slot="tabs-list"] { + [data-shell-slot="tabs-portal"] [data-slot="tabs-list"] { width: 100% !important; flex-grow: 1 !important; }As per coding guidelines, "Use
data-component,data-action, or semantic roles for selectors instead of CSS class names or IDs".🤖 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 `@packages/app/e2e/session/titlebar-right-rail-contract.spec.ts` around lines 302 - 305, Replace the injected ID selector in the CSS string passed to page.addStyleTag with a stable data-attribute selector; specifically, in the content string where it currently uses "`#pawwork-titlebar-tabs` [data-slot=\"tabs-list\"]", change the ID part to a data attribute such as "[data-component=\"pawwork-titlebar-tabs\"]" (or the project's canonical data attribute) so the selector becomes "[data-component=\"pawwork-titlebar-tabs\"] [data-slot=\"tabs-list\"]"; update the CSS string inside the page.addStyleTag call accordingly.
🤖 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 `@packages/app/e2e/session/titlebar-right-rail-contract.spec.ts`:
- Around line 194-201: Ensure the test normalizes the right-panel toggle state
before measuring "closed": query the toggle button (page.getByRole("button", {
name: "Right utility panel" })) and read its aria-expanded state, and if it is
"true" invoke the toggle (use the same click path or helper toggleRight()) to
force it closed, then call toggleRight() to sample the closed state; update the
test around toggleRight() to perform this precondition check and only then
assert expect(closed).not.toBeNull().
---
Nitpick comments:
In `@packages/app/e2e/session/titlebar-right-rail-contract.spec.ts`:
- Around line 302-305: Replace the injected ID selector in the CSS string passed
to page.addStyleTag with a stable data-attribute selector; specifically, in the
content string where it currently uses "`#pawwork-titlebar-tabs`
[data-slot=\"tabs-list\"]", change the ID part to a data attribute such as
"[data-component=\"pawwork-titlebar-tabs\"]" (or the project's canonical data
attribute) so the selector becomes "[data-component=\"pawwork-titlebar-tabs\"]
[data-slot=\"tabs-list\"]"; update the CSS string inside the page.addStyleTag
call accordingly.
🪄 Autofix (Beta)
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 Plus
Run ID: 3dbef095-c4b4-4a88-93be-ded718accd60
📒 Files selected for processing (4)
packages/app/e2e/session/titlebar-right-rail-contract.spec.tspackages/app/src/components/titlebar.tsxpackages/app/src/index.csspackages/app/src/pages/session/session-side-panel.tsx
Toggle moved to absolute right-2 in this PR; the closed-state inset comment still pointed at the removed pr-2 utility.
|
P3 follow-up: stale |
- x-axis seam test: poll for steady state instead of reading immediately after open. Panel aside width and shell --right-panel-width both run 240ms transitions on panel open/close, and data-resizing-right-panel only gates drag, so mid-flight sub-pixel drift could bust the <1px tolerance. - toggle x-position test: normalize aria-expanded to false before sampling the closed state, instead of relying on the e2e harness default. Same lookup uses page.locator (DropdownMenu marks the rest inert when open, breaking getByRole).
Summary
Three connected fixes for the right-panel titlebar seam, reported post-release as a follow-up to #880. All three only become visible together: once the seam aligns, the toggle slide and the drag-resize lag become readable.
Fix 1 — macOS seam alignment
padding-inline: 8px→padding-inline-start: 8pxon the macOS[data-component="titlebar-shell"]rule.The symmetric 8px padding pushed the right rail (and the tabs slot pinned to its right edge) 8px inboard of the viewport. The right-panel
<aside>directly below sits in a sibling layout tree without that padding, so itsborder-leftreached the actual viewport edge — the twoborder-ls ended up 8px apart on macOS. Windows has no equivalent rule and was already correct.The titlebar comment block in #880 explicitly assumed the right rail's outer edge =
viewport.right— true on Windows, false on macOS. The toggle's existingpr-2now provides the closed-state 8px inset directly (matching macOS native toolbar conventions for the rightmost control), without doubling up with the rail's own padding.Fix 2 — Stable toggle position across open/close
#pawwork-titlebar-rightchanged from in-flow flex sibling of the tabs slot to absolute-positioned at the rail's top-right corner (right-2on macOS,right-0on Windows).PR #880's structural layout made the toggle a flex sibling, so opening the panel pushed it left by
--right-panel-widthover the 240ms transition. That slide read as visually jarring once fix #1 aligned the seam and the motion became readable against the now-continuous border-l.The toggle visually overlaps the tabs slot's rightmost area when open, but they don't collide in practice — Kobalte's
Tabs.Listsizes to content (~150px for 1-4 chips, observed146.5pxin the regression test debug dump), leaving the trailing+button in the middle of the panel column with empty space at the corner for the toggle to float in.Fix 3 — Drag-resize seam sync
New effect in
SessionSidePanelmirrorsprops.size.active()to adata-resizing-right-panelattribute on<desktop-shell>. Matching CSS rule inindex.cssdisables the shell's transition while the attribute is present.Root cause: while dragging the panel resize handle, the body's
<aside>inlinewidthsnapped to each pointermove (its transition is gated on the session-scopeprops.size.active()), but--right-panel-widthon the desktop shell kept its 240ms cubic-bezier transition because the shell's gate usesstate.sizing— a layout.tsx-local flag scoped to the sidebar resize handler only. The two sides of the seam moved at different speeds; one led, one trailed.The attribute-bridge approach avoids plumbing session-scope sizing state into the global layout context — the bridge stays one attribute wide and one CSS rule shallow. The 120ms timeout in
createSizingre-enables the transition shortly after the user lets go, so the open/close animation still uses the full 240ms cubic-bezier curve.Regression tests
Two new assertions in
titlebar-right-rail-contract.spec.ts:tabs slot border-l aligns horizontally with the right-panel body border-l (no x-axis seam break)— asserts|tabs.left - body.left| < 1px(sub-pixel tolerance for Retina rounding). Pre-fix chore: v1.1 polish — skills, panel fixes, README SEO #1:Received: 8. Post-fix: passes.right utility toggle keeps the same x-position across open/close— asserts|toggle.right(open) - toggle.right(closed)| < 12px. Pre-fix feat: split desktop panel and thread locale into skills #2:Received: 229.25(the full panel-width displacement). Post-fix: within sub-pixel rounding.Related Issue
None — post-release regression report; no separate GitHub issue filed.
Human Review Status
Pending
Review Focus
+would overlap.createEffectthat mirrors sizing state to the DOM attribute. The alternative was liftingcreateSizinginto the layout context; this is the smaller change.padding-inline-starton macOS titlebar,[data-resizing-right-panel]transition reset).Risk Notes
padding-inlinerule is already gated on[data-shell-os="macos"]). Fixes feat: split desktop panel and thread locale into skills #2 and fix(config): wait for background installs on dispose #3 affect both macOS and Windows.padding-inline: 8px+pr-2) to 8px (pr-2only on macOS,right-0on Windows after fix feat: split desktop panel and thread locale into skills #2). Matches macOS native toolbar conventions.Tabs.Listsizing to content. If a future change makes the strip stretch to fill the slot, the trailing+button would slide under the absolute-positioned toggle. The new regression test would not catch this — it only checks toggle position, not collision..github/workflows/touched.How To Verify
Manual Electron (mac) verification: toggle stable across open/close, drag-resize seam moves as one continuous line.
Screenshots or Recordings
Snap grid at
docs/design/preview/screenshots/right-panel-titlebar.png(local-only perdocs/exclusion) shows the toggle at the viewport corner in both light + dark, with the seam continuous from titlebar top through the right-panel body's left edge.Checklist
bug)app,ui)P2)Pendingdev; title is Conventional Commits in EnglishSummary by CodeRabbit