Skip to content

test(core): gesture browser proof in Chromium and WebKit, and the README - #8074

Closed
vjeux wants to merge 149 commits into
meta:mainfrom
vjeux:wip/uikit-pr-g2d
Closed

vjeux wants to merge 149 commits into
meta:mainfrom
vjeux:wip/uikit-pr-g2d

Conversation

@vjeux

@vjeux vjeux commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Depends on #8073 (and its predecessors); review this PR's own commits (the merges bring main and the stack in).

Primary intent

Prove the gesture system in real browser engines and document it: the same touch scripts run in Chromium and WebKit, and gestures/README.md gives component authors the API, UIKit semantics, the browser negotiation and the web differences.

User or maintainer impact

jsdom cannot show what a browser does with a cancelled touchmove, a touch-action value or a native pinch, and migrating components need a document.

Problem and solution fit

Negotiation lives in the browser, outside jsdom, so a regression ships as scroll-jank, ghost clicks or lost zoom, which touch users notice first.

Deltas:

  • __tests__/browser/:
    • a fixture: a horizontal pan over a scroller, a tap, a long press, a pinch with a pan, a sheet body, a passive control, upward cards (early claim on, off);
    • a driver per engine (CDP touches in Chromium, WebKit's own touch input);
    • both engines in one config (WebKit in the official Playwright image), and a standalone fixture build for Mobile Safari.
  • README.md: recognizers, the pan's UIKit numbers, the arena, press profiles and paint, what the browser is told (including the early claim), native scrolling, the event clock, velocity, haptics, the touch presentation (feat(core): one touch presentation resolver #8062), known limits, web differences.

Authority

spec:AST-094 FR9–FR23 (current); FR14: a drag never changes hands midway. The README describes the system in the present tense and cites the constants table for every number.

Change and evidence

Both engines check:

  • touch-action per element;
  • a horizontal drag claimed over a vertical scroller (no scroll, no click), a vertical one left to it;
  • a tap recognizer replacing the click (no compatibility mouse events);
  • a scroll-edge pull at the top; a pinch on its element, with its pan;
  • a second finger never breaking a claimed pan;
  • a passive pan-y control keeping its press sideways;
  • a one-way overlay pan keeping its direction from the page (Chromium);
  • blocking touchmove listeners only where a recognizer claims a moving touch;
  • a long press at exactly touch-down + 500 ms despite a blocked thread.

Chromium also checks that a drag begun mid-content stays the content's to its top, the sheet never beginning (FR14), and page pinch-zoom, which Linux WebKit cannot drive. The fixture passed in Mobile Safari 26.2 (iOS 26.2 simulator), native scroll and pinch included.

Scope

  • This PR has one primary intent.
  • Independently removable deltas were removed or split.
  • Public text and artifacts contain no internal Meta context.

Testing

  • The second commit answers two review points on the gesture hooks, with failing-first tests in useGestureRecognizer.test.tsx:

    • turning isEnabled off mid-gesture ends the gesture without calling the listener from the layout Effect (architecture:react-component-runtime INV2); a handler that calls recognizer.setEnabled(false) itself still hears cancelled, from that handler;
    • usePressTracker applies a changed trackingExpansion, highlightDelay, cancelTravel or quickTapFlash from the next touch, and forwards the caller's isBraking.
    • Gestures and hooks: 674 tests passed; tsc and strict ESLint clean.
  • pnpm exec playwright test --config packages/core/src/gestures/__tests__/browser/playwright.config.ts --project chromium: 13 passed.

  • WebKit in mcr.microsoft.com/playwright:v1.61.1-noble, same config, --project webkit: 10 passed, 3 skipped.

  • pnpm exec vitest run --project ui packages/core/src/gestures packages/core/src/motion: 318 tests passed.

  • pnpm check:changesets, changeset coverage: clean.

  • Size: 6 files, 1,321 lines added.

Stack (merge in this order): #8052 spec → #8053 constants table → #8058 gesture core → #8059 tap and long press → #8060 pan → #8061 other recognizers → #8054 math → #8055 animator → #8056 animator curves → #8067 useAnimator → #8071 browser negotiation → #8072 gesture host → #8073 press tracking → #8074 browser proof and README.

vjeux added 4 commits October 23, 2026 20:53
Springs, scroll physics, timing curves, navigation values and gesture
thresholds for the touch presentations of core components, each with its
source (Apple documentation, a WWDC session, WebKit's UIKit-matched source,
or a measurement of UIKit on the iOS 26.2 simulator) or labelled a guess
with its measurement task. A test enforces the source rules on every domain
file, in their JSDoc and typed forms, and checks the list of simulator
measurements still owed.
…rena

An internal gesture system for touch presentations, not exported from the
package. GestureRecognizer carries UIKit's states and allowed transitions,
discrete and continuous recognizers, require-to-fail, simultaneous
recognition on request, shouldBegin and shouldReceiveTouch,
cancelsTouchesInView and the touch delays. GestureArena settles each touch
sequence without the DOM: participants, resolution order, claims, delivery
to the view, click dispositions (allow, suppress, defer and replay) and
timers as deadlines on the event clock. Its rules are tested with scripted
recognizers, independent of any real one.
…sholds

Tap recognizes at the lift within 45 pt of movement, taps 0.35 s and 45 pt
apart, and a 1.5 s hold; long press begins at exactly touch-down + 0.5 s on
the event clock within 10 pt, even when the main thread was blocked, and a
zero duration begins on the touch itself. The arena tests with these real
recognizers cover tap against double tap, delegate requirements, deferred
clicks and cancellation.
@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 28, 2026 6:07pm 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 28, 2026, 6:21 PM UTC

cixzhang and others added 10 commits October 26, 2026 00:01
From CodeBunny's review at 5b7d1eb (spec:AST-094 FR23): with the hidden
switch taking a label's activation, a tap on the label of a disabled
control still toggled the switch (playing the haptic) and, since the
disabled control cannot take focus, left focus on the aria-hidden switch.
The switch's click is now cancelled for a disabled or aria-disabled
control, and its focus always moves on: to the control when it can take
it, else back where it was, else nowhere. The new test fails on the
previous head.
…hing is attached

From CodeBunny on meta#8073 (spec:AST-094 FR24), fixed once here: the
(pointer: coarse) change listener was added when the host was created,
on a fine pointer too, and never removed. It is now added with the first
attachment and removed when the last one goes, so nothing touch-related
remains after the last instance unmounts. The test fails on the previous
head (a listener at creation, none removed).
…t syncs, and the stale headers and changesets match the code

- useGestureRecognizer and usePressTracker re-attach when the element behind
  the ref is replaced and detach when it goes, checked after every render at
  no render cost (architecture:react-component-runtime INV6); each Effect
  names the system it syncs (INV3).
- A control's profile releases on the deselect clock the shared press styles
  play, so a painter's `fading` never outlasts the fade it shows; UIButton's
  250 ms arrives with a control's own release layer.
- PressTracker's and GestureHost's headers describe the cell's delay inside
  scroll views only, and no delegated consumers or scroll handoff.
- One changeset for the press tracking, the one press writer and the hooks.

Co-authored-by: Christopher Chedeau <vjeux@users.noreply.github.com>
…ing is attached) into UIControl press tracking

