@weasel-js/core 1.0.0 → 1.0.2

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 (38) hide show
  1. package/CHANGELOG.md +604 -0
  2. package/dist/{chunk-D2C6K6FO.js → chunk-PI56ORHE.js} +2162 -1491
  3. package/dist/chunk-PI56ORHE.js.map +1 -0
  4. package/dist/{chunk-DCIU3SRK.js → chunk-W2WB4352.js} +79 -151
  5. package/dist/chunk-W2WB4352.js.map +1 -0
  6. package/dist/clipboard.d.ts +2 -2
  7. package/dist/clipboard.js +0 -1
  8. package/dist/clone.d.ts +2 -2
  9. package/dist/clone.js +0 -1
  10. package/dist/{geometry-DVeZ2i-c.d.ts → geometry-CnISZ11w.d.ts} +1 -1
  11. package/dist/{grid-XcFQVS0f.d.ts → grid-CBiLBnyc.d.ts} +2 -2
  12. package/dist/index.d.ts +202 -54
  13. package/dist/index.js +2 -3
  14. package/dist/insert.d.ts +3 -3
  15. package/dist/insert.js +0 -1
  16. package/dist/insert.js.map +1 -1
  17. package/dist/move.d.ts +5 -5
  18. package/dist/move.js +0 -1
  19. package/dist/move.js.map +1 -1
  20. package/dist/{options-wAvCnOZd.d.ts → options-DoOvx50U.d.ts} +1 -1
  21. package/dist/patterns-builtin.js +0 -1
  22. package/dist/{pointSnapToGrid-BuDZWBrL.d.ts → pointSnapToGrid-Bj4r_lEQ.d.ts} +3 -3
  23. package/dist/{index-ChM3ZJ2v.d.ts → registry-BQL4oyTg.d.ts} +140 -349
  24. package/dist/renderer.d.ts +63 -8
  25. package/dist/renderer.js +2 -3
  26. package/dist/resize.d.ts +5 -5
  27. package/dist/resize.js +0 -1
  28. package/dist/routing.d.ts +105 -6
  29. package/dist/routing.js +1 -2
  30. package/dist/{types-B_-khFM0.d.ts → types-Ch0Goo-j.d.ts} +12 -0
  31. package/dist/{types-BhJJ2OCM.d.ts → types-Ct4n0Z38.d.ts} +1 -1
  32. package/dist/{types-BJ8_cyT7.d.ts → types-Dtxe7jTS.d.ts} +20 -1
  33. package/dist/{types-DUpI7Apc.d.ts → types-Kh_osAq9.d.ts} +8 -1
  34. package/package.json +6 -6
  35. package/dist/chunk-D2C6K6FO.js.map +0 -1
  36. package/dist/chunk-DCIU3SRK.js.map +0 -1
  37. package/dist/chunk-PZ5AY32C.js +0 -9
  38. package/dist/chunk-PZ5AY32C.js.map +0 -1
