@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
@@ -149,7 +149,7 @@ import {
149
149
  createInstantPresenceClock,
150
150
  realPresenceClock,
151
151
  tweenPresenceCursor
152
- } from "../chunk-BSGPXPTW.js";
152
+ } from "../chunk-PEEP2YR7.js";
153
153
  import {
154
154
  readZip
155
155
  } from "../chunk-CIQIM2IP.js";
@@ -925,6 +925,242 @@ function chartHitTest(scene, point, measure = (t, s, w = 400) => t.length * s *
925
925
  return point.x >= 0 && point.y >= 0 && point.x <= scene.width && point.y <= scene.height ? { kind: "chart" } : null;
926
926
  }
927
927
 
928
+ // src/chart/presence.ts
929
+ var ChartPresence = class extends PresenceStore {
930
+ };
931
+ var CHART_PRESENCE_COLORS = PRESENCE_COLORS;
932
+ var realChartClock = realPresenceClock;
933
+ var createInstantChartClock = createInstantPresenceClock;
934
+ function chartPointAnchor(scene, seriesId, index) {
935
+ const mark = scene.marks.find((m) => (m.role === "bar" || m.role === "point") && m.target?.kind === "point" && m.target.seriesId === seriesId && m.target.index === index);
936
+ if (!mark) return null;
937
+ if (mark.kind === "rect") return { x: mark.x + mark.width / 2, y: mark.y };
938
+ if (mark.kind === "circle") return { x: mark.cx, y: mark.cy };
939
+ return null;
940
+ }
941
+ var anchorFor = (scene, role) => {
942
+ const m = scene.marks.find((x) => x.role === role);
943
+ if (!m) return null;
944
+ if (m.kind === "text") return { x: m.x, y: m.y };
945
+ if (m.kind === "rect") return { x: m.x + m.width / 2, y: m.y + m.height / 2 };
946
+ if (m.kind === "line") return { x: (m.x1 + m.x2) / 2, y: (m.y1 + m.y2) / 2 };
947
+ return null;
948
+ };
949
+ function planChartCommand(spec, scene, command, speed = 1) {
950
+ const ms = (n) => Math.round(n / Math.max(0.1, speed));
951
+ const steps = [];
952
+ const note = "note" in command && command.note ? command.note : void 0;
953
+ if (note) steps.push({ type: "say", text: note, ms: ms(500) });
954
+ switch (command.kind) {
955
+ case "say":
956
+ steps.push({ type: "say", text: command.text, ms: ms(900) });
957
+ return steps;
958
+ case "setValue": {
959
+ const at = chartPointAnchor(scene, command.seriesId, command.index);
960
+ const series = spec.series.find((s) => s.id === command.seriesId);
961
+ const from = series?.values[command.index] ?? 0;
962
+ const scale = series?.axis === "secondary" ? scene.valueScale2 ?? scene.valueScale : scene.valueScale;
963
+ if (at && scale) {
964
+ const px = linearMap(command.value, scale.domain, scale.range);
965
+ const to = scale.axis === "y" ? { x: at.x, y: px } : { x: px, y: at.y };
966
+ steps.push({ type: "move", to: at, ms: ms(380) }, { type: "press" }, { type: "drag", to, ms: ms(520), from, value: command.value }, { type: "release" });
967
+ } else steps.push({ type: "drag", to: { x: scene.plot.x + scene.plot.width / 2, y: scene.plot.y + scene.plot.height / 2 }, ms: ms(200), from, value: command.value });
968
+ steps.push({ type: "commit" });
969
+ return steps;
970
+ }
971
+ case "select": {
972
+ const sel = command.selection;
973
+ let to = null;
974
+ if (sel?.kind === "point") to = chartPointAnchor(scene, sel.seriesId, sel.index);
975
+ else if (sel) {
976
+ const m = scene.marks.find((x) => x.target && JSON.stringify(x.target) === JSON.stringify(sel));
977
+ if (m?.kind === "text") to = { x: m.x, y: m.y };
978
+ else if (m?.kind === "rect") to = { x: m.x + m.width / 2, y: m.y + m.height / 2 };
979
+ else if (m?.kind === "circle") to = { x: m.cx, y: m.cy };
980
+ }
981
+ if (to) steps.push({ type: "move", to, ms: ms(380) }, { type: "press" }, { type: "release" });
982
+ steps.push({ type: "commit" });
983
+ return steps;
984
+ }
985
+ case "update": {
986
+ const keys = Object.keys(command.patch);
987
+ const role = keys.includes("title") ? "title" : keys.includes("legend") ? "legend-label" : keys.some((k) => k === "xAxis") ? "axis" : keys.some((k) => k === "yAxis" || k === "yAxis2") ? "tick-label" : null;
988
+ const to = role && anchorFor(scene, role) || { x: scene.plot.x + scene.plot.width / 2, y: scene.plot.y + 12 };
989
+ steps.push({ type: "move", to, ms: ms(380) }, { type: "press" }, { type: "release" }, { type: "commit" });
990
+ return steps;
991
+ }
992
+ }
993
+ }
994
+ var ChartDirector = class {
995
+ constructor(controller, presence, options) {
996
+ this.controller = controller;
997
+ this.presence = presence;
998
+ this.options = options;
999
+ this.clock = options.clock ?? realChartClock;
1000
+ this.queue = new PerformanceQueue(presence, {
1001
+ clock: this.clock,
1002
+ linger: options.linger,
1003
+ beginGesture: (key) => controller.beginGesture(key),
1004
+ endGesture: () => controller.endGesture()
1005
+ });
1006
+ }
1007
+ controller;
1008
+ presence;
1009
+ options;
1010
+ clock;
1011
+ queue;
1012
+ /** Acts out `commands` in order for `actor`. Resolves with one result per command. */
1013
+ perform(actor, commands) {
1014
+ return this.queue.run(actor, async () => {
1015
+ const results = [];
1016
+ for (const command of commands) results.push(await this.performOne(actor.id, command));
1017
+ return results;
1018
+ });
1019
+ }
1020
+ stale(command) {
1021
+ const spec = this.controller.getSpec();
1022
+ if (command.kind === "setValue") {
1023
+ const s = spec.series.find((x) => x.id === command.seriesId);
1024
+ if (!s) return `The series "${command.seriesId}" no longer exists.`;
1025
+ if (command.index < 0 || command.index >= spec.categories.length) return `Category ${command.index} no longer exists.`;
1026
+ if (!Number.isFinite(command.value)) return "The value must be a finite number.";
1027
+ }
1028
+ return null;
1029
+ }
1030
+ move(id, from, to, ms, each) {
1031
+ return tweenPresenceCursor(this.presence, this.clock, id, from, to, ms, each);
1032
+ }
1033
+ async performOne(id, command) {
1034
+ const stale = this.stale(command);
1035
+ if (stale) return { ok: false, command, message: stale };
1036
+ const steps = planChartCommand(this.controller.getSpec(), this.options.scene(), command, this.options.speed);
1037
+ const where = () => {
1038
+ const a = this.presence.getState().actors.find((x) => x.id === id);
1039
+ return { x: a.x, y: a.y };
1040
+ };
1041
+ {
1042
+ for (const step of steps) {
1043
+ switch (step.type) {
1044
+ case "say":
1045
+ this.presence.set({ id, message: step.text });
1046
+ await this.clock.wait(step.ms);
1047
+ break;
1048
+ case "move":
1049
+ await this.move(id, where(), step.to, step.ms);
1050
+ break;
1051
+ case "press":
1052
+ this.presence.set({ id, pressed: true });
1053
+ break;
1054
+ case "release":
1055
+ this.presence.set({ id, pressed: false });
1056
+ break;
1057
+ case "drag": {
1058
+ if (command.kind !== "setValue") break;
1059
+ const stale2 = this.stale(command);
1060
+ if (stale2) return { ok: false, command, message: stale2 };
1061
+ const label = `${this.presence.getState().actors.find((a) => a.id === id)?.name ?? "Agent"}: edit value`;
1062
+ await this.move(id, where(), step.to, step.ms, (e) => {
1063
+ const v = step.from + (step.value - step.from) * e;
1064
+ this.controller.setValue(command.seriesId, command.index, Number(v.toPrecision(12)), { label });
1065
+ });
1066
+ this.controller.setValue(command.seriesId, command.index, step.value, { label });
1067
+ break;
1068
+ }
1069
+ case "commit": {
1070
+ if (command.kind === "select") this.controller.select(command.selection);
1071
+ else if (command.kind === "update") {
1072
+ const result = this.controller.update(command.patch, command.label ? { label: command.label } : {});
1073
+ if (!result.ok) return { ok: false, command, message: result.message };
1074
+ }
1075
+ break;
1076
+ }
1077
+ }
1078
+ }
1079
+ return { ok: true, command };
1080
+ }
1081
+ }
1082
+ };
1083
+
1084
+ // src/chart/playback.ts
1085
+ var click = (ref) => [{ type: "target", ref }, { type: "press" }, { type: "release" }];
1086
+ var control = (id) => ({ kind: "control", id });
1087
+ function chartPointRef(series, category) {
1088
+ return { kind: "point", id: JSON.stringify([series, category]) };
1089
+ }
1090
+ function locateChartPoint(spec, scene, ref) {
1091
+ if (ref.kind !== "point") return null;
1092
+ let parsed;
1093
+ try {
1094
+ parsed = JSON.parse(ref.id);
1095
+ } catch {
1096
+ return null;
1097
+ }
1098
+ if (!Array.isArray(parsed)) return null;
1099
+ const [seriesRef, categoryRef] = parsed;
1100
+ const series = spec.series.find((s) => s.id === seriesRef) ?? spec.series.find((s) => s.name === seriesRef);
1101
+ const index = typeof categoryRef === "number" ? categoryRef : spec.categories.findIndex((c) => String(c) === String(categoryRef));
1102
+ if (!series || index < 0) return null;
1103
+ const at = chartPointAnchor(scene, series.id, index);
1104
+ return at ? { x: at.x - 6, y: at.y - 6, width: 12, height: 12 } : null;
1105
+ }
1106
+ function planChartToolPlayback(tool, args) {
1107
+ switch (tool) {
1108
+ case "update_chart": {
1109
+ const steps = [];
1110
+ for (const key of Object.keys(args).slice(0, 4)) {
1111
+ steps.push(...click(key === "type" && typeof args.type === "string" ? { kind: "chart-type", id: args.type } : control(key)));
1112
+ }
1113
+ return steps.length ? steps : null;
1114
+ }
1115
+ case "apply_chart_preset":
1116
+ return [...args.layout ? click(control("preset.layout")) : [], ...args.colors ? click(control("preset.colors")) : []];
1117
+ case "set_chart_values": {
1118
+ const cells = Array.isArray(args.cells) ? args.cells : [];
1119
+ const steps = cells.slice(0, 6).filter((c) => typeof c.series === "string" && (typeof c.category === "string" || typeof c.category === "number")).flatMap((c) => click(chartPointRef(c.series, c.category)));
1120
+ return steps.length ? steps : null;
1121
+ }
1122
+ case "select_chart_element":
1123
+ if (args.kind === "point" && typeof args.series === "string" && args.category !== void 0) return click(chartPointRef(args.series, args.category));
1124
+ return typeof args.kind === "string" && args.kind !== "none" ? click(control(args.kind)) : null;
1125
+ case "set_chart_data":
1126
+ case "import_chart_csv":
1127
+ case "import_chart_mermaid":
1128
+ case "import_chart_ooxml":
1129
+ case "add_chart_series":
1130
+ case "update_chart_series":
1131
+ case "remove_chart_series":
1132
+ case "add_chart_category":
1133
+ case "remove_chart_category":
1134
+ case "transpose_chart":
1135
+ return [{ type: "target", ref: { kind: "grid", id: "data" } }];
1136
+ case "add_chart_trendline":
1137
+ case "remove_chart_trendline":
1138
+ return click(control("trendline.add"));
1139
+ case "render_chart":
1140
+ return [{ type: "target", ref: { kind: "action", id: "download-png" } }];
1141
+ case "undo_chart":
1142
+ return click({ kind: "action", id: "undo" });
1143
+ case "redo_chart":
1144
+ return click({ kind: "action", id: "redo" });
1145
+ default:
1146
+ return null;
1147
+ }
1148
+ }
1149
+ var DIRECTED = /* @__PURE__ */ new Set(["set_chart_values", "select_chart_element"]);
1150
+ function withChartPlayback(provider, options) {
1151
+ const plan = (tool) => {
1152
+ if (tool.plan || tool.playback !== void 0) return tool;
1153
+ if (options.director && DIRECTED.has(tool.name)) return { ...tool, playback: "self" };
1154
+ return { ...tool, plan: (args) => planChartToolPlayback(tool.name, args) };
1155
+ };
1156
+ const tools = provider.tools;
1157
+ return {
1158
+ ...provider,
1159
+ playbackSurface: provider.playbackSurface ?? options.surface ?? "chart",
1160
+ tools: typeof tools === "function" ? () => tools().map(plan) : tools.map(plan)
1161
+ };
1162
+ }
1163
+
928
1164
  // src/chart/tools.ts