The host has no delegated consumers at this layer, so the listener follows
the registry alone.
…egan-elsewhere; a recognizer's own scroll is not the browser taking the finger

From CodeBunny on meta#8195, fixed once here where the host enters:
- spec:AST-094 FR28: a touch with no recognizer or consumer on its path
  never reached the arena, so a recognizer tracking a finger was never
  told about a second finger beside it (a menu press could not end on
  one). GestureArena.untrackedBegan reports such a touch to recognizers
  that are tracking touches; it creates no record, claim or lock, and its
  moves and end stay the page's.
- spec:AST-094 FR30: a recognizer that scrolls a scroller itself (a menu's
  edge autoscroll) was cancelled by its own write. The internal
  GestureHost.ownScroll(scroller) makes onScroll skip that scroller until
  two frames pass; a real pan in that window still reports itself through
  pointercancel or touchcancel.
Three tests; two fail on the previous head.
… FR30 ownScroll) into UIControl press tracking
…s a dependency

useGestureRecognizer.ts: meta#8073's node attachment (a disabled gesture is
not attached, a replaced node re-attaches), its host listener sync and the
scroll-brake default, plus meta#8074's own changes from c248459: the
isEnabled sync keeps the listener quiet (architecture:react-component-runtime
INV2) and a change to the press tracker's four build options rebuilds it
from the next touch. The tests keep both sides' cases.

