Skip to content

feat(core): in Chrome on Android, the chat composer rides the keyboard (AST-099 FR8) - #8287

Closed
vjeux wants to merge 687 commits into
meta:mainfrom
vjeux:uikit/c08-keyboard-android
Closed

vjeux wants to merge 687 commits into
meta:mainfrom
vjeux:uikit/c08-keyboard-android

Conversation

@vjeux

@vjeux vjeux commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Depends on #8176; review the commits after its merge.

User need

In Chrome on Android, Gboard slides over ChatLayout's composer, which jumps once Chrome reports the keyboard. spec:AST-099 FR8 asks core surfaces to ride the slide.

Evidence

Chromium 158 + Gboard, Android 15 emulator, 540×1200; backdrop blur off for capture (blur on). The emulator drops Chrome's page frames for 50–150 ms around each report (#8176 too), so the ride is judged on the page's clock: composer bottom per animation frame against Gboard's top per recorded frame.

#8176 (UIKit's spring, left) and this (Gboard's slides) gif
Show / hide show hide
A Tall BottomSheet sheet
Reduced motion on: fades out, lands, fades in; never travels reduced motion
interactive-widget=resizes-content: Chrome resizes the page; no ride resizes-content

Per frame (2 takes each): under reduced motion the dock held two positions, fading out then in; under resizes-content FR2 read 0 and the dock never translated, while Chrome laid it out above the keyboard.