929
1165
  var RENDER_CHART_INSTRUCTIONS = 'Draws the chart without changing it so you can check how it looks: SVG text by default, or a PNG image with format "png" when the host can rasterize. Returns the layout warnings (hidden or thinned labels, dropped markers) alongside. An image costs tokens, so render after a batch of edits rather than after each one, and use it to check what text cannot tell you: overlapping or cut-off labels, a crowded legend, unreadable colours, whether the chart reads at the given size. For values, ids and warnings alone, get_chart is cheaper.';
930
1166
  var READ = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false };
@@ -1488,7 +1724,7 @@ function createChartToolProvider(controller, options = {}) {
1488
1724
  };
1489
1725
  const reads = [getChart, renderChart, checkColors, recommend, getTrendline, listPresets, exportMermaid, exportOoxml];
1490
1726
  const writes = [updateChart, setData, setValues, addSeries, updateSeries, removeSeries, addCategory, removeCategory, importCsv, importMermaid, addTrendline, removeTrendline, applyPreset, transpose, select, drag, key, undo, redo];
1491
- return {
1727
+ return withChartPlayback({
1492
1728
  id: options.id ?? "chart",
1493
1729
  namespace: options.namespace,
1494
1730
  title: "Chart editor",
@@ -1508,7 +1744,7 @@ function createChartToolProvider(controller, options = {}) {
1508
1744
  }
1509
1745
  });
1510
1746
  }
