From 49f8cdd40145709247a9797ba973b9cd0d7919f4 Mon Sep 17 00:00:00 2001 From: creatixchu Date: Fri, 31 Jul 2026 15:25:29 +0800 Subject: [PATCH 01/72] fix(web): bind the composer's caret to its glyphs with one scrollport MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- ...omposer-glyph-layer-tracks-the-textarea.md | 77 ----- ...oser-glyph-layer-tracks-the-textarea.zh.md | 77 ----- ...ext-layers-share-one-scrollport.i18n.yaml} | 6 +- ...mposer-text-layers-share-one-scrollport.md | 73 +++++ ...ser-text-layers-share-one-scrollport.zh.md | 73 +++++ apps/web/tests/approval-composer.e2e.ts | 12 +- apps/web/tests/composer-draft-scroll.e2e.ts | 287 ++++++++++-------- .../geometry.expected.md | 18 +- .../src/client/skeleton/InputBar.module.css | 59 ++-- .../src/client/skeleton/InputBar.tsx | 110 +++---- .../ui-conversation/tests/input-bar.spec.tsx | 50 ++- 11 files changed, 412 insertions(+), 430 deletions(-) delete mode 100644 .agents/notes/implemented/bug-fix/2026-07-31-composer-glyph-layer-tracks-the-textarea.md delete mode 100644 .agents/notes/implemented/bug-fix/2026-07-31-composer-glyph-layer-tracks-the-textarea.zh.md rename .agents/notes/implemented/bug-fix/{2026-07-31-composer-glyph-layer-tracks-the-textarea.i18n.yaml => 2026-07-31-composer-text-layers-share-one-scrollport.i18n.yaml} (53%) create mode 100644 .agents/notes/implemented/bug-fix/2026-07-31-composer-text-layers-share-one-scrollport.md create mode 100644 .agents/notes/implemented/bug-fix/2026-07-31-composer-text-layers-share-one-scrollport.zh.md diff --git a/.agents/notes/implemented/bug-fix/2026-07-31-composer-glyph-layer-tracks-the-textarea.md b/.agents/notes/implemented/bug-fix/2026-07-31-composer-glyph-layer-tracks-the-textarea.md deleted file mode 100644 index d60a100be9..0000000000 --- a/.agents/notes/implemented/bug-fix/2026-07-31-composer-glyph-layer-tracks-the-textarea.md +++ /dev/null @@ -1,77 +0,0 @@ -# Agent Note: The composer's glyph layer tracks the textarea's scroll offset - -Status: implemented - -English | [中文](2026-07-31-composer-glyph-layer-tracks-the-textarea.zh.md) - -## Problem - -A composer draft longer than the 14-line cap could not be scrolled. The caret moved and the selection moved, but the words stayed frozen at line 1 — no wheel gesture, drag, or arrow key brought the end of a long draft on screen, so the bottom of anything past ~14 lines was unreachable and unreadable while writing it. - -The cap itself was working. The composer paints its text in two stacked layers ([InputBar](../../../../packages/client/ui-conversation/src/client/skeleton/InputBar.tsx)): the `