@weasel-js/core 1.0.2 → 1.0.3

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 (50) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/dist/{DrawCommand-DCmiNkBj.d.ts → DrawCommand-BbPrSTra.d.ts} +24 -0
  3. package/dist/{chunk-Z3KNFTTS.js → chunk-3JYFAQIB.js} +3 -3
  4. package/dist/chunk-3JYFAQIB.js.map +1 -0
  5. package/dist/{chunk-UB6A6L77.js → chunk-3KGMJMOS.js} +3 -3
  6. package/dist/{chunk-UB6A6L77.js.map → chunk-3KGMJMOS.js.map} +1 -1
  7. package/dist/{chunk-IO5Z75X4.js → chunk-HGF5X6MZ.js} +2 -2
  8. package/dist/chunk-HGF5X6MZ.js.map +1 -0
  9. package/dist/{chunk-775XXAHR.js → chunk-JOSUVS6C.js} +2 -2
  10. package/dist/{chunk-775XXAHR.js.map → chunk-JOSUVS6C.js.map} +1 -1
  11. package/dist/{chunk-PI56ORHE.js → chunk-SMCZRSYB.js} +88 -59
  12. package/dist/chunk-SMCZRSYB.js.map +1 -0
  13. package/dist/{chunk-W2WB4352.js → chunk-TR3HZN34.js} +2 -2
  14. package/dist/chunk-TR3HZN34.js.map +1 -0
  15. package/dist/{chunk-6SHZHXXR.js → chunk-U45SODC2.js} +2 -2
  16. package/dist/{chunk-6SHZHXXR.js.map → chunk-U45SODC2.js.map} +1 -1
  17. package/dist/clipboard.d.ts +5 -2
  18. package/dist/clipboard.js +1 -1
  19. package/dist/clone.d.ts +2 -2
  20. package/dist/{geometry-CnISZ11w.d.ts → geometry-5X6TrAkX.d.ts} +1 -1
  21. package/dist/{grid-CBiLBnyc.d.ts → grid-sfilv5HP.d.ts} +7 -2
  22. package/dist/index.d.ts +546 -20
  23. package/dist/index.js +7 -7
  24. package/dist/insert.d.ts +6 -3
  25. package/dist/insert.js +1 -1
  26. package/dist/insert.js.map +1 -1
  27. package/dist/move.d.ts +26 -5
  28. package/dist/move.js +2 -2
  29. package/dist/move.js.map +1 -1
  30. package/dist/{options-DoOvx50U.d.ts → options-CbsIgQci.d.ts} +3 -1
  31. package/dist/patterns-builtin.d.ts +8 -0
  32. package/dist/patterns-builtin.js +1 -1
  33. package/dist/{pointSnapToGrid-Bj4r_lEQ.d.ts → pointSnapToGrid-C3wowsQy.d.ts} +9 -3
  34. package/dist/{registry-BQL4oyTg.d.ts → registry-CxnkXbG8.d.ts} +90 -6
  35. package/dist/renderer.d.ts +25 -4
  36. package/dist/renderer.js +7 -7
  37. package/dist/resize.d.ts +12 -5
  38. package/dist/resize.js +2 -2
  39. package/dist/routing.d.ts +7 -7
  40. package/dist/routing.js +1 -1
  41. package/dist/{types-Dtxe7jTS.d.ts → types-BHK2dkMu.d.ts} +23 -0
  42. package/dist/{types-Ct4n0Z38.d.ts → types-ByZxoD6P.d.ts} +1 -1
  43. package/dist/{types-Ch0Goo-j.d.ts → types-DpQ0cWYC.d.ts} +20 -0
  44. package/dist/{types-Kh_osAq9.d.ts → types-vqxpCFVp.d.ts} +31 -0
  45. package/dist/{viewToMat3-BvaWCN99.d.ts → viewToMat3-CQW_YqDw.d.ts} +1 -1
  46. package/package.json +6 -6
  47. package/dist/chunk-IO5Z75X4.js.map +0 -1
  48. package/dist/chunk-PI56ORHE.js.map +0 -1
  49. package/dist/chunk-W2WB4352.js.map +0 -1
  50. package/dist/chunk-Z3KNFTTS.js.map +0 -1
@@ -1,9 +1,9 @@
1
- import { I as ImageMinification, G as GLImageCache } from './viewToMat3-BvaWCN99.js';
2
- export { V as ViewLike, v as viewToMat3 } from './viewToMat3-BvaWCN99.js';
1
+ import { I as ImageMinification, G as GLImageCache } from './viewToMat3-CQW_YqDw.js';
2
+ export { V as ViewLike, v as viewToMat3 } from './viewToMat3-CQW_YqDw.js';
3
3
  import { G as GradStop, S as Stroke } from './paint-types-CnLIzqq1.js';
4
4
  export { a as TextureHandle, r as registerTexture } from './paint-types-CnLIzqq1.js';
5
- import { M as Mat3, S as ShaderProgramHandle, D as DrawCommand } from './DrawCommand-DCmiNkBj.js';
6
- export { G as GroupDrawCommand, I as ImageDrawCommand, P as PathDrawCommand, a as ShaderDrawCommand, b as ShaderUniform, c as SolidPaint, T as TextDrawCommand, m as mat3, r as registerProgram } from './DrawCommand-DCmiNkBj.js';
5
+ import { M as Mat3, S as ShaderProgramHandle, D as DrawCommand } from './DrawCommand-BbPrSTra.js';
6
+ export { G as GroupDrawCommand, I as ImageDrawCommand, P as PathDrawCommand, a as ShaderDrawCommand, b as ShaderUniform, c as SolidPaint, T as TextDrawCommand, m as mat3, r as registerProgram } from './DrawCommand-BbPrSTra.js';
7
7
  import { P as Path } from './path-B6MMiodD.js';
