@pixerate/editor 0.140.4 → 0.142.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 (125) hide show
  1. package/dist/agent/canvasPlayback.d.cts +15 -0
  2. package/dist/agent/canvasPlayback.d.ts +15 -0
  3. package/dist/agent/index.cjs +173 -33
  4. package/dist/agent/index.d.cts +1 -0
  5. package/dist/agent/index.d.ts +1 -0
  6. package/dist/agent/index.js +147 -7
  7. package/dist/agent/providers.d.cts +12 -1
  8. package/dist/agent/providers.d.ts +12 -1
  9. package/dist/agent/types.d.cts +3 -0
  10. package/dist/agent/types.d.ts +3 -0
  11. package/dist/animation/index.cjs +2 -2
  12. package/dist/animation/index.js +1 -1
  13. package/dist/animation/presence.d.cts +1 -75
  14. package/dist/animation/presence.d.ts +1 -75
  15. package/dist/animation-editor/index.cjs +69 -19
  16. package/dist/animation-editor/index.d.cts +1 -0
  17. package/dist/animation-editor/index.d.ts +1 -0
  18. package/dist/animation-editor/index.js +54 -4
  19. package/dist/animation-editor/playback.d.cts +10 -0
  20. package/dist/animation-editor/playback.d.ts +10 -0
  21. package/dist/bridge/cli.cjs +3 -3
  22. package/dist/bridge/cli.js +2 -2
  23. package/dist/bridge/index.cjs +3 -3
  24. package/dist/bridge/index.js +2 -2
  25. package/dist/canvas/index.cjs +336 -646
  26. package/dist/canvas/index.d.cts +1 -0
  27. package/dist/canvas/index.d.ts +1 -0
  28. package/dist/canvas/index.js +44 -354
  29. package/dist/canvas/playback.d.cts +14 -0
  30. package/dist/canvas/playback.d.ts +14 -0
  31. package/dist/chart/index.cjs +255 -171
  32. package/dist/chart/index.d.cts +1 -0
  33. package/dist/chart/index.d.ts +1 -0
  34. package/dist/chart/index.js +244 -160
  35. package/dist/chart/playback.d.cts +22 -0
  36. package/dist/chart/playback.d.ts +22 -0
  37. package/dist/chunk-54GVEVXS.js +408 -0
  38. package/dist/{chunk-E747F3D5.js → chunk-CC6WKVIH.js} +86 -5
  39. package/dist/chunk-DW4CV5H4.js +591 -0
  40. package/dist/{chunk-UMS6YFA6.js → chunk-EAQ4BGNS.js} +1 -1
  41. package/dist/{chunk-AGCD3EHB.cjs → chunk-ENIZYNEF.cjs} +7 -7
  42. package/dist/{chunk-BSGPXPTW.js → chunk-PEEP2YR7.js} +1 -1
  43. package/dist/{chunk-VTY64I6H.cjs → chunk-QSM3MDDJ.cjs} +85 -85
  44. package/dist/{chunk-2RUNJYOX.cjs → chunk-QZFEX2N4.cjs} +86 -5
  45. package/dist/{chunk-RB62U6MS.js → chunk-RFBFTLVN.js} +27 -5
  46. package/dist/chunk-UBRYHNRU.cjs +591 -0
  47. package/dist/{chunk-GEY73RUW.cjs → chunk-UJGAA6AK.cjs} +1 -1
  48. package/dist/{chunk-LMSFXRAJ.js → chunk-VGJTUCWM.js} +1 -1
  49. package/dist/chunk-X2HQP7DF.cjs +408 -0
  50. package/dist/chunk-XH32BATE.cjs +68 -0
  51. package/dist/{chunk-HDK3T2QS.cjs → chunk-Y4BWZB6J.cjs} +29 -7
  52. package/dist/chunk-Y6SGC6KO.js +68 -0
  53. package/dist/commands/index.cjs +37 -1
  54. package/dist/commands/index.js +36 -0
  55. package/dist/commands/registry.d.cts +11 -0
  56. package/dist/commands/registry.d.ts +11 -0
  57. package/dist/diagram/index.cjs +7 -7
  58. package/dist/diagram/index.js +1 -1
  59. package/dist/image-editor/index.cjs +108 -16
  60. package/dist/image-editor/index.d.cts +1 -0
  61. package/dist/image-editor/index.d.ts +1 -0
  62. package/dist/image-editor/index.js +107 -15
  63. package/dist/image-editor/playback.d.cts +22 -0
  64. package/dist/image-editor/playback.d.ts +22 -0
  65. package/dist/index.cjs +7 -3
  66. package/dist/index.js +6 -2
  67. package/dist/paint-editor/index.cjs +73 -25
  68. package/dist/paint-editor/index.d.cts +1 -0
  69. package/dist/paint-editor/index.d.ts +1 -0
  70. package/dist/paint-editor/index.js +68 -20
  71. package/dist/paint-editor/playback.d.cts +16 -0
  72. package/dist/paint-editor/playback.d.ts +16 -0
  73. package/dist/parameters/index.cjs +32 -8
  74. package/dist/parameters/index.d.cts +1 -0
  75. package/dist/parameters/index.d.ts +1 -0
  76. package/dist/parameters/index.js +28 -4
  77. package/dist/parameters/playback.d.cts +10 -0
  78. package/dist/parameters/playback.d.ts +10 -0
  79. package/dist/spreadsheet/index.cjs +7 -3
  80. package/dist/spreadsheet/index.d.cts +1 -0
  81. package/dist/spreadsheet/index.d.ts +1 -0
  82. package/dist/spreadsheet/index.js +6 -2
  83. package/dist/spreadsheet/playback.d.cts +15 -0
  84. package/dist/spreadsheet/playback.d.ts +15 -0
  85. package/dist/tools/index.cjs +40 -3
  86. package/dist/tools/index.d.cts +3 -0
  87. package/dist/tools/index.d.ts +3 -0
  88. package/dist/tools/index.js +39 -2
  89. package/dist/tools/playback.d.cts +205 -0
  90. package/dist/tools/playback.d.ts +205 -0
  91. package/dist/tools/presence.d.cts +82 -0
  92. package/dist/tools/presence.d.ts +82 -0
  93. package/dist/tools/registry.d.cts +45 -6
  94. package/dist/tools/registry.d.ts +45 -6
  95. package/dist/tools/script.d.cts +77 -0
  96. package/dist/tools/script.d.ts +77 -0
  97. package/dist/tools/view.d.cts +2 -4
  98. package/dist/tools/view.d.ts +2 -4
  99. package/dist/vector/index.cjs +217 -100
  100. package/dist/vector/index.d.cts +1 -0
  101. package/dist/vector/index.d.ts +1 -0
  102. package/dist/vector/index.js +140 -23
  103. package/dist/vector/playback.d.cts +13 -0
  104. package/dist/vector/playback.d.ts +13 -0
  105. package/dist/video-editor/index.cjs +81 -28
  106. package/dist/video-editor/index.d.cts +1 -0
  107. package/dist/video-editor/index.d.ts +1 -0
  108. package/dist/video-editor/index.js +58 -5
  109. package/dist/video-editor/playback.d.cts +20 -0
  110. package/dist/video-editor/playback.d.ts +20 -0
  111. package/package.json +1 -1
  112. package/skills/pixerate-agent-tools/SKILL.md +13 -0
  113. package/skills/pixerate-animation-editor/SKILL.md +1 -0
  114. package/skills/pixerate-canvas/SKILL.md +3 -0
  115. package/skills/pixerate-chart/SKILL.md +1 -0
  116. package/skills/pixerate-image-editor/SKILL.md +2 -2
  117. package/skills/pixerate-paint-editor/SKILL.md +1 -0
  118. package/skills/pixerate-parameters/SKILL.md +1 -0
  119. package/skills/pixerate-spreadsheet/SKILL.md +1 -0
  120. package/skills/pixerate-vector/SKILL.md +5 -0
  121. package/skills/pixerate-video-editor/SKILL.md +1 -0
  122. package/dist/chunk-BTZS3KZY.js +0 -221
  123. package/dist/chunk-K73HMKQP.js +0 -0
  124. package/dist/chunk-LACYURUW.cjs +0 -1
  125. package/dist/chunk-MRD3NXKR.cjs +0 -221
