@weasel-js/core 1.8.0 → 1.9.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 (139) hide show
  1. package/CHANGELOG.md +84 -0
  2. package/README.md +9 -5
  3. package/dist/animation/useAnimator.js +20 -13
  4. package/dist/animation/useAnimator.js.map +1 -1
  5. package/dist/{builtins-BW32dOXy.d.ts → builtins-CQcvcLXh.d.ts} +105 -31
  6. package/dist/canvas/DrawCanvas.js +33 -33
  7. package/dist/canvas/DrawCanvas.js.map +1 -1
  8. package/dist/canvas/MinimapCanvas.js +29 -29
  9. package/dist/canvas/MinimapCanvas.js.map +1 -1
  10. package/dist/canvas/NodeShape.js +63 -54
  11. package/dist/canvas/NodeShape.js.map +1 -1
  12. package/dist/canvas/SceneCanvas/features.js +26 -5
  13. package/dist/canvas/SceneCanvas/features.js.map +1 -1
  14. package/dist/canvas/SceneCanvas/useSceneSelectTool.js +3 -2
  15. package/dist/canvas/SceneCanvas/useSceneSelectTool.js.map +1 -1
  16. package/dist/canvas/SceneCanvas.js +526 -529
  17. package/dist/canvas/SceneCanvas.js.map +1 -1
  18. package/dist/canvas/SceneViewCanvas.js +35 -35
  19. package/dist/canvas/SceneViewCanvas.js.map +1 -1
  20. package/dist/canvas/defaultDrawOne.js +6 -1
  21. package/dist/canvas/defaultDrawOne.js.map +1 -1
  22. package/dist/canvas/pickWalk.js.map +1 -1
  23. package/dist/canvas/sceneAdapter.js.map +1 -1
  24. package/dist/canvas/useHitObserver.js +23 -0
  25. package/dist/canvas/useHitObserver.js.map +1 -0
  26. package/dist/canvas/useHostAnchor.js.map +1 -1
  27. package/dist/core/device/useDeviceProfile.js.map +1 -1
  28. package/dist/core/scene/derivedPath.js.map +1 -1
  29. package/dist/core/scene/effectivePose.js.map +1 -1
  30. package/dist/core/scene/kitRegistry.js.map +1 -1
  31. package/dist/core/scene/scene.js +9 -0
  32. package/dist/core/scene/scene.js.map +1 -1
  33. package/dist/core/scene/types.js.map +1 -1
  34. package/dist/core/scene/useScene.js.map +1 -1
  35. package/dist/core/selection/useSelection.js +27 -33
  36. package/dist/core/selection/useSelection.js.map +1 -1
  37. package/dist/core/stableStringify.js +8 -0
  38. package/dist/core/stableStringify.js.map +1 -0
  39. package/dist/core/viewport/fitViewToBounds.js +3 -3
  40. package/dist/core/viewport/fitViewToBounds.js.map +1 -1
  41. package/dist/core/viewport/screenSlop.js +8 -2
  42. package/dist/core/viewport/screenSlop.js.map +1 -1
  43. package/dist/features/minimap/actions.js.map +1 -1
  44. package/dist/features/paths/markerCommands.js +95 -63
  45. package/dist/features/paths/markerCommands.js.map +1 -1
  46. package/dist/features/paths/tessellate/dash.js +52 -0
  47. package/dist/features/paths/tessellate/dash.js.map +1 -0
  48. package/dist/features/paths/tessellate/insets.js +32 -0
  49. package/dist/features/paths/tessellate/insets.js.map +1 -0
  50. package/dist/features/paths/tessellate/metric.js +72 -0
  51. package/dist/features/paths/tessellate/metric.js.map +1 -0
  52. package/dist/features/paths/tessellate/stroke.js +74 -134
  53. package/dist/features/paths/tessellate/stroke.js.map +1 -1
  54. package/dist/features/paths/tessellate/vertexWidths.js +32 -0
  55. package/dist/features/paths/tessellate/vertexWidths.js.map +1 -0
  56. package/dist/features/pointer/PointerContext.js +2 -2
  57. package/dist/features/pointer/PointerContext.js.map +1 -1
  58. package/dist/features/poseRun/usePoseRun.js.map +1 -1
  59. package/dist/features/text/useTextEdit.js +86 -84
  60. package/dist/features/text/useTextEdit.js.map +1 -1
  61. package/dist/index.d.ts +84 -45
  62. package/dist/index.js +269 -268
  63. package/dist/interactions/actions/defaults/align.js.map +1 -1
  64. package/dist/interactions/actions/defaults/anchorEditing.js.map +1 -1
  65. package/dist/interactions/actions/defaults/areaSelect.js.map +1 -1
  66. package/dist/interactions/actions/defaults/booleans.js.map +1 -1
  67. package/dist/interactions/actions/defaults/cancelGesture.js.map +1 -1
  68. package/dist/interactions/actions/defaults/clearSelection.js.map +1 -1
  69. package/dist/interactions/actions/defaults/clipboard.js.map +1 -1
  70. package/dist/interactions/actions/defaults/clone.js.map +1 -1
  71. package/dist/interactions/actions/defaults/createOutlines.js +1 -1
  72. package/dist/interactions/actions/defaults/createOutlines.js.map +1 -1
  73. package/dist/interactions/actions/defaults/createPaintAction.js.map +1 -1
  74. package/dist/interactions/actions/defaults/delete.js.map +1 -1
  75. package/dist/interactions/actions/defaults/distribute.js.map +1 -1
  76. package/dist/interactions/actions/defaults/duplicate.js.map +1 -1
  77. package/dist/interactions/actions/defaults/editAnchors.js.map +1 -1
  78. package/dist/interactions/actions/defaults/enterPathEdit.js.map +1 -1
  79. package/dist/interactions/actions/defaults/enterTextEdit.js.map +1 -1
  80. package/dist/interactions/actions/defaults/escape.js +1 -1
  81. package/dist/interactions/actions/defaults/escape.js.map +1 -1
  82. package/dist/interactions/actions/defaults/exitPathEdit.js.map +1 -1
  83. package/dist/interactions/actions/defaults/flip.js.map +1 -1
  84. package/dist/interactions/actions/defaults/group.js.map +1 -1
  85. package/dist/interactions/actions/defaults/ingest.js.map +1 -1
  86. package/dist/interactions/actions/defaults/insert.js.map +1 -1
  87. package/dist/interactions/actions/defaults/insertPathAnchor.js +1 -1
  88. package/dist/interactions/actions/defaults/insertPathAnchor.js.map +1 -1
  89. package/dist/interactions/actions/defaults/lassoSelect.js.map +1 -1
  90. package/dist/interactions/actions/defaults/move.js.map +1 -1
  91. package/dist/interactions/actions/defaults/nudge.js.map +1 -1
  92. package/dist/interactions/actions/defaults/pinchZoom.js.map +1 -1
  93. package/dist/interactions/actions/defaults/reorder.js.map +1 -1
  94. package/dist/interactions/actions/defaults/requiresSelection.js.map +1 -1
  95. package/dist/interactions/actions/defaults/resize.js.map +1 -1
  96. package/dist/interactions/actions/defaults/rotate.js.map +1 -1
  97. package/dist/interactions/actions/defaults/selectAll.js.map +1 -1
  98. package/dist/interactions/actions/defaults/slice.js.map +1 -1
  99. package/dist/interactions/actions/defaults/toolActivate.js.map +1 -1
  100. package/dist/interactions/actions/defaults/toolResetToDefault.js.map +1 -1
  101. package/dist/interactions/actions/defaults/undoRedo.js +1 -1
  102. package/dist/interactions/actions/defaults/undoRedo.js.map +1 -1
  103. package/dist/interactions/actions/defaults/viewportDragPan.js.map +1 -1
  104. package/dist/interactions/actions/defaults/viewportWheelPan.js.map +1 -1
  105. package/dist/interactions/actions/defaults/viewportZoom.js.map +1 -1
  106. package/dist/interactions/actions/planeInput.js.map +1 -1
  107. package/dist/interactions/gestures/shared/screenTolerance.js.map +1 -1
  108. package/dist/{registerProgram-ateHDGjw.d.ts → registerProgram-BMZRDgrh.d.ts} +4 -0
  109. package/dist/renderer/cache/markerCommandCache.js +46 -42
  110. package/dist/renderer/cache/markerCommandCache.js.map +1 -1
  111. package/dist/renderer/cache/outlineStrokeMeshCache.js +30 -25
  112. package/dist/renderer/cache/outlineStrokeMeshCache.js.map +1 -1
  113. package/dist/renderer/cache/strokeMeshCache.js +42 -31
  114. package/dist/renderer/cache/strokeMeshCache.js.map +1 -1
  115. package/dist/renderer/cullDrawCommands.js +32 -31
  116. package/dist/renderer/cullDrawCommands.js.map +1 -1
  117. package/dist/renderer/draw.js +315 -362
  118. package/dist/renderer/draw.js.map +1 -1
  119. package/dist/renderer/drawBatch.js.map +1 -1
  120. package/dist/renderer/glyphOutlineMesh.js +67 -0
  121. package/dist/renderer/glyphOutlineMesh.js.map +1 -0
  122. package/dist/renderer/math/mat3.js +8 -3
  123. package/dist/renderer/math/mat3.js.map +1 -1
  124. package/dist/renderer/shaders/batchFill.js +3 -2
  125. package/dist/renderer/shaders/batchFill.js.map +1 -1
  126. package/dist/renderer/strokeLengths.js +50 -0
  127. package/dist/renderer/strokeLengths.js.map +1 -0
  128. package/dist/renderer.d.ts +2 -2
  129. package/dist/{test-seams-fYf6m5pu.d.ts → test-seams-CuIxe0xY.d.ts} +1 -1
  130. package/dist/test-seams.d.ts +2 -2
  131. package/dist/tools/builtin/eyedropper/useEyedropperTool.js.map +1 -1
  132. package/dist/tools/builtin/pen/usePenTool.js +1 -1
  133. package/dist/tools/builtin/pen/usePenTool.js.map +1 -1
  134. package/dist/tools/builtin/select/selectionContributions.js +43 -23
  135. package/dist/tools/builtin/select/selectionContributions.js.map +1 -1
  136. package/dist/tools/builtin/select/useSelectTool.js.map +1 -1
  137. package/dist/tools/builtin/slice/useSliceTool.js.map +1 -1
  138. package/dist/version.js +1 -1
  139. package/package.json +17 -16
