Skip to content

feat(shaders): add scripted strokes and onPushEnd to a scene's cursor input - #248

Merged
hunterbecton merged 9 commits into
mainfrom
hunter/sha-229-add-scripted-strokes-and-onpushend-to-a-scenes-cursor-input
Oct 21, 2026
Merged

hunterbecton merged 9 commits into
mainfrom
hunter/sha-229-add-scripted-strokes-and-onpushend-to-a-scenes-cursor-input

Conversation

@hunterbecton

Copy link
Copy Markdown
Member

Why

The Reactive card (SHA-216) holds a smudge along a scripted path as its still frame, and lays it again as its arrow swoops in, so code has to stroke CursorSmudge with no pointer. On touch, the card runs its story from a scroll push's start to its end, and nothing reported the end. This is SHA-229.

What changes

Scripted strokes

<ShaderScene strokeAt={point}> takes an animation signal, such as a MotionValue, holding a point in 0..1 canvas units. Each frame strokes from where the point was to where it is, and a move wakes a parked scene. Attaching the point, resuming from a pause, and resizing each start a fresh stroke, so no segment spans a gap. CursorInput.setStrokePoint holds the point, which keeps the engine framework-free, and it works with or without scrollPush.

Precedence

While a point is attached, CursorSmudge and CursorRipple ignore the mouse and the touch push. Readers check isScripting() first. Chaining getScriptedStroke() ?? mouse would let the mouse through on every frame where an attached point holds still.

Push end

CursorInput.onPushEnd fires once for every onPushStart, however the contact ends. The tracker now also sees a pinch's last finger lift when that finger landed off the canvas. That case used to leave the contact open until the next touch.

Probes and docs

/dev/cursor-smudge-probe runs the card's J path through strokeAt, and /dev/touch-probe counts push starts and ends. The scroll-push spec checks that one drag gives one of each. The React API docs, the glossary's "scripted stroke", and a minor changeset ship with it.

Copilot AI balanced review requested due to automatic review settings October 15, 2026 10:41
@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

React Doctor found no new issues. 🎉

Reviewed by React Doctor for commit 77149df.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Buffered scripted smudge strokes are mishandled during resize and pointer boundary events.

2 open findings
What changed in this PR

Adds scripted cursor strokes and push-end notifications to support the Reactive card’s animation and touch interactions while keeping the input engine framework-free.

Changes:

  • Adds strokeAt signals with precedence over pointer and scroll input.
  • Adds push-end notifications and handles the last off-canvas pinch contact.
  • Extends tests, development probes, API documentation, and release notes.
File Description
packages/​shaders/​src/​react/​hooks/​use-cursor/​use-cursor.ts Exposes scripted stroke readers.
packages/​shaders/​src/​react/​components/​shader-scene/​shader-scene.tsx Connects stroke signals to scene lifecycle.
packages/​shaders/​src/​react/​components/​shader-scene/​shader-scene.test.tsx Tests stroke lifecycle and push completion.
packages/​shaders/​src/​inputs/​scroll-push/​scroll-push.ts Tracks contact completion and cancellation.
packages/​shaders/​src/​inputs/​cursor-input/​cursor-input.ts Implements scripted strokes and push-end subscriptions.
packages/​shaders/​src/​inputs/​cursor-input/​cursor-input.test.ts Tests input strokes and contact endings.
packages/​shaders/​src/​engine.ts Exports the stroke-point type.
packages/​shaders/​src/​components/​cursor-smudge/​shader.tsx Prioritizes scripted smudge strokes.
packages/​shaders/​src/​components/​cursor-smudge/​cursor-smudge.test.tsx Tests scripted smudging and precedence.
packages/​shaders/​src/​components/​cursor-ripple/​cursor-ripple.tsx Prioritizes scripted ripple strokes.
packages/​shaders/​src/​components/​cursor-ripple/​cursor-ripple.test.tsx Tests scripted ripples and precedence.
GLOSSARY.md Defines scripted strokes.
apps/​docs/​src/​app/​dev/​touch-probe/​push-recorder.tsx Records input push notifications.
apps/​docs/​src/​app/​dev/​touch-probe/​probe-scene.tsx Displays push-start and push-end counts.
apps/​docs/​src/​app/​dev/​cursor-smudge-probe/​probe-scene.tsx Adds a scripted J-path demonstration.
apps/​docs/​content/​docs/​react/​api.mdx Documents the new APIs.
apps/​docs-tests/​docs/​scroll-push.spec.ts Checks balanced push notifications.
.changeset/​scene-stroke-at.md Records the minor release additions.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread packages/shaders/src/components/cursor-smudge/shader.tsx
Comment thread packages/shaders/src/components/cursor-smudge/shader.tsx
@hunterbecton
hunterbecton added this pull request to stack #250 October 16, 2026 10:28
Copilot AI balanced review requested due to automatic review settings October 21, 2026 02:23
@hunterbecton
hunterbecton force-pushed the hunter/sha-229-add-scripted-strokes-and-onpushend-to-a-scenes-cursor-input branch from 399cd7a to 077b48f Compare October 21, 2026 02:23

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

A queued scripted smudge segment can survive a resize and redraw into the cleared field.

3 open issues

Review effort: Balanced

Comment thread packages/shaders/src/components/cursor-smudge/shader.tsx
Add a strokeAt prop to ShaderScene. It takes an animation signal, such as
a MotionValue, holding a point in 0..1 canvas units, and each frame strokes
the scene's cursor effects from where the point was to where it is.
Attaching the point, resuming from a pause, and resizing each start a fresh
stroke, so no segment spans a gap. A move wakes a parked scene. It works
with or without scrollPush.

While a point is attached, CursorSmudge ignores the mouse and the scroll
push. CursorInput holds the source through setStrokeSource, and readers
check isScripting and getScriptedStroke. Scripted strokes are SHA-229's
first phase. CursorRipple and onPushEnd follow.
Add a button, and the S key, that run the Reactive card's J path through
strokeAt, at the card's 0.85 s or at 3 s so a mouse can move over it.
CursorInput gains onPushEnd, which fires once for every onPushStart,
however the contact ends: the fling settles, every finger lifts after a
pinch, a new finger lands mid-fling, scroll push turns off, or the scene
resumes from a pause. Dispose ends a contact without telling anyone. The
push tracker ends a contact through the one reset every ending already
went through, so a start and its end always pair.
The signal useCursor returns gains getScriptedStroke and isScripting.
While the scene's strokeAt point is attached, CursorRipple stirs the
water along the scripted stroke and ignores the pointer and the push, as
CursorSmudge does.
The touch probe shows how many pushes the Input started and ended, and
adds the count to its report, so a tester can see every start get its
end on a real device. The scroll-push spec checks that one drag gives one
of each.
Add strokeAt to the ShaderScene props table, the scripted-stroke readers
to useCursor, and the push-start and push-end listeners to
useShaderContext. Define scripted stroke in the glossary, and add the
minor changeset.
GLOSSARY.md uses Source for a component that draws its own image, so
StrokeSource and setStrokeSource gave the word a second meaning in public
API. The prop and the docs already call it a point, so the type is now
StrokePoint and the method setStrokePoint.

Other review fixes ride along. The strokeAt doc states its default and
asks for the same signal on every render, because a new one restarts the
stroke. ShaderScene names the restart it does on a resume or a resize.
CursorInput's header covers scripted strokes, and PushStroke's doc covers
both kinds of stroke. New tests check that CursorSmudge and CursorRipple
ignore the touch push while a point is attached, and that swapping or
unmounting a strokeAt point moves or drops the subscription.
… on resize

CursorSmudge gathers a stroke on one frame and draws it on the next. A
pointerleave, pointercancel, or window blur between the two dropped a
gathered scripted segment. The Input had already moved past it, so the
scripted path got a gap. A resize between the two drew the old segment
into the cleared field when the scene redrew.

A pointer boundary now leaves a scripted segment alone. A resize drops
the gathered stroke before it clears the field.
Copilot AI balanced review requested due to automatic review settings October 21, 2026 09:44
@hunterbecton
hunterbecton force-pushed the hunter/sha-229-add-scripted-strokes-and-onpushend-to-a-scenes-cursor-input branch from 077b48f to 59a0c2d Compare October 21, 2026 09:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Zero-duration initial and post-resume ticks can permanently discard scripted movement before either effect draws it.

1 open issue
3 resolved since last review

Review effort: Balanced

Comment thread packages/shaders/src/react/components/shader-scene/shader-scene.tsx
A scheduler's first tick after a start or a resume has a delta of 0.
CursorInput read the scripted segment on that tick and moved its start
forward, but CursorSmudge and CursorRipple both drop a stroke with no
time in it. A move made before that tick was lost, and a point that then
held still never drew it.

CursorInput now reads no segment on a zero-delta tick, so the next tick
strokes the whole way. The scene asks for that next tick while a point
is attached, so a parked scene draws the move without waiting for the
point to move again.
Copilot AI balanced review requested due to automatic review settings October 21, 2026 10:17

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Cross-frame input and GPU timing across pause, resize, and idle transitions warrant browser-level validation and final human review.

No open issues 🎉

1 resolved since last review

Review effort: Balanced

@hunterbecton
hunterbecton merged commit 924fe6e into main Oct 21, 2026
12 checks passed
@hunterbecton
hunterbecton deleted the hunter/sha-229-add-scripted-strokes-and-onpushend-to-a-scenes-cursor-input branch October 21, 2026 13:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants