system-canvas 0.1.0 → 0.1.1

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 (65) hide show
  1. package/dist/actions.d.ts +50 -0
  2. package/dist/actions.d.ts.map +1 -0
  3. package/dist/actions.js +85 -0
  4. package/dist/actions.js.map +1 -0
  5. package/dist/canvas.d.ts +40 -1
  6. package/dist/canvas.d.ts.map +1 -1
  7. package/dist/canvas.js +180 -0
  8. package/dist/canvas.js.map +1 -1
  9. package/dist/index.d.ts +12 -4
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +15 -3
  12. package/dist/index.js.map +1 -1
  13. package/dist/lanes.d.ts +50 -0
  14. package/dist/lanes.d.ts.map +1 -0
  15. package/dist/lanes.js +129 -0
  16. package/dist/lanes.js.map +1 -0
  17. package/dist/paths.d.ts +15 -0
  18. package/dist/paths.d.ts.map +1 -0
  19. package/dist/paths.js +41 -0
  20. package/dist/paths.js.map +1 -0
  21. package/dist/rendering/index.d.ts +2 -1
  22. package/dist/rendering/index.d.ts.map +1 -1
  23. package/dist/rendering/index.js +1 -1
  24. package/dist/rendering/index.js.map +1 -1
  25. package/dist/rendering/viewport-math.d.ts +28 -0
  26. package/dist/rendering/viewport-math.d.ts.map +1 -1
  27. package/dist/rendering/viewport-math.js +49 -0
  28. package/dist/rendering/viewport-math.js.map +1 -1
  29. package/dist/rollup.d.ts +23 -0
  30. package/dist/rollup.d.ts.map +1 -0
  31. package/dist/rollup.js +56 -0
  32. package/dist/rollup.js.map +1 -0
  33. package/dist/slots.d.ts +65 -0
  34. package/dist/slots.d.ts.map +1 -0
  35. package/dist/slots.js +367 -0
  36. package/dist/slots.js.map +1 -0
  37. package/dist/themes/blueprint.d.ts.map +1 -1
  38. package/dist/themes/blueprint.js +13 -1
  39. package/dist/themes/blueprint.js.map +1 -1
  40. package/dist/themes/dark.d.ts.map +1 -1
  41. package/dist/themes/dark.js +13 -1
  42. package/dist/themes/dark.js.map +1 -1
  43. package/dist/themes/index.d.ts +1 -0
  44. package/dist/themes/index.d.ts.map +1 -1
  45. package/dist/themes/index.js +1 -0
  46. package/dist/themes/index.js.map +1 -1
  47. package/dist/themes/light.d.ts.map +1 -1
  48. package/dist/themes/light.js +13 -1
  49. package/dist/themes/light.js.map +1 -1
  50. package/dist/themes/midnight.d.ts.map +1 -1
  51. package/dist/themes/midnight.js +13 -1
  52. package/dist/themes/midnight.js.map +1 -1
  53. package/dist/themes/resolve.d.ts.map +1 -1
  54. package/dist/themes/resolve.js +5 -0
  55. package/dist/themes/resolve.js.map +1 -1
  56. package/dist/themes/roadmap.d.ts +13 -0
  57. package/dist/themes/roadmap.d.ts.map +1 -0
  58. package/dist/themes/roadmap.js +264 -0
  59. package/dist/themes/roadmap.js.map +1 -0
  60. package/dist/themes/warm.d.ts.map +1 -1
  61. package/dist/themes/warm.js +13 -1
  62. package/dist/themes/warm.js.map +1 -1
  63. package/dist/types.d.ts +419 -0
  64. package/dist/types.d.ts.map +1 -1
  65. package/package.json +13 -3
package/dist/lanes.js ADDED
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Find the lane (if any) that contains the given position along its axis.
3
+ *
4
+ * For `columns`, pass the node's x (or x + width/2). For `rows`, pass y.
5
+ * Returns the first matching lane, or null if the position sits outside
6
+ * every lane.
7
+ */
8
+ export function findLaneAt(pos, lanes) {
9
+ if (!lanes || lanes.length === 0)
10
+ return null;
11
+ for (const lane of lanes) {
12
+ if (pos >= lane.start && pos < lane.start + lane.size)
13
+ return lane;
14
+ }
15
+ return null;
16
+ }
17
+ /**
18
+ * Snap a position to a lane boundary.
19
+ *
20
+ * `edge: 'start'` (default) snaps to the lane's start edge — useful for
21
+ * snapping a node's x/y when you want nodes to left/top-align in their lane.
22
+ * `edge: 'center'` snaps so an object of the given `size` is centered
23
+ * within the lane — useful when nodes sit visually centered in their row
24
+ * or column. Requires `size` to behave as expected; falls back to `start`
25
+ * behavior when `size` is 0.
26
+ * `edge: 'nearest'` snaps to whichever boundary (start, end, or any interior
27
+ * divider between two adjacent lanes) is closest — useful when the user
28
+ * drags across lane borders.
29
+ *
30
+ * If `size` is provided, the input is treated as the leading edge of an
31
+ * object of that size, and the snap target accounts for keeping the object
32
+ * fully inside a single lane when possible.
33
+ *
34
+ * Returns `pos` unchanged when `lanes` is empty or the position falls
35
+ * outside every lane (nothing meaningful to snap to).
36
+ */
37
+ export function snapToLane(pos, lanes, options = {}) {
38
+ if (!lanes || lanes.length === 0)
39
+ return pos;
40
+ const edge = options.edge ?? 'start';
41
+ const size = options.size ?? 0;
42
+ if (edge === 'start' || edge === 'center') {
43
+ // Use the object's center for lane selection so a node doesn't flip
44
+ // into the neighbor lane just because its top/left edge crosses a
45
+ // divider. Prefer the lane whose body contains the center; fall back
46
+ // to nearest-start by center.
47
+ const center = pos + size / 2;
48
+ const containing = findLaneAt(center, lanes);
49
+ let target;
50
+ if (containing) {
51
+ target = containing;
52
+ }
53
+ else {
54
+ let best = lanes[0];
55
+ let bestDist = Math.abs(center - (best.start + best.size / 2));
56
+ for (const lane of lanes) {
57
+ const d = Math.abs(center - (lane.start + lane.size / 2));
58
+ if (d < bestDist) {
59
+ best = lane;
60
+ bestDist = d;
61
+ }
62
+ }
63
+ target = best;
64
+ }
65
+ if (edge === 'center') {
66
+ return target.start + (target.size - size) / 2;
67
+ }
68
+ return target.start;
69
+ }
70
+ // 'nearest': consider every lane boundary (start of each lane, plus end
71
+ // of the last lane) and pick the closest.
72
+ const boundaries = [];
73
+ for (const lane of lanes)
74
+ boundaries.push(lane.start);
75
+ const last = lanes[lanes.length - 1];
76
+ boundaries.push(last.start + last.size);
77
+ // When size is given, the object's right/bottom edge is `pos + size`.
78
+ // We want `pos` itself to snap to a boundary — simple nearest is fine.
79
+ let best = boundaries[0];
80
+ let bestDist = Math.abs(pos - best);
81
+ for (const b of boundaries) {
82
+ const d = Math.abs(pos - b);
83
+ if (d < bestDist) {
84
+ best = b;
85
+ bestDist = d;
86
+ }
87
+ }
88
+ // If snapping would overflow the last lane given the object size, pull
89
+ // back to keep the object inside.
90
+ if (size > 0) {
91
+ const maxStart = last.start + last.size - size;
92
+ if (best > maxStart)
93
+ best = maxStart;
94
+ }
95
+ return best;
96
+ }
97
+ /**
98
+ * Convenience: produce a set of equally-sized lanes from an array of labels.
99
+ *
100
+ * Consumers that don't want to compute pixel positions can write
101
+ * `evenLanes(['Now', 'Next', 'Later'])` and get three 400-wide lanes
102
+ * starting at x = 0.
103
+ */
104
+ export function evenLanes(labels, size = 400, start = 0) {
105
+ return labels.map((label, i) => ({
106
+ id: label,
107
+ label,
108
+ start: start + i * size,
109
+ size,
110
+ }));
111
+ }
112
+ /**
113
+ * Total extent covered by a lane list (from first.start to last end).
114
+ * Returns 0 when the list is empty.
115
+ */
116
+ export function lanesExtent(lanes) {
117
+ if (!lanes || lanes.length === 0)
118
+ return { start: 0, end: 0 };
119
+ let start = Infinity;
120
+ let end = -Infinity;
121
+ for (const lane of lanes) {
122
+ if (lane.start < start)
123
+ start = lane.start;
124
+ if (lane.start + lane.size > end)
125
+ end = lane.start + lane.size;
126
+ }
127
+ return { start, end };
128
+ }
129
+ //# sourceMappingURL=lanes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lanes.js","sourceRoot":"","sources":["../src/lanes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Minimal dot-path utilities used by the form editor to read/write nested
3
+ * fields like `customData.status`. Dependency-free; no array-index syntax.
4
+ *
5
+ * `getAtPath({customData: {status: 'done'}}, 'customData.status') === 'done'`
6
+ * `setAtPath({}, 'customData.status', 'done')` → `{customData: {status: 'done'}}`
7
+ */
8
+ export declare function getAtPath(obj: unknown, path: string): unknown;
9
+ /**
10
+ * Set a value at the given dot-path, returning a new object with the value
11
+ * applied. Existing branches are shallow-cloned along the path; siblings
12
+ * are preserved by reference. Does not mutate `obj`.
13
+ */
14
+ export declare function setAtPath<T extends Record<string, any>>(obj: T | undefined, path: string, value: unknown): T;
15
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAS7D;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACrD,GAAG,EAAE,CAAC,GAAG,SAAS,EAClB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,OAAO,GACb,CAAC,CAgBH"}
package/dist/paths.js ADDED
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Minimal dot-path utilities used by the form editor to read/write nested
3
+ * fields like `customData.status`. Dependency-free; no array-index syntax.
4
+ *
5
+ * `getAtPath({customData: {status: 'done'}}, 'customData.status') === 'done'`
6
+ * `setAtPath({}, 'customData.status', 'done')` → `{customData: {status: 'done'}}`
7
+ */
8
+ export function getAtPath(obj, path) {
9
+ if (obj == null)
10
+ return undefined;
11
+ const parts = path.split('.');
12
+ let cur = obj;
13
+ for (const p of parts) {
14
+ if (cur == null || typeof cur !== 'object')
15
+ return undefined;
16
+ cur = cur[p];
17
+ }
18
+ return cur;
19
+ }
20
+ /**
21
+ * Set a value at the given dot-path, returning a new object with the value
22
+ * applied. Existing branches are shallow-cloned along the path; siblings
23
+ * are preserved by reference. Does not mutate `obj`.
24
+ */
25
+ export function setAtPath(obj, path, value) {
26
+ const parts = path.split('.');
27
+ const root = obj ? { ...obj } : {};
28
+ let cur = root;
29
+ for (let i = 0; i < parts.length - 1; i++) {
30
+ const key = parts[i];
31
+ const next = cur[key];
32
+ const cloned = next != null && typeof next === 'object' && !Array.isArray(next)
33
+ ? { ...next }
34
+ : {};
35
+ cur[key] = cloned;
36
+ cur = cloned;
37
+ }
38
+ cur[parts[parts.length - 1]] = value;
39
+ return root;
40
+ }
41
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,UAAU,SAAS,CAAC,GAAY,EAAE,IAAY;IAClD,IAAI,GAAG,IAAI,IAAI;QAAE,OAAO,SAAS,CAAA;IACjC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAC7B,IAAI,GAAG,GAAQ,GAAG,CAAA;IAClB,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,IAAI,GAAG,IAAI,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAA;QAC5D,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,CAAA;IACd,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS,CACvB,GAAkB,EAClB,IAAY,EACZ,KAAc;IAEd,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAC7B,MAAM,IAAI,GAAwB,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;IACvD,IAAI,GAAG,GAAG,IAAI,CAAA;IACd,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC1C,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;QACpB,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,CAAA;QACrB,MAAM,MAAM,GACV,IAAI,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;YAC9D,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE;YACb,CAAC,CAAC,EAAE,CAAA;QACR,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,CAAA;QACjB,GAAG,GAAG,MAAM,CAAA;IACd,CAAC;IACD,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,CAAA;IACpC,OAAO,IAAS,CAAA;AAClB,CAAC"}
@@ -1,4 +1,5 @@
1
1
  export { computeAnchorPoint, inferSide } from './anchor-points.js';
2
2
  export { computeEdgePath, computeEdgeMidpoint } from './edge-routing.js';
3
- export { computeBoundingBox, fitToBounds, screenToCanvas, canvasToScreen, } from './viewport-math.js';
3
+ export { computeBoundingBox, fitToBounds, fitBoundsIntoRect, canvasRectToScreenRect, screenToCanvas, canvasToScreen, } from './viewport-math.js';
4
+ export type { Rect } from './viewport-math.js';
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rendering/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAClE,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AACxE,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,cAAc,EACd,cAAc,GACf,MAAM,oBAAoB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rendering/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAClE,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AACxE,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,iBAAiB,EACjB,sBAAsB,EACtB,cAAc,EACd,cAAc,GACf,MAAM,oBAAoB,CAAA;AAC3B,YAAY,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAA"}
@@ -1,4 +1,4 @@
1
1
  export { computeAnchorPoint, inferSide } from './anchor-points.js';
2
2
  export { computeEdgePath, computeEdgeMidpoint } from './edge-routing.js';
3
- export { computeBoundingBox, fitToBounds, screenToCanvas, canvasToScreen, } from './viewport-math.js';
3
+ export { computeBoundingBox, fitToBounds, fitBoundsIntoRect, canvasRectToScreenRect, screenToCanvas, canvasToScreen, } from './viewport-math.js';
4
4
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rendering/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAClE,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AACxE,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,cAAc,EACd,cAAc,GACf,MAAM,oBAAoB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rendering/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAClE,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AACxE,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,iBAAiB,EACjB,sBAAsB,EACtB,cAAc,EACd,cAAc,GACf,MAAM,oBAAoB,CAAA"}
@@ -22,4 +22,32 @@ export declare function canvasToScreen(canvasX: number, canvasY: number, viewpor
22
22
  x: number;
23
23
  y: number;
24
24
  };
