feat(desktop): bump default text scale#1455
Draft
tellaho wants to merge 5 commits into
Draft
Conversation
Co-authored-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Two regressions surfaced by the 1.1 default text scale: - Closing the profile avatar editor stranded the settings panel ~69px below where the user left it: the tall editor invites scroll (browser focus-scroll to the Done button), and on collapse the scroller clamps to the shorter closed layout. ProfileSettingsCard now snapshots the settings scroller position when the editor opens and re-applies it on close, so the pre-edit view is restored exactly. The original <8px scroll-restore assertion in profile.spec.ts is reinstated unchanged. - The custom-section dnd reorder smoke failed at 1.1 because the sidebar overflows the test viewport and Playwright's click auto-scroll leaves the section headers above the scroller clip; raw mouse coords then hit the workspace rail and the drag never activates. dragOver now scrolls both handles into view before reading bounding boxes — the reorder still has to activate, drop, and commit for the test to pass. Co-authored-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Three scroll-anchor gaps surfaced by the 1.1 default text scale (all app-side; no test tolerances changed): - Prepend drift: after the older-history re-pin, prepended rows above the viewport realize their content-visibility intrinsic-size estimates a few frames later. No scroll event fires for that shrink and native scroll anchoring has no anchor node above the viewport, so the reading row drifted ~30px (scales with root font size — 1.1 pushed it past the 28px e2e budget). The content ResizeObserver now re-pins mid-history message anchors (it only handled at-bottom), and onScroll swallows the echoes of our own anchor-holding writes instead of re-capturing the drifted position as the new anchor. - Stranded bottom jump: the scroll-to-latest pill's smooth jump computes its animation target from metrics at call time; rows realizing their true heights mid-flight make that target stale, the engine settles ~52px above the real floor, and then emits no further scroll events — an event-driven settle guard can never issue the final corrective write. The settle guard is now a rAF loop outside the event stream: hands-off while the position is moving, corrective pin once it holds still short of the true floor, disarmed at the floor, on trusted user input, or a hard time cap. - Anchor-blind system rows: SystemRow rendered no data-message-id, so in a young channel whose only rows are system events computeAnchor found nothing and fell through to at-bottom — a user who scrolled up never got the scroll-to-latest pill and was yanked down by the next arrival. System rows now carry their message id. Co-authored-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Category: improvement
User Impact: Desktop now opens with text and UI chrome slightly larger by default for better readability.
Problem: The previous default zoom made the desktop app feel a little too dense, especially in message-heavy views where text and controls sit close together. Bumping the default text scale exposed visual tests that were hard-coded to the old 16px baseline, plus real scroll/interaction regressions around profile restore, custom-section drag reorder, scroll anchoring, and young-channel scroll-to-latest behavior.
Solution: Raise the default text scale from 1.0 to 1.1 while keeping webview coordinates stable, preserve custom zoom persistence and keyboard shortcuts, rebase affected visual expectations around the active root scale, and fix the app-side regressions exposed by the larger default. The latest re-gate confirmed root font is 17.6px under the harness, all PR checks are green at
e9ad63d8, and no scroll/test guard thresholds were weakened.File changes
desktop/src/app/useWebviewZoomShortcuts.ts
Updates the default text scale to 1.1 while preserving persisted user overrides and existing zoom shortcut behavior. The underlying webview zoom remains 1.0 so pointer coordinates and browser geometry stay stable.
desktop/src/features/messages/lib/rowHeightEstimate.ts
Expresses timeline intrinsic row reserves in
remso reserved heights scale with the root text size.desktop/src/features/messages/lib/rowHeightEstimate.test.mjs
Updates unit expectations for the new
rem-based timeline reserve output.desktop/src/features/messages/ui/TimelineMessageList.tsx
Adds
data-message-idto system rows so young channels containing only system events can still participate in scroll anchoring and correctly show the scroll-to-latest pill when the user scrolls away from bottom.desktop/src/features/messages/ui/useAnchoredScroll.ts
Fixes two scale-exposed scroll-anchor bugs app-side: mid-history anchors are re-pinned through delayed
content-visibilitysize realization, and bottom settling now uses a requestAnimationFrame loop that can issue the final corrective pin even when smooth scrolling stops short of the true floor without another scroll event.desktop/src/features/settings/ui/ProfileSettingsCard.tsx
Fixes the profile scroll-restore regression: snapshots the settings scroller position when the avatar editor opens and re-applies it on close, so the pre-edit view is restored exactly instead of landing ~69px lower after the tall editor collapses.
desktop/src/features/settings/ui/SettingsView.tsx
Tags the settings content scroller with
data-settings-scrollerso the profile card can find it for the scroll snapshot/restore.desktop/scripts/check-px-text.mjs
Updates the line number of the existing documented
text-[6rem]avatar-glyph exception after the ProfileSettingsCard edits shifted it.desktop/tests/helpers/css.ts
Adds helpers for reading the active root font scale and checking scaled radii where the UI intentionally grows with zoom.
desktop/tests/e2e/channels.spec.ts
Rebases intro card/inset geometry assertions around the active root scale.
desktop/tests/e2e/custom-emoji.spec.ts
Rebases inline reaction button dimensions around the active root scale while preserving the existing interaction checks. Note: the reaction-button height check is floor-based rather than exact while width stays precise.
desktop/tests/e2e/file-attachment.spec.ts
Marks file-card corner radius as root-scale-dependent.
desktop/tests/e2e/image-attachment-gallery.spec.ts
Marks image thumbnail and lightbox surface radii as root-scale-dependent.
desktop/tests/e2e/messaging.spec.ts
Rebases link-preview/code-block radii and thread participant avatar sizing around the active root scale.
desktop/tests/e2e/profile.spec.ts
Updates profile editor geometry expectations for the larger default scale and reinstates the original
<8pxscroll-restore assertion, which now passes against the ProfileSettingsCard fix (measured drift: 0px).desktop/tests/e2e/video-attachment.spec.ts
Marks inline video surface radius as root-scale-dependent.
desktop/tests/e2e/virtualization.spec.ts
Fixes the custom-section DnD reorder smoke, which this PR's scale bump broke: at 1.1 the sidebar overflows the 720px test viewport and Playwright's click auto-scroll leaves the section headers above the scroller clip, so raw mouse coords landed on the workspace rail and the drag never activated.
dragOvernow scrolls both handles into view before reading bounding boxes; the reorder still has to activate, drop, and commit for the test to pass. The app-side reorder logic itself works correctly at 1.1.Reproduction Steps
general.<8pxassertion).Profileheader is visible at the top of the scroller; this PR did not find a separate initial-load header cutoff beyond the editor-close drift case.Screenshots/Demos
Before — default 1.0 / 16px:
After — default 1.1 / 17.6px:
Latest re-gate timeline at 1.1:
Profile initial-load header at 1.1 — confirmed visible, not cut off:
Profile scroll-restore regression at 1.1 (now fixed — kept for review context):
Reference behavior at 1.0 after closing profile editor:
Validation
At
e9ad63d8, Marge re-ran the full gate and confirmed every PR check is passing:pnpm checkfromdesktop/pnpm typecheckfromdesktop/pnpm testfromdesktop/(1475 pass)pnpm buildfromdesktop/pnpm exec playwright test scroll-history.spec.ts --project=smoke --repeat-each=5(14 tests × 5 = 70/70; includes the:114prepend-drift and:288bottom-jump cases)pnpm exec playwright test virtualization --project=smokeplus overscroll coverage (18/18 in the final gate)pnpm exec playwright test timeline-no-shift.spec.ts --project=smoke(15/15 in the final gate)pnpm exec playwright test profile.spec.ts --project=integration(includes the reinstated<8pxscroll-restore guard; earlier gated 19/19)pnpm exec playwright test custom-emoji.spec.ts --project=smoke(13/13)Regression accounting
Review correctly flagged that the earlier revision of this PR (a) caused the custom-section DnD smoke failure while describing it as unrelated, and (b) replaced the profile scroll-restore assertion with a weaker visibility check while calling the drift "documented". Both are now actually fixed: the DnD failure was a direct consequence of the 1.1 bump (viewport-clip interaction described above), and the ~69px profile drift is eliminated at the source in
ProfileSettingsCard, with the original numeric guard restored unchanged.The final scroll round fixed three additional app-side bugs exposed by the 1.1 scale without weakening any test guard:
content-visibilityrow-size realization above the viewport moved the reading row after the first re-pin.useAnchoredScrollnow re-pins mid-history message anchors through those resize settles and swallows only echoes of its own anchor-holding writes. Drift measured ~31px before and ~0.16px after.requestAnimationFrame, stays hands-off while motion continues, pins only once the position is stable short of the floor, and disarms at the floor, on trusted user input, or after a 2s cap.data-message-id, so channels showing only system events could not produce an anchor and were treated as still at bottom.SystemRownow exposes a message id for anchoring, with a load-bearing comment.Caveats are resolved/covered: the locally observed
timeline-no-shift.spec.ts:205flake was conservative and unrelated (Marge got 15/15), and the relay-backedstream.spec.ts:455case could not be run locally because of Docker org sign-in, but CI proves Desktop E2E Integration and Relay are green ate9ad63d8.Coordination: Buzz channel
buzz-message-timeline, threadcca5fa4c25340bf73e42688e3ad10720a98e9e7564c3d3ebf79d83a8deab012a.