@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.

Requesting changes. The browser proof and the README are a useful base, and the disabled-recognizer and fixed-bar issues from the last round are fixed, but this branch's diff against main still carries the four open pull requests beneath it (#8061, #8071, #8072, #8073), and two parts disagree with the current specs. Please land the predecessors first so this pull request is reviewed as its own commits, and:

  • Conform the host's uncancelable-touchmove rule (GestureHost.ts) to spec:AST-094 FR20, or have an engineering owner amend FR20 on main to the narrower rule first.
  • Rewrite the README passages that no longer match the code: the velocity strategies (spec:AST-100 FR2), the removed installPressAttribute / usePressAttribute, the press profiles, axisRatio, the one-edge pan's touch-action and the 744 px compact width (spec:AST-012 FR9), and fix the fixture header's spec name.

[Automated review]


- `control` (a button, a switch): the highlight comes on in the same frame
outside a scroller, the finger is tracked 70 pt beyond the bounds, and
the release takes UIButton's 0.25 s;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Both profiles release on timing.press.highlightOutMs, the 200 ms press fade (PressTracker.ts:120-141), not UIButton's 0.25 s; the header comment there says a control's own 250 ms release is still to come.

outside a scroller, the finger is tracked 70 pt beyond the bounds, and
the release takes UIButton's 0.25 s;
- `cell` (a table or collection row, always in a scroll view in UIKit): the
highlight always waits the content-touch delay, travel past the pan

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The cell profile is highlightDelay: 'inScrollers': it highlights at once outside a scroll view and waits the content-touch delay only inside one, as spec:AST-094 FR11 requires ("the 150 ms delay applies only inside scroll views"). "Always waits" no longer describes it.

### Rows and cards: delegated press tracking

A list of a thousand rows cannot pay for a hook and four listeners per row.
`installPressAttribute(document)` (or the `usePressAttribute()` hook) finds

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

installPressAttribute, usePressAttribute and __tests__/delegatedPress.test.ts (line 400) do not exist at this head: the delegated consumer was removed. This section, and the attribute-pair warning at the end of it, can go or be rewritten around paintPress.

overshoot. A dragged surface does not jump. `totalTranslation()` is the
movement since the fingers landed, for a surface that wants it.
- `axisRatio` (for a pan limited to an axis): the movement along the axis
must exceed `axisRatio` times the movement across it to begin; movement

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

An axis-limited pan that is still between axisRatio's lines at its 10 px begin fails (PanGestureRecognizer.ts:329-334), as spec:AST-094 FR19 requires a pan that competes with native scrolling to decide within 10 px; it no longer waits.

| Recognizers on the element | `touch-action` |
| -------------------------------------------- | ------------------- |
| tap, long press | `manipulation` |
| horizontal pan or swipe, horizontal edge pan | `pan-y pinch-zoom` |

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

A screen-edge pan from one edge (the default ['leading']) leaves touch-action as is (ScreenEdgePanGestureRecognizer.ts:63-66, browserNegotiation.test.ts:62), as FR18 asks for a one-way pan; only pans from several horizontal edges get pan-y pinch-zoom.

vjeux added 5 commits October 28, 2026 10:39
Main merged meta#8045 and meta#8046. The one conflict is utils/pressFeedback.ts:
it takes main's merged meta#8046 text (the finger and button slots, the mouse
and key press) and keeps this pull request's own additions, the one
writer of the press attribute (writePress, paintPress, readPressPaint)
and the header line naming them.
Main's button slot, merged with meta#8046, ends a press's release at once
when the next press starts, by clearing the press attribute directly. On
a row another painter holds, that clear erased the hold, which this pull
request's one writer refuses. A mouse press on a held row, released, then
a press elsewhere must leave the row held. Fails until the early end goes
through the one writer.
finish() writes none through writePress, as this pull request's
controller paints, so a held row stays its owner's when the next press
ends a release early, under the finger and the button slots alike.
meta#8073 merged main 70ea859, where meta#8045 and meta#8046 landed, and keeps its
one writer of the press attribute on main's press controller. Taking that
head resolves this pull request's conflict with main in
utils/pressFeedback.ts with no change of its own.