@@ -109,6 +109,8 @@ declare class GLMeshCache {
109
109
  private readonly gl;
110
110
  private readonly aPositionLoc;
111
111
  private readonly map;
112
+ /** Meshes this context has drawn at least once; see `uploadRecurring`. */
113
+ private readonly seen;
112
114
  private readonly finalizer;
113
115
  private readonly pendingDeletes;
114
116
  /** Transient resources allocated this frame; freed at end of render(). */
@@ -123,6 +125,15 @@ declare class GLMeshCache {
123
125
  * and the FinalizationRegistry, so transient meshes never wait on GC.
124
126
  */
125
127
  uploadTransient(mesh: Mesh): GLMeshHandle;
128
+ /**
129
+ * Upload a Mesh that may or may not recur across frames. Its first sight in
130
+ * *this* context takes `uploadTransient`; every later one takes the
131
+ * persistent handle. One transient upload to find out is cheaper than
132
+ * stranding a persistent VAO on a mesh that never returns, whose release
133
+ * would wait on GC. The transient upload does not populate the persistent
134
+ * map, so steady-state reuse begins on the third frame.
135
+ */
136
+ uploadRecurring(mesh: Mesh): GLMeshHandle;
126
137
  /**
127
138
  * Free all transient resources allocated since the last call. Called by
128
139
  * the renderer at the end of each `render()`. Safe under context loss.
@@ -233,6 +244,56 @@ declare class GroupState {
233
244
  pop(): void;
234
245
  }
235
246
 
247
+ /**
248
+ * Growable vertex staging for consecutive solid-fill geometry.
249
+ *
250
+ * Geometry only: `draw.ts` owns when a run starts, what breaks it, and the
251
+ * uniforms the flush draws under. Colors ride the vertices (the batch program
252
+ * is `pathFillVColor` with `u_color` left at white) because shapes in a run
253
+ * differ in color and a merged draw has one set of uniforms — and so does the
254
+ * model transform, applied here rather than uploaded, so that shapes under
255
+ * different transforms still share a draw.
256
+ */
257
+
258
+ declare class SolidBatch {
259
+ private readonly gl;
260
+ private readonly vao;
261
+ private readonly vbo;
262
+ private readonly ibo;
263
+ private verts;
264
+ private idx;
265
+ private nVerts;
266
+ private nIdx;
267
+ /** What the GPU buffers are currently sized for. */
268
+ private vboVerts;
269
+ private iboIdx;
270
+ constructor(gl: WebGL2RenderingContext, prog: ShaderProgram);
271
+ get length(): number;
272
+ /** Whether staging `vertices` more would put the run past the per-flush cap. */
273
+ wouldOverflow(vertices: number): boolean;
274
+ /**
275
+ * Append one rect's four corners through `m`, all carrying `rgba` (straight
276
+ * alpha). An affine maps a rect to a parallelogram, so two triangles still
277
+ * cover it and the batch draws at `u_model` identity.
278
+ */
279
+ pushRect(x: number, y: number, w: number, h: number, m: Mat3, r: number, g: number, b: number, a: number): void;
280
+ /**
281
+ * Append a tessellated mesh through `m`, all vertices carrying `rgba`. The
282
+ * mesh's own indices are rebased onto the staged vertices, which is why the
283
+ * index buffer is uploaded per flush rather than written once.
284
+ */
285
+ pushMesh(mesh: Mesh, m: Mat3, r: number, g: number, b: number, a: number): void;
286
+ /** Upload the staged geometry and bind the batch VAO. Returns the index
287
+ * count for the caller's `drawElements`. */
288
+ uploadAndBind(): number;
289
+ reset(): void;
290
+ dispose(): void;
291
+ /** Grow the CPU arrays so `vertices` / `indices` more fit. */
292
+ private reserve;
293
+ /** Size both GPU buffers for at least what is staged, doubling to amortize. */
294
+ private growGpu;
295
+ }
296
+
236
297
  interface WeaselRendererOptions {
237
298
  gl?: WebGL2RenderingContext;
238
299
  canvas?: HTMLCanvasElement;
@@ -281,10 +342,7 @@ declare class WeaselRenderer {
281
342
  private programRegistry;
282
343
  private quadVbo;
283
344
  private quadIbo;
284
- /** Shared rect-fill geometry: 4 verts × 2 floats (dynamic), 6-index static IBO. */
285
- private rectVao;
286
- private rectVbo;
287
- private rectIbo;
345
+ private solidBatch;
288
346
  private readonly groupState;
289
347
  private widthCss;
290
348
  private heightCss;
@@ -301,10 +359,6 @@ declare class WeaselRenderer {
301
359
  * calls and `registerProgram()` calls. */
302
360
  private disposed;
303
361
  constructor(opts: WeaselRendererOptions);
304
- /** Allocate the shared rect VAO + dynamic VBO (4 verts × 2 floats) + static
305
- * IBO (6 indices). drawRectFast bufferSubData's the 4 corner coords each
306
- * draw, avoiding per-rect buffer allocation in animated demos. */
307
- private uploadRectGeometry;
308
362
  private uploadQuadGeometry;
309
363
  /**
310
364
  * Compile a consumer-registered shader program against this renderer's GL context.
@@ -363,6 +417,7 @@ declare class WeaselRenderer {
363
417
  /** @internal */ _gl(): WebGL2RenderingContext;
364
418
  /** @internal */ _pathFill(): ShaderProgram;
365
419
  /** @internal */ _pathFillVColor(): ShaderProgram;
420
+ /** @internal */ _solidBatch(): SolidBatch;
366
421
  /** @internal */ _textSdf(): ShaderProgram;
367
422
  /** @internal */ _textSdfR8(): ShaderProgram;
368
423
  /** @internal */ _imageFill(): ShaderProgram;
package/dist/renderer.js CHANGED
@@ -1,5 +1,5 @@
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-D2C6K6FO.js';
2
- import './chunk-DCIU3SRK.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-PI56ORHE.js';
2
+ import './chunk-W2WB4352.js';
3
3
  import './chunk-UB6A6L77.js';
4
4
  import './chunk-Z3KNFTTS.js';
5
5
  import './chunk-6SHZHXXR.js';
@@ -8,6 +8,5 @@ import './chunk-UGFFCMQP.js';
8
8
  import './chunk-BHVYVFGV.js';
9
9
  import './chunk-GVCNT7UH.js';
10
10
  export { registerTexture } from './chunk-IO5Z75X4.js';
11
- import './chunk-PZ5AY32C.js';
12
11
  //# sourceMappingURL=renderer.js.map
13
12
  //# sourceMappingURL=renderer.js.map
package/dist/resize.d.ts CHANGED
@@ -1,11 +1,11 @@
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-BuDZWBrL.js';
2
- export { P as PoseProjection, R as RECT_POSE_DESCRIPTOR, a as ROTATED_POSE_DESCRIPTOR } from './geometry-DVeZ2i-c.js';
3
- import { R as ResizePose, B as BoundsConstraint, M as ModifierState } from './types-BhJJ2OCM.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-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';
4
4
  import { G as Guide } from './types-D2tTKEU0.js';
5
5
  import { V as View } from './view-DSQgxBJB.js';
6
- import './types-BJ8_cyT7.js';
6
+ import './types-Dtxe7jTS.js';
7
7
  import '@weasel-js/history';
8
- import './types-B_-khFM0.js';
8
+ import './types-Ch0Goo-j.js';
9
9
 
10
10
  declare function clampMinSize<TPose extends ResizePose>(args: {
11
11
  minWidth: number;
package/dist/resize.js CHANGED
@@ -1,5 +1,4 @@
1
1
  export { CORNER_ANCHORS, DEFAULT_RESIZE_BEHAVIORS, clampMinSize, cornerPoint, cornerResizeHandles, fixedCornerOf, hitCornerHandle, lockAspectWithModifier, pointSnapToGrid, snapToGrid, snapToGuides } from './chunk-Z3KNFTTS.js';
2
2
  export { RECT_POSE_DESCRIPTOR, ROTATED_POSE_DESCRIPTOR } from './chunk-6SHZHXXR.js';
3
- import './chunk-PZ5AY32C.js';
4
3
  //# sourceMappingURL=resize.js.map
5
4
  //# sourceMappingURL=resize.js.map
package/dist/routing.d.ts CHANGED
@@ -1,16 +1,115 @@
1
- export { bd as Conflict, be as PREDICATE_TARGET, bf as RegistryEntry, bg as ToolDef, m as ToolKeybinding, bh as ToolScopes, bi as ViewportToolDef, bj as buildRouteRegistry, bk as defineTool, bl as defineViewportTool, bm as findConflicts, bn as findScopedConflicts, bo as formatConflict, bp as reportRouteConflicts } from './index-ChM3ZJ2v.js';
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 './types-DUpI7Apc.js';
3
+ import { T as Tool } from './registry-BQL4oyTg.js';
4
+ import './types-Kh_osAq9.js';
4
5
  import '@weasel-js/history';
5
6
  import './path-B6MMiodD.js';
6
- import './types-BhJJ2OCM.js';
7
- import './types-B_-khFM0.js';
7
+ import './types-Ct4n0Z38.js';
8
+ import './types-Ch0Goo-j.js';
8
9
  import './view-DSQgxBJB.js';
9
10
  import '@weasel-js/modes';
10
11
  import 'react';
11
- import './geometry-DVeZ2i-c.js';
12
+ import './geometry-CnISZ11w.js';
12
13
  import './paint-types-CnLIzqq1.js';
13
14
  import './DrawCommand-DCmiNkBj.js';
14
15
  import 'react/jsx-runtime';
16
+ import './types-Dtxe7jTS.js';
15
17
  import '@weasel-js/geom';
16
- import './types-BJ8_cyT7.js';
18
+
19
+ /**
20
+ * One row in the route registry — a single `GestureBinding` on one tool,
21
+ * flattened into the route grammar's vocabulary so the inspector, the
22
+ * conflict checker, and `describeRoute` can all read the same shape.
23
+ *
24
+ * Multiple rows can share (gesture, arg, target, modifiers) if different tools
25
+ * declare them; consumers walk the list and group client-side.
26
+ */
27
+ interface RegistryEntry {
28
+ toolId: string;
29
+ /** Which phase the binding's `phase` spec restricts it to. `'any'` when it
30
+ * declares none, declares `'*'`, or declares an atom list whose atoms
31
+ * don't agree on one phase — such a binding fires in either phase, and
32
+ * reporting it as `'initial'` made it collide with genuinely-initial
33
+ * bindings in `findConflicts`' bucket key. */
34
+ phase: 'initial' | 'engaged' | 'any';
35
+ /** Structured v3 modifier requirements. Empty object = "no modifiers
36
+ * held" (the strict default). */
37
+ modifiers: ParsedModifiers;
38
+ gesture: GestureName;
39
+ /** Resolved arg value for arg-bearing gestures (wheel direction,
40
+ * key name, multiTouchTap fingers). Undefined for gestures whose
41
+ * descriptor has no `arg`. */
42
+ arg: string | undefined;
43
+ /** Target class for hit-testing gestures. `undefined` when the descriptor
44
+ * has `hasTarget: false` or the spec declares no target;
45
+ * {@link PREDICATE_TARGET} when the spec uses a `kindOf` predicate, which
46
+ * the route grammar has no notation for. */
47
+ target: string | undefined;
48
+ /** The action this binding fires. */
49
+ actionId: string;
50
+ /** The `GestureSpec` this row was flattened from, by reference. Reflection
51
+ * consumers that need something the grammar doesn't capture — the
52
+ * specificity tuple, a `kindOf` predicate identity — read it here rather
53
+ * than re-walking `Tool.bindings`. */
54
+ spec: GestureSpec;
55
+ }
56
+ /**
57
+ * Flatten every tool's `bindings` into route-registry rows.
58
+ *
59
+ * This was `buildActionRegistry`, and it walked `ToolDef.initial` /
60
+ * `.engaged` phase tables — the grammar that no longer exists. It was also
61
+ * blind to `Tool.bindings` the entire time the two lived side by side, which
62
+ * is why the inspector under-reported select by more than half. The rename
63
+ * fixes a second thing: it never had anything to do with the Actions
64
+ * Registry, and reading `buildActionRegistry` next to `ActionsRegistry`
65
+ * suggested otherwise.
66
+ */
67
+ declare function buildRouteRegistry(tools: readonly Tool<unknown>[]): RegistryEntry[];
68
+
69
+ /** Two or more tools declare the same exact (phase, gesture, arg, target,
70
+ * modifiers) tuple — the dispatcher's slot precedence picks one
71
+ * arbitrarily (well, deterministically by slot order, but the author
72
+ * probably didn't intend the duplication). */
73
+ interface Conflict {
74
+ phase: 'initial' | 'engaged' | 'any';
75
+ gesture: GestureName;
76
+ arg: string | undefined;
77
+ target: string | undefined;
78
+ modifiers: ParsedModifiers;
79
+ /** All tool ids that registered the same tuple. At least 2 by
80
+ * construction. Order matches the input tools[] order. */
81
+ toolIds: string[];
82
+ }
83
+ /** Detect exact-tuple overlaps across a tool registration set.
84
+ *
85
+ * Intentionally NOT flagged:
86
+ * - Broad vs. narrow targets (e.g. an untargeted `click` alongside
87
+ * `click` on `empty`) — the dispatcher's specificity ordering resolves
88
+ * those cleanly, and the broad one is usually the intended fallback.
89
+ * - Different modifier requirements on the same target — they fire on
90
+ * different inputs.
91
+ * - A binding whose action declines via `enabled()` so a lower-priority
92
+ * one can take the gesture. Detecting that intent would mean evaluating
93
+ * the action; consumers can suppress known-intentional compositions in
94
+ * their UI layer.
95
+ *
96
+ * - Two `{ kindOf }` predicate targets. The route grammar renders every
97
+ * predicate as the single token {@link PREDICATE_TARGET}, so bucketing on
98
+ * the rendered target alone reported select's `resize` / `rotate` / `move`
99
+ * drags — three genuinely different predicates — as a three-way conflict.
100
+ * Predicate entries bucket by function identity instead, which means two
101
+ * *separately written but equivalent* predicates go unflagged. That's the
102
+ * right way to be wrong here: this check has to be silent when nothing is
103
+ * wrong or nobody will keep it on.
104
+ *
105
+ * Note that two bindings sharing a tuple on the SAME tool are now possible
106
+ * (bindings are an array, where phase tables were objects with unique
107
+ * keys) — so a conflict may name one tool twice.
108
+ *
109
+ * This is the raw same-tuple detector. Feeding it a whole tool *registry*
110
+ * over-reports, because registry tools take turns in the active slot and
111
+ * can't collide with each other — see {@link findScopedConflicts}.
112
+ */
113
+ declare function findConflicts(tools: readonly Tool<unknown>[]): Conflict[];
114
+
115
+ export { type Conflict, type RegistryEntry, buildRouteRegistry, findConflicts };
package/dist/routing.js CHANGED
@@ -1,4 +1,3 @@
1
- export { GESTURE_DESCRIPTORS, PREDICATE_TARGET, RESERVED_ID_NAMES, RESERVED_ID_PREFIXES, ROUTE_FIELD_DEFINITIONS, ROUTE_TERMS, buildRouteRegistry, canonicalModifiers, collapseShiftPairs, defineTool, defineViewportTool, describeRoute, describeRouteParts, findConflicts, findScopedConflicts, formatConflict, formatPhaseAtom, formatRoute, getGestureDescriptor, isKnownGestureName, parseRoute, reportRouteConflicts } from './chunk-DCIU3SRK.js';
2
- import './chunk-PZ5AY32C.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-W2WB4352.js';
3
2
  //# sourceMappingURL=routing.js.map
4
3
  //# sourceMappingURL=routing.js.map
@@ -66,6 +66,12 @@ interface LayoutStrategy<TPose> {
66
66
  x: number;
67
67
  y: number;
68
68
  }): boolean;
69
+ /** Optional: reject a drag before any drop-target work happens. A type-aware
70
+ * container (a palette that only takes swatches, say) returns `false` and
71
+ * the drag falls through to whatever container is under it next. When
72
+ * absent, every drag is considered — rejection is still possible later, by
73
+ * `snap.pickTarget` returning null. */
74
+ acceptsDrop?(container: LayoutContainer, dragged: LayoutDragged<TPose>): boolean;
69
75
  }
70
76
 
71
77
  /**
@@ -134,6 +140,12 @@ interface MoveAdapter<TNode extends {
134
140
  * targeting (`getLayout` present), nested-hit collapse
135
141
  * (`pickTopMostHit`), and group-pose composition. Flat scenes may omit. */
136
142
  getParent?(id: string): string | null;
143
+ /** Optional paint depth, read by `pickTopMostHit` to resolve two *siblings*
144
+ * whose bodies both cover the pointer. Without it the hit list's own order
145
+ * decides, which is right for a back-to-front walk and wrong for anything
146
+ * else. See `PickTopMostHitAdapter` for the `compareZ` alternative. */
147
+ getZIndex?(id: string): number | null | undefined;
148
+ compareZ?(a: string, b: string): number;
137
149
  setPose(id: string, pose: TPose): void;
138
150
  /** Optional. Used only by reparent ops (e.g. drag-into-container drops via
139
151
  * layout strategies). Flat scenes that never reparent may omit. */
@@ -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-B_-khFM0.js';
2
+ import { S as SnapTarget, M as MoveAdapter, I as InsertAdapter } from './types-Ch0Goo-j.js';
3
3
 
4
4
  /**
5
5
  * Snapshot of modifier-key state at gesture dispatch.
@@ -16,6 +16,9 @@ interface DebugConfig {
16
16
  fps?: boolean;
17
17
  /** Optional per-feature color overrides; falls back to the default theme. */
18
18
  theme?: Partial<DebugTheme>;
19
+ /** Optional per-feature line-width / dash overrides; falls back to
20
+ * {@link DEFAULT_DEBUG_STROKES}. */
21
+ strokes?: Partial<DebugStrokes>;
19
22
  }
20
23
  type DebugFeature = 'hitboxes' | 'handles' | 'bounds' | 'origins' | 'snap' | 'layers' | 'ids' | 'fps';
21
24
  interface DebugTheme {
@@ -33,6 +36,22 @@ interface DebugTheme {
33
36
  fpsText: string;
34
37
  fpsTextBg: string;
35
38
  }
39
+ /** Line width and dash pattern for one stroked debug feature. An empty (or
40
+ * omitted) `dash` is a solid line. */
41
+ interface DebugStroke {
42
+ width: number;
43
+ dash?: readonly number[];
44
+ }
45
+ /** The stroked half of the debug overlay's appearance, split from
46
+ * {@link DebugTheme} because only some features draw a line at all — text
47
+ * panels and filled origin dots take color and nothing else. */
48
+ interface DebugStrokes {
49
+ hitbox: DebugStroke;
50
+ bounds: DebugStroke;
51
+ handle: DebugStroke;
52
+ /** Rejected snap candidates only; accepted ones paint as a filled dot. */
53
+ snap: DebugStroke;
54
+ }
36
55
  type HandleKind = 'corner' | 'rotation' | 'anchor';
37
56
  type HitShape = {
38
57
  kind: 'rect';
@@ -127,4 +146,4 @@ interface DebugSink {
127
146
  clearSnap(): void;
128
147
  }
129
148
 
130
- export type { DebugSink as D, HandleKind as H, RecordedBounds as R, DebugConfig as a, DebugSnapshot as b, DebugTheme as c, DebugFeature as d, HitShape as e, RecordedHandle as f, RecordedHitbox as g, RecordedLayer as h, RecordedOrigin as i, RecordedSnap as j };
149
+ export type { DebugSink as D, HandleKind as H, RecordedBounds as R, DebugConfig as a, DebugSnapshot as b, DebugStrokes as c, DebugTheme as d, DebugFeature as e, DebugStroke as f, HitShape as g, RecordedHandle as h, RecordedHitbox as i, RecordedLayer as j, RecordedOrigin as k, RecordedSnap as l };
@@ -7,7 +7,7 @@ import { P as Path } from './path-B6MMiodD.js';
7
7
  * shape used by `composeRectPose` and the `unionBounds` helper. Rotation is
8
8
  * in radians, pivoted on the unrotated AABB center; absent === 0. Kit-side
9
9
  * code that consumes rotation already reads `pose.rotation ?? 0`
10
- * (`SceneCanvas.defaultDrawOne`, `rotate/handle.ts`, `pathInWorld.ts`), so
10
+ * (`wrapNodeOutput`, `rotate/handle.ts`, `pathInWorld.ts`), so
11
11
  * the slot exists on every default scene whether or not the consumer
12
12
  * populates it.
13
13
  */
@@ -212,6 +212,13 @@ interface Scene<TData, TLayer extends string, TPose = RectPose> {
212
212
  childrenOf(id: NodeId): readonly NodeId[];
213
213
  ancestorsOf(id: NodeId): readonly NodeId[];
214
214
  renderOrder(): Iterable<NodeId>;
215
+ /** The same layer-major sequence as {@link Scene.renderOrder}, as the nodes
216
+ * themselves. Prefer this wherever the ids are only going to be resolved
217
+ * back to nodes: the traversal already holds them, and re-looking each one
218
+ * up was ~40% of the area hit-test's per-node cost. Cached until a
219
+ * structural edit, so repeat calls hand back the same array — a snapshot,
220
+ * not a live view, and not yours to mutate. */
221
+ renderOrderNodes(): readonly Node<TData, TLayer, TPose>[];
215
222
  add(spec: AddNodeSpec<TData, TLayer, TPose>): NodeId;
216
223
  /** Delete `id` **and its entire subtree** — every descendant is removed in
217
224
  * the same operation. Recorded as one undoable step; `undo()` restores the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weasel-js/core",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
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.0",
78
- "@weasel-js/geom": "1.0.0",
79
- "@weasel-js/gestures": "1.0.0",
80
- "@weasel-js/history": "1.0.0",
81
- "@weasel-js/modes": "1.0.0",
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",
82
82
  "earcut": "2.2.4",
83
83
  "polygon-clipping": "^0.15.7"
84
84
  },