Problem and solution fit

  • Measurement is feat(ChatLayout): the composer rides the software keyboard #8176's: Chrome's visual viewport, like every browser (DEC-4); under resizes-content FR2 reads 0 and nothing rides.
  • Timing (a browser with the VirtualKeyboard API):
    • Show: from the report dated back 25 ms (the 33 ms median less ~8 ms event-to-paint; spread ±10 ms), on cubic-bezier(0.1, 0, 0, 1) over 267 ms (20 shows, 2.4 px rms).
    • Hide: from 16 ms after a blur on cubic-bezier(0.3, 0.1, 0.2, 0.9) over 167 ms (21 hides, 7.7 px rms); Back from its report, dated back 33 ms.
    • Chrome's momentary hide between fields is held.
  • No theme's: Gboard's slides are values, like keyboard.spring (feat(ChatLayout): the composer rides the software keyboard #8176); a ride plays the curve its transition publishes (INV11).
  • Reduce Motion: nothing travels (DR2).

Current specification

spec:AST-099 FR6, FR8 and DEC-4: the height comes from the visual viewport in every browser; under interactive-widget=resizes-content FR2 reads 0 and a field does not ride; Astryx never overrides the app's viewport setting.

Public delta and caller burden

No public API change; iOS and desktop are unchanged. The browser-support guide covers Chrome.

Design and alternatives

Rejected: landing at the report (DEC-2's one step) and resizes-content with a FLIP cover. No physical phone was used (DEC-3 gates release).

Scope

  • One capability is added.
  • Unrelated fixes, cleanup, and policy changes were removed or split.
  • Consumer docs, focused tests, and migration evidence ship with the feature.
  • Public text and artifacts contain no internal Meta context.

Testing

  • Interaction suites (ChatLayoutKeyboard, BottomSheet; WebKit with BottomSheetKeyboardInset): Chromium 22 passed, 5 skipped; WebKit 17 passed; BottomSheet and ChatLayout a11y 51 passed. Under a mouse (desktop Chromium, built storybook), the composer focuses on a click, Enter sends and the wheel scrolls. Chrome on Android in ChatLayoutKeyboard.interaction.browser.spec.ts: the dock rides up on Gboard's show curve from the report, dated back 25 ms (start within 2 ms, curve within 0.5 px), and the layout takes the lift at rest; it rides down on the hide curve from the blur (start = blur + 16 ms ± 8, curve within 0.5 px); under Reduce Motion every frame is at the old or the new place.
  • vitest (all of core, the CLI, the interaction harness and the scripts): every file passes; four that fail in one parallel run (30 s timeouts, and the DropdownMenu lazy sheet's teardown, main's issue that fix(DropdownMenu): a failed sheet preload is handled and retried, never an unhandled rejection #8478 fixes) pass on their own. Among them: keyboardChromium.test.ts, where under resizes-content the guide rides nothing through a show, a hide and a second show (it fails if the layout height is cached); rideKeyboard.test.ts, the published spring (red before); the model's Chrome profile; the table's sources and that it registers no motion role; the module measuring Chrome under a finger but not under a mouse. pnpm build and the root pnpm typecheck pass.
  • CI=true pnpm lint, prettier, check:sync, check:changesets, motion roles check, sync-exports --check, check:knowledge, the i18n catalog, the CLI goldens.
  • A Tall BottomSheet in Chrome (3 takes): no room until Gboard's report, then 360 px within 37 ms, the field above the keyboard, no page pan, and the room gone within 28 ms of the hide report. The 0.3 px viewport overhang the emulator showed, which laid out 48.3 px of room with no keyboard, is fixed in feat(ChatLayout): the composer rides the software keyboard #8176, with its tests.
  • Emulator, scripted (record lease, golden image): 20 shows and 21 hides fitted frame by frame, five blurs with a marker for the hide's start, four ChatLayout takes reviewed frame by frame, and at this head reduced motion (Back and a blur) and resizes-content (2 takes), per frame, plus the ride re-taken at this head beside feat(ChatLayout): the composer rides the software keyboard #8176's: the composer rests on Gboard 218 and 244 ms after the show's report (feat(ChatLayout): the composer rides the software keyboard #8176's spring: 320 ms) and back at the bottom 128 and 140 ms after the hide's (feat(ChatLayout): the composer rides the software keyboard #8176: 418 ms).
  • Release gate: a real phone, Samsung Keyboard included.

@vercel

vercel Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
astryx Ready Ready Preview Oct 26, 2026 10:47am UTC

Request Review

@astracat-bot

astracat-bot Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

AI review status for this pull request.

Review status Updated
🟡 Reviewed (for maintainers only) Oct 26, 2026, 11:41 AM UTC

vjeux added a commit to vjeux/astryx that referenced this pull request Oct 24, 2026
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Oct 24, 2026

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This pull request is on hold for an owner decision: the Android keyboard ride in ChatLayout, its focus-scoped navigator.virtualKeyboard.overlaysContent toggle, the theme-retunable touch motion roles (including androidKeyboard.*) and the draft spec:AST-094 requirements have no approved spec authority, and the roles also contradict the current theme-tokens record and the draft's own FR4. An engineering owner records the decision by commenting /approve-spec <full head SHA> on this pull request, and the author may do so if they are an engineering owner; the decision needs to settle whether a component may toggle overlaysContent and whether touch motion is theme-retunable. These changes are needed either way:

  • Rebase once #8045, #8067, #8074, #8176 and #8258 land, so this pull request contains only the Android commit.
  • Leave the platform adaptation condition out of this pull request; it changes the theme authoring contract's fixed condition vocabulary.
  • State a removal condition for the -webkit-touch-callout keyboard profile or key it on a capability (AST-013 FR6), and add Chrome evidence that the dock comes down when the composer blurs with the keyboard up.
  • Add ## Writing rubric results for the new spec record.

[Automated review]

* is everything else. Pair `ios` with `pointer: 'coarse'` for a touch
* profile, such as touch motion tuned per platform.
*/
platform?: 'ios' | 'other';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

platform adds a condition axis to theme adaptations, while the current theme authoring contract (Change coupling) says "An adaptation change preserves the fixed condition vocabulary". The Android keyboard path never uses it; it arrives from #8258, where the contract needs an owner amendment first, so please leave it out of this pull request.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gone: this branch no longer touches the theme adaptations. After the restack onto #8176, its diff is Chrome's timing model only.

const css = (
win as Window & {CSS?: {supports?: (p: string, v: string) => boolean}}
).CSS;
return css?.supports?.('-webkit-touch-callout', 'none') === true

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This picks the keyboard model from -webkit-touch-callout support, a property the guide never uses, so every engine other than iOS WebKit, Firefox for Android included, gets Chrome's start-of-slide model. AST-013 FR6 allows engine inference only as a narrow workaround for a reproduced defect with a regression fixture and a review or removal condition. Please state that condition here and in the README, or key the Chrome path on the capability it relies on.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed: the guide picks Chrome's schedule where the VirtualKeyboard API exists (keyboardProfileOf, keyboardLayoutGuide.ts:58), the API Chrome reports Gboard's slides through, and the WebKit schedule everywhere else, Firefox for Android included. No property the guide never uses decides it, and no user agent. Both schedules' values are sourced in motion/uikit/keyboard.ts and motion/android/keyboard.ts.

};
const onOverlayFocusOut = (event: FocusEvent) => {
if (!inOverlayField(event.relatedTarget)) {
release();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the composer blurs with the keyboard up (the send button, a tap on the transcript), this releases overlaysContent before Chrome reports the hide. onGeometry then ignores that geometrychange, and blurred() predicts nothing on Chrome, so the dock comes down only if Chrome resizes the visual viewport for a keyboard it laid out as an overlay. Could you add a Chrome recording or test of this path, or hold the overlay until the hide is reported?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gone: the guide no longer sets overlaysContent. Chrome is measured from its visual viewport (spec:AST-099 DEC-4), and a blur predicts Chrome's hide 16 ms later on Gboard's hide curve. ChatLayoutKeyboard.interaction.browser.spec.ts covers the ride down from the blur (its start at blur + 16 ms ± 8, the curve within 0.5 px), and the emulator take at this head rests the composer at the bottom 128 and 140 ms after the hide's report.

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This pull request is on hold for an engineering owner's decision: the Android keyboard ride in ChatLayout, its focus-scoped navigator.virtualKeyboard.overlaysContent toggle, the theme-retunable touch motion roles (including androidKeyboard.*) and the requirements of the new draft spec:AST-094 have no approved spec authority, and the roles also contradict the current theme-tokens record and the draft's own FR4. An engineering owner records the decision by commenting /approve-spec <full head SHA> on this pull request, and the author may do so if they are an engineering owner; the decision needs to settle whether a component may toggle overlaysContent, whether touch motion is theme-retunable, and whether AST-094's requirements stand. These changes are needed either way:

  • Rebase once #8045, #8067, #8074, #8176 and #8258 land, so this pull request contains only the Android commit (the range is about 29,500 authored lines today).
  • Leave the platform adaptation condition out of this pull request; it changes the theme authoring contract's fixed condition vocabulary.
  • State a review or removal condition for the -webkit-touch-callout keyboard model or key it on a capability (AST-013 FR6), and add Chrome evidence that the dock comes down when the composer blurs with the keyboard up.
  • Add ## Writing rubric results for the new record, and correct its FR7 citation of AST-090, which does not exist in the repository.

[Automated review]

@github-actions

github-actions Bot commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

PR Analysis Report

📚 Storybook Preview

View Storybook for this PR

🧪 Sandbox Preview

View Sandbox for this PR

Component Size

Component Import before Import after Delta CSS before CSS after Delta
core/BottomSheet 26.0KB 28.4KB +2.4KB ⚠️ 7.7KB 7.7KB +2B
core/BottomSheetSwitcher 9.2KB 9.2KB 0B 3.3KB 3.3KB 0B
core/ChatComposer 60.3KB 60.8KB +504B 10.8KB 10.8KB 0B
core/ChatComposerDrawer 23.9KB 23.9KB +15B 4.5KB 4.5KB 0B
core/ChatComposerInput 55.3KB 55.8KB +484B 10.1KB 10.1KB 0B
core/ChatComposerTokenElement 55.3KB 55.8KB +482B 10.1KB 10.1KB 0B
core/ChatDictationButton 46.4KB 46.4KB +12B 9.5KB 9.5KB 0B
core/ChatLayout 48.7KB 62.8KB +14.1KB ⚠️ 9.9KB 10.0KB +39B
core/ChatLayoutScrollButton 46.3KB 46.3KB +12B 9.6KB 9.6KB 0B
core/ChatMessage 23.1KB 23.1KB +13B 3.5KB 3.5KB 0B
core/ChatMessageBubble 7.0KB 7.0KB 0B 3.6KB 3.6KB 0B
core/ChatMessageList 28.4KB 28.4KB +13B 7.8KB 7.8KB 0B
core/ChatMessageMetadata 25.8KB 25.8KB +12B 3.9KB 3.9KB 0B
core/ChatSendButton 45.7KB 45.7KB +11B 9.2KB 9.2KB 0B
core/ChatSystemMessage 6.7KB 6.7KB 0B 3.7KB 3.7KB 0B
core/ChatTokenizedText 6.9KB 6.9KB 0B 3.9KB 3.9KB 0B
core/ChatToolCalls 31.9KB 31.9KB +12B 8.9KB 8.9KB 0B
core/ContextMenu 77.1KB 76.8KB -318B 14.4KB 14.4KB +4B
core/DateInput 92.7KB 94.0KB +1.3KB 14.6KB 14.6KB +4B
core/DateTimeInput 102.0KB 103.3KB +1.3KB 15.0KB 15.0KB +4B
core/DropdownMenu 72.1KB 71.8KB -301B 14.5KB 14.5KB +3B
core/Markdown 141.9KB 141.6KB -265B 17.1KB 17.1KB +2B
core/MoreMenu 72.3KB 72.0KB -334B 14.5KB 14.5KB +3B
core/MultiSelector 75.5KB 75.2KB -372B 14.4KB 14.4KB +2B
core/Pagination 87.2KB 87.2KB +45B 15.1KB 15.1KB +5B
core/PowerSearch 147.6KB 148.6KB +1.0KB 18.4KB 18.4KB +4B
core/Selector 78.6KB 78.1KB -440B 14.8KB 14.8KB +4B
core/Table 83.3KB 83.0KB -358B 14.9KB 14.9KB +4B
core/TableCell 78.2KB 77.9KB -339B 14.6KB 14.6KB +2B
core/TableHeaderCell 78.0KB 77.6KB -363B 14.5KB 14.5KB +3B
core/TimeInput 79.2KB 80.6KB +1.4KB 13.5KB 13.5KB +2B

Sizes are gzip. Import counts the JavaScript the import loads eagerly; CSS counts the package stylesheet rules referenced by the modules it can load from its own package, lazily loaded ones included.

⚠️ Touch-only size budget

These components grew past the touch-only budget (2 KB gzip JavaScript or 1 KB gzip CSS per component). If the growth runs only on a coarse pointer, load its JavaScript lazily so the import does not carry it, or justify the cost in the pull request description; CSS stays in the package stylesheet, so justify it.

Component JavaScript CSS
core/BottomSheet +2.4KB ⚠️ +2B
core/ChatLayout +14.1KB ⚠️ +39B

Bundle Size Summary

Gzip figures measure each package's entry file, not the code an import pulls in.

Package Entry (ESM) Entry gzip before Entry gzip after Delta
@astryxdesign/core 4.9KB 1.2KB 1.2KB 0B
Package stylesheet Gzip before Gzip after Delta
@astryxdesign/core 34.2KB 34.2KB +25B

Accessibility Audit

Status: 2 accessibility violation(s) found — 2 serious.

ChatComposer - 1 issue(s)
  • 🟠 serious: Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds
    • Rule: color-contrast · Affects 1/19 stories · Learn more
    • WCAG: 1.4.3 (Level AA)
ChatComposerInput - 1 issue(s)
  • 🟠 serious: Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds
    • Rule: color-contrast · Affects 1/18 stories · Learn more
    • WCAG: 1.4.3 (Level AA)

Visual Regression

55 of 502 shot(s) changed.

A change here is a question, not a failure: check whether the after is the
picture you intended. Record that review on the PR. Baseline maintenance is an
explicit dispatch of CI; this report never rewrites the baseline or adds a release gate.

component story theme mode pixels
Selector Indicator Space Bottom Sheet Narrow End RTL neutral light 753,933
Selector Indicator Space Bottom Sheet Wide Start neutral light 740,072
RichTextEditor Markdown parity: side by side probe dark 558,840
RichTextEditor Markdown parity: side by side probe light 540,756
Chat Full AI Chat probe light 440,175
Chat Full AI Chat probe dark 438,105
AppShell With Mobile Nav probe light 217,010
AppShell With Mobile Nav probe dark 216,960
Chat Empty State probe light 122,185
Chat Empty State probe dark 120,076
Collapsible Dividers probe dark 66,445
Collapsible Dividers probe light 66,445
AppShell Playground neutral dark 65,198
AppShell Playground neutral light 63,788
Chat Default neutral dark 36,304
Chat Default neutral light 35,767
ChatAutoScroll Scroll Behavior Comparison neutral dark 15,378
ChatAutoScroll Scroll Behavior Comparison neutral light 14,107
Chat With Attachments probe light 13,406
Chat With Attachments probe dark 11,232

and 35 more.


Generated by PR Enrichment workflow | Storybook | Sandbox | View full report

@cixzhang

Copy link
Copy Markdown
Contributor

Posted by Cindy's agent on her behalf.

Parts of this PR re-implement behavior that already exists, or is being specified, as a shared part of core. Converging keeps one implementation per behavior:

  • Reduced motion. core/src/keyboard/rideKeyboard.ts:52 handles reduced motion locally. Per design:reduced-motion (DR1–DR4), the animator is the one place that resolves it (touch cross-fades in 200 ms or less with no travel, a fine pointer changes at once), and core has no opt-out, so reducedMotion: 'keep' should be removed.

This is about where the behavior lives, not about the PR's intent.

@vjeux

vjeux commented Oct 24, 2026

Copy link
Copy Markdown
Contributor Author

Agreed: one implementation.

vjeux added a commit to vjeux/astryx that referenced this pull request Oct 25, 2026
vjeux added a commit to vjeux/astryx that referenced this pull request Oct 25, 2026
…eta#8176 with interactive-widget=resizes-content beside meta#8287

Backdrop blur off for capture (software GL can't composite it at 60 fps); one still with the blur on beside it. Same story and text in both; the story's wrapper at 100dvh in both.
@cixzhang

Copy link
Copy Markdown
Contributor

Posted by Cindy's agent on her behalf.

Decision on the Android question in #8304: smooth keyboard riding on Chrome for Android is wanted, ahead of the one-step relayout. spec:AST-099 will state the observable requirement and leave the implementation to this PR. This PR stays held until #8304 merges.

@vjeux

vjeux commented Oct 25, 2026

Copy link
Copy Markdown
Contributor Author

Agreed: this PR becomes the smooth Android ride on the shared keyboard module, rebuilt on #8176, and stays held until #8304 merges. The measured mechanism question is on #8304: #8304 (comment)

@vjeux
vjeux force-pushed the uikit/c08-keyboard-android branch from 4be76ef to 44fd946 Compare October 25, 2026 05:16
@vjeux vjeux changed the title feat(core): on Android, the chat composer rides the keyboard in step with its slide feat(core): in Chrome on Android, the chat composer rides the keyboard (AST-099 FR8) Oct 25, 2026
@github-actions github-actions Bot added the needs:design-review Affects visuals — Design should review label Oct 25, 2026
vjeux added 2 commits October 26, 2026 01:02
It pointed a component with no interaction to infer from at
useMotionPresentation(), which does not exist, and at theme tokens under a
mouse, which no theme sets. Such a component reads useTouchPresentation()
like any other behaviour: UIKit timings under a finger, its own motion
under a mouse.
meta#8072, the gesture host without a delegated consumer) into the experimental touch exports

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes are needed before this can be approved: the Chrome-on-Android ride itself follows the current spec:AST-099 FR8 and DEC-4, but this pull request still contains the content of sixteen open predecessor pull requests (about 31,600 authored lines), which their own reviews need to settle first. Please rebase once #8176 and the pull requests beneath it have landed, so that this pull request carries only the 18 files of the Chrome work. While there, drop "under a finger" from the Chrome section of packages/core/src/keyboard/README.md, since the keyboard module measures Chrome under any pointer.

[Automated review]

Comment thread packages/core/src/keyboard/README.md Outdated
report and Gboard's top read from every recorded frame (`../motion/android/
keyboard.ts` has the numbers and their sources). The guide reads these reports
in a browser with the VirtualKeyboard API (`keyboardProfileOf`), and the
keyboard module measures them from the visual viewport under a finger

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This says the keyboard module measures Chrome "under a finger", but isKeyboardMeasurable has no pointer condition (useMobileKeyboard.ts:97-99), line 25 of this README says the decision is never from the pointer, and the updated test measures Chrome with (pointer: coarse) false (useMobileKeyboard.test.ts:238-252). Could you drop "under a finger" here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 099d9de: the Chrome section says the keyboard module measures the visual viewport as in every browser, with no pointer condition. The Chrome interaction tests' setup comment said the same thing; it now says they run under a finger because Chrome on Android does, where the composer's focus routes are the touch ones.


// A keyboard height the guide had no ride for (a resize, a rotation)
// reaches the layout directly.
useIsomorphicLayoutEffect(() => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This Effect sets the lift store through commit(), which measures the root against the layout viewport (lines 257-288), but its comment names neither that measurement nor an external system (architecture:react-component-runtime INV3). Could the comment name the measurement, or could the commit run from the keyboard module's subscription? It arrives with #8176, so the fix may belong there.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in #8176's bf7859a, merged here: the comment names what the Effect synchronizes, the keyboard module's height (the visual viewport, through useKeyboardHeight), and the measurement commit makes (the root against the layout viewport, into the lift store). It also says why a replay is safe: it acquires nothing, sets the lift only when that changes by half a pixel or more, and never during a ride.

vjeux and others added 18 commits October 26, 2026 01:14
architecture:react-component-runtime INV3 asks an Effect to name the
external system it synchronizes. The Effect that commits a keyboard height
the guide had no ride for said only that the height reaches the layout. Its
comment now names the keyboard module's height (the visual viewport,
through useKeyboardHeight) and the measurement `commit` makes (the root
against the layout viewport, into the lift store), and why a replay is
safe: it acquires nothing, sets the lift only when that changes by half a
pixel or more, and never during a ride.
… Chrome section now says

The keyboard README's Chrome section said the keyboard module measures
Chrome's reports from the visual viewport "under a finger", but
`isKeyboardMeasurable` has no pointer condition (meta#8176, spec:AST-099
DEC-4) and the README's own "Where it measures" says the decision is never
from the pointer. The section says the visual viewport is read as in every
browser.

The Chrome interaction tests' setup comment said the same; it says they run
under a finger because Chrome on Android does, where the composer's focus
routes are the touch ones, and that the module measures under any pointer.
meta#8176's head names what the keyboard-height Effect synchronizes
(architecture:react-component-runtime INV3). No conflicts.
…to the motion-role animator

# Conflicts:
#	packages/core/src/motion/README.md
#	packages/core/src/motion/roles.test.ts
#	packages/core/src/motion/uikit/timing.ts
#	packages/core/src/motion/uikit/uikit.test.ts
meta#8076's head folds main f69c31c (the scroll table's note), pins the press
release curve by value, drops the `hooks/isRtlElement` resolver entry, and
carries the maintainer's meta#8073 touch-action gate. No conflicts.

`timing.ts` checked against meta#8484: the file is meta#8076's with exactly
meta#8484's change (the `keyboardNotification` group, its binding and the
`keyboardNotificationMs` shorthand), nothing else.
…tays internal (spec:AST-094 DEC-11)

resolveMotion read each role's --motion-* variable from the page, which let
a page's or theme's style retune touch motion that DEC-11 keeps internal
until the public motion-token set is decided. It now returns the role's
table value, reading no style; every motion still passes through it, so a
public role can reach the animator later in one place. With no variable
read, a role value that cannot settle can no longer arrive either.

The unit tests and the browser proof now show a scope's variable changing
nothing, and the README, useAnimator and coast docs no longer say a theme
sets the roles.
…plays its table value) into the experimental touch exports

Four files both sides changed: coast.ts takes the animator's wording (the
edge spring plays its table value); useAnimator.ts takes its registration
line and keeps this side's note that a component may play its own measured
spring; roles.test.ts takes the menu spring's motionRoleOf check beside this
side's keyboard checks; resolveMotion.test.ts takes the animator's tests and
keeps this side's coast test (scroll.bounce is no role).
meta#8176's head merges meta#8076's 66edc03 (main f69c31c's scroll note, the
press release curve pinned by value, the maintainer's touch-action gate).
No conflicts; `timing.ts` is meta#8176's, with meta#8484's keyboard export.
meta#8076's head is a fast-forward of 66edc03 that folds main 61480b5
with meta#8257 (a motion role plays its table value). No conflicts.

`timing.ts` checked against meta#8484: the file is meta#8076's with exactly
meta#8484's change (the `keyboardNotification` group, its binding and the
`keyboardNotificationMs` shorthand), nothing else.
meta#8176's head merges meta#8076's 4adaf8b (main 61480b5 with meta#8257). No
conflicts; `timing.ts` is meta#8176's, with meta#8484's keyboard export.
…reader

architecture:react-component-runtime INV11 allows the keyboard module at
most one layout read per keyboard change. `getMobileKeyboardHeight`
measured again on every call, and the layout guide, each component's
`useKeyboardHeight` and ChatLayout's commit all call it after the module
has already measured the report: one show read the visual viewport's
height 6 times in Full AI Chat in Chromium, 13 times in the module's test
with the guide, a listener and two hooks.

While the module listens, `getMobileKeyboardHeight` returns the height it
measured at the last report. Every change of the visual viewport or the
window is a report, so a reader between two reports still sees the current
height; with nothing listening yet, it measures.

Tests: the module's test counts one read for a report with the guide, a
listener and two hooks reading the height (red before: 13), and an
interaction test counts one read for a show in Full AI Chat (red on the
previous build: 6).
…is focused without the reveal

spec:AST-099 FR4 says every route into the composer's text, a tap
included, focuses it without scrolling the page. The tap route claimed only
contenteditable text: a `<textarea>` or `<input>` passed as ChatComposer's
input (its documented plain textarea) was left to the browser, and with
nothing focused no blur reaches the focus takeover, so Safari revealed the
field by scrolling the document.

The tap route now claims an unfocused text control in the composer that
brings up a keyboard, and delivers it as it delivers contenteditable text:
focused with `preventScroll`, with the caret where the standard
`caretPositionFromPoint` maps the point inside the control, or after its
text where the browser maps none. A focused control keeps its own taps (the
browser moves the caret); a checkbox or an `inputmode="none"` field is never
claimed.

Tests (useChatKeyboard.test.tsx): ChatLayout with a textarea composer,
under a coarse pointer, cancels the tap's default and focuses the textarea
with `preventScroll` (red before: the browser's own focus); the claim takes
an unfocused text control and leaves a focused one, a checkbox and an
`inputmode="none"` field; the caret lands at the mapped offset, or after
the text.
…d Chrome it moves once reported

spec:AST-099 FR8 asks a ride within one frame of the keyboard on the
measured keyboards, with Android emulator evidence against Gboard; Firefox
for Android has FR2–FR5 only, and reports the keyboard once it is up. This
branch plays UIKit's keyboard spring from the browser's report there, and
times no ride to Gboard. The docs said the composer rides in step in
Firefox for Android and rides the keyboard in Chrome on Android.

ChatLayout's docs (English, Chinese, dense), the browser-support guide and
the changeset now say: in Safari on iOS the composer rides on top of the
keyboard in step with it; in Firefox for Android and Chrome on Android it
moves above the keyboard once the browser reports it; under Chrome's
`interactive-widget=resizes-content` it stays above it without moving.
…ard module

# Conflicts:
#	packages/core/src/BottomSheet/BottomSheetPanel.tsx
Her af953bd merges meta#8076's bd9246d (main 2af4d30, with meta#8229's
Popover presentation) into this branch, with her resolution of
BottomSheetPanel.tsx against meta#8229. No conflicts with this branch's
commits since 2d520a6: the result is her head plus those commits (the
keyboard-height Effect's INV3 comment, one layout read per keyboard
change, the text control tap route, and the Safari/Firefox/Chrome docs).

`timing.ts` checked: meta#8076's with exactly meta#8484's change.
meta#8176's local head reads the layout once per keyboard change, focuses a
text control composer tapped with nothing focused without the reveal, says
the composer moves above the keyboard once reported in Firefox and Chrome
for Android, and merges the maintainer's af953bd (meta#8076's bd9246d,
main 2af4d30 with meta#8229). No conflicts; `timing.ts` is meta#8176's.
…ides in step

meta#8176 says the composer moves above the keyboard once Chrome reports it,
since it times no ride to Gboard (spec:AST-099 FR8 asks a ride within one
frame of the keyboard, with Android emulator evidence). This branch times
the ride to Gboard's measured slides, with that evidence: ChatLayout's docs
(English, Chinese, dense) and the browser-support guide say the composer
rides Gboard's slides in step in Chrome on Android, while a Tall
BottomSheet makes its room once Chrome reports the keyboard. Firefox for
Android keeps meta#8176's wording: it moves once Firefox reports the keyboard.
@vjeux

vjeux commented Oct 26, 2026

Copy link
Copy Markdown
Contributor Author

On the scope item: this branch lands after #8176 and the touch foundation under it; once those land, it carries only the Chrome work's 18 files.

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes are needed before this can be approved: the Chrome-on-Android ride itself follows the current spec:AST-099 FR8 and DEC-4, but the pull request still contains the content of fifteen open predecessor pull requests (about 29,900 authored lines), which their own reviews need to settle. Please rebase onto main once #8176 and the pull requests beneath it have landed, so that this pull request carries only the 20 files of the Chrome work.

[Automated review]

vjeux added a commit to vjeux/astryx that referenced this pull request Oct 26, 2026
… rides Gboard

spec:AST-099 FR8 asks Core's keyboard-aware surfaces to ride the keyboard
in Chrome on Android within one frame of it on the measured keyboards, and
DEC-2 rules out a one-step relayout; on its own this branch played UIKit's
keyboard spring from Chrome's report. meta#8287's Chrome work comes in here, so
the change conforms on its own: Gboard's measured slides
(`motion/android/keyboard.ts`), the keyboard model's Chrome profile, the
guide's schedule picked by the VirtualKeyboard capability, `rideKeyboard`,
and their tests. meta#8287 closes as folded in.

Conflicts, resolved:
- The browser-support guide takes meta#8287's Chrome sentence (the composer
  rides the keyboard as Gboard slides it), with this branch's correction
  that a Tall BottomSheet adds its scroll room once Chrome reports the
  keyboard, and that under `resizes-content` nothing changes.
- The changesets: ChatLayout's says the dock rides in step in Safari on
  iOS and in Chrome on Android (Gboard's measured slides) and moves once
  reported in Firefox for Android; meta#8287's own changeset folds into it and
  is removed. The browser-support changeset says the same for Chrome.
vjeux added a commit to vjeux/astryx that referenced this pull request Oct 26, 2026
meta#8076's head folds main through meta#8055's landing (the compositor
animator) and is a fast-forward of the 0d1a829 meta#8287 brought in. It
merges cleanly with main dbcfc30, which this branch's previous base
no longer did. No conflicts; `timing.ts` is meta#8076's with exactly meta#8484's
change.
@vjeux

vjeux commented Oct 26, 2026

Copy link
Copy Markdown
Contributor Author

Folded into #8176: spec:AST-099 FR8 and DEC-2 can't be met in Chrome without this Gboard timing, so the two land as one there.

@vjeux vjeux closed this Oct 26, 2026
@vjeux
vjeux deleted the uikit/c08-keyboard-android branch October 26, 2026 13:03
vjeux added a commit to vjeux/astryx that referenced this pull request Oct 26, 2026
meta#8176 now carries meta#8287's Chrome-on-Android timing (Gboard's measured
slides), the keyboard module's shared reveal-pan watch (a browser's own
pan to the focused composer put back, which Chrome 124 animated by the
keyboard's whole height), the scrollbar thickness measured once, and
meta#8076's 2267ee5 (main through meta#8055's landing).

Conflicts, resolved as the union of both sides:
- keyboard/index.ts and useMobileKeyboard.ts headers and exports:
  rideKeyboard, keyboardProfileOf and watchRevealPan beside
  observeKeyboardDismiss and blurAsDismissal; the README's file table
  lists rideKeyboard.ts and keyboardDismiss.ts.
- useChatKeyboard.ts: the header names both; the reveal-pan watch uses
  this branch's `isTouch` (one touch-presentation read).
- ChatLayout.doc.mjs (EN, ZH, dense): meta#8176's ride sentence (in step in
  Safari and in Chrome on Gboard, once reported in Firefox) with this
  branch's drag sentence, and no drag dismissing under
  `resizes-content`.
- ChatLayoutKeyboard spec: `openChat` takes both `android` and `args`.

`timing.ts` is meta#8176's, with meta#8484's keyboard export.

This branch was successfully deployed

1 active deployment
Preview — 7d09c3c7 Deployed Oct 26, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Meta Open Source bot. needs:design-review Affects visuals — Design should review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants