feat(plugins): v1.3 L2+L3+L4 — wheel, host pan/zoom, hover, position-patch channel#170
Merged
Merged
Conversation
…patch channel
L2 — wheel + host-owned pan/zoom:
- onWheel on VNodeSvg + VNodeBox (WheelEventPayload: deltaX/deltaY/x/y/ctrlKey,
coords in the surface space); high-frequency, gated on interaction.wheel.
- Non-passive wheel listeners (preventDefault) for interactive surfaces only.
- VNodeSvg.panZoom:'host' — host owns the viewBox: drag-pan + wheel-zoom mutate
the rendered svg locally (no worker round-trip), emitting ONE surface.transform
{x,y,scale} on settle (pointerup / 150ms wheel-idle debounce).
L3 — hover:
- onPointerEnter / onPointerLeave on circle/rect/svg/box (HoverEventPayload:
target/x/y); high-frequency, coalesced, gated on interaction.hover.
L4 — position-patch channel:
- worker:patchSvgPositions {viewId?,panelId?,patches:{id,x,y}[]} on WorkerToHost +
isWorkerToHost; subject to MAX_ENVELOPE_BYTES, host sanitises the patch shape.
- Host applies patches by mutating cx/cy on mounted circles + the connected edge
line endpoints (via data-node-id / data-edge-source/target), no React re-render.
- New SDK method ctx.patchSvgPositions(...).
Reuses L1's rAF coalescing: high-frequency dispatches now carry an interaction kind
so the host gates each on its own manifest sub-flag (pointer/wheel/hover); an unset
kind defaults to pointer (L1 behaviour). Shipped as one PR since all three layers
edit the same platform files. noteser-graph (G2/G3/G4) untouched.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Round 2 of the v1.3 interaction surface, building on the merged L1 (pointer events +
interactionmanifest opt-in + rAF coalescing + HF budget). Ships L2, L3, and L4 together because all three edit the same platform files (PluginVNode.tsx,PluginHost.ts,protocol.ts, the surface adapters) — splitting them would mean three PRs conflicting over the same code.L2 — wheel + host-owned pan/zoom (plan 2.2, 2.7 Cost 2)
onWheelonVNodeSvg+VNodeBox.WheelEventPayload { deltaX, deltaY, x, y, ctrlKey }; x/y in the surface coordinate space (svg user-space via inverse CTM, box element-local). High-frequency → rides L1's coalescing, gated oninteraction.wheel.preventDefaultcan stop page scroll; a plain svg/box attaches no wheel listener and scrolls normally.VNodeSvg.panZoom: 'host'— the host owns the viewBox transform: drag-pan (surface pointer) and wheel-zoom mutate the rendered<svg>directly (no worker round-trip, 60fps) and emit exactly ONEsurface.transform{ x, y, scale }on gesture settle (pointerup, or a 150ms wheel-idle debounce).surface.transformis a reserved event name on the existinghost:vnodeEventenvelope — not a new envelope.L3 — hover (plan 2.2, 2.3)
onPointerEnter/onPointerLeaveon circle/rect/svg/box.HoverEventPayload { target, x, y }. High-frequency → coalesced, gated oninteraction.hover.L4 — position-patch channel (plan 2.7 Cost 2, 2.8)
worker:patchSvgPositions { viewId?, panelId?, patches: {id,x,y}[] }on theWorkerToHostunion +isWorkerToHost. Subject toMAX_ENVELOPE_BYTES; the host sanitises the patch shape.cx/cyon mounted SVG circles + the matching endpoint of any connected edgeline(keyed viadata-node-id/data-edge-source/data-edge-target), with NO React re-render. New SDK methodctx.patchSvgPositions(...).Why ship together
All three are platform-layer changes to the same five files. The HF gate also had to grow from L1's single pointer flag into a per-kind gate (pointer/wheel/hover), which touches
sendVNodeEvent+ every surface adapter — a shared change all three depend on.Notes
interactionkind; an unset kind defaults topointerso L1 behaviour + tests are unchanged.public/plugins/noteser-graph/(G2/G3/G4) untouched — separate PR.docs/plugins-v1.3-impl-notes.md.Tests
npm run lintclean,tsc --noEmitzero errors, full Jest suite green (2869 passed, no flake).surface.transformfires once on pan settle + once after wheel-idle debounce,worker:patchSvgPositionsvalidate + oversized reject, host applies a patch to the mounted svg without remounting the React tree.🤖 Generated with Claude Code