@modoki/engine / index / InteractionHandle
Interface: InteractionHandle
One draggable/clickable handle in an authoring editor, in viewport CSS px.
Rendering
editor
editor:
string
Which editor produced it — 'collider2d' | 'dopesheet' | 'curves' | 'skin' | …
id
id:
string
Stable id UNIQUE across all providers — namespace it by editor+kind+index, e.g. 'collider:vert:0', 'dope🔑Transform.x:3', 'skin🦴root'. This is how drag-handle/tap-handle address it, so it must survive between a read and the immediately-following gesture (fine while the game is paused/authoring).
kind
kind:
string
Handle category — 'collider-vertex' | 'keyframe' | 'tangent' | 'bone-joint' | 'gizmo-axis' | 'slice-rect' | 'gradient-stop' | … — for filtering.
label?
optionallabel?:string
Optional human-readable label ('vertex 0', 'key t=0.5 Transform.x').
meta?
optionalmeta?:Record<string,unknown>
Optional provider-specific data (bone name, keyframe time/value, entity id) — passed through verbatim so Claude can pick the right handle semantically. By convention meta.disabled: true marks a control that is present but inert (a greyed-out Paste). That is DATA; a screenshot only offers a shade of grey.
occludedBy?
optionaloccludedBy?:string|null
Descriptor of whatever currently covers this handle's centre (a modal, an open menu, a scrim). Absent when nothing does, or when occlusion could not be checked. A trusted click hit-tests by coordinate, so an occluded handle CANNOT be clicked where it appears to be — the silent-miss class of bug, surfaced as data.
Filled in by computeHandles, not by providers: occlusion is a property of anything addressed by COORDINATE, which is every handle — not a DOM-chrome feature.
owner?
optionalowner?:unknown
The DOM element this handle lives in — its own element for chrome, the owning <canvas> for a Canvas2D/SVG editor's handle. Supplied so occlusion can be checked uniformly: an element is covered iff the topmost element at the handle's point is neither it nor a descendant.
NOT SERIALIZABLE. computeHandles strips it before the handle crosses the agent bridge as JSON. A provider that omits it gets no occlusion check (and is counted in occlusionUnchecked) rather than a silently wrong "not occluded".
rect?
optionalrect?:object
The handle's full box, not just its centre — so an agent can compute overlap and aim off-centre. Canvas handles are points and omit it; DOM chrome always has one.
Rendering
h
h:
number
w
w:
number
x
x:
number
y
y:
number
x
x:
number
Handle CENTRE in viewport CSS px (origin top-left) — the point to drag/click.
y
y:
number