@weasel-js/core 1.1.0 → 1.3.0-pre.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/CHANGELOG.md +1988 -0
  2. package/dist/{DrawCommand-BbPrSTra.d.ts → DrawCommand-BnZyQaDc.d.ts} +29 -189
  3. package/dist/chunk-2KKYDDDD.js +121 -0
  4. package/dist/chunk-2KKYDDDD.js.map +1 -0
  5. package/dist/{chunk-J7LUY47N.js → chunk-3FB44DBC.js} +3 -3
  6. package/dist/{chunk-J7LUY47N.js.map → chunk-3FB44DBC.js.map} +1 -1
  7. package/dist/{chunk-J3YC3GVF.js → chunk-3ZV53ZW2.js} +51 -14
  8. package/dist/chunk-3ZV53ZW2.js.map +1 -0
  9. package/dist/{chunk-JOSUVS6C.js → chunk-67KE7SDP.js} +4 -4
  10. package/dist/chunk-67KE7SDP.js.map +1 -0
  11. package/dist/{chunk-CSMF654J.js → chunk-AQS4JGRA.js} +50 -7
  12. package/dist/chunk-AQS4JGRA.js.map +1 -0
  13. package/dist/{chunk-HGF5X6MZ.js → chunk-BJN5ZRIJ.js} +2 -2
  14. package/dist/chunk-BJN5ZRIJ.js.map +1 -0
  15. package/dist/{chunk-4Z6J4IVH.js → chunk-F74NVEHT.js} +14 -46
  16. package/dist/chunk-F74NVEHT.js.map +1 -0
  17. package/dist/{chunk-3B4QEB2G.js → chunk-IFOVTYAK.js} +26291 -24809
  18. package/dist/chunk-IFOVTYAK.js.map +1 -0
  19. package/dist/{chunk-UGFFCMQP.js → chunk-MXFSHJOM.js} +4 -4
  20. package/dist/{chunk-UGFFCMQP.js.map → chunk-MXFSHJOM.js.map} +1 -1
  21. package/dist/{chunk-BHVYVFGV.js → chunk-ZB7UYJVG.js} +3 -3
  22. package/dist/{chunk-BHVYVFGV.js.map → chunk-ZB7UYJVG.js.map} +1 -1
  23. package/dist/clipboard.d.ts +3 -3
  24. package/dist/clipboard.js +3 -3
  25. package/dist/clone.d.ts +2 -2
  26. package/dist/clone.js +3 -3
  27. package/dist/{geometry-C56YbnfD.d.ts → geometry-Dtt_k6Dq.d.ts} +1 -1
  28. package/dist/{grid-DqOi0Vgi.d.ts → grid-nnXU4VjN.d.ts} +1 -1
  29. package/dist/index.d.ts +1929 -1342
  30. package/dist/index.js +10 -10
  31. package/dist/insert.d.ts +3 -3
  32. package/dist/insert.js +1 -1
  33. package/dist/move.d.ts +4 -4
  34. package/dist/move.js +3 -3
  35. package/dist/{options-C6HYCKP7.d.ts → options-CdFl510T.d.ts} +1 -1
  36. package/dist/{path-B6MMiodD.d.ts → path-JEV2c5If.d.ts} +2 -13
  37. package/dist/patterns-builtin.d.ts +1 -1
  38. package/dist/patterns-builtin.js +1 -1
  39. package/dist/{pointSnapToGrid-BBtn5bus.d.ts → pointSnapToGrid-Dmthv94u.d.ts} +2 -2
  40. package/dist/{registry-DGBrjGbB.d.ts → registry-BU1_3L_C.d.ts} +691 -38
  41. package/dist/renderer.d.ts +38 -83
  42. package/dist/renderer.js +10 -10
  43. package/dist/resize.d.ts +4 -4
  44. package/dist/resize.js +2 -2
  45. package/dist/routing.d.ts +35 -9
  46. package/dist/routing.js +1 -1
  47. package/dist/stroke-5pBwOm9s.d.ts +190 -0
  48. package/dist/{types-vqxpCFVp.d.ts → types-DIQAisSG.d.ts} +107 -5
  49. package/dist/{types-DbysOO-2.d.ts → types-XBcDEp3Y.d.ts} +14 -9
  50. package/dist/{types-BhrifbZ1.d.ts → types-miXHGrZM.d.ts} +1 -1
  51. package/package.json +8 -6
  52. package/dist/chunk-3B4QEB2G.js.map +0 -1
  53. package/dist/chunk-4Z6J4IVH.js.map +0 -1
  54. package/dist/chunk-CSMF654J.js.map +0 -1
  55. package/dist/chunk-GVCNT7UH.js +0 -47
  56. package/dist/chunk-GVCNT7UH.js.map +0 -1
  57. package/dist/chunk-HGF5X6MZ.js.map +0 -1
  58. package/dist/chunk-J3YC3GVF.js.map +0 -1
  59. package/dist/chunk-JOSUVS6C.js.map +0 -1
  60. package/dist/paint-types-CnLIzqq1.d.ts +0 -261
  61. package/dist/viewToMat3-CQW_YqDw.d.ts +0 -67
@@ -1,25 +1,20 @@
1
- import { N as NodeId, R as RectPose, S as Scene } from './types-vqxpCFVp.js';
2
- import { M as ModifierState, A as ActionBehavior, c as ResizeAnchor, R as ResizePose, B as BoundsConstraint, P as PointSnapBehavior } from './types-BhrifbZ1.js';
1
+ import { N as NodeId, R as RectPose, S as Scene } from './types-DIQAisSG.js';
2
+ import { M as ModifierState, A as ActionBehavior, c as ResizeAnchor, R as ResizePose, B as BoundsConstraint, P as PointSnapBehavior } from './types-miXHGrZM.js';
3
3
  import { V as View } from './view-DSQgxBJB.js';
4
4
  import { CapabilityTag } from '@weasel-js/modes';
5
5
  import * as React from 'react';
6
6
  import { MutableRefObject, ReactNode, ReactElement } from 'react';
7
7
  import { GestureSpec, IngestItem, InputEvent } from '@weasel-js/gestures';
8
- import { B as Bounds, P as PoseProjection } from './geometry-C56YbnfD.js';
9
- import { F as FillStyle, S as Stroke } from './paint-types-CnLIzqq1.js';
10
- import { D as DrawCommand } from './DrawCommand-BbPrSTra.js';
8
+ import { D as DrawCommand } from './DrawCommand-BnZyQaDc.js';
9
+ import { B as Bounds, P as PoseProjection, F as FitViewToBoundsOptions, V as ViewportDims } from './geometry-Dtt_k6Dq.js';
10
+ import { FillStyle, Stroke } from '@weasel-js/paint';
11
11
  import * as react_jsx_runtime from 'react/jsx-runtime';
12
- import { P as Path } from './path-B6MMiodD.js';
12
+ import { P as Path } from './path-JEV2c5If.js';
13
13
  import { Op, History } from '@weasel-js/history';
14
- import { L as LayoutStrategy, I as InsertAdapter } from './types-DbysOO-2.js';
14
+ import { a as SceneAdapter, L as LayoutStrategy, I as InsertAdapter } from './types-XBcDEp3Y.js';
15
15
  import { D as DebugSink } from './types-BHK2dkMu.js';
16
16
  import { Mat3 } from '@weasel-js/geom';
17
17
 