@@ -0,0 +1,591 @@
1
+ // src/canvas/layout.ts
2
+ import dagre from "@dagrejs/dagre";
3
+ var DEFAULT_NODE_WIDTH = 200;
4
+ var DEFAULT_NODE_HEIGHT = 60;
5
+ var DEFAULT_PLACEMENT_GAP = 48;
6
+ function getCenteredNodePosition(width = 0, height = 0, screenToFlowPosition, screenDimensions) {
7
+ const rect = screenDimensions ?? (typeof document !== "undefined" ? { width: document.documentElement.clientWidth, height: document.documentElement.clientHeight } : { width: 1920, height: 1080 });
8
+ const flowPos = screenToFlowPosition({ x: rect.width / 2, y: rect.height / 2 });
9
+ return {
10
+ x: flowPos.x - width / 2,
11
+ y: flowPos.y - height / 2
12
+ };
13
+ }
14
+ function centerNodes(nodes, screenToFlowPosition, getNodesBounds, screenDimensions) {
15
+ if (nodes.length === 0) {
16
+ return [];
17
+ }
18
+ const bounds = getNodesBounds(nodes);
19
+ const boundingBoxOrigin = { x: bounds.x, y: bounds.y };
20
+ const centeredPosition = getCenteredNodePosition(
21
+ bounds.width,
22
+ bounds.height,
23
+ screenToFlowPosition,
24
+ screenDimensions
25
+ );
26
+ const deltaX = centeredPosition.x - boundingBoxOrigin.x;
27
+ const deltaY = centeredPosition.y - boundingBoxOrigin.y;
28
+ return nodes.map((node) => ({
29
+ ...node,
30
+ position: {
31
+ x: node.position.x + deltaX,
32
+ y: node.position.y + deltaY
33
+ }
34
+ }));
35
+ }
36
+ function getLayoutedNodes(nodes, edges, getNodesBounds, options = {}) {
37
+ if (nodes.length === 0) return [];
38
+ const {
39
+ direction = "LR",
40
+ ranksep = 100,
41
+ nodesep = 25,
42
+ defaultNodeWidth = DEFAULT_NODE_WIDTH,
43
+ defaultNodeHeight = DEFAULT_NODE_HEIGHT
44
+ } = options;
45
+ const dagreGraph = new dagre.graphlib.Graph();
46
+ dagreGraph.setDefaultEdgeLabel(() => ({}));
47
+ dagreGraph.setGraph({ rankdir: direction, ranksep, nodesep });
48
+ const nodeIds = new Set(nodes.map((node) => node.id));
49
+ nodes.forEach((node) => {
50
+ const nodeWidth = node.measured?.width ?? node.width ?? defaultNodeWidth;
51
+ const nodeHeight = node.measured?.height ?? node.height ?? defaultNodeHeight;
52
+ dagreGraph.setNode(node.id, { width: nodeWidth, height: nodeHeight });
53
+ });
54
+ edges.forEach((edge) => {
55
+ if (nodeIds.has(edge.source) && nodeIds.has(edge.target)) {
56
+ dagreGraph.setEdge(edge.source, edge.target);
57
+ }
58
+ });
59
+ dagre.layout(dagreGraph);
60
+ return nodes.map((node) => {
61
+ const nodeWithPosition = dagreGraph.node(node.id);
62
+ const nodeWidth = node.measured?.width ?? node.width ?? defaultNodeWidth;
63
+ const nodeHeight = node.measured?.height ?? node.height ?? defaultNodeHeight;
64
+ return {
65
+ ...node,
66
+ position: {
67
+ x: nodeWithPosition.x - nodeWidth / 2,
68
+ y: nodeWithPosition.y - nodeHeight / 2
69
+ }
70
+ };
71
+ });
72
+ }
73
+ function estimateNodeSize(node, defaultWidth = DEFAULT_NODE_WIDTH, defaultHeight = DEFAULT_NODE_HEIGHT) {
74
+ const label = String(node.data?.label ?? node.data?.title ?? node.id ?? "");
75
+ const lines = label.split(/\n|<br\s*\/?>/);
76
+ const longest = Math.max(...lines.map((l) => l.replace(/<[^>]+>/g, "").length), 1);
77
+ const width = Math.min(480, Math.max(defaultWidth, longest * 8 + 48));
78
+ const height = Math.max(defaultHeight, lines.length * 20 + 24);
79
+ return { width, height };
80
+ }
81
+ function boundsOf(rects) {
82
+ if (!rects.length) return null;
83
+ const minX = Math.min(...rects.map((r) => r.x));
84
+ const minY = Math.min(...rects.map((r) => r.y));
85
+ const maxX = Math.max(...rects.map((r) => r.x + r.width));
86
+ const maxY = Math.max(...rects.map((r) => r.y + r.height));
87
+ return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
88
+ }
89
+ function centre(r) {
90
+ return { x: r.x + r.width / 2, y: r.y + r.height / 2 };
91
+ }
92
+ function overlaps(a, b, pad = 16) {
93
+ return a.x < b.x + b.width + pad && a.x + a.width + pad > b.x && a.y < b.y + b.height + pad && a.y + a.height + pad > b.y;
94
+ }
95
+ function flowSide(direction = "LR") {
96
+ switch (direction) {
97
+ case "LR":
98
+ return "right";
99
+ case "RL":
100
+ return "left";
101
+ case "BT":
102
+ return "above";
103
+ case "TB":
104
+ default:
105
+ return "below";
106
+ }
107
+ }
108
+ function ensureLayout(nodes, edges, options = {}) {
109
+ if (nodes.length === 0) return [];
110
+ const stored = options.storedPositions ?? {};
111
+ const missing = nodes.filter((n) => !stored[n.id]);
112
+ if (missing.length === 0) {
113
+ return nodes.map((n) => ({
114
+ ...n,
115
+ position: { ...stored[n.id] }
116
+ }));
117
+ }
118
+ const autoLayouted = getLayoutedNodes(nodes, edges, void 0, options);
119
+ const autoMap = new Map(
120
+ autoLayouted.map((n) => [n.id, n.position])
121
+ );
122
+ const both = nodes.filter((n) => stored[n.id] && autoMap.has(n.id));
123
+ if (both.length === 0) {
124
+ return autoLayouted;
125
+ }
126
+ let dx = 0;
127
+ let dy = 0;
128
+ for (const n of both) {
129
+ const s = stored[n.id];
130
+ const a = autoMap.get(n.id);
131
+ dx += s.x - a.x;
132
+ dy += s.y - a.y;
133
+ }
134
+ dx /= both.length;
135
+ dy /= both.length;
136
+ return nodes.map((node) => {
137
+ if (stored[node.id]) {
138
+ return {
139
+ ...node,
140
+ position: { ...stored[node.id] }
141
+ };
142
+ }
143
+ const autoPos = autoMap.get(node.id) ?? { x: 0, y: 0 };
144
+ return {
145
+ ...node,
146
+ position: {
147
+ x: Math.round(autoPos.x + dx),
148
+ y: Math.round(autoPos.y + dy)
149
+ }
150
+ };
151
+ });
152
+ }
153
+ function placeBeside(rects, size, side, nearNodeId, options = {}) {
154
+ const {
155
+ gap = DEFAULT_PLACEMENT_GAP,
156
+ ignore = [],
157
+ maxAttempts = 32
158
+ } = options;
159
+ const skip = new Set([nearNodeId, ...ignore].filter(Boolean));
160
+ const obstacles = Object.entries(rects).filter(([id]) => !skip.has(id)).map(([, r]) => r);
161
+ const anchor = nearNodeId && rects[nearNodeId] || boundsOf(Object.values(rects)) || { x: 0, y: 0, width: 0, height: 0 };
162
+ const anchorCenter = centre(anchor);
163
+ let baseX;
164
+ let baseY;
165
+ switch (side) {
166
+ case "right":
167
+ baseX = anchor.x + anchor.width + gap;
168
+ baseY = anchorCenter.y - size.height / 2;
169
+ break;
170
+ case "left":
171
+ baseX = anchor.x - gap - size.width;
172
+ baseY = anchorCenter.y - size.height / 2;
173
+ break;
174
+ case "above":
175
+ baseX = anchorCenter.x - size.width / 2;
176
+ baseY = anchor.y - gap - size.height;
177
+ break;
178
+ case "below":
179
+ default:
180
+ baseX = anchorCenter.x - size.width / 2;
181
+ baseY = anchor.y + anchor.height + gap;
182
+ break;
183
+ }
184
+ const isHorizontal = side === "right" || side === "left";
185
+ const step = (isHorizontal ? size.height : size.width) + 20;
186
+ for (let i = 0; i < maxAttempts; i++) {
187
+ const offset = i === 0 ? 0 : Math.ceil(i / 2) * step * (i % 2 === 1 ? 1 : -1);
188
+ const candidateBox = {
189
+ x: isHorizontal ? baseX : baseX + offset,
190
+ y: isHorizontal ? baseY + offset : baseY,
191
+ width: size.width,
192
+ height: size.height
193
+ };
194
+ if (!obstacles.some((obstacle) => overlaps(candidateBox, obstacle, 8))) {
195
+ return {
196
+ x: Math.round(candidateBox.x),
197
+ y: Math.round(candidateBox.y)
198
+ };
199
+ }
200
+ }
201
+ return {
202
+ x: Math.round(baseX),
203
+ y: Math.round(baseY)
204
+ };
205
+ }
206
+
207
+ // src/canvas/arrange.ts
208
+ var LOCKED_DATA_KEY = "locked";
209
+ var LOCKED_PREVIOUS_FLAGS_KEY = "lockedPrevious";
210
+ function parsePx(value) {
211
+ if (typeof value === "number" && Number.isFinite(value)) return value;
212
+ if (typeof value === "string") {
213
+ const match = /^\s*(-?\d+(?:\.\d+)?)(px)?\s*$/i.exec(value);
214
+ if (match) return Number(match[1]);
215
+ }
216
+ return void 0;
217
+ }
218
+ function readStyleSize(style) {
219
+ if (!style) return {};
220
+ if (typeof style === "object") {
221
+ return { width: parsePx(style.width), height: parsePx(style.height) };
222
+ }
223
+ const read = (prop) => {
224
+ const match = new RegExp(`(?:^|;)\\s*${prop}\\s*:\\s*([^;]+)`, "i").exec(style);
225
+ return match ? parsePx(match[1]) : void 0;
226
+ };
227
+ return { width: read("width"), height: read("height") };
228
+ }
229
+ function withStyleSize(style, size) {
230
+ if (!style) return style ?? void 0;
231
+ if (typeof style === "object") {
232
+ const next2 = { ...style };
233
+ if (size.width !== void 0 && "width" in next2) next2.width = size.width;
234
+ if (size.height !== void 0 && "height" in next2) next2.height = size.height;
235
+ return next2;
236
+ }
237
+ let next = style;
238
+ for (const prop of ["width", "height"]) {
239
+ const value = size[prop];
240
+ if (value === void 0) continue;
241
+ next = next.replace(
242
+ new RegExp(`((?:^|;)\\s*${prop}\\s*:\\s*)[^;]+`, "i"),
243
+ `$1${value}px`
244
+ );
245
+ }
246
+ return next;
247
+ }
248
+ function getNodeSize(node, options = {}) {
249
+ const styleSize = readStyleSize(node.style);
250
+ let width = node.measured?.width ?? node.width ?? styleSize.width;
251
+ let height = node.measured?.height ?? node.height ?? styleSize.height;
252
+ if (width === void 0 || height === void 0) {
253
+ const fallback = options.estimate === false ? { width: options.defaultWidth ?? 200, height: options.defaultHeight ?? 60 } : estimateNodeSize(node, options.defaultWidth, options.defaultHeight);
254
+ width ??= fallback.width;
255
+ height ??= fallback.height;
256
+ }
257
+ return { width, height };
258
+ }
259
+ function getAbsolutePosition(node, lookup) {
260
+ let x = node.position.x;
261
+ let y = node.position.y;
262
+ const seen = /* @__PURE__ */ new Set([node.id]);
263
+ let parentId = node.parentId;
264
+ while (parentId && !seen.has(parentId)) {
265
+ const parent = lookup.get(parentId);
266
+ if (!parent) break;
267
+ seen.add(parentId);
268
+ x += parent.position.x;
269
+ y += parent.position.y;
270
+ parentId = parent.parentId;
271
+ }
272
+ return { x, y };
273
+ }
274
+ function createNodeLookup(nodes) {
275
+ return new Map(nodes.map((n) => [n.id, n]));
276
+ }
277
+ function getNodeRect(node, lookup, options) {
278
+ const { x, y } = node.parentId && lookup ? getAbsolutePosition(node, lookup) : node.position;
279
+ return { x, y, ...getNodeSize(node, options) };
280
+ }
281
+ function isLocked(node) {
282
+ return node?.data?.[LOCKED_DATA_KEY] === true;
283
+ }
284
+ function lockNodes(nodes, ids) {
285
+ const idSet = new Set(ids);
286
+ return nodes.map((node) => {
287
+ if (!idSet.has(node.id) || isLocked(node)) return node;
288
+ const previous = {};
289
+ if (node.draggable !== void 0) previous.draggable = node.draggable;
290
+ if (node.deletable !== void 0) previous.deletable = node.deletable;
291
+ return {
292
+ ...node,
293
+ draggable: false,
294
+ deletable: false,
295
+ data: { ...node.data, [LOCKED_DATA_KEY]: true, [LOCKED_PREVIOUS_FLAGS_KEY]: previous }
296
+ };
297
+ });
298
+ }
299
+ function unlockNodes(nodes, ids) {
300
+ const idSet = new Set(ids);
301
+ return nodes.map((node) => {
302
+ if (!idSet.has(node.id) || !isLocked(node)) return node;
303
+ const {
304
+ [LOCKED_DATA_KEY]: _locked,
305
+ [LOCKED_PREVIOUS_FLAGS_KEY]: previous = {},
306
+ ...data
307
+ } = node.data;
308
+ const next = { ...node, data };
309
+ delete next.draggable;
310
+ delete next.deletable;
311
+ if (previous.draggable !== void 0) next.draggable = previous.draggable;
312
+ if (previous.deletable !== void 0) next.deletable = previous.deletable;
313
+ return next;
314
+ });
315
+ }
316
+ function toggleLockNodes(nodes, ids) {
317
+ const idSet = new Set(ids);
318
+ const anyUnlocked = nodes.some((n) => idSet.has(n.id) && !isLocked(n));
319
+ return anyUnlocked ? lockNodes(nodes, idSet) : unlockNodes(nodes, idSet);
320
+ }
321
+ function collect(nodes, ids, options) {
322
+ const lookup = createNodeLookup(nodes);
323
+ const items = [];
324
+ const seen = /* @__PURE__ */ new Set();
325
+ for (const id of ids) {
326
+ const node = lookup.get(id);
327
+ if (!node || seen.has(id)) continue;
328
+ seen.add(id);
329
+ items.push({ node, rect: getNodeRect(node, lookup, options), locked: isLocked(node) });
330
+ }
331
+ return { lookup, items };
332
+ }
333
+ function toPositionUpdates(lookup, targets) {
334
+ const updates = [];
335
+ for (const [id, abs] of targets) {
336
+ const node = lookup.get(id);
337
+ let position = abs;
338
+ if (node.parentId && lookup.has(node.parentId)) {
339
+ const parent = lookup.get(node.parentId);
340
+ const parentAbs = targets.get(parent.id) ?? getAbsolutePosition(parent, lookup);
341
+ position = { x: abs.x - parentAbs.x, y: abs.y - parentAbs.y };
342
+ }
343
+ if (position.x !== node.position.x || position.y !== node.position.y) {
344
+ updates.push({ id, position });
345
+ }
346
+ }
347
+ return updates;
348
+ }
349
+ function alignNodes(nodes, ids, edge, options = {}) {
350
+ const { lookup, items } = collect(nodes, ids, options);
351
+ const fixed = typeof options.to === "object" ? options.to : null;
352
+ if (items.length === 0 || !fixed && items.length < 2) return [];
353
+ const bounds = fixed ?? boundsOf(items.map((i) => i.rect));
354
+ const targets = /* @__PURE__ */ new Map();
355
+ for (const { node, rect, locked } of items) {
356
+ if (locked) continue;
357
+ let { x, y } = rect;
358
+ switch (edge) {
359
+ case "left":
360
+ x = bounds.x;
361
+ break;
362
+ case "center":
363
+ x = bounds.x + bounds.width / 2 - rect.width / 2;
364
+ break;
365
+ case "right":
366
+ x = bounds.x + bounds.width - rect.width;
367
+ break;
368
+ case "top":
369
+ y = bounds.y;
370
+ break;
371
+ case "middle":
372
+ y = bounds.y + bounds.height / 2 - rect.height / 2;
373
+ break;
374
+ case "bottom":
375
+ y = bounds.y + bounds.height - rect.height;
376
+ break;
377
+ }
378
+ targets.set(node.id, { x, y });
379
+ }
380
+ return toPositionUpdates(lookup, targets);
381
+ }
382
+ function distributeAxis(items, horizontal, mode, gapOverride, targets) {
383
+ const start = (r) => horizontal ? r.x : r.y;
384
+ const size = (r) => horizontal ? r.width : r.height;
385
+ const sorted = [...items].sort(
386
+ (a, b) => start(a.rect) + size(a.rect) / 2 - (start(b.rect) + size(b.rect) / 2)
387
+ );
388
+ const first = sorted[0].rect;
389
+ const last = sorted[sorted.length - 1].rect;
390
+ const n = sorted.length;
391
+ const set = (item, value) => {
392
+ if (item.locked) return;
393
+ const current = targets.get(item.node.id) ?? { x: item.rect.x, y: item.rect.y };
394
+ targets.set(item.node.id, horizontal ? { ...current, x: value } : { ...current, y: value });
395
+ };
396
+ if (mode === "centres") {
397
+ const c0 = start(first) + size(first) / 2;
398
+ const c1 = start(last) + size(last) / 2;
399
+ const step = (c1 - c0) / (n - 1);
400
+ sorted.forEach((item, i) => set(item, c0 + step * i - size(item.rect) / 2));
401
+ return;
402
+ }
403
+ const span = start(last) + size(last) - start(first);
404
+ const total = sorted.reduce((sum, item) => sum + size(item.rect), 0);
405
+ const gap = gapOverride ?? (span - total) / (n - 1);
406
+ let cursor = start(first);
407
+ for (const item of sorted) {
408
+ set(item, cursor);
409
+ cursor += size(item.rect) + gap;
410
+ }
411
+ }
412
+ function distributeNodes(nodes, ids, axis, mode = "gaps", options = {}) {
413
+ const { lookup, items } = collect(nodes, ids, options);
414
+ const minimum = options.gap !== void 0 && mode === "gaps" ? 2 : 3;
415
+ if (items.length < minimum) return [];
416
+ const targets = /* @__PURE__ */ new Map();
417
+ if (axis !== "vertical") distributeAxis(items, true, mode, options.gap, targets);
418
+ if (axis !== "horizontal") distributeAxis(items, false, mode, options.gap, targets);
419
+ return toPositionUpdates(lookup, targets);
420
+ }
421
+ function matchSize(nodes, ids, dimension, options = {}) {
422
+ const { items } = collect(nodes, ids, options);
423
+ if (items.length < 2) return [];
424
+ const reference = options.reference ?? "largest";
425
+ const pick = (dim) => {
426
+ if (typeof reference === "object") {
427
+ const item = items.find((i) => i.node.id === reference.id);
428
+ if (item) return item.rect[dim];
429
+ }
430
+ if (reference === "first") return items[0].rect[dim];
431
+ const values = items.map((i) => i.rect[dim]);
432
+ return reference === "smallest" ? Math.min(...values) : Math.max(...values);
433
+ };
434
+ const width = dimension !== "height" ? pick("width") : void 0;
435
+ const height = dimension !== "width" ? pick("height") : void 0;
436
+ const updates = [];
437
+ for (const { node, rect, locked } of items) {
438
+ if (locked) continue;
439
+ const update = { id: node.id };
440
+ if (width !== void 0 && width !== rect.width) update.width = width;
441
+ if (height !== void 0 && height !== rect.height) update.height = height;
442
+ if (update.width === void 0 && update.height === void 0) continue;
443
+ if (options.keepCentre) {
444
+ update.position = {
445
+ x: node.position.x - ((update.width ?? rect.width) - rect.width) / 2,
446
+ y: node.position.y - ((update.height ?? rect.height) - rect.height) / 2
447
+ };
448
+ }
449
+ updates.push(update);
450
+ }
451
+ return updates;
452
+ }
453
+ function reorderNodes(nodes, ids, operation, options = {}) {
454
+ const lookup = createNodeLookup(nodes);
455
+ const targetIds = new Set(
456
+ [...ids].filter((id) => lookup.has(id) && !isLocked(lookup.get(id)))
457
+ );
458
+ if (targetIds.size === 0) return [];
459
+ const groups = /* @__PURE__ */ new Map();
460
+ for (const node of nodes) {
461
+ const key = node.parentId ?? "";
462
+ if (!groups.has(key)) groups.set(key, []);
463
+ groups.get(key).push(node);
464
+ }
465
+ const updates = [];
466
+ for (const siblings of groups.values()) {
467
+ if (!siblings.some((n) => targetIds.has(n.id))) continue;
468
+ const order = siblings.map((node, index) => ({ node, index, z: typeof node.zIndex === "number" ? node.zIndex : 0 })).sort((a, b) => a.z - b.z || a.index - b.index).map((entry) => entry.node);
469
+ const isTarget = (n) => targetIds.has(n.id);
470
+ let next;
471
+ if (operation === "bringToFront") {
472
+ next = [...order.filter((n) => !isTarget(n)), ...order.filter(isTarget)];
473
+ } else if (operation === "sendToBack") {
474
+ next = [...order.filter(isTarget), ...order.filter((n) => !isTarget(n))];
475
+ } else {
476
+ next = [...order];
477
+ const forward = operation === "bringForward";
478
+ const rects = new Map(next.map((n) => [n.id, getNodeRect(n, lookup, options)]));
479
+ const intersects = (a, b) => a.x < b.x + b.width && a.x + a.width > b.x && a.y < b.y + b.height && a.y + a.height > b.y;
480
+ const indices = next.map((_, i) => i);
481
+ if (forward) indices.reverse();
482
+ for (const i of indices) {
483
+ const node = next[i];
484
+ if (!isTarget(node)) continue;
485
+ const step = forward ? 1 : -1;
486
+ let j = i + step;
487
+ let swapWith = -1;
488
+ let firstNonTarget = -1;
489
+ while (j >= 0 && j < next.length) {
490
+ if (!isTarget(next[j])) {
491
+ if (firstNonTarget === -1) firstNonTarget = j;
492
+ if (intersects(rects.get(node.id), rects.get(next[j].id))) {
493
+ swapWith = j;
494
+ break;
495
+ }
496
+ }
497
+ j += step;
498
+ }
499
+ if (swapWith === -1) swapWith = firstNonTarget;
500
+ if (swapWith === -1) continue;
501
+ next.splice(i, 1);
502
+ next.splice(swapWith, 0, node);
503
+ }
504
+ }
505
+ if (next.every((node, i) => node === order[i])) continue;
506
+ next.forEach((node, rank) => {
507
+ const current = typeof node.zIndex === "number" ? node.zIndex : 0;
508
+ if (current !== rank) updates.push({ id: node.id, zIndex: rank });
509
+ });
510
+ }
511
+ return updates;
512
+ }
513
+ function applyNodeUpdates(nodes, updates) {
514
+ if (updates.length === 0) return nodes;
515
+ const byId = /* @__PURE__ */ new Map();
516
+ for (const update of updates) {
517
+ byId.set(update.id, { ...byId.get(update.id), ...update });
518
+ }
519
+ return nodes.map((node) => {
520
+ const update = byId.get(node.id);
521
+ if (!update) return node;
522
+ const next = { ...node };
523
+ if (update.position) next.position = { ...update.position };
524
+ if (update.zIndex !== void 0) next.zIndex = update.zIndex;
525
+ if (update.width !== void 0 || update.height !== void 0) {
526
+ if (update.width !== void 0) next.width = update.width;
527
+ if (update.height !== void 0) next.height = update.height;
528
+ if (node.style) next.style = withStyleSize(node.style, update);
529
+ if (node.measured) {
530
+ next.measured = {
531
+ ...node.measured,
532
+ ...update.width !== void 0 ? { width: update.width } : {},
533
+ ...update.height !== void 0 ? { height: update.height } : {}
534
+ };
535
+ }
536
+ }
537
+ return next;
538
+ });
539
+ }
540
+ function invertNodeUpdates(nodes, updates) {
541
+ const lookup = createNodeLookup(nodes);
542
+ const inverse = [];
543
+ for (const update of updates) {
544
+ const node = lookup.get(update.id);
545
+ if (!node) continue;
546
+ const back = { id: node.id };
547
+ if (update.position) back.position = { ...node.position };
548
+ if (update.zIndex !== void 0) back.zIndex = typeof node.zIndex === "number" ? node.zIndex : 0;
549
+ if (update.width !== void 0 || update.height !== void 0) {
550
+ const size = getNodeSize(node);
551
+ if (update.width !== void 0) back.width = size.width;
552
+ if (update.height !== void 0) back.height = size.height;
553
+ }
554
+ inverse.push(back);
555
+ }
556
+ return inverse;
557
+ }
558
+
559
+ export {
560
+ DEFAULT_NODE_WIDTH,
561
+ DEFAULT_NODE_HEIGHT,
562
+ DEFAULT_PLACEMENT_GAP,
563
+ getCenteredNodePosition,
564
+ centerNodes,
565
+ getLayoutedNodes,
566
+ estimateNodeSize,
567
+ boundsOf,
568
+ centre,
569
+ overlaps,
570
+ flowSide,
571
+ ensureLayout,
572
+ placeBeside,
573
+ LOCKED_DATA_KEY,
574
+ LOCKED_PREVIOUS_FLAGS_KEY,
575
+ readStyleSize,
576
+ withStyleSize,
577
+ getNodeSize,
578
+ getAbsolutePosition,
579
+ createNodeLookup,
580
+ getNodeRect,
581
+ isLocked,
582
+ lockNodes,
583
+ unlockNodes,
584
+ toggleLockNodes,
585
+ alignNodes,
586
+ distributeNodes,
587
+ matchSize,
588
+ reorderNodes,
589
+ applyNodeUpdates,
590
+ invertNodeUpdates
591
+ };
@@ -3,7 +3,7 @@ import {
3
3
  TOOL_BRIDGE_PROTOCOL,
4
4
  ToolRegistry,
5
5
  createMcpServer
6
- } from "./chunk-RB62U6MS.js";
6
+ } from "./chunk-RFBFTLVN.js";
7
7
 