8
8
  export { canQueryLocalFonts, enableLocalFontOutlines, hasFontOutlines, isCanvasFont, listFontOutlines, outlineStatus, registerCanvasFont, registerFont, registerFontOutlines, subscribeGlyphReady, unregisterCanvasFont, unregisterFontOutlines } from '@weasel-js/font';
9
9
 
@@ -16,6 +16,8 @@ export { canQueryLocalFonts, enableLocalFontOutlines, hasFontOutlines, isCanvasF
16
16
  * via the recorder Proxy without further special-casing.
17
17
  */
18
18
  type Stage = 'vertex' | 'fragment' | 'link';
19
+ /** Thrown when a shader fails to compile or link, carrying which stage failed
20
+ * and the driver's log. */
19
21
  declare class ShaderCompileError extends Error {
20
22
  readonly stage: Stage;
21
23
  readonly log: string;
@@ -203,6 +205,8 @@ declare class GLTextureCache {
203
205
  * shader applies premultiplication before writing outColor.
204
206
  */
205
207
 
208
+ /** Bake gradient stops into a 256-entry RGBA lookup strip, which the shader
209
+ * samples instead of evaluating stops per fragment. */
206
210
  declare function buildGradientRamp(stops: GradStop[]): Uint8ClampedArray;
207
211
  declare class GradientRampCache {
208
212
  private readonly gl;
@@ -294,6 +298,9 @@ declare class SolidBatch {
294
298
  private growGpu;
295
299
  }
296
300
 
301
+ /** How to construct a `WeaselRenderer`: the GL context or canvas to draw
302
+ * into, the output size, and the quality knobs that separate screen
303
+ * rendering from print. */
297
304
  interface WeaselRendererOptions {
298
305
  gl?: WebGL2RenderingContext;
299
306
  canvas?: HTMLCanvasElement;
@@ -326,6 +333,14 @@ interface WeaselRendererOptions {
326
333
  * print has no reason to sample a field it could evaluate exactly). */
327
334
  textOutlineMinScreenSize?: number;
328
335
  }
336
+ /**
337
+ * The WebGL2 renderer: takes a list of draw commands and paints them.
338
+ *
339
+ * It knows nothing about the scene — commands are the whole interface, which
340
+ * is what lets layers, HUD widgets and overlays all draw through the same
341
+ * pipeline. GPU resources (meshes, textures, gradient ramps) are cached across
342
+ * frames and keyed by identity, so re-issuing the same command is cheap.
343
+ */
329
344
  declare class WeaselRenderer {
330
345
  private readonly gl;
331
346
  private pathFill;
@@ -433,12 +448,18 @@ declare class WeaselRenderer {
433
448
  /** @internal */ _dpr(): number;
434
449
  }
435
450
 
451
+ /** Options for `tessellate`. A smaller tolerance means more triangles and
452
+ * smoother curves; it is in path-local units, so callers rendering at a
453
+ * larger scale should tighten it. */
436
454
  interface TessellateOptions {
437
455
  /** Flatness tolerance for bezier subdivision in path-local units. */
438
456
  flattenTolerance?: number;
439
457
  }
458
+ /** Turn a path into a triangle mesh the renderer can draw: curves flattened to
459
+ * line segments, then the resulting polygon triangulated. */
440
460
  declare function tessellate(path: Path, opts?: TessellateOptions): Mesh;
441
461
 
462
+ /** Options for stroke tessellation. */
442
463
  interface StrokeOptions {
443
464
  flattenTolerance?: number;
444
465
  }
package/dist/renderer.js CHANGED
@@ -1,12 +1,12 @@
1
- export { IDENTITY_COLOR_MATRIX, OUTLINE_MIN_SCREEN_PX, ShaderCompileError, WeaselRenderer, buildGradientRamp, canQueryLocalFonts, enableLocalFontOutlines, hasFontOutlines, isCanvasFont, listFontOutlines, mat3, outlineStatus, registerCanvasFont, registerFont, registerFontOutlines, registerProgram, subscribeGlyphReady, tessellate, tessellateStroke, unregisterCanvasFont, unregisterFontOutlines, viewToMat3 } from './chunk-PI56ORHE.js';
2
- import './chunk-W2WB4352.js';
3
- import './chunk-UB6A6L77.js';
4
- import './chunk-Z3KNFTTS.js';
5
- import './chunk-6SHZHXXR.js';
6
- import './chunk-775XXAHR.js';
1
+ export { IDENTITY_COLOR_MATRIX, OUTLINE_MIN_SCREEN_PX, ShaderCompileError, WeaselRenderer, buildGradientRamp, canQueryLocalFonts, enableLocalFontOutlines, hasFontOutlines, isCanvasFont, listFontOutlines, mat3, outlineStatus, registerCanvasFont, registerFont, registerFontOutlines, registerProgram, subscribeGlyphReady, tessellate, tessellateStroke, unregisterCanvasFont, unregisterFontOutlines, viewToMat3 } from './chunk-SMCZRSYB.js';
2
+ import './chunk-TR3HZN34.js';
3
+ import './chunk-3KGMJMOS.js';
4
+ import './chunk-3JYFAQIB.js';
5
+ import './chunk-U45SODC2.js';
6
+ import './chunk-JOSUVS6C.js';
7
7
  import './chunk-UGFFCMQP.js';
8
8
  import './chunk-BHVYVFGV.js';
9
9
  import './chunk-GVCNT7UH.js';
10
- export { registerTexture } from './chunk-IO5Z75X4.js';
10
+ export { registerTexture } from './chunk-HGF5X6MZ.js';
11
11
  //# sourceMappingURL=renderer.js.map
12
12
  //# sourceMappingURL=renderer.js.map
package/dist/resize.d.ts CHANGED
@@ -1,12 +1,15 @@
1
- export { C as CORNER_ANCHORS, a as CornerAnchor, b as CornerHandle, U as UseResizeOptions, c as cornerPoint, d as cornerResizeHandles, f as fixedCornerOf, h as hitCornerHandle, p as pointSnapToGrid } from './pointSnapToGrid-Bj4r_lEQ.js';
2
- export { P as PoseProjection, R as RECT_POSE_DESCRIPTOR, a as ROTATED_POSE_DESCRIPTOR } from './geometry-CnISZ11w.js';
3
- import { R as ResizePose, B as BoundsConstraint, M as ModifierState } from './types-Ct4n0Z38.js';
1
+ export { C as CORNER_ANCHORS, a as CornerAnchor, b as CornerHandle, U as UseResizeOptions, c as cornerPoint, d as cornerResizeHandles, f as fixedCornerOf, h as hitCornerHandle, p as pointSnapToGrid } from './pointSnapToGrid-C3wowsQy.js';
2
+ export { P as PoseProjection, R as RECT_POSE_DESCRIPTOR, a as ROTATED_POSE_DESCRIPTOR } from './geometry-5X6TrAkX.js';
3
+ import { R as ResizePose, B as BoundsConstraint, M as ModifierState } from './types-ByZxoD6P.js';
4
4
  import { G as Guide } from './types-D2tTKEU0.js';
5
5
  import { V as View } from './view-DSQgxBJB.js';
6
- import './types-Dtxe7jTS.js';
6
+ import './types-BHK2dkMu.js';
7
7
  import '@weasel-js/history';
8
- import './types-Ch0Goo-j.js';
8
+ import './types-DpQ0cWYC.js';
9
9
 
10
+ /** Resize constraint that stops a node shrinking past a minimum. The anchored
11
+ * edge stays put, so clamping pushes the dragged edge back rather than moving
12
+ * the node. */
10
13
  declare function clampMinSize<TPose extends ResizePose>(args: {
11
14
  minWidth: number;
12
15
  minHeight: number;
@@ -39,6 +42,10 @@ declare function lockAspectWithModifier<TPose extends ResizePose>(opts?: {
39
42
  }): BoundsConstraint<TPose>;
40
43
 
41
44
  type ModKey$1 = keyof ModifierState;
45
+ /** Resize constraint that snaps the moving edges to a grid, leaving the
46
+ * anchored ones where they are. By default a node already smaller than one
47
+ * grid cell on an axis is left unsnapped on that axis, so it stays resizable
48
+ * at sub-grid sizes. */
42
49
  declare function snapToGrid<TPose extends ResizePose>(args: {
43
50
  spacing: number;
44
51
  bypassKey?: ModKey$1;
package/dist/resize.js CHANGED
@@ -1,4 +1,4 @@
1
- export { CORNER_ANCHORS, DEFAULT_RESIZE_BEHAVIORS, clampMinSize, cornerPoint, cornerResizeHandles, fixedCornerOf, hitCornerHandle, lockAspectWithModifier, pointSnapToGrid, snapToGrid, snapToGuides } from './chunk-Z3KNFTTS.js';
2
- export { RECT_POSE_DESCRIPTOR, ROTATED_POSE_DESCRIPTOR } from './chunk-6SHZHXXR.js';
1
+ export { CORNER_ANCHORS, DEFAULT_RESIZE_BEHAVIORS, clampMinSize, cornerPoint, cornerResizeHandles, fixedCornerOf, hitCornerHandle, lockAspectWithModifier, pointSnapToGrid, snapToGrid, snapToGuides } from './chunk-3JYFAQIB.js';
2
+ export { RECT_POSE_DESCRIPTOR, ROTATED_POSE_DESCRIPTOR } from './chunk-U45SODC2.js';
3
3
  //# sourceMappingURL=resize.js.map
4
4
  //# sourceMappingURL=resize.js.map
package/dist/routing.d.ts CHANGED
@@ -1,19 +1,19 @@
1
1
  import { ParsedModifiers, GestureName, GestureSpec } from '@weasel-js/gestures';
2
2
  export { ChannelRef, DescribeRouteOptions, GESTURE_DESCRIPTORS, GestureArgSpec, GestureDescriptor, GestureName, ModRequirement, ModifierKey, ParsedModifiers, ParsedRoute, PhaseAtom, RESERVED_ID_NAMES, RESERVED_ID_PREFIXES, ROUTE_FIELD_DEFINITIONS, ROUTE_TERMS, RouteDescriptionPart, RouteFieldName, RouteTermLabel, canonicalModifiers, collapseShiftPairs, describeRoute, describeRouteParts, formatPhaseAtom, formatRoute, getGestureDescriptor, isKnownGestureName, parseRoute } from '@weasel-js/gestures';
3
- import { T as Tool } from './registry-BQL4oyTg.js';
4
- import './types-Kh_osAq9.js';
3
+ import { T as Tool } from './registry-CxnkXbG8.js';
4
+ import './types-vqxpCFVp.js';
5
5
  import '@weasel-js/history';
6
6
  import './path-B6MMiodD.js';
7
- import './types-Ct4n0Z38.js';
8
- import './types-Ch0Goo-j.js';
7
+ import './types-ByZxoD6P.js';
8
+ import './types-DpQ0cWYC.js';
9
9
  import './view-DSQgxBJB.js';
10
10
  import '@weasel-js/modes';
11
11
  import 'react';
12
- import './geometry-CnISZ11w.js';
12
+ import './geometry-5X6TrAkX.js';
13
13
  import './paint-types-CnLIzqq1.js';
14
- import './DrawCommand-DCmiNkBj.js';
14
+ import './DrawCommand-BbPrSTra.js';
15
15
  import 'react/jsx-runtime';
16
- import './types-Dtxe7jTS.js';
16
+ import './types-BHK2dkMu.js';
17
17
  import '@weasel-js/geom';
18
18
 
19
19
  /**
package/dist/routing.js CHANGED
@@ -1,3 +1,3 @@
1
- export { GESTURE_DESCRIPTORS, RESERVED_ID_NAMES, RESERVED_ID_PREFIXES, ROUTE_FIELD_DEFINITIONS, ROUTE_TERMS, buildRouteRegistry, canonicalModifiers, collapseShiftPairs, describeRoute, describeRouteParts, findConflicts, formatPhaseAtom, formatRoute, getGestureDescriptor, isKnownGestureName, parseRoute } from './chunk-W2WB4352.js';
1
+ export { GESTURE_DESCRIPTORS, RESERVED_ID_NAMES, RESERVED_ID_PREFIXES, ROUTE_FIELD_DEFINITIONS, ROUTE_TERMS, buildRouteRegistry, canonicalModifiers, collapseShiftPairs, describeRoute, describeRouteParts, findConflicts, formatPhaseAtom, formatRoute, getGestureDescriptor, isKnownGestureName, parseRoute } from './chunk-TR3HZN34.js';
2
2
  //# sourceMappingURL=routing.js.map
3
3
  //# sourceMappingURL=routing.js.map
@@ -20,7 +20,10 @@ interface DebugConfig {
20
20
  * {@link DEFAULT_DEBUG_STROKES}. */
21
21
  strokes?: Partial<DebugStrokes>;
22
22
  }
23
+ /** The name of one debug-overlay feature — the keys of {@link DebugConfig}
24
+ * that toggle a visualization. */
23
25
  type DebugFeature = 'hitboxes' | 'handles' | 'bounds' | 'origins' | 'snap' | 'layers' | 'ids' | 'fps';
26
+ /** Colors the debug overlay draws with, one entry per feature. */
24
27
  interface DebugTheme {
25
28
  hitboxFill: string;
26
29
  hitboxStroke: string;
@@ -52,7 +55,10 @@ interface DebugStrokes {
52
55
  /** Rejected snap candidates only; accepted ones paint as a filled dot. */
53
56
  snap: DebugStroke;
54
57
  }
58
+ /** Which kind of handle a recorded handle marker represents. */
55
59
  type HandleKind = 'corner' | 'rotation' | 'anchor';
60
+ /** The geometry a hit region actually tests against, as reported to the debug
61
+ * sink so the overlay can draw the real shape rather than its bounding box. */
56
62
  type HitShape = {
57
63
  kind: 'rect';
58
64
  x: number;
@@ -69,11 +75,13 @@ type HitShape = {
69
75
  kind: 'path';
70
76
  d: Path2D;
71
77
  };
78
+ /** A hit region tested during the current frame. */
72
79
  interface RecordedHitbox {
73
80
  id: string;
74
81
  kind: 'body' | 'handle' | 'rotation' | 'anchor';
75
82
  shape: HitShape;
76
83
  }
84
+ /** A handle drawn during the current frame, and where. */
77
85
  interface RecordedHandle {
78
86
  id: string;
79
87
  position: {
@@ -82,6 +90,7 @@ interface RecordedHandle {
82
90
  };
83
91
  kind: HandleKind;
84
92
  }
93
+ /** A node's bounds as computed during the current frame. */
85
94
  interface RecordedBounds {
86
95
  id: string;
87
96
  bounds: {
@@ -91,6 +100,7 @@ interface RecordedBounds {
91
100
  height: number;
92
101
  };
93
102
  }
103
+ /** A node's transform origin as used during the current frame. */
94
104
  interface RecordedOrigin {
95
105
  id: string;
96
106
  point: {
@@ -98,6 +108,9 @@ interface RecordedOrigin {
98
108
  y: number;
99
109
  };
100
110
  }
111
+ /** A snap candidate considered during the current gesture, and whether it
112
+ * won. Unlike the other records these survive across frames, until the
113
+ * gesture ends. */
101
114
  interface RecordedSnap {
102
115
  point: {
103
116
  x: number;
@@ -105,12 +118,14 @@ interface RecordedSnap {
105
118
  };
106
119
  accepted: boolean;
107
120
  }
121
+ /** A render layer that drew during the current frame. */
108
122
  interface RecordedLayer {
109
123
  id: string;
110
124
  label: string;
111
125
  space: 'world' | 'screen';
112
126
  index: number;
113
127
  }
128
+ /** Everything the sink collected, ready for the overlay to draw. */
114
129
  interface DebugSnapshot {
115
130
  hitboxes: RecordedHitbox[];
116
131
  handles: RecordedHandle[];
@@ -119,6 +134,14 @@ interface DebugSnapshot {
119
134
  snap: RecordedSnap[];
120
135
  layers: RecordedLayer[];
121
136
  }
137
+ /**
138
+ * Where the kit reports what it is doing so the debug overlay can draw it.
139
+ *
140
+ * Recording is push-based and cheap: hit-testers, handle painters and snap
141
+ * strategies call these as they run, whether or not any overlay is watching.
142
+ * Nothing here affects behavior — a sink that discards everything is a valid
143
+ * sink.
144
+ */
122
145
  interface DebugSink {
123
146
  recordHitbox(id: string, kind: 'body' | 'handle' | 'rotation' | 'anchor', shape: HitShape): void;
124
147
  recordHandle(id: string, position: {
@@ -1,5 +1,5 @@
1
1
  import { Op } from '@weasel-js/history';
2
- import { S as SnapTarget, M as MoveAdapter, I as InsertAdapter } from './types-Ch0Goo-j.js';
2
+ import { S as SnapTarget, M as MoveAdapter, I as InsertAdapter } from './types-DpQ0cWYC.js';
3
3
 
4
4
  /**
5
5
  * Snapshot of modifier-key state at gesture dispatch.
@@ -1,15 +1,20 @@
1
1
  import { Op } from '@weasel-js/history';
2
2
 
3
+ /** A layout container's extent in world units. */
3
4
  type ContainerBounds = {
4
5
  x: number;
5
6
  y: number;
6
7
  width: number;
7
8
  height: number;
8
9
  };
10
+ /** A child a layout strategy is arranging. */
9
11
  interface LayoutChild<TPose> {
10
12
  id: string;
11
13
  pose: TPose;
12
14
  }
15
+ /** One place a dragged child could land. A strategy offers these as the drag
16
+ * moves, a `LayoutSnap` picks between them, and the chosen one decides both
17
+ * the preview and the committed poses. */
13
18
  interface DropTarget<TPose> {
14
19
  /** Where the dragged child lands if this target is picked. */
15
20
  pose: TPose;
@@ -33,16 +38,22 @@ interface DropTarget<TPose> {
33
38
  /** Strategy-private metadata (e.g. cell coords for tile-grid). */
34
39
  meta?: unknown;
35
40
  }
41
+ /** Chooses which of a strategy's drop targets the pointer means, or `null`
42
+ * to reject the drop. Separate from the strategy so the same arrangement can
43
+ * be paired with different snapping rules. */
36
44
  interface LayoutSnap<TPose> {
37
45
  pickTarget(targets: DropTarget<TPose>[], pointer: {
38
46
  x: number;
39
47
  y: number;
40
48
  }): DropTarget<TPose> | null;
41
49
  }
50
+ /** The container a layout strategy is arranging children within. */
42
51
  interface LayoutContainer {
43
52
  id: string;
44
53
  bounds: ContainerBounds;
45
54
  }
55
+ /** The child currently being dragged: where it started, where the pointer
56
+ * currently proposes it goes, and which container it came from. */
46
57
  interface LayoutDragged<TPose> {
47
58
  id: string;
48
59
  /** The pose the dragged child currently has (pre-drop). */
@@ -51,6 +62,15 @@ interface LayoutDragged<TPose> {
51
62
  pose: TPose;
52
63
  sourceContainerId: string | null;
53
64
  }
65
+ /**
66
+ * How a container arranges its children, and what happens when one is dragged
67
+ * into or around it.
68
+ *
69
+ * The four required methods cover the whole cycle: `childPoses` is the resting
70
+ * arrangement, `getDropTargets` enumerates where a drag could land,
71
+ * `reflowPoses` is the live preview once a target is picked, and `commitDrop`
72
+ * turns the result into ops so the drop is undoable.
73
+ */
54
74
  interface LayoutStrategy<TPose> {
55
75
  childPoses(container: LayoutContainer, children: ReadonlyArray<LayoutChild<TPose>>): Map<string, TPose>;
56
76
  getDropTargets(container: LayoutContainer, children: ReadonlyArray<LayoutChild<TPose>>, dragged: LayoutDragged<TPose>): DropTarget<TPose>[];
@@ -80,9 +80,13 @@ interface NodeBase<TData, TLayer extends string, TPose> {
80
80
  data: TData;
81
81
  parent: NodeId | null;
82
82
  }
83
+ /** A node with no children — a shape, a label, an image. */
83
84
  interface LeafNode<TData, TLayer extends string, TPose = RectPose> extends NodeBase<TData, TLayer, TPose> {
84
85
  kind: 'leaf';
85
86
  }
87
+ /** A node with an ordered list of children. This is the real group: what
88
+ * Cmd+G creates, what SVG `<g>` round-trips to. A container has its own pose,
89
+ * which its children's poses are relative to, and may optionally clip them. */
86
90
  interface ContainerNode<TData, TLayer extends string, TPose = RectPose> extends NodeBase<TData, TLayer, TPose> {
87
91
  kind: 'container';
88
92
  children: NodeId[];
@@ -93,20 +97,30 @@ interface ContainerNode<TData, TLayer extends string, TPose = RectPose> extends
93
97
  * paints descendants only where it covers. */
94
98
  clipFromPose?: (pose: TPose) => Path | null;
95
99
  }
100
+ /** A node in the scene tree: either a leaf or a container. Re-exported
101
+ * publicly as `SceneNode`, to avoid colliding with the DOM's `Node`. */
96
102
  type Node<TData, TLayer extends string, TPose = RectPose> = LeafNode<TData, TLayer, TPose> | ContainerNode<TData, TLayer, TPose>;
97
103
  interface LayerRecordBase<TLayer extends string> {
98
104
  id: TLayer;
99
105
  visible: boolean;
100
106
  locked: boolean;
101
107
  }
108
+ /** A layer declared when the scene was created. Fixed set, no display name —
109
+ * these are the kit's own render bands, not something a user manages. */
102
110
  interface SystemLayerRecord<TLayer extends string> extends LayerRecordBase<TLayer> {
103
111
  kind: 'system';
104
112
  }
113
+ /** A layer the user created and can rename, reorder or delete. */
105
114
  interface UserLayerRecord<TLayer extends string> extends LayerRecordBase<TLayer> {
106
115
  kind: 'user';
107
116
  name: string;
108
117
  }
118
+ /** Per-layer metadata held by the scene: whether it is visible and locked,
119
+ * and where it sits in the render stack. Distinct from a node's `layer` tag,
120
+ * which merely names one of these. */
109
121
  type LayerRecord<TLayer extends string> = SystemLayerRecord<TLayer> | UserLayerRecord<TLayer>;
122
+ /** What `Scene.add` needs to mint a node. Everything except the id is
123
+ * required; the id is generated unless one is supplied. */
110
124
  interface AddNodeSpec<TData, TLayer extends string, TPose = RectPose> {
111
125
  kind: 'leaf' | 'container';
112
126
  layer: TLayer;
@@ -120,10 +134,13 @@ interface AddNodeSpec<TData, TLayer extends string, TPose = RectPose> {
120
134
  * to the node; ignored for leaves. Mirrors `ContainerNode.clipFromPose`. */
121
135
  clipFromPose?: (pose: TPose) => Path | null;
122
136
  }
137
+ /** A custom scene mutation registered with `Scene.registerOp`: how to apply
138
+ * it and how to undo it. The pair is what makes it participate in history. */
123
139
  interface RegisteredOp<P> {
124
140
  apply: (payload: P) => void;
125
141
  revert: (payload: P) => void;
126
142
  }
143
+ /** One of the layers a scene is created with. */
127
144
  interface SystemLayerSpec<TLayer extends string> {
128
145
  id: TLayer;
129
146
  visible?: boolean;
@@ -171,6 +188,8 @@ interface SceneRegistry<TPose> {
171
188
  /** Maps registry keys to `clipFromPose` factory functions for container nodes. */
172
189
  clipFromPose?: Readonly<Record<string, (pose: TPose) => Path | null>>;
173
190
  }
191
+ /** Options for `useScene` — the layers the scene has, what it starts out
192
+ * holding, and how its history behaves. */
174
193
  interface UseSceneOptions<TData, TLayer extends string, TPose = RectPose> {
175
194
  systemLayers: readonly SystemLayerSpec<TLayer>[];
176
195
  initial?: readonly AddNodeSpec<TData, TLayer, TPose>[];
@@ -204,6 +223,18 @@ interface UseSceneOptions<TData, TLayer extends string, TPose = RectPose> {
204
223
  * wire it after construction via `scene.setActiveJournalAccessor(fn)`. */
205
224
  getActiveJournal?: () => _weasel_js_history.Journal | null;
206
225
  }
226
+ /**
227
+ * The kit-owned scene tree: nodes, layers, and the undo history over both.
228
+ *
229
+ * A scene is logical, not visual — it says what exists and where, and nothing
230
+ * about how it is painted. Every mutating method is undoable, and reads are
231
+ * snapshots rather than live views. Nodes are addressed by `NodeId`; hold ids
232
+ * across mutations, not node objects.
233
+ *
234
+ * Three type parameters keep it domain-agnostic: `TData` is the app's payload,
235
+ * which the kit never inspects; `TPose` is the transform shape, `RectPose` by
236
+ * default; `TLayer` is the union of layer names.
237
+ */
207
238
  interface Scene<TData, TLayer extends string, TPose = RectPose> {
208
239
  readonly nodes: ReadonlyMap<NodeId, Node<TData, TLayer, TPose>>;
209
240
  readonly roots: readonly NodeId[];
@@ -1,4 +1,4 @@
1
- import { M as Mat3 } from './DrawCommand-DCmiNkBj.js';
1
+ import { M as Mat3 } from './DrawCommand-BbPrSTra.js';
2
2
 
3
3
  /**
4
4
  * GL texture upload cache for ImageBitmap objects.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weasel-js/core",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "Domain-agnostic 2D scene graph primitives for React: viewport math, drag/resize/insert/clone interactions, layered canvas rendering.",
5
5
  "license": "MIT",
6
6
  "author": "orochi235",
@@ -74,11 +74,11 @@
74
74
  "react": ">=18"
75
75
  },
76
76
  "dependencies": {
77
- "@weasel-js/font": "1.0.2",
78
- "@weasel-js/geom": "1.0.2",
79
- "@weasel-js/gestures": "1.0.2",
80
- "@weasel-js/history": "1.0.2",
81
- "@weasel-js/modes": "1.0.2",
77
+ "@weasel-js/font": "1.0.3",
78
+ "@weasel-js/geom": "1.0.3",
79
+ "@weasel-js/gestures": "1.0.3",
80
+ "@weasel-js/history": "1.0.3",
81
+ "@weasel-js/modes": "1.0.3",
82
82
  "earcut": "2.2.4",
83
83
  "polygon-clipping": "^0.15.7"
84
84
  },
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/renderer/textures/registerTexture.ts","../src/features/patterns/tileGeometry.ts","../src/features/patterns/index.ts","../src/features/patterns/patterns-builtin.ts"],"names":[],"mappings":"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","file":"chunk-IO5Z75X4.js","sourcesContent":["/**\n * registerTexture — accepts an image source, assigns an opaque id, stores in\n * a module-level registry. Actual GL upload happens lazily at draw time in\n * drawShader() via GLTextureCache.upload (which is idempotent).\n *\n * Lifecycle: textures live for the renderer's lifetime. No unregister in v1.\n *\n * Convention §9: this registry stores image data only — no per-renderer state.\n * Each WeaselRenderer's GLTextureCache does its own dedup via has(id).\n */\n\nexport interface TextureHandle {\n readonly id: string;\n}\n\nexport interface TextureEntry {\n source: HTMLImageElement | ImageBitmap;\n}\n\nlet counter = 0;\nlet registry = new Map<string, TextureEntry>();\n\n/** @internal Test helper — do not call from product code. */\nexport function _resetTextureRegistryForTests(): void {\n registry = new Map();\n counter = 0;\n}\n\nexport function getTexture(id: string): TextureEntry | null {\n return registry.get(id) ?? null;\n}\n\n/**\n * Register an image for use as a shader texture uniform.\n *\n * Returns a TextureHandle whose `id` can be passed as a ShaderUniform value.\n * The handle is valid for the lifetime of the renderer it will be used with.\n *\n * @param image The image to register. HTMLImageElement must be fully loaded.\n * @returns Opaque TextureHandle.\n */\nexport function registerTexture(\n image: HTMLImageElement | ImageBitmap,\n): TextureHandle {\n const id = `tex_${++counter}`;\n registry.set(id, { source: image });\n return { id };\n}\n","/**\n * The shapes a built-in tile is made of, as data.\n *\n * One description, two renderers: `patterns-builtin` strokes/fills these to\n * an `OffscreenCanvas` to build the GL texture, and `@weasel-js/svg` maps\n * them to `<pattern>` children. Duplicating the tile drawing in the\n * serializer would let the raster and vector forms drift apart silently.\n */\n\nimport type { TilePatternSpec } from '../../core/paint-types';\n\nexport type TileShape =\n | { kind: 'line'; x1: number; y1: number; x2: number; y2: number; width: number; color: string }\n | { kind: 'circle'; cx: number; cy: number; r: number; color: string }\n | { kind: 'ellipse'; cx: number; cy: number; rx: number; ry: number; rotation: number; color: string }\n | { kind: 'rect'; x: number; y: number; width: number; height: number; color: string };\n\n/** A built-in tile's shapes plus the edge length they're drawn within. */\nexport interface TileGeometry {\n size: number;\n shapes: TileShape[];\n}\n\n/** Defaults per tile kind, matching each factory's signature. */\nexport function tileSize(spec: TilePatternSpec): number {\n if (spec.size !== undefined) return spec.size;\n switch (spec.tile) {\n case 'hatch': return 5;\n case 'crosshatch': return 6;\n case 'dots': return 6;\n case 'chunks': return 48;\n default: return 8;\n }\n}\n\n/** Seeded PRNG (mulberry32) — `chunks` places its scatter with this, so the\n * same `seed` yields the same tile in both the raster and vector paths. */\nfunction mulberry32(seed: number): () => number {\n let s = seed | 0;\n return () => {\n s = (s + 0x6D2B79F5) | 0;\n let t = Math.imul(s ^ (s >>> 15), 1 | s);\n t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n}\n\nexport function tileGeometry(spec: TilePatternSpec): TileGeometry {\n const s = tileSize(spec);\n const { color } = spec;\n\n switch (spec.tile) {\n case 'hatch': {\n const width = spec.lineWidth ?? 1;\n return { size: s, shapes: forwardDiagonals(s, width, color) };\n }\n case 'crosshatch': {\n const width = spec.lineWidth ?? 0.8;\n return {\n size: s,\n shapes: [...forwardDiagonals(s, width, color), ...backwardDiagonals(s, width, color)],\n };\n }\n case 'dots': {\n const r = spec.radius ?? 1;\n return { size: s, shapes: [{ kind: 'circle', cx: s / 2, cy: s / 2, r, color }] };\n }\n case 'chunks': {\n const density = spec.density ?? 0.35;\n const chunkSize = spec.chunkSize ?? 3;\n const rand = mulberry32(spec.seed ?? 42);\n const count = Math.round((s * s * density) / (chunkSize * chunkSize));\n const shapes: TileShape[] = [];\n if (spec.bg !== undefined) {\n shapes.push({ kind: 'rect', x: 0, y: 0, width: s, height: s, color: spec.bg });\n }\n for (let i = 0; i < count; i++) {\n const cx = rand() * s;\n const cy = rand() * s;\n const w = chunkSize * (0.5 + rand());\n const h = chunkSize * (0.5 + rand());\n const rotation = rand() * Math.PI;\n shapes.push({ kind: 'ellipse', cx, cy, rx: w / 2, ry: h / 2, rotation, color });\n }\n return { size: s, shapes };\n }\n default:\n return { size: s, shapes: [] };\n }\n}\n\n/** The forward (`/`) diagonal, plus the two corner stubs that make it tile\n * seamlessly across the edges. */\nfunction forwardDiagonals(s: number, width: number, color: string): TileShape[] {\n return [\n { kind: 'line', x1: -1, y1: s + 1, x2: s + 1, y2: -1, width, color },\n { kind: 'line', x1: -1, y1: 1, x2: 1, y2: -1, width, color },\n { kind: 'line', x1: s - 1, y1: s + 1, x2: s + 1, y2: s - 1, width, color },\n ];\n}\n\n/** The backward (`\\`) diagonal and its two stubs. */\nfunction backwardDiagonals(s: number, width: number, color: string): TileShape[] {\n return [\n { kind: 'line', x1: -1, y1: -1, x2: s + 1, y2: s + 1, width, color },\n { kind: 'line', x1: s - 1, y1: -1, x2: s + 1, y2: 1, width, color },\n { kind: 'line', x1: -1, y1: s - 1, x2: 1, y2: s + 1, width, color },\n ];\n}\n\n/** Draw a tile's shapes to a 2D context — the raster half of the pair. */\nexport function drawTileShapes(oc: CanvasRenderingContext2D, geometry: TileGeometry): void {\n for (const shape of geometry.shapes) {\n if (shape.kind === 'line') {\n oc.strokeStyle = shape.color;\n oc.lineWidth = shape.width;\n oc.beginPath();\n oc.moveTo(shape.x1, shape.y1);\n oc.lineTo(shape.x2, shape.y2);\n oc.stroke();\n continue;\n }\n oc.fillStyle = shape.color;\n if (shape.kind === 'rect') {\n oc.fillRect(shape.x, shape.y, shape.width, shape.height);\n continue;\n }\n oc.beginPath();\n if (shape.kind === 'circle') {\n oc.arc(shape.cx, shape.cy, shape.r, 0, Math.PI * 2);\n } else {\n oc.ellipse(shape.cx, shape.cy, shape.rx, shape.ry, shape.rotation, 0, Math.PI * 2);\n }\n oc.fill();\n }\n}\n","/**\n * Tile-pattern primitive — a small helper for building repeating texture\n * tiles from a draw callback. The result plugs into a `FillStyle` of\n * `{ fill: 'pattern', pattern }` (see `../../core/paint-types.ts`).\n *\n * GL-backed: the tile is rendered to an `OffscreenCanvas` (with a 2D fallback\n * to a regular `<canvas>` for environments without `OffscreenCanvas`),\n * converted to an `ImageBitmap`, and registered with `registerTexture()`.\n * The returned `TextureHandle` is what `FillStyle.pattern` accepts.\n *\n * For ready-to-use named patterns (hatch, crosshatch, dots, chunks),\n * see the `@weasel-js/core/patterns-builtin` subpath.\n */\n\nimport { registerTexture } from '../../renderer/textures/registerTexture';\nimport type { TextureHandle } from '../../renderer/textures/registerTexture';\n\n/** Options for `createTilePattern`. */\nexport interface TilePatternOpts {\n /** Edge length (in pixels) of the square offscreen tile. */\n size: number;\n /** FillStyle one tile at integer-pixel coordinates `(0, 0) .. (size, size)`. */\n draw: (oc: CanvasRenderingContext2D, size: number) => void;\n}\n\n/**\n * Render a single tile to an offscreen canvas and register it as a GL\n * texture, returning a `TextureHandle` you can wrap in a `FillStyle` of\n * `{ fill: 'pattern', pattern }`. Returns `null` only when an\n * `OffscreenCanvas`/2D context can't be acquired (very rare; same fallback\n * semantics as the prior `CanvasPattern`-based implementation).\n */\nexport function createTilePattern(opts: TilePatternOpts): TextureHandle | null {\n // Prefer OffscreenCanvas; fall back to <canvas> for environments without it\n // (tests in jsdom may need that fallback — try OffscreenCanvas first).\n let off: OffscreenCanvas | HTMLCanvasElement;\n try {\n off = new OffscreenCanvas(opts.size, opts.size);\n } catch {\n if (typeof document === 'undefined') return null;\n const c = document.createElement('canvas');\n c.width = opts.size;\n c.height = opts.size;\n off = c;\n }\n const oc = off.getContext('2d') as CanvasRenderingContext2D | null;\n if (!oc) return null;\n opts.draw(oc, opts.size);\n\n // Convert to ImageBitmap so registerTexture can store it.\n // OffscreenCanvas.transferToImageBitmap() is the fast path; the\n // synchronous path requires OffscreenCanvas. If we got here without it,\n // the env doesn't support OffscreenCanvas — fail gracefully.\n let bitmap: ImageBitmap;\n if (typeof (off as OffscreenCanvas).transferToImageBitmap === 'function') {\n bitmap = (off as OffscreenCanvas).transferToImageBitmap();\n } else {\n return null;\n }\n return registerTexture(bitmap);\n}\n","/**\n * Built-in pattern factories — generic line/dot/scatter textures. Each\n * returns a `TextureHandle` you can wrap in a `FillStyle` of\n * `{ fill: 'pattern', pattern }` and pass to anything that accepts a `FillStyle`\n * (`FillStyle`-aware layers, path fills, etc.).\n * Imported from a subpath so consumers that ship their own catalog can\n * avoid the bundle cost.\n *\n * All factories require an explicit `color` (and `bg` for `chunks`). No\n * defaults are baked in — the caller's design system is the source of truth\n * for palette decisions.\n *\n * Each is a thin wrapper over `tileGeometry`, which describes the tile as\n * shapes so `@weasel-js/svg` can emit the same tile as a `<pattern>`.\n * Prefer naming a tile in a `TilePatternSpec` over calling these directly —\n * a spec serializes, a `TextureHandle` does not.\n */\n\nexport { tileGeometry, tileSize, drawTileShapes } from './tileGeometry';\nexport type { TileShape, TileGeometry } from './tileGeometry';\n\nimport { createTilePattern } from '.';\nimport { tileGeometry, drawTileShapes, tileSize } from './tileGeometry';\nimport type { TilePatternSpec } from '../../core/paint-types';\nimport type { TextureHandle } from '../../renderer/textures/registerTexture';\n\nfunction build(spec: TilePatternSpec): TextureHandle | null {\n const geometry = tileGeometry(spec);\n return createTilePattern({\n size: tileSize(spec),\n draw: (oc) => drawTileShapes(oc, geometry),\n });\n}\n\n/** Diagonal hatch (forward slash). */\nexport interface HatchParams {\n color: string;\n size?: number;\n lineWidth?: number;\n}\n\nexport function hatch(params: HatchParams): TextureHandle | null {\n return build({ tile: 'hatch', ...params });\n}\n\n/** Diagonal hatch in both directions. */\nexport interface CrosshatchParams {\n color: string;\n size?: number;\n lineWidth?: number;\n}\n\nexport function crosshatch(params: CrosshatchParams): TextureHandle | null {\n return build({ tile: 'crosshatch', ...params });\n}\n\n/** Regular grid of filled dots. */\nexport interface DotsParams {\n color: string;\n size?: number;\n radius?: number;\n}\n\nexport function dots(params: DotsParams): TextureHandle | null {\n return build({ tile: 'dots', ...params });\n}\n\n/**\n * Random scatter of small ellipses on top of a solid background. Driven by a\n * seeded PRNG so the same `seed` produces a deterministic tile.\n *\n * Note: the `bg` field is optional — omit it for a transparent background\n * (the scatter shapes draw directly onto whatever sits beneath the overlay).\n */\nexport interface ChunksParams {\n color: string;\n bg?: string;\n size?: number;\n /** Coverage: `chunks * chunkSize²` per `size²` tile. Range ~[0, 1]. */\n density?: number;\n chunkSize?: number;\n seed?: number;\n}\n\nexport function chunks(params: ChunksParams): TextureHandle | null {\n return build({ tile: 'chunks', ...params });\n}\n"]}