@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
@@ -52,3 +52,4 @@ export { createCanvasTextShaper, createMetricTextShaper, createOutlineTextShaper
52
52
  export { loadOpenTypeFont, parseOpenTypeFont, type OpenTypeFont } from '../text/opentype.cjs';
53
53
  export type { TextFont, TextShaper } from '../text/types.cjs';
54
54
  export * from './effect-tools.cjs';
55
+ export * from './playback.cjs';
@@ -52,3 +52,4 @@ export { createCanvasTextShaper, createMetricTextShaper, createOutlineTextShaper
52
52
  export { loadOpenTypeFont, parseOpenTypeFont, type OpenTypeFont } from '../text/opentype.js';
53
53
  export type { TextFont, TextShaper } from '../text/types.js';
54
54
  export * from './effect-tools.js';
55
+ export * from './playback.js';
@@ -4169,8 +4169,8 @@ var Page = class {
4169
4169
  const fns = list.slice(1).map((s, k) => `<< /FunctionType 2 /Domain [0 1] /C0 ${rgb(list[k].c)} /C1 ${rgb(s.c)} /N 1 >>`);
4170
4170
  const fn = fns.length === 1 ? fns[0] : `<< /FunctionType 3 /Domain [${n(list[0].t)} ${n(list[list.length - 1].t)}] /Functions [${fns.join(" ")}] /Bounds [${list.slice(1, -1).map((s) => n(s.t)).join(" ")}] /Encode [${fns.map(() => "0 1").join(" ")}] >>`;
4171
4171
  const a = def.attrs;
4172
- const num = (k, d = 0) => a[k] === void 0 ? d : parseFloat(String(a[k]));
4173
- const coords = def.type === "linear" ? `/ShadingType 2 /Coords [${n(num("x1"))} ${n(num("y1"))} ${n(num("x2", 1))} ${n(num("y2"))}]` : `/ShadingType 3 /Coords [${n(num("fx", num("cx", 0.5)))} ${n(num("fy", num("cy", 0.5)))} ${n(num("fr"))} ${n(num("cx", 0.5))} ${n(num("cy", 0.5))} ${n(num("r", 0.5))}]`;
4172
+ const num2 = (k, d = 0) => a[k] === void 0 ? d : parseFloat(String(a[k]));
4173
+ const coords = def.type === "linear" ? `/ShadingType 2 /Coords [${n(num2("x1"))} ${n(num2("y1"))} ${n(num2("x2", 1))} ${n(num2("y2"))}]` : `/ShadingType 3 /Coords [${n(num2("fx", num2("cx", 0.5)))} ${n(num2("fy", num2("cy", 0.5)))} ${n(num2("fr"))} ${n(num2("cx", 0.5))} ${n(num2("cy", 0.5))} ${n(num2("r", 0.5))}]`;
4174
4174
  this.writer.shadingDicts.set(`${this.writer.pageIndex}:${name}`, `<< ${coords} /ColorSpace /DeviceRGB /Function ${fn} /Extend [true true] >>`);
4175
4175
  return name;
4176
4176
  }
@@ -4807,28 +4807,28 @@ function gradientOf(it, frame) {
4807
4807
  }
4808
4808
  function effectOf(it, frame, c, id) {
4809
4809
  const v = (k) => lottieValueAt(it[k], frame);
4810
- const num = (k, d = 0) => v(k)[0] ?? d;
4810
+ const num2 = (k, d = 0) => v(k)[0] ?? d;
4811
4811
  let raw = null;
4812
4812
  switch (it.ty) {
4813
4813
  case "tm":
4814
- raw = { type: "trim", params: { start: num("s"), end: num("e", 100), offset: num("o"), mode: Number(it.m) === 2 ? "individual" : "simultaneous" } };
4814
+ raw = { type: "trim", params: { start: num2("s"), end: num2("e", 100), offset: num2("o"), mode: Number(it.m) === 2 ? "individual" : "simultaneous" } };
4815
4815
  break;
4816
4816
  case "rd":
4817
- raw = { type: "roundCorners", params: { radius: num("r") } };
4817
+ raw = { type: "roundCorners", params: { radius: num2("r") } };
4818
4818
  break;
4819
4819
  case "pb":
4820
- raw = { type: "puckerBloat", params: { amount: num("a") } };
4820
+ raw = { type: "puckerBloat", params: { amount: num2("a") } };
4821
4821
  break;
4822
4822
  case "tw": {
4823
4823
  const [x = c.x, y = c.y] = v("c");
4824
- raw = { type: "twist", params: { angle: num("a"), center: { x: x - c.x, y: y - c.y } } };
4824
+ raw = { type: "twist", params: { angle: num2("a"), center: { x: x - c.x, y: y - c.y } } };
4825
4825
  break;
4826
4826
  }
4827
4827
  case "zz":
4828
- raw = { type: "zigzag", params: { size: num("s"), ridges: num("r"), smooth: num("pt", 1) === 2 } };
4828
+ raw = { type: "zigzag", params: { size: num2("s"), ridges: num2("r"), smooth: num2("pt", 1) === 2 } };
4829
4829
  break;
4830
4830
  case "op":
4831
- raw = { type: "offset", params: { amount: num("a"), join: ["miter", "round", "bevel"][Number(it.lj ?? 1) - 1] ?? "miter", miterLimit: num("ml", 4) || 4 } };
4831
+ raw = { type: "offset", params: { amount: num2("a"), join: ["miter", "round", "bevel"][Number(it.lj ?? 1) - 1] ?? "miter", miterLimit: num2("ml", 4) || 4 } };
4832
4832
  break;
4833
4833
  case "mm":
4834
4834
  raw = { type: "merge", params: { mode: MERGE[Number(it.mm ?? 1) - 1] ?? "merge" } };
@@ -4844,7 +4844,7 @@ function effectOf(it, frame, c, id) {
4844
4844
  const [sx, sy] = at("s", [100, 100]);
4845
4845
  raw = {
4846
4846
  type: "repeater",
4847
- params: { copies: num("c", 1), offset: num("o"), position: { x: px, y: py }, anchor: { x: ax - c.x, y: ay - c.y }, rotation: at("r", [0])[0], scale: { x: sx, y: sy }, startOpacity: at("so", [100])[0], endOpacity: at("eo", [100])[0], composite: Number(it.m) === 1 ? "above" : "below" }
4847
+ params: { copies: num2("c", 1), offset: num2("o"), position: { x: px, y: py }, anchor: { x: ax - c.x, y: ay - c.y }, rotation: at("r", [0])[0], scale: { x: sx, y: sy }, startOpacity: at("so", [100])[0], endOpacity: at("eo", [100])[0], composite: Number(it.m) === 1 ? "above" : "below" }
4848
4848
  };
4849
4849
  break;
4850
4850
  }
@@ -6456,11 +6456,11 @@ function segsToSubpath(segs, closed, types) {
6456
6456
  const last = anchors.pop();
6457
6457
  if (last.in) anchors[0].in = last.in;
6458
6458
  }
6459
- const typed = anchors.map((a) => {
6459
+ const typed2 = anchors.map((a) => {
6460
6460
  const type = types.get(`${a.x},${a.y}`);
6461
6461
  return tidy(type ? { ...a, type } : a, !!type);
6462
6462
  });
6463
- return { anchors: typed, closed };
6463
+ return { anchors: typed2, closed };
6464
6464
  }
6465
6465
  var isZeroSeg = (s) => s.c.every((p) => Math.abs(p[0] - s.c[0][0]) < 1e-9 && Math.abs(p[1] - s.c[0][1]) < 1e-9);
6466
6466
  function mergePair(a, b, tol) {
@@ -8497,10 +8497,10 @@ var VectorEditorController = class _VectorEditorController {
8497
8497
  if (node?.type !== "path") return { ok: false, message: "There is no such path." };
8498
8498
  const g = vectorStrokeWidthGeometry(node);
8499
8499
  if (!g || !(g.unit > 0)) return { ok: false, message: "The path has no visible stroke." };
8500
- const num = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
8501
- const t = num(values.t);
8502
- const left = num(values.left);
8503
- const right = num(values.right);
8500
+ const num2 = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
8501
+ const t = num2(values.t);
8502
+ const left = num2(values.left);
8503
+ const right = num2(values.right);
8504
8504
  const scale = (w) => w === void 0 ? void 0 : Math.max(0, w) / g.unit;
8505
8505
  const profile = sanitizeWidthProfile(node.strokeWidthProfile);
8506
8506
  let points;
@@ -10768,18 +10768,18 @@ var VectorEditorController = class _VectorEditorController {
10768
10768
  const sp = pen && node ? node.subpaths[pen.subpath] : void 0;
10769
10769
  if (!pen || !node || !sp?.anchors.length) return { ok: false, message: "Start a path with the pen first: typed entry places its next point." };
10770
10770
  const last = getAnchorWorld(this.doc, { nodeId: pen.nodeId, subpath: pen.subpath, index: sp.anchors.length - 1 });
10771
- const num = "(-?(?:\\d+\\.?\\d*|\\.\\d+))";
10771
+ const num2 = "(-?(?:\\d+\\.?\\d*|\\.\\d+))";
10772
10772
  const t = text.replace(/\s+/g, "");
10773
10773
  let target = null;
10774
10774
  let m;
10775
- if (m = t.match(new RegExp(`^@${num},${num}$`))) target = { x: last.x + parseFloat(m[1]), y: last.y + parseFloat(m[2]) };
10776
- else if (m = t.match(new RegExp(`^${num},${num}$`))) target = { x: parseFloat(m[1]), y: parseFloat(m[2]) };
10777
- else if (m = t.match(new RegExp(`^${num}<${num}$`))) {
10775
+ if (m = t.match(new RegExp(`^@${num2},${num2}$`))) target = { x: last.x + parseFloat(m[1]), y: last.y + parseFloat(m[2]) };
10776
+ else if (m = t.match(new RegExp(`^${num2},${num2}$`))) target = { x: parseFloat(m[1]), y: parseFloat(m[2]) };
10777
+ else if (m = t.match(new RegExp(`^${num2}<${num2}$`))) {
10778
10778
  const base = this.snap.polarRelative ? (this.penAngles().angles?.[0] ?? this.snap.angles[0] ?? 0) - (this.snap.angles[0] ?? 0) : 0;
10779
10779
  const dir = vectorAngleDirection(parseFloat(m[2]) + base);
10780
10780
  const len2 = parseFloat(m[1]);
10781
10781
  target = { x: last.x + dir.x * len2, y: last.y + dir.y * len2 };
10782
- } else if (m = t.match(new RegExp(`^${num}$`))) {
10782
+ } else if (m = t.match(new RegExp(`^${num2}$`))) {
10783
10783
  const toward = this.pointer;
10784
10784
  const d = toward ? Math.hypot(toward.x - last.x, toward.y - last.y) : 0;
10785
10785
  if (!toward || d < 1e-9) return { ok: false, message: "Point the pointer the way the distance should go (or type length<angle)." };
@@ -14974,6 +14974,120 @@ function pencilPressureProfile(points, pressures) {
14974
14974
  return pressureWidthProfile(s.map((d, i) => ({ t: d / total, pressure: pressures[i] })));
14975
14975
  }
14976
14976
 
14977
+ // src/vector/playback.ts
14978
+ var VECTOR_PLAYBACK_KIND = "vector";
14979
+ function createVectorPlaybackLocator(controller) {
14980
+ return (ref) => {
14981
+ if (ref.kind !== VECTOR_PLAYBACK_KIND && ref.kind !== "item") return null;
14982
+ const node = controller.getNode(ref.id);
14983
+ if (!node) return null;
14984
+ const b = vectorNodeBounds(node, vectorParentMatrix(controller.getState().document.nodes, ref.id) ?? IDENTITY_MATRIX);
14985
+ if (!b) return null;
14986
+ const w = Math.max(b.width, 4);
14987
+ const h = Math.max(b.height, 4);
14988
+ return { x: b.x - (w - b.width) / 2, y: b.y - (h - b.height) / 2, width: w, height: h };
14989
+ };
14990
+ }
14991
+ var num = (v) => typeof v === "number" && Number.isFinite(v);
14992
+ var dragOut = (from, to) => [
14993
+ { type: "move", to: from },
14994
+ { type: "press" },
14995
+ { type: "drag", to },
14996
+ { type: "release" }
14997
+ ];
14998
+ var click = (at) => [{ type: "move", to: at }, { type: "press" }, { type: "release" }];
14999
+ var typed = (text) => {
15000
+ if (typeof text !== "string" || !text.trim()) return [];
15001
+ const line2 = text.replace(/\s+/g, " ").trim();
15002
+ return [{ type: "type", text: line2.length > 60 ? `${line2.slice(0, 59)}\u2026` : line2 }];
15003
+ };
15004
+ function pathPoints(d, max = 24) {
15005
+ let anchors;
15006
+ try {
15007
+ anchors = bezierToSubpaths(d).flatMap((s) => s.anchors.map((a) => ({ x: a.x, y: a.y })));
15008
+ } catch {
15009
+ return [];
15010
+ }
15011
+ if (anchors.length <= max) return anchors;
15012
+ const step = (anchors.length - 1) / (max - 1);
15013
+ return Array.from({ length: max }, (_, i) => anchors[Math.round(i * step)]);
15014
+ }
15015
+ function trace(points) {
15016
+ if (!points.length) return [];
15017
+ const [first, ...rest] = points;
15018
+ const total = rest.reduce((sum, p, i) => sum + Math.hypot(p.x - (i ? rest[i - 1] : first).x, p.y - (i ? rest[i - 1] : first).y), 0);
15019
+ const ms = (p, i) => {
15020
+ const prev = i ? rest[i - 1] : first;
15021
+ return total > 0 ? Math.max(16, 900 * Math.hypot(p.x - prev.x, p.y - prev.y) / total) : 16;
15022
+ };
15023
+ return [{ type: "move", to: first }, { type: "press" }, ...rest.map((p, i) => ({ type: "drag", to: p, ms: ms(p, i) })), { type: "release" }];
15024
+ }
15025
+ var PLANS = {
15026
+ addShape: (p) => {
15027
+ if (![p.x, p.y, p.width, p.height].every(num)) return null;
15028
+ return dragOut({ x: p.x, y: p.y }, { x: p.x + p.width, y: p.y + p.height });
15029
+ },
15030
+ addPath: (p) => typeof p.d === "string" ? trace(pathPoints(p.d)) : null,
15031
+ addText: (p) => {
15032
+ if (typeof p.path === "string") {
15033
+ const points = pathPoints(p.path, 2);
15034
+ return points.length ? [...click(points[0]), ...typed(p.text)] : null;
15035
+ }
15036
+ const x = num(p.x) ? p.x : 0;
15037
+ const y = num(p.y) ? p.y : 0;
15038
+ const start = num(p.width) && num(p.height) ? dragOut({ x, y }, { x: x + p.width, y: y + p.height }) : click({ x, y });
15039
+ return [...start, ...typed(p.text)];
15040
+ },
15041
+ placeSymbol: (p, controller) => {
15042
+ const doc = controller.getState().document;
15043
+ const t = Array.isArray(p.transform) && p.transform.length === 6 ? p.transform : null;
15044
+ const at = t ? { x: t[4], y: t[5] } : { x: num(p.x) ? p.x : doc.width / 2, y: num(p.y) ? p.y : doc.height / 2 };
15045
+ return click(at);
15046
+ },
15047
+ setTextStyle: (p, controller) => {
15048
+ const node = controller.getNode(p.id);
15049
+ if (node?.type !== "text") return null;
15050
+ const rect = createVectorPlaybackLocator(controller)({ kind: VECTOR_PLAYBACK_KIND, id: p.id });
15051
+ if (!rect) return null;
15052
+ const length = Math.max(1, node.text.length);
15053
+ const start = Math.min(length, Math.max(0, num(p.start) ? p.start : 0));
15054
+ const end = Math.min(length, Math.max(start, num(p.end) ? p.end : length));
15055
+ const y = rect.y + rect.height / 2;
15056
+ return [
15057
+ { type: "focus", rect },
15058
+ ...dragOut({ x: rect.x + rect.width * start / length, y }, { x: rect.x + rect.width * end / length, y })
15059
+ ];
15060
+ },
15061
+ setMeshPoint: (p, controller) => {
15062
+ const shading = controller.getShading(p.id);
15063
+ const point = shading?.points[p.index];
15064
+ if (!point) return null;
15065
+ return num(p.x) && num(p.y) ? dragOut(point, { x: p.x, y: p.y }) : click(point);
15066
+ }
15067
+ };
15068
+ var placeImage = (p, controller) => {
15069
+ const doc = controller.getState().document;
15070
+ const x = num(p.x) ? p.x : 0;
15071
+ const y = num(p.y) ? p.y : 0;
15072
+ const w = num(p.width) ? p.width : doc.width - x;
15073
+ return dragOut({ x, y }, { x: x + w, y: y + w });
15074
+ };
15075
+ function withVectorPlayback(controller, prefix, commands) {
15076
+ return commands.map((spec) => {
15077
+ if (!spec.id.startsWith(prefix)) return spec;
15078
+ const name = spec.id.slice(prefix.length);
15079
+ const props = spec.params?.properties ?? {};
15080
+ const planner = PLANS[name] ?? (/Image$/.test(name) && "x" in props && "width" in props ? placeImage : void 0);
15081
+ const surface = spec.playbackSurface ?? spec.scope ?? "vector";
15082
+ if (!planner || spec.plan) return { ...spec, playbackSurface: surface };
15083
+ return {
15084
+ ...spec,
15085
+ playbackSurface: surface,
15086
+ plan: (_ctx, params) => planner(params ?? {}, controller)
15087
+ };
15088
+ });
15089
+ }
15090
+
14977
15091
  // src/vector/commands.ts
14978
15092
  var EFFECT_PARAMS_DESCRIPTION = "Effect parameters (missing ones keep their value or default). trim: start, end (percent), offset (degrees), mode simultaneous|individual. roundCorners: radius. puckerBloat: amount (\u2212100\u2026100). twist: angle, center {x, y} (offset from the middle). zigzag: size, ridges, smooth. wiggle: size, detail, smooth, speed, correlation, phase, seed, time (seconds). offset: amount, join miter|round|bevel, miterLimit, copies, copyOffset. merge: mode merge|add|subtract|intersect|exclude. repeater: copies (fractions fade the last), offset, position {x, y}, anchor {x, y} (offset from the middle), rotation, scale {x, y} (percent), startOpacity, endOpacity (percent), composite below|above. contour: direction inside|outside|center, steps, offset, fillColor / strokeColor (end colours, null keeps the colour), acceleration (\u2212100\u2026100), join. blockShadow: angle (degrees, 0 right, 90 down), distance, color (null: the shape fill colour), opacity, endOpacity (percent; lower than opacity fades it into a long shadow). extrude: mode parallel|perspective, angle (degrees) and depth (units) for parallel, vanishingPoint {x, y} (offset from the middle) and toward (percent of the way there) for perspective, color (null: the shape fill colour), lightAngle (degrees the light comes from, 0 right, -90 above), shading (percent). bevel: width (units), lightAngle (degrees), shading (percent), color (null: the shape fill colour). envelope: style (a warp preset: arc|arcLower|arcUpper|arch|bulge|shellLower|shellUpper|flag|wave|fish|rise|fisheye|inflate|squeeze|twist, or mesh), bend, horizontal, vertical (percent, \u2212100\u2026100), orientation horizontal|vertical, rows, columns, mesh { us, vs, points } (node space; prefer vector.envelope).";
14979
15093
  var fail = (message) => {
@@ -17275,7 +17389,7 @@ function createVectorCommands(controller, options = {}) {
17275
17389
  }
17276
17390
  }
17277
17391
  ];
17278
- return [
17392
+ return withVectorPlayback(controller, prefix, [
17279
17393
  {
17280
17394
  ...base,
17281
17395
  id: `${prefix}list`,
@@ -18186,7 +18300,7 @@ function createVectorCommands(controller, options = {}) {
18186
18300
  return { nodes: controller.getState().document.nodes.length, selection: controller.getState().selection };
18187
18301
  }
18188
18302
  }
18189
- ];
18303
+ ]);
18190
18304
  }
18191
18305
 
18192
18306
  // src/vector/generate.ts
@@ -18680,6 +18794,7 @@ export {
18680
18794
  VECTOR_PATH_TEXT_BASELINES,
18681
18795
  VECTOR_PENCIL_STRING_PX,
18682
18796
  VECTOR_PERSPECTIVE_PLANES,
18797
+ VECTOR_PLAYBACK_KIND,
18683
18798
  VECTOR_PRESSURE_NOMINAL,
18684
18799
  VECTOR_ROTATE_HANDLE_OFFSET,
18685
18800
  VECTOR_SPATIAL_MIN,
@@ -18768,6 +18883,7 @@ export {
18768
18883
  createVectorGenerationToolProvider,
18769
18884
  createVectorMeshGradient,
18770
18885
  createVectorMorph,
18886
+ createVectorPlaybackLocator,
18771
18887
  cssFontFamily,
18772
18888
  curvatureMoveAnchor,
18773
18889
  cutSubpathAt,
@@ -19283,6 +19399,7 @@ export {
19283
19399
  withVectorAnchorIds,
19284
19400
  withVectorDocumentAnchorIds,
19285
19401
  withVectorFills,
19402
+ withVectorPlayback,
19286
19403
  withVectorStrokes,
19287
19404
  withVectorTargetPaint,
19288
19405
  zigzagPaths
@@ -0,0 +1,13 @@
1
+ import type { CommandSpec } from '../commands/registry.cjs';
2
+ import type { PlaybackRect, PlaybackRef } from '../tools/playback.cjs';
3
+ import type { VectorEditorController } from './controller.cjs';
4
+ /** The playback kind of a vector node (`{ kind: 'vector', id }`, as in `data-vector-id`). */
5
+ export declare const VECTOR_PLAYBACK_KIND = "vector";
6
+ /** Finds `{ kind: 'vector', id }` (a node's document-space bounds) for agent playback. */
7
+ export declare function createVectorPlaybackLocator(controller: Pick<VectorEditorController, 'getState' | 'getNode'>): (ref: PlaybackRef) => PlaybackRect | null;
8
+ /**
9
+ * Adds agent playback plans to vector commands whose effect has a place but no ids yet (shapes,
10
+ * paths, text, symbols, stylized images) and to edits of a part of a node (a text range, a mesh
11
+ * point). Other commands play from the ids in their params.
12
+ */
13
+ export declare function withVectorPlayback(controller: VectorEditorController, prefix: string, commands: CommandSpec[]): CommandSpec[];
@@ -0,0 +1,13 @@
1
+ import type { CommandSpec } from '../commands/registry.js';
2
+ import type { PlaybackRect, PlaybackRef } from '../tools/playback.js';
3
+ import type { VectorEditorController } from './controller.js';
4
+ /** The playback kind of a vector node (`{ kind: 'vector', id }`, as in `data-vector-id`). */
5
+ export declare const VECTOR_PLAYBACK_KIND = "vector";
6
+ /** Finds `{ kind: 'vector', id }` (a node's document-space bounds) for agent playback. */
7
+ export declare function createVectorPlaybackLocator(controller: Pick<VectorEditorController, 'getState' | 'getNode'>): (ref: PlaybackRef) => PlaybackRect | null;
8
+ /**
9
+ * Adds agent playback plans to vector commands whose effect has a place but no ids yet (shapes,
10
+ * paths, text, symbols, stylized images) and to edits of a part of a node (a text range, a mesh
11
+ * point). Other commands play from the ids in their params.
12
+ */
13
+ export declare function withVectorPlayback(controller: VectorEditorController, prefix: string, commands: CommandSpec[]): CommandSpec[];