fix(web): reserve one scrollbar gutter across the composer's text layers
Second review round escalated the wrap-width divergence from a separate concern to a defect in this fix's own premise, and it is right. Only .input scrolls, so only .input loses content width to a scrollbar that consumes layout space — what Windows and Firefox draw, and what the theme's global `::-webkit-scrollbar` width makes chromium treat as occupying space. A narrower .input wraps a long soft-wrapped draft onto more lines, so it grows taller, its scroll maximum exceeds the backdrop's, and the mirrored offset clamps below the caret. That is the same failure the trailing-line sentinel fixes, in the same direction, so deferring it would have shipped a fix that does not hold where users run a classic scrollbar. My first attempt to reproduce it found nothing and was wrong: the probe content was not wrap-sensitive. With varied-length words the effect is plain — the same draft laid out at 8px-apart widths differs by 2 to 5 lines, while at equal widths a textarea and a div agree exactly. The three layers now reserve the gutter together, in the shared metrics block that already exists to keep them symmetric. `overflow: hidden` is still a scroll container, so the non-scrolling layers honour it: 8px is reserved on each, measured. The cost is a text column 8px narrower on every platform, which is the price of one geometry rather than a per-platform one. The browser scenario asserts the premise directly — equal wrap widths, and a reserved band greater than zero on each layer. The band is what stops the assertion being vacuous: the widths would also match with no reservation at all on this engine's overlay scrollbar, and it is the reservation, not the match, that carries the guarantee to a platform whose scrollbar takes real width. Removing the declaration fails it with `expected 0 to be greater than 0`, and fails the golden with it. Also from the same round, three comment corrections: the e2e file header no longer describes the deleted layout effect, the measurement guard no longer claims the backdrop holds exactly one text node (the sentinel makes a second), and the sentinel comment now carries the one-sidedness argument that also settles the ghost hint — the mirror only fails when the backdrop is SHORTER, and the hint can only add content, never remove a line box.
This commit is contained in:
@@ -192,6 +192,20 @@
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
overflow-wrap: anywhere;
|
||||
/* Equal wrap width on every platform, which is what lets one scroll offset
|
||||
be mirrored between the layers (InputBar's `scroll` listener). Only .input
|
||||
scrolls, so only .input would otherwise lose width to a space-consuming
|
||||
scrollbar — the kind Windows and Firefox draw, and the kind the theme's
|
||||
`::-webkit-scrollbar` width makes chromium treat as occupying layout space
|
||||
(see ui-theme styles/scrollbar.css and the sidebar-scrollbar scenario).
|
||||
A narrower .input wraps a long soft-wrapped draft onto MORE lines than the
|
||||
backdrop, so it grows taller, its scroll maximum exceeds the backdrop's,
|
||||
and the mirrored offset clamps below the caret — the same failure the
|
||||
trailing-line sentinel fixes, in the same direction. Reserving the gutter
|
||||
on all three keeps the widths identical whatever that platform's
|
||||
scrollbar costs; `overflow: hidden` is still a scroll container, so the
|
||||
non-scrolling layers honour it. Measured: 8px reserved on each. */
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
/* figma 34:10434: #ADB2B8 light / #81858C dark — the caption pair exactly. */
|
||||
|
||||
@@ -383,6 +383,13 @@ export function InputBar({
|
||||
// offset at the very bottom clamps and the glyphs sit a line behind the
|
||||
// caret. The extra newline is absorbed by that same collapse when the draft
|
||||
// does not end in one, so it costs no height in the ordinary case.
|
||||
//
|
||||
// The mirror only fails one way — a backdrop SHORTER than the textarea
|
||||
// clamps the assignment, while a taller one takes every offset exactly and
|
||||
// hides the surplus below the clip. That is why the ghost hint needs no
|
||||
// handling of its own: it can only add content after the draft and before
|
||||
// this sentinel, never remove a line box, so it moves the pair to equal or
|
||||
// to the safe side.
|
||||
backdrop.push('\n')
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user