sketchmark 1.0.2 → 1.0.3
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 +21 -7
- package/dist/animation/index.d.ts +9 -0
- package/dist/animation/index.d.ts.map +1 -1
- package/dist/index.cjs +177 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +177 -21
- package/dist/index.js.map +1 -1
- package/dist/renderer/svg/index.d.ts.map +1 -1
- package/dist/sketchmark.iife.js +177 -21
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7701,6 +7701,21 @@ function mkGroup(id, cls) {
|
|
|
7701
7701
|
g.setAttribute("class", cls);
|
|
7702
7702
|
return g;
|
|
7703
7703
|
}
|
|
7704
|
+
function buildParentGroupLookup(sg) {
|
|
7705
|
+
const parentGroups = new Map();
|
|
7706
|
+
for (const g of sg.groups) {
|
|
7707
|
+
if (g.parentId)
|
|
7708
|
+
parentGroups.set(`group:${g.id}`, g.parentId);
|
|
7709
|
+
for (const child of g.children) {
|
|
7710
|
+
parentGroups.set(`${child.kind}:${child.id}`, g.id);
|
|
7711
|
+
}
|
|
7712
|
+
}
|
|
7713
|
+
return parentGroups;
|
|
7714
|
+
}
|
|
7715
|
+
function setParentGroupData(el, groupId) {
|
|
7716
|
+
if (groupId)
|
|
7717
|
+
el.dataset.parentGroup = groupId;
|
|
7718
|
+
}
|
|
7704
7719
|
// ── Node shapes ───────────────────────────────────────────────────────────
|
|
7705
7720
|
function renderShape$1(rc, n, palette) {
|
|
7706
7721
|
const s = n.style ?? {};
|
|
@@ -7797,6 +7812,7 @@ function renderToSVG(sg, container, options = {}) {
|
|
|
7797
7812
|
}
|
|
7798
7813
|
// ── Groups ───────────────────────────────────────────────
|
|
7799
7814
|
const gmMap = new Map(sg.groups.map((g) => [g.id, g]));
|
|
7815
|
+
const parentGroups = buildParentGroupLookup(sg);
|
|
7800
7816
|
const sortedGroups = [...sg.groups].sort((a, b) => groupDepth(a, gmMap) - groupDepth(b, gmMap));
|
|
7801
7817
|
const GL = mkGroup("grp-layer");
|
|
7802
7818
|
for (const g of sortedGroups) {
|
|
@@ -7804,6 +7820,7 @@ function renderToSVG(sg, container, options = {}) {
|
|
|
7804
7820
|
continue;
|
|
7805
7821
|
const gs = g.style ?? {};
|
|
7806
7822
|
const gg = mkGroup(`group-${g.id}`, "gg");
|
|
7823
|
+
setParentGroupData(gg, g.parentId);
|
|
7807
7824
|
if (gs.opacity != null)
|
|
7808
7825
|
gg.setAttribute("opacity", String(gs.opacity));
|
|
7809
7826
|
gg.appendChild(rc.rectangle(g.x, g.y, g.w, g.h, {
|
|
@@ -7908,6 +7925,7 @@ function renderToSVG(sg, container, options = {}) {
|
|
|
7908
7925
|
const idPrefix = shapeDef?.idPrefix ?? "node";
|
|
7909
7926
|
const cssClass = shapeDef?.cssClass ?? "ng";
|
|
7910
7927
|
const ng = mkGroup(`${idPrefix}-${n.id}`, cssClass);
|
|
7928
|
+
setParentGroupData(ng, n.groupId ?? parentGroups.get(`node:${n.id}`));
|
|
7911
7929
|
ng.dataset.nodeShape = n.shape;
|
|
7912
7930
|
ng.dataset.x = String(n.x);
|
|
7913
7931
|
ng.dataset.y = String(n.y);
|
|
@@ -7989,6 +8007,7 @@ function renderToSVG(sg, container, options = {}) {
|
|
|
7989
8007
|
const TL = mkGroup("table-layer");
|
|
7990
8008
|
for (const t of sg.tables) {
|
|
7991
8009
|
const tg = mkGroup(`table-${t.id}`, "tg");
|
|
8010
|
+
setParentGroupData(tg, parentGroups.get(`table:${t.id}`));
|
|
7992
8011
|
const gs = t.style ?? {};
|
|
7993
8012
|
const fill = String(gs.fill ?? palette.tableFill);
|
|
7994
8013
|
const strk = String(gs.stroke ?? palette.tableStroke);
|
|
@@ -8089,6 +8108,7 @@ function renderToSVG(sg, container, options = {}) {
|
|
|
8089
8108
|
const MDL = mkGroup('markdown-layer');
|
|
8090
8109
|
for (const m of sg.markdowns) {
|
|
8091
8110
|
const mg = mkGroup(`markdown-${m.id}`, 'mdg');
|
|
8111
|
+
setParentGroupData(mg, parentGroups.get(`markdown:${m.id}`));
|
|
8092
8112
|
const gs = m.style ?? {};
|
|
8093
8113
|
const mFont = resolveStyleFont(gs, diagramFont);
|
|
8094
8114
|
const baseColor = String(gs.color ?? palette.nodeText);
|
|
@@ -8152,7 +8172,9 @@ function renderToSVG(sg, container, options = {}) {
|
|
|
8152
8172
|
// ── Charts ────────────────────────────────────────────────
|
|
8153
8173
|
const CL = mkGroup("chart-layer");
|
|
8154
8174
|
for (const c of sg.charts) {
|
|
8155
|
-
|
|
8175
|
+
const cg = renderRoughChartSVG(rc, c, palette, themeName !== "light");
|
|
8176
|
+
setParentGroupData(cg, parentGroups.get(`chart:${c.id}`));
|
|
8177
|
+
CL.appendChild(cg);
|
|
8156
8178
|
}
|
|
8157
8179
|
svg.appendChild(CL);
|
|
8158
8180
|
return svg;
|
|
@@ -8865,18 +8887,33 @@ const getTableEl = (svg, id) => getEl(svg, `table-${id}`);
|
|
|
8865
8887
|
const getNoteEl = (svg, id) => getEl(svg, `note-${id}`);
|
|
8866
8888
|
const getChartEl = (svg, id) => getEl(svg, `chart-${id}`);
|
|
8867
8889
|
const getMarkdownEl = (svg, id) => getEl(svg, `markdown-${id}`);
|
|
8890
|
+
const POSITIONABLE_SELECTOR = ".ng, .gg, .tg, .ntg, .cg, .mdg";
|
|
8891
|
+
function resolveNonEdgeDrawEl(svg, target) {
|
|
8892
|
+
return (getGroupEl(svg, target) ??
|
|
8893
|
+
getTableEl(svg, target) ??
|
|
8894
|
+
getNoteEl(svg, target) ??
|
|
8895
|
+
getChartEl(svg, target) ??
|
|
8896
|
+
getMarkdownEl(svg, target) ??
|
|
8897
|
+
getNodeEl(svg, target) ??
|
|
8898
|
+
null);
|
|
8899
|
+
}
|
|
8900
|
+
function hideDrawEl(el) {
|
|
8901
|
+
if (el.classList.contains("ng")) {
|
|
8902
|
+
el.classList.add("hidden");
|
|
8903
|
+
return;
|
|
8904
|
+
}
|
|
8905
|
+
el.classList.add("gg-hidden");
|
|
8906
|
+
}
|
|
8907
|
+
function showDrawEl(el) {
|
|
8908
|
+
el.classList.remove("hidden", "gg-hidden");
|
|
8909
|
+
}
|
|
8868
8910
|
function resolveEl(svg, target) {
|
|
8869
8911
|
// check edge first — target contains connector like "a-->b"
|
|
8870
8912
|
const edge = parseEdgeTarget(target);
|
|
8871
8913
|
if (edge)
|
|
8872
8914
|
return getEdgeEl(svg, edge.from, edge.to);
|
|
8873
8915
|
// everything else resolved by prefixed id
|
|
8874
|
-
return (
|
|
8875
|
-
getGroupEl(svg, target) ??
|
|
8876
|
-
getTableEl(svg, target) ??
|
|
8877
|
-
getNoteEl(svg, target) ??
|
|
8878
|
-
getChartEl(svg, target) ??
|
|
8879
|
-
getMarkdownEl(svg, target) ??
|
|
8916
|
+
return (resolveNonEdgeDrawEl(svg, target) ??
|
|
8880
8917
|
null);
|
|
8881
8918
|
}
|
|
8882
8919
|
function pathLength(p) {
|
|
@@ -9066,6 +9103,7 @@ function prepareNodeForDraw(el) {
|
|
|
9066
9103
|
el.appendChild(guide);
|
|
9067
9104
|
}
|
|
9068
9105
|
function revealNodeInstant(el) {
|
|
9106
|
+
showDrawEl(el);
|
|
9069
9107
|
clearNodeDrawStyles(el);
|
|
9070
9108
|
}
|
|
9071
9109
|
// ── Text writing reveal (clipPath) ───────────────────────
|
|
@@ -9114,6 +9152,7 @@ function animateTextReveal(textEl, delayMs, durationMs = ANIMATION.textRevealMs)
|
|
|
9114
9152
|
}, delayMs);
|
|
9115
9153
|
}
|
|
9116
9154
|
function animateNodeDraw(el, strokeDur = ANIMATION.nodeStrokeDur) {
|
|
9155
|
+
showDrawEl(el);
|
|
9117
9156
|
const guide = nodeGuidePathEl(el);
|
|
9118
9157
|
if (!guide) {
|
|
9119
9158
|
const firstPath = el.querySelector("path");
|
|
@@ -9168,6 +9207,15 @@ function flattenSteps(items) {
|
|
|
9168
9207
|
}
|
|
9169
9208
|
return out;
|
|
9170
9209
|
}
|
|
9210
|
+
function forEachPlaybackStep(items, visit) {
|
|
9211
|
+
items.forEach((item, stepIndex) => {
|
|
9212
|
+
if (item.kind === "beat") {
|
|
9213
|
+
item.children.forEach((child) => visit(child, stepIndex));
|
|
9214
|
+
return;
|
|
9215
|
+
}
|
|
9216
|
+
visit(item, stepIndex);
|
|
9217
|
+
});
|
|
9218
|
+
}
|
|
9171
9219
|
// ── Draw target helpers ───────────────────────────────────
|
|
9172
9220
|
function getDrawTargetEdgeIds(steps) {
|
|
9173
9221
|
const ids = new Set();
|
|
@@ -9352,7 +9400,7 @@ class AnimationController {
|
|
|
9352
9400
|
for (const s of flattenSteps(steps)) {
|
|
9353
9401
|
if (s.action !== "draw" || parseEdgeTarget(s.target))
|
|
9354
9402
|
continue;
|
|
9355
|
-
if (svg.
|
|
9403
|
+
if (resolveNonEdgeDrawEl(svg, s.target)?.id === `group-${s.target}`) {
|
|
9356
9404
|
this.drawTargetGroups.add(`group-${s.target}`);
|
|
9357
9405
|
this.drawTargetNodes.delete(`node-${s.target}`);
|
|
9358
9406
|
}
|
|
@@ -9373,6 +9421,10 @@ class AnimationController {
|
|
|
9373
9421
|
this.drawTargetNodes.delete(`node-${s.target}`);
|
|
9374
9422
|
}
|
|
9375
9423
|
}
|
|
9424
|
+
this._drawStepIndexByElementId = this._buildDrawStepIndex();
|
|
9425
|
+
const { parentGroupByElementId, groupDescendantIds } = this._buildGroupVisibilityIndex();
|
|
9426
|
+
this._parentGroupByElementId = parentGroupByElementId;
|
|
9427
|
+
this._groupDescendantIds = groupDescendantIds;
|
|
9376
9428
|
this._clearAll();
|
|
9377
9429
|
// Init narration caption
|
|
9378
9430
|
if (this._container)
|
|
@@ -9391,6 +9443,104 @@ class AnimationController {
|
|
|
9391
9443
|
if (this._tts)
|
|
9392
9444
|
this._warmUpSpeech();
|
|
9393
9445
|
}
|
|
9446
|
+
_buildDrawStepIndex() {
|
|
9447
|
+
const drawStepIndexByElementId = new Map();
|
|
9448
|
+
forEachPlaybackStep(this.steps, (step, stepIndex) => {
|
|
9449
|
+
if (step.action !== "draw" || parseEdgeTarget(step.target))
|
|
9450
|
+
return;
|
|
9451
|
+
const el = resolveNonEdgeDrawEl(this.svg, step.target);
|
|
9452
|
+
if (el && !drawStepIndexByElementId.has(el.id)) {
|
|
9453
|
+
drawStepIndexByElementId.set(el.id, stepIndex);
|
|
9454
|
+
}
|
|
9455
|
+
});
|
|
9456
|
+
return drawStepIndexByElementId;
|
|
9457
|
+
}
|
|
9458
|
+
_buildGroupVisibilityIndex() {
|
|
9459
|
+
const parentGroupByElementId = new Map();
|
|
9460
|
+
const directChildIdsByGroup = new Map();
|
|
9461
|
+
this.svg.querySelectorAll(POSITIONABLE_SELECTOR).forEach((el) => {
|
|
9462
|
+
const parentGroupId = el.dataset.parentGroup;
|
|
9463
|
+
if (!parentGroupId)
|
|
9464
|
+
return;
|
|
9465
|
+
const parentGroupElId = `group-${parentGroupId}`;
|
|
9466
|
+
parentGroupByElementId.set(el.id, parentGroupElId);
|
|
9467
|
+
const children = directChildIdsByGroup.get(parentGroupElId) ?? new Set();
|
|
9468
|
+
children.add(el.id);
|
|
9469
|
+
directChildIdsByGroup.set(parentGroupElId, children);
|
|
9470
|
+
});
|
|
9471
|
+
const groupDescendantIds = new Map();
|
|
9472
|
+
const visit = (groupElId) => {
|
|
9473
|
+
if (groupDescendantIds.has(groupElId))
|
|
9474
|
+
return groupDescendantIds.get(groupElId);
|
|
9475
|
+
const descendants = new Set();
|
|
9476
|
+
const directChildren = directChildIdsByGroup.get(groupElId);
|
|
9477
|
+
if (directChildren) {
|
|
9478
|
+
for (const childId of directChildren) {
|
|
9479
|
+
descendants.add(childId);
|
|
9480
|
+
if (childId.startsWith("group-")) {
|
|
9481
|
+
visit(childId).forEach((nestedId) => descendants.add(nestedId));
|
|
9482
|
+
}
|
|
9483
|
+
}
|
|
9484
|
+
}
|
|
9485
|
+
groupDescendantIds.set(groupElId, descendants);
|
|
9486
|
+
return descendants;
|
|
9487
|
+
};
|
|
9488
|
+
this.svg.querySelectorAll(".gg").forEach((el) => {
|
|
9489
|
+
visit(el.id);
|
|
9490
|
+
});
|
|
9491
|
+
return { parentGroupByElementId, groupDescendantIds };
|
|
9492
|
+
}
|
|
9493
|
+
_hideGroupDescendants(groupElId) {
|
|
9494
|
+
const descendants = this._groupDescendantIds.get(groupElId);
|
|
9495
|
+
if (!descendants)
|
|
9496
|
+
return;
|
|
9497
|
+
for (const descendantId of descendants) {
|
|
9498
|
+
const el = getEl(this.svg, descendantId);
|
|
9499
|
+
if (el)
|
|
9500
|
+
hideDrawEl(el);
|
|
9501
|
+
}
|
|
9502
|
+
}
|
|
9503
|
+
_isDeferredForGroupReveal(elementId, stepIndex, groupElId) {
|
|
9504
|
+
let currentId = elementId;
|
|
9505
|
+
while (currentId) {
|
|
9506
|
+
const firstDrawStep = this._drawStepIndexByElementId.get(currentId);
|
|
9507
|
+
if (firstDrawStep != null && firstDrawStep > stepIndex)
|
|
9508
|
+
return true;
|
|
9509
|
+
if (currentId === groupElId)
|
|
9510
|
+
break;
|
|
9511
|
+
currentId = this._parentGroupByElementId.get(currentId);
|
|
9512
|
+
}
|
|
9513
|
+
return false;
|
|
9514
|
+
}
|
|
9515
|
+
_revealGroupSubtree(groupElId, stepIndex) {
|
|
9516
|
+
const descendants = this._groupDescendantIds.get(groupElId);
|
|
9517
|
+
if (!descendants)
|
|
9518
|
+
return;
|
|
9519
|
+
for (const descendantId of descendants) {
|
|
9520
|
+
if (this._isDeferredForGroupReveal(descendantId, stepIndex, groupElId))
|
|
9521
|
+
continue;
|
|
9522
|
+
const el = getEl(this.svg, descendantId);
|
|
9523
|
+
if (el)
|
|
9524
|
+
showDrawEl(el);
|
|
9525
|
+
}
|
|
9526
|
+
}
|
|
9527
|
+
_resolveCascadeTargets(target) {
|
|
9528
|
+
const edge = parseEdgeTarget(target);
|
|
9529
|
+
if (edge) {
|
|
9530
|
+
const el = getEdgeEl(this.svg, edge.from, edge.to);
|
|
9531
|
+
return el ? [el] : [];
|
|
9532
|
+
}
|
|
9533
|
+
const el = resolveEl(this.svg, target);
|
|
9534
|
+
if (!el)
|
|
9535
|
+
return [];
|
|
9536
|
+
if (!el.id.startsWith("group-"))
|
|
9537
|
+
return [el];
|
|
9538
|
+
const ids = new Set([el.id]);
|
|
9539
|
+
this._groupDescendantIds.get(el.id)?.forEach((id) => ids.add(id));
|
|
9540
|
+
return Array.from(ids)
|
|
9541
|
+
.map((id) => getEl(this.svg, id))
|
|
9542
|
+
.filter((candidate) => candidate != null);
|
|
9543
|
+
}
|
|
9394
9544
|
/** The narration caption element — mount it anywhere via `yourContainer.appendChild(anim.captionElement)` */
|
|
9395
9545
|
get captionElement() {
|
|
9396
9546
|
return this._captionEl;
|
|
@@ -9660,6 +9810,9 @@ class AnimationController {
|
|
|
9660
9810
|
el.style.opacity = "";
|
|
9661
9811
|
el.classList.remove("hl", "faded");
|
|
9662
9812
|
});
|
|
9813
|
+
for (const groupElId of this.drawTargetGroups) {
|
|
9814
|
+
this._hideGroupDescendants(groupElId);
|
|
9815
|
+
}
|
|
9663
9816
|
// Clear narration caption
|
|
9664
9817
|
if (this._captionEl) {
|
|
9665
9818
|
this._captionEl.style.opacity = "0";
|
|
@@ -9722,7 +9875,7 @@ class AnimationController {
|
|
|
9722
9875
|
this._doDraw(s, silent);
|
|
9723
9876
|
break;
|
|
9724
9877
|
case "erase":
|
|
9725
|
-
this._doErase(s.target, s.duration);
|
|
9878
|
+
this._doErase(s.target, silent, s.duration);
|
|
9726
9879
|
break;
|
|
9727
9880
|
case "show":
|
|
9728
9881
|
this._doShowHide(s.target, true, silent, s.duration);
|
|
@@ -9778,7 +9931,9 @@ class AnimationController {
|
|
|
9778
9931
|
}
|
|
9779
9932
|
// ── fade / unfade ─────────────────────────────────────────
|
|
9780
9933
|
_doFade(target, doFade) {
|
|
9781
|
-
|
|
9934
|
+
for (const el of this._resolveCascadeTargets(target)) {
|
|
9935
|
+
el.classList.toggle("faded", doFade);
|
|
9936
|
+
}
|
|
9782
9937
|
}
|
|
9783
9938
|
_writeTransform(el, target, silent, duration = 420) {
|
|
9784
9939
|
const t = this._transforms.get(target) ?? {
|
|
@@ -9877,11 +10032,12 @@ class AnimationController {
|
|
|
9877
10032
|
// Check if target is a group (has #group-{target} element)
|
|
9878
10033
|
const groupEl = getGroupEl(this.svg, target);
|
|
9879
10034
|
if (groupEl) {
|
|
10035
|
+
showDrawEl(groupEl);
|
|
10036
|
+
this._revealGroupSubtree(groupEl.id, this._step);
|
|
9880
10037
|
// ── Group draw ──────────────────────────────────────
|
|
9881
10038
|
if (silent) {
|
|
9882
10039
|
clearDrawStyles(groupEl);
|
|
9883
10040
|
groupEl.style.transition = "none";
|
|
9884
|
-
groupEl.classList.remove("gg-hidden");
|
|
9885
10041
|
groupEl.style.opacity = "1";
|
|
9886
10042
|
requestAnimationFrame(() => requestAnimationFrame(() => {
|
|
9887
10043
|
groupEl.style.transition = "";
|
|
@@ -9889,7 +10045,6 @@ class AnimationController {
|
|
|
9889
10045
|
}));
|
|
9890
10046
|
}
|
|
9891
10047
|
else {
|
|
9892
|
-
groupEl.classList.remove("gg-hidden");
|
|
9893
10048
|
// Groups use slightly longer stroke-draw (bigger box, dashed border = more paths)
|
|
9894
10049
|
const firstPath = groupEl.querySelector("path");
|
|
9895
10050
|
if (!firstPath?.style.strokeDasharray)
|
|
@@ -10000,6 +10155,7 @@ class AnimationController {
|
|
|
10000
10155
|
const nodeEl = getNodeEl(this.svg, target);
|
|
10001
10156
|
if (!nodeEl)
|
|
10002
10157
|
return;
|
|
10158
|
+
showDrawEl(nodeEl);
|
|
10003
10159
|
if (silent) {
|
|
10004
10160
|
revealNodeInstant(nodeEl);
|
|
10005
10161
|
}
|
|
@@ -10011,20 +10167,20 @@ class AnimationController {
|
|
|
10011
10167
|
}
|
|
10012
10168
|
}
|
|
10013
10169
|
// ── erase ─────────────────────────────────────────────────
|
|
10014
|
-
_doErase(target, duration = 400) {
|
|
10015
|
-
const el
|
|
10016
|
-
|
|
10017
|
-
el.style.transition = `opacity ${duration}ms`;
|
|
10170
|
+
_doErase(target, silent, duration = 400) {
|
|
10171
|
+
for (const el of this._resolveCascadeTargets(target)) {
|
|
10172
|
+
el.style.transition = silent ? "none" : `opacity ${duration}ms`;
|
|
10018
10173
|
el.style.opacity = "0";
|
|
10019
10174
|
}
|
|
10020
10175
|
}
|
|
10021
10176
|
// ── show / hide ───────────────────────────────────────────
|
|
10022
10177
|
_doShowHide(target, show, silent, duration = 400) {
|
|
10023
|
-
const el
|
|
10024
|
-
|
|
10025
|
-
|
|
10026
|
-
|
|
10027
|
-
|
|
10178
|
+
for (const el of this._resolveCascadeTargets(target)) {
|
|
10179
|
+
if (show)
|
|
10180
|
+
showDrawEl(el);
|
|
10181
|
+
el.style.transition = silent ? "none" : `opacity ${duration}ms`;
|
|
10182
|
+
el.style.opacity = show ? "1" : "0";
|
|
10183
|
+
}
|
|
10028
10184
|
}
|
|
10029
10185
|
// ── pulse ─────────────────────────────────────────────────
|
|
10030
10186
|
_doPulse(target, duration = 500) {
|