fix(scroll): name what a scroll observed and read an edge off one tree - #2770
Conversation
Size Report
Startup median (7 runs, lower is better):
|
|
Reviewed at bd8464a. The refactor keeps behavior: One question before this goes to review. CI was still running when I looked, with no failures. This slice changes no route that is in use, so check whether a later Smoke or Integration failure exercises scroll before tying it to this PR. No device run is needed for this slice. |
bd8464a to
571d5cd
Compare
|
Reviewed at 571d5cd: this is the same patch as bd8464a after a rebase, and CI is green. The question from the bd8464a review is still open. Should Not blocking: the PR body says |
571d5cd to
a624ec1
Compare
|
Kept the split, and the reason is the one you allowed — stated explicitly: this layer is behavior-neutral, and the layer above it is behavior-bearing. Not "pure refactor vs the rest": Every new field and branch does have a real producer and reader, one layer up:
Two of those rows are new in this round: the hint consolidation (my PR was adding a third copy of that sentence, so it now owns one) and the swipe-midpoint reader, which moved out of the daemon into the file that already owns "what the leaf reported". Also note the layer count changed while addressing #2771: this is layer 1 of 4, with the observation module split out as #2776 to keep each PR under the budget. Validation is on the PR bodies at named commits. |
|
Reviewed a624ec1. The code is ready for review. This layer changes no behavior, and the no-progress hint strings are byte-identical to main. No conflicts. CI was still running with nothing failed. Please fix one claim in the PR body. Not blocking: |
bb4ae93 to
0e99f4d
Compare
|
This is ready at 0e99f4d: the guard now names what a scroll observed and reads the edge off one tree, fixing what the earlier pass on a624ec1 flagged. Not blocking: honoredScrollSwipeMidpoint and readScrollEdgeState still have no production importer outside tests, and the new "a horizontal scroll never claims a vertical edge" test calls the pure formatter directly rather than the scroll-runtime route, so landing this together with (or right before) the layer that wires movement into scrollResult, with a scroll-runtime-level test for the at-edge and horizontal cases, can be taken or left. Every check was still queued or in progress when I looked, and none had failed; since the diff touches scroll message formatting and the scroll --until import, which Smoke, Integration and Coverage exercise, a failure there needs its own explanation rather than being pinned on main. I did not run the tests or typecheck myself and judged the new test by reading the diff, and CI results were not in yet. CI needs to go green on 0e99f4d before this can merge. |
|
Both points taken, on The body claim was wrong and is corrected. On the at-edge branch accepting any direction: it should not, and it no longer does. Validation for this layer: related suites 146 files / 786 tests, 🤖 Generated with Apex by Callstack |
Two pieces the directional-scroll answer needs, with no behavior change on its own. `movement` is the vocabulary a scroll uses to say what happened to the surface: `moved`, `at-edge`, `unchanged` when the visible content did not change, and `unobserved` when the command could not compare anything. It is absent when the runtime cannot read a screen at all, so a caller never has to guess whether a missing claim means "did not move". `readScrollEdgeState` is the one pure door into the edge analyzer — the question `canScrollFurtherAtEdge` and `scrollSurfaceFingerprint` already ask, exposed with the frame of the container that answer was taken on. Without `containerRect`, `canScroll: false` cannot distinguish content that ends here from a tree that named no container, and a caller checking whether its gesture landed inside the list has nothing to check against.
0e99f4d to
a266c39
Compare
|
CI note: this layer's two Smoke failures both die at 🤖 Generated with Apex by Callstack |
What
Four behavior-neutral pieces the #2714 fix needs, extracted so the layer that changes an answer (#2771) can be reviewed against nothing else. Nothing in this PR changes an answer on any surface — no producer of any of it exists until the layers above.
movement(packages/contracts/src/scroll-command.ts) — the vocabulary for what a directional scroll saw:moved/at-edge/unchanged/unobserved, plus the result field. Absent is not the same claim asunobserved, and the type doc says so.honoredScrollSwipeMidpoint(same file) — where a leaf's swipe ran, next to thehonoredScrollPixels/honoredScrollDurationMsreaders it belongs with, because "what the leaf reported about its gesture" is one question with one owner. Declines to guess when the owner reported no coordinates (a tvOS scroll is a remote keypress).readScrollEdgeState+containerRect(packages/capture-kit/src/snapshot/scroll-edge-state.ts) — the analyzer's answer exposed as one pure door, widened withcontainerRect.canScrollFurtherAtEdgenow goes through it.captureScrollEdgeStatestill callsanalyzeScrollEdgeStatedirectly because it passes atarget, andscrollSurfaceFingerprintdoes so because it wants only the fingerprint: three callers, one analyzer, no second set of selection rules.containerRectanswers whatScrollEdgeStatecould not:canScroll: falsecannot distinguish this content ends here from the tree named no container at all.scrollNoProgressHint— the recovery hint that a scroll failure prints. Three copies existed by the time this feature needed a fourth (capture-kit'sscroll_edge_no_progress,scroll --until's, and the newscroll_no_progress); this consolidates them into one owner with the two axes that actually differ — whether the failing command has an--untilform to point at, and whether it can name where a raw drag would land. The three existing strings are reproduced byte-for-byte, which the passing tests show rather than assert.Why the split rather than one PR
Because the seam is behavior-neutral vs behavior-bearing, not "pure refactor vs the rest". Every line here is unreachable from the CLI:
movementhas no producer until #2776 computes it,containerRecthas no reader until #2776's edge verdict reads it,honoredScrollSwipeMidpointhas no caller until #2771's swipe evidence, and the hint owner has a third consumer the same day #2771 lands. #2776 and #2771 are where each field earns its producer and its reader, and the CI of those two layers is where a mistake in this one surfaces.Validation
verticalEdgeForis exported fromscroll-edge-state.tsnext toScrollEdge(it was duplicated verbatim inscroll-until.ts, and fix(scroll): observe the movement a directional scroll can claim #2776 needs it too), andformatScrollEdgeMessagenow reads the axis through it: a horizontal scroll can no longer be answered with "no hidden content above was detected" — it falls through to the distance it was asked for. Test pinned.pnpm check:affected --run→ all runnable checks passed at the stack head882256121; at this layer's own head (a266c3927) the related suites (146 files / 786 tests),tsc,oxlint,oxfmtandfalloware clean standalone.scroll-edge-state-read.test.ts(a resolved container reports the frame the decision was taken on; pair: no container and a zero-area scrollable report none; and the two doors pinned against each other —canScroll,containerRect,fingerprintidentical for one tree, so the extraction cannot fork a second truth);scroll-command.test.ts(midpoint from reported coordinates, pair: absent/partial/non-numeric coordinates); theformatScrollEdgeMessagemovement branches (an observed edge answers without a distance; pair:unobservedkeeps it, because the disclosure travels in the field).No device run is needed for this slice — it is unreachable until the layers above, and the live evidence for the feature is recorded on #2771.
🤖 Generated with Apex by Callstack