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/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
- CL.appendChild(renderRoughChartSVG(rc, c, palette, themeName !== "light"));
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 (getNodeEl(svg, target) ??
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.querySelector(`#group-${s.target}`)) {
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
- resolveEl(this.svg, target)?.classList.toggle("faded", doFade);
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 = resolveEl(this.svg, target); // handles edges too now
10016
- if (el) {
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 = resolveEl(this.svg, target);
10024
- if (!el)
10025
- return;
10026
- el.style.transition = silent ? "none" : `opacity ${duration}ms`;
10027
- el.style.opacity = show ? "1" : "0";
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) {