ExperimentalOptional ExperimentalactiveCSS cursor shown while THIS action's ongoing handle is in flight —
grabbing while panning, move while dragging a selection, crosshair
while pulling a marquee.
Separate from cursor because the two answer different questions:
cursor is a prediction ("a drag from here would pan"), this is a state
("you are panning"). An action can declare either, both, or neither;
with only cursor set, the hover hint holds for the duration of the
gesture.
This is where mid-gesture cursors live now. They used to come from the
tool side — ViewportToolDef.engaged.cursor for a phase-gated string,
or a function-form Tool.cursor reading the gesture scratch out of the
tool-routing dispatcher. Both belonged to a pipeline whose whole job was
being taken over by bindings, and neither could describe a cursor for an
action a tool doesn't own.
Optional ExperimentalcursorCSS cursor shown while the pointer hovers a spot where this action
would win the drag. The hover-cursor pump (in useGestureDispatcher)
runs Dispatcher.resolveOnly on each idle pointermove — the same
match walk a real pointerdown takes — and applies the winning
action's cursor, so the hint and the actual click target stay in
sync by construction. Omitted = no override (the active tool's
Tool.cursor shows). Affordance hits are resolved earlier in the
pump via AffordanceRegion.cursor and never reach this field.
Static string only. Prediction runs enabled() but cannot run the
invoker, so an action that matches yet bails at start() (empty
handle) may still show its cursor — keep enabled accurate for
actions that declare one.
Optional ExperimentaldefaultThe gesture-spec form of the binding, read by the gesture dispatcher.
May be a single GestureSpec, a bare GestureSpec[] (any-of semantics),
or a BoundGesture[] where each entry is either a bare GestureSpec or
{ spec, opts } — use the object form for parametric actions where two
bindings for the same action differ only by opts.params (e.g. flip
with axis: 'x' vs 'y'). The dispatcher extracts opts.params and
passes them to ImmediateInvoker.run as its second argument.
Optional ExperimentaleligibleDeclarative eligibility rule, evaluated against the current
RuleCtx by the dispatcher before invoking start(). Omitted =
always eligible.
Accepts either a fluent Condition (callable with .rule) or a
raw Rule tree; the dispatcher normalizes via .rule unwrap.
Prefer capability:-based rules (e.g. { capability: 'transforms-selection' })
over mode: rules — capability rules survive new modes being added
that allow the same capability.
Optional ExperimentalenabledOptional predicate the command palette consults when rendering. Return
true when the action is currently triggerable. Return a reason string
(e.g. 'Selection required') when disabled — the palette greys out
the row, skips it in keyboard nav, ignores clicks, and shows the
reason next to the label. Keystroke dispatch (the registered binding)
is unaffected; the action's own run should self-guard.
Contract: must be pure (no side effects), fast (< 4ms in dev), and
must not throw. If a call throws or exceeds the budget in dev mode,
evaluateEnabled logs a one-time warning per action id; throws are
caught and treated as disabled with reason '(predicate threw)'.
Snapshot-on-open semantics: the palette evaluates enabled once when
opened and does NOT re-evaluate on selection changes while open. Live
reactive updates are deferred — palette is short-lived.
The reason set is a closed enum — to add a new reason, edit
ActionDisabledReason and the consumer's display map.
The optional deps argument is the same bag passed to
ImmediateInvoker.run; callers (evaluateEnabled / the ActionBar) may
synthesize it from the surrounding DepRegistry so predicates can
inspect selection / scene / etc. Predicates that don't need deps just
ignore the arg.
Optional ExperimentalgroupGrouping key for palette/menu surfaces. Free-form string; the kit
ships defaults for 'align' (six edges/centers), 'distribute'
(two axes), and recommends 'pathfinder' for boolean ops.
Optional ExperimentaliconInline-SVG icon for palette / toolbar surfaces. Mirrors
ToolPresentation.icon so a generic <ActionBar> can render from
action metadata the same way <ToolPalette> renders from tool
metadata. May be a static ReactNode or a function (rare; useful
for state-aware icons like a "lock" toggle).
ExperimentalidOptional ExperimentalinvokerPluggable invocation strategy. The gesture dispatcher routes matched
bindings through invoker.start / invoker.run depending on timing.
All kit-standard descriptors ship one; consumer-supplied actions
without an invoker can still register but won't be triggered.
ExperimentallabelOptional ExperimentalrequiresNames of the deps this action's invoker reads (keys of DepSchema).
The dispatcher (and trigger, when requires is present) resolves
each name against the DepRegistry at invocation time and passes the
resulting bag to the invoker. Dev builds warn when the invoker reads a
dep it didn't declare here — see buildDepsFromRequires.
Optional ExperimentalscopeWhen set to 'hotkey', this action's defaultBinding rides the hotkey
BindingScope instead of the ambient scope — meaning it beats any
active-tool binding on the same input shape. Use for tool-switch
shortcuts and global held-key triggers. Default: ambient.
Optional ExperimentalshortcutDisplay override for the keyboard shortcut. When omitted, palette
surfaces derive a label from defaultBinding via their own
formatter.
Single registered action. v1: one binding per action.