1511
- };
1747
+ }, { director: !!options.director });
1512
1748
  }
1513
1749
 
1514
1750
  // src/chart/stream.ts
@@ -1643,162 +1879,6 @@ function blendChartScenes(from, to, t) {
1643
1879
  }
1644
1880
  var easeChartTween = (t) => 1 - Math.pow(1 - Math.max(0, Math.min(1, t)), 3);
1645
1881
 
1646
- // src/chart/presence.ts
1647
- var ChartPresence = class extends PresenceStore {
1648
- };
1649
- var CHART_PRESENCE_COLORS = PRESENCE_COLORS;
1650
- var realChartClock = realPresenceClock;
1651
- var createInstantChartClock = createInstantPresenceClock;
1652
- function chartPointAnchor(scene, seriesId, index) {
1653
- const mark = scene.marks.find((m) => (m.role === "bar" || m.role === "point") && m.target?.kind === "point" && m.target.seriesId === seriesId && m.target.index === index);
1654
- if (!mark) return null;
1655
- if (mark.kind === "rect") return { x: mark.x + mark.width / 2, y: mark.y };
1656
- if (mark.kind === "circle") return { x: mark.cx, y: mark.cy };
1657
- return null;
1658
- }
1659
- var anchorFor = (scene, role) => {
1660
- const m = scene.marks.find((x) => x.role === role);
1661
- if (!m) return null;
1662
- if (m.kind === "text") return { x: m.x, y: m.y };
1663
- if (m.kind === "rect") return { x: m.x + m.width / 2, y: m.y + m.height / 2 };
1664
- if (m.kind === "line") return { x: (m.x1 + m.x2) / 2, y: (m.y1 + m.y2) / 2 };
1665
- return null;
1666
- };
1667
- function planChartCommand(spec, scene, command, speed = 1) {
1668
- const ms = (n) => Math.round(n / Math.max(0.1, speed));
1669
- const steps = [];
1670
- const note = "note" in command && command.note ? command.note : void 0;
1671
- if (note) steps.push({ type: "say", text: note, ms: ms(500) });
1672
- switch (command.kind) {
1673
- case "say":
1674
- steps.push({ type: "say", text: command.text, ms: ms(900) });
1675
- return steps;
1676
- case "setValue": {
1677
- const at = chartPointAnchor(scene, command.seriesId, command.index);
1678
- const series = spec.series.find((s) => s.id === command.seriesId);
1679
- const from = series?.values[command.index] ?? 0;
1680
- const scale = series?.axis === "secondary" ? scene.valueScale2 ?? scene.valueScale : scene.valueScale;
1681
- if (at && scale) {
1682
- const px = linearMap(command.value, scale.domain, scale.range);
1683
- const to = scale.axis === "y" ? { x: at.x, y: px } : { x: px, y: at.y };
1684
- steps.push({ type: "move", to: at, ms: ms(380) }, { type: "press" }, { type: "drag", to, ms: ms(520), from, value: command.value }, { type: "release" });
1685
- } else steps.push({ type: "drag", to: { x: scene.plot.x + scene.plot.width / 2, y: scene.plot.y + scene.plot.height / 2 }, ms: ms(200), from, value: command.value });
1686
- steps.push({ type: "commit" });
1687
- return steps;
1688
- }
1689
- case "select": {
1690
- const sel = command.selection;
1691
- let to = null;
1692
- if (sel?.kind === "point") to = chartPointAnchor(scene, sel.seriesId, sel.index);
1693
- else if (sel) {
1694
- const m = scene.marks.find((x) => x.target && JSON.stringify(x.target) === JSON.stringify(sel));
1695
- if (m?.kind === "text") to = { x: m.x, y: m.y };
1696
- else if (m?.kind === "rect") to = { x: m.x + m.width / 2, y: m.y + m.height / 2 };
1697
- else if (m?.kind === "circle") to = { x: m.cx, y: m.cy };
1698
- }
1699
- if (to) steps.push({ type: "move", to, ms: ms(380) }, { type: "press" }, { type: "release" });
1700
- steps.push({ type: "commit" });
1701
- return steps;
1702
- }
1703
- case "update": {
1704
- const keys = Object.keys(command.patch);
1705
- const role = keys.includes("title") ? "title" : keys.includes("legend") ? "legend-label" : keys.some((k) => k === "xAxis") ? "axis" : keys.some((k) => k === "yAxis" || k === "yAxis2") ? "tick-label" : null;
1706
- const to = role && anchorFor(scene, role) || { x: scene.plot.x + scene.plot.width / 2, y: scene.plot.y + 12 };
1707
- steps.push({ type: "move", to, ms: ms(380) }, { type: "press" }, { type: "release" }, { type: "commit" });
1708
- return steps;
1709
- }
1710
- }
1711
- }
1712
- var ChartDirector = class {
1713
- constructor(controller, presence, options) {
1714
- this.controller = controller;
1715
- this.presence = presence;
1716
- this.options = options;
1717
- this.clock = options.clock ?? realChartClock;
1718
- this.queue = new PerformanceQueue(presence, {
1719
- clock: this.clock,
1720
- linger: options.linger,
1721
- beginGesture: (key) => controller.beginGesture(key),
1722
- endGesture: () => controller.endGesture()
1723
- });
1724
- }
1725
- controller;
1726
- presence;
1727
- options;
1728
- clock;
1729
- queue;
1730
- /** Acts out `commands` in order for `actor`. Resolves with one result per command. */
1731
- perform(actor, commands) {
1732
- return this.queue.run(actor, async () => {
1733
- const results = [];
1734
- for (const command of commands) results.push(await this.performOne(actor.id, command));
1735
- return results;
1736
- });
1737
- }
1738
- stale(command) {
1739
- const spec = this.controller.getSpec();
1740
- if (command.kind === "setValue") {
1741
- const s = spec.series.find((x) => x.id === command.seriesId);
1742
- if (!s) return `The series "${command.seriesId}" no longer exists.`;
1743
- if (command.index < 0 || command.index >= spec.categories.length) return `Category ${command.index} no longer exists.`;
1744
- if (!Number.isFinite(command.value)) return "The value must be a finite number.";
1745
- }
1746
- return null;
1747
- }
1748
- move(id, from, to, ms, each) {
1749
- return tweenPresenceCursor(this.presence, this.clock, id, from, to, ms, each);
1750
- }
1751
- async performOne(id, command) {
1752
- const stale = this.stale(command);
1753
- if (stale) return { ok: false, command, message: stale };
1754
- const steps = planChartCommand(this.controller.getSpec(), this.options.scene(), command, this.options.speed);
1755
- const where = () => {
1756
- const a = this.presence.getState().actors.find((x) => x.id === id);
1757
- return { x: a.x, y: a.y };
1758
- };
1759
- {
1760
- for (const step of steps) {
1761
- switch (step.type) {
1762
- case "say":
1763
- this.presence.set({ id, message: step.text });
1764
- await this.clock.wait(step.ms);
1765
- break;
1766
- case "move":
1767
- await this.move(id, where(), step.to, step.ms);
1768
- break;
1769
- case "press":
1770
- this.presence.set({ id, pressed: true });
1771
- break;
1772
- case "release":
1773
- this.presence.set({ id, pressed: false });
1774
- break;
1775
- case "drag": {
1776
- if (command.kind !== "setValue") break;
1777
- const stale2 = this.stale(command);
1778
- if (stale2) return { ok: false, command, message: stale2 };
1779
- const label = `${this.presence.getState().actors.find((a) => a.id === id)?.name ?? "Agent"}: edit value`;
1780
- await this.move(id, where(), step.to, step.ms, (e) => {
1781
- const v = step.from + (step.value - step.from) * e;
1782
- this.controller.setValue(command.seriesId, command.index, Number(v.toPrecision(12)), { label });
1783
- });
1784
- this.controller.setValue(command.seriesId, command.index, step.value, { label });
1785
- break;
1786
- }
1787
- case "commit": {
1788
- if (command.kind === "select") this.controller.select(command.selection);
1789
- else if (command.kind === "update") {
1790
- const result = this.controller.update(command.patch, command.label ? { label: command.label } : {});
1791
- if (!result.ok) return { ok: false, command, message: result.message };
1792
- }
1793
- break;
1794
- }
1795
- }
1796
- }
1797
- return { ok: true, command };
1798
- }
1799
- }
1800
- };
1801
-
1802
1882
  // src/chart/office-files.ts