@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.

Requesting changes: the browser proof and README are a useful base and all 13 Chromium checks pass, but the gesture host they prove no longer matches the current spec:AST-094 on main, and the diff still carries the four open pull requests beneath it.

  • GestureHost.ts listens only where the primary pointer is coarse, while FR43 and architecture:react-component-runtime INV13 have gestures attach for every pointer, so a mouse, trackpad or desktop pen gets no gesture.
  • The host writes touch-action over the author's value (FR18, DEC-8), lets any scroll under a still finger cancel its tap (FR20, DEC-8), and counts only a finger's first uncancelable touchmove (FR20), and the screen-edge pan starts at 15 px on elements wider than the edge band (DEC-22).
  • The README's velocity, compact-size, press-profile and delegated-press passages describe behavior the code, spec:AST-100 and spec:AST-012 do not have.

Please conform these, or have an engineering owner amend FR20 or FR43 on main first, and land #8061, #8071, #8072 and #8073 so this diff is its own nine files.

[Automated review]

}

private shouldListen(): boolean {
if (!this.isPrimaryPointerCoarse()) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Current spec:AST-094 FR43 has gestures attach for every pointer, "not only a coarse primary pointer", and architecture:react-component-runtime INV13 says the host attaches "with no touch-only query; under a fine pointer only a component that uses a gesture listens". With this gate a mouse, trackpad or desktop pen never reaches a recognizer (a mouse drag and a hover produce no transition in Chromium).

element.style.getPropertyValue(property),
);
}
element.style.setProperty(property, value);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

