@pixerate/editor 0.280.0 → 0.281.0
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/canvas/edge-animation-tools.d.cts +115 -0
- package/dist/canvas/edge-animation-tools.d.ts +115 -0
- package/dist/canvas/edgeAnimation.d.cts +164 -0
- package/dist/canvas/edgeAnimation.d.ts +164 -0
- package/dist/canvas/edgePaths.d.cts +63 -0
- package/dist/canvas/edgePaths.d.ts +63 -0
- package/dist/canvas/index.cjs +679 -9
- package/dist/canvas/index.d.cts +3 -0
- package/dist/canvas/index.d.ts +3 -0
- package/dist/canvas/index.js +678 -8
- package/package.json +1 -1
- package/skills/pixerate-canvas/SKILL.md +25 -2
package/dist/canvas/index.js
CHANGED
|
@@ -2058,8 +2058,8 @@ function snapResize(rect2, handle, targets, options = {}) {
|
|
|
2058
2058
|
const ratio = rect2.height > 0 ? rect2.width / rect2.height : 0;
|
|
2059
2059
|
if (options.keepAspectRatio && ratio > 0 && (hitX || hitY)) {
|
|
2060
2060
|
if (axes.x !== 0 && axes.y !== 0) {
|
|
2061
|
-
const
|
|
2062
|
-
const useX =
|
|
2061
|
+
const dist2 = (h, horizontal) => h ? Math.abs(h.value - (horizontal ? axes.x < 0 ? x0 : x1 : axes.y < 0 ? y0 : y1)) : Infinity;
|
|
2062
|
+
const useX = dist2(hitX, true) <= dist2(hitY, false);
|
|
2063
2063
|
if (useX && hitX) {
|
|
2064
2064
|
setEdge(true, hitX.value);
|
|
2065
2065
|
const height = (nx1 - nx0) / ratio;
|
|
@@ -5219,7 +5219,7 @@ var defaultLabel = (node) => typeof node.data?.label === "string" && node.data.l
|
|
|
5219
5219
|
function getTimelineRows(nodes, ids, options = {}) {
|
|
5220
5220
|
const byId = new Map(nodes.map((n3) => [n3.id, n3]));
|
|
5221
5221
|
const selection = options.selection ?? [];
|
|
5222
|
-
const
|
|
5222
|
+
const labelOf4 = options.labelOf ?? defaultLabel;
|
|
5223
5223
|
const rows = [];
|
|
5224
5224
|
for (const id of new Set(ids)) {
|
|
5225
5225
|
const node = byId.get(id);
|
|
@@ -5230,7 +5230,7 @@ function getTimelineRows(nodes, ids, options = {}) {
|
|
|
5230
5230
|
rows.push({
|
|
5231
5231
|
id: `${id}:${property}`,
|
|
5232
5232
|
nodeId: id,
|
|
5233
|
-
nodeLabel:
|
|
5233
|
+
nodeLabel: labelOf4(node),
|
|
5234
5234
|
property,
|
|
5235
5235
|
label,
|
|
5236
5236
|
enabled: track.enabled !== false,
|
|
@@ -8058,8 +8058,8 @@ function findStrokesAtPoint(point, nodes, radius = 12) {
|
|
|
8058
8058
|
}
|
|
8059
8059
|
const localPoint = [point[0] - rect2.x, point[1] - rect2.y];
|
|
8060
8060
|
try {
|
|
8061
|
-
const
|
|
8062
|
-
if (
|
|
8061
|
+
const dist2 = distanceToBezierPath(shape.d, localPoint);
|
|
8062
|
+
if (dist2 <= effectiveRadius) {
|
|
8063
8063
|
matches2.push(node);
|
|
8064
8064
|
}
|
|
8065
8065
|
} catch {
|
|
@@ -11314,8 +11314,8 @@ function calculateCursorArrowRotation(cursor, targetCenter, isAgentOrServiceAcco
|
|
|
11314
11314
|
}
|
|
11315
11315
|
const dx = targetCenter.x - cursor.x;
|
|
11316
11316
|
const dy = targetCenter.y - cursor.y;
|
|
11317
|
-
const
|
|
11318
|
-
if (
|
|
11317
|
+
const dist2 = Math.hypot(dx, dy);
|
|
11318
|
+
if (dist2 < 2) {
|
|
11319
11319
|
return { shouldPoint: true, rotationDeg: currentRotation };
|
|
11320
11320
|
}
|
|
11321
11321
|
const angleDeg = Math.atan2(dy, dx) * (180 / Math.PI);
|
|
@@ -12966,6 +12966,665 @@ function createCanvasStickerHost(options) {
|
|
|
12966
12966
|
}
|
|
12967
12967
|
};
|
|
12968
12968
|
}
|
|
12969
|
+
|
|
12970
|
+
// src/canvas/edgePaths.ts
|
|
12971
|
+
var DIRECTIONS = {
|
|
12972
|
+
left: { x: -1, y: 0 },
|
|
12973
|
+
right: { x: 1, y: 0 },
|
|
12974
|
+
top: { x: 0, y: -1 },
|
|
12975
|
+
bottom: { x: 0, y: 1 }
|
|
12976
|
+
};
|
|
12977
|
+
function controlOffset(distance, curvature) {
|
|
12978
|
+
return distance >= 0 ? 0.5 * distance : curvature * 25 * Math.sqrt(-distance);
|
|
12979
|
+
}
|
|
12980
|
+
function curvatureControl(pos, x1, y1, x2, y2, c) {
|
|
12981
|
+
switch (pos) {
|
|
12982
|
+
case "left":
|
|
12983
|
+
return [x1 - controlOffset(x1 - x2, c), y1];
|
|
12984
|
+
case "right":
|
|
12985
|
+
return [x1 + controlOffset(x2 - x1, c), y1];
|
|
12986
|
+
case "top":
|
|
12987
|
+
return [x1, y1 - controlOffset(y1 - y2, c)];
|
|
12988
|
+
default:
|
|
12989
|
+
return [x1, y1 + controlOffset(y2 - y1, c)];
|
|
12990
|
+
}
|
|
12991
|
+
}
|
|
12992
|
+
function simpleControl(pos, x1, y1, x2, y2) {
|
|
12993
|
+
return pos === "left" || pos === "right" ? [0.5 * (x1 + x2), y1] : [x1, 0.5 * (y1 + y2)];
|
|
12994
|
+
}
|
|
12995
|
+
function cubicPath(p, s, t) {
|
|
12996
|
+
const { sourceX: sx, sourceY: sy, targetX: tx, targetY: ty } = p;
|
|
12997
|
+
return {
|
|
12998
|
+
path: `M${sx},${sy} C${s[0]},${s[1]} ${t[0]},${t[1]} ${tx},${ty}`,
|
|
12999
|
+
labelX: sx * 0.125 + s[0] * 0.375 + t[0] * 0.375 + tx * 0.125,
|
|
13000
|
+
labelY: sy * 0.125 + s[1] * 0.375 + t[1] * 0.375 + ty * 0.125
|
|
13001
|
+
};
|
|
13002
|
+
}
|
|
13003
|
+
function edgeCenter(sx, sy, tx, ty) {
|
|
13004
|
+
const xOffset = Math.abs(tx - sx) / 2;
|
|
13005
|
+
const yOffset = Math.abs(ty - sy) / 2;
|
|
13006
|
+
return [tx < sx ? tx + xOffset : tx - xOffset, ty < sy ? ty + yOffset : ty - yOffset];
|
|
13007
|
+
}
|
|
13008
|
+
var dist = (a, b) => Math.hypot(b.x - a.x, b.y - a.y);
|
|
13009
|
+
function stepPoints(source, sourcePosition, target, targetPosition, offset) {
|
|
13010
|
+
const sourceDir = DIRECTIONS[sourcePosition];
|
|
13011
|
+
const targetDir = DIRECTIONS[targetPosition];
|
|
13012
|
+
const sourceGapped = { x: source.x + sourceDir.x * offset, y: source.y + sourceDir.y * offset };
|
|
13013
|
+
const targetGapped = { x: target.x + targetDir.x * offset, y: target.y + targetDir.y * offset };
|
|
13014
|
+
const dir = sourcePosition === "left" || sourcePosition === "right" ? { x: sourceGapped.x < targetGapped.x ? 1 : -1, y: 0 } : { x: 0, y: sourceGapped.y < targetGapped.y ? 1 : -1 };
|
|
13015
|
+
const axis = dir.x !== 0 ? "x" : "y";
|
|
13016
|
+
const currDir = dir[axis];
|
|
13017
|
+
let points;
|
|
13018
|
+
let centerX;
|
|
13019
|
+
let centerY;
|
|
13020
|
+
const sourceGapOffset = { x: 0, y: 0 };
|
|
13021
|
+
const targetGapOffset = { x: 0, y: 0 };
|
|
13022
|
+
if (sourceDir[axis] * targetDir[axis] === -1) {
|
|
13023
|
+
centerX = (sourceGapped.x + targetGapped.x) / 2;
|
|
13024
|
+
centerY = (sourceGapped.y + targetGapped.y) / 2;
|
|
13025
|
+
const verticalSplit = [{ x: centerX, y: sourceGapped.y }, { x: centerX, y: targetGapped.y }];
|
|
13026
|
+
const horizontalSplit = [{ x: sourceGapped.x, y: centerY }, { x: targetGapped.x, y: centerY }];
|
|
13027
|
+
points = sourceDir[axis] === currDir ? axis === "x" ? verticalSplit : horizontalSplit : axis === "x" ? horizontalSplit : verticalSplit;
|
|
13028
|
+
} else {
|
|
13029
|
+
const sourceTarget = [{ x: sourceGapped.x, y: targetGapped.y }];
|
|
13030
|
+
const targetSource = [{ x: targetGapped.x, y: sourceGapped.y }];
|
|
13031
|
+
points = axis === "x" ? sourceDir.x === currDir ? targetSource : sourceTarget : sourceDir.y === currDir ? sourceTarget : targetSource;
|
|
13032
|
+
if (sourcePosition === targetPosition) {
|
|
13033
|
+
const diff = Math.abs(source[axis] - target[axis]);
|
|
13034
|
+
if (diff <= offset) {
|
|
13035
|
+
const gapOffset = Math.min(offset - 1, offset - diff);
|
|
13036
|
+
if (sourceDir[axis] === currDir) sourceGapOffset[axis] = (sourceGapped[axis] > source[axis] ? -1 : 1) * gapOffset;
|
|
13037
|
+
else targetGapOffset[axis] = (targetGapped[axis] > target[axis] ? -1 : 1) * gapOffset;
|
|
13038
|
+
}
|
|
13039
|
+
} else {
|
|
13040
|
+
const opposite = axis === "x" ? "y" : "x";
|
|
13041
|
+
const isSameDir = sourceDir[axis] === targetDir[opposite];
|
|
13042
|
+
const sourceGt = sourceGapped[opposite] > targetGapped[opposite];
|
|
13043
|
+
const sourceLt = sourceGapped[opposite] < targetGapped[opposite];
|
|
13044
|
+
const flip = sourceDir[axis] === 1 && (!isSameDir && sourceGt || isSameDir && sourceLt) || sourceDir[axis] !== 1 && (!isSameDir && sourceLt || isSameDir && sourceGt);
|
|
13045
|
+
if (flip) points = axis === "x" ? sourceTarget : targetSource;
|
|
13046
|
+
}
|
|
13047
|
+
const sourceGapPoint = { x: sourceGapped.x + sourceGapOffset.x, y: sourceGapped.y + sourceGapOffset.y };
|
|
13048
|
+
const targetGapPoint = { x: targetGapped.x + targetGapOffset.x, y: targetGapped.y + targetGapOffset.y };
|
|
13049
|
+
const maxX = Math.max(Math.abs(sourceGapPoint.x - points[0].x), Math.abs(targetGapPoint.x - points[0].x));
|
|
13050
|
+
const maxY = Math.max(Math.abs(sourceGapPoint.y - points[0].y), Math.abs(targetGapPoint.y - points[0].y));
|
|
13051
|
+
if (maxX >= maxY) {
|
|
13052
|
+
centerX = (sourceGapPoint.x + targetGapPoint.x) / 2;
|
|
13053
|
+
centerY = points[0].y;
|
|
13054
|
+
} else {
|
|
13055
|
+
centerX = points[0].x;
|
|
13056
|
+
centerY = (sourceGapPoint.y + targetGapPoint.y) / 2;
|
|
13057
|
+
}
|
|
13058
|
+
}
|
|
13059
|
+
const gappedSource = { x: sourceGapped.x + sourceGapOffset.x, y: sourceGapped.y + sourceGapOffset.y };
|
|
13060
|
+
const gappedTarget = { x: targetGapped.x + targetGapOffset.x, y: targetGapped.y + targetGapOffset.y };
|
|
13061
|
+
const first = points[0];
|
|
13062
|
+
const last = points[points.length - 1];
|
|
13063
|
+
const path = [
|
|
13064
|
+
source,
|
|
13065
|
+
...gappedSource.x !== first.x || gappedSource.y !== first.y ? [gappedSource] : [],
|
|
13066
|
+
...points,
|
|
13067
|
+
...gappedTarget.x !== last.x || gappedTarget.y !== last.y ? [gappedTarget] : [],
|
|
13068
|
+
target
|
|
13069
|
+
];
|
|
13070
|
+
return [path, centerX, centerY];
|
|
13071
|
+
}
|
|
13072
|
+
function bend(a, b, c, size2) {
|
|
13073
|
+
const bendSize = Math.min(dist(a, b) / 2, dist(b, c) / 2, size2);
|
|
13074
|
+
const { x, y } = b;
|
|
13075
|
+
if (a.x === x && x === c.x || a.y === y && y === c.y) return `L${x} ${y}`;
|
|
13076
|
+
if (a.y === y) {
|
|
13077
|
+
const xDir2 = a.x < c.x ? -1 : 1;
|
|
13078
|
+
const yDir2 = a.y < c.y ? 1 : -1;
|
|
13079
|
+
return `L ${x + bendSize * xDir2},${y}Q ${x},${y} ${x},${y + bendSize * yDir2}`;
|
|
13080
|
+
}
|
|
13081
|
+
const xDir = a.x < c.x ? 1 : -1;
|
|
13082
|
+
const yDir = a.y < c.y ? -1 : 1;
|
|
13083
|
+
return `L ${x},${y + bendSize * yDir}Q ${x},${y} ${x + bendSize * xDir},${y}`;
|
|
13084
|
+
}
|
|
13085
|
+
function getCanvasEdgePath(params) {
|
|
13086
|
+
const { sourceX: sx, sourceY: sy, targetX: tx, targetY: ty } = params;
|
|
13087
|
+
const sourcePosition = params.sourcePosition ?? "bottom";
|
|
13088
|
+
const targetPosition = params.targetPosition ?? "top";
|
|
13089
|
+
switch (params.type ?? "default") {
|
|
13090
|
+
case "straight": {
|
|
13091
|
+
const [labelX, labelY] = edgeCenter(sx, sy, tx, ty);
|
|
13092
|
+
return { path: `M ${sx},${sy}L ${tx},${ty}`, labelX, labelY };
|
|
13093
|
+
}
|
|
13094
|
+
case "simplebezier":
|
|
13095
|
+
return cubicPath(params, simpleControl(sourcePosition, sx, sy, tx, ty), simpleControl(targetPosition, tx, ty, sx, sy));
|
|
13096
|
+
case "step":
|
|
13097
|
+
case "smoothstep": {
|
|
13098
|
+
const radius = params.type === "step" ? 0 : params.borderRadius ?? 5;
|
|
13099
|
+
const [points, labelX, labelY] = stepPoints({ x: sx, y: sy }, sourcePosition, { x: tx, y: ty }, targetPosition, params.offset ?? 20);
|
|
13100
|
+
let path = "";
|
|
13101
|
+
points.forEach((p, i) => {
|
|
13102
|
+
path += i > 0 && i < points.length - 1 ? bend(points[i - 1], p, points[i + 1], radius) : `${i === 0 ? "M" : "L"}${p.x} ${p.y}`;
|
|
13103
|
+
});
|
|
13104
|
+
return { path, labelX, labelY };
|
|
13105
|
+
}
|
|
13106
|
+
default: {
|
|
13107
|
+
const c = params.curvature ?? 0.25;
|
|
13108
|
+
return cubicPath(params, curvatureControl(sourcePosition, sx, sy, tx, ty, c), curvatureControl(targetPosition, tx, ty, sx, sy, c));
|
|
13109
|
+
}
|
|
13110
|
+
}
|
|
13111
|
+
}
|
|
13112
|
+
function measureSvgPath(d, segments = 24) {
|
|
13113
|
+
const pts = [];
|
|
13114
|
+
const tokens = d.match(/[a-zA-Z]|-?(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?/gi) ?? [];
|
|
13115
|
+
let i = 0;
|
|
13116
|
+
let cmd = "";
|
|
13117
|
+
let cur = { x: 0, y: 0 };
|
|
13118
|
+
let start = { x: 0, y: 0 };
|
|
13119
|
+
let lastCtrl = null;
|
|
13120
|
+
let lastCmd = "";
|
|
13121
|
+
const num4 = () => Number(tokens[i++]);
|
|
13122
|
+
const push2 = (p) => {
|
|
13123
|
+
pts.push(p);
|
|
13124
|
+
cur = p;
|
|
13125
|
+
};
|
|
13126
|
+
const cubic = (c1, c2, end) => {
|
|
13127
|
+
const p0 = cur;
|
|
13128
|
+
for (let k = 1; k <= segments; k++) {
|
|
13129
|
+
const t = k / segments;
|
|
13130
|
+
const u = 1 - t;
|
|
13131
|
+
push2({
|
|
13132
|
+
x: u * u * u * p0.x + 3 * u * u * t * c1.x + 3 * u * t * t * c2.x + t * t * t * end.x,
|
|
13133
|
+
y: u * u * u * p0.y + 3 * u * u * t * c1.y + 3 * u * t * t * c2.y + t * t * t * end.y
|
|
13134
|
+
});
|
|
13135
|
+
}
|
|
13136
|
+
};
|
|
13137
|
+
const quad = (c, end) => {
|
|
13138
|
+
const p0 = cur;
|
|
13139
|
+
for (let k = 1; k <= segments; k++) {
|
|
13140
|
+
const t = k / segments;
|
|
13141
|
+
const u = 1 - t;
|
|
13142
|
+
push2({ x: u * u * p0.x + 2 * u * t * c.x + t * t * end.x, y: u * u * p0.y + 2 * u * t * c.y + t * t * end.y });
|
|
13143
|
+
}
|
|
13144
|
+
};
|
|
13145
|
+
while (i < tokens.length) {
|
|
13146
|
+
if (/^[a-zA-Z]$/.test(tokens[i])) cmd = tokens[i++];
|
|
13147
|
+
else if (!cmd) break;
|
|
13148
|
+
const rel = cmd === cmd.toLowerCase();
|
|
13149
|
+
const at = (x, y) => rel ? { x: cur.x + x, y: cur.y + y } : { x, y };
|
|
13150
|
+
switch (cmd.toUpperCase()) {
|
|
13151
|
+
case "M": {
|
|
13152
|
+
const p = at(num4(), num4());
|
|
13153
|
+
push2(p);
|
|
13154
|
+
start = p;
|
|
13155
|
+
cmd = rel ? "l" : "L";
|
|
13156
|
+
lastCtrl = null;
|
|
13157
|
+
lastCmd = "M";
|
|
13158
|
+
continue;
|
|
13159
|
+
}
|
|
13160
|
+
case "L":
|
|
13161
|
+
push2(at(num4(), num4()));
|
|
13162
|
+
lastCtrl = null;
|
|
13163
|
+
break;
|
|
13164
|
+
case "H": {
|
|
13165
|
+
const x = num4();
|
|
13166
|
+
push2({ x: rel ? cur.x + x : x, y: cur.y });
|
|
13167
|
+
lastCtrl = null;
|
|
13168
|
+
break;
|
|
13169
|
+
}
|
|
13170
|
+
case "V": {
|
|
13171
|
+
const y = num4();
|
|
13172
|
+
push2({ x: cur.x, y: rel ? cur.y + y : y });
|
|
13173
|
+
lastCtrl = null;
|
|
13174
|
+
break;
|
|
13175
|
+
}
|
|
13176
|
+
case "C": {
|
|
13177
|
+
const c1 = at(num4(), num4());
|
|
13178
|
+
const c2 = at(num4(), num4());
|
|
13179
|
+
const end = at(num4(), num4());
|
|
13180
|
+
cubic(c1, c2, end);
|
|
13181
|
+
lastCtrl = c2;
|
|
13182
|
+
break;
|
|
13183
|
+
}
|
|
13184
|
+
case "S": {
|
|
13185
|
+
const c1 = lastCtrl && /[CS]/i.test(lastCmd) ? { x: 2 * cur.x - lastCtrl.x, y: 2 * cur.y - lastCtrl.y } : cur;
|
|
13186
|
+
const c2 = at(num4(), num4());
|
|
13187
|
+
const end = at(num4(), num4());
|
|
13188
|
+
cubic(c1, c2, end);
|
|
13189
|
+
lastCtrl = c2;
|
|
13190
|
+
break;
|
|
13191
|
+
}
|
|
13192
|
+
case "Q": {
|
|
13193
|
+
const c = at(num4(), num4());
|
|
13194
|
+
const end = at(num4(), num4());
|
|
13195
|
+
quad(c, end);
|
|
13196
|
+
lastCtrl = c;
|
|
13197
|
+
break;
|
|
13198
|
+
}
|
|
13199
|
+
case "T": {
|
|
13200
|
+
const c = lastCtrl && /[QT]/i.test(lastCmd) ? { x: 2 * cur.x - lastCtrl.x, y: 2 * cur.y - lastCtrl.y } : cur;
|
|
13201
|
+
const end = at(num4(), num4());
|
|
13202
|
+
quad(c, end);
|
|
13203
|
+
lastCtrl = c;
|
|
13204
|
+
break;
|
|
13205
|
+
}
|
|
13206
|
+
case "A": {
|
|
13207
|
+
for (let skip = 0; skip < 5; skip++) num4();
|
|
13208
|
+
push2(at(num4(), num4()));
|
|
13209
|
+
lastCtrl = null;
|
|
13210
|
+
break;
|
|
13211
|
+
}
|
|
13212
|
+
case "Z":
|
|
13213
|
+
push2(start);
|
|
13214
|
+
lastCtrl = null;
|
|
13215
|
+
break;
|
|
13216
|
+
default:
|
|
13217
|
+
i = tokens.length;
|
|
13218
|
+
}
|
|
13219
|
+
lastCmd = cmd;
|
|
13220
|
+
if (cmd.toUpperCase() === "Z") cmd = "";
|
|
13221
|
+
}
|
|
13222
|
+
const cumulative = [0];
|
|
13223
|
+
for (let k = 1; k < pts.length; k++) cumulative.push(cumulative[k - 1] + dist(pts[k - 1], pts[k]));
|
|
13224
|
+
const length = cumulative[cumulative.length - 1] ?? 0;
|
|
13225
|
+
const angleAt = (k) => {
|
|
13226
|
+
for (let j = Math.max(1, k); j < pts.length; j++) {
|
|
13227
|
+
if (cumulative[j] > cumulative[j - 1]) return Math.atan2(pts[j].y - pts[j - 1].y, pts[j].x - pts[j - 1].x);
|
|
13228
|
+
}
|
|
13229
|
+
for (let j = Math.min(k, pts.length - 1); j >= 1; j--) {
|
|
13230
|
+
if (cumulative[j] > cumulative[j - 1]) return Math.atan2(pts[j].y - pts[j - 1].y, pts[j].x - pts[j - 1].x);
|
|
13231
|
+
}
|
|
13232
|
+
return 0;
|
|
13233
|
+
};
|
|
13234
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
13235
|
+
for (const p of pts) {
|
|
13236
|
+
if (p.x < minX) minX = p.x;
|
|
13237
|
+
if (p.y < minY) minY = p.y;
|
|
13238
|
+
if (p.x > maxX) maxX = p.x;
|
|
13239
|
+
if (p.y > maxY) maxY = p.y;
|
|
13240
|
+
}
|
|
13241
|
+
const bounds = pts.length ? { x: minX, y: minY, width: maxX - minX, height: maxY - minY } : { x: 0, y: 0, width: 0, height: 0 };
|
|
13242
|
+
return {
|
|
13243
|
+
length,
|
|
13244
|
+
bounds,
|
|
13245
|
+
pointAt(distance) {
|
|
13246
|
+
if (pts.length === 0) return { x: 0, y: 0, angle: 0 };
|
|
13247
|
+
if (pts.length === 1 || length === 0) return { ...pts[0], angle: 0 };
|
|
13248
|
+
const s = Math.min(Math.max(distance, 0), length);
|
|
13249
|
+
let lo = 1;
|
|
13250
|
+
let hi = pts.length - 1;
|
|
13251
|
+
while (lo < hi) {
|
|
13252
|
+
const mid = lo + hi >> 1;
|
|
13253
|
+
if (cumulative[mid] < s) lo = mid + 1;
|
|
13254
|
+
else hi = mid;
|
|
13255
|
+
}
|
|
13256
|
+
const piece = cumulative[lo] - cumulative[lo - 1];
|
|
13257
|
+
const t = piece > 0 ? (s - cumulative[lo - 1]) / piece : 0;
|
|
13258
|
+
const a = pts[lo - 1];
|
|
13259
|
+
const b = pts[lo];
|
|
13260
|
+
return { x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t, angle: angleAt(lo) };
|
|
13261
|
+
}
|
|
13262
|
+
};
|
|
13263
|
+
}
|
|
13264
|
+
|
|
13265
|
+
// src/canvas/edgeAnimation.ts
|
|
13266
|
+
var CANVAS_EDGE_ANIMATION_KINDS = [
|
|
13267
|
+
{
|
|
13268
|
+
kind: "dots",
|
|
13269
|
+
title: "Travelling dots",
|
|
13270
|
+
description: "Dots that travel along the edge, evenly spaced. Good for data or messages flowing between nodes.",
|
|
13271
|
+
defaults: { speed: 80, direction: "forward", size: 3, count: 3, length: 0, gap: 0, glow: 0, trackOpacity: 1 }
|
|
13272
|
+
},
|
|
13273
|
+
{
|
|
13274
|
+
kind: "dash",
|
|
13275
|
+
title: "Marching dashes",
|
|
13276
|
+
description: 'The edge drawn as dashes that march along it. A quiet "this is live" signal.',
|
|
13277
|
+
defaults: { speed: 40, direction: "forward", size: 2, count: 0, length: 8, gap: 6, glow: 0, trackOpacity: 0 }
|
|
13278
|
+
},
|
|
13279
|
+
{
|
|
13280
|
+
kind: "beam",
|
|
13281
|
+
title: "Beam",
|
|
13282
|
+
description: "A short glowing streak that shoots along the edge and leaves it before the next one starts. Good for a request or a trigger.",
|
|
13283
|
+
defaults: { speed: 200, direction: "forward", size: 3, count: 1, length: 40, gap: 0, glow: 4, trackOpacity: 0.35 }
|
|
13284
|
+
},
|
|
13285
|
+
{
|
|
13286
|
+
kind: "pulse",
|
|
13287
|
+
title: "Pulse",
|
|
13288
|
+
description: "The whole edge glows brighter and fades again. Good for an active or highlighted connection.",
|
|
13289
|
+
defaults: { speed: 0, direction: "forward", size: 4, count: 0, length: 0, gap: 0, glow: 3, trackOpacity: 1, duration: 1600 }
|
|
13290
|
+
}
|
|
13291
|
+
];
|
|
13292
|
+
var KINDS = CANVAS_EDGE_ANIMATION_KINDS.map((k) => k.kind);
|
|
13293
|
+
var DIRECTIONS2 = ["forward", "reverse", "alternate"];
|
|
13294
|
+
var kindInfo = (kind) => CANVAS_EDGE_ANIMATION_KINDS.find((k) => k.kind === kind);
|
|
13295
|
+
var FIELDS = {
|
|
13296
|
+
speed: { min: 1, max: 1e4, description: "canvas units per second" },
|
|
13297
|
+
duration: { min: 100, max: 6e5, description: "ms" },
|
|
13298
|
+
size: { min: 0.5, max: 100, description: "canvas units" },
|
|
13299
|
+
count: { min: 1, max: 50, int: true, description: "dots or beams" },
|
|
13300
|
+
length: { min: 1, max: 1e4, description: "canvas units" },
|
|
13301
|
+
gap: { min: 0, max: 1e4, description: "canvas units" },
|
|
13302
|
+
glow: { min: 0, max: 50, description: "canvas units" },
|
|
13303
|
+
trackOpacity: { min: 0, max: 1, description: "0\u20131" }
|
|
13304
|
+
};
|
|
13305
|
+
function normalizeCanvasEdgeAnimation(input) {
|
|
13306
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) throw new Error('An edge animation is an object with a "kind" (dots, dash, beam or pulse).');
|
|
13307
|
+
const raw = input;
|
|
13308
|
+
if (!KINDS.includes(raw.kind)) {
|
|
13309
|
+
throw new Error(`Unknown edge animation kind ${JSON.stringify(raw.kind)}. Use one of: ${KINDS.join(", ")}.`);
|
|
13310
|
+
}
|
|
13311
|
+
const out = { kind: raw.kind };
|
|
13312
|
+
for (const [key, spec] of Object.entries(FIELDS)) {
|
|
13313
|
+
const value = raw[key];
|
|
13314
|
+
if (value === void 0 || value === null) continue;
|
|
13315
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < spec.min || value > spec.max || spec.int && !Number.isInteger(value)) {
|
|
13316
|
+
throw new Error(`Edge animation "${key}" must be ${spec.int ? "a whole number" : "a number"} from ${spec.min} to ${spec.max} (${spec.description}); got ${JSON.stringify(value)}.`);
|
|
13317
|
+
}
|
|
13318
|
+
out[key] = value;
|
|
13319
|
+
}
|
|
13320
|
+
if (raw.direction !== void 0 && raw.direction !== null) {
|
|
13321
|
+
if (!DIRECTIONS2.includes(raw.direction)) {
|
|
13322
|
+
throw new Error(`Edge animation "direction" must be one of: ${DIRECTIONS2.join(", ")}; got ${JSON.stringify(raw.direction)}.`);
|
|
13323
|
+
}
|
|
13324
|
+
out.direction = raw.direction;
|
|
13325
|
+
}
|
|
13326
|
+
if (raw.color !== void 0 && raw.color !== null) {
|
|
13327
|
+
if (typeof raw.color !== "string" || raw.color.trim() === "" || /[;{}<>]/.test(raw.color)) {
|
|
13328
|
+
throw new Error(`Edge animation "color" must be a CSS colour such as "#38bdf8" or "rgb(56 189 248)"; got ${JSON.stringify(raw.color)}.`);
|
|
13329
|
+
}
|
|
13330
|
+
out.color = raw.color.trim();
|
|
13331
|
+
}
|
|
13332
|
+
if (raw.paused !== void 0 && raw.paused !== null) {
|
|
13333
|
+
if (typeof raw.paused !== "boolean") throw new Error(`Edge animation "paused" must be true or false; got ${JSON.stringify(raw.paused)}.`);
|
|
13334
|
+
out.paused = raw.paused;
|
|
13335
|
+
}
|
|
13336
|
+
return out;
|
|
13337
|
+
}
|
|
13338
|
+
function getCanvasEdgeAnimation(edge) {
|
|
13339
|
+
const value = edge?.data?.animation;
|
|
13340
|
+
if (value === void 0 || value === null) return null;
|
|
13341
|
+
try {
|
|
13342
|
+
return normalizeCanvasEdgeAnimation(value);
|
|
13343
|
+
} catch {
|
|
13344
|
+
return null;
|
|
13345
|
+
}
|
|
13346
|
+
}
|
|
13347
|
+
function setCanvasEdgeAnimation(edge, animation) {
|
|
13348
|
+
const { animation: _old, ...rest } = edge.data ?? {};
|
|
13349
|
+
if (animation === null) return { ...edge, data: rest };
|
|
13350
|
+
return { ...edge, data: { ...rest, animation: normalizeCanvasEdgeAnimation(animation) } };
|
|
13351
|
+
}
|
|
13352
|
+
function resolveCanvasEdgeAnimation(animation) {
|
|
13353
|
+
const d = kindInfo(animation.kind).defaults;
|
|
13354
|
+
return {
|
|
13355
|
+
kind: animation.kind,
|
|
13356
|
+
speed: animation.speed ?? d.speed,
|
|
13357
|
+
direction: animation.direction ?? d.direction,
|
|
13358
|
+
size: animation.size ?? d.size,
|
|
13359
|
+
count: animation.count ?? d.count,
|
|
13360
|
+
length: animation.length ?? d.length,
|
|
13361
|
+
gap: animation.gap ?? d.gap,
|
|
13362
|
+
glow: animation.glow ?? d.glow,
|
|
13363
|
+
trackOpacity: animation.trackOpacity ?? d.trackOpacity,
|
|
13364
|
+
paused: animation.paused ?? false,
|
|
13365
|
+
...animation.color !== void 0 ? { color: animation.color } : {},
|
|
13366
|
+
...animation.duration !== void 0 ? { duration: animation.duration } : d.duration !== void 0 ? { duration: d.duration } : {}
|
|
13367
|
+
};
|
|
13368
|
+
}
|
|
13369
|
+
var round4 = (n3) => Math.round(n3 * 1e3) / 1e3;
|
|
13370
|
+
var idSafe = (id) => id.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
13371
|
+
function cycleSeconds(anim, distance) {
|
|
13372
|
+
if (anim.duration !== void 0) return anim.duration / 1e3;
|
|
13373
|
+
return Math.max(distance / anim.speed, 0.05);
|
|
13374
|
+
}
|
|
13375
|
+
function getCanvasEdgeAnimationRender(animation, options) {
|
|
13376
|
+
const anim = resolveCanvasEdgeAnimation(normalizeCanvasEdgeAnimation(animation));
|
|
13377
|
+
const measured2 = options.measured ?? measureSvgPath(options.path);
|
|
13378
|
+
const length = measured2.length;
|
|
13379
|
+
const color = anim.color ?? options.stroke ?? "currentColor";
|
|
13380
|
+
const moving = !anim.paused && !options.reducedMotion && length > 0;
|
|
13381
|
+
const margin = anim.size * 2 + anim.glow * 3 + 2;
|
|
13382
|
+
const b = measured2.bounds;
|
|
13383
|
+
const glowFilter = anim.glow > 0 ? {
|
|
13384
|
+
id: `pixerate-edge-glow-${idSafe(options.id)}`,
|
|
13385
|
+
stdDeviation: round4(anim.glow / 2),
|
|
13386
|
+
x: round4(b.x - margin),
|
|
13387
|
+
y: round4(b.y - margin),
|
|
13388
|
+
width: round4(b.width + margin * 2),
|
|
13389
|
+
height: round4(b.height + margin * 2)
|
|
13390
|
+
} : void 0;
|
|
13391
|
+
const filter = glowFilter ? `url(#${glowFilter.id})` : void 0;
|
|
13392
|
+
const alternate = anim.direction === "alternate";
|
|
13393
|
+
const reverse = anim.direction === "reverse";
|
|
13394
|
+
const elements = [];
|
|
13395
|
+
if (anim.kind === "dots") {
|
|
13396
|
+
const oneWay = cycleSeconds(anim, length);
|
|
13397
|
+
const dur = alternate ? oneWay * 2 : oneWay;
|
|
13398
|
+
const keyPoints = alternate ? "0;1;0" : reverse ? "1;0" : "0;1";
|
|
13399
|
+
const keyTimes = alternate ? "0;0.5;1" : "0;1";
|
|
13400
|
+
for (let i = 0; i < anim.count; i++) {
|
|
13401
|
+
const phase = i / anim.count;
|
|
13402
|
+
const s = alternate ? phase < 0.5 ? phase * 2 : 2 - phase * 2 : reverse ? 1 - phase : phase;
|
|
13403
|
+
elements.push({
|
|
13404
|
+
type: "dot",
|
|
13405
|
+
key: `dot-${i}`,
|
|
13406
|
+
r: anim.size,
|
|
13407
|
+
fill: color,
|
|
13408
|
+
filter,
|
|
13409
|
+
at: measured2.pointAt(s * length),
|
|
13410
|
+
motion: { dur: round4(dur), begin: round4(-phase * dur), keyPoints, keyTimes }
|
|
13411
|
+
});
|
|
13412
|
+
}
|
|
13413
|
+
} else if (anim.kind === "dash") {
|
|
13414
|
+
const period = anim.length + anim.gap;
|
|
13415
|
+
const dur = cycleSeconds(anim, period);
|
|
13416
|
+
const to = reverse ? period : -period;
|
|
13417
|
+
elements.push({
|
|
13418
|
+
type: "stroke",
|
|
13419
|
+
key: "dash",
|
|
13420
|
+
stroke: color,
|
|
13421
|
+
strokeWidth: anim.size,
|
|
13422
|
+
strokeLinecap: "butt",
|
|
13423
|
+
strokeDasharray: `${round4(anim.length)} ${round4(anim.gap)}`,
|
|
13424
|
+
strokeDashoffset: 0,
|
|
13425
|
+
filter,
|
|
13426
|
+
animate: [{ attributeName: "stroke-dashoffset", values: alternate ? `0;${round4(-period)};0` : `0;${round4(to)}`, dur: round4(alternate ? dur * 2 : dur), begin: 0 }]
|
|
13427
|
+
});
|
|
13428
|
+
} else if (anim.kind === "beam") {
|
|
13429
|
+
const period = (length + anim.length) / anim.count;
|
|
13430
|
+
const beam = Math.min(anim.length, period * 0.9);
|
|
13431
|
+
const oneWay = cycleSeconds(anim, period);
|
|
13432
|
+
const from = beam;
|
|
13433
|
+
const to = beam - period;
|
|
13434
|
+
const values = alternate ? `${round4(from)};${round4(to)};${round4(from)}` : reverse ? `${round4(to)};${round4(from)}` : `${round4(from)};${round4(to)}`;
|
|
13435
|
+
elements.push({
|
|
13436
|
+
type: "stroke",
|
|
13437
|
+
key: "beam",
|
|
13438
|
+
stroke: color,
|
|
13439
|
+
strokeWidth: anim.size,
|
|
13440
|
+
strokeLinecap: "round",
|
|
13441
|
+
strokeDasharray: `${round4(beam)} ${round4(Math.max(period - beam, 1e-3))}`,
|
|
13442
|
+
// Held still, the beam sits in the middle of the edge rather than off its start.
|
|
13443
|
+
strokeDashoffset: round4(moving ? reverse ? to : from : beam / 2 - length / 2),
|
|
13444
|
+
filter,
|
|
13445
|
+
animate: [{ attributeName: "stroke-dashoffset", values, dur: round4(alternate ? oneWay * 2 : oneWay), begin: 0 }]
|
|
13446
|
+
});
|
|
13447
|
+
} else {
|
|
13448
|
+
const dur = (anim.duration ?? 1600) / 1e3;
|
|
13449
|
+
elements.push({
|
|
13450
|
+
type: "stroke",
|
|
13451
|
+
key: "pulse",
|
|
13452
|
+
stroke: color,
|
|
13453
|
+
strokeWidth: anim.size,
|
|
13454
|
+
strokeLinecap: "round",
|
|
13455
|
+
opacity: 0.2,
|
|
13456
|
+
filter,
|
|
13457
|
+
animate: [
|
|
13458
|
+
{ attributeName: "stroke-opacity", values: "0.2;1;0.2", dur: round4(dur), begin: 0 },
|
|
13459
|
+
{ attributeName: "stroke-width", values: `${round4(anim.size)};${round4(anim.size * 1.75)};${round4(anim.size)}`, dur: round4(dur), begin: 0 }
|
|
13460
|
+
]
|
|
13461
|
+
});
|
|
13462
|
+
}
|
|
13463
|
+
return { kind: anim.kind, length: round4(length), trackOpacity: anim.trackOpacity, moving, glowFilter, elements };
|
|
13464
|
+
}
|
|
13465
|
+
function valuesAt(values, t) {
|
|
13466
|
+
if (values.length === 1) return values[0];
|
|
13467
|
+
const f = Math.min(Math.max(t, 0), 1) * (values.length - 1);
|
|
13468
|
+
const i = Math.min(Math.floor(f), values.length - 2);
|
|
13469
|
+
return values[i] + (values[i + 1] - values[i]) * (f - i);
|
|
13470
|
+
}
|
|
13471
|
+
function sampleCanvasEdgeAnimation(animation, path, timeMs) {
|
|
13472
|
+
const measured2 = typeof path === "string" ? measureSvgPath(path) : path;
|
|
13473
|
+
const render = getCanvasEdgeAnimationRender(animation, { path: "", id: "sample", measured: measured2 });
|
|
13474
|
+
const anim = resolveCanvasEdgeAnimation(normalizeCanvasEdgeAnimation(animation));
|
|
13475
|
+
const seconds = anim.paused ? 0 : timeMs / 1e3;
|
|
13476
|
+
const progress = (t) => {
|
|
13477
|
+
const p = (seconds - t.begin) / t.dur;
|
|
13478
|
+
return p - Math.floor(p);
|
|
13479
|
+
};
|
|
13480
|
+
const frame = { kind: anim.kind, length: render.length, dots: [], segments: [], opacity: 1, strokeWidth: anim.size };
|
|
13481
|
+
for (const el of render.elements) {
|
|
13482
|
+
if (el.type === "dot") {
|
|
13483
|
+
const points = el.motion.keyPoints.split(";").map(Number);
|
|
13484
|
+
frame.dots.push(measured2.pointAt(valuesAt(points, progress(el.motion)) * measured2.length));
|
|
13485
|
+
continue;
|
|
13486
|
+
}
|
|
13487
|
+
for (const a of el.animate) {
|
|
13488
|
+
const value = anim.paused && a.attributeName === "stroke-dashoffset" ? el.strokeDashoffset ?? 0 : valuesAt(a.values.split(";").map(Number), progress(a));
|
|
13489
|
+
if (a.attributeName === "stroke-opacity") frame.opacity = value;
|
|
13490
|
+
else if (a.attributeName === "stroke-width") frame.strokeWidth = value;
|
|
13491
|
+
else if (el.strokeDasharray) {
|
|
13492
|
+
const [on, off] = el.strokeDasharray.split(" ").map(Number);
|
|
13493
|
+
const period = on + off;
|
|
13494
|
+
const start = Math.floor((0 + value) / period) - 1;
|
|
13495
|
+
for (let k = start; k * period - value < measured2.length; k++) {
|
|
13496
|
+
const from = k * period - value;
|
|
13497
|
+
const to = from + on;
|
|
13498
|
+
const a0 = Math.max(from, 0);
|
|
13499
|
+
const b0 = Math.min(to, measured2.length);
|
|
13500
|
+
if (b0 > a0) frame.segments.push([round4(a0), round4(b0)]);
|
|
13501
|
+
}
|
|
13502
|
+
}
|
|
13503
|
+
}
|
|
13504
|
+
if (anim.kind === "pulse" && frame.segments.length === 0) frame.segments.push([0, render.length]);
|
|
13505
|
+
}
|
|
13506
|
+
return frame;
|
|
13507
|
+
}
|
|
13508
|
+
|
|
13509
|
+
// src/canvas/edge-animation-tools.ts
|
|
13510
|
+
var READ3 = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false };
|
|
13511
|
+
var WRITE_IDEMPOTENT2 = { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false };
|
|
13512
|
+
var EDGE_IDS = {
|
|
13513
|
+
type: "array",
|
|
13514
|
+
minItems: 1,
|
|
13515
|
+
uniqueItems: true,
|
|
13516
|
+
items: { type: "string", minLength: 1 },
|
|
13517
|
+
description: "Ids of the edges to change, from list_edge_animations. All of them change in one undo step."
|
|
13518
|
+
};
|
|
13519
|
+
var CANVAS_EDGE_ANIMATION_SCHEMA = {
|
|
13520
|
+
type: "object",
|
|
13521
|
+
properties: {
|
|
13522
|
+
kind: {
|
|
13523
|
+
type: "string",
|
|
13524
|
+
enum: CANVAS_EDGE_ANIMATION_KINDS.map((k) => k.kind),
|
|
13525
|
+
description: "What moves: dots (travelling dots), dash (marching dashes), beam (a glowing streak that shoots along), pulse (the edge glows and fades)."
|
|
13526
|
+
},
|
|
13527
|
+
speed: { type: "number", minimum: 1, maximum: 1e4, description: "Travel speed in canvas units per second (dots, dash, beam). Defaults: dots 80, dash 40, beam 200." },
|
|
13528
|
+
duration: { type: "number", minimum: 100, maximum: 6e5, description: "One cycle in ms; overrides speed. The pulse timing (default 1600)." },
|
|
13529
|
+
direction: { type: "string", enum: ["forward", "reverse", "alternate"], description: "forward = source to target (default), reverse = target to source, alternate = back and forth." },
|
|
13530
|
+
color: { type: "string", minLength: 1, description: 'CSS colour, e.g. "#38bdf8". Default: the edge stroke colour.' },
|
|
13531
|
+
size: { type: "number", minimum: 0.5, maximum: 100, description: "Dot radius, or stroke width of dashes, beams and the pulse, in canvas units. Defaults: dots 3, dash 2, beam 3, pulse 4." },
|
|
13532
|
+
count: { type: "integer", minimum: 1, maximum: 50, description: "How many dots or beams are on the edge at once. Defaults: dots 3, beam 1." },
|
|
13533
|
+
length: { type: "number", minimum: 1, maximum: 1e4, description: "Dash or beam length in canvas units. Defaults: dash 8, beam 40." },
|
|
13534
|
+
gap: { type: "number", minimum: 0, maximum: 1e4, description: "Gap between dashes in canvas units (dash). Default 6." },
|
|
13535
|
+
glow: { type: "number", minimum: 0, maximum: 50, description: "Glow (blur) radius in canvas units; 0 is none. Defaults: beam 4, pulse 3, others 0." },
|
|
13536
|
+
trackOpacity: { type: "number", minimum: 0, maximum: 1, description: "Opacity of the edge's own line under the animation, 0-1. Defaults: dash 0 (the dashes are the line), beam 0.35, others 1." },
|
|
13537
|
+
paused: { type: "boolean", description: "Hold the animation still at its first frame. Default false." }
|
|
13538
|
+
},
|
|
13539
|
+
required: ["kind"],
|
|
13540
|
+
additionalProperties: false
|
|
13541
|
+
};
|
|
13542
|
+
var labelOf3 = (edge) => typeof edge.label === "string" && edge.label !== "" ? edge.label : void 0;
|
|
13543
|
+
function createCanvasEdgeAnimationToolProvider(target, options = {}) {
|
|
13544
|
+
const readOnlyReason = () => {
|
|
13545
|
+
const ro = typeof options.readOnly === "function" ? options.readOnly() : options.readOnly;
|
|
13546
|
+
return ro ? typeof ro === "string" ? ro : "Edges are read-only on this canvas." : null;
|
|
13547
|
+
};
|
|
13548
|
+
const checkIds = (ids) => {
|
|
13549
|
+
const known = new Set(target.getEdges().map((e) => e.id));
|
|
13550
|
+
const missing = ids.filter((id) => !known.has(id));
|
|
13551
|
+
if (missing.length) {
|
|
13552
|
+
throw new Error(`No edge${missing.length > 1 ? "s" : ""} ${missing.map((id) => `"${id}"`).join(", ")} on the canvas. Call list_edge_animations for edge ids.`);
|
|
13553
|
+
}
|
|
13554
|
+
};
|
|
13555
|
+
const apply = (label, next) => {
|
|
13556
|
+
const before = /* @__PURE__ */ new Map();
|
|
13557
|
+
for (const edge of target.getEdges()) if (next.has(edge.id)) before.set(edge.id, getCanvasEdgeAnimation(edge));
|
|
13558
|
+
const write = (values) => () => target.setEdges((current) => current.map((edge) => values.has(edge.id) ? setCanvasEdgeAnimation(edge, values.get(edge.id) ?? null) : edge));
|
|
13559
|
+
if (target.history) target.history.execute({ name: label, execute: write(next), undo: write(before) });
|
|
13560
|
+
else write(next)();
|
|
13561
|
+
};
|
|
13562
|
+
const listTool = {
|
|
13563
|
+
name: "list_edge_animations",
|
|
13564
|
+
title: "List edge animations",
|
|
13565
|
+
description: "Lists the canvas edges (id, source, target, label, animation or null) and the animation kinds with their defaults, plus the readOnly reason if edges cannot change. Call it before set_edge_animation or clear_edge_animation: it is the source of edge ids.",
|
|
13566
|
+
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
13567
|
+
annotations: { title: "List edge animations", ...READ3 },
|
|
13568
|
+
handler: () => ({
|
|
13569
|
+
edges: target.getEdges().map((e) => ({
|
|
13570
|
+
id: e.id,
|
|
13571
|
+
source: e.source,
|
|
13572
|
+
target: e.target,
|
|
13573
|
+
...labelOf3(e) ? { label: labelOf3(e) } : {},
|
|
13574
|
+
animation: getCanvasEdgeAnimation(e)
|
|
13575
|
+
})),
|
|
13576
|
+
kinds: CANVAS_EDGE_ANIMATION_KINDS.map((k) => ({ kind: k.kind, title: k.title, description: k.description, defaults: k.defaults })),
|
|
13577
|
+
readOnly: readOnlyReason()
|
|
13578
|
+
})
|
|
13579
|
+
};
|
|
13580
|
+
const setTool = {
|
|
13581
|
+
name: "set_edge_animation",
|
|
13582
|
+
title: "Animate edges",
|
|
13583
|
+
description: "Sets the animation of one or more edges (replacing any they had): travelling dots, marching dashes, a beam or a pulse, with optional speed, direction, colour, size, count, length, glow and track opacity. Fields left out use the kind's defaults. One undo step. Returns the edge ids and the animation as it will play, with every default filled in.",
|
|
13584
|
+
inputSchema: {
|
|
13585
|
+
type: "object",
|
|
13586
|
+
properties: { edges: EDGE_IDS, animation: { ...CANVAS_EDGE_ANIMATION_SCHEMA, description: "The animation. Only kind is required." } },
|
|
13587
|
+
required: ["edges", "animation"],
|
|
13588
|
+
additionalProperties: false
|
|
13589
|
+
},
|
|
13590
|
+
annotations: { title: "Animate edges", ...WRITE_IDEMPOTENT2 },
|
|
13591
|
+
handler: (args) => {
|
|
13592
|
+
const reason = readOnlyReason();
|
|
13593
|
+
if (reason) throw new Error(reason);
|
|
13594
|
+
const ids = args.edges;
|
|
13595
|
+
checkIds(ids);
|
|
13596
|
+
const animation = normalizeCanvasEdgeAnimation(args.animation);
|
|
13597
|
+
apply(ids.length === 1 ? "Animate edge" : `Animate ${ids.length} edges`, new Map(ids.map((id) => [id, animation])));
|
|
13598
|
+
return { edges: ids, animation, resolved: resolveCanvasEdgeAnimation(animation) };
|
|
13599
|
+
}
|
|
13600
|
+
};
|
|
13601
|
+
const clearTool = {
|
|
13602
|
+
name: "clear_edge_animation",
|
|
13603
|
+
title: "Stop edge animations",
|
|
13604
|
+
description: "Removes the animation from one or more edges, leaving the plain edge. Edges without one are left alone. One undo step. Returns the ids cleared and those that had no animation.",
|
|
13605
|
+
inputSchema: { type: "object", properties: { edges: EDGE_IDS }, required: ["edges"], additionalProperties: false },
|
|
13606
|
+
annotations: { title: "Stop edge animations", ...WRITE_IDEMPOTENT2 },
|
|
13607
|
+
handler: (args) => {
|
|
13608
|
+
const reason = readOnlyReason();
|
|
13609
|
+
if (reason) throw new Error(reason);
|
|
13610
|
+
const ids = args.edges;
|
|
13611
|
+
checkIds(ids);
|
|
13612
|
+
const animated = new Set(target.getEdges().filter((e) => getCanvasEdgeAnimation(e) || e.data?.animation !== void 0).map((e) => e.id));
|
|
13613
|
+
const cleared = ids.filter((id) => animated.has(id));
|
|
13614
|
+
if (cleared.length) apply(cleared.length === 1 ? "Stop edge animation" : `Stop ${cleared.length} edge animations`, new Map(cleared.map((id) => [id, null])));
|
|
13615
|
+
return { cleared, unchanged: ids.filter((id) => !animated.has(id)) };
|
|
13616
|
+
}
|
|
13617
|
+
};
|
|
13618
|
+
return {
|
|
13619
|
+
id: options.id ?? "canvas-edge-animation",
|
|
13620
|
+
namespace: options.namespace,
|
|
13621
|
+
title: "Canvas edge animation",
|
|
13622
|
+
description: "Animate canvas edges with travelling dots, marching dashes, beams or a pulse.",
|
|
13623
|
+
skill: "pixerate-canvas",
|
|
13624
|
+
tools: () => readOnlyReason() ? [listTool] : [listTool, setTool, clearTool],
|
|
13625
|
+
subscribe: options.subscribe
|
|
13626
|
+
};
|
|
13627
|
+
}
|
|
12969
13628
|
export {
|
|
12970
13629
|
ANIMATABLE_PROPERTIES,
|
|
12971
13630
|
ANIMATION_DATA_KEY,
|
|
@@ -12980,6 +13639,8 @@ export {
|
|
|
12980
13639
|
CANVAS_CAPTURE_OVERLAY_SELECTORS,
|
|
12981
13640
|
CANVAS_CAPTURE_VIEWPORT_SELECTOR,
|
|
12982
13641
|
CANVAS_COMPONENT_DRAG_TYPE,
|
|
13642
|
+
CANVAS_EDGE_ANIMATION_KINDS,
|
|
13643
|
+
CANVAS_EDGE_ANIMATION_SCHEMA,
|
|
12983
13644
|
CANVAS_EFFECT_MISSING_PATTERN,
|
|
12984
13645
|
CANVAS_EFFECT_PLACES,
|
|
12985
13646
|
CANVAS_GRADIENT_PRESETS,
|
|
@@ -13113,6 +13774,7 @@ export {
|
|
|
13113
13774
|
createCanvasComponent,
|
|
13114
13775
|
createCanvasComponentsController,
|
|
13115
13776
|
createCanvasDrawController,
|
|
13777
|
+
createCanvasEdgeAnimationToolProvider,
|
|
13116
13778
|
createCanvasEditorDocument,
|
|
13117
13779
|
createCanvasEffectsController,
|
|
13118
13780
|
createCanvasEffectsToolProvider,
|
|
@@ -13211,6 +13873,9 @@ export {
|
|
|
13211
13873
|
getBindableTokenProperties,
|
|
13212
13874
|
getBrushedShapeSvg,
|
|
13213
13875
|
getCanvasComponentDragId,
|
|
13876
|
+
getCanvasEdgeAnimation,
|
|
13877
|
+
getCanvasEdgeAnimationRender,
|
|
13878
|
+
getCanvasEdgePath,
|
|
13214
13879
|
getCanvasTokenProperty,
|
|
13215
13880
|
getCenteredNodePosition,
|
|
13216
13881
|
getChildLayout,
|
|
@@ -13312,6 +13977,7 @@ export {
|
|
|
13312
13977
|
measureBetween,
|
|
13313
13978
|
measureLabel,
|
|
13314
13979
|
measureSelection,
|
|
13980
|
+
measureSvgPath,
|
|
13315
13981
|
measureToLine,
|
|
13316
13982
|
mergeNodeUpdates,
|
|
13317
13983
|
mermaidEdgeDash,
|
|
@@ -13338,6 +14004,7 @@ export {
|
|
|
13338
14004
|
nearestHandlePositions,
|
|
13339
14005
|
nodeSizeOf,
|
|
13340
14006
|
normalizeBezierPath,
|
|
14007
|
+
normalizeCanvasEdgeAnimation,
|
|
13341
14008
|
normalizeKeyframes,
|
|
13342
14009
|
offsetPath,
|
|
13343
14010
|
offsetShapeNodes,
|
|
@@ -13406,6 +14073,7 @@ export {
|
|
|
13406
14073
|
resetCanvasInstanceOverrides,
|
|
13407
14074
|
resizeHandleAxes,
|
|
13408
14075
|
resolveCanvasBrushSpec,
|
|
14076
|
+
resolveCanvasEdgeAnimation,
|
|
13409
14077
|
resolveCanvasGradient,
|
|
13410
14078
|
resolveCanvasTokens,
|
|
13411
14079
|
resolveEasing,
|
|
@@ -13419,6 +14087,7 @@ export {
|
|
|
13419
14087
|
runCanvasNodeEffect,
|
|
13420
14088
|
runMultiNodeTransition,
|
|
13421
14089
|
sampleAnimationFrames,
|
|
14090
|
+
sampleCanvasEdgeAnimation,
|
|
13422
14091
|
sanitizeCanvasDocument,
|
|
13423
14092
|
scaleKeyframes,
|
|
13424
14093
|
sceneColor,
|
|
@@ -13435,6 +14104,7 @@ export {
|
|
|
13435
14104
|
sequenceScene,
|
|
13436
14105
|
serializeCanvasNodes,
|
|
13437
14106
|
setCanvasComponentProperty,
|
|
14107
|
+
setCanvasEdgeAnimation,
|
|
13438
14108
|
setCanvasInstanceProperties,
|
|
13439
14109
|
setCanvasTokenMode,
|
|
13440
14110
|
setCanvasVariantProperties,
|