Repository navigation
feat(ChatLayout): the composer rides the software keyboard - #8176
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
AI review status for this pull request.
|
PR Analysis Report🔎 Preview ·
|
| Component | Size | LoC | Visual · A11y · RTL |
|---|---|---|---|
| ChatLayout | 🔴 78.2 kB (+16.5 kB) · 10.4 kB CSS (+48 B) | — | ✓ · ✓ · — |
| lab/Comments | 🔴 326.8 kB (+4.7 kB) · 2.5 kB CSS (0) | — | — · — · — |
| lab/CommentThread | 🔴 320.3 kB (+3.1 kB) · 2.4 kB CSS (0) | — | — · — · — |
118 more components
| Component | Size | LoC | Visual · A11y · RTL |
|---|---|---|---|
| lab/CommentComposer | 🔴 146.2 kB (+3.0 kB) · 1.9 kB CSS (0) | — | — · — · — |
| ChatComposer | 75.8 kB (+311 B) · 11.7 kB CSS (0) | — | ✓ · ✓ · — |
| ChatComposerInput | 73.8 kB (+281 B) · 11.2 kB CSS (0) | — | ✓ · ✓ · — |
| ChatComposerTokenElement | 73.8 kB (+280 B) · 11.2 kB CSS (0) | — | ✓ · ✓ · — |
| charts/ChartTooltip | 174.0 kB (+225 B) · 423 B CSS (0) | — | — · — · — |
| TabList | 42.1 kB (+220 B) · 6.4 kB CSS (0) | — | — · — · — |
| charts/Chart | 196.7 kB (+214 B) · 494 B CSS (0) | — | — · — · — |
| PowerSearch | 280.6 kB (+213 B) · 23.5 kB CSS (0) | — | — · — · — |
| richtext/RichTextView | 274.3 kB (+212 B) · 2.3 kB CSS (0) | — | — · — · — |
| StatusDot | 29.6 kB (+210 B) · 5.3 kB CSS (0) | — | — · — · — |
| Switch | 99.4 kB (+210 B) · 9.9 kB CSS (0) | — | — · — · — |
| FieldLabel | 49.2 kB (+209 B) · 5.6 kB CSS (0) | — | — · — · — |
| MobileNav | 68.8 kB (+209 B) · 10.5 kB CSS (0) | — | — · — · — |
| ChatSendButton | 59.0 kB (+208 B) · 9.9 kB CSS (0) | — | ✓ · ✓ · — |
| Link | 51.5 kB (+207 B) · 7.0 kB CSS (0) | — | — · — · — |
| Banner | 60.6 kB (+206 B) · 10.4 kB CSS (0) | — | — · — · — |
| TextArea | 60.5 kB (+206 B) · 9.2 kB CSS (0) | — | — · — · — |
| CommandPalette | 71.3 kB (+205 B) · 12.4 kB CSS (0) | — | — · — · — |
| TextInput | 65.9 kB (+205 B) · 11.0 kB CSS (0) | — | — · — · — |
| DropdownMenu | 94.4 kB (+203 B) · 17.1 kB CSS (0) | — | — · — · — |
| ListItem | 68.1 kB (+203 B) · 11.3 kB CSS (0) | — | — · — · — |
| Calendar | 69.6 kB (+202 B) · 10.5 kB CSS (0) | — | — · — · — |
| ChatDictationButton | 59.1 kB (+202 B) · 10.0 kB CSS (0) | — | ✓ · ✓ · — |
| ChatLayoutScrollButton | 59.1 kB (+202 B) · 10.1 kB CSS (0) | — | ✓ · ✓ · — |
| Toolbar | 41.4 kB (+202 B) · 7.9 kB CSS (0) | — | — · — · — |
| richtext/RichTextEditor | 294.6 kB (+202 B) · 2.6 kB CSS (0) | — | — · — · — |
| AvatarGroup | 44.3 kB (+201 B) · 5.6 kB CSS (0) | — | — · — · — |
| SideNavCollapseButton | 59.4 kB (+201 B) · 9.9 kB CSS (0) | — | — · — · — |
| Toast | 96.1 kB (+201 B) · 10.2 kB CSS (0) | — | — · — · — |
| ToggleButton | 59.5 kB (+200 B) · 10.0 kB CSS (0) | — | — · — · — |
| Lightbox | 110.0 kB (+199 B) · 10.3 kB CSS (0) | — | — · — · — |
| RadioList | 54.5 kB (+199 B) · 7.2 kB CSS (0) | — | — · — · — |
| Slider | 104.9 kB (+199 B) · 8.7 kB CSS (0) | — | — · — · — |
| Button | 58.6 kB (+198 B) · 9.9 kB CSS (0) | — | — · — · — |
| DateRangeInput | 90.3 kB (+198 B) · 12.3 kB CSS (0) | — | — · — · — |
| InputGroup | 54.8 kB (+198 B) · 7.8 kB CSS (0) | — | — · — · — |
| MobileNavToggle | 58.9 kB (+198 B) · 9.9 kB CSS (0) | — | — · — · — |
| IconButton | 58.6 kB (+197 B) · 9.9 kB CSS (0) | — | — · — · — |
| Tooltip | 27.9 kB (+197 B) · 4.3 kB CSS (0) | — | — · — · — |
| Typeahead | 77.8 kB (+197 B) · 12.3 kB CSS (0) | — | — · — · — |
| lab/Schedule | 103.1 kB (+197 B) · 3.9 kB CSS (0) | — | — · — · — |
| Avatar | 41.5 kB (+195 B) · 5.5 kB CSS (0) | — | — · — · — |
| ComplexSelector | 69.1 kB (+194 B) · 11.7 kB CSS (0) | — | — · — · — |
| Field | 53.1 kB (+194 B) · 7.2 kB CSS (0) | — | — · — · — |
| TimeInput | 157.1 kB (+194 B) · 15.7 kB CSS (0) | — | — · — · — |
| lab/EmojiPicker | 84.5 kB (+194 B) · 751 B CSS (0) | — | — · — · — |
| DrawerHeader | 61.6 kB (+193 B) · 12.8 kB CSS (0) | — | — · — · — |
| Selector | 97.4 kB (+193 B) · 17.1 kB CSS (0) | — | — · — · — |
| TableHeaderCell | 106.8 kB (+193 B) · 17.2 kB CSS (0) | — | — · — · — |
| CheckboxInput | 62.6 kB (+192 B) · 9.1 kB CSS (0) | — | — · — · — |
| TableCell | 107.3 kB (+192 B) · 17.4 kB CSS (0) | — | — · — · — |
| DialogHeader | 61.4 kB (+191 B) · 11.4 kB CSS (0) | — | — · — · — |
| Markdown | 177.9 kB (+191 B) · 20.0 kB CSS (0) | — | — · — · — |
| Stepper | 60.2 kB (+191 B) · 10.0 kB CSS (0) | — | — · — · — |
| lab/TransferList | 106.1 kB (+191 B) · 1.7 kB CSS (0) | — | — · — · — |
| HoverCard | 26.3 kB (+190 B) · 4.3 kB CSS (0) | — | — · — · — |
| lab/ListInput | 99.7 kB (+190 B) · 1.6 kB CSS (0) | — | — · — · — |
| BaseTypeahead | 69.3 kB (+189 B) · 10.8 kB CSS (0) | — | — · — · — |
| CheckboxListItem | 69.2 kB (+189 B) · 11.3 kB CSS (0) | — | — · — · — |
| Pagination | 107.4 kB (+189 B) · 17.5 kB CSS (0) | — | — · — · — |
| Popover | 87.0 kB (+189 B) · 13.9 kB CSS (0) | — | — · — · — |
| TabMenu | 69.8 kB (+189 B) · 11.0 kB CSS (0) | — | — · — · — |
| TopNav | 70.5 kB (+188 B) · 10.8 kB CSS (0) | — | — · — · — |
| MultiSelector | 92.7 kB (+187 B) · 16.6 kB CSS (0) | — | — · — · — |
| lab/TransferListSelector | 112.7 kB (+187 B) · 1.9 kB CSS (0) | — | — · — · — |
| ContextMenu | 106.0 kB (+186 B) · 17.0 kB CSS (0) | — | — · — · — |
| DateInput | 181.1 kB (+186 B) · 16.7 kB CSS (0) | — | — · — · — |
| richtext/RichTextEditorToolbar | 249.2 kB (+186 B) · 454 B CSS (0) | — | — · — · — |
| CheckboxList | 67.9 kB (+185 B) · 8.1 kB CSS (0) | — | — · — · — |
| Table | 115.4 kB (+185 B) · 17.7 kB CSS (0) | — | — · — · — |
| Carousel | 67.7 kB (+184 B) · 10.8 kB CSS (0) | — | — · — · — |
| CodeBlock | 73.2 kB (+184 B) · 11.2 kB CSS (0) | — | — · — · — |
| TopNavMegaMenu | 67.8 kB (+183 B) · 11.7 kB CSS (0) | — | — · — · — |
| MoreMenu | 94.7 kB (+181 B) · 17.1 kB CSS (0) | — | — · — · — |
| SideNavHeading | 73.6 kB (+181 B) · 11.2 kB CSS (0) | — | — · — · — |
| Tokenizer | 89.5 kB (+181 B) · 18.3 kB CSS (0) | — | — · — · — |
| TopNavMenu | 72.6 kB (+181 B) · 11.1 kB CSS (0) | — | — · — · — |
| TopNavHeading | 73.0 kB (+179 B) · 11.0 kB CSS (0) | — | — · — · — |
| InfoTip | 45.4 kB (+177 B) · 5.3 kB CSS (0) | — | — · — · — |
| BreadcrumbItem | 82.5 kB (+174 B) · 12.0 kB CSS (0) | — | — · — · — |
| lab/DiffViewer | 84.9 kB (+172 B) · 2.1 kB CSS (0) | — | — · — · — |
| SegmentedControl | 91.7 kB (+171 B) · 6.8 kB CSS (0) | — | — · — · — |
| lab/ChatReactionBar | 102.6 kB (+171 B) · 1.1 kB CSS (0) | — | — · — · — |
| lab/CommentPin | 54.9 kB (+171 B) · 1.2 kB CSS (0) | — | — · — · — |
| RadioListItem | 65.6 kB (+170 B) · 10.1 kB CSS (0) | — | — · — · — |
| lab/SelectableCardGroup | 62.5 kB (+170 B) · 162 B CSS (0) | — | — · — · — |
| DateTimeInput | 203.3 kB (+169 B) · 18.4 kB CSS (0) | — | — · — · — |
| SideNavItem | 68.6 kB (+169 B) · 11.0 kB CSS (0) | — | — · — · — |
| SideNav | 88.4 kB (+168 B) · 11.3 kB CSS (0) | — | — · — · — |
| lab/ChatEmojiPicker | 101.5 kB (+168 B) · 773 B CSS (0) | — | — · — · — |
| FileInput | 67.4 kB (+167 B) · 10.8 kB CSS (0) | — | — · — · — |
| AlertDialog | 103.2 kB (+166 B) · 14.2 kB CSS (0) | — | — · — · — |
| AppShell | 89.0 kB (+165 B) · 13.4 kB CSS (0) | — | — · — · — |
| TableSelectionToolbar | 68.2 kB (+165 B) · 13.0 kB CSS (0) | — | — · — · — |
| BottomSheet | 65.0 kB (+163 B) · 7.5 kB CSS (0) | — | — · — · — |
| Thumbnail | 62.9 kB (+159 B) · 11.5 kB CSS (0) | — | — · — · — |
| AvatarGroupOverflow | 46.7 kB (+157 B) · 5.5 kB CSS (0) | — | — · — · — |
| NumberInput | 70.0 kB (+154 B) · 11.3 kB CSS (0) | — | — · — · — |
| DropdownMenuSubMenu | 68.3 kB (+148 B) · 9.7 kB CSS (0) | — | — · — · — |
| lab/TourStep | 93.3 kB (+147 B) · 590 B CSS (0) | — | — · — · — |
| ProgressBar | 11.7 kB (+37 B) · 6.2 kB CSS (0) | — | — · — · — |
| charts/ChartLegend | 47.1 kB (+4 B) · 224 B CSS (0) | — | — · — · — |
| Timestamp | 31.4 kB (+4 B) · 10.2 kB CSS (0) | — | — · — · — |
| ChatComposerDrawer | 17.9 kB (0) · 4.7 kB CSS (0) | — | ✓ · ✓ · — |
| ChatMessage | 17.1 kB (0) · 3.7 kB CSS (0) | — | ✓ · ✓ · ✓ |
| ChatMessageBubble | 7.5 kB (0) · 3.7 kB CSS (0) | — | ✓ · ✓ · ✓ |
| ChatMessageList | 27.3 kB (0) · 7.0 kB CSS (0) | — | ✓ · ✓ · — |
| ChatMessageMetadata | 20.2 kB (0) · 4.1 kB CSS (0) | — | ✓ · ✓ · — |
| ChatSystemMessage | 7.0 kB (0) · 3.8 kB CSS (0) | — | ✓ · ✓ · — |
| ChatTokenizedText | 7.2 kB (0) · 4.0 kB CSS (0) | — | ✓ · ✓ · — |
| ChatToolCalls | 31.2 kB (0) · 8.3 kB CSS (0) | — | ✓ · ✓ · — |
| CommandPaletteInput | 30.8 kB (0) · 7.5 kB CSS (0) | — | — · — · — |
| Heading | 11.6 kB (0) · 5.4 kB CSS (0) | — | — · — · — |
| Text | 11.0 kB (0) · 5.4 kB CSS (0) | — | — · — · — |
| Timer | 13.1 kB (0) · 5.9 kB CSS (0) | — | — · — · — |
| Spinner | 23.9 kB (−1 B) · 6.1 kB CSS (0) | — | — · — · — |
| chat | — | — | — · ✓ · — |
| Chat | — | — | ✓ · ✓ · — |
Package size:
- @astryxdesign/cli 3.49 MB packed (+52 B); no new dependencies
- @astryxdesign/core 6.91 MB packed (+17.0 kB); no new dependencies
API changes: none in this pull request
Committed API report behind this build: cli 19 rows, core 25 rows; run pnpm api:report.
Touched components checked; add ci:run-visual, ci:run-a11y or ci:run-rtl to run the full suites.
Generated by PR Enrichment workflow | View full report
…rinks to stay above it On iOS 26.2 a popover that would run under the software keyboard shrinks, its top held on its source, so that its far edge stays 10 pt above the keyboard; its content scrolls in the shorter box, and it grows back when the keyboard goes down. Popover's height cap is the layout viewport, which the iOS keyboard does not shrink, so the keyboard covered the bottom of a tall popover and a Save button there could not be reached. usePopoverKeyboardAvoidance follows the keyboard layout guide (meta#8176): while the popover is open, a keyboard that would cover it caps its height popover.keyboardClearancePx above the keyboard and makes it scroll, keeping the focused field in view. UIKit animates the bounds; a height cannot change on the compositor, so it changes once where the keyboard hides it: when a rising keyboard has arrived, and as a falling one starts down. A Keyboard Avoidance story exercises it.
There was a problem hiding this comment.
This can't be approved yet: ChatLayout's new keyboard behavior has no governing spec, and the draft spec:AST-094 it builds on is unapproved and amends current records (architecture:theme-tokens INV3, design:user-states DR1 and DR3, family:buttons, spec:AST-025 FR14). An engineering owner needs to decide whether ChatLayout owns keyboard following as built here and which record states it, and whether to approve AST-094 and update the current records it amends; they record approval by commenting /approve-spec <full head SHA> on this pull request, and the author may do so if they are an engineering owner. Independently of that decision:
- Land #8045, #8067 and #8074 first and rebase, so this pull request is the 1,751-line keyboard change rather than 26,380 lines.
- Make the
lintcheck pass; ESLint runs out of heap on this head. - Add the writing rubric results for AST-094 to the description, and fix its citation of
spec:AST-090, which does not exist in the repository.
[Automated review]
# Conflicts: # packages/core/src/motion/README.md # packages/core/src/motion/uikit/index.ts
…rinks to stay above it On iOS 26.2 a popover that would run under the software keyboard shrinks, its top held on its source, so that its far edge stays 10 pt above the keyboard; its content scrolls in the shorter box, and it grows back when the keyboard goes down. Popover's height cap is the layout viewport, which the iOS keyboard does not shrink, so the keyboard covered the bottom of a tall popover and a Save button there could not be reached. usePopoverKeyboardAvoidance follows the keyboard layout guide (meta#8176): while the popover is open, a keyboard that would cover it caps its height popover.keyboardClearancePx above the keyboard and makes it scroll, keeping the focused field in view. UIKit animates the bounds; a height cannot change on the compositor, so it changes once where the keyboard hides it: when a rising keyboard has arrived, and as a falling one starts down. A Keyboard Avoidance story exercises it.
There was a problem hiding this comment.
This can't be approved yet: ChatLayout's new keyboard behavior has no governing spec, and the draft spec:AST-094 it builds on is unapproved and amends current records (architecture:theme-tokens INV3, design:user-states DR1 and DR3, family:buttons, spec:AST-025 FR14). An engineering owner needs to decide whether ChatLayout owns keyboard following as built here and which record states it, and whether to approve AST-094 and update the records it amends; they record that decision by commenting /approve-spec <full head SHA> on this pull request, and the author may do so if they are an engineering owner. Independently of that decision:
- Land #8045, #8067 and #8074 first and rebase, so this pull request is the 1,831-line keyboard change rather than 26,460 lines.
- The claimed composer tap places the caret only in contenteditable editors, so a tap inside a
<textarea>input's draft now leaves the caret at its previous position instead of under the finger. - The same claim walks past
contenteditable="false"chips, so a tap on a button inside a chip in an unfocused composer focuses the editor and never fires the button's click. - The ChatLayout docs say tapping the composer focuses it without scrolling, but only taps on the text are claimed; a tap on the composer body still focuses without
preventScroll. - Add the writing rubric results for AST-094 to the description, and fix its citation of
spec:AST-090, which does not exist in the repository.
[Automated review]
There was a problem hiding this comment.
This can't be approved yet: ChatLayout's keyboard following has no governing spec, and the new draft spec:AST-094 (FR1–FR23, DEC-1, DEC-2, including its amendments of architecture:theme-tokens, design:user-states, family:buttons and spec:AST-025 FR14) is unapproved, so an engineering owner needs to identify or provide the spec for keyboard following and approve or reject AST-094 by commenting /approve-spec <full head SHA> on this pull request; the author may do so if they are an engineering owner. Whatever that decision, these fixes are needed:
- A touch tap on a button inside the composer's text (a chip in a
contenteditable="false"island) is swallowed by the new composer tap recognizer, and a tapped<textarea>gets its caret at the start instead of under the finger (useChatKeyboard.ts). - Land #8045, #8067 and #8074 on their own and rebase, so this pull request carries only the keyboard change of about 1,850 lines.
- Add AST-094's writing-rubric results to the description, fix its reference to
spec:AST-090(no such record exists), and narrow the tap claim inChatLayout.doc.mjs.
[Automated review]
|
/approve-spec 51552d9 |
There was a problem hiding this comment.
Requesting changes. ChatLayout's composer now rests on the on-screen keyboard, its touch focus routes skip the page scroll, and a transcript the stream scroll follows stays at its newest message through useChatStreamScroll, so the earlier scroll-ownership item is fixed. Two items remain:
spec:AST-099Platform support makes Android emulator evidence at the exact head a merge gate, so please add the Chrome-with-Gboard takes (show, hide, reduced motion,interactive-widget=resizes-content) and the Firefox for Android takes that the description says come from a separate run.- Miss:
keyboardSurface.ts:38-46and the takeover'sisActive(useChatKeyboard.ts:481-483) re-implement the keyboard module's touch-and-keyboard gate (followsKeyboardinkeyboard/keyboardTarget.ts) andisCoarsePointerfromutils/coarsePointer.ts, so please export the module's gate and call it, and callisCoarsePointer, instead of keeping a second copy that can drift.
The record rows this pull request adds are owner-approved for this head, and the size is one inseparable change.
[Automated review]
The composer's focus and its focus takeover call the keyboard module's touch gate, followsKeyboard, now exported, and isCoarsePointer instead of their own copies. ChatLayout passes the stream scroll's scrollToBottom to useChatKeyboard directly. Each Effect's comment names what it synchronizes. Behavior does not change.
|
/approve-spec b6803cc |
There was a problem hiding this comment.
Requesting changes. The Android emulator takes at this head now cover what spec:AST-094 DEC-27 asks for, and the record rows are approved for this head; one blocking finding remains:
- Miss: under Reduce Motion in Chrome on Android, each of Chrome's stepped hide reports on Back moves the dock at once and commits the layout (the keyboard module turns a report between two non-zero heights into a non-animated
change, whichuseChatKeyboard.ts:277-285lands immediately), so the dock steps down as the description's Remove-animations take says, whilespec:AST-099FR8 requires that nothing travels with the keyboard under reduced motion, one DR2 cross-fade with one layout commit at rest; please make the Reduce Motion hide a single cross-faded change, ideally in the keyboard module so BottomSheet shares it, with a stepped-hide test and a Remove-animations Back take.
[Automated review]
The keyboard spec's header says Done's park shows the focus ring after typing only, as the code does. The changeset says nothing changes under a mouse with no software keyboard, the case the touch gate leaves alone.
|
Both text notes from the last review (the keyboard spec's header and the changeset's last sentence) go to the hide-ride follow-up, so this head keeps its device evidence. |
Under Remove animations, Chrome on Android reports a hide as several lower keyboards that are still up, then none. Each step reached every reader, so ChatLayout's dock stepped down and laid out each time. Where the VirtualKeyboard API exists and reduced motion is on, the keyboard module now holds such a step and keeps the shown height. The report of none, a report back up, or 300 ms without a new height ends the hold, which lasts 1 s at most; a pan leaves it as it is. So every reader lays out once and cross-fades once.
The composer's comments no longer cite INV11 or the theme-tokens record, so the comment-provenance baseline lists four fewer sites.
|
/approve-spec a303db7 |
There was a problem hiding this comment.
Requesting changes. ChatLayout's composer now rests on the on-screen keyboard and keeps the page still on touch screens, and the spec:AST-099 record rows are approved for this head. Three items remain:
- Regression: since #10438 landed on
main, the interaction harness requires every hold to name the motion it judges, soChatLayoutKeyboard.interaction.browser.spec.ts:652,:688and:756throw ("holdAnimations: name the motion that the test judges") and fail typecheck once merged withmain; please pass{judges: {target: dock, property: 'translate'}}to bothholdAnimationscalls andjudges: {target: dock, property: 'translate'}toanimationStoryboard, then update the branch ontomain. - Found by CI: the proof "the rise does no Layout or Paint per frame" (
ChatLayoutKeyboard.interaction.browser.spec.ts:638) fails in CI's interaction job at this head, where the rise paints every frame for its first 131 ms and two of those paints land in the 125–220 ms window, so please find what repaints early in the rise and take it off the ride (or show it is not the dock and narrow the proof) so the required check passes reliably. - Please add the Android emulator takes at this head that
spec:AST-099Platform support requires before merge, at least Back under Remove animations at normal speed and under the slow-device profile, showing the dock held at the keyboard's old top and one cross-fade to rest.
The inline note on the hold's 250 ms figure does not block.
[Automated review]
Focus starts the composer's own feedback, a 125 ms box-shadow transition that paints every frame. In CI its first style pass landed in the keyboard report's frame, so it painted into the proof's 125-220 ms window. The proof now lets it end before the trace. It also reads the ride's start in a microtask: a promise on the animation's ready gave the main thread a frame of its own.
The comment names the measured hide, hideMs, instead of 250 ms.
CI runs on a branch updated onto main, as review asked.
Main's interaction harness now asks each hold and storyboard to name the motion the test judges. The dock's translate is that motion here.
|
/approve-spec 6ed3557 |
There was a problem hiding this comment.
Requesting changes. The code is unchanged: ChatLayout's composer rests on the on-screen keyboard and keeps the page still on touch screens, the spec:AST-099 record rows are approved for this head, and CI is green.
One item remains:
- Please add Android emulator takes at this exact head, as
spec:AST-099Platform support requires before merge: the Remove animations Back take in the description is from an earlier head, and the merge ofmainsince then changed how the animator lands a Reduce Motion cross-fade (#10378), so at least Back under Remove animations at normal speed and under the slow-device profile need taking here.
The note on getKeyboardHeight's doc comment does not block.
[Automated review]
The layout took each keyboard height change through useKeyboardHeight, which re-rendered ChatLayout on every report only to run an Effect that called commit(). commit() now listens to the keyboard module's reports itself, after the layout guide, so a ride still claims a change first. The composer's reveal-pan watch repeated keyboardTarget's watchPans; it now calls it, exported. The keyboard-surface lines in ChatComposerInput move above the doc comments they sat under.
Main's scroll-anchoring owner module now writes the stream scroll's mark. isFollowingNewest reads the lock from a set the hook keeps in step with it, not from the old attribute.
Main now registers every Core context under a versioned key, so two copies of Core share one context. The keyboard surface context, new in this change, registers the same way.
|
/approve-spec 3c1f5d1 |
There was a problem hiding this comment.
Requesting changes. ChatLayout's composer rests on the on-screen keyboard and keeps the page still on touch screens at this head, the spec:AST-099 record rows are approved for this head, and CI is green.
One item remains:
- Please add Android emulator takes at this exact head, as
spec:AST-099Platform support requires before merge: the description dates its Chrome and Firefox takes to two earlier heads and says the full set at this head follows, and this head changes when the layout takes each keyboard report (useChatKeyboard.ts) and how the transcript's follow state is read, so show, Back and a blur hide at normal speed, Back under Remove animations, and the slow-device profile need taking here.
The note on getKeyboardHeight's doc comment does not block.
[Automated review]
There was a problem hiding this comment.
Approved. ChatLayout's composer now rests on the on-screen keyboard on touch screens and keeps the page still, the spec:AST-099 record rows are approved for this head by an engineering owner, the Android emulator takes in the description are at this exact head, and CI is green.
One non-blocking note:
getKeyboardHeight's doc comment (packages/core/src/keyboard/keyboardHeight.ts) still says a reader between two reports sees the current height; during the Remove-animations hold it sees the shown height, so a sentence saying so would keep the doc accurate.
[Automated review]
User need
On a phone, the keyboard covers ChatLayout's composer. Safari pans the page to show it, leaving it scrolled after Done. In Messages, the field sits on the keyboard.
Problem and solution fit
ChatLayout reads the keyboard module's height (
spec:AST-099FR2): the dock rests on the keyboard, and the message area's end padding keeps the newest message above it, via the stream scroll's lock (FR3). Under a finger, every route into the composer's text skips the browser's reveal, a claimed tap still clicks, and Done rests focus on the composer's group, ringed after typing (FR4). In Chrome on Android the dock rides Gboard's slide on the compositor (FR8). Under Reduce Motion it lays out once, with one cross-fade.Current specification
spec:AST-099FR2–FR4, FR8, and FR9's docked containers: a Core host applies the keyboard height on its own element, riding where FR8 applies. The records commit removes Gap 9, drops ChatLayout from Gaps 1 and 11, adds rows.Public delta and caller burden
component:ChatLayoutAR4), where Done rests focus; no tab stop. A mouse or an iframe changes nothing.100dvhcontainer.Design and alternatives
deliverTap,followsKeyboard,watchPans):useChatKeyboardreads its height, gate, focus routes, tap delivery, guide.keyboardProfileOf(win) === 'chromium'), as BottomSheet's does.Evidence
Scope
Writing rubric results
spec:AST-099(Gaps 1, 9 and 11, four verification rows): R1, R2, R4–R6 none; R3: each row names tests or the simulator.Testing
Dropped from the earlier draft, because main's guide has no WebKit profile (feat(core): one keyboard module; a Tall BottomSheet reads it and rides Gboard #8559/feat(core): the keyboard module's docked ride, the scroller's reveal and the tap route #9776's design), not because they failed: the spring rise, the unconfirmed hide, focus returning mid-hide, the 600 ms block, the mid-rise block, the rise through a blocked main thread, and both iOS Reduce Motion proofs. The rise trace and the storyboard now run on Chrome's profile.
useChatKeyboard.test.tsx(31): one case moved to Chrome's profile, and a new case shows that without the VirtualKeyboard API nothing subscribes the guide. The composer group has no name, and in an iframe the region stays a plain container: both fail on dbd0285 and pass here. A claimed tap clicks once (fails on af17d9a), and the parked group's ring follows the input modality (fails on 44c7740). A reader 5 px above the end, whomuseChatStreamScrollstill follows, keeps the newest message (fails on 5377ca2): the end-keeping reads that hook's lock (its live mark on the scroller) and jumps with its instantscrollToBottom. The local caret code and its cases are gone: main'sfocusAtPointand its tests cover the tap's caret. jsdom dated events on the epoch clock, so main's guide timers overflowed and every ride ended after 1 ms. The file now dates events onperformance.now(), as a browser does, with no warnings left.ChatLayoutKeyboardspec: Chromium 18/18; WebKit (CI-profile container) 12 passed, 6 skipped (Chrome's profile needs the VirtualKeyboard API). New: after typing, Done's park shows the shared focus ring (fails on af17d9a's build); after a tap and no typing it shows none (fails on 44c7740's build). New: under Reduce Motion, a hide reported in steps (60%, 35%, 15%, then none of the keyboard, 40 ms apart) draws the dock where the keyboard's top was or at rest, and the layout changes once. On b6803cc's build the dock steps down with each report (layout offsets 321, 186, 102, 35, 0 px; 3/3 fail); here 5/5 pass.keyboardHeight.test.ts, ten new cases: under Remove animations with the VirtualKeyboard API, the store keeps the shown height through the steps and reports the hide once; the guide publishes one hide and no change; a lower keyboard that stays up is reported after 300 ms; a pan's scroll events leave that wait as it is; steps that go on are reported after 1 s; a rise, the last reader leaving and a show end or skip the hold. Seven fail on b6803cc; the three guards (a show, no Reduce Motion, no API) pass on both. Keyboard, Chat, BottomSheet and layer-clearance units at this head (main merged): 1,219/1,219.The keyboard browser specs (ChatLayout, BottomSheet keyboard inset, Search, capped lists, Popover): Chromium 49 passed; WebKit (CI-profile container) 34 passed, 15 skipped. After the main merge, the spec's holds and storyboard name the dock's
translateas the motion they judge, as main's harness now asks."the rise does no Layout or Paint per frame", on Gboard's slide, 125–220 ms after the report: it first checks that a ride runs through that window, so a missing or early-ended ride fails instead of reading 0. It isolates the ride: focus starts the composer's own feedback, a 125 ms
box-shadowtransition that paints every frame, and the proof lets it end before the trace. In CI at a303db7 (pr-interaction (1/6)) that transition's first style pass landed in the report's frame, so it painted from 14 to 131 ms and into the window. The proof also reads the ride's start in a microtask, where the engine sets it, instead of waiting on the animation'sready. That focus paint during a rising keyboard is tracked separately, as a design question outside this PR. At this head: 24/24 on a 2-thread pin (one physical core) at 1 worker and 24/24 in the CI-profile container. At 4 workers on the pin, a busy machine can put the caret's first blink or the ride's starved start frames in the window; test(ChatLayout): the rise proof keeps the caret out and names a starved start #10489, stacked on this one, makes the proof hold there.The iOS 26.2 Simulator, builds from main d0d1f30 and a9c87ac (this head's ChatLayout code; the store's hold needs the VirtualKeyboard API, which Safari lacks): home-screen web-app takes (1 main; 4 this PR, 2 typed and 2 tapped before Done, one click per tap, no pan) and Safari takes at af17d9a. The image is home-screen frames, with no browser chrome. The numbers come from the page itself.
Android 16 emulator with Gboard, Chrome 133 and Firefox for Android 159, at this head, 3c1f5d1, on its own deployment, from a separate run: all 9 cells pass (show, with Back or a blur hide, at normal speed, under Remove animations and at 4× CPU), plus
resizes-contentand Firefox. The dock's bottom is 767.2 px at rest and the keyboard's top is 495.2 px; times are after the tap, Back or blur.interactive-widget=resizes-content(set on the viewport meta before the tap): Chrome resizes the page (innerHeight783 → 495), and the dock moves to 15.8 px above the keyboard in one step, with no ride and no pan.Core UI suite (
pnpm test:ui) at this head: 913 files, 22,385 tests pass, and this change's tests print no warning. Core typecheck: 0 errors.pnpm check:knowledgepasses, andCI=true pnpm lintpasses (0 errors), including the comment-provenance baseline check that failed CI's lint at b6803cc. ESLint (strict) and Prettier are clean on the changed TypeScript and Markdown files (lint-staged's set).The touch-gate commit, a refactor, is behavior-neutral: the composer's focus and the takeover call the keyboard module's touch gate (
followsKeyboard, now exported, with a unit test) andisCoarsePointerinstead of copies, and ChatLayout passesscrollToBottomdirectly. The same suites pass unchanged, and the iOS takes match the previous head's.The commit-path commit, from test(ChatLayout): the rise proof keeps the caret out and names a starved start #10489's review, is a refactor too.
commit()listens to the keyboard module's reports itself, after the guide, instead of through a ChatLayout render per report, and the composer's reveal-pan watch callswatchPansinstead of a copy. Timed in the page (probe, not for landing; median of 24 interleaved runs): in the harness's Chrome Android profile, the first frame after the report, 7.7 → 6.7 ms at 1× CPU and 33.0 → 28.7 ms at 4×; Safari's profile commits the lift in 2.9 → 2.2 ms and 13.1 → 9.5 ms. The ride starts in the report's own task either way. The same suites pass unchanged.Review notes
isFollowingNewestreads the lock from a set the hook keeps, since main's scroll-anchoring owner now writes the mark. The last commit registers the keyboard surface context under a versioned key, as main now asks of every Core context. The earlier draft's keyboard-module refactor for BottomSheet is gone: main'suseMobileKeyboardcovers it.