sketchmark 1.1.6 → 1.2.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/README.md +193 -147
- package/dist/animation/index.d.ts +2 -0
- package/dist/animation/index.d.ts.map +1 -1
- package/dist/ast/types.d.ts +3 -0
- package/dist/ast/types.d.ts.map +1 -1
- package/dist/index.cjs +228 -95
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +228 -95
- package/dist/index.js.map +1 -1
- package/dist/layout/index.d.ts +8 -0
- package/dist/layout/index.d.ts.map +1 -1
- package/dist/parser/index.d.ts +2 -1
- package/dist/parser/index.d.ts.map +1 -1
- package/dist/parser/tokenizer.d.ts.map +1 -1
- package/dist/plugins.d.ts +12 -0
- package/dist/plugins.d.ts.map +1 -0
- package/dist/render.d.ts +2 -0
- package/dist/render.d.ts.map +1 -1
- package/dist/renderer/shared.d.ts +1 -1
- package/dist/renderer/shared.d.ts.map +1 -1
- package/dist/renderer/svg/index.d.ts.map +1 -1
- package/dist/scene/index.d.ts +2 -0
- package/dist/scene/index.d.ts.map +1 -1
- package/dist/sketchmark.iife.js +228 -95
- package/dist/ui/canvas.d.ts +2 -0
- package/dist/ui/canvas.d.ts.map +1 -1
- package/dist/ui/embed.d.ts +2 -0
- package/dist/ui/embed.d.ts.map +1 -1
- package/package.json +18 -1
package/dist/index.cjs
CHANGED
|
@@ -146,10 +146,16 @@ function tokenize$1(src) {
|
|
|
146
146
|
val += "\n";
|
|
147
147
|
else if (esc === "t")
|
|
148
148
|
val += "\t";
|
|
149
|
+
else if (esc === "r")
|
|
150
|
+
val += "\r";
|
|
149
151
|
else if (esc === "\\")
|
|
150
152
|
val += "\\";
|
|
153
|
+
else if (esc === q)
|
|
154
|
+
val += q;
|
|
155
|
+
else if (esc)
|
|
156
|
+
val += `\\${esc}`;
|
|
151
157
|
else
|
|
152
|
-
val +=
|
|
158
|
+
val += "\\";
|
|
153
159
|
}
|
|
154
160
|
else
|
|
155
161
|
val += src[i];
|
|
@@ -226,6 +232,47 @@ function tokenize$1(src) {
|
|
|
226
232
|
return tokens;
|
|
227
233
|
}
|
|
228
234
|
|
|
235
|
+
function pluginMessage(plugin, stage, error) {
|
|
236
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
237
|
+
return `Plugin "${plugin.name}" ${stage} failed: ${detail}`;
|
|
238
|
+
}
|
|
239
|
+
function applyPluginPreprocessors(source, plugins = []) {
|
|
240
|
+
let nextSource = source;
|
|
241
|
+
for (const plugin of plugins) {
|
|
242
|
+
if (!plugin.preprocess)
|
|
243
|
+
continue;
|
|
244
|
+
try {
|
|
245
|
+
const transformed = plugin.preprocess(nextSource);
|
|
246
|
+
if (typeof transformed !== "string") {
|
|
247
|
+
throw new Error("preprocess must return a string");
|
|
248
|
+
}
|
|
249
|
+
nextSource = transformed;
|
|
250
|
+
}
|
|
251
|
+
catch (error) {
|
|
252
|
+
throw new Error(pluginMessage(plugin, "preprocess", error));
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return nextSource;
|
|
256
|
+
}
|
|
257
|
+
function applyPluginAstTransforms(ast, plugins = []) {
|
|
258
|
+
let nextAst = ast;
|
|
259
|
+
for (const plugin of plugins) {
|
|
260
|
+
if (!plugin.transformAst)
|
|
261
|
+
continue;
|
|
262
|
+
try {
|
|
263
|
+
const transformed = plugin.transformAst(nextAst);
|
|
264
|
+
if (!transformed || transformed.kind !== "diagram") {
|
|
265
|
+
throw new Error('transformAst must return a DiagramAST with kind="diagram"');
|
|
266
|
+
}
|
|
267
|
+
nextAst = transformed;
|
|
268
|
+
}
|
|
269
|
+
catch (error) {
|
|
270
|
+
throw new Error(pluginMessage(plugin, "transformAst", error));
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return nextAst;
|
|
274
|
+
}
|
|
275
|
+
|
|
229
276
|
// ============================================================
|
|
230
277
|
// sketchmark - Parser (Tokens -> DiagramAST)
|
|
231
278
|
// ============================================================
|
|
@@ -312,9 +359,10 @@ function isValueToken(t) {
|
|
|
312
359
|
function isPropKeyToken(t) {
|
|
313
360
|
return !!t && (t.type === "IDENT" || t.type === "KEYWORD");
|
|
314
361
|
}
|
|
315
|
-
function parse(src) {
|
|
362
|
+
function parse(src, options = {}) {
|
|
316
363
|
resetUid();
|
|
317
|
-
const
|
|
364
|
+
const preparedSource = applyPluginPreprocessors(src, options.plugins);
|
|
365
|
+
const tokens = tokenize$1(preparedSource).filter((t) => t.type !== "NEWLINE" || t.value === "\n");
|
|
318
366
|
const flat = [];
|
|
319
367
|
let lastNL = false;
|
|
320
368
|
for (const t of tokens) {
|
|
@@ -497,6 +545,7 @@ function parse(src) {
|
|
|
497
545
|
const toks = lineTokens();
|
|
498
546
|
const id = requireExplicitId(keywordTok, toks);
|
|
499
547
|
const props = parseSimpleProps(toks, 1);
|
|
548
|
+
const meta = extractNodeMeta(props);
|
|
500
549
|
const node = {
|
|
501
550
|
kind: "node",
|
|
502
551
|
id,
|
|
@@ -511,6 +560,7 @@ function parse(src) {
|
|
|
511
560
|
...(props.dy ? { dy: parseFloat(props.dy) } : {}),
|
|
512
561
|
...(props.factor ? { factor: parseFloat(props.factor) } : {}),
|
|
513
562
|
...(props.theme ? { theme: props.theme } : {}),
|
|
563
|
+
...(meta ? { meta } : {}),
|
|
514
564
|
style: propsToStyle(props),
|
|
515
565
|
};
|
|
516
566
|
if (props.url)
|
|
@@ -535,12 +585,14 @@ function parse(src) {
|
|
|
535
585
|
j = 2;
|
|
536
586
|
}
|
|
537
587
|
Object.assign(props, parseSimpleProps(toks, j));
|
|
588
|
+
const meta = extractNodeMeta(props);
|
|
538
589
|
return {
|
|
539
590
|
kind: "node",
|
|
540
591
|
id,
|
|
541
592
|
shape: "note",
|
|
542
593
|
label: (props.label ?? "").replace(/\\n/g, "\n"),
|
|
543
594
|
theme: props.theme,
|
|
595
|
+
...(meta ? { meta } : {}),
|
|
544
596
|
style: propsToStyle(props),
|
|
545
597
|
...(props.width ? { width: parseFloat(props.width) } : {}),
|
|
546
598
|
...(props.height ? { height: parseFloat(props.height) } : {}),
|
|
@@ -552,6 +604,13 @@ function parse(src) {
|
|
|
552
604
|
...(props.factor ? { factor: parseFloat(props.factor) } : {}),
|
|
553
605
|
};
|
|
554
606
|
}
|
|
607
|
+
function extractNodeMeta(props) {
|
|
608
|
+
const meta = {};
|
|
609
|
+
if (props["animation-parent"]) {
|
|
610
|
+
meta.animationParent = props["animation-parent"];
|
|
611
|
+
}
|
|
612
|
+
return Object.keys(meta).length ? meta : undefined;
|
|
613
|
+
}
|
|
555
614
|
function parseGroup() {
|
|
556
615
|
const keywordTok = cur();
|
|
557
616
|
skip();
|
|
@@ -624,6 +683,8 @@ function parse(src) {
|
|
|
624
683
|
to: toTok.value,
|
|
625
684
|
connector: connector,
|
|
626
685
|
label: props.label,
|
|
686
|
+
fromAnchor: props["anchor-from"],
|
|
687
|
+
toAnchor: props["anchor-to"],
|
|
627
688
|
dashed,
|
|
628
689
|
bidirectional,
|
|
629
690
|
style: propsToStyle(props),
|
|
@@ -937,6 +998,7 @@ function parse(src) {
|
|
|
937
998
|
registerAuthoredId(grp.id, "group", t);
|
|
938
999
|
if (isBare) {
|
|
939
1000
|
grp.label = "";
|
|
1001
|
+
grp.padding = grp.padding ?? 0;
|
|
940
1002
|
grp.style = {
|
|
941
1003
|
...grp.style,
|
|
942
1004
|
fill: grp.style?.fill ?? "none",
|
|
@@ -1107,7 +1169,7 @@ function parse(src) {
|
|
|
1107
1169
|
node.style = { ...ast.styles[node.id], ...node.style };
|
|
1108
1170
|
}
|
|
1109
1171
|
}
|
|
1110
|
-
return ast;
|
|
1172
|
+
return applyPluginAstTransforms(ast, options.plugins);
|
|
1111
1173
|
}
|
|
1112
1174
|
|
|
1113
1175
|
// ============================================================
|
|
@@ -3554,6 +3616,8 @@ function buildSceneGraph(ast) {
|
|
|
3554
3616
|
to: e.to,
|
|
3555
3617
|
connector: e.connector,
|
|
3556
3618
|
label: e.label,
|
|
3619
|
+
fromAnchor: e.fromAnchor,
|
|
3620
|
+
toAnchor: e.toAnchor,
|
|
3557
3621
|
dashed: e.dashed ?? false,
|
|
3558
3622
|
bidirectional: e.bidirectional ?? false,
|
|
3559
3623
|
style: e.style ?? {},
|
|
@@ -4131,28 +4195,13 @@ function connMeta(connector) {
|
|
|
4131
4195
|
return { arrowAt: "start", dashed };
|
|
4132
4196
|
return { arrowAt: "end", dashed };
|
|
4133
4197
|
}
|
|
4134
|
-
// ── Generic rect connection point ────────────────────────────────────────
|
|
4135
|
-
function rectConnPoint$1(rx, ry, rw, rh, ox, oy) {
|
|
4136
|
-
const cx = rx + rw / 2, cy = ry + rh / 2;
|
|
4137
|
-
const dx = ox - cx, dy = oy - cy;
|
|
4138
|
-
if (Math.abs(dx) < 0.01 && Math.abs(dy) < 0.01)
|
|
4139
|
-
return [cx, cy];
|
|
4140
|
-
const hw = rw / 2 - 2, hh = rh / 2 - 2;
|
|
4141
|
-
const tx = Math.abs(dx) > 0.01 ? hw / Math.abs(dx) : 1e9;
|
|
4142
|
-
const ty = Math.abs(dy) > 0.01 ? hh / Math.abs(dy) : 1e9;
|
|
4143
|
-
const t = Math.min(tx, ty);
|
|
4144
|
-
return [cx + t * dx, cy + t * dy];
|
|
4145
|
-
}
|
|
4146
4198
|
// ── Resolve an endpoint entity by ID across all maps ─────────────────────
|
|
4147
4199
|
function resolveEndpoint(id, nm, tm, gm, cm) {
|
|
4148
4200
|
return nm.get(id) ?? tm.get(id) ?? gm.get(id) ?? cm.get(id) ?? null;
|
|
4149
4201
|
}
|
|
4150
4202
|
// ── Get connection point for any entity ──────────────────────────────────
|
|
4151
|
-
function getConnPoint(src, dstCX, dstCY) {
|
|
4152
|
-
|
|
4153
|
-
return connPoint(src, { x: dstCX - 1, y: dstCY - 1, w: 2, h: 2});
|
|
4154
|
-
}
|
|
4155
|
-
return rectConnPoint$1(src.x, src.y, src.w, src.h, dstCX, dstCY);
|
|
4203
|
+
function getConnPoint(src, dstCX, dstCY, anchor) {
|
|
4204
|
+
return anchoredConnPoint(src, anchor, dstCX, dstCY);
|
|
4156
4205
|
}
|
|
4157
4206
|
// ── Group depth (for paint order) ────────────────────────────────────────
|
|
4158
4207
|
function groupDepth(g, gm) {
|
|
@@ -4587,6 +4636,50 @@ function connPoint(n, other) {
|
|
|
4587
4636
|
const t = Math.min(tx, ty);
|
|
4588
4637
|
return [cx + t * dx, cy + t * dy];
|
|
4589
4638
|
}
|
|
4639
|
+
function clampInset(value) {
|
|
4640
|
+
return Math.max(2, value);
|
|
4641
|
+
}
|
|
4642
|
+
function anchoredConnPoint(entity, anchor, otherCX, otherCY) {
|
|
4643
|
+
if (!anchor) {
|
|
4644
|
+
if (entity.shape && otherCX != null && otherCY != null) {
|
|
4645
|
+
return connPoint(entity, { x: otherCX - 1, y: otherCY - 1, w: 2, h: 2});
|
|
4646
|
+
}
|
|
4647
|
+
if (otherCX != null && otherCY != null) {
|
|
4648
|
+
return rectConnPoint(entity.x, entity.y, entity.w, entity.h, otherCX, otherCY);
|
|
4649
|
+
}
|
|
4650
|
+
return [entity.x + entity.w / 2, entity.y + entity.h / 2];
|
|
4651
|
+
}
|
|
4652
|
+
const insetX = clampInset(Math.min(10, entity.w / 2));
|
|
4653
|
+
const insetY = clampInset(Math.min(10, entity.h / 2));
|
|
4654
|
+
const left = entity.x + insetX;
|
|
4655
|
+
const right = entity.x + entity.w - insetX;
|
|
4656
|
+
const top = entity.y + insetY;
|
|
4657
|
+
const bottom = entity.y + entity.h - insetY;
|
|
4658
|
+
const cx = entity.x + entity.w / 2;
|
|
4659
|
+
const cy = entity.y + entity.h / 2;
|
|
4660
|
+
switch (anchor) {
|
|
4661
|
+
case "top":
|
|
4662
|
+
return [cx, top];
|
|
4663
|
+
case "right":
|
|
4664
|
+
return [right, cy];
|
|
4665
|
+
case "bottom":
|
|
4666
|
+
return [cx, bottom];
|
|
4667
|
+
case "left":
|
|
4668
|
+
return [left, cy];
|
|
4669
|
+
case "center":
|
|
4670
|
+
return [cx, cy];
|
|
4671
|
+
case "top-left":
|
|
4672
|
+
return [left, top];
|
|
4673
|
+
case "top-right":
|
|
4674
|
+
return [right, top];
|
|
4675
|
+
case "bottom-left":
|
|
4676
|
+
return [left, bottom];
|
|
4677
|
+
case "bottom-right":
|
|
4678
|
+
return [right, bottom];
|
|
4679
|
+
default:
|
|
4680
|
+
return [cx, cy];
|
|
4681
|
+
}
|
|
4682
|
+
}
|
|
4590
4683
|
function rectConnPoint(rx, ry, rw, rh, ox, oy) {
|
|
4591
4684
|
const cx = rx + rw / 2, cy = ry + rh / 2;
|
|
4592
4685
|
const dx = ox - cx, dy = oy - cy;
|
|
@@ -4618,17 +4711,6 @@ function routeEdges(sg) {
|
|
|
4618
4711
|
return c;
|
|
4619
4712
|
return null;
|
|
4620
4713
|
}
|
|
4621
|
-
function connPt(src, dstCX, dstCY) {
|
|
4622
|
-
// SceneNode has a .shape field; use the existing connPoint for it
|
|
4623
|
-
if ("shape" in src && src.shape) {
|
|
4624
|
-
return connPoint(src, {
|
|
4625
|
-
x: dstCX - 1,
|
|
4626
|
-
y: dstCY - 1,
|
|
4627
|
-
w: 2,
|
|
4628
|
-
h: 2});
|
|
4629
|
-
}
|
|
4630
|
-
return rectConnPoint(src.x, src.y, src.w, src.h, dstCX, dstCY);
|
|
4631
|
-
}
|
|
4632
4714
|
for (const e of sg.edges) {
|
|
4633
4715
|
const src = resolve(e.from);
|
|
4634
4716
|
const dst = resolve(e.to);
|
|
@@ -4638,7 +4720,10 @@ function routeEdges(sg) {
|
|
|
4638
4720
|
}
|
|
4639
4721
|
const dstCX = dst.x + dst.w / 2, dstCY = dst.y + dst.h / 2;
|
|
4640
4722
|
const srcCX = src.x + src.w / 2, srcCY = src.y + src.h / 2;
|
|
4641
|
-
e.points = [
|
|
4723
|
+
e.points = [
|
|
4724
|
+
anchoredConnPoint(src, e.fromAnchor, dstCX, dstCY),
|
|
4725
|
+
anchoredConnPoint(dst, e.toAnchor, srcCX, srcCY),
|
|
4726
|
+
];
|
|
4642
4727
|
}
|
|
4643
4728
|
}
|
|
4644
4729
|
function computeBounds(sg, margin) {
|
|
@@ -7779,8 +7864,8 @@ function renderToSVG(sg, container, options = {}) {
|
|
|
7779
7864
|
continue;
|
|
7780
7865
|
const dstCX = dst.x + dst.w / 2, dstCY = dst.y + dst.h / 2;
|
|
7781
7866
|
const srcCX = src.x + src.w / 2, srcCY = src.y + src.h / 2;
|
|
7782
|
-
const [x1, y1] = getConnPoint(src, dstCX, dstCY);
|
|
7783
|
-
const [x2, y2] = getConnPoint(dst, srcCX, srcCY);
|
|
7867
|
+
const [x1, y1] = getConnPoint(src, dstCX, dstCY, e.fromAnchor);
|
|
7868
|
+
const [x2, y2] = getConnPoint(dst, srcCX, srcCY, e.toAnchor);
|
|
7784
7869
|
const eg = mkGroup(`edge-${e.from}-${e.to}`, "eg");
|
|
7785
7870
|
if (e.style?.opacity != null)
|
|
7786
7871
|
eg.setAttribute("opacity", String(e.style.opacity));
|
|
@@ -7857,6 +7942,8 @@ function renderToSVG(sg, container, options = {}) {
|
|
|
7857
7942
|
ng.dataset.h = String(n.h);
|
|
7858
7943
|
if (n.pathData)
|
|
7859
7944
|
ng.dataset.pathData = n.pathData;
|
|
7945
|
+
if (n.meta?.animationParent)
|
|
7946
|
+
ng.dataset.animationParent = n.meta.animationParent;
|
|
7860
7947
|
if (n.style?.opacity != null)
|
|
7861
7948
|
ng.setAttribute("opacity", String(n.style.opacity));
|
|
7862
7949
|
// ── Static transform (deg, dx, dy, factor) ──────────
|
|
@@ -8510,8 +8597,8 @@ function renderToCanvas(sg, canvas, options = {}) {
|
|
|
8510
8597
|
continue;
|
|
8511
8598
|
const dstCX = dst.x + dst.w / 2, dstCY = dst.y + dst.h / 2;
|
|
8512
8599
|
const srcCX = src.x + src.w / 2, srcCY = src.y + src.h / 2;
|
|
8513
|
-
const [x1, y1] = getConnPoint(src, dstCX, dstCY);
|
|
8514
|
-
const [x2, y2] = getConnPoint(dst, srcCX, srcCY);
|
|
8600
|
+
const [x1, y1] = getConnPoint(src, dstCX, dstCY, e.fromAnchor);
|
|
8601
|
+
const [x2, y2] = getConnPoint(dst, srcCX, srcCY, e.toAnchor);
|
|
8515
8602
|
if (e.style?.opacity != null)
|
|
8516
8603
|
ctx.globalAlpha = Number(e.style.opacity);
|
|
8517
8604
|
const ecol = String(e.style?.stroke ?? palette.edgeStroke);
|
|
@@ -9368,6 +9455,13 @@ class AnimationController {
|
|
|
9368
9455
|
this.drawTargetNodes.delete(`node-${s.target}`);
|
|
9369
9456
|
}
|
|
9370
9457
|
}
|
|
9458
|
+
this._relatedElementIdsByPrimaryId = this._buildRelatedElementIndex();
|
|
9459
|
+
for (const nodeId of Array.from(this.drawTargetNodes)) {
|
|
9460
|
+
const relatedIds = this._relatedElementIdsByPrimaryId.get(nodeId);
|
|
9461
|
+
if (!relatedIds)
|
|
9462
|
+
continue;
|
|
9463
|
+
relatedIds.forEach((id) => this.drawTargetNodes.add(id));
|
|
9464
|
+
}
|
|
9371
9465
|
this._drawStepIndexByElementId = this._buildDrawStepIndex();
|
|
9372
9466
|
const { parentGroupByElementId, groupDescendantIds } = this._buildGroupVisibilityIndex();
|
|
9373
9467
|
this._parentGroupByElementId = parentGroupByElementId;
|
|
@@ -9398,10 +9492,30 @@ class AnimationController {
|
|
|
9398
9492
|
const el = resolveNonEdgeDrawEl(this.svg, step.target);
|
|
9399
9493
|
if (el && !drawStepIndexByElementId.has(el.id)) {
|
|
9400
9494
|
drawStepIndexByElementId.set(el.id, stepIndex);
|
|
9495
|
+
this._relatedElementIdsByPrimaryId.get(el.id)?.forEach((relatedId) => {
|
|
9496
|
+
if (!drawStepIndexByElementId.has(relatedId)) {
|
|
9497
|
+
drawStepIndexByElementId.set(relatedId, stepIndex);
|
|
9498
|
+
}
|
|
9499
|
+
});
|
|
9401
9500
|
}
|
|
9402
9501
|
});
|
|
9403
9502
|
return drawStepIndexByElementId;
|
|
9404
9503
|
}
|
|
9504
|
+
_buildRelatedElementIndex() {
|
|
9505
|
+
const relatedElementIdsByPrimaryId = new Map();
|
|
9506
|
+
this.svg.querySelectorAll(POSITIONABLE_SELECTOR).forEach((el) => {
|
|
9507
|
+
const animationParent = el.dataset.animationParent;
|
|
9508
|
+
if (!animationParent)
|
|
9509
|
+
return;
|
|
9510
|
+
const primaryEl = resolveNonEdgeDrawEl(this.svg, animationParent);
|
|
9511
|
+
if (!primaryEl || primaryEl.id === el.id)
|
|
9512
|
+
return;
|
|
9513
|
+
const related = relatedElementIdsByPrimaryId.get(primaryEl.id) ?? new Set();
|
|
9514
|
+
related.add(el.id);
|
|
9515
|
+
relatedElementIdsByPrimaryId.set(primaryEl.id, related);
|
|
9516
|
+
});
|
|
9517
|
+
return relatedElementIdsByPrimaryId;
|
|
9518
|
+
}
|
|
9405
9519
|
_buildGroupVisibilityIndex() {
|
|
9406
9520
|
const parentGroupByElementId = new Map();
|
|
9407
9521
|
const directChildIdsByGroup = new Map();
|
|
@@ -9480,10 +9594,18 @@ class AnimationController {
|
|
|
9480
9594
|
const el = resolveEl(this.svg, target);
|
|
9481
9595
|
if (!el)
|
|
9482
9596
|
return [];
|
|
9483
|
-
if (!el.id.startsWith("group-"))
|
|
9484
|
-
|
|
9597
|
+
if (!el.id.startsWith("group-")) {
|
|
9598
|
+
const ids = new Set([el.id]);
|
|
9599
|
+
this._relatedElementIdsByPrimaryId.get(el.id)?.forEach((id) => ids.add(id));
|
|
9600
|
+
return Array.from(ids)
|
|
9601
|
+
.map((id) => getEl(this.svg, id))
|
|
9602
|
+
.filter((candidate) => candidate != null);
|
|
9603
|
+
}
|
|
9485
9604
|
const ids = new Set([el.id]);
|
|
9486
9605
|
this._groupDescendantIds.get(el.id)?.forEach((id) => ids.add(id));
|
|
9606
|
+
Array.from(ids).forEach((id) => {
|
|
9607
|
+
this._relatedElementIdsByPrimaryId.get(id)?.forEach((relatedId) => ids.add(relatedId));
|
|
9608
|
+
});
|
|
9487
9609
|
return Array.from(ids)
|
|
9488
9610
|
.map((id) => getEl(this.svg, id))
|
|
9489
9611
|
.filter((candidate) => candidate != null);
|
|
@@ -9892,9 +10014,11 @@ class AnimationController {
|
|
|
9892
10014
|
// ── highlight ────────────────────────────────────────────
|
|
9893
10015
|
_doHighlight(target) {
|
|
9894
10016
|
this.svg
|
|
9895
|
-
.querySelectorAll(".ng.hl, .tg.hl, .ntg.hl, .cg.hl, .eg.hl")
|
|
10017
|
+
.querySelectorAll(".ng.hl, .gg.hl, .tg.hl, .ntg.hl, .cg.hl, .mdg.hl, .eg.hl")
|
|
9896
10018
|
.forEach((e) => e.classList.remove("hl"));
|
|
9897
|
-
|
|
10019
|
+
for (const el of this._resolveCascadeTargets(target)) {
|
|
10020
|
+
el.classList.add("hl");
|
|
10021
|
+
}
|
|
9898
10022
|
}
|
|
9899
10023
|
// ── fade / unfade ─────────────────────────────────────────
|
|
9900
10024
|
_doFade(target, doFade) {
|
|
@@ -9930,8 +10054,8 @@ class AnimationController {
|
|
|
9930
10054
|
}
|
|
9931
10055
|
// ── move ──────────────────────────────────────────────────
|
|
9932
10056
|
_doMove(target, step, silent) {
|
|
9933
|
-
const
|
|
9934
|
-
if (!
|
|
10057
|
+
const targets = this._resolveCascadeTargets(target);
|
|
10058
|
+
if (!targets.length)
|
|
9935
10059
|
return;
|
|
9936
10060
|
const cur = this._transforms.get(target) ?? {
|
|
9937
10061
|
tx: 0,
|
|
@@ -9944,12 +10068,14 @@ class AnimationController {
|
|
|
9944
10068
|
tx: cur.tx + (step.dx ?? 0),
|
|
9945
10069
|
ty: cur.ty + (step.dy ?? 0),
|
|
9946
10070
|
});
|
|
9947
|
-
|
|
10071
|
+
for (const el of targets) {
|
|
10072
|
+
this._writeTransform(el, target, silent, step.duration ?? 420);
|
|
10073
|
+
}
|
|
9948
10074
|
}
|
|
9949
10075
|
// ── scale ─────────────────────────────────────────────────
|
|
9950
10076
|
_doScale(target, step, silent) {
|
|
9951
|
-
const
|
|
9952
|
-
if (!
|
|
10077
|
+
const targets = this._resolveCascadeTargets(target);
|
|
10078
|
+
if (!targets.length)
|
|
9953
10079
|
return;
|
|
9954
10080
|
const cur = this._transforms.get(target) ?? {
|
|
9955
10081
|
tx: 0,
|
|
@@ -9958,12 +10084,14 @@ class AnimationController {
|
|
|
9958
10084
|
rotate: 0,
|
|
9959
10085
|
};
|
|
9960
10086
|
this._transforms.set(target, { ...cur, scale: step.factor ?? 1 });
|
|
9961
|
-
|
|
10087
|
+
for (const el of targets) {
|
|
10088
|
+
this._writeTransform(el, target, silent, step.duration ?? 350);
|
|
10089
|
+
}
|
|
9962
10090
|
}
|
|
9963
10091
|
// ── rotate ────────────────────────────────────────────────
|
|
9964
10092
|
_doRotate(target, step, silent) {
|
|
9965
|
-
const
|
|
9966
|
-
if (!
|
|
10093
|
+
const targets = this._resolveCascadeTargets(target);
|
|
10094
|
+
if (!targets.length)
|
|
9967
10095
|
return;
|
|
9968
10096
|
const cur = this._transforms.get(target) ?? {
|
|
9969
10097
|
tx: 0,
|
|
@@ -9975,7 +10103,9 @@ class AnimationController {
|
|
|
9975
10103
|
...cur,
|
|
9976
10104
|
rotate: cur.rotate + (step.deg ?? 0),
|
|
9977
10105
|
});
|
|
9978
|
-
|
|
10106
|
+
for (const el of targets) {
|
|
10107
|
+
this._writeTransform(el, target, silent, step.duration ?? 400);
|
|
10108
|
+
}
|
|
9979
10109
|
}
|
|
9980
10110
|
_doDraw(step, silent) {
|
|
9981
10111
|
const { target } = step;
|
|
@@ -10119,18 +10249,20 @@ class AnimationController {
|
|
|
10119
10249
|
return;
|
|
10120
10250
|
}
|
|
10121
10251
|
// ── Node draw ──────────────────────────────────────
|
|
10122
|
-
const
|
|
10123
|
-
if (!
|
|
10252
|
+
const nodeEls = this._resolveCascadeTargets(target).filter((el) => el.classList.contains("ng"));
|
|
10253
|
+
if (!nodeEls.length)
|
|
10124
10254
|
return;
|
|
10125
|
-
|
|
10126
|
-
|
|
10127
|
-
|
|
10128
|
-
|
|
10129
|
-
|
|
10130
|
-
|
|
10131
|
-
|
|
10255
|
+
for (const nodeEl of nodeEls) {
|
|
10256
|
+
showDrawEl(nodeEl);
|
|
10257
|
+
if (silent) {
|
|
10258
|
+
revealNodeInstant(nodeEl);
|
|
10259
|
+
}
|
|
10260
|
+
else {
|
|
10261
|
+
if (!nodeGuidePathEl(nodeEl) && !nodeEl.querySelector("path")?.style.strokeDasharray) {
|
|
10262
|
+
prepareNodeForDraw(nodeEl);
|
|
10263
|
+
}
|
|
10264
|
+
animateNodeDraw(nodeEl, step.duration ?? ANIMATION.nodeStrokeDur, step.duration ?? ANIMATION.textRevealMs);
|
|
10132
10265
|
}
|
|
10133
|
-
animateNodeDraw(nodeEl, step.duration ?? ANIMATION.nodeStrokeDur, step.duration ?? ANIMATION.textRevealMs);
|
|
10134
10266
|
}
|
|
10135
10267
|
}
|
|
10136
10268
|
// ── erase ─────────────────────────────────────────────────
|
|
@@ -10151,45 +10283,44 @@ class AnimationController {
|
|
|
10151
10283
|
}
|
|
10152
10284
|
// ── pulse ─────────────────────────────────────────────────
|
|
10153
10285
|
_doPulse(target, duration = 500) {
|
|
10154
|
-
|
|
10155
|
-
|
|
10156
|
-
|
|
10157
|
-
|
|
10158
|
-
|
|
10286
|
+
for (const el of this._resolveCascadeTargets(target)) {
|
|
10287
|
+
el.animate([
|
|
10288
|
+
{ filter: "brightness(1)" },
|
|
10289
|
+
{ filter: "brightness(1.6)" },
|
|
10290
|
+
{ filter: "brightness(1)" },
|
|
10291
|
+
], { duration, iterations: 3 });
|
|
10292
|
+
}
|
|
10159
10293
|
}
|
|
10160
10294
|
// ── color ─────────────────────────────────────────────────
|
|
10161
10295
|
_doColor(target, color) {
|
|
10162
10296
|
if (!color)
|
|
10163
10297
|
return;
|
|
10164
|
-
const el
|
|
10165
|
-
|
|
10166
|
-
|
|
10167
|
-
|
|
10168
|
-
|
|
10169
|
-
|
|
10170
|
-
|
|
10171
|
-
|
|
10172
|
-
|
|
10173
|
-
|
|
10174
|
-
|
|
10175
|
-
|
|
10176
|
-
|
|
10177
|
-
|
|
10178
|
-
|
|
10179
|
-
|
|
10180
|
-
|
|
10181
|
-
|
|
10182
|
-
|
|
10183
|
-
|
|
10184
|
-
if (attrFill === null && c.tagName === "path")
|
|
10185
|
-
return;
|
|
10186
|
-
c.style.fill = color;
|
|
10187
|
-
hit = true;
|
|
10188
|
-
});
|
|
10189
|
-
if (!hit) {
|
|
10190
|
-
el.querySelectorAll("text").forEach((t) => {
|
|
10191
|
-
t.style.fill = color;
|
|
10298
|
+
for (const el of this._resolveCascadeTargets(target)) {
|
|
10299
|
+
if (parseEdgeTarget(target)) {
|
|
10300
|
+
el.querySelectorAll("path, line, polyline").forEach((p) => {
|
|
10301
|
+
p.style.stroke = color;
|
|
10302
|
+
});
|
|
10303
|
+
el.querySelectorAll("polygon").forEach((p) => {
|
|
10304
|
+
p.style.fill = color;
|
|
10305
|
+
p.style.stroke = color;
|
|
10306
|
+
});
|
|
10307
|
+
continue;
|
|
10308
|
+
}
|
|
10309
|
+
let hit = false;
|
|
10310
|
+
el.querySelectorAll("path, rect, ellipse, polygon").forEach((c) => {
|
|
10311
|
+
const attrFill = c.getAttribute("fill");
|
|
10312
|
+
if (attrFill === "none")
|
|
10313
|
+
return;
|
|
10314
|
+
if (attrFill === null && c.tagName === "path")
|
|
10315
|
+
return;
|
|
10316
|
+
c.style.fill = color;
|
|
10317
|
+
hit = true;
|
|
10192
10318
|
});
|
|
10319
|
+
if (!hit) {
|
|
10320
|
+
el.querySelectorAll("text").forEach((t) => {
|
|
10321
|
+
t.style.fill = color;
|
|
10322
|
+
});
|
|
10323
|
+
}
|
|
10193
10324
|
}
|
|
10194
10325
|
}
|
|
10195
10326
|
// ── narration ───────────────────────────────────────────
|
|
@@ -10783,7 +10914,7 @@ class EventEmitter {
|
|
|
10783
10914
|
}
|
|
10784
10915
|
|
|
10785
10916
|
function render(options) {
|
|
10786
|
-
const { container: rawContainer, dsl, renderer = "svg", injectCSS = true, tts, svgOptions = {}, canvasOptions = {}, onNodeClick, onReady, } = options;
|
|
10917
|
+
const { container: rawContainer, dsl, plugins, renderer = "svg", injectCSS = true, tts, svgOptions = {}, canvasOptions = {}, onNodeClick, onReady, } = options;
|
|
10787
10918
|
if (injectCSS && !document.getElementById("ai-diagram-css")) {
|
|
10788
10919
|
const style = document.createElement("style");
|
|
10789
10920
|
style.id = "ai-diagram-css";
|
|
@@ -10799,7 +10930,7 @@ function render(options) {
|
|
|
10799
10930
|
else {
|
|
10800
10931
|
el = rawContainer;
|
|
10801
10932
|
}
|
|
10802
|
-
const ast = parse(dsl);
|
|
10933
|
+
const ast = parse(dsl, { plugins });
|
|
10803
10934
|
const scene = buildSceneGraph(ast);
|
|
10804
10935
|
layout(scene);
|
|
10805
10936
|
let svg;
|
|
@@ -11143,6 +11274,7 @@ class SketchmarkCanvas {
|
|
|
11143
11274
|
const instance = render({
|
|
11144
11275
|
container: this.diagramWrap,
|
|
11145
11276
|
dsl: this.dsl,
|
|
11277
|
+
plugins: this.options.plugins,
|
|
11146
11278
|
renderer: this.renderer,
|
|
11147
11279
|
svgOptions: { interactive: true, showTitle: true, theme: this.options.svgOptions?.theme ?? this.theme, ...this.options.svgOptions },
|
|
11148
11280
|
canvasOptions: this.options.canvasOptions,
|
|
@@ -12220,6 +12352,7 @@ class SketchmarkEmbed {
|
|
|
12220
12352
|
const instance = render({
|
|
12221
12353
|
container: this.diagramWrap,
|
|
12222
12354
|
dsl: this.dsl,
|
|
12355
|
+
plugins: this.options.plugins,
|
|
12223
12356
|
renderer: "svg",
|
|
12224
12357
|
svgOptions: {
|
|
12225
12358
|
showTitle: true,
|