Repository navigation
fix(tabs): a tab strip the user scrolled is not pulled back to the open tab when a typeface arrives - #479
Conversation
…en tab when a typeface arrives Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configuration
📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 8 remain after this review. 📝 SummarySummary by CodeRabbit
WalkthroughThe tab strip now tracks user scrolling. Late font loading brings the active tab into view unless the user has scrolled the strip. Regression checks cover both scroll states and verify that opening a tab brings it into view. ChangesLate font loading and tab scrolling
Priority: ⬇️ Low Estimated code review effort: 3 (Moderate) | ~20 minutes Change: Bug fix Merge Risk: ⚪ Minimal · up to No actionable merge-blocking risk is identified; the change is mergeable after normal checks. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Its branch had appended the tab-strip sentence to the fonts entry under [0.3.50], which was released before #479 merged. The sentence is its own entry under [0.3.51] Fixed, and the 0.3.50 entry links #473 alone again. Bumps package.json and herdr-plugin.toml to 0.3.51 and moves the Unreleased notes under [0.3.51] - 2026-10-06, with the compare link. Raises the remote PC bundle to 18: #368 adds the `take-over` capability the bridge advertises, and #477, #483, #484, #485 and #486 change what the bridge reads (transcripts, prompts, process start times, a pane's Codex store), so a remote PC on bundle 17 keeps the old behaviour until its bridge is updated. Push the `remote-v18` tag and let the bundle workflow publish before running release.yml. Carries #368 #477 #479 #481 #482 #483 #484 #485 #486 #487 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Follow-up to #473 (unreleased), from its review: two Low findings.
What was wrong
1. The tab strip jumped back to the open tab on every face arrival. Since #473
TabStripfollowsuseFacesArrived()and brings the open tab into view again each time a face loads, because the names are redrawn at another width. That is right when nobody touched the strip, and wrong after the user scrolled it: on a phone with more tabs than fit, open the last tab, drag the strip back to look at the first ones, and then the first Korean text appears on the page. Its Pretendard chunk loads (this can be long after page load) and the strip jumps back to the last tab.2.
scripts/font-swap-demo-regression.tsdid not test what it said. Its end-following cases opened an 844px-tall window in which the demo conversation does not overflow (measured here: 437 in 437), so "still at the end" was true of anything, and the height change was only logged.What changed
src/lib/tabStripScroll.ts(new, pure): the rule. The strip keeps where it is known to be (at) and whether the user scrolled it (moved).TabStrip.tsx: oneonScrollon the strip. The effect is split in two: selection, tab count and strip visibility place the open tab as before; a face arrival places it only whilemovedis false. A change of the open tab clearsmoved.How a user scroll is told from the strip's own. No input event is listened for. Wheel, touch drag and its momentum, a key, and a focus that scrolls a tab into view all end in a
scrollevent, and so does the strip's ownscrollLeftassignment, so they are told apart by position:scrollLeftback (the value the browser kept after clamping) and stores it; ascrollevent that finds the strip exactly there is its own;scrollevent that finds it lower, at the new end of a row that got shorter (a face that drew names narrower, a tab renamed or closed: the browser clamps), is nobody's scroll and only updates the known position;The strip has no scrollbar (
scrollbar-width: none), so there is no separate scrollbar path; a scrollbar drag would arrive as the same event anyway.scroll-behavioris not smooth here, so one assignment is one final position. One frame of ambiguity remains by nature: a user scroll whose event has not fired yet when the strip places itself in the same frame is overwritten, as before.scripts/font-swap-demo-regression.ts: the two end-following cases use a 480px window and now require an overflowing transcript and a height that changed with the face before asserting the reader is at the end. Two strip cases added on the demo transport (tabs made through the demo's/api/tab/create).scripts/mobile-tabs-regression.ts: feat(type): the app ships Pretendard and JetBrains Mono, and the chat reads a step larger #473's held-fonts case runs twice, the second time with a user scroll before the faces come.Tested
bun run typecheck: clean.bun run test:unit: 1322 pass, 4 skip, 0 fail (6 new insrc/lib/tabStripScroll.test.ts).bun run build: ok.CHROME_PATH=/usr/bin/google-chrome bun scripts/font-swap-demo-regression.ts: 5 PASS (transcript height 430 -> 455 on arrival; strip follows its open tab,scrollLeft154 -> 157, after a scroll that ended in opening that tab; a scrolled strip stays at 0).main'sTabStrip.tsx: the new scrolled-strip case fails (157 !== 0), the others pass.Not tested
scripts/mobile-tabs-regression.tsneeds a live herdr session and was not run locally: it was only bundle-checked (bun build, together withui-regression.ts). CI's "Integration and browser" is its first run.font-swap-demo-regression.tsis not part of CI (as before this PR), so its assertions are checked only on a developer's machine; "height changed" depends on the machine's fallback font differing from Pretendard in width.scrollLeftfrom the page. The app cannot tell that from a wheel or a finger (it sees only the scroll event), but no real touch drag with momentum was driven, and no real phone was used.🤖 Generated with Claude Code