@@ -1,4 +1,4 @@
1
- import { G as GlMat3, a as ShaderProgramHandle, b as ShaderUniform, c as ShaderProgram } from './registerProgram-ateHDGjw.js';
1
+ import { G as GlMat3, a as ShaderProgramHandle, b as ShaderUniform, c as ShaderProgram } from './registerProgram-BMZRDgrh.js';
2
2
  import { Mesh } from '@weasel-js/geom/tessellate';
3
3
  import { GradStop, ColorSpace, FillStyle, Stroke, ScreenLength } from '@weasel-js/paint';
4
4
  import { NodeId, Bounds, View, ModifierState, SelectionApi, ActionDeps, IngestItem, DragSample, DebugSink, Point2, LongPressState, AnyToolOf, Contribution as Contribution$1, ContributionChrome as ContributionChrome$1, Tool as Tool$1, ToolDef as ToolDef$1 } from '@weasel-js/routing';
@@ -112,8 +112,8 @@ declare const asNodeId: (s: string) => NodeId;
112
112
  * so handing over only the pose made the invalidation pay for a read nothing
113
113
  * could perform.
114
114
  */
115
- interface DerivedDep<TPose> {
116
- node: Node<unknown, string, TPose>;
115
+ interface DerivedDep<TPose, TData = unknown, TLayer extends string = string> {
116
+ node: Node<TData, TLayer, TPose>;
117
117
  /** Its override when it has one, else its own derived pose, else the pose
118
118
  * the document stores — `effectivePose`, the same answer the renderer uses. */
119
119
  pose: TPose;
@@ -126,12 +126,21 @@ interface DerivedDep<TPose> {
126
126
  * about costs nothing. */
127
127
  readonly path: Path | null;
128
128
  }
129
+ /** `F` with its parameters checked bivariantly, as a method's are, so that a
130
+ * `Scene<D, L, MyPose>` is a `Scene<unknown, string, unknown>`. A callback for
131
+ * an unrelated pose is still rejected; one for a narrower pose is not. */
132
+ type Bivariant<F extends (...args: never[]) => unknown> = {
133
+ method(...args: Parameters<F>): ReturnType<F>;
134
+ }['method'];
129
135
  /** Computes a node's path from its dependencies: the type of a node's
130
136
  * `derivePath` and of each entry in `SceneRegistry.derivePath`. */
131
- type DerivePathFn<TPose> = (node: Node<unknown, string, TPose>, deps: readonly (DerivedDep<TPose> | undefined)[]) => Path | null;
137
+ type DerivePathFn<TPose, TData = unknown, TLayer extends string = string> = Bivariant<(node: Node<TData, TLayer, TPose>, deps: readonly (DerivedDep<TPose, TData, TLayer> | undefined)[]) => Path | null>;
132
138
  /** Computes a node's pose from its dependencies: the type of a node's
133
139
  * `derivePose` and of each entry in `SceneRegistry.derivePose`. */
134
- type DerivePoseFn<TPose> = (node: Node<unknown, string, TPose>, deps: readonly (DerivedDep<TPose> | undefined)[]) => TPose | null;
140
+ type DerivePoseFn<TPose, TData = unknown, TLayer extends string = string> = Bivariant<(node: Node<TData, TLayer, TPose>, deps: readonly (DerivedDep<TPose, TData, TLayer> | undefined)[]) => TPose | null>;
141
+ /** A container's clip, from its pose: the type of `ContainerNode.clipFromPose`
142
+ * and of each entry in `SceneRegistry.clipFromPose`. */
143
+ type ClipFromPoseFn<TPose> = Bivariant<(pose: TPose) => Path | null>;
135
144
  interface NodeBase<TData, TLayer extends string, TPose> {
136
145
  id: NodeId;
137
146
  layer: TLayer;
@@ -165,14 +174,11 @@ interface NodeBase<TData, TLayer extends string, TPose> {
165
174
  * Returning `null` means "nothing to draw right now". Re-evaluated when a
166
175
  * dependency's world pose changes, never authored. Absolute-pose `Scene`
167
176
  * makes that the dependency's own pose, and an ancestor's move reaches it as
168
- * a `setPose` of its own from the container cascade.
169
- * `node` is deliberately widened: naming `TData`/`TLayer` here puts them in
170
- * a contravariant position, making `Scene` invariant in both and breaking
171
- * assignment kit-wide. The cost is that a `derivePath` casts to read `node.data`. */
172
- derivePath?: DerivePathFn<TPose>;
177
+ * a `setPose` of its own from the container cascade. */
178
+ derivePath?: DerivePathFn<TPose, TData, TLayer>;
173
179
  /** Computes this node's pose from its dependencies' poses, the same way
174
- * `derivePath` computes its path — same `dependsOn` list, same widened
175
- * `node`, same registry-keyed serialization.
180
+ * `derivePath` computes its path — same `dependsOn` list, same
181
+ * registry-keyed serialization.
176
182
  *
177
183
  * Where a derived path is resolved at paint time and reaches only the
178
184
  * painter, a derived pose is what the node *is* at: it feeds bounds,
@@ -183,7 +189,7 @@ interface NodeBase<TData, TLayer extends string, TPose> {
183
189
  * Returning `null` means "I have nothing to derive from right now", and
184
190
  * the node falls back to its authored `pose`. `setPose` on a derived node
185
191
  * still writes that authored pose; it is simply not what anything reads. */
186
- derivePose?: DerivePoseFn<TPose>;
192
+ derivePose?: DerivePoseFn<TPose, TData, TLayer>;
187
193
  }
188
194
  /** A node with no children — a shape, a label, an image. */
189
195
  interface LeafNode<TData, TLayer extends string, TPose = RectPose> extends NodeBase<TData, TLayer, TPose> {
@@ -200,7 +206,7 @@ interface ContainerNode<TData, TLayer extends string, TPose = RectPose> extends
200
206
  * means "clip everything out" (children render nowhere). When set, the
201
207
  * renderer rasterizes the returned path into the stencil buffer and
202
208
  * paints descendants only where it covers. */
203
- clipFromPose?: (pose: TPose) => Path | null;
209
+ clipFromPose?: ClipFromPoseFn<TPose>;
204
210
  /** How this container arranges its children. Whenever the container's
205
211
  * child set or order changes, or its bounds change size, the scene hands
206
212
  * the change to the strategy — `depart` for children that left it,
@@ -270,14 +276,14 @@ interface AddNodeSpec<TData, TLayer extends string, TPose = RectPose> {
270
276
  pickable?: boolean;
271
277
  /** Only meaningful when `kind === 'container'`. Attach a clip-path function
272
278
  * to the node; ignored for leaves. Mirrors `ContainerNode.clipFromPose`. */
273
- clipFromPose?: (pose: TPose) => Path | null;
279
+ clipFromPose?: ClipFromPoseFn<TPose>;
274
280
  /** Mirrors `SceneNode.dependsOn`. */
275
281
  dependsOn?: readonly NodeId[] | 'children';
276
282
  /** Mirrors `SceneNode.derivePath`. Taken as a live function; its registry key is
277
283
  * looked up from it, never passed in. */
278
- derivePath?: DerivePathFn<TPose>;
284
+ derivePath?: DerivePathFn<TPose, TData, TLayer>;
279
285
  /** Mirrors `SceneNode.derivePose`, on the same terms as `derivePath`. */
280
- derivePose?: DerivePoseFn<TPose>;
286
+ derivePose?: DerivePoseFn<TPose, TData, TLayer>;
281
287
  /** Mirrors `ContainerNode.layout`; ignored for leaves. */
282
288
  layout?: LayoutStrategy<TPose>;
283
289
  }
@@ -356,13 +362,13 @@ interface SerializedNode<TData, TLayer extends string, TPose> {
356
362
  /** Per-scene registry mapping string keys to live function references.
357
363
  * Passed to `createScene({ ..., registry })` and `sceneFromJSON(json, { registry })`.
358
364
  * Each function-field type has its own keyed map. */
359
- interface SceneRegistry<TPose> {
365
+ interface SceneRegistry<TPose, TData = unknown, TLayer extends string = string> {
360
366
  /** Maps registry keys to `clipFromPose` factory functions for container nodes. */
361
- clipFromPose?: Readonly<Record<string, (pose: TPose) => Path | null>>;
367
+ clipFromPose?: Readonly<Record<string, ClipFromPoseFn<TPose>>>;
362
368
  /** Maps registry keys to `derivePath` functions for nodes with `dependsOn`. */
363
- derivePath?: Readonly<Record<string, DerivePathFn<TPose>>>;
369
+ derivePath?: Readonly<Record<string, DerivePathFn<TPose, TData, TLayer>>>;
364
370
  /** Maps registry keys to `derivePose` functions for nodes with `dependsOn`. */
365
- derivePose?: Readonly<Record<string, DerivePoseFn<TPose>>>;
371
+ derivePose?: Readonly<Record<string, DerivePoseFn<TPose, TData, TLayer>>>;
366
372
  /** Maps registry keys to container layout strategies. */
367
373
  layout?: Readonly<Record<string, LayoutStrategy<TPose>>>;
368
374
  }
@@ -405,7 +411,7 @@ interface UseSceneOptions<TData, TLayer extends string, TPose = RectPose> {
405
411
  generateId?: () => NodeId;
406
412
  /** Per-scene registry for non-serializable function fields (clipFromPose, etc.).
407
413
  * Required only when serializing/deserializing scenes that use function fields. */
408
- registry?: SceneRegistry<TPose>;
414
+ registry?: SceneRegistry<TPose, TData, TLayer>;
409
415
  /** How laid-out containers are measured. See {@link LayoutFrame}. */
410
416
  layoutFrame?: LayoutFrame<TPose>;
411
417
  /** When supplied, `scene.applyOps(ops, label)` consults this on every call.
@@ -501,7 +507,7 @@ interface Scene<TData, TLayer extends string, TPose = RectPose> {
501
507
  readonly layers: readonly LayerRecord<TLayer>[];
502
508
  /** The registry this scene resolves node functions against — the consumer's
503
509
  * entries over the kit's. */
504
- readonly registry: SceneRegistry<TPose>;
510
+ readonly registry: SceneRegistry<TPose, TData, TLayer>;
505
511
  get(id: NodeId): Node<TData, TLayer, TPose> | undefined;
506
512
  /** A token for the node `id` names right now, for telling one node from a
507
513
  * later one that reuses its id. It holds for as long as the node stays in
@@ -791,6 +797,13 @@ interface FitViewToBoundsOptions {
791
797
  * - `'stretch'`: per-axis scale. Bounds match viewport exactly; scale is non-uniform.
792
798
  */
793
799
  mode?: 'contain' | 'fill' | 'stretch';
800
+ /**
801
+ * Where bounds that overflow the viewport on an axis sit, which happens when
802
+ * `minScale` stops the fit or `mode` is `'fill'`. `'center'` (default) shows
803
+ * their middle; `'start'` shows their left or top edge, inset by `padding`.
804
+ * An axis that fits is centered either way.
805
+ */
806
+ anchor?: 'center' | 'start';
794
807
  }
795
808
  /**
796
809
  * Compute the target {@link View} that fits `bounds` (world space) inside
@@ -2950,6 +2963,9 @@ interface LiveAnimation extends AnimationInfo {
2950
2963
  * - `cancel` — stopped by its handle, `cancel`, `cancelKey` or `cancelAll`.
2951
2964
  * - `interrupt` — stopped because `by` claimed its `cancelKey`. Delivered
2952
2965
  * before `by`'s `start`.
2966
+ * - `error` — its tick threw (an `onTick`, `onDone` or interpolator), so it was
2967
+ * taken off the table, after the frame's other animations ticked. In place of
2968
+ * `cancel`, or after `end` when `onDone` threw. The error is also logged.
2953
2969
  * - `lap` — a looping timeline wrapped; `lap` counts wraps since it started.
2954
2970
  * - `fire` — an event on a timeline's event track was crossed, at any depth.
2955
2971
  * `path` indexes from the root timeline's `tracks()` down through each
@@ -2962,6 +2978,10 @@ type AnimatorEvent = {
2962
2978
  readonly type: 'interrupt';
2963
2979
  readonly animation: AnimationInfo;
2964
2980
  readonly by: AnimationInfo;
2981
+ } | {
2982
+ readonly type: 'error';
2983
+ readonly animation: AnimationInfo;
2984
+ readonly error: unknown;
2965
2985
  } | {
2966
2986
  readonly type: 'lap';
2967
2987
  readonly animation: AnimationInfo;
@@ -4147,7 +4167,8 @@ declare module '@weasel-js/routing' {
4147
4167
  * Scene tree — structural reads + undoable mutations.
4148
4168
  *
4149
4169
  * The entry uses the fully-erased form `Scene<unknown, string, unknown>`
4150
- * because `DepSchema` must be concrete. Actions that need a typed scene
4170
+ * because `DepSchema` must be concrete; any `Scene<MyData, MyLayer,
4171
+ * MyPose>` is assignable to it. Actions that need a typed scene back
4151
4172
  * should cast: `deps.scene as Scene<MyData, MyLayer, MyPose>`.
4152
4173
  */
4153
4174
  scene: Scene<unknown, string, unknown>;
@@ -4376,17 +4397,70 @@ type UseToolsOptions = UseToolsOptions$1<Overlay>;
4376
4397
  /** Routing's `useTools`, typed for `RenderLayer` overlays. */
4377
4398
  declare const useTools: (opts: UseToolsOptions) => ToolsApi;
4378
4399
 
4400
+ /**
4401
+ * The space a screen-pixel stroke is built in.
4402
+ *
4403
+ * A `{ px }` stroke is exact on screen only when its ribbon is offset in a
4404
+ * space that measures lengths the way the screen does. The transform's linear
4405
+ * part is `scale · R · P`: a uniform scale, a rotation (or reflection) that
4406
+ * moves no lengths, and `P`, the symmetric unit-determinant stretch left over.
4407
+ * Offsetting the ribbon after `P` and mapping it back through `P⁻¹` gives a
4408
+ * world-space mesh that the full transform turns into a ribbon of exactly
4409
+ * `width · scale` pixels in every direction — and keeps the mesh drawable by
4410
+ * every path that draws a world-space ribbon.
4411
+ *
4412
+ * `P` depends only on the lengths the transform induces (`LᵀL`), not on any
4413
+ * rotation applied after it, so a view that only pans or zooms uniformly keeps
4414
+ * the same metric and the ribbon cache keeps hitting.
4415
+ */
4416
+
4417
+ /** `P` = [[p, q], [q, r]], with `pr − q² = 1`. */
4418
+ interface StrokeMetric {
4419
+ readonly p: number;
4420
+ readonly q: number;
4421
+ readonly r: number;
4422
+ }
4423
+
4424
+ /**
4425
+ * Trimming a stroke's ends where each inset is measured.
4426
+ *
4427
+ * A head stops the line by a length in the head's own space, which need not be
4428
+ * the ribbon's: a world-sized head on a `{ px }` stroke stops it a world
4429
+ * distance short, a `{ px }` head on a world-width stroke a screen distance.
4430
+ * Under a stretching view one length means different distances in the two
4431
+ * spaces depending on which way the line runs, so each end is trimmed in its
4432
+ * own space rather than converted.
4433
+ */
4434
+
4435
+ /** A length along the line measured in `metric`'s space, or in world where
4436
+ * `metric` is `null`. */
4437
+ interface MeasuredLength {
4438
+ readonly length: number;
4439
+ readonly metric: StrokeMetric | null;
4440
+ }
4441
+ /** A plain number is a length in the ribbon's own space. */
4442
+ type InsetLength = number | MeasuredLength;
4443
+
4379
4444
  /** Options for stroke tessellation. */
4380
4445
  interface StrokeOptions {
4381
4446
  flattenTolerance?: number;
4382
4447
  /**
4383
- * Shorten each open subpath by this much at its start / end, in the same
4384
- * world units as the path. Stroke markers use this so a filled head is not
4385
- * speared by its own line; the caller resolves the distance, because this
4386
- * layer knows nothing about the marker registry.
4448
+ * Shorten each open subpath by this much at its start / end: a length in
4449
+ * the ribbon's space, or one measured in a space of its own. Stroke markers
4450
+ * use this so a filled head is not speared by its own line; the caller
4451
+ * resolves the distance, because this layer knows nothing about the marker
4452
+ * registry.
4453
+ */
4454
+ startInset?: InsetLength;
4455
+ endInset?: InsetLength;
4456
+ /**
4457
+ * Build the ribbon in this metric's space and map it back out, so the
4458
+ * transform it was derived from draws it at an exact screen width — see
4459
+ * `./metric`. Width and dash are then lengths in that space; `vertexWidths`
4460
+ * stay world widths. The path is flattened in world first, so the ribbon
4461
+ * follows the same vertices its fill does.
4387
4462
  */
4388
- startInset?: number;
4389
- endInset?: number;
4463
+ metric?: StrokeMetric;
4390
4464
  }
4391
4465
  /** Resolve a stroke width to world units. A number is already world units;
4392
4466
  * `{ px }` is screen pixels divided by the accumulated scale, so it holds its
@@ -4435,4 +4509,4 @@ declare function vignette({ amount, feather }: {
4435
4509
  feather?: number;
4436
4510
  }): Effect[];
4437
4511
 
4438
- export { type SvgIngestOptions as $, type PoseComposition as A, type PoseAdapter as B, type CullRect as C, type DrawCommand as D, type Effect as E, type GestureLifecycleOptions as F, type GroupDrawCommand as G, type SliceDep as H, IDENTITY_COLOR_MATRIX as I, type ToolsApi as J, type Tool as K, type Contribution as L, type LayerHit as M, type Node as N, type ChromeState as O, type PathDrawCommand as P, type VertexColorChannel as Q, type RenderOptions as R, SPRITE_STRIDE as S, type TextDrawCommand as T, type LayerGroup as U, type ViewAnimationOptions as V, WeaselRenderer as W, type InsertExtras as X, type SerializedScene as Y, type GeometryProjection as Z, type ContentHandlerEntry as _, type ImageDrawCommand as a, type LayoutMove as a$, type AnyTool as a0, type PanBounds as a1, type Animator as a2, ColorOverrideRegistry as a3, type UseSceneOptions as a4, type SceneRegistry as a5, type RegisteredOp as a6, type PoseOverrides as a7, type DerivedDep as a8, type ViewportDims as a9, type ContainerNode as aA, type ContributionChrome as aB, type CreateOutlinesResult as aC, type CustomPaintContext as aD, type DecayLoopConfig as aE, type DecayOptions as aF, type DerivePathFn as aG, type EditAnchorsDep as aH, type EventBooking as aI, type EventBookingHandle as aJ, type EventTrack as aK, type FitViewToBoundsOptions as aL, type HitTestView as aM, IDENTITY_POSE_COMPOSITION as aN, type IngestCtx as aO, type IngestionDep as aP, type InsertDep as aQ, type Interpolate as aR, type InterpolatorFactory as aS, type Keyframe as aT, type LassoSelectDep as aU, type LayerCommandCache as aV, type LayerDrawFailure as aW, type LayerDrawSpan as aX, type LayerRecord as aY, type LayoutDep as aZ, type LayoutFrame as a_, type ViewApi as aa, type Affordance as ab, type AffordanceRegion as ac, type AffordanceBinding as ad, type CommonAffordanceScratch as ae, type DerivePoseFn as af, type BooleansAdapter as ag, type CreateOutlinesAdapter as ah, type UseAnimatorOptions as ai, type AnimationHandle as aj, type SampledTrack as ak, type SceneArrivalHandler as al, type AddLayerSpec as am, type AddNodeSpec as an, type AnimateToBoundsOptions as ao, type AnimationInfo as ap, type AnimationKind as aq, type AnimatorEvent as ar, type AnimatorListener as as, type AreaSelectDep as at, type BooleanOp as au, type BooleanOpResult as av, type ClipboardDep as aw, type ClipboardIngestCtx as ax, type ColorOverride as ay, type ColorOverrideFn as az, type ImageMinification as b, loadTextOutlines as b$, type LeafNode as b0, type LiveAnimation as b1, type LoopFactory as b2, type LoopOptions as b3, type NestedTimeline as b4, type NodeAtPointDep as b5, type OutlinePathSpec as b6, type Overlay as b7, type PathInWorldPose as b8, type PhysicsHandle as b9, type TextPaintPath as bA, type TextToPathOptions as bB, type TimelineClock as bC, type TimelineEvent as bD, type TimelineHandle as bE, type TimelineOptions as bF, type TimelineTrack as bG, type ToolDef as bH, type Track as bI, type TweenLoopOptions as bJ, type TweenOptions as bK, type UseToolsOptions as bL, type UserLayerRecord as bM, VIEW_ANIMATION_KEY as bN, type ViewAnimationApi as bO, type ViewChannel as bP, applyBooleanOp as bQ, applyCreateOutlines as bR, asNodeId as bS, composeWorldPose as bT, createPointerStore as bU, depsUnchanged as bV, drawLayers as bW, drawOneLayer as bX, fitViewToBounds as bY, isLayerPainted as bZ, isLayerVisible as b_, type PhysicsOptions as ba, PointerContextProvider as bb, type PointerContextValue as bc, type PointerWorldPos as bd, type PoseClosure as be, type PoseOverride as bf, type ResizePolicy as bg, type SerializedLayer as bh, type SerializedNode as bi, type SnapDep as bj, type SpringOptions as bk, type StaggerBuilder as bl, type StaggerDelay as bm, type StaggerFactory as bn, type StaggerOptions as bo, type StaggerPerItem as bp, type StaggerSpringPoseOptions as bq, type StaggerTweenOptions as br, type SvgUnpacker as bs, type SystemLayerRecord as bt, type SystemLayerSpec as bu, type TextEditDep as bv, type TextNodeSource as bw, type TextOutlineSource as bx, TextOutlinesError as by, type TextOutlinesFailure as bz, type RenderSpan as c, pathInPoseFrame as c0, pathInWorld as c1, rebaseLocalPose as c2, registerContentHandler as c3, resolveStrokeWidth as c4, subscribeToSources as c5, textToPath as c6, textToPathsByPaint as c7, useDecayLoop as c8, usePointerContext as c9, usePointerPosition as ca, useTools as cb, useViewAnimation as cc, useViewAnimationOn as cd, worldEditToStorage as ce, type RenderSpanStats as d, type RenderStats as e, type RenderTarget as f, type ShaderDrawCommand as g, type SolidPaint as h, type SpriteSheet as i, type SpritesDrawCommand as j, type StrokeOptions as k, type WeaselRendererOptions as l, blur as m, buildGradientRamp as n, cullDrawCommands as o, frameRect as p, vignette as q, registerEffect as r, type RenderLayer as s, tessellateStroke as t, type Dims as u, viewToMat3 as v, type RedrawSource as w, type Scene as x, type RectPose as y, type InertiaConfig as z };
4512
+ export { type SvgIngestOptions as $, type PoseComposition as A, type PoseAdapter as B, type CullRect as C, type DrawCommand as D, type Effect as E, type GestureLifecycleOptions as F, type GroupDrawCommand as G, type SliceDep as H, IDENTITY_COLOR_MATRIX as I, type ToolsApi as J, type Tool as K, type Contribution as L, type LayerHit as M, type Node as N, type ChromeState as O, type PathDrawCommand as P, type VertexColorChannel as Q, type RenderOptions as R, SPRITE_STRIDE as S, type TextDrawCommand as T, type LayerGroup as U, type ViewAnimationOptions as V, WeaselRenderer as W, type InsertExtras as X, type SerializedScene as Y, type GeometryProjection as Z, type ContentHandlerEntry as _, type ImageDrawCommand as a, type LayoutFrame as a$, type AnyTool as a0, type PanBounds as a1, type Animator as a2, ColorOverrideRegistry as a3, type UseSceneOptions as a4, type SceneRegistry as a5, type RegisteredOp as a6, type PoseOverrides as a7, type DerivePoseFn as a8, type DerivePathFn as a9, type ColorOverride as aA, type ColorOverrideFn as aB, type ContainerNode as aC, type ContributionChrome as aD, type CreateOutlinesResult as aE, type CustomPaintContext as aF, type DecayLoopConfig as aG, type DecayOptions as aH, type EditAnchorsDep as aI, type EventBooking as aJ, type EventBookingHandle as aK, type EventTrack as aL, type FitViewToBoundsOptions as aM, type HitTestView as aN, IDENTITY_POSE_COMPOSITION as aO, type IngestCtx as aP, type IngestionDep as aQ, type InsertDep as aR, type Interpolate as aS, type InterpolatorFactory as aT, type Keyframe as aU, type LassoSelectDep as aV, type LayerCommandCache as aW, type LayerDrawFailure as aX, type LayerDrawSpan as aY, type LayerRecord as aZ, type LayoutDep as a_, type DerivedDep as aa, type ViewportDims as ab, type ViewApi as ac, type Affordance as ad, type AffordanceRegion as ae, type AffordanceBinding as af, type CommonAffordanceScratch as ag, type BooleansAdapter as ah, type CreateOutlinesAdapter as ai, type UseAnimatorOptions as aj, type AnimationHandle as ak, type SampledTrack as al, type SceneArrivalHandler as am, type AddLayerSpec as an, type AddNodeSpec as ao, type AnimateToBoundsOptions as ap, type AnimationInfo as aq, type AnimationKind as ar, type AnimatorEvent as as, type AnimatorListener as at, type AreaSelectDep as au, type BooleanOp as av, type BooleanOpResult as aw, type ClipFromPoseFn as ax, type ClipboardDep as ay, type ClipboardIngestCtx as az, type ImageMinification as b, isLayerVisible as b$, type LayoutMove as b0, type LeafNode as b1, type LiveAnimation as b2, type LoopFactory as b3, type LoopOptions as b4, type NestedTimeline as b5, type NodeAtPointDep as b6, type OutlinePathSpec as b7, type Overlay as b8, type PathInWorldPose as b9, type TextOutlinesFailure as bA, type TextPaintPath as bB, type TextToPathOptions as bC, type TimelineClock as bD, type TimelineEvent as bE, type TimelineHandle as bF, type TimelineOptions as bG, type TimelineTrack as bH, type ToolDef as bI, type Track as bJ, type TweenLoopOptions as bK, type TweenOptions as bL, type UseToolsOptions as bM, type UserLayerRecord as bN, VIEW_ANIMATION_KEY as bO, type ViewAnimationApi as bP, type ViewChannel as bQ, applyBooleanOp as bR, applyCreateOutlines as bS, asNodeId as bT, composeWorldPose as bU, createPointerStore as bV, depsUnchanged as bW, drawLayers as bX, drawOneLayer as bY, fitViewToBounds as bZ, isLayerPainted as b_, type PhysicsHandle as ba, type PhysicsOptions as bb, PointerContextProvider as bc, type PointerContextValue as bd, type PointerWorldPos as be, type PoseClosure as bf, type PoseOverride as bg, type ResizePolicy as bh, type SerializedLayer as bi, type SerializedNode as bj, type SnapDep as bk, type SpringOptions as bl, type StaggerBuilder as bm, type StaggerDelay as bn, type StaggerFactory as bo, type StaggerOptions as bp, type StaggerPerItem as bq, type StaggerSpringPoseOptions as br, type StaggerTweenOptions as bs, type SvgUnpacker as bt, type SystemLayerRecord as bu, type SystemLayerSpec as bv, type TextEditDep as bw, type TextNodeSource as bx, type TextOutlineSource as by, TextOutlinesError as bz, type RenderSpan as c, loadTextOutlines as c0, pathInPoseFrame as c1, pathInWorld as c2, rebaseLocalPose as c3, registerContentHandler as c4, resolveStrokeWidth as c5, subscribeToSources as c6, textToPath as c7, textToPathsByPaint as c8, useDecayLoop as c9, usePointerContext as ca, usePointerPosition as cb, useTools as cc, useViewAnimation as cd, useViewAnimationOn as ce, worldEditToStorage as cf, type RenderSpanStats as d, type RenderStats as e, type RenderTarget as f, type ShaderDrawCommand as g, type SolidPaint as h, type SpriteSheet as i, type SpritesDrawCommand as j, type StrokeOptions as k, type WeaselRendererOptions as l, blur as m, buildGradientRamp as n, cullDrawCommands as o, frameRect as p, vignette as q, registerEffect as r, type RenderLayer as s, tessellateStroke as t, type Dims as u, viewToMat3 as v, type RedrawSource as w, type Scene as x, type RectPose as y, type InertiaConfig as z };
@@ -5,10 +5,10 @@ import { useFrameLoop as r } from "./useFrameLoop.js";
5
5
  import { useRedrawOn as i } from "./useRedrawOn.js";
6
6
  import { useCanvasRenderer as a } from "./useCanvasRenderer.js";
7
7
  import { useCallback as o, useEffect as s, useLayoutEffect as c, useRef as l } from "react";
8
- import { useLatest as u } from "@weasel-js/react";
9
- import { jsx as d } from "react/jsx-runtime";
8
+ import { assignRef as u, useLatest as d } from "@weasel-js/react";
9
+ import { jsx as f } from "react/jsx-runtime";
10
10
  //#region src/canvas/DrawCanvas.tsx
11
- var f = {
11
+ var p = {
12
12
  x: 0,
13
13
  y: 0,
14
14
  scale: {
@@ -16,52 +16,52 @@ var f = {
16
16
  y: 1
17
17
  }
18
18
  };
19
- function p(p) {
20
- let { width: m, height: h, draw: g, background: _, redrawOn: v, dpr: y, className: b, canvasRef: x } = p, S = p.view ? t(p.view) : f, C = l(null), w = o((e) => {
21
- C.current = e, typeof x == "function" ? x(e) : x && (x.current = e);
22
- }, [x]), { paint: T } = a(), E = u(() => {
23
- let t = C.current;
19
+ function m(m) {
20
+ let { width: h, height: g, draw: _, background: v, redrawOn: y, dpr: b, className: x, canvasRef: S } = m, C = m.view ? t(m.view) : p, w = l(null), T = o((e) => {
21
+ w.current = e, u(S, e);
22
+ }, [S]), { paint: E } = a(), D = d(() => {
23
+ let t = w.current;
24
24
  if (!t) return !1;
25
- let n = typeof g == "function" ? g({
26
- width: m,
27
- height: h
28
- }) : g, r = [];
29
- return _ && r.push({
25
+ let n = typeof _ == "function" ? _({
26
+ width: h,
27
+ height: g
28
+ }) : _, r = [];
29
+ return v && r.push({
30
30
  kind: "path",
31
31
  path: {
32
32
  kind: "rect",
33
33
  x: 0,
34
34
  y: 0,
35
- width: m,
36
- height: h
35
+ width: h,
36
+ height: g
37
37
  },
38
- fill: _
38
+ fill: v
39
39
  }), r.push({
40
40
  kind: "group",
41
- transform: e(S),
41
+ transform: e(C),
42
42
  children: [...n]
43
- }), T(t, r, S, {
44
- width: m,
45
- height: h,
46
- dpr: y
43
+ }), E(t, r, C, {
44
+ width: h,
45
+ height: g,
46
+ dpr: b
47
47
  });
48
- }), { requestRedraw: D } = r(o(() => E.current(), [E]), { target: C }), O = l(!1);
48
+ }), { requestRedraw: O } = r(o(() => D.current(), [D]), { target: w }), k = l(!1);
49
49
  return c(() => {
50
- if (O.current) {
51
- D();
50
+ if (k.current) {
51
+ O();
52
52
  return;
53
53
  }
54
- O.current = E.current(), O.current || D();
55
- }), n(D), i(v, D), s(() => () => {
56
- O.current = !1;
57
- }, []), /* @__PURE__ */ d("canvas", {
58
- ref: w,
59
- width: m,
60
- height: h,
61
- className: b
54
+ k.current = D.current(), k.current || O();
55
+ }), n(O), i(y, O), s(() => () => {
56
+ k.current = !1;
57
+ }, []), /* @__PURE__ */ f("canvas", {
58
+ ref: T,
59
+ width: h,
60
+ height: g,
61
+ className: x
62
62
  });
63
63
  }
64
64
  //#endregion
65
- export { p as DrawCanvas };
65
+ export { m as DrawCanvas };
66
66
 
67
67
  //# sourceMappingURL=DrawCanvas.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"DrawCanvas.js","names":["IDENTITY_VIEW","DrawCanvas","props","view","normalizeView","canvasElRef","useRef","setRefs","useCallback","el","canvasRef","paintCanvas","useCanvasRenderer","paintRef","useLatest","content","draw","commands","background","viewToMat3","useFrameLoop","paintedRef","useLayoutEffect","requestRedraw","useLateContentRedraw","useRedrawOn","redrawOn","useEffect"],"sources":["../../src/canvas/DrawCanvas.tsx"],"sourcesContent":["/**\n * `<DrawCanvas>` — a sized, DPR-correct WebGL2 surface that paints a list of\n * draw commands, with no scene, tools or dispatcher behind it.\n *\n * It paints through the same per-canvas renderer as `<SceneViewCanvas>`\n * (`useCanvasRenderer`), on a `useFrameLoop` that stops while the canvas is off\n * screen or the tab is hidden. Like `<SceneViewCanvas>` it attaches no event\n * listeners and emits no inline styles: size the element with `className`.\n */\nimport { useCallback, useEffect, useLayoutEffect, useRef } from 'react';\nimport type { Ref } from 'react';\nimport { useLatest } from '@weasel-js/react';\nimport type { FillStyle } from '@weasel-js/paint';\nimport { useFrameLoop } from './useFrameLoop';\nimport { useLateContentRedraw } from './useLateContentRedraw';\nimport { useRedrawOn } from './useRedrawOn';\nimport { useCanvasRenderer } from './useCanvasRenderer';\nimport { viewToMat3 } from '../renderer/math/viewToMat3';\nimport type { DrawCommand } from '../renderer/DrawCommand';\nimport type { RedrawSource } from '../core/layers/render';\nimport { normalizeView, type View } from '../core/viewport/view';\n\n/** What a `<DrawCanvas>` paints: a command list, or a function returning one\n * that is called on every paint with the surface's CSS-pixel size. */\nexport type DrawCanvasDraw =\n | readonly DrawCommand[]\n | ((size: { width: number; height: number }) => readonly DrawCommand[]);\n\n/** Props for `<DrawCanvas>`. */\nexport interface DrawCanvasProps {\n /** CSS-pixel width. The drawing buffer is `width × dpr`. */\n width: number;\n /** CSS-pixel height. */\n height: number;\n /** The commands to paint, in the coordinate space `view` maps to the\n * screen — CSS pixels when `view` is omitted. A new array repaints on the\n * next frame. A function is read at paint time, so state it reads outside\n * React repaints when one of `redrawOn`'s sources notifies. */\n draw: DrawCanvasDraw;\n /** Camera over `draw`'s commands. Defaults to the identity view. */\n view?: View;\n /** Fills the whole surface beneath `draw`, in screen space. The canvas is\n * transparent without it. */\n background?: FillStyle;\n /** External state a `draw` function reads. Each source is subscribed while\n * mounted and repaints the canvas when it notifies. */\n redrawOn?: readonly RedrawSource[];\n /** Device-pixel ratio. Defaults to `window.devicePixelRatio`, read per\n * paint. */\n dpr?: number;\n /** CSS class for the `<canvas>` element. */\n className?: string;\n /** Receives the underlying `<canvas>`, for pointer listeners or `toBlob`. */\n canvasRef?: Ref<HTMLCanvasElement>;\n}\n\nconst IDENTITY_VIEW: View = { x: 0, y: 0, scale: { x: 1, y: 1 } };\n\n/**\n * Paint draw commands onto a canvas of its own. For a surface that only draws —\n * a package demo, a swatch, a preview — where a `<SceneCanvas>` would bring a\n * scene, tools and an input dispatcher that nothing uses.\n */\nexport function DrawCanvas(props: DrawCanvasProps) {\n const { width, height, draw, background, redrawOn, dpr, className, canvasRef } = props;\n const view = props.view ? normalizeView(props.view) : IDENTITY_VIEW;\n\n const canvasElRef = useRef<HTMLCanvasElement | null>(null);\n const setRefs = useCallback((el: HTMLCanvasElement | null) => {\n canvasElRef.current = el;\n if (typeof canvasRef === 'function') canvasRef(el);\n else if (canvasRef) (canvasRef as { current: HTMLCanvasElement | null }).current = el;\n }, [canvasRef]);\n\n const { paint: paintCanvas } = useCanvasRenderer();\n const paint = (): boolean => {\n const el = canvasElRef.current;\n if (!el) return false;\n const content = typeof draw === 'function' ? draw({ width, height }) : draw;\n const commands: DrawCommand[] = [];\n if (background) {\n commands.push({ kind: 'path', path: { kind: 'rect', x: 0, y: 0, width, height }, fill: background });\n }\n commands.push({ kind: 'group', transform: viewToMat3(view), children: [...content] });\n return paintCanvas(el, commands, view, { width, height, dpr });\n };\n const paintRef = useLatest(paint);\n const { requestRedraw } = useFrameLoop(\n useCallback(() => paintRef.current(), [paintRef]),\n { target: canvasElRef },\n );\n\n // Every committed render marks the surface dirty; the first paints in place\n // so the first frame shown is the content rather than a blank canvas.\n const paintedRef = useRef(false);\n useLayoutEffect(() => {\n if (paintedRef.current) { requestRedraw(); return; }\n paintedRef.current = paintRef.current();\n if (!paintedRef.current) requestRedraw();\n });\n\n useLateContentRedraw(requestRedraw);\n useRedrawOn(redrawOn, requestRedraw);\n\n // `useCanvasRenderer` frees the renderer on unmount; a remount (StrictMode)\n // paints its first frame in place again.\n useEffect(() => () => { paintedRef.current = false; }, []);\n\n return <canvas ref={setRefs} width={width} height={height} className={className} />;\n}\n"],"mappings":";;;;;;;;;;AAwDA,IAAMA,IAAsB;CAAE,GAAG;CAAG,GAAG;CAAG,OAAO;EAAE,GAAG;EAAG,GAAG;CAAE;AAAE;AAOhE,SAAgBC,EAAW,GAAwB;CACjD,IAAM,EAAE,UAAO,WAAQ,SAAM,eAAY,aAAU,QAAK,cAAW,iBAAcC,GAC3EC,IAAOD,EAAM,OAAOE,EAAcF,EAAM,IAAI,IAAIF,GAEhDK,IAAcC,EAAiC,IAAI,GACnDC,IAAUC,GAAa,MAAiC;EAE5D,AADA,EAAY,UAAUC,GAClB,OAAOC,KAAc,aAAYA,EAAUD,CAAE,IACxCC,MAAW,EAAqD,UAAUD;CACrF,GAAG,CAACC,CAAS,CAAC,GAER,EAAE,OAAOC,MAAgBC,EAAkB,GAY3CC,IAAWC,QAXY;EAC3B,IAAML,IAAKJ,EAAY;EACvB,IAAI,CAACI,GAAI,OAAO;EAChB,IAAMM,IAAU,OAAOC,KAAS,aAAaA,EAAK;GAAE;GAAO;EAAO,CAAC,IAAIA,GACjEC,IAA0B,CAAC;EAKjC,OAJIC,KACFD,EAAS,KAAK;GAAE,MAAM;GAAQ,MAAM;IAAE,MAAM;IAAQ,GAAG;IAAG,GAAG;IAAG;IAAO;GAAO;GAAG,MAAMC;EAAW,CAAC,GAErGD,EAAS,KAAK;GAAE,MAAM;GAAS,WAAWE,EAAWhB,CAAI;GAAG,UAAU,CAAC,GAAGY,CAAO;EAAE,CAAC,GAC7EJ,EAAYF,GAAIQ,GAAUd,GAAM;GAAE;GAAO;GAAQ;EAAI,CAAC;CAC/D,CACgC,GAC1B,EAAE,qBAAkBiB,EACxBZ,QAAkBK,EAAS,QAAQ,GAAG,CAACA,CAAQ,CAAC,GAChD,EAAE,QAAQR,EAAY,CACxB,GAIMgB,IAAaf,EAAO,EAAK;CAc/B,OAbAgB,QAAsB;EACpB,IAAID,EAAW,SAAS;GAAE,EAAc;GAAG;EAAQ;EAEnD,AADA,EAAW,UAAUR,EAAS,QAAQ,GACjCQ,EAAW,WAASE,EAAc;CACzC,CAAC,GAEDC,EAAqBD,CAAa,GAClCE,EAAYC,GAAUH,CAAa,GAInCI,cAAsB;EAAE,EAAW,UAAU;CAAO,GAAG,CAAC,CAAC,GAElD,kBAAC,UAAD;EAAQ,KAAKpB;EAAgB;EAAe;EAAmB;CAAY,CAAA;AACpF"}
1
+ {"version":3,"file":"DrawCanvas.js","names":["IDENTITY_VIEW","DrawCanvas","props","view","normalizeView","canvasElRef","useRef","setRefs","useCallback","el","assignRef","canvasRef","paintCanvas","useCanvasRenderer","paintRef","useLatest","content","draw","commands","background","viewToMat3","useFrameLoop","paintedRef","useLayoutEffect","requestRedraw","useLateContentRedraw","useRedrawOn","redrawOn","useEffect"],"sources":["../../src/canvas/DrawCanvas.tsx"],"sourcesContent":["/**\n * `<DrawCanvas>` — a sized, DPR-correct WebGL2 surface that paints a list of\n * draw commands, with no scene, tools or dispatcher behind it.\n *\n * It paints through the same per-canvas renderer as `<SceneViewCanvas>`\n * (`useCanvasRenderer`), on a `useFrameLoop` that stops while the canvas is off\n * screen or the tab is hidden. Like `<SceneViewCanvas>` it attaches no event\n * listeners and emits no inline styles: size the element with `className`.\n */\nimport { useCallback, useEffect, useLayoutEffect, useRef } from 'react';\nimport type { Ref } from 'react';\nimport { assignRef, useLatest } from '@weasel-js/react';\nimport type { FillStyle } from '@weasel-js/paint';\nimport { useFrameLoop } from './useFrameLoop';\nimport { useLateContentRedraw } from './useLateContentRedraw';\nimport { useRedrawOn } from './useRedrawOn';\nimport { useCanvasRenderer } from './useCanvasRenderer';\nimport { viewToMat3 } from '../renderer/math/viewToMat3';\nimport type { DrawCommand } from '../renderer/DrawCommand';\nimport type { RedrawSource } from '../core/layers/render';\nimport { normalizeView, type View } from '../core/viewport/view';\n\n/** What a `<DrawCanvas>` paints: a command list, or a function returning one\n * that is called on every paint with the surface's CSS-pixel size. */\nexport type DrawCanvasDraw =\n | readonly DrawCommand[]\n | ((size: { width: number; height: number }) => readonly DrawCommand[]);\n\n/** Props for `<DrawCanvas>`. */\nexport interface DrawCanvasProps {\n /** CSS-pixel width. The drawing buffer is `width × dpr`. */\n width: number;\n /** CSS-pixel height. */\n height: number;\n /** The commands to paint, in the coordinate space `view` maps to the\n * screen — CSS pixels when `view` is omitted. A new array repaints on the\n * next frame. A function is read at paint time, so state it reads outside\n * React repaints when one of `redrawOn`'s sources notifies. */\n draw: DrawCanvasDraw;\n /** Camera over `draw`'s commands. Defaults to the identity view. */\n view?: View;\n /** Fills the whole surface beneath `draw`, in screen space. The canvas is\n * transparent without it. */\n background?: FillStyle;\n /** External state a `draw` function reads. Each source is subscribed while\n * mounted and repaints the canvas when it notifies. */\n redrawOn?: readonly RedrawSource[];\n /** Device-pixel ratio. Defaults to `window.devicePixelRatio`, read per\n * paint. */\n dpr?: number;\n /** CSS class for the `<canvas>` element. */\n className?: string;\n /** Receives the underlying `<canvas>`, for pointer listeners or `toBlob`. */\n canvasRef?: Ref<HTMLCanvasElement>;\n}\n\nconst IDENTITY_VIEW: View = { x: 0, y: 0, scale: { x: 1, y: 1 } };\n\n/**\n * Paint draw commands onto a canvas of its own. For a surface that only draws —\n * a package demo, a swatch, a preview — where a `<SceneCanvas>` would bring a\n * scene, tools and an input dispatcher that nothing uses.\n */\nexport function DrawCanvas(props: DrawCanvasProps) {\n const { width, height, draw, background, redrawOn, dpr, className, canvasRef } = props;\n const view = props.view ? normalizeView(props.view) : IDENTITY_VIEW;\n\n const canvasElRef = useRef<HTMLCanvasElement | null>(null);\n const setRefs = useCallback((el: HTMLCanvasElement | null) => {\n canvasElRef.current = el;\n assignRef(canvasRef, el);\n }, [canvasRef]);\n\n const { paint: paintCanvas } = useCanvasRenderer();\n const paint = (): boolean => {\n const el = canvasElRef.current;\n if (!el) return false;\n const content = typeof draw === 'function' ? draw({ width, height }) : draw;\n const commands: DrawCommand[] = [];\n if (background) {\n commands.push({ kind: 'path', path: { kind: 'rect', x: 0, y: 0, width, height }, fill: background });\n }\n commands.push({ kind: 'group', transform: viewToMat3(view), children: [...content] });\n return paintCanvas(el, commands, view, { width, height, dpr });\n };\n const paintRef = useLatest(paint);\n const { requestRedraw } = useFrameLoop(\n useCallback(() => paintRef.current(), [paintRef]),\n { target: canvasElRef },\n );\n\n // Every committed render marks the surface dirty; the first paints in place\n // so the first frame shown is the content rather than a blank canvas.\n const paintedRef = useRef(false);\n useLayoutEffect(() => {\n if (paintedRef.current) { requestRedraw(); return; }\n paintedRef.current = paintRef.current();\n if (!paintedRef.current) requestRedraw();\n });\n\n useLateContentRedraw(requestRedraw);\n useRedrawOn(redrawOn, requestRedraw);\n\n // `useCanvasRenderer` frees the renderer on unmount; a remount (StrictMode)\n // paints its first frame in place again.\n useEffect(() => () => { paintedRef.current = false; }, []);\n\n return <canvas ref={setRefs} width={width} height={height} className={className} />;\n}\n"],"mappings":";;;;;;;;;;AAwDA,IAAMA,IAAsB;CAAE,GAAG;CAAG,GAAG;CAAG,OAAO;EAAE,GAAG;EAAG,GAAG;CAAE;AAAE;AAOhE,SAAgBC,EAAW,GAAwB;CACjD,IAAM,EAAE,UAAO,WAAQ,SAAM,eAAY,aAAU,QAAK,cAAW,iBAAcC,GAC3EC,IAAOD,EAAM,OAAOE,EAAcF,EAAM,IAAI,IAAIF,GAEhDK,IAAcC,EAAiC,IAAI,GACnDC,IAAUC,GAAa,MAAiC;EAE5D,AADA,EAAY,UAAUC,GACtBC,EAAUC,GAAWF,CAAE;CACzB,GAAG,CAACE,CAAS,CAAC,GAER,EAAE,OAAOC,MAAgBC,EAAkB,GAY3CC,IAAWC,QAXY;EAC3B,IAAMN,IAAKJ,EAAY;EACvB,IAAI,CAACI,GAAI,OAAO;EAChB,IAAMO,IAAU,OAAOC,KAAS,aAAaA,EAAK;GAAE;GAAO;EAAO,CAAC,IAAIA,GACjEC,IAA0B,CAAC;EAKjC,OAJIC,KACFD,EAAS,KAAK;GAAE,MAAM;GAAQ,MAAM;IAAE,MAAM;IAAQ,GAAG;IAAG,GAAG;IAAG;IAAO;GAAO;GAAG,MAAMC;EAAW,CAAC,GAErGD,EAAS,KAAK;GAAE,MAAM;GAAS,WAAWE,EAAWjB,CAAI;GAAG,UAAU,CAAC,GAAGa,CAAO;EAAE,CAAC,GAC7EJ,EAAYH,GAAIS,GAAUf,GAAM;GAAE;GAAO;GAAQ;EAAI,CAAC;CAC/D,CACgC,GAC1B,EAAE,qBAAkBkB,EACxBb,QAAkBM,EAAS,QAAQ,GAAG,CAACA,CAAQ,CAAC,GAChD,EAAE,QAAQT,EAAY,CACxB,GAIMiB,IAAahB,EAAO,EAAK;CAc/B,OAbAiB,QAAsB;EACpB,IAAID,EAAW,SAAS;GAAE,EAAc;GAAG;EAAQ;EAEnD,AADA,EAAW,UAAUR,EAAS,QAAQ,GACjCQ,EAAW,WAASE,EAAc;CACzC,CAAC,GAEDC,EAAqBD,CAAa,GAClCE,EAAYC,GAAUH,CAAa,GAInCI,cAAsB;EAAE,EAAW,UAAU;CAAO,GAAG,CAAC,CAAC,GAElD,kBAAC,UAAD;EAAQ,KAAKrB;EAAgB;EAAe;EAAmB;CAAY,CAAA;AACpF"}
@@ -3,13 +3,13 @@ import { AUTO_POSE_DESCRIPTOR as t } from "../interactions/actions/resize/autoPo
3
3
  import { usePointerContext as n, usePointerPosition as r } from "../features/pointer/PointerContext.js";
4
4
  import { PointerProviderIfRoot as i } from "./SceneCanvas/PointerProviderIfRoot.js";
5
5
  import { accentColorOf as a } from "./accentColor.js";
6
- import { SceneViewCanvas as ee } from "./SceneViewCanvas.js";
7
- import { MINIMAP_CENTER as o, MINIMAP_PAN as s, minimapCenterAction as c, minimapPanAction as l } from "../features/minimap/actions.js";
8
- import { createLinkedCursorLayer as u } from "../features/minimap/layers.js";
9
- import { computeFitView as d, computeIndicatorCommand as f } from "./minimapMath.js";
10
- import { useCallback as p, useEffect as m, useMemo as h, useRef as g, useSyncExternalStore as _ } from "react";
11
- import { useLatest as v } from "@weasel-js/react";
12
- import { ActionsProvider as y, ActiveToolContextProvider as b, DepRegistryProvider as x, useActionsRegistry as S, useDepSource as C, useGestureDispatcher as te } from "@weasel-js/routing/react";
6
+ import { SceneViewCanvas as o } from "./SceneViewCanvas.js";
7
+ import { MINIMAP_CENTER as s, MINIMAP_PAN as c, minimapCenterAction as l, minimapPanAction as u } from "../features/minimap/actions.js";
8
+ import { createLinkedCursorLayer as d } from "../features/minimap/layers.js";
9
+ import { computeFitView as f, computeIndicatorCommand as p } from "./minimapMath.js";
10
+ import { useCallback as m, useEffect as h, useMemo as g, useRef as ee, useSyncExternalStore as te } from "react";
11
+ import { assignRef as _, useLatest as v } from "@weasel-js/react";
12
+ import { ActionsProvider as y, ActiveToolContextProvider as b, DepRegistryProvider as x, useActionsRegistry as S, useDepSource as C, useGestureDispatcher as ne } from "@weasel-js/routing/react";
13
13
  import { jsx as w } from "react/jsx-runtime";
14
14
  //#region src/canvas/MinimapCanvas.tsx
15
15
  var T = {
@@ -17,10 +17,10 @@ var T = {
17
17
  eligibility: { always: !0 },
18
18
  bindings: [{
19
19
  spec: { kind: "pointerDown" },
20
- actionId: o
20
+ actionId: s
21
21
  }, {
22
22
  spec: { kind: "drag" },
23
- actionId: s
23
+ actionId: c
24
24
  }]
25
25
  }, E = /* @__PURE__ */ new Map([[T.id, T]]), D = {
26
26
  wheel: !1,
@@ -29,21 +29,21 @@ var T = {
29
29
  ingest: !1
30
30
  };
31
31
  function O(i) {
32
- let { scene: o, mainView: s, mainViewDims: y, onMainViewChange: b, width: x, height: T, drawOne: O, alphaFor: k, layerVisibility: A, layerOrder: j, animator: ne, fit: M = "scene", poseDescriptor: N, indicatorStyle: P, className: F, canvasRef: I, id: L = "minimap" } = i, R = e(s), z = _(o.subscribe, o.getVersion, o.getVersion), B = N ?? t, V = h(() => d(o, {
32
+ let { scene: s, mainView: c, mainViewDims: y, onMainViewChange: b, width: x, height: T, drawOne: O, alphaFor: k, layerVisibility: A, layerOrder: j, animator: M, fit: N = "scene", poseDescriptor: re, indicatorStyle: P, className: F, canvasRef: I, id: L = "minimap" } = i, R = e(c), z = te(s.subscribe, s.getVersion, s.getVersion), B = re ?? t, V = g(() => f(s, {
33
33
  width: x,
34
34
  height: T
35
- }, M, B), [
36
- o,
35
+ }, N, B), [
36
+ s,
37
37
  z,
38
38
  x,
39
39
  T,
40
- M,
40
+ N,
41
41
  B
42
- ]), H = h(() => f(R, y, P), [
42
+ ]), H = g(() => p(R, y, P), [
43
43
  R,
44
44
  y,
45
45
  P
46
- ]), U = g(null), W = v(R), G = v(y), K = v(b), q = v(V), J = h(() => ({
46
+ ]), U = ee(null), W = v(R), G = v(y), K = v(b), q = v(V), J = g(() => ({
47
47
  get: () => W.current,
48
48
  set: (e) => K.current(e),
49
49
  hostSize: () => G.current
@@ -54,21 +54,21 @@ function O(i) {
54
54
  ]);
55
55
  C("rootView", () => J);
56
56
  let Y = S();
57
- m(() => {
57
+ h(() => {
58
58
  if (!Y) return;
59
- let e = [Y.register(c()), Y.register(l())];
59
+ let e = [Y.register(l()), Y.register(u())];
60
60
  return () => {
61
61
  for (let t of e) t();
62
62
  };
63
63
  }, [Y]);
64
- let X = p((e, t) => {
64
+ let X = m((e, t) => {
65
65
  let n = U.current?.getBoundingClientRect(), r = q.current;
66
66
  return {
67
67
  x: r.x + (e - (n?.left ?? 0)) / r.scale.x,
68
68
  y: r.y + (t - (n?.top ?? 0)) / r.scale.y
69
69
  };
70
70
  }, [q]);
71
- te({
71
+ ne({
72
72
  canvasRef: U,
73
73
  actions: Y,
74
74
  entriesById: E,
@@ -77,7 +77,7 @@ function O(i) {
77
77
  channels: D
78
78
  });
79
79
  let Z = n();
80
- m(() => {
80
+ h(() => {
81
81
  let e = U.current;
82
82
  if (!e || !Z) return;
83
83
  let t = (e) => {
@@ -98,11 +98,11 @@ function O(i) {
98
98
  X,
99
99
  L
100
100
  ]);
101
- let Q = r(), $ = h(() => u({
101
+ let Q = r(), $ = g(() => d({
102
102
  id: `${L}.cursor`,
103
103
  pointer: () => Q,
104
104
  color: () => a(U.current)
105
- }), [L, Q]), re = h(() => [H, ...$.draw({ viewId: L }, V, {
105
+ }), [L, Q]), ie = g(() => [H, ...$.draw({ viewId: L }, V, {
106
106
  width: x,
107
107
  height: T
108
108
  })], [
@@ -112,22 +112,22 @@ function O(i) {
112
112
  V,
113
113
  x,
114
114
  T
115
- ]), ie = p((e) => {
116
- U.current = e, typeof I == "function" ? I(e) : I && typeof I == "object" && (I.current = e);
115
+ ]), ae = m((e) => {
116
+ U.current = e, _(I, e);
117
117
  }, [I]);
118
- return /* @__PURE__ */ w(ee, {
119
- scene: o,
118
+ return /* @__PURE__ */ w(o, {
119
+ scene: s,
120
120
  view: V,
121
121
  width: x,
122
122
  height: T,
123
123
  drawOne: O,
124
- extraCommands: re,
124
+ extraCommands: ie,
125
125
  alphaFor: k,
126
126
  layerVisibility: A,
127
127
  layerOrder: j,
128
- animator: ne,
128
+ animator: M,
129
129
  className: F,
130
- canvasRef: ie
130
+ canvasRef: ae
131
131
  });
132
132
  }
133
133
  function k(e) {