@pixerate/editor 0.141.0 → 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.
- package/dist/agent/index.cjs +33 -14
- package/dist/agent/index.js +24 -5
- package/dist/agent/providers.d.cts +4 -1
- package/dist/agent/providers.d.ts +4 -1
- package/dist/animation-editor/index.cjs +69 -19
- package/dist/animation-editor/index.d.cts +1 -0
- package/dist/animation-editor/index.d.ts +1 -0
- package/dist/animation-editor/index.js +54 -4
- package/dist/animation-editor/playback.d.cts +10 -0
- package/dist/animation-editor/playback.d.ts +10 -0
- package/dist/chart/index.cjs +254 -170
- package/dist/chart/index.d.cts +1 -0
- package/dist/chart/index.d.ts +1 -0
- package/dist/chart/index.js +243 -159
- package/dist/chart/playback.d.cts +22 -0
- package/dist/chart/playback.d.ts +22 -0
- package/dist/{chunk-MFFE55CS.js → chunk-54GVEVXS.js} +6 -4
- package/dist/{chunk-E747F3D5.js → chunk-CC6WKVIH.js} +86 -5
- package/dist/{chunk-VTY64I6H.cjs → chunk-QSM3MDDJ.cjs} +85 -85
- package/dist/{chunk-2RUNJYOX.cjs → chunk-QZFEX2N4.cjs} +86 -5
- package/dist/{chunk-LMSFXRAJ.js → chunk-VGJTUCWM.js} +1 -1
- package/dist/{chunk-6SFREX6H.cjs → chunk-X2HQP7DF.cjs} +6 -4
- package/dist/chunk-XH32BATE.cjs +68 -0
- package/dist/chunk-Y6SGC6KO.js +68 -0
- package/dist/commands/index.cjs +2 -2
- package/dist/commands/index.js +1 -1
- package/dist/index.cjs +7 -3
- package/dist/index.js +6 -2
- package/dist/paint-editor/index.cjs +67 -19
- package/dist/paint-editor/index.d.cts +1 -0
- package/dist/paint-editor/index.d.ts +1 -0
- package/dist/paint-editor/index.js +67 -19
- package/dist/paint-editor/playback.d.cts +16 -0
- package/dist/paint-editor/playback.d.ts +16 -0
- package/dist/parameters/index.cjs +32 -8
- package/dist/parameters/index.d.cts +1 -0
- package/dist/parameters/index.d.ts +1 -0
- package/dist/parameters/index.js +28 -4
- package/dist/parameters/playback.d.cts +10 -0
- package/dist/parameters/playback.d.ts +10 -0
- package/dist/spreadsheet/index.cjs +7 -3
- package/dist/spreadsheet/index.d.cts +1 -0
- package/dist/spreadsheet/index.d.ts +1 -0
- package/dist/spreadsheet/index.js +6 -2
- package/dist/spreadsheet/playback.d.cts +15 -0
- package/dist/spreadsheet/playback.d.ts +15 -0
- package/dist/tools/index.cjs +10 -3
- package/dist/tools/index.d.cts +1 -0
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.js +9 -2
- package/dist/tools/script.d.cts +77 -0
- package/dist/tools/script.d.ts +77 -0
- package/dist/video-editor/index.cjs +81 -28
- package/dist/video-editor/index.d.cts +1 -0
- package/dist/video-editor/index.d.ts +1 -0
- package/dist/video-editor/index.js +58 -5
- package/dist/video-editor/playback.d.cts +20 -0
- package/dist/video-editor/playback.d.ts +20 -0
- package/package.json +1 -1
- package/skills/pixerate-animation-editor/SKILL.md +1 -0
- package/skills/pixerate-chart/SKILL.md +1 -0
- package/skills/pixerate-paint-editor/SKILL.md +1 -0
- package/skills/pixerate-parameters/SKILL.md +1 -0
- package/skills/pixerate-spreadsheet/SKILL.md +1 -0
- package/skills/pixerate-video-editor/SKILL.md +1 -0
- package/dist/chunk-PLKIYNJZ.js +0 -0
- package/dist/chunk-TNCWKC5M.cjs +0 -1
package/dist/chart/index.js
CHANGED
|
@@ -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;
|
|
@@ -87,6 +87,7 @@ function normalizePlan(plan) {
|
|
|
87
87
|
}
|
|
88
88
|
var isErrorResult = (value) => !!value && typeof value === "object" && value.isError === true;
|
|
89
89
|
var center = (r) => ({ x: r.x + r.width / 2, y: r.y + r.height / 2 });
|
|
90
|
+
var finiteRect = (r) => r && [r.x, r.y, r.width, r.height].every(Number.isFinite) ? r : null;
|
|
90
91
|
var GLANCE_STEPS = /* @__PURE__ */ new Set(["move", "target", "focus", "say", "wait", "commit"]);
|
|
91
92
|
var AgentPlayback = class {
|
|
92
93
|
/** 1 = natural pace; `Infinity` plays nothing (calls run instantly). Takes effect on the next step. */
|
|
@@ -222,7 +223,7 @@ var AgentPlayback = class {
|
|
|
222
223
|
const pace = () => Math.max(0.1, this.speed) * (run.glance ? 1.6 : 1);
|
|
223
224
|
const wait = (ms) => instant() || ms <= 0 ? Promise.resolve() : this.clock.wait(ms / pace());
|
|
224
225
|
const presence = surface.presence;
|
|
225
|
-
const locate = (ref) => surface.locate?.(ref.kind === "item" ? { kind: surface.defaultKind ?? "item", id: ref.id } : ref) ?? null;
|
|
226
|
+
const locate = (ref) => finiteRect(surface.locate?.(ref.kind === "item" ? { kind: surface.defaultKind ?? "item", id: ref.id } : ref) ?? null);
|
|
226
227
|
const record = (step) => {
|
|
227
228
|
for (const list of this.recordings) list.push({ actor: id, surface: surfaceId, tool: call.name, step });
|
|
228
229
|
};
|
|
@@ -346,7 +347,7 @@ var AgentPlayback = class {
|
|
|
346
347
|
return wait(60);
|
|
347
348
|
case "type": {
|
|
348
349
|
const total = step.ms ?? Math.min(900, 40 * step.text.length + 150);
|
|
349
|
-
if (instant() || total
|
|
350
|
+
if (instant() || !(total > 0) || !Number.isFinite(total)) {
|
|
350
351
|
presence.set({ id, message: step.text });
|
|
351
352
|
return;
|
|
352
353
|
}
|
|
@@ -366,11 +367,12 @@ var AgentPlayback = class {
|
|
|
366
367
|
}
|
|
367
368
|
/** Glides along a slight arc with ease-out, by elapsed time (fewer frames in a throttled tab, never longer). */
|
|
368
369
|
async glide(presence, id, to, ms, instant) {
|
|
370
|
+
if (!Number.isFinite(to.x) || !Number.isFinite(to.y)) return;
|
|
369
371
|
const a = presence.get(id);
|
|
370
|
-
const from = a ? { x: a.x, y: a.y } : to;
|
|
372
|
+
const from = a && Number.isFinite(a.x) && Number.isFinite(a.y) ? { x: a.x, y: a.y } : to;
|
|
371
373
|
const dist = Math.hypot(to.x - from.x, to.y - from.y);
|
|
372
374
|
const duration = (ms ?? Math.min(900, 260 + dist * 0.5)) / Math.max(0.1, this.speed);
|
|
373
|
-
if (instant() || dist < 1 || duration
|
|
375
|
+
if (instant() || dist < 1 || !(duration > 0) || !Number.isFinite(duration)) {
|
|
374
376
|
presence.set({ id, x: to.x, y: to.y });
|
|
375
377
|
return;
|
|
376
378
|
}
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
formatIsoDate,
|
|
9
9
|
formatIsoDateTime,
|
|
10
10
|
formatTextFunction,
|
|
11
|
+
indexToColName,
|
|
11
12
|
isBlank,
|
|
12
13
|
isFormulaError,
|
|
13
14
|
isoDateHasTime,
|
|
@@ -2947,16 +2948,16 @@ var FormulaEvaluator = class {
|
|
|
2947
2948
|
const value = this.evaluateNode(argument);
|
|
2948
2949
|
if (!Array.isArray(value)) return value;
|
|
2949
2950
|
if (value.length <= 1) return firstValue(value);
|
|
2950
|
-
const
|
|
2951
|
-
if (argument.type === "Range" &&
|
|
2951
|
+
const cell2 = this.context.getFormulaCell?.();
|
|
2952
|
+
if (argument.type === "Range" && cell2) {
|
|
2952
2953
|
const range = toFormulaRange(value);
|
|
2953
2954
|
const top = Math.min(argument.startRow, argument.endRow);
|
|
2954
2955
|
const left = Math.min(colNameToIndex(argument.startCol), colNameToIndex(argument.endCol));
|
|
2955
2956
|
if (range.cols === 1) {
|
|
2956
|
-
const r =
|
|
2957
|
+
const r = cell2.row - top;
|
|
2957
2958
|
if (r >= 0 && r < range.rows) return range.values[r];
|
|
2958
2959
|
} else if (range.rows === 1) {
|
|
2959
|
-
const c = colNameToIndex(
|
|
2960
|
+
const c = colNameToIndex(cell2.col) - left;
|
|
2960
2961
|
if (c >= 0 && c < range.cols) return range.values[c];
|
|
2961
2962
|
}
|
|
2962
2963
|
return this.fail("#VALUE!", `The @ operator found no cell of ${argument.raw} in the formula's row or column.`);
|
|
@@ -3757,6 +3758,84 @@ function getFillDirection(source, target) {
|
|
|
3757
3758
|
return null;
|
|
3758
3759
|
}
|
|
3759
3760
|
|
|
3761
|
+
// src/spreadsheet/playback.ts
|
|
3762
|
+
var CELL = /^\s*\$?([A-Za-z]{1,3})\$?(\d+)\s*$/;
|
|
3763
|
+
var COL = /^\s*([A-Za-z]{1,3})\s*$/;
|
|
3764
|
+
var ROW = /^\s*(\d+)\s*$/;
|
|
3765
|
+
function parseSpreadsheetPlaybackRef(ref) {
|
|
3766
|
+
const [a, b = a] = ref.id.split(":");
|
|
3767
|
+
if (!a) return null;
|
|
3768
|
+
if (ref.kind === "cell") {
|
|
3769
|
+
const s = CELL.exec(a);
|
|
3770
|
+
const e = CELL.exec(b);
|
|
3771
|
+
if (!s || !e) return null;
|
|
3772
|
+
const r = [Number(s[2]) - 1, Number(e[2]) - 1];
|
|
3773
|
+
const c = [colNameToIndex(s[1].toUpperCase()), colNameToIndex(e[1].toUpperCase())];
|
|
3774
|
+
return { startRow: Math.min(r[0], r[1]), endRow: Math.max(r[0], r[1]), startCol: Math.min(c[0], c[1]), endCol: Math.max(c[0], c[1]) };
|
|
3775
|
+
}
|
|
3776
|
+
if (ref.kind === "row") {
|
|
3777
|
+
const s = ROW.exec(a);
|
|
3778
|
+
const e = ROW.exec(b);
|
|
3779
|
+
if (!s || !e) return null;
|
|
3780
|
+
return { startRow: Math.min(+s[1], +e[1]) - 1, endRow: Math.max(+s[1], +e[1]) - 1, startCol: null, endCol: null };
|
|
3781
|
+
}
|
|
3782
|
+
if (ref.kind === "column") {
|
|
3783
|
+
const s = COL.exec(a);
|
|
3784
|
+
const e = COL.exec(b);
|
|
3785
|
+
if (!s || !e) return null;
|
|
3786
|
+
const c = [colNameToIndex(s[1].toUpperCase()), colNameToIndex(e[1].toUpperCase())];
|
|
3787
|
+
return { startRow: null, endRow: null, startCol: Math.min(c[0], c[1]), endCol: Math.max(c[0], c[1]) };
|
|
3788
|
+
}
|
|
3789
|
+
return null;
|
|
3790
|
+
}
|
|
3791
|
+
var cell = (id) => ({ kind: "cell", id });
|
|
3792
|
+
var shown = (v) => {
|
|
3793
|
+
if (v === null || v === void 0 || v === "") return null;
|
|
3794
|
+
const text = String(v);
|
|
3795
|
+
return text.length > 40 ? `${text.slice(0, 39)}\u2026` : text;
|
|
3796
|
+
};
|
|
3797
|
+
function planSpreadsheetToolPlayback(tool, args) {
|
|
3798
|
+
const count = Number.isInteger(args.count) && args.count > 0 ? args.count : 1;
|
|
3799
|
+
switch (tool) {
|
|
3800
|
+
case "set_cell": {
|
|
3801
|
+
if (typeof args.cell !== "string") return null;
|
|
3802
|
+
const value = shown(args.value);
|
|
3803
|
+
return [{ type: "target", ref: cell(args.cell) }, { type: "press" }, { type: "release" }, ...value ? [{ type: "type", text: value }] : []];
|
|
3804
|
+
}
|
|
3805
|
+
case "set_range": {
|
|
3806
|
+
const start = typeof args.start === "string" ? CELL.exec(args.start) : null;
|
|
3807
|
+
const rows = Array.isArray(args.values) ? args.values : [];
|
|
3808
|
+
if (!start || !rows.length) return null;
|
|
3809
|
+
const width = Math.max(1, ...rows.map((r) => Array.isArray(r) ? r.length : 1));
|
|
3810
|
+
const col = colNameToIndex(start[1].toUpperCase());
|
|
3811
|
+
const end = `${indexToColName(col + width - 1)}${Number(start[2]) + rows.length - 1}`;
|
|
3812
|
+
const first = shown(rows.flat().find((v) => v !== null && v !== void 0 && v !== ""));
|
|
3813
|
+
return [
|
|
3814
|
+
{ type: "target", ref: cell(args.start) },
|
|
3815
|
+
{ type: "press" },
|
|
3816
|
+
{ type: "target", ref: cell(`${args.start}:${end}`) },
|
|
3817
|
+
{ type: "release" },
|
|
3818
|
+
...first ? [{ type: "type", text: first }] : []
|
|
3819
|
+
];
|
|
3820
|
+
}
|
|
3821
|
+
case "insert_rows":
|
|
3822
|
+
case "delete_rows": {
|
|
3823
|
+
const from = Number(tool === "insert_rows" ? args.before : args.start);
|
|
3824
|
+
if (!Number.isInteger(from) || from < 1) return null;
|
|
3825
|
+
return [{ type: "target", ref: { kind: "row", id: `${from}:${from + count - 1}` } }, { type: "press" }, { type: "release" }];
|
|
3826
|
+
}
|
|
3827
|
+
case "insert_columns":
|
|
3828
|
+
case "delete_columns": {
|
|
3829
|
+
const from = String(tool === "insert_columns" ? args.before : args.start ?? "");
|
|
3830
|
+
if (!COL.test(from)) return null;
|
|
3831
|
+
const index = colNameToIndex(from.trim().toUpperCase());
|
|
3832
|
+
return [{ type: "target", ref: { kind: "column", id: `${indexToColName(index)}:${indexToColName(index + count - 1)}` } }, { type: "press" }, { type: "release" }];
|
|
3833
|
+
}
|
|
3834
|
+
default:
|
|
3835
|
+
return null;
|
|
3836
|
+
}
|
|
3837
|
+
}
|
|
3838
|
+
|
|
3760
3839
|
export {
|
|
3761
3840
|
tokenizeFormula,
|
|
3762
3841
|
WHOLE_COLUMN_END_ROW,
|
|
@@ -3794,5 +3873,7 @@ export {
|
|
|
3794
3873
|
roundFloat,
|
|
3795
3874
|
fillSeries,
|
|
3796
3875
|
computeFillTarget,
|
|
3797
|
-
getFillDirection
|
|
3876
|
+
getFillDirection,
|
|
3877
|
+
parseSpreadsheetPlaybackRef,
|
|
3878
|
+
planSpreadsheetToolPlayback
|
|
3798
3879
|
};
|