25
+ /**
26
+ * Axis-aligned rectangle in either canvas-space or screen-space.
27
+ */
28
+ export interface Rect {
29
+ x: number;
30
+ y: number;
31
+ width: number;
32
+ height: number;
33
+ }
34
+ /**
35
+ * Compute a viewport transform that places the given canvas-space bounds
36
+ * (`bounds`) so that they fit inside the given screen-space rect
37
+ * (`targetScreenRect`), preserving aspect ratio (contain). When the aspect
38
+ * ratios differ, the bounds are letterboxed (centered) on the shorter axis.
39
+ *
40
+ * `padding` (in screen pixels) insets the target rect on all sides before
41
+ * fitting, giving the content visual breathing room.
42
+ *
43
+ * This is the core "seamless handoff" math: given a parent node's on-screen
44
+ * rect and a child canvas's bounding box, it returns the new viewport
45
+ * transform that makes the child canvas appear exactly where the parent
46
+ * node was.
47
+ */
48
+ export declare function fitBoundsIntoRect(bounds: BoundingBox, targetScreenRect: Rect, padding?: number): ViewportState;
49
+ /**
50
+ * Compute the on-screen rect of a canvas-space rect under the given viewport.
51
+ */
52
+ export declare function canvasRectToScreenRect(canvasRect: Rect, viewport: ViewportState): Rect;
25
53
  //# sourceMappingURL=viewport-math.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"viewport-math.d.ts","sourceRoot":"","sources":["../../src/rendering/viewport-math.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,WAAW,EACX,aAAa,EACd,MAAM,aAAa,CAAA;AAEpB;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,WAAW,CAyBrE;AAED;;;GAGG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,YAAY,EAAE,EACrB,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,EACtB,OAAO,GAAE,MAAW,GACnB,aAAa,CAsBf;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,aAAa,GACtB;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAK1B;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,aAAa,GACtB;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAK1B"}
1
+ {"version":3,"file":"viewport-math.d.ts","sourceRoot":"","sources":["../../src/rendering/viewport-math.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,WAAW,EACX,aAAa,EACd,MAAM,aAAa,CAAA;AAEpB;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,WAAW,CAyBrE;AAED;;;GAGG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,YAAY,EAAE,EACrB,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,EACtB,OAAO,GAAE,MAAW,GACnB,aAAa,CAsBf;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,aAAa,GACtB;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAK1B;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,aAAa,GACtB;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAK1B;AAED;;GAEG;AACH,MAAM,WAAW,IAAI;IACnB,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,WAAW,EACnB,gBAAgB,EAAE,IAAI,EACtB,OAAO,GAAE,MAAU,GAClB,aAAa,CA4Bf;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,IAAI,EAChB,QAAQ,EAAE,aAAa,GACtB,IAAI,CAON"}
@@ -63,4 +63,53 @@ export function canvasToScreen(canvasX, canvasY, viewport) {
63
63
  y: canvasY * viewport.zoom + viewport.y,
64
64
  };
65
65
  }
