Editor pointers
A tool is written once and driven by a mouse or by a hand. That is the whole point of this file: nothing downstream of it may name a mouse, a click, a screen coordinate or a DOM event.
VR is not the initial focus, but starting flat-only is the trap — a flat-only contract bakes mouse assumptions into every tool, and each one has to be found and removed later. So the contract is XR-shaped from the start and the flat pointer is simply its first implementation.
What a pointer is
interface EditorPointer {
ray(): EditorRay | null // far interaction: point at a thing
grip(): Vec3 | null // near interaction: reach out and hold it
active: boolean // trigger / primary button
secondary: boolean // squeeze / secondary button
}
Two ways to grab, because both are real: a hand inside a handle grabs it directly, and anything out of reach is grabbed by pointing. An ensemble is routinely larger than arm's reach, so near-grab alone would mean flying the world around to edit it; ray alone throws away the thing a headset is for.
Hands are symmetric until they aren't
There is no fixed "right hand is the tool". Whichever pointer starts a gesture is its PRIMARY, and whatever else is present becomes the helper for that gesture. That is what makes two-handed actions expressible later — dragging an object along a path with one hand while the other drops keyframes — without a left/right special case in every tool.