FR18 (DEC-8): "The derived value is intersected with the element's own author touch-action and is never wider than it". This writes the derived value over the author's, so touch-action: none plus a horizontal pan computes pan-y pinch-zoom.

}
}
if (taken.length > 0) {
this.arena.browserTookOver(taken, time);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

FR20 (DEC-8) counts a native scroll as the browser taking the touch "only once that finger has moved the scroll take distance" (10 px). Here any scroll above an unclaimed touch takes it at any travel, so a 1 px scroll under a still finger fails its tap.

constructor(options: ScreenEdgePanGestureRecognizerOptions = {}) {
super({
maximumNumberOfTouches: 1,
hysteresis: UIKIT_SCREEN_EDGE_PAN.hysteresis.value,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

FR18 and DEC-22 keep the 15 px start only for an element within the 40.2 px band; on a wider element an in-band touch decides at 10 px. This hysteresis applies to every element (a full-width element's edge pan begins at 15 px).

first), and the velocity is 0.2 times the newest one plus 0.8 times the
one before. It reproduces every value UIKit reported on six recorded touch
streams. Like UIKit's, it reacts a frame late to an acceleration, includes
a hold before the first move, and never decays: a finger at rest sends no

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

VelocityTracker has one filter that releases at 0 after the stillness interval, as spec:AST-100 FR2 and DEC-1 require; the four strategies and the "never decays" reading described here do not exist.

### Rows and cards: delegated press tracking

A list of a thousand rows cannot pay for a hook and four listeners per row.
`installPressAttribute(document)` (or the `usePressAttribute()` hook) finds

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

installPressAttribute and usePressAttribute do not exist at this head (the delegated consumer was removed), so this section documents a call that does not compile.

@vjeux

vjeux commented Oct 28, 2026

Copy link
Copy Markdown
Contributor Author

Closing: carried by #8360 — 398d4eb, c248459 and a272650 by hash, and the interaction-lane wiring and the early-claim cleanup by content; #8360 merges first in the maintainer's order.

@vjeux vjeux closed this Oct 28, 2026
vjeux added a commit to vjeux/astryx that referenced this pull request Oct 28, 2026
Takes meta#8360's head, which merges first in her order and is the
reconciled gesture foundation (meta#8076, meta#8073, meta#8446 and meta#8074). Two of its
test files, the presented hit-testing fixture and the animator browser
spec, name the internal animator createAnimator; this pull request
renamed it createMotionAnimator, so they take that name here. Without
it, main would stop type-checking when this lands after meta#8360.
vjeux added a commit to vjeux/astryx that referenced this pull request Oct 30, 2026
meta#8360's change on the internal touch foundation: one click guard shared
by the menu press and the gesture host (its click listener stays once
the menu press arms it, as its record has it, and lasts only while armed
for the gesture host, FR24), the host as the one touch owner (finger
scroll, page holds, attachments, the press tracker hook and a held
control's kept touch, the long press's single source, presented hit
testing on iOS WebKit), and the gesture browser proof in Chromium and
WebKit with the README. It carries meta#8074, meta#8270, meta#8293, meta#8315, meta#8320 and
meta#8356, and folds in meta#8358 (the long press's tolerance comment and an
unused import). Rebased onto section 2 as one commit; the touch-action
rule now comes with meta#8071, and meta#8072's claimed-touchend fix, which this
line never had, comes back with meta#8309.

The animator's busy proof is judged against the harness's compositor-only
control (meta#8906), with a main-thread mutant as its negative, and the
spring's curve is placed against the control's own clock, so the late
frames a loaded runner sends fail nothing.

With the host serving every pointer (meta#8071), this change's own pieces
follow it: a press tracker takes a mouse press as a press, painting at
once and braking no scroll (FR11); a finger's or a pen's hold that does
something keeps text unselected under any primary pointer, while a mouse
keeps its released behavior (FR41); a claimed pen's stylus touchend is
cancelled as a finger's (FR21); and `perEventPointer`, from before FR43,
changes nothing. These come from meta#8665, which keeps only the undecided
rule for text selection during a mouse or pen drag.

The hover recognizer and the host's hover routing land with hover's
first user.

Co-authored-by: Cindy Zhang <cindyxz@meta.com>
vjeux added a commit to vjeux/astryx that referenced this pull request Oct 31, 2026
meta#8360's change on the internal touch foundation: one click guard shared
by the menu press and the gesture host (its click listener stays once
the menu press arms it, as its record has it, and lasts only while armed
for the gesture host, FR24), the host as the one touch owner (finger
scroll, page holds, attachments, the press tracker hook and a held
control's kept touch, the long press's single source, presented hit
testing on iOS WebKit), its cases in the gesture browser proof (Chromium
and WebKit) and the README. It carries meta#8074, meta#8270, meta#8293, meta#8315, meta#8320
and meta#8356, and folds in meta#8358 (the long press's tolerance comment and an
unused import). Rebased onto section 2 as one commit; the touch-action
rule now comes with meta#8071, and meta#8072's claimed-touchend fix, which this
line never had, comes back with meta#8309.

The animator's busy proof is judged against the harness's compositor-only
control (meta#8906), with a main-thread mutant as its negative, and the
spring's curve is placed against the control's own clock, so the late
frames a loaded runner sends fail nothing.

With the host serving every pointer (meta#8071), this change's own pieces
follow it: a press tracker takes a mouse press as a press, painting at
once and braking no scroll (FR11); a finger's or a pen's hold that does
something keeps text unselected under any primary pointer, while a mouse
keeps its released behavior (FR41); and `perEventPointer`, from before
FR43, changes nothing. These come from meta#8665, which keeps only the
undecided rule for text selection during a mouse or pen drag.

The hover recognizer and the host's hover routing land with hover's
first user.

Co-authored-by: Cindy Zhang <cindyxz@meta.com>
vjeux added a commit to vjeux/astryx that referenced this pull request Oct 31, 2026
meta#8360's change on the internal touch foundation: one click guard shared
by the menu press and the gesture host (its click listener stays once
the menu press arms it, as its record has it, and lasts only while armed
for the gesture host, FR24), the host as the one touch owner (finger
scroll, page holds, attachments, the press tracker hook and a held
control's kept touch, the long press's single source, presented hit
testing on iOS WebKit), its cases in the gesture browser proof (Chromium
and WebKit) and the README. It carries meta#8074, meta#8270, meta#8293, meta#8315, meta#8320
and meta#8356, and folds in meta#8358 (the long press's tolerance comment and an
unused import). Rebased onto main after section 2 as one commit; the
touch-action rule came with meta#8071. Where fingers come from their pointer
events, a claimed finger's touchend is cancelled from its element
(FR21), under a fine primary pointer wherever the browser sends Touch
Events and inside an open shadow root too.

The animator's busy proof is judged against the harness's compositor-only
control (meta#8906), with a main-thread mutant as its negative, and the
spring's curve is placed against the control's own clock, so the late
frames a loaded runner sends fail nothing.

With the host serving every pointer (meta#8071), this change's own pieces
follow it: a press tracker takes a mouse press as a press, painting at
once and braking no scroll (FR11); a finger's or a pen's hold that does
something keeps text unselected under any primary pointer, while a mouse
keeps its released behavior (FR41); and `perEventPointer`, from before
FR43, changes nothing. These come from meta#8665, which keeps only the
undecided rule for text selection during a mouse or pen drag.

The hover recognizer and the host's hover routing land with hover's
first user.

Co-authored-by: Cindy Zhang <cindyxz@meta.com>
vjeux added a commit to vjeux/astryx that referenced this pull request Oct 31, 2026
meta#8360's change on the internal touch foundation: one click guard shared
by the menu press and the gesture host (its click listener stays once
the menu press arms it, as its record has it, and lasts only while armed
for the gesture host, FR24), the host as the one touch owner (finger
scroll, page holds, attachments, the press tracker hook and a held
control's kept touch, the long press's single source, presented hit
testing on iOS WebKit), its cases in the gesture browser proof (Chromium
and WebKit) and the README. It carries meta#8074, meta#8270, meta#8293, meta#8315, meta#8320
and meta#8356, and folds in meta#8358 (the long press's tolerance comment and an
unused import). Rebased onto main after section 2 as one commit; the
touch-action rule came with meta#8071. Where fingers come from their pointer
events, a claimed finger's touchend is cancelled from its element
(FR21), under a fine primary pointer wherever the browser sends Touch
Events and inside an open shadow root too; at most eight such lifts wait
for their touchend, and each goes after 500 ms or with the host (FR24).

The animator's busy proof is judged against the harness's compositor-only
control (meta#8906), with a main-thread mutant as its negative, and the
spring's curve is placed against the control's own clock, so the late
frames a loaded runner sends fail nothing.

With the host serving every pointer (meta#8071), this change's own pieces
follow it: a press tracker takes a mouse press as a press, painting at
once and braking no scroll (FR11); a finger's or a pen's hold that does
something keeps text unselected under any primary pointer, while a mouse
keeps its released behavior (FR41); and `perEventPointer`, from before
FR43, changes nothing. These come from meta#8665, which keeps only the
undecided rule for text selection during a mouse or pen drag.

The hover recognizer and the host's hover routing land with hover's
first user.

Co-authored-by: Cindy Zhang <cindyxz@meta.com>
vjeux added a commit to vjeux/astryx that referenced this pull request Oct 31, 2026
meta#8360's change on the internal touch foundation: one click guard shared
by the menu press and the gesture host (its click listener stays once
the menu press arms it, as its record has it, and lasts only while armed
for the gesture host, FR24), the host as the one touch owner (finger
scroll, page holds, attachments, the press tracker hook and a held
control's kept touch, the long press's single source, presented hit
testing on iOS WebKit), its cases in the gesture browser proof (Chromium
and WebKit) and the README. It carries meta#8074, meta#8270, meta#8293, meta#8315, meta#8320
and meta#8356, and folds in meta#8358 (the long press's tolerance comment and an
unused import). Rebased onto main after section 2 as one commit; the
touch-action rule came with meta#8071. Where fingers come from their pointer
events, a claimed finger's touchend is cancelled from its element
(FR21), under a fine primary pointer wherever the browser sends Touch
Events and inside an open shadow root too; at most eight such lifts wait
for their touchend, and each goes after 500 ms or with the host (FR24).

The animator's busy proof is judged against the harness's compositor-only
control (meta#8906), with a main-thread mutant as its negative, and the
spring's curve is placed against the control's own clock, so the late
frames a loaded runner sends fail nothing.

With the host serving every pointer (meta#8071), this change's own pieces
follow it: a press tracker takes a mouse press as a press, painting at
once and braking no scroll (FR11); a finger's or a pen's hold that does
something keeps text unselected under any primary pointer, while a mouse
keeps its released behavior (FR41); and `perEventPointer`, from before
FR43, changes nothing. These come from meta#8665, which keeps only the
undecided rule for text selection during a mouse or pen drag.

The hover recognizer and the host's hover routing land with hover's
first user.

Co-authored-by: Cindy Zhang <cindyxz@meta.com>
vjeux added a commit to vjeux/astryx that referenced this pull request Oct 31, 2026
meta#8360's change on the internal touch foundation: one click guard shared
by the menu press and the gesture host (its click listener stays once
the menu press arms it, as its record has it, and lasts only while armed
for the gesture host, FR24), the host as the one touch owner (finger
scroll, page holds, attachments, the press tracker hook and a held
control's kept touch, the long press's single source, presented hit
testing on iOS WebKit), its cases in the gesture browser proof (Chromium
and WebKit) and the README. It carries meta#8074, meta#8270, meta#8293, meta#8315, meta#8320
and meta#8356, and folds in meta#8358 (the long press's tolerance comment and an
unused import). Rebased onto main after section 2 as one commit; the
touch-action rule came with meta#8071. Where fingers come from their pointer
events, a claimed finger's touchend is cancelled from its element
(FR21), under a fine primary pointer wherever the browser sends Touch
Events and inside an open shadow root too; at most eight such lifts wait
for their touchend, and each goes once taken or after 500 ms, kept
across the host's uninstall since the touchend comes in a later task
(FR24).

The animator's busy proof is judged against the harness's compositor-only
control (meta#8906), with a main-thread mutant as its negative, and the
spring's curve is placed against the control's own clock, so the late
frames a loaded runner sends fail nothing.

With the host serving every pointer (meta#8071), this change's own pieces
follow it: a press tracker takes a mouse press as a press, painting at
once and braking no scroll (FR11); a finger's or a pen's hold that does
something keeps text unselected under any primary pointer, while a mouse
keeps its released behavior (FR41); and `perEventPointer`, from before
FR43, changes nothing. These come from meta#8665, which keeps only the
undecided rule for text selection during a mouse or pen drag.

The hover recognizer and the host's hover routing land with hover's
first user.

Co-authored-by: Cindy Zhang <cindyxz@meta.com>
vjeux added a commit to vjeux/astryx that referenced this pull request Oct 31, 2026
meta#8360's change on the internal touch foundation: one click guard shared
by the menu press and the gesture host (its click listener stays once
the menu press arms it, as its record has it, and lasts only while armed
for the gesture host, FR24), the host as the one touch owner (finger
scroll, page holds, attachments, the press tracker hook and a held
control's kept touch, the long press's single source, presented hit
testing on iOS WebKit), its cases in the gesture browser proof (Chromium
and WebKit) and the README. It carries meta#8074, meta#8270, meta#8293, meta#8315, meta#8320
and meta#8356, and folds in meta#8358 (the long press's tolerance comment and an
unused import). Rebased onto main after section 2 as one commit; the
touch-action rule came with meta#8071. Where fingers come from their pointer
events, a claimed finger's touchend is cancelled from its element
(FR21), under a fine primary pointer wherever the browser sends Touch
Events and inside an open shadow root too; at most eight such lifts wait
for their touchend, and each goes once taken or after 500 ms, kept
across the host's uninstall, which can run in the microtask after the
pointerup, before the touchend that comes later in the same task (FR24).
An armed press's page listeners go once the arena has decided its last
touch, a tap that waits on a double tap included, and after its last
detach the host holds no listener, note, frame or timer (FR24).

The animator's busy proof is judged against the harness's compositor-only
control (meta#8906), with a main-thread mutant as its negative, and the
spring's curve is placed against the control's own clock, so the late
frames a loaded runner sends fail nothing.

With the host serving every pointer (meta#8071), this change's own pieces
follow it: a press tracker takes a mouse press as a press, painting at
once and braking no scroll (FR11); a finger's or a pen's hold that does
something keeps text unselected under any primary pointer, while a mouse
keeps its released behavior (FR41); and `perEventPointer`, from before
FR43, changes nothing. These come from meta#8665, which keeps only the
undecided rule for text selection during a mouse or pen drag.

The hover recognizer and the host's hover routing land with hover's
first user.

Co-authored-by: Cindy Zhang <cindyxz@meta.com>

This branch was successfully deployed

1 active deployment
Preview — 1c90ed11 Deployed Oct 28, 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