66
+ /**
67
+ * Compute a viewport transform that places the given canvas-space bounds
68
+ * (`bounds`) so that they fit inside the given screen-space rect
69
+ * (`targetScreenRect`), preserving aspect ratio (contain). When the aspect
70
+ * ratios differ, the bounds are letterboxed (centered) on the shorter axis.
71
+ *
72
+ * `padding` (in screen pixels) insets the target rect on all sides before
73
+ * fitting, giving the content visual breathing room.
74
+ *
75
+ * This is the core "seamless handoff" math: given a parent node's on-screen
76
+ * rect and a child canvas's bounding box, it returns the new viewport
77
+ * transform that makes the child canvas appear exactly where the parent
78
+ * node was.
79
+ */
80
+ export function fitBoundsIntoRect(bounds, targetScreenRect, padding = 0) {
81
+ if (bounds.width === 0 || bounds.height === 0) {
82
+ // Degenerate bounds — center the single point in the target rect at 1x.
83
+ return {
84
+ x: targetScreenRect.x + targetScreenRect.width / 2 - bounds.minX,
85
+ y: targetScreenRect.y + targetScreenRect.height / 2 - bounds.minY,
86
+ zoom: 1,
87
+ };
88
+ }
89
+ const availW = Math.max(1, targetScreenRect.width - padding * 2);
90
+ const availH = Math.max(1, targetScreenRect.height - padding * 2);
91
+ const scaleX = availW / bounds.width;
92
+ const scaleY = availH / bounds.height;
93
+ // Contain (fit-inside): use the smaller scale so both dimensions fit.
94
+ const zoom = Math.min(scaleX, scaleY);
95
+ // Center the scaled bounds inside the padded target rect.
96
+ const scaledW = bounds.width * zoom;
97
+ const scaledH = bounds.height * zoom;
98
+ const offsetX = padding + (availW - scaledW) / 2;
99
+ const offsetY = padding + (availH - scaledH) / 2;
100
+ const x = targetScreenRect.x + offsetX - bounds.minX * zoom;
101
+ const y = targetScreenRect.y + offsetY - bounds.minY * zoom;
102
+ return { x, y, zoom };
103
+ }
104
+ /**
105
+ * Compute the on-screen rect of a canvas-space rect under the given viewport.
106
+ */
107
+ export function canvasRectToScreenRect(canvasRect, viewport) {
108
+ return {
109
+ x: canvasRect.x * viewport.zoom + viewport.x,
110
+ y: canvasRect.y * viewport.zoom + viewport.y,
111
+ width: canvasRect.width * viewport.zoom,
112
+ height: canvasRect.height * viewport.zoom,
113
+ };
114
+ }
66
115
  //# sourceMappingURL=viewport-math.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"viewport-math.js","sourceRoot":"","sources":["../../src/rendering/viewport-math.ts"],"names":[],"mappings":"AAMA;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAqB;IACtD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAA;IACpE,CAAC;IAED,IAAI,IAAI,GAAG,QAAQ,CAAA;IACnB,IAAI,IAAI,GAAG,QAAQ,CAAA;IACnB,IAAI,IAAI,GAAG,CAAC,QAAQ,CAAA;IACpB,IAAI,IAAI,GAAG,CAAC,QAAQ,CAAA;IAEpB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAA;QAC7B,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAA;QAC7B,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAA;QAC1C,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAA;IAC7C,CAAC;IAED,OAAO;QACL,IAAI;QACJ,IAAI;QACJ,IAAI;QACJ,IAAI;QACJ,KAAK,EAAE,IAAI,GAAG,IAAI;QAClB,MAAM,EAAE,IAAI,GAAG,IAAI;KACpB,CAAA;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CACzB,KAAqB,EACrB,aAAqB,EACrB,cAAsB,EACtB,UAAkB,EAAE;IAEpB,MAAM,MAAM,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAA;IAExC,IAAI,MAAM,CAAC,KAAK,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9C,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAA;IAChC,CAAC;IAED,MAAM,cAAc,GAAG,aAAa,GAAG,OAAO,GAAG,CAAC,CAAA;IAClD,MAAM,eAAe,GAAG,cAAc,GAAG,OAAO,GAAG,CAAC,CAAA;IAEpD,MAAM,MAAM,GAAG,cAAc,GAAG,MAAM,CAAC,KAAK,CAAA;IAC5C,MAAM,MAAM,GAAG,eAAe,GAAG,MAAM,CAAC,MAAM,CAAA;IAC9C,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAAA,CAAC,YAAY;IAErD,qBAAqB;IACrB,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,KAAK,GAAG,CAAC,CAAA;IACrD,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAA;IAEtD,MAAM,CAAC,GAAG,aAAa,GAAG,CAAC,GAAG,cAAc,GAAG,IAAI,CAAA;IACnD,MAAM,CAAC,GAAG,cAAc,GAAG,CAAC,GAAG,cAAc,GAAG,IAAI,CAAA;IAEpD,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,CAAA;AACvB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAC5B,OAAe,EACf,OAAe,EACf,QAAuB;IAEvB,OAAO;QACL,CAAC,EAAE,CAAC,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,IAAI;QACzC,CAAC,EAAE,CAAC,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,IAAI;KAC1C,CAAA;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAC5B,OAAe,EACf,OAAe,EACf,QAAuB;IAEvB,OAAO;QACL,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC,IAAI,GAAG,QAAQ,CAAC,CAAC;QACvC,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC,IAAI,GAAG,QAAQ,CAAC,CAAC;KACxC,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"viewport-math.js","sourceRoot":"","sources":["../../src/rendering/viewport-math.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { CanvasData, CanvasNode, RollupResult } from './types.js';