1803
1883
  var OfficeChartsError = class extends Error {
1804
1884
  constructor(message, options) {
@@ -2168,6 +2248,7 @@ export {
2168
2248
  chartMovingAverage,
2169
2249
  chartPointAnchor,
2170
2250
  chartPointColor,
2251
+ chartPointRef,
2171
2252
  chartPresetPatch,
2172
2253
  chartQuantile,
2173
2254
  chartRasterSize,
@@ -2231,6 +2312,7 @@ export {
2231
2312
  layoutChart,
2232
2313
  layoutSparkline,
2233
2314
  linearMap,
2315
+ locateChartPoint,
2234
2316
  monochromeChartPalette,
2235
2317
  niceLogScale,
2236
2318
  niceScale,
@@ -2246,6 +2328,7 @@ export {
2246
2328
  parseChartValue,
2247
2329
  parsePartialChartJson,
2248
2330
  planChartCommand,
2331
+ planChartToolPlayback,
2249
2332
  rasterizeChartSvg,
2250
2333
  realChartClock,
2251
2334
  recommendChartTypes,
@@ -2262,5 +2345,6 @@ export {
2262
2345
  summarizeChart,
2263
2346
  summarizeChartSeries,
2264
2347
  trendNumberText,
2265
- uniqueChartSeriesId
2348
+ uniqueChartSeriesId,
2349
+ withChartPlayback
2266
2350
  };
@@ -0,0 +1,22 @@
1
+ import type { AgentToolProvider } from '../tools/registry.cjs';
2
+ import type { PlaybackRef, PlaybackStep } from '../tools/playback.cjs';
3
+ import type { ChartScene, ChartSpec } from './types.cjs';
4
+ /** A point ref: a series (id or name) and a category (index or label), as the tools take them. */
5
+ export declare function chartPointRef(series: string, category: string | number): PlaybackRef;
6
+ /** Where a point ref is drawn in `scene` (a small box around its anchor), or null. */
7
+ export declare function locateChartPoint(spec: ChartSpec, scene: ChartScene, ref: PlaybackRef): {
8
+ x: number;
9
+ y: number;
10
+ width: number;
11
+ height: number;
12
+ } | null;
13
+ /** The cursor steps for one chart tool call (by bare name), or null to fall back to the ids it names. */
14
+ export declare function planChartToolPlayback(tool: string, args: Record<string, any>): PlaybackStep[] | null;
15
+ /**
16
+ * Gives a chart tool provider agent playback on `surface` (default `chart`, where `ChartEditor`
17
+ * attaches). With a director, value changes and selections play through it (`playback: 'self'`).
18
+ */
19
+ export declare function withChartPlayback(provider: AgentToolProvider, options: {
20
+ director: boolean;
21
+ surface?: string;
22
+ }): AgentToolProvider;
@@ -0,0 +1,22 @@
1
+ import type { AgentToolProvider } from '../tools/registry.js';
2
+ import type { PlaybackRef, PlaybackStep } from '../tools/playback.js';
3
+ import type { ChartScene, ChartSpec } from './types.js';
4
+ /** A point ref: a series (id or name) and a category (index or label), as the tools take them. */
5
+ export declare function chartPointRef(series: string, category: string | number): PlaybackRef;
6
+ /** Where a point ref is drawn in `scene` (a small box around its anchor), or null. */
7
+ export declare function locateChartPoint(spec: ChartSpec, scene: ChartScene, ref: PlaybackRef): {
8
+ x: number;
9
+ y: number;
10
+ width: number;
11
+ height: number;
12
+ } | null;
13
+ /** The cursor steps for one chart tool call (by bare name), or null to fall back to the ids it names. */
14
+ export declare function planChartToolPlayback(tool: string, args: Record<string, any>): PlaybackStep[] | null;
15
+ /**
16
+ * Gives a chart tool provider agent playback on `surface` (default `chart`, where `ChartEditor`
17
+ * attaches). With a director, value changes and selections play through it (`playback: 'self'`).
18
+ */
19
+ export declare function withChartPlayback(provider: AgentToolProvider, options: {
20
+ director: boolean;
21
+ surface?: string;
22
+ }): AgentToolProvider;