8
8
  // src/bridge/index.ts
9
9
  import { createServer } from "http";
@@ -3,7 +3,7 @@
3
3
 
4
4
 
5
5
 
6
- var _chunkHDK3T2QScjs = require('./chunk-HDK3T2QS.cjs');
6
+ var _chunkY4BWZB6Jcjs = require('./chunk-Y4BWZB6J.cjs');
7
7
 
8
8
  // src/bridge/index.ts
9
9
  var _http = require('http');
@@ -197,7 +197,7 @@ var SKILL_INSTRUCTIONS = "Each editor's tools are documented by an agent skill (
197
197
  async function createToolBridge(options) {
198
198
  const log = _nullishCoalesce(options.log, () => ( ((message) => process.stderr.write(`[${options.name}] ${message}
199
199
  `))));
200
- const port = _nullishCoalesce(options.port, () => ( _chunkHDK3T2QScjs.TOOL_BRIDGE_DEFAULT_PORT));
200
+ const port = _nullishCoalesce(options.port, () => ( _chunkY4BWZB6Jcjs.TOOL_BRIDGE_DEFAULT_PORT));
201
201
  const retryMs = _nullishCoalesce(options.retryMs, () => ( 2e3));
202
202
  const timeoutMs = _nullishCoalesce(options.timeoutMs, () => ( 6e4));
203
203
  const extraOrigins = new Set(_nullishCoalesce(options.origins, () => ( [])));
@@ -211,7 +211,7 @@ async function createToolBridge(options) {
211
211
  let closed = false;
212
212
  let retry;
213
213
  const send = (editor, message) => editor.socket.send(JSON.stringify(message));
214
- const registry = new (0, _chunkHDK3T2QScjs.ToolRegistry)();
214
+ const registry = new (0, _chunkY4BWZB6Jcjs.ToolRegistry)();
215
215
  const listeners = /* @__PURE__ */ new Set();
216
216
  const toolsChanged = () => listeners.forEach((l) => l());
217
217
  const describeEditors = () => [...editors.values()].map((e) => `${e.id} (${e.name})`).join(", ");
@@ -319,13 +319,13 @@ async function createToolBridge(options) {
319
319
  return;
320
320
  }
321
321
  if (message.t === "hello") {
322
- if (message.v !== _chunkHDK3T2QScjs.TOOL_BRIDGE_PROTOCOL) {
322
+ if (message.v !== _chunkY4BWZB6Jcjs.TOOL_BRIDGE_PROTOCOL) {
323
323
  log(`refused an editor speaking protocol v${String(message.v)}`);
324
- return ws.close(1008, `This bridge speaks protocol v${_chunkHDK3T2QScjs.TOOL_BRIDGE_PROTOCOL}; update @pixerate/editor.`);
324
+ return ws.close(1008, `This bridge speaks protocol v${_chunkY4BWZB6Jcjs.TOOL_BRIDGE_PROTOCOL}; update @pixerate/editor.`);
325
325
  }
326
326
  editor = { id: `e${++editorCounter}`, name: String(message.name || "Editor"), ...message.kind ? { kind: message.kind } : {}, socket: ws, tools: Array.isArray(message.tools) ? message.tools : [], activeAt: Date.now(), activeSeq: ++activity };
327
327
  editors.set(editor.id, editor);
328
- ws.send(JSON.stringify({ t: "welcome", v: _chunkHDK3T2QScjs.TOOL_BRIDGE_PROTOCOL, id: editor.id }));
328
+ ws.send(JSON.stringify({ t: "welcome", v: _chunkY4BWZB6Jcjs.TOOL_BRIDGE_PROTOCOL, id: editor.id }));
329
329
  log(`editor ${editor.id} connected: ${editor.name} (${editor.tools.length} tools)`);
330
330
  toolsChanged();
331
331
  } else if (message.t === "tools" && editor) {
@@ -381,7 +381,7 @@ async function createToolBridge(options) {
381
381
  `)]);
382
382
  const skills = options.skills === false ? {} : _nullishCoalesce(options.skills, () => ( loadSkillFiles()));
383
383
  const hasSkills = Object.keys(skills).length > 0;
384
- const mcp = _chunkHDK3T2QScjs.createMcpServer.call(void 0, registry, {
384
+ const mcp = _chunkY4BWZB6Jcjs.createMcpServer.call(void 0, registry, {
385
385
  name: options.name,
386
386
  version: _nullishCoalesce(options.version, () => ( "0.1.0")),
387
387
  instructions: [options.instructions, BRIDGE_INSTRUCTIONS, hasSkills ? SKILL_INSTRUCTIONS : ""].filter(Boolean).join("\n\n"),
@@ -1,4 +1,4 @@
1
- // src/animation/presence.ts
1
+ // src/tools/presence.ts
2
2
  var PRESENCE_COLORS = ["#7c3aed", "#db2777", "#0891b2", "#ea580c", "#16a34a"];
3
3
  var PresenceStore = class {
4
4
  state = { actors: [] };