18
- /**
19
- * Read-only state that affordances consult on every render and hit-test
20
- * call. Built once per Canvas render via `buildChromeState`; affordances
21
- * must not cache it across calls.
22
- */
23
18
  interface ChromeState {
24
19
  /** Currently selected ids. Live; reflects useSelection's React state. */
25
20
  readonly selection: readonly NodeId[];
@@ -31,7 +26,8 @@ interface ChromeState {
31
26
  * computable. */
32
27
  boundsOf(id: string): Bounds | null;
33
28
  /** Multi-union AABB when `multiActive`. Computed lazily from `boundsOf`
34
- * over every selected id; null otherwise. */
29
+ * over every selected id, expanding rotated members to the extent of
30
+ * their ink; null otherwise. */
35
31
  readonly unionBounds: Bounds | null;
36
32
  /** Active modifier state at the moment of the call. */
37
33
  readonly modifiers: ModifierState;
@@ -282,13 +278,21 @@ interface Dims {
282
278
  width: number;
283
279
  height: number;
284
280
  }
281
+ /** One layer's memoized output, keyed by layer id. Owned by the canvas that
282
+ * calls `drawLayers`, not by `drawLayers` itself — the function is pure. */
283
+ type LayerCommandCache = Map<string, {
284
+ deps: readonly unknown[];
285
+ cmds: DrawCommand[];
286
+ }>;
285
287
  /**
286
288
  * A single named render sub-layer within a canvas renderer.
287
289
  *
288
290
  * @template TData - The data object passed to each draw call.
289
291
  */
290
292
  interface RenderLayer<TData> {
291
- /** Unique identifier used in visibility maps and ordering arrays. */
293
+ /** Unique identifier used in visibility maps and ordering arrays. When a
294
+ * cache is in use, an id must identify the same logical layer across
295
+ * frames — reusing it for a different layer can serve cross-layer commands. */
292
296
  id: string;
293
297
  /** Human-readable name for UI toggles. */
294
298
  label: string;
@@ -306,6 +310,21 @@ interface RenderLayer<TData> {
306
310
  * subset manually with `viewToMat3(view)`.
307
311
  */
308
312
  draw: (data: TData, view: View, dims: Dims) => DrawCommand[];
313
+ /**
314
+ * Optional cache key. When present and a `LayerCommandCache` is supplied to
315
+ * `drawLayers`, the layer's previous `DrawCommand[]` is reused as long as
316
+ * every entry is `Object.is`-equal to the previous call's. A layer with no
317
+ * `deps` rebuilds on every frame.
318
+ *
319
+ * **The returned commands must be treated as immutable.** A cached tree is
320
+ * handed to the renderer again on later frames, so mutating a tree you
321
+ * previously returned corrupts the cache silently rather than erroring.
322
+ *
323
+ * **Screen-space layers are not protected against a stale `view`/`dims`
324
+ * the way world-space layers are** (see `space` below) — include them in
325
+ * `deps` if `draw` reads them.
326
+ */
327
+ deps?: (data: TData, view: View, dims: Dims) => readonly unknown[];
309
328
  /**
310
329
  * Whether the layer is shown when no explicit visibility entry exists.
311
330
  * Defaults to `true` when absent.
@@ -379,7 +398,34 @@ interface RenderLayer<TData> {
379
398
  * `viewToMat3(view)` before they reach the renderer. Screen-space layers
380
399
  * (`space: 'screen'`) pass through unchanged.
381
400
  */
382
- declare function drawLayers<TData>(layers: RenderLayer<TData>[], data: TData, visibility: Record<string, boolean>, order: string[] | undefined, view: View | undefined, dims: Dims): DrawCommand[];
401
+ declare function drawLayers<TData>(layers: RenderLayer<TData>[], data: TData, visibility: Record<string, boolean>, order: string[] | undefined, view: View | undefined, dims: Dims, cache?: LayerCommandCache): DrawCommand[];
402
+ /**
403
+ * Resolve one layer's visibility: `alwaysOn` wins, then an explicit entry in
404
+ * `visibility`, then `defaultVisible`, defaulting to shown.
405
+ */
406
+ declare function isLayerVisible<TData>(layer: RenderLayer<TData>, visibility: Record<string, boolean>): boolean;
407
+ /**
408
+ * Does this layer reach the screen at all — both gates, in the order
409
+ * `drawLayers` applies them.
410
+ *
411
+ * **A listed `order` is the whole list**, so omission from it drops a layer
412
+ * that `visibility` would have shown, and `alwaysOn` does not rescue it.
413
+ * Hit-testing asks this, not `isLayerVisible`: a layer that is not painted
414
+ * but still claims pointer events is a pointer landing on nothing the user
415
+ * can see.
416
+ */
417
+ declare function isLayerPainted<TData>(layer: RenderLayer<TData>, visibility: Record<string, boolean>, order: string[] | undefined): boolean;
418
+ /**
419
+ * Draw one layer and put its commands in the space its `space` declares:
420
+ * world-space output wrapped in a `viewToMat3(view)` group, screen-space
421
+ * output passed through.
422
+ *
423
+ * Anything rendering layers through a view — the canvas itself, a viewport
424
+ * node's inner pass — goes through here. A second copy of this rule that
425
+ * forgets the wrap draws world content at raw world coords, which looks
426
+ * plausible at the identity view and wrong everywhere else.
427
+ */
428
+ declare function drawOneLayer<TData>(layer: RenderLayer<TData>, data: TData, view: View, dims: Dims, cache?: LayerCommandCache): DrawCommand[];
383
429
 
384
430
  /**
385
431
  * Facts about the device the canvas is running on.
@@ -634,6 +680,96 @@ type ChromeId = 'selection.outline' | 'selection.resize-handles' | 'selection.ro
634
680
  */
635
681
  type VisibilityRules = Partial<Record<ChromeId, Condition | Rule>>;
636
682
 
683
+ /**
684
+ * The kit's built-in shape kinds — one table, every other spelling derived.
685
+ *
686
+ * Lives in `core/` rather than beside the shape tools because both layers ask
687
+ * questions of the same set: the interactions layer decides whether
688
+ * `insertAction` can paint a live preview for a kind, and the canvas layer
689
+ * decides which shape tools `useBuiltinShapeTools` mounts and what
690
+ * `BUNDLE_TOOLS` / `defaultNodeRouting` / `defaultNodeProperties` enumerate.
691
+ *
692
+ * This module imports nothing on purpose: `useBuiltinShapeTools` imports the
693
+ * package barrel, so anything barrel-reachable that needs these lists at
694
+ * module-evaluation time must not route through it.
695
+ */
696
+ /** What the kit knows about one built-in shape kind. */
697
+ interface ShapeKindDescriptor {
698
+ /** Mounted as a built-in shape tool by `useBuiltinShapeTools`, and so a
699
+ * member of `BuiltinShapeToolId` / `KIT_SHAPE_KINDS`. `image` is false:
700
+ * `useImageTool` needs a `src` and can't be auto-mounted. */
701
+ readonly tool: boolean;
702
+ /** `insertAction` emits an `insertPreview` overlay for this kind, and the
703
+ * dispatcher overlay layer knows how to draw it. Kinds without one still
704
+ * commit; they just have no live drag preview (`pen` and `lasso` don't
705
+ * route through `insertAction` at all). */
706
+ readonly insertPreview: boolean;
707
+ }
708
+ /**
709
+ * Declaration order is the enumeration order of every derived list —
710
+ * `KIT_SHAPE_KINDS`, and through it `defaultNodeRouting` /
711
+ * `defaultNodeProperties` / `BUNDLE_TOOLS.exhaustive`.
712
+ */
713
+ declare const SHAPE_KINDS: {
714
+ readonly rect: {
715
+ readonly tool: true;
716
+ readonly insertPreview: true;
717
+ };
718
+ readonly ellipse: {
719
+ readonly tool: true;
720
+ readonly insertPreview: true;
721
+ };
722
+ readonly line: {
723
+ readonly tool: true;
724
+ readonly insertPreview: true;
725
+ };
726
+ readonly polygon: {
727
+ readonly tool: true;
728
+ readonly insertPreview: true;
729
+ };
730
+ readonly star: {
731
+ readonly tool: true;
732
+ readonly insertPreview: true;
733
+ };
734
+ readonly pen: {
735
+ readonly tool: true;
736
+ readonly insertPreview: false;
737
+ };
738
+ readonly pencil: {
739
+ readonly tool: true;
740
+ readonly insertPreview: true;
741
+ };
742
+ readonly lasso: {
743
+ readonly tool: true;
744
+ readonly insertPreview: false;
745
+ };
746
+ readonly text: {
747
+ readonly tool: true;
748
+ readonly insertPreview: true;
749
+ };
750
+ readonly image: {
751
+ readonly tool: false;
752
+ readonly insertPreview: true;
753
+ };
754
+ };
755
+ /** The keys of a shape-kind table whose descriptor sets `F` to `true`. */
756
+ type ShapeKindsWhere<T, F extends keyof ShapeKindDescriptor> = {
757
+ [K in keyof T]: T[K] extends Record<F, true> ? K : never;
758
+ }[keyof T];
759
+ /**
760
+ * Built-in shape tool ids handled by `useBuiltinShapeTools`. Each maps to a
761
+ * kit tool hook + a default `create` that produces a leaf node compatible
762
+ * with `PATH_PAINTER`.
763
+ */
764
+ type BuiltinShapeToolId = ShapeKindsWhere<typeof SHAPE_KINDS, 'tool'>;
765
+ /** The insert kinds the kit's dispatcher overlay layer knows how to render.
766
+ * Consumer-defined kinds fall outside it: no live preview, commit unaffected. */
767
+ type KitInsertShape = ShapeKindsWhere<typeof SHAPE_KINDS, 'insertPreview'>;
768
+ /** Runtime, iterable list of the shape-tool ids in `BuiltinShapeToolId`.
769
+ * Surfaced so consumers (e.g. the Bundle Inspector) can enumerate the
770
+ * builtin shape kinds without re-encoding the union. */
771
+ declare const KIT_SHAPE_KINDS: readonly BuiltinShapeToolId[];
772
+
637
773
  /** A 2D point in either world or screen coordinates. */
638
774
  interface Point2 {
639
775
  x: number;
@@ -804,6 +940,9 @@ interface BindingOpts {
804
940
  * via `resolveParams(opts?.params)`. */
805
941
  params?: Record<string, unknown> | (() => Record<string, unknown>);
806
942
  }
943
+ /** Resolve `BindingOpts.params` to a concrete record (calling the thunk if
944
+ * needed). Returns `undefined` for absent params. */
945
+ declare function resolveParams(params: BindingOpts['params']): Record<string, unknown> | undefined;
807
946
  /** Convention-shaped action dependencies bag. Actions declare which
808
947
  * contexts they consume; the dispatcher composes them per call.
809
948
  * Consumer-side contexts (e.g. ColorContext) plug in by extending. */
@@ -876,7 +1015,7 @@ type OngoingOverlay = {
876
1015
  * renderer narrows on `shape` and casts the field shape it expects.
877
1016
  */
878
1017
  kind: 'insertPreview';
879
- shape: 'rect' | 'ellipse' | 'line' | 'polygon' | 'star' | 'pencil' | 'image';
1018
+ shape: KitInsertShape;
880
1019
  bounds: {
881
1020
  x: number;
882
1021
  y: number;
@@ -1232,6 +1371,13 @@ interface SelectionApi {
1232
1371
  setSelection: (ids: NodeId[]) => void;
1233
1372
  };
1234
1373
  }
1374
+ /** Somewhere selection can live outside this hook. `Scene` satisfies it;
1375
+ * so does any store with the same three methods. */
1376
+ interface SelectionStore {
1377
+ getSelection(): readonly NodeId[];
1378
+ setSelection(ids: readonly NodeId[]): void;
1379
+ subscribe(listener: () => void): () => void;
1380
+ }
1235
1381
  /** Options for {@link useSelection}. */
1236
1382
  interface UseSelectionOptions {
1237
1383
  /** Default `'single'`. */
@@ -1240,6 +1386,11 @@ interface UseSelectionOptions {
1240
1386
  extend?: SelectionExtendKey;
1241
1387
  /** Default `[]`. */
1242
1388
  initial?: readonly NodeId[];
1389
+ /** Keep the selection on this store rather than in the hook, so every
1390
+ * consumer of the same scene shares one selection and undo / redo can
1391
+ * restore it. `initial` then only seeds a store that has none yet.
1392
+ * Omit it and the hook owns a selection nobody else sees. */
1393
+ scene?: SelectionStore;
1243
1394
  /** When `true`, every mutator (`set`/`add`/`remove`/`toggle`/`clear`/
1244
1395
  * `applyClick`) is a no-op — selection stays at whatever `initial`
1245
1396
  * pinned it to. Useful for demos that exist to showcase a single
@@ -1327,6 +1478,499 @@ interface ContentHandlerEntry {
1327
1478
  /** Register a content handler. Returns a disposer that removes it. */
1328
1479
  declare function registerContentHandler(entry: ContentHandlerEntry): () => void;
1329
1480
 
1481
+ /** Which of a node's two per-anchor color arrays an override applies to. */
1482
+ type VertexColorChannel = 'fill' | 'stroke';
1483
+ /** Function-form override: receives the consumer-supplied base color
1484
+ * array and the current animation timestamp (ms, from the animator's
1485
+ * clock). Returns a flat RGBA float array (values in 0..1, matching
1486
+ * the renderer's `stroke.vertexColors` / `PathDrawCommand.vertexColors`
1487
+ * color space) of the same length as `base`. */
1488
+ type ColorOverrideFn = (base: readonly number[], tMs: number) => number[];
1489
+ /** Either a static per-anchor RGBA float array (0..1) or a function-form
1490
+ * override (see {@link ColorOverrideFn}). */
1491
+ type ColorOverride = readonly number[] | ColorOverrideFn;
1492
+ /** Per-node, per-channel store of color overrides consulted by `createPathLayer`
1493
+ * before falling back to the consumer's `getVertexColors` / `getStrokeVertexColors`
1494
+ * accessor. Attached to `useAnimator` as `animator.colorOverrides`. */
1495
+ declare class ColorOverrideRegistry {
1496
+ private readonly map;
1497
+ private _version;
1498
+ set(id: string, channel: VertexColorChannel, override: ColorOverride): void;
1499
+ clear(id: string, channel: VertexColorChannel): void;
1500
+ clearAll(): void;
1501
+ get(id: string, channel: VertexColorChannel): ColorOverride | undefined;
1502
+ version(): number;
1503
+ }
1504
+
1505
+ /** One keyframe. `easing` shapes the approach INTO this key from the previous
1506
+ * one, so the first key's easing is never consulted. */
1507
+ interface Keyframe<T> {
1508
+ /** Time within the track's timeline, in ms. */
1509
+ t: number;
1510
+ value: T;
1511
+ easing?: EasingFn;
1512
+ }
1513
+ /** A track sampled as a pure function of the playhead. Scrubbing one is free
1514
+ * and order-independent. */
1515
+ interface SampledTrack<T> {
1516
+ kind: 'sampled';
1517
+ label?: string;
1518
+ /** Sorted ascending by `t`. `sampleTrack` assumes this and does not sort. */
1519
+ keys: Keyframe<T>[];
1520
+ /** Required when T is not `number`; defaults to numeric lerp otherwise. */
1521
+ interpolate?: Interpolate<T>;
1522
+ /** Built once per segment and cached. Takes precedence over `interpolate`. */
1523
+ interpolator?: InterpolatorFactory<T>;
1524
+ onTick: (value: T) => void;
1525
+ }
1526
+ /** A track of edge crossings. Fires only when the playhead advances forward
1527
+ * under playback — never on `seek`. */
1528
+ interface EventTrack {
1529
+ kind: 'event';
1530
+ label?: string;
1531
+ /** Sorted ascending by `t`. */
1532
+ events: {
1533
+ t: number;
1534
+ fire: () => void;
1535
+ }[];
1536
+ }
1537
+ /** A nested timeline, evaluated at `playhead - at`. Children are NOT registered
1538
+ * with the animator separately; the parent evaluates them. */
1539
+ interface TimelineTrack {
1540
+ kind: 'timeline';
1541
+ label?: string;
1542
+ at: number;
1543
+ timeline: NestedTimeline;
1544
+ }
1545
+ type Track = SampledTrack<any> | EventTrack | TimelineTrack;
1546
+ /** What a child timeline may declare. The parent owns playback, so `loop`,
1547
+ * `autoplay`, `onDone` and `cancelKey` have no meaning below the root. */
1548
+ interface NestedTimeline {
1549
+ tracks: Track[];
1550
+ /** Defaults to the largest end time across `tracks`. */
1551
+ duration?: number;
1552
+ }
1553
+ interface TimelineOptions extends NestedTimeline {
1554
+ /** `true` loops forever, `n` loops n additional times. Default false. */
1555
+ loop?: boolean | number;
1556
+ /** Default true. When false the timeline registers but holds at t=0 until resumed. */
1557
+ autoplay?: boolean;
1558
+ onDone?: () => void;
1559
+ cancelKey?: string;
1560
+ }
1561
+ interface TimelineHandle extends AnimationHandle {
1562
+ /** Move the playhead. Never fires event tracks, at any depth. */
1563
+ seek(t: number): void;
1564
+ /** Current playhead in ms. */
1565
+ time(): number;
1566
+ duration(): number;
1567
+ tracks(): readonly Track[];
1568
+ /** Run `fn`, then recompute duration, drop cached interpolators, and notify.
1569
+ * Every mutation must go through this — an edited keyframe otherwise keeps
1570
+ * interpolating toward its old value with no visible error. */
1571
+ edit(fn: () => void): void;
1572
+ /** Notified after each `edit`. Returns an unsubscribe. */
1573
+ subscribe(cb: () => void): () => void;
1574
+ }
1575
+
1576
+ /** An easing curve: maps normalized progress `t ∈ [0, 1]` to eased progress.
1577
+ * Curves may leave the 0–1 range in the middle (back, elastic) but should
1578
+ * pass through 0 at 0 and 1 at 1. */
1579
+ type EasingFn = (t: number) => number;
1580
+ /** Blends two `T` values at eased progress `t`. Called once per frame; see
1581
+ * {@link InterpolatorFactory} when the blend has setup worth hoisting. */
1582
+ type Interpolate<T> = (from: T, to: T, t: number) => T;
1583
+ /** Factory interpolator: built ONCE at tween start with (from, to), the returned
1584
+ * function is called with `t ∈ [0, 1]` each frame. Use for interpolators with
1585
+ * expensive setup (color-space conversion, path-string parsing) — d3-interpolate's
1586
+ * shape exactly. For cheap interpolations the per-tick `Interpolate<T>` form is
1587
+ * fine; this is the escape hatch when setup-per-tick is wasteful. */
1588
+ type InterpolatorFactory<T> = (from: T, to: T) => (t: number) => T;
1589
+ /** A spring's physical parameters. Higher stiffness settles faster, higher
1590
+ * damping overshoots less, higher mass makes both sluggish. */
1591
+ interface SpringPreset {
1592
+ stiffness: number;
1593
+ damping: number;
1594
+ mass: number;
1595
+ }
1596
+ /** One of the tunings in `SPRING_PRESETS`. */
1597
+ type SpringPresetName = 'gentle' | 'wobbly' | 'stiff' | 'slow';
1598
+ /** A running animation. Cancel it, or bend its time — pausing and time-scaling
1599
+ * act on this animation's own virtual clock, independent of the animator's. */
1600
+ interface AnimationHandle {
1601
+ /** Monotonic id assigned by the animator. */
1602
+ id: number;
1603
+ /** Cancel this animation. Idempotent — no-op once already finished/canceled. */
1604
+ cancel(): void;
1605
+ /** Freeze this animation's virtual clock. Idempotent. */
1606
+ pause(): void;
1607
+ /** Resume this animation's virtual clock. Idempotent. */
1608
+ resume(): void;
1609
+ /** Multiply this animation's virtual-clock rate by `scale`. 1 = normal. */
1610
+ setTimeScale(scale: number): void;
1611
+ /** True iff this handle is currently paused. */
1612
+ isPaused(): boolean;
1613
+ }
1614
+ /** A duration-based animation from `from` to `to` over `ms`, shaped by an
1615
+ * easing curve. Reach for a spring instead when the motion should respond to
1616
+ * where the value already is rather than restart from a fixed duration. */
1617
+ interface TweenOptions<T> {
1618
+ from: T;
1619
+ to: T;
1620
+ ms: number;
1621
+ easing?: EasingFn;
1622
+ /** Required when T is not `number`. For T = number, defaults to linear numeric lerp.
1623
+ * Called per-tick with `(from, to, t)`. For interpolators with expensive setup,
1624
+ * prefer `interpolator` which is built once at tween start. */
1625
+ interpolate?: Interpolate<T>;
1626
+ /** Factory interpolator built once at tween start. Takes precedence over
1627
+ * `interpolate` when both are provided. Use this for d3-interpolate or any
1628
+ * `(from, to) => (t) => v` shape. */
1629
+ interpolator?: InterpolatorFactory<T>;
1630
+ onTick: (value: T) => void;
1631
+ onDone?: () => void;
1632
+ /** Any new animation passed the same cancelKey cancels the prior one in flight. */
1633
+ cancelKey?: string;
1634
+ }
1635
+ /** A spring animation: runs until the value settles on `to` rather than for a
1636
+ * set duration, so it absorbs an initial velocity naturally. Non-numeric `T`
1637
+ * needs the four vector helpers. */
1638
+ interface SpringOptions<T> {
1639
+ from: T;
1640
+ to: T;
1641
+ /** Initial velocity in T-units per second. Default: zero (T-shape-aware). */
1642
+ velocity?: T;
1643
+ preset?: SpringPresetName;
1644
+ stiffness?: number;
1645
+ damping?: number;
1646
+ mass?: number;
1647
+ interpolate?: Interpolate<T>;
1648
+ /** Vector helpers — required for non-numeric T. */
1649
+ add?: (a: T, b: T) => T;
1650
+ subtract?: (a: T, b: T) => T;
1651
+ scale?: (v: T, k: number) => T;
1652
+ magnitude?: (v: T) => number;
1653
+ /** Velocity magnitude below which the spring is considered settled. Default 0.01. */
1654
+ restThreshold?: number;
1655
+ onTick: (value: T) => void;
1656
+ onDone?: () => void;
1657
+ cancelKey?: string;
1658
+ }
1659
+ /** Spring and decay as one animation. With a `to`, a spring pulls toward it;
1660
+ * with `to: null`, the value coasts on its velocity. Either can become the
1661
+ * other mid-flight through the handle. */
1662
+ interface PhysicsOptions<T> {
1663
+ from: T;
1664
+ /** Target. `null` ⇒ no spring force (decay-mode). */
1665
+ to?: T | null;
1666
+ /** Initial velocity in T-units per second. */
1667
+ velocity?: T;
1668
+ preset?: SpringPresetName;
1669
+ stiffness?: number;
1670
+ damping?: number;
1671
+ mass?: number;
1672
+ restThreshold?: number;
1673
+ /** Vector helpers — required for non-numeric T. */
1674
+ add?: (a: T, b: T) => T;
1675
+ subtract?: (a: T, b: T) => T;
1676
+ scale?: (v: T, k: number) => T;
1677
+ magnitude?: (v: T) => number;
1678
+ onTick: (value: T) => void;
1679
+ onDone?: () => void;
1680
+ cancelKey?: string;
1681
+ }
1682
+ /** An `AnimationHandle` that can also be steered while it runs — the point of
1683
+ * the physics primitive. */
1684
+ interface PhysicsHandle<T = unknown> extends AnimationHandle {
1685
+ /** Retarget mid-flight. `null` ⇒ switch to decay-mode (no spring force). */
1686
+ setTarget(to: T | null): void;
1687
+ /** Replace the current velocity in T-units per second. */
1688
+ setVelocity(v: T): void;
1689
+ }
1690
+ /** Momentum: coast from `from` at `velocity`, slowing by `friction` each
1691
+ * second until below `threshold`. What a flick-to-pan leaves behind. */
1692
+ interface DecayOptions<T> {
1693
+ from: T;
1694
+ velocity: T;
1695
+ /** Per-second velocity multiplier in (0, 1). Default 0.95. */
1696
+ friction?: number;
1697
+ /** Velocity magnitude below which decay stops. Default 0.5. */
1698
+ threshold?: number;
1699
+ add: (a: T, b: T) => T;
1700
+ scale: (v: T, k: number) => T;
1701
+ magnitude: (v: T) => number;
1702
+ onTick: (value: T) => void;
1703
+ onDone?: () => void;
1704
+ cancelKey?: string;
1705
+ }
1706
+ /** Options for `useAnimator`. Everything here is an injection seam for tests;
1707
+ * the defaults are the real clock, rAF, and `setTimeout`. */
1708
+ interface UseAnimatorOptions {
1709
+ /** Optional clock injection for tests. Returns ms since some epoch. */
1710
+ now?: () => number;
1711
+ /** Optional rAF / cAF injection for tests. Defaults to window.requestAnimationFrame. */
1712
+ requestFrame?: (cb: (t: number) => void) => number;
1713
+ cancelFrame?: (handle: number) => void;
1714
+ /** Optional `setTimeout` injection used by `stagger` for per-item delays.
1715
+ * Defaults to the global `setTimeout`. Tests inject a virtual scheduler. */
1716
+ setTimer?: (cb: () => void, ms: number) => unknown;
1717
+ /** Companion to `setTimer`. Defaults to global `clearTimeout`. */
1718
+ clearTimer?: (handle: unknown) => void;
1719
+ }
1720
+ /**
1721
+ * Owns every running animation on a canvas and drives them from one rAF loop.
1722
+ * Beyond the primitives (`tween`, `spring`, `decay`, `physics`) it offers
1723
+ * composition — `loop`, `stagger` — and bulk control by handle, by cancel-key,
1724
+ * or over everything at once.
1725
+ *
1726
+ * An animator does not know about the scene: animations report values through
1727
+ * `onTick` and the caller decides what to do with them.
1728
+ */
1729
+ interface Animator {
1730
+ tween<T>(opts: TweenOptions<T>): AnimationHandle;
1731
+ spring<T>(opts: SpringOptions<T>): AnimationHandle;
1732
+ decay<T>(opts: DecayOptions<T>): AnimationHandle;
1733
+ /** Unified spring/decay primitive. With `to` set, behaves as a spring;
1734
+ * with `to: null`, behaves as a velocity-driven decay. Supports
1735
+ * mid-flight retargeting via the returned handle's `setTarget`. */
1736
+ physics<T>(opts: PhysicsOptions<T>): PhysicsHandle<T>;
1737
+ /** Cancel a specific animation by handle. Pose stays at current value (no jump). */
1738
+ cancel(handle: AnimationHandle): void;
1739
+ /** Cancel every animation currently active under `key`. */
1740
+ cancelKey(key: string): void;
1741
+ /** Cancel everything. Useful from a destructor or "reset scene" path. */
1742
+ cancelAll(): void;
1743
+ /** True iff at least one animation is active. With `key`, scoped to that cancelKey. */
1744
+ isActive(key?: string): boolean;
1745
+ /**
1746
+ * True while the animator is currently executing an animation tick. Useful
1747
+ * for adapter wrappers (e.g. `animateOnSetPose`) that need to detect
1748
+ * "this `setPose` was called from inside another animation's onTick"
1749
+ * (momentum decay, in-flight tween, spring) and avoid recursively
1750
+ * scheduling a new wrap-animation that would fight the caller.
1751
+ */
1752
+ isTicking(): boolean;
1753
+ /** Freeze every animation managed by this animator. */
1754
+ pause(): void;
1755
+ /** Resume every animation managed by this animator. */
1756
+ resume(): void;
1757
+ /** True iff the animator is currently globally paused. */
1758
+ isPaused(): boolean;
1759
+ /** Multiply every animation's virtual-clock rate by `scale`. 1 = normal. */
1760
+ setTimeScale(scale: number): void;
1761
+ /** Freeze every animation whose `cancelKey` matches. */
1762
+ pauseKey(key: string): void;
1763
+ /** Resume every animation whose `cancelKey` matches. */
1764
+ resumeKey(key: string): void;
1765
+ /** Set per-animation timeScale for every animation whose `cancelKey` matches. */
1766
+ setTimeScaleByKey(key: string, scale: number): void;
1767
+ /**
1768
+ * Loop primitive: repeatedly invoke `factory` to produce a child animation.
1769
+ * The factory must wire its returned handle's `onDone` to call `next` so
1770
+ * the loop advances. Returns a handle whose pause/resume/setTimeScale/cancel
1771
+ * delegate to the current in-flight child (and prevent future iterations
1772
+ * on cancel).
1773
+ *
1774
+ * The loop is registered with the animator under a supervisor entry so
1775
+ * `animator.cancel(handle)`, `animator.cancelKey(opts.cancelKey)`, and
1776
+ * `animator.isActive(opts.cancelKey)` all work for it.
1777
+ */
1778
+ loop(factory: LoopFactory, opts?: LoopOptions): AnimationHandle;
1779
+ /** Sugar over `loop` for the common case of looping a tween between two
1780
+ * values with optional direction handling (`restart` | `reverse` |
1781
+ * `alternate`). Registered with the animator like `loop`. */
1782
+ tweenLoop<T>(opts: TweenLoopOptions<T>): AnimationHandle;
1783
+ /**
1784
+ * Stagger primitive: schedule a per-item animation, offset by `delay` ms
1785
+ * per index (or a custom function of the index). Two forms:
1786
+ * - Factory form: pass `factory` directly, returns a composite
1787
+ * `AnimationHandle`.
1788
+ * - Builder form: omit `factory`, get a `StaggerBuilder` for fluent
1789
+ * `.each` / `.tween` / `.springPose` calls.
1790
+ *
1791
+ * The composite handle's `cancel` cancels pending timers AND in-flight
1792
+ * children. `pause` / `resume` / `setTimeScale` propagate to in-flight
1793
+ * children; `pause`/`resume` also freeze and thaw pending per-item timers
1794
+ * (the remaining time before each pending fire is preserved across the
1795
+ * pause).
1796
+ *
1797
+ * The stagger is registered with the animator under a supervisor entry so
1798
+ * `animator.cancel(handle)`, `animator.cancelKey(opts.cancelKey)`, and
1799
+ * `animator.isActive(opts.cancelKey)` all work for it.
1800
+ */
1801
+ stagger<TItem>(items: readonly TItem[], delay: StaggerDelay): StaggerBuilder<TItem>;
1802
+ stagger<TItem>(items: readonly TItem[], delay: StaggerDelay, factory: StaggerFactory<TItem>, opts?: StaggerOptions): AnimationHandle;
1803
+ /**
1804
+ * Keyframe timeline. Registered like any other animation, so its playhead
1805
+ * responds to `pause`, `setTimeScale` and `cancelKey`. Sampled tracks are a
1806
+ * pure function of the playhead; event tracks fire only on forward playback.
1807
+ */
1808
+ timeline(opts: TimelineOptions): TimelineHandle;
1809
+ /** Per-node, per-channel color override registry consulted by the renderer's
1810
+ * path layer before reading consumer accessors. Used by `tweenVertexColors`,
1811
+ * `springVertexColors`, `cycleVertexColors`, `staggerVertexColors`. Cleared
1812
+ * automatically on animator unmount. */
1813
+ colorOverrides: ColorOverrideRegistry;
1814
+ /**
1815
+ * Subscribe to a callback fired once per RAF frame while any animation is
1816
+ * active. Returns an unsubscribe function. Used by consumers (typically
1817
+ * `<SceneCanvas>`) that need to repaint when an animation's side-effect
1818
+ * is read from a non-scene channel (e.g. `colorOverrides` consulted from
1819
+ * a custom `drawOne`) — scene mutations naturally trigger a repaint, but
1820
+ * `colorOverrides` writes do not.
1821
+ *
1822
+ * The callback fires AFTER the per-frame tick of each registered
1823
+ * animation, so by the time it runs `colorOverrides.get(...)` returns
1824
+ * the latest values. If no animations are active, no tick fires.
1825
+ */
1826
+ onTick(cb: () => void): () => void;
1827
+ /**
1828
+ * Keep the animator's RAF loop running until the returned cancel
1829
+ * function is called. Use for animations whose effect is read on every
1830
+ * frame but which don't have a natural progress state (e.g.
1831
+ * `cycleVertexColors`, which expresses its current value as a function
1832
+ * of `performance.now()` rather than as a tween from `from` to `to`).
1833
+ * Without a keep-alive entry the loop would idle and `onTick` would
1834
+ * stop firing even though the override is still installed.
1835
+ */
1836
+ keepAlive(): () => void;
1837
+ }
1838
+ /** Options for `Animator.loop`. */
1839
+ interface LoopOptions {
1840
+ /** Maximum number of iterations. Default Infinity. */
1841
+ count?: number;
1842
+ /** Invoked when the loop reaches `count` iterations naturally (not on cancel). */
1843
+ onDone?: () => void;
1844
+ /** Any new animation passed the same cancelKey cancels the prior one in flight.
1845
+ * Also enables `animator.cancelKey` / `animator.isActive(key)` for this loop. */
1846
+ cancelKey?: string;
1847
+ }
1848
+ /** Options for the top-level `Animator.stagger` factory form (third overload). */
1849
+ interface StaggerOptions {
1850
+ /** Cancel-key for the supervising registration. `animator.cancelKey(key)`
1851
+ * cancels the whole stagger; `animator.isActive(key)` returns true while
1852
+ * any timer or child is alive. */
1853
+ cancelKey?: string;
1854
+ }
1855
+ /** Produces one iteration of a loop. Must arrange for `next` to be called when
1856
+ * the animation it returns finishes, or the loop stalls after one pass. */
1857
+ type LoopFactory = (iteration: number, next: () => void) => AnimationHandle;
1858
+ /** Per-index delay schedule. Number ⇒ `index * delay` ms. Function ⇒ caller
1859
+ * decides the absolute delay for each index (e.g. `i => i * i * 30`). */
1860
+ type StaggerDelay = number | ((index: number) => number);
1861
+ /** Produces the animation for one staggered item. */
1862
+ type StaggerFactory<TItem> = (item: TItem, index: number) => AnimationHandle;
1863
+ /** A `T` value or a function that derives one from the per-item context. Used
1864
+ * by the fluent builder methods (`.tween`, `.springPose`) so each item can
1865
+ * vary an option (e.g. `to: (_item, i) => (i + 1) * 10`). */
1866
+ type StaggerPerItem<T, TItem> = T | ((item: TItem, index: number) => T);
1867
+ /** Options for the stagger builder's `.tween`: a tween per item, where
1868
+ * `from`, `to` and `ms` may each vary by item. */
1869
+ interface StaggerTweenOptions<T, TItem> {
1870
+ from: StaggerPerItem<T, TItem>;
1871
+ to: StaggerPerItem<T, TItem>;
1872
+ ms: StaggerPerItem<number, TItem>;
1873
+ easing?: EasingFn;
1874
+ interpolate?: Interpolate<T>;
1875
+ onTick: (value: T, item: TItem, index: number) => void;
1876
+ onDone?: (item: TItem, index: number) => void;
1877
+ }
1878
+ /** Options for the stagger builder's `.springPose`: the spring tuning, and
1879
+ * whether each item's settle is recorded as an undoable op. */
1880
+ interface StaggerSpringPoseOptions<TPose> {
1881
+ preset?: SpringPresetName;
1882
+ stiffness?: number;
1883
+ damping?: number;
1884
+ mass?: number;
1885
+ geometry?: PoseProjection<TPose>;
1886
+ recordOp?: boolean;
1887
+ opLabel?: string;
1888
+ }
1889
+ /** Fluent form of `Animator.stagger`: pick what to run per item after the
1890
+ * items and the delay schedule are already fixed. */
1891
+ interface StaggerBuilder<TItem> {
1892
+ /** Run an arbitrary per-item factory. */
1893
+ each(factory: StaggerFactory<TItem>): AnimationHandle;
1894
+ /** Sugar: per-item `animator.tween` with per-item-varying options. */
1895
+ tween<T>(opts: StaggerTweenOptions<T, TItem>): AnimationHandle;
1896
+ /** Sugar: per-item `springPose` against an adapter. `poseFn` returns the
1897
+ * target pose for each item. Each item must either be a primitive
1898
+ * (string/number) or expose a string `id` field — otherwise pose ids
1899
+ * would collide on `"[object Object]"` and successive tweens would
1900
+ * cancel each other. Throws on items that satisfy neither. */
1901
+ springPose<TPose>(adapter: SceneAdapter<{
1902
+ id: string;
1903
+ }, TPose>, poseFn: (item: TItem, index: number) => TPose, opts?: StaggerSpringPoseOptions<TPose>): AnimationHandle;
1904
+ }
1905
+ /** Options for `Animator.tweenLoop` — a tween's options plus how each
1906
+ * iteration relates to the last. */
1907
+ interface TweenLoopOptions<T> {
1908
+ from: T;
1909
+ to: T;
1910
+ ms: number;
1911
+ easing?: EasingFn;
1912
+ /** `restart` (default): from→to every iteration.
1913
+ * `reverse`: to→from every iteration.
1914
+ * `alternate`: even iterations from→to, odd iterations to→from. */
1915
+ direction?: 'restart' | 'reverse' | 'alternate';
1916
+ count?: number;
1917
+ interpolate?: Interpolate<T>;
1918
+ onTick: (value: T) => void;
1919
+ onDone?: () => void;
1920
+ cancelKey?: string;
1921
+ }
1922
+
1923
+ /** Cancel-key prefix. Each hook instance appends its own id, so two runners
1924
+ * sharing an animator do not cancel each other. */
1925
+ declare const VIEW_ANIMATION_KEY = "view";
1926
+ /** How the camera should move. */
1927
+ interface ViewAnimationOptions {
1928
+ /** Duration in ms. Default 250. */
1929
+ ms?: number;
1930
+ /** Easing curve. Default `easeOutCubic`. */
1931
+ easing?: EasingFn;
1932
+ /** Replace the kit's log-scale / fixed-anchor curve. */
1933
+ interpolator?: InterpolatorFactory<View>;
1934
+ /** Fires when the target is reached. Not called on cancel. */
1935
+ onDone?: () => void;
1936
+ }
1937
+ /** Options accepted by {@link ViewAnimationApi.animateToBounds}. */
1938
+ interface AnimateToBoundsOptions extends FitViewToBoundsOptions, ViewAnimationOptions {
1939
+ }
1940
+ /** What the runner reads and writes. On `<SceneCanvas>` this is the same
1941
+ * channel `view.set` uses, so a camera animation on an uncontrolled canvas
1942
+ * costs no React render. */
1943
+ interface ViewChannel {
1944
+ get(): View;
1945
+ set(v: View): void;
1946
+ }
1947
+ /** The camera animation surface. One animation at a time. */
1948
+ interface ViewAnimationApi {
1949
+ /** Glide from the live view to `to`. A thunk receives the pending target when
1950
+ * one is in flight, so successive discrete steps compound. */
1951
+ animate(to: View | ((base: View) => View), opts?: ViewAnimationOptions): void;
1952
+ /** `fitViewToBounds` composed with `animate`. */
1953
+ animateToBounds(bounds: Bounds, dims: ViewportDims, opts?: AnimateToBoundsOptions): void;
1954
+ /** Cancel. The view stays where it is — no jump to the target. */
1955
+ stop(): void;
1956
+ isAnimating(): boolean;
1957
+ /** Where the in-flight animation is heading, or null when none is. */
1958
+ target(): View | null;
1959
+ /** Cancel unless the write that prompted this came from the runner's own
1960
+ * per-frame write. Feed it from every channel that can move the camera. */
1961
+ stopIfExternal(): void;
1962
+ }
1963
+ /**
1964
+ * Animate the viewport `View`. Runs on the kit's {@link Animator} — pass one to
1965
+ * share a canvas's animator, or omit it and the hook makes its own.
1966
+ *
1967
+ * Every animation from one instance registers under that instance's cancel key,
1968
+ * so starting one cancels whatever *it* had in flight, and each starts from the
1969
+ * *live* view rather than a captured value — an interrupted camera never jumps.
1970
+ * Two instances on one animator are independent.
1971
+ */
1972
+ declare function useViewAnimation(view: ViewChannel, animator?: Animator): ViewAnimationApi;
1973
+
1330
1974
  /**
1331
1975
  * @experimental
1332
1976
  * PointerContext — a tiny ambient context that publishes the world-space
@@ -1588,16 +2232,16 @@ interface GeometryProjection {
1588
2232
  }, m: Mat3): unknown | null;
1589
2233
  }
1590
2234
 
1591
- /** Minimal view API the action layer consumes. May be refined later. */
2235
+ /** Minimal view API the action layer consumes. */
1592
2236
  interface ViewApi {
1593
2237
  get(): View;
1594
2238
  set(v: View): void;
1595
- /** Optional recenter callback. When wired, `viewportZoomAction`'s
1596
- * `reset` branch (Cmd-0) calls this instead of resetting to identity —
1597
- * letting consumers re-fit the page (or other reference bounds) into
1598
- * the workspace. Receives no args; the consumer reads its own bounds
1599
- * + host dims and dispatches `setView(...)`. */
1600
- recenter?(): void;
2239
+ /** Optional recenter callback. When wired, `viewportZoomAction`'s `reset`
2240
+ * branch (Cmd-0) calls this instead of resetting to identity — letting
2241
+ * consumers re-fit the page (or other reference bounds) into the workspace.
2242
+ * Return the target `View` to let the action animate there; return nothing
2243
+ * to keep dispatching the view yourself. */
2244
+ recenter?(): View | void;
1601
2245
  /** Optional canvas-local host dimensions (CSS px). When wired,
1602
2246
  * `viewportZoomAction`'s keyboard branches (Cmd+= / Cmd+-) anchor at the
1603
2247
  * host center instead of the top-left origin. Null when the host isn't
@@ -1606,6 +2250,13 @@ interface ViewApi {
1606
2250
  width: number;
1607
2251
  height: number;
1608
2252
  } | null;
2253
+ /** Optional camera animation. `<SceneCanvas>` wires these three; a consumer
2254
+ * publishing their own `view` dep need not, and actions fall back to `set`. */
2255
+ animate?(to: View, opts?: ViewAnimationOptions): void;
2256
+ stopAnimation?(): void;
2257
+ /** Where an in-flight camera animation is heading, or null. Compute the next
2258
+ * discrete step from this so repeated presses compound. */
2259
+ animationTarget?(): View | null;
1609
2260
  }
1610
2261
  /**
1611
2262
  * Adapter dep for `areaSelectAction`.
@@ -2557,14 +3208,13 @@ interface DispatcherContext {
2557
3208
  /** Platform flag for `mod` shorthand resolution. */
2558
3209
  isMac: boolean;
2559
3210
  /**
2560
- * Thunk returning a fresh `RuleCtx` for the current frame. When
2561
- * supplied, the dispatcher filters matched candidates by their
2562
- * declared `Action.eligible` rule (omitted => always eligible).
2563
- * When omitted, no eligibility filtering is applied preserves
2564
- * backward compatibility for callers (tests, legacy harnesses) that
2565
- * don't wire up chrome-caps state.
3211
+ * Thunk returning a fresh `RuleCtx` for the current frame — the routed
3212
+ * view's, when the surface hosts several. When it answers, the dispatcher
3213
+ * filters matched candidates by their declared `Action.eligible` rule
3214
+ * (omitted => always eligible). Absent, or answering `undefined`, applies
3215
+ * no eligibility filtering.
2566
3216
  */
2567
- getRuleCtx?: () => RuleCtx;
3217
+ getRuleCtx?: () => RuleCtx | undefined;
2568
3218
  }
2569
3219
  /**
2570
3220
  * Handle returned by `Dispatcher.beginUiOngoing()` for driving an
@@ -2812,6 +3462,12 @@ type BoundGesture = GestureSpec | {
2812
3462
  spec: GestureSpec;
2813
3463
  opts: BindingOpts;
2814
3464
  };
3465
+ /**
3466
+ * @experimental
3467
+ * Flatten an action's `defaultBinding` into `GestureBinding`s. A bare
3468
+ * `GestureSpec` has `kind` at top level; the object form has `spec`.
3469
+ */
3470
+ declare function actionBindings(action: Action): GestureBinding[];
2815
3471
  /**
2816
3472
  * @experimental
2817
3473
  * Single registered action. v1: one binding per action.
@@ -3030,17 +3686,14 @@ interface ActionsRegistry {
3030
3686
  declare function ActionsProvider({ children }: {
3031
3687
  children: ReactNode;
3032
3688
  }): ReactElement;
3033
- /**
3034
- * @experimental
3035
- * Returns the parent `ActionsRegistry`, or `null` when no provider is in scope.
3036
- */
3037
3689
  declare function useActionsRegistry(): ActionsRegistry | null;
3038
3690
  /**
3039
3691
  * @experimental
3040
- * Register an `Action` for the lifetime of the calling component. No-op when
3041
- * no `ActionsProvider` is in scope. Re-registers on `action` reference change
3042
- * (consumers should memoize stable identities to avoid churn).
3692
+ * Register an `Action` for the lifetime of the calling component. No-op (with
3693
+ * a dev-only warning) when no `ActionsProvider` is in scope. Re-registers on
3694
+ * `action` reference change (consumers should memoize stable identities to
3695
+ * avoid churn).
3043
3696
  */
3044
3697
  declare function useAction(action: Action): void;
3045
3698
 
3046
- export { type BooleanOp as $, type Action as A, type BooleansAdapter as B, type Condition as C, type Dims as D, type Eligibility as E, ALWAYS as F, type GeometryProjection as G, type HotkeyTrigger as H, type InsertExtras as I, type ActionDeps as J, ActionDisabledReason as K, type LayerHit as L, type ActionEnabledResult as M, type ActionEntry as N, type OverlayPosition as O, ActionsProvider as P, ActiveToolContextProvider as Q, type RenderLayer as R, type SliceDep as S, type Tool as T, type UseSelectionOptions as U, type VisibilityRules as V, ActiveToolContextProviderIfRoot as W, type ActiveToolContextProviderProps as X, type ActiveToolContextValue as Y, type AreaSelectDep as Z, type BindingOpts as _, type DeviceProfile as a, evaluateEnabled as a$, type BooleanOpResult as a0, type BoundGesture as a1, type BuildRuleCtxArgs as a2, type ClaimableGesture as a3, type ClipboardDep as a4, type ClipboardIngestCtx as a5, type CustomPaintContext as a6, type DepName as a7, type DepRegistry as a8, DepRegistryProvider as a9, type ResolveAllOptions as aA, type ResolveOnlyResult as aB, type ResolvedCandidate as aC, type SelectionExtendKey as aD, type SelectionMode as aE, type Selector as aF, type SnapDep as aG, type SvgUnpacker as aH, type TextEditDep as aI, type ToolCtx as aJ, type ToolModifiers as aK, type ToolPresentation as aL, type ToolSlot as aM, type UiOngoingControl as aN, type ViewApi as aO, applyBooleanOp as aP, buildRuleCtx as aQ, clipboardCopyAction as aR, clipboardCutAction as aS, composeRectPose as aT, composeWorldPose as aU, createDispatcher as aV, decomposeRectPose as aW, describeRule as aX, drawLayers as aY, enterTextEditAction as aZ, evaluate as a_, type DispatcherContext as aa, type DragSample as ab, type EditAnchorsDep as ac, type GestureBinding as ad, IDENTITY_POSE_COMPOSITION as ae, type ImmediateInvoker as af, type IngestCtx as ag, type IngestionDep as ah, type InsertDep as ai, type InvocationCtx as aj, type Invoker as ak, type LassoSelectDep as al, type LayoutDep as am, type MatchResult as an, NEVER as ao, type NodeAtPointDep as ap, type OngoingHandle as aq, type OngoingInvoker as ar, type OngoingOverlay as as, type Point2 as at, PointerContextProvider as au, type PointerContextValue as av, type PointerWorldPos as aw, type PoseAdapter as ax, type PoseComposition as ay, type ResizePolicy as az, type DetectedDeviceFacts as b, rebaseLocalPose as b0, registerContentHandler as b1, sliceAction as b2, specificity as b3, translateRectPose as b4, useAction as b5, useActionsRegistry as b6, useActiveToolContext as b7, useDepRegistry as b8, useDepSource as b9, useOptionalActiveToolContext as ba, useOptionalDepRegistry as bb, usePointerContext as bc, useSelection as bd, worldPoseLookup as be, type Rule as c, type RuleCtx as d, type ChromeCtx as e, type ChromeId as f, type DepSchema as g, type ActionsRegistry as h, type AffordanceHit as i, type Dispatcher as j, type ToolDef as k, type ViewportToolDef as l, type AnyTool as m, type ToolKeybinding as n, type ChromeState as o, type SelectionApi as p, type ContentHandlerEntry as q, type SvgIngestOptions as r, type ActionsProp as s, type Affordance as t, type AffordanceRegion as u, type AffordanceBinding as v, type CommonAffordanceScratch as w, type BindingScope as x, type Contribution as y, type ScopedBinding as z };
3699
+ export { ALWAYS as $, type Action as A, type BuiltinShapeToolId as B, type Condition as C, type Dims as D, ColorOverrideRegistry as E, type BooleansAdapter as F, type GeometryProjection as G, type HotkeyTrigger as H, type InsertExtras as I, type EasingFn as J, type SpringPresetName as K, type LayerHit as L, type SpringPreset as M, type UseAnimatorOptions as N, type OverlayPosition as O, type AnimationHandle as P, type VertexColorChannel as Q, type RenderLayer as R, type SliceDep as S, type Tool as T, type UseSelectionOptions as U, type VisibilityRules as V, type SampledTrack as W, type Eligibility as X, type BindingScope as Y, type Contribution as Z, type ScopedBinding as _, type DeviceProfile as a, type ResolveOnlyResult as a$, type ActionDeps as a0, ActionDisabledReason as a1, type ActionEnabledResult as a2, type ActionEntry as a3, ActionsProvider as a4, ActiveToolContextProvider as a5, ActiveToolContextProviderIfRoot as a6, type ActiveToolContextProviderProps as a7, type ActiveToolContextValue as a8, type AnimateToBoundsOptions as a9, type Interpolate as aA, type InterpolatorFactory as aB, type InvocationCtx as aC, type Invoker as aD, KIT_SHAPE_KINDS as aE, type Keyframe as aF, type LassoSelectDep as aG, type LayerCommandCache as aH, type LayoutDep as aI, type LoopFactory as aJ, type LoopOptions as aK, type MatchResult as aL, NEVER as aM, type NestedTimeline as aN, type NodeAtPointDep as aO, type OngoingHandle as aP, type OngoingInvoker as aQ, type PhysicsHandle as aR, type PhysicsOptions as aS, type Point2 as aT, PointerContextProvider as aU, type PointerContextValue as aV, type PointerWorldPos as aW, type PoseAdapter as aX, type PoseComposition as aY, type ResizePolicy as aZ, type ResolveAllOptions as a_, type AreaSelectDep as aa, type BindingOpts as ab, type BooleanOp as ac, type BooleanOpResult as ad, type BoundGesture as ae, type BuildRuleCtxArgs as af, type ClaimableGesture as ag, type ClipboardDep as ah, type ClipboardIngestCtx as ai, type ColorOverride as aj, type ColorOverrideFn as ak, type CustomPaintContext as al, type DecayOptions as am, type DepName as an, type DepRegistry as ao, DepRegistryProvider as ap, type DispatcherContext as aq, type DragSample as ar, type EditAnchorsDep as as, type EventTrack as at, type GestureBinding as au, IDENTITY_POSE_COMPOSITION as av, type ImmediateInvoker as aw, type IngestCtx as ax, type IngestionDep as ay, type InsertDep as az, type DetectedDeviceFacts as b, worldPoseLookup as b$, type ResolvedCandidate as b0, type SelectionExtendKey as b1, type SelectionMode as b2, type Selector as b3, type SnapDep as b4, type SpringOptions as b5, type StaggerBuilder as b6, type StaggerDelay as b7, type StaggerFactory as b8, type StaggerOptions as b9, composeWorldPose as bA, createDispatcher as bB, decomposeRectPose as bC, describeRule as bD, drawLayers as bE, drawOneLayer as bF, enterTextEditAction as bG, evaluate as bH, evaluateEnabled as bI, isLayerPainted as bJ, isLayerVisible as bK, rebaseLocalPose as bL, registerContentHandler as bM, resolveParams as bN, sliceAction as bO, specificity as bP, translateRectPose as bQ, useAction as bR, useActionsRegistry as bS, useActiveToolContext as bT, useDepRegistry as bU, useDepSource as bV, useOptionalActiveToolContext as bW, useOptionalDepRegistry as bX, usePointerContext as bY, useSelection as bZ, useViewAnimation as b_, type StaggerPerItem as ba, type StaggerSpringPoseOptions as bb, type StaggerTweenOptions as bc, type SvgUnpacker as bd, type TextEditDep as be, type TimelineHandle as bf, type TimelineOptions as bg, type TimelineTrack as bh, type ToolCtx as bi, type ToolModifiers as bj, type ToolPresentation as bk, type ToolSlot as bl, type Track as bm, type TweenLoopOptions as bn, type TweenOptions as bo, type UiOngoingControl as bp, VIEW_ANIMATION_KEY as bq, type ViewAnimationApi as br, type ViewApi as bs, type ViewChannel as bt, actionBindings as bu, applyBooleanOp as bv, buildRuleCtx as bw, clipboardCopyAction as bx, clipboardCutAction as by, composeRectPose as bz, type Rule as c, type RuleCtx as d, type ChromeCtx as e, type ChromeId as f, type ViewAnimationOptions as g, type DepSchema as h, type ActionsRegistry as i, type AffordanceHit as j, type Dispatcher as k, type ToolDef as l, type ViewportToolDef as m, type AnyTool as n, type ToolKeybinding as o, type OngoingOverlay as p, type ChromeState as q, type SelectionApi as r, type ContentHandlerEntry as s, type SvgIngestOptions as t, type ActionsProp as u, type Animator as v, type Affordance as w, type AffordanceRegion as x, type AffordanceBinding as y, type CommonAffordanceScratch as z };