feat(framework): gesture layer over the touch snapshot#161
Draft
doodlewind wants to merge 1 commit into
Draft
Conversation
touches() was a stateless per-frame snapshot with exactly one consumer (the OSK's contact-edge tap). This adds framework/src/gesture.ts — contact lifecycles (down/move/up/cancel), per-contact history with deterministic release velocity, and recognizers for tap, long-press, and axis-lockable pan with UIKit-style claim semantics (a list pan cancels the row's press highlight). Steady state allocates nothing: 8 pooled tracks (the wire cap), Int16Array history rings, one flag byte per recognizer×slot. The pump runs in the frame handler between effect delivery and app frame hooks, so app code always observes the frame's completed gesture output; on hosts without touch (PSP) it costs two comparisons. The gesture layer never touches focus itself — input.ts grows three exports (setActiveNode, pressNode, hitNode) so touch, cursor, and d-pad share one authority over focus:/active: variants, and pushTouchBlock() mirrors pushButtonHandlerBlock for modals (opening the OSK cancels list gestures the same frame). New @pocketjs/framework/gesture subpath registered in all three maps (package.json exports, tsconfig paths, jsx-plugin FRAMEWORKS.solid.subpaths). Tests: 19 unit cases driving __setTouches/__runGestures directly — edges, slop, claim priority, hz-scaled long-press deadlines and velocities, block/ dispose cancellation. Sim byte-exact suites (cafe, im) stay green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This was referenced Jul 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part 1/6 of the touch-input infrastructure series (→ Vita YouTube with modern mobile UX, graceful PSP degradation).
What
framework/src/gesture.ts(new,@pocketjs/framework/gesture): turns the statelesstouches()snapshot into contact lifecycles with recognizers:Int16Arrayhistory, release velocity(x[t]−x[t−k])·hz/k— integer deltas + one IEEE division, bit-identical on every host.simulationHz()), axis-lockable pan with release velocity for flings.hitTestop withrectas both fallback (no op 27) and complement (ink misses inside the region); occluding ink outside the subtree wins.pushTouchBlock()— the touch mirror ofpushButtonHandlerBlock: synchronously cancels non-exempt in-flight contacts and mutes new downs (OSK modality).touches()always empty) the pump is two comparisons.input.ts:setActiveNode/pressNode/hitNodeexports so touch, cursor, and d-pad routefocus:/active:through one authority (no stranded pressed visuals).index.ts:__runGestures()between effect delivery and app frame hooks;resetGestures()on mount/dispose.FRAMEWORKS.solid.subpaths).Determinism
Pump order is fixed (after
__setTouches/__drainEffects, beforerunFrameHooks), velocities and deadlines are pure functions of the input tape andsimulationHz(). NoMath.pow/transcendentals anywhere.Tests
tests/gesture.test.ts: 19 cases — lifecycle edges, tap slop boundaries, single-fire priority, exact velocities at 60/30 Hz, long-press deadlines at 60/30 Hz, axis-lock rejection, claim ordering,pushTouchBlockcancel+exempt, dispose/cancel, 8-contact cap.tests/sim.test.ts,tests/im-sim.test.ts) green over the modified frame pump;bunx tsc --noEmitclean.Follow-ups in this series
kinetic scroller → tape v2 touch track → VirtualList → OSK touch model → scripted touch goldens.
🤖 Generated with Claude Code