fix(web): bind the composer's caret to its glyphs with one scrollport
The composer paints its draft in two layers — the textarea owns the value, the selection and the caret, the backdrop paints every visible glyph — and they had one scroll offset each, kept equal by a `scroll` listener. That holds at rest and not in motion: a wheel gesture scrolls the textarea on the compositor, the listener runs afterwards, and for those frames the caret sits at the new offset with the words at the old one. Measured on a harness of the same geometry, a 200px offset change separates caret from glyphs by ~200px (chromium 203, firefox 202, WebKit 203) until a later frame — the caret flying out of its own text when a user swipes a long draft quickly. Both layers now ride one scrollport: `[data-input-scroll]` carries the 14-line cap, the auto-grow stack inside it is as tall as the whole draft, and the textarea holds no scrollable overflow of its own. The browser applies one offset to both layers in the same frame, so the coupling is structural rather than maintained. The backdrop's trailing-line sentinel and the cross-engine wrap-width premise go with the mirror: the layers now share a containing block, which closes the WebKit 768-against-776 gap by construction.
This commit is contained in:
@@ -77,12 +77,14 @@ describe('web e2e: approval takeover keeps its actions reachable', () => {
|
||||
const input = page.locator('textarea').first()
|
||||
await input.waitFor({ timeout: 10_000 })
|
||||
|
||||
// The composer's own text cap, measured on the live textarea before the
|
||||
// takeover replaces it. The panel's scroll region must stop at the same
|
||||
// height (the designer's requirement: one cap for the composer seat), and
|
||||
// measuring it here keeps the assertion free of the px value itself.
|
||||
// The composer's own text cap, measured on the live draft scrollport before
|
||||
// the takeover replaces it — the box that carries the cap, while the
|
||||
// textarea inside it is as tall as the whole draft. The panel's scroll
|
||||
// region must stop at the same height (the designer's requirement: one cap
|
||||
// for the composer seat), and measuring it here keeps the assertion free of
|
||||
// the px value itself.
|
||||
await input.fill(CAP_PROBE)
|
||||
const composerCap = await input.evaluate(el => el.clientHeight)
|
||||
const composerCap = await input.evaluate(el => el.closest('[data-input-scroll]')?.clientHeight ?? 0)
|
||||
expect(composerCap).toBeGreaterThan(0)
|
||||
await input.fill('')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user