2
+ /**
3
+ * Count how many direct children of `canvas` match `predicate`, and return
4
+ * a `RollupResult` with `total`, `matched`, and `fraction`.
5
+ *
6
+ * Safe on `undefined` / empty canvases: returns `{ total: 0, matched: 0,
7
+ * fraction: 0 }`. This is the ergonomic fallback used by slot accessors
8
+ * when a node's sub-canvas hasn't resolved yet.
9
+ *
10
+ * Only counts direct children. For recursive counts through nested
11
+ * sub-canvases, use `rollupNodesDeep`.
12
+ */
13
+ export declare function rollupNodes(canvas: CanvasData | undefined, predicate: (node: CanvasNode) => boolean): RollupResult;
14
+ /**
15
+ * Recursive version of `rollupNodes`. For each node that has a `ref`,
16
+ * looks up its sub-canvas via `getSubCanvas` and counts its descendants as
17
+ * well. Nodes with refs but unresolved sub-canvases still count as a single
18
+ * node (themselves) in `total`.
19
+ *
20
+ * Cycle-safe: each canvas ref is visited at most once per rollup.
21
+ */
22
+ export declare function rollupNodesDeep(canvas: CanvasData | undefined, predicate: (node: CanvasNode) => boolean, getSubCanvas: (ref: string) => CanvasData | undefined): RollupResult;
23
+ //# sourceMappingURL=rollup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rollup.d.ts","sourceRoot":"","sources":["../src/rollup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAEtE;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,UAAU,GAAG,SAAS,EAC9B,SAAS,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,OAAO,GACvC,YAAY,CASd;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,UAAU,GAAG,SAAS,EAC9B,SAAS,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,OAAO,EACxC,YAAY,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,UAAU,GAAG,SAAS,GACpD,YAAY,CAuBd"}
package/dist/rollup.js ADDED
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Count how many direct children of `canvas` match `predicate`, and return
3
+ * a `RollupResult` with `total`, `matched`, and `fraction`.
4
+ *
5
+ * Safe on `undefined` / empty canvases: returns `{ total: 0, matched: 0,
6
+ * fraction: 0 }`. This is the ergonomic fallback used by slot accessors
7
+ * when a node's sub-canvas hasn't resolved yet.
8
+ *
9
+ * Only counts direct children. For recursive counts through nested
10
+ * sub-canvases, use `rollupNodesDeep`.
11
+ */
12
+ export function rollupNodes(canvas, predicate) {
13
+ const nodes = canvas?.nodes ?? [];
14
+ const total = nodes.length;
15
+ if (total === 0)
16
+ return { total: 0, matched: 0, fraction: 0 };
17
+ let matched = 0;
18
+ for (const n of nodes) {
19
+ if (predicate(n))
20
+ matched++;
21
+ }
22
+ return { total, matched, fraction: matched / total };
23
+ }
24
+ /**
25
+ * Recursive version of `rollupNodes`. For each node that has a `ref`,
26
+ * looks up its sub-canvas via `getSubCanvas` and counts its descendants as
27
+ * well. Nodes with refs but unresolved sub-canvases still count as a single
28
+ * node (themselves) in `total`.
29
+ *
30
+ * Cycle-safe: each canvas ref is visited at most once per rollup.
31
+ */
32
+ export function rollupNodesDeep(canvas, predicate, getSubCanvas) {
33
+ const visited = new Set();
34
+ let total = 0;
35
+ let matched = 0;
36
+ const walk = (c) => {
37
+ if (!c)
38
+ return;
39
+ for (const n of c.nodes ?? []) {
40
+ total++;
41
+ if (predicate(n))
42
+ matched++;
43
+ if (n.ref && !visited.has(n.ref)) {
44
+ visited.add(n.ref);
45
+ walk(getSubCanvas(n.ref));
46
+ }
47
+ }
48
+ };
49
+ walk(canvas);
50
+ return {
51
+ total,
52
+ matched,
53
+ fraction: total === 0 ? 0 : matched / total,
54
+ };
55
+ }
56
+ //# sourceMappingURL=rollup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rollup.js","sourceRoot":"","sources":["../src/rollup.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AACH,MAAM,UAAU,WAAW,CACzB,MAA8B,EAC9B,SAAwC;IAExC,MAAM,KAAK,GAAG,MAAM,EAAE,KAAK,IAAI,EAAE,CAAA;IACjC,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAA;IAC1B,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAA;IAC7D,IAAI,OAAO,GAAG,CAAC,CAAA;IACf,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,IAAI,SAAS,CAAC,CAAC,CAAC;YAAE,OAAO,EAAE,CAAA;IAC7B,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,GAAG,KAAK,EAAE,CAAA;AACtD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAC7B,MAA8B,EAC9B,SAAwC,EACxC,YAAqD;IAErD,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAA;IACjC,IAAI,KAAK,GAAG,CAAC,CAAA;IACb,IAAI,OAAO,GAAG,CAAC,CAAA;IAEf,MAAM,IAAI,GAAG,CAAC,CAAyB,EAAE,EAAE;QACzC,IAAI,CAAC,CAAC;YAAE,OAAM;QACd,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;YAC9B,KAAK,EAAE,CAAA;YACP,IAAI,SAAS,CAAC,CAAC,CAAC;gBAAE,OAAO,EAAE,CAAA;YAC3B,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;gBACjC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;gBAClB,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;YAC3B,CAAC;QACH,CAAC;IACH,CAAC,CAAA;IAED,IAAI,CAAC,MAAM,CAAC,CAAA;IACZ,OAAO;QACL,KAAK;QACL,OAAO;QACP,QAAQ,EAAE,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,GAAG,KAAK;KAC5C,CAAA;AACH,CAAC"}
@@ -0,0 +1,65 @@
1
+ import type { CanvasTheme, CategorySlots, NodeAccessor, ResolvedNode, SlotContext, SlotPosition, SlotRect, SlotSpec } from './types.js';
2
+ /**
3
+ * Compute the region rect (in canvas-space) for every slot position on a
4
+ * resolved node. Pure geometry — no knowledge of which slots are actually
5
+ * in use. Callers index by `SlotPosition`.
6
+ */
7
+ export declare function computeCategorySlotRegions(node: ResolvedNode, theme: CanvasTheme, slots?: CategorySlots): Record<SlotPosition, SlotRect>;
8
+ /**
9
+ * Resolve an accessor against a `SlotContext`. Function accessors are
10
+ * called; static values are returned as-is.
11
+ */
12
+ export declare function resolveAccessor<T>(accessor: NodeAccessor<T>, ctx: SlotContext): T;
13
+ /**
14
+ * Resolve an accessor with a fallback for the function case. Useful when
15
+ * a slot renders a default from the theme but allows per-call overrides.
16
+ */
17
+ export declare function resolveAccessorOr<T>(accessor: NodeAccessor<T> | undefined, fallback: T, ctx: SlotContext): T;
18
+ /**
19
+ * Look up the slots map for a node's category. Returns undefined when the
20
+ * node has no category, or the category has no slots declared.
21
+ */
22
+ export declare function getCategorySlots(node: ResolvedNode, theme: CanvasTheme): CategorySlots | undefined;
23
+ type Corner = 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight';
24
+ /**
25
+ * Pick a corner for the ref indicator given which slots are occupied.
26
+ *
27
+ * If the default corner is free, it wins. Otherwise we try the diagonally
28
+ * opposite corner, then the remaining two corners in clockwise order from
29
+ * the default. If all four corners are occupied we keep the default
30
+ * position — the caller can decide whether to log a dev warning.
31
+ *
32
+ * Corners occupied by `header` or `footer` count as occupied on *both*
33
+ * corners of that row: a header in the top strip blocks both `topLeft` and
34
+ * `topRight` (the indicator would overlap the header text strip anyway).
35
+ */
36
+ export declare function pickRefIndicatorCorner(defaultCorner: Corner, slots: CategorySlots | undefined): Corner;
37
+ /**
38
+ * Iterate over a slots map in a deterministic order. Returns entries as
39
+ * `[position, spec]` pairs. Order matches the `SlotPosition` definition —
40
+ * stable across runs so SVG paint order is stable.
41
+ */
42
+ export declare function slotEntries(slots: CategorySlots): Array<[SlotPosition, SlotSpec]>;
43
+ export interface ReflowReservations {
44
+ top: number;
45
+ bottom: number;
46
+ left: number;
47
+ right: number;
48
+ }
49
+ /**
50
+ * Compute how many pixels of the node's content box must be reserved on
51
+ * each side for library-owned slots so node text/icons can be reflowed
52
+ * without colliding with slots.
53
+ *
54
+ * - `header` → shift text down by header region height + inset
55
+ * - `footer` → shrink text vertical extent by footer region height + inset
56
+ * - `leftEdge` / `rightEdge` → inset text by the edge strip width
57
+ *
58
+ * Top/bottom edges and corner slots don't reflow text (they're thin strips
59
+ * or small badges that sit over the existing layout). If the node is so
60
+ * short that reservations would leave less than `fontSize` vertical space
61
+ * for text, the reflow is skipped (text stays centered).
62
+ */
63
+ export declare function computeReflowReservations(node: ResolvedNode, theme: CanvasTheme, slots: CategorySlots | undefined): ReflowReservations;
64
+ export {};
65
+ //# sourceMappingURL=slots.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slots.d.ts","sourceRoot":"","sources":["../src/slots.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EAEX,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,QAAQ,EACR,QAAQ,EACT,MAAM,YAAY,CAAA;AAmCnB;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,YAAY,EAClB,KAAK,EAAE,WAAW,EAClB,KAAK,CAAC,EAAE,aAAa,GACpB,MAAM,CAAC,YAAY,EAAE,QAAQ,CAAC,CAmHhC;AAsCD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,WAAW,GAAG,CAAC,CAEjF;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EACjC,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,SAAS,EACrC,QAAQ,EAAE,CAAC,EACX,GAAG,EAAE,WAAW,GACf,CAAC,CAGH;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,YAAY,EAClB,KAAK,EAAE,WAAW,GACjB,aAAa,GAAG,SAAS,CAI3B;AAMD,KAAK,MAAM,GAAG,SAAS,GAAG,UAAU,GAAG,YAAY,GAAG,aAAa,CAAA;AAEnE;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CACpC,aAAa,EAAE,MAAM,EACrB,KAAK,EAAE,aAAa,GAAG,SAAS,GAC/B,MAAM,CAyBR;AAMD;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,aAAa,GACnB,KAAK,CAAC,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC,CAwBjC;AAMD,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;CACd;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,YAAY,EAClB,KAAK,EAAE,WAAW,EAClB,KAAK,EAAE,aAAa,GAAG,SAAS,GAC/B,kBAAkB,CAEpB"}