@cardenelabs/cdl 0.7.0 → 0.10.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.
Files changed (45) hide show
  1. package/CHANGELOG.md +446 -0
  2. package/SPEC.md +42 -18
  3. package/dist/index.cjs +793 -347
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.d.cts +73 -14
  6. package/dist/index.d.ts +73 -14
  7. package/dist/index.js +793 -347
  8. package/dist/index.js.map +1 -1
  9. package/dist/react.cjs +658 -236
  10. package/dist/react.cjs.map +1 -1
  11. package/dist/react.d.cts +1 -1
  12. package/dist/react.d.ts +1 -1
  13. package/dist/react.js +658 -236
  14. package/dist/react.js.map +1 -1
  15. package/dist/{render-DquCvgOB.d.cts → render-CH1Qn3Jv.d.cts} +47 -2
  16. package/dist/{render-DquCvgOB.d.ts → render-CH1Qn3Jv.d.ts} +47 -2
  17. package/package.json +6 -3
  18. package/src/builder.ts +13 -1
  19. package/src/kinds/box-lines.ts +120 -0
  20. package/src/kinds/chart-line.tsx +94 -25
  21. package/src/kinds/gantt.tsx +152 -24
  22. package/src/kinds/mind-map.tsx +50 -12
  23. package/src/kinds/quadrant.tsx +28 -2
  24. package/src/kinds/shape-blockchain.tsx +65 -12
  25. package/src/kinds/shape-region.tsx +152 -34
  26. package/src/kinds/tree.tsx +92 -20
  27. package/src/layout/collisions.ts +10 -2
  28. package/src/layout/footer-shape.ts +86 -24
  29. package/src/layout/lifeline.ts +80 -0
  30. package/src/layout/spec.ts +21 -2
  31. package/src/layout/text-width.ts +92 -4
  32. package/src/layout/viewbox.ts +0 -8
  33. package/src/layout.ts +8 -1
  34. package/src/presets.ts +120 -22
  35. package/src/render/header.tsx +3 -1
  36. package/src/render/nodes.tsx +15 -1
  37. package/src/render/payload-binding.ts +31 -8
  38. package/src/render/stage.tsx +21 -34
  39. package/src/render/template-fields.ts +212 -0
  40. package/src/render/utils.ts +32 -2
  41. package/src/render.tsx +2 -9
  42. package/src/types.ts +47 -2
  43. package/src/validate.ts +45 -13
  44. package/src/visual-validate.ts +34 -1
  45. package/src/layout/label-shift.ts +0 -28
package/dist/react.cjs CHANGED
@@ -462,7 +462,35 @@ function isCJK(code) {
462
462
  function isHalfwidthKana(code) {
463
463
  return code >= 65377 && code <= 65500;
464
464
  }
465
+ var EMOJI_ADVANCE_EM = 1.05;
466
+ var MONO_EMOJI_ADVANCE_EM = 1.16;
467
+ var WIDE_SYMBOL_ADVANCE_EM = 0.95;
468
+ function isEmoji(code) {
469
+ return code >= 126976 && code <= 129791 || // 絵文字の主要面
470
+ code >= 9728 && code <= 10175 || // 記号と絵文字 (♻ ✅ ❤ 等)
471
+ code >= 127462 && code <= 127487;
472
+ }
473
+ function isEmojiGrapheme(grapheme, firstCode) {
474
+ return isEmoji(firstCode) || grapheme.includes("\uFE0F");
475
+ }
476
+ function isWideSymbol(code) {
477
+ return code >= 8592 && code <= 8703 || // 矢印
478
+ code >= 9472 && code <= 9599 || // 罫線
479
+ code >= 9632 && code <= 9727 || // 幾何図形
480
+ code >= 11008 && code <= 11263;
481
+ }
482
+ var CLUSTER_JOINER_RE = /\u200d|\ufe0f|[\u{1f000}-\u{1faff}]|[\u{2600}-\u{27bf}]/u;
483
+ var \u66F8\u8A18\u7D20\u3067\u5206\u3051\u308B;
484
+ function segmentGraphemes(text) {
485
+ if (typeof Intl === "undefined" || typeof Intl.Segmenter !== "function") {
486
+ return [...text];
487
+ }
488
+ \u66F8\u8A18\u7D20\u3067\u5206\u3051\u308B ??= new Intl.Segmenter(void 0, { granularity: "grapheme" });
489
+ return [...\u66F8\u8A18\u7D20\u3067\u5206\u3051\u308B.segment(text)].map((s) => s.segment);
490
+ }
465
491
  function defaultAdvanceEm(ch, code) {
492
+ if (isEmoji(code)) return EMOJI_ADVANCE_EM;
493
+ if (isWideSymbol(code)) return WIDE_SYMBOL_ADVANCE_EM;
466
494
  if (isCJK(code)) return 1.08;
467
495
  if (isHalfwidthKana(code)) return 0.72;
468
496
  if (ch >= "a" && ch <= "z") return 0.73;
@@ -476,19 +504,23 @@ function measureTextWidth(text, opts = {}) {
476
504
  const fontFamily = opts.fontFamily ?? "sans";
477
505
  let advanceEmSum = 0;
478
506
  let charCount = 0;
479
- for (const ch of text) {
507
+ const \u6587\u5B57\u5217 = CLUSTER_JOINER_RE.test(text) ? segmentGraphemes(text) : [...text];
508
+ for (const ch of \u6587\u5B57\u5217) {
480
509
  charCount++;
510
+ const code = ch.codePointAt(0) ?? 0;
511
+ if (isEmojiGrapheme(ch, code)) {
512
+ advanceEmSum += fontFamily === "mono" ? MONO_EMOJI_ADVANCE_EM : EMOJI_ADVANCE_EM;
513
+ continue;
514
+ }
481
515
  if (fontFamily === "mono") {
482
- const code2 = ch.codePointAt(0) ?? 0;
483
- advanceEmSum += isCJK(code2) ? 1.08 : JETBRAINS_MONO_ADVANCE_EM;
516
+ advanceEmSum += isCJK(code) ? 1.08 : JETBRAINS_MONO_ADVANCE_EM;
484
517
  continue;
485
518
  }
486
- const code = ch.codePointAt(0) ?? 0;
487
519
  if (isCJK(code)) {
488
520
  advanceEmSum += 1.08;
489
521
  continue;
490
522
  }
491
- const table = INTER_BOLD_ADVANCE_EM[ch];
523
+ const table = ch.length === 1 ? INTER_BOLD_ADVANCE_EM[ch] : void 0;
492
524
  advanceEmSum += table !== void 0 ? table : defaultAdvanceEm(ch, code);
493
525
  }
494
526
  const w = advanceEmSum * fontSize + letterSpacing * charCount;
@@ -544,13 +576,15 @@ var KINDS_WITHOUT_TITLE_TEXT = /* @__PURE__ */ new Set([
544
576
  "funnel-stages",
545
577
  "quadrant-matrix",
546
578
  "tree-hierarchy",
547
- "journey-map",
548
- // 形の中に自前の文字を置く (title は使わない)
549
- "shape-blockchain-block"
579
+ "journey-map"
550
580
  ]);
551
581
  function rendersTitleText(kind) {
552
582
  return !KINDS_WITHOUT_TITLE_TEXT.has(kind ?? "");
553
583
  }
584
+ var KINDS_FITTING_OWN_TITLE = /* @__PURE__ */ new Set(["shape-blockchain-block"]);
585
+ function fitsOwnTitleWidth(kind) {
586
+ return KINDS_FITTING_OWN_TITLE.has(kind ?? "");
587
+ }
554
588
  var KINDS_WITH_ROW_TEXT = /* @__PURE__ */ new Set([
555
589
  // 専用の表組み (StorageNode)
556
590
  "storage",
@@ -737,6 +771,70 @@ function buildDiagramAltText(topic, phase) {
737
771
  return parts.join(" ");
738
772
  }
739
773
 
774
+ // src/layout/footer-shape.ts
775
+ var SHAPE_DRAW_UP_EXTENT = {
776
+ "shape-robot-arm": 201,
777
+ // 実測 上 129
778
+ "shape-iot-sensor": 120,
779
+ // 48
780
+ "shape-credit-card": 113,
781
+ // 40.8
782
+ "shape-exchange": 107,
783
+ // 35
784
+ "shape-storefront": 104,
785
+ // 32
786
+ "shape-cdn-edge": 103,
787
+ // 30.8
788
+ "shape-payment-provider": 101,
789
+ // 29
790
+ "shape-gear": 96,
791
+ // 23.9
792
+ "shape-rpc-node": 94,
793
+ // 22
794
+ "shape-wallet": 84
795
+ // 12.1
796
+ };
797
+ function lifelineFooterNodes(lanes, nodes) {
798
+ const \u6570 = /* @__PURE__ */ new Map();
799
+ for (const n of nodes) \u6570.set(n.lane, (\u6570.get(n.lane) ?? 0) + 1);
800
+ const lifelineLanes = /* @__PURE__ */ new Set();
801
+ for (const lane of lanes) {
802
+ if (lane.lifeline) lifelineLanes.add(lane.id);
803
+ }
804
+ return nodes.filter(
805
+ (n) => n.role === "lifeline-footer" && lifelineLanes.has(n.lane) && (\u6570.get(n.lane) ?? 0) >= 2
806
+ );
807
+ }
808
+ function footerShapeDropY(kind, h) {
809
+ const extent = SHAPE_DRAW_UP_EXTENT[kind];
810
+ if (extent === void 0) return 0;
811
+ if (!Number.isFinite(h) || h <= 0) return 0;
812
+ return Math.max(0, extent - h);
813
+ }
814
+ function footerShapeDrops(lanes, nodes) {
815
+ return footerShapeDropsOf(lifelineFooterNodes(lanes, nodes));
816
+ }
817
+ function footerShapeDropsOf(footers) {
818
+ const drops = /* @__PURE__ */ new Map();
819
+ for (const footer of footers) {
820
+ const dy = footerShapeDropY(footer.kind, footer.h);
821
+ if (dy > 0) drops.set(footer.id, dy);
822
+ }
823
+ return drops;
824
+ }
825
+ function applyFooterShapeDrops(lanes, nodes) {
826
+ const drops = footerShapeDrops(lanes, nodes);
827
+ if (drops.size === 0) return nodes;
828
+ return nodes.map((n) => {
829
+ if (n.posX !== void 0 && n.posY !== void 0) return n;
830
+ const dy = drops.get(n.id);
831
+ return dy === void 0 ? n : { ...n, cy: n.cy + dy };
832
+ });
833
+ }
834
+ function footerShapeDrawTop(node) {
835
+ return node.cy - node.h / 2 - footerShapeDropY(node.kind, node.h);
836
+ }
837
+
740
838
  // src/layout/collisions.ts
741
839
  var LANE_LABEL_CLEARANCE_MARGIN = 2;
742
840
  function collectBBoxes(lanes, nodes, edges) {
@@ -752,14 +850,16 @@ function collectBBoxes(lanes, nodes, edges) {
752
850
  h: 28
753
851
  });
754
852
  }
853
+ const footerDraw = footerShapeDrops(lanes, nodes);
755
854
  for (const n of nodes) {
855
+ const up = footerDraw.get(n.id) ?? 0;
756
856
  out.push({
757
857
  kind: "node",
758
858
  id: n.id,
759
859
  x: n.cx - n.w / 2,
760
- y: n.cy - n.h / 2,
860
+ y: n.cy - n.h / 2 - up,
761
861
  w: n.w,
762
- h: n.h
862
+ h: n.h + up
763
863
  });
764
864
  }
765
865
  for (const e of edges) {
@@ -3584,67 +3684,6 @@ function layoutNodes(diag, lanes) {
3584
3684
  return out;
3585
3685
  }
3586
3686
 
3587
- // src/layout/footer-shape.ts
3588
- var SHAPE_DRAW_UP_EXTENT = {
3589
- "shape-robot-arm": 201,
3590
- // 実測 上 129
3591
- "shape-iot-sensor": 120,
3592
- // 48
3593
- "shape-credit-card": 113,
3594
- // 40.8
3595
- "shape-exchange": 107,
3596
- // 35
3597
- "shape-storefront": 104,
3598
- // 32
3599
- "shape-cdn-edge": 103,
3600
- // 30.8
3601
- "shape-payment-provider": 101,
3602
- // 29
3603
- "shape-gear": 96,
3604
- // 23.9
3605
- "shape-rpc-node": 94,
3606
- // 22
3607
- "shape-wallet": 84
3608
- // 12.1
3609
- };
3610
- function lifelineFooterNodes(lanes, nodes) {
3611
- const byLane = /* @__PURE__ */ new Map();
3612
- for (const n of nodes) {
3613
- const inside = byLane.get(n.lane);
3614
- if (inside) inside.push(n);
3615
- else byLane.set(n.lane, [n]);
3616
- }
3617
- const footers = [];
3618
- for (const lane of lanes) {
3619
- if (!lane.lifeline) continue;
3620
- const inside = byLane.get(lane.id);
3621
- if (inside === void 0 || inside.length < 2) continue;
3622
- let footer = inside[0];
3623
- for (const n of inside) {
3624
- if (n.cy >= footer.cy) footer = n;
3625
- }
3626
- footers.push(footer);
3627
- }
3628
- return footers;
3629
- }
3630
- function footerShapeDropY(kind, h) {
3631
- const extent = SHAPE_DRAW_UP_EXTENT[kind];
3632
- if (extent === void 0) return 0;
3633
- if (!Number.isFinite(h) || h <= 0) return 0;
3634
- return Math.max(0, extent - h);
3635
- }
3636
- function footerShapeDrops(lanes, nodes) {
3637
- return footerShapeDropsOf(lifelineFooterNodes(lanes, nodes));
3638
- }
3639
- function footerShapeDropsOf(footers) {
3640
- const drops = /* @__PURE__ */ new Map();
3641
- for (const footer of footers) {
3642
- const dy = footerShapeDropY(footer.kind, footer.h);
3643
- if (dy > 0) drops.set(footer.id, dy);
3644
- }
3645
- return drops;
3646
- }
3647
-
3648
3687
  // src/layout/viewbox.ts
3649
3688
  function computeViewBox(lanes, nodes, edges, bboxes, viewport) {
3650
3689
  let maxX = 0;
@@ -3665,11 +3704,6 @@ function computeViewBox(lanes, nodes, edges, bboxes, viewport) {
3665
3704
  maxX = Math.max(maxX, n.cx + n.w / 2);
3666
3705
  maxY = Math.max(maxY, n.cy + n.h / 2);
3667
3706
  }
3668
- const footerDrops = footerShapeDrops(lanes, nodes);
3669
- for (const n of nodes) {
3670
- const dy = footerDrops.get(n.id);
3671
- if (dy !== void 0) maxY = Math.max(maxY, n.cy + n.h / 2 + dy);
3672
- }
3673
3707
  if (!Number.isFinite(minX)) minX = 0;
3674
3708
  if (!Number.isFinite(minY)) minY = 0;
3675
3709
  const x = minX - VIEW_PAD;
@@ -3747,7 +3781,7 @@ function applyRowLevelNodeShifts(nodes, lanes, nodeShifts) {
3747
3781
  }
3748
3782
  function layout(diag) {
3749
3783
  const lanes = layoutLanes(diag);
3750
- const rawNodes = layoutNodes(diag, lanes);
3784
+ const rawNodes = applyFooterShapeDrops(lanes, layoutNodes(diag, lanes));
3751
3785
  const expandedLanes = expandLanesForNodes(lanes, rawNodes);
3752
3786
  const gapExpandedLanes = expandLaneGapsForEdgeLabels(expandedLanes, rawNodes, diag.edges);
3753
3787
  const nodes = rawNodes.map((n) => {
@@ -4610,7 +4644,7 @@ function templateRefIds(text) {
4610
4644
  return out;
4611
4645
  }
4612
4646
  function computeStateValues(laid, phaseIdx, progress) {
4613
- const values = {};
4647
+ const values = /* @__PURE__ */ Object.create(null);
4614
4648
  for (const s of laid.states) values[s.id] = s.initial;
4615
4649
  for (let i = 0; i < laid.phases.length; i++) {
4616
4650
  const phase = laid.phases[i];
@@ -4628,10 +4662,20 @@ function computeStateValues(laid, phaseIdx, progress) {
4628
4662
  }
4629
4663
  }
4630
4664
  }
4631
- const out = {};
4665
+ const out = /* @__PURE__ */ Object.create(null);
4632
4666
  for (const [k, v] of Object.entries(values)) out[k] = String(v);
4633
4667
  return withDerivedValues(out, laid.derived);
4634
4668
  }
4669
+ function mergeStateValues(base, overrides) {
4670
+ const merged = Object.assign(/* @__PURE__ */ Object.create(null), base);
4671
+ for (const key of Object.keys(overrides)) {
4672
+ const v = overrides[key];
4673
+ if (typeof v === "number" || typeof v === "string" || typeof v === "boolean") {
4674
+ merged[key] = String(v);
4675
+ }
4676
+ }
4677
+ return merged;
4678
+ }
4635
4679
  function interpolate(template, values) {
4636
4680
  return template.replace(TEMPLATE_REF_RE, (_, id, accessor) => {
4637
4681
  const raw = Object.hasOwn(values, id) ? values[id] : void 0;
@@ -4781,6 +4825,65 @@ function pathSubpath(d, progress) {
4781
4825
  return d;
4782
4826
  }
4783
4827
 
4828
+ // src/render/template-fields.ts
4829
+ var TEXT_FIELDS = ["title", "eyebrow", "subtitle", "value", "wBind", "hBind", "visibleIf"];
4830
+ var LIST_FIELDS = ["rows"];
4831
+ var NUMERIC_FIELDS = ["opacity", "renderOffsetX", "renderOffsetY"];
4832
+ var SHAPE_TEMPLATE_FIELDS = ["source", "radius", "fillProgress", "angle", "level", "rotation", "fill"];
4833
+ function shapeTexts(shape) {
4834
+ if (!shape) return [];
4835
+ const out = [];
4836
+ const \u4E2D\u8EAB = shape;
4837
+ for (const f of SHAPE_TEMPLATE_FIELDS) {
4838
+ const v = \u4E2D\u8EAB[f];
4839
+ if (typeof v === "string" && v) out.push(v);
4840
+ }
4841
+ return out;
4842
+ }
4843
+ function payloadTexts(node) {
4844
+ const out = [];
4845
+ const \u8DB3\u3059 = (v) => {
4846
+ if (typeof v === "string" && v) out.push(v);
4847
+ };
4848
+ for (const d of node.chartData ?? []) \u8DB3\u3059(d.value);
4849
+ for (const s of node.funnelData ?? []) \u8DB3\u3059(s.count);
4850
+ for (const t of node.ganttData ?? []) {
4851
+ \u8DB3\u3059(t.startIdx);
4852
+ \u8DB3\u3059(t.endIdx);
4853
+ }
4854
+ for (const i of node.quadrantData?.items ?? []) \u8DB3\u3059(i.quadrant);
4855
+ for (const s of node.journeyData ?? []) \u8DB3\u3059(s.emotion);
4856
+ for (const n of node.treeData ?? []) {
4857
+ \u8DB3\u3059(n.title);
4858
+ \u8DB3\u3059(n.subtitle);
4859
+ }
4860
+ if (node.mindData) {
4861
+ \u8DB3\u3059(node.mindData.rootTitle);
4862
+ for (const b of node.mindData.branches) {
4863
+ \u8DB3\u3059(b.title);
4864
+ \u8DB3\u3059(b.subtitle);
4865
+ }
4866
+ }
4867
+ return out;
4868
+ }
4869
+ function nodeTemplateTexts(node) {
4870
+ const out = [];
4871
+ for (const f of TEXT_FIELDS) {
4872
+ const v = node[f];
4873
+ if (typeof v === "string" && v) out.push(v);
4874
+ }
4875
+ for (const f of LIST_FIELDS) {
4876
+ for (const v of node[f] ?? []) if (v) out.push(v);
4877
+ }
4878
+ for (const f of NUMERIC_FIELDS) {
4879
+ const v = node[f];
4880
+ if (typeof v === "string" && v) out.push(v);
4881
+ }
4882
+ out.push(...shapeTexts(node.shape));
4883
+ out.push(...payloadTexts(node));
4884
+ return out;
4885
+ }
4886
+
4784
4887
  // src/validate.ts
4785
4888
  var TONES2 = new Set(TONES);
4786
4889
  var KINDS = new Set(NODE_KINDS);
@@ -4839,8 +4942,14 @@ function validate(diag) {
4839
4942
  const nodeIds = new Set(diag.nodes.map((n) => n.id));
4840
4943
  const edgeIds = new Set(diag.edges.map((e) => e.id));
4841
4944
  const stateIds = new Set(diag.states.map((s) => s.id));
4842
- const readableIds = /* @__PURE__ */ new Set([...stateIds, ...(diag.derived ?? []).map((d) => d.id)]);
4843
- const suggest = (target, candidates) => {
4945
+ const readableIds = /* @__PURE__ */ new Set([
4946
+ ...stateIds,
4947
+ ...(diag.derived ?? []).map((d) => d.id),
4948
+ ...(diag.inputs ?? []).map((i) => i.id),
4949
+ ...(diag.formulas ?? []).map((f) => f.id),
4950
+ ...(diag.scrollTriggers ?? []).map((t) => t.id)
4951
+ ]);
4952
+ const suggest = (target, candidates, candidateKind = "lane") => {
4844
4953
  const list = [...candidates];
4845
4954
  if (list.length === 0) return "";
4846
4955
  let best = "";
@@ -4852,7 +4961,7 @@ function validate(diag) {
4852
4961
  best = c;
4853
4962
  }
4854
4963
  }
4855
- return bestD <= 2 ? ` (\u3082\u3057\u304B\u3057\u3066 "${best}"?)` : ` (\u5B9A\u7FA9\u6E08 lane: ${list.slice(0, 5).join(", ")}${list.length > 5 ? "..." : ""})`;
4964
+ return bestD <= 2 ? ` (\u3082\u3057\u304B\u3057\u3066 "${best}"?)` : ` (\u5B9A\u7FA9\u6E08 ${candidateKind}: ${list.slice(0, 5).join(", ")}${list.length > 5 ? "..." : ""})`;
4856
4965
  };
4857
4966
  for (const n of diag.nodes) {
4858
4967
  if (!laneIds.has(n.lane)) {
@@ -4865,13 +4974,13 @@ function validate(diag) {
4865
4974
  for (const e of diag.edges) {
4866
4975
  if (!nodeIds.has(e.from)) {
4867
4976
  errors.push(
4868
- `[cdl] unknown-ref: edge "${e.id}" \u306E from "${e.from}" \u304C node \u306B\u5B58\u5728\u3057\u307E\u305B\u3093${suggest(e.from, nodeIds)}
4977
+ `[cdl] unknown-ref: edge "${e.id}" \u306E from "${e.from}" \u304C node \u306B\u5B58\u5728\u3057\u307E\u305B\u3093${suggest(e.from, nodeIds, "node")}
4869
4978
  \u2192 \u4FEE\u6B63\u4F8B: .node("${e.from}", { lane: "...", stack: 0, kind: "actor", title: "..." }) \u3092 edge \u5BA3\u8A00\u524D\u306B\u8FFD\u52A0`
4870
4979
  );
4871
4980
  }
4872
4981
  if (!nodeIds.has(e.to)) {
4873
4982
  errors.push(
4874
- `[cdl] unknown-ref: edge "${e.id}" \u306E to "${e.to}" \u304C node \u306B\u5B58\u5728\u3057\u307E\u305B\u3093${suggest(e.to, nodeIds)}
4983
+ `[cdl] unknown-ref: edge "${e.id}" \u306E to "${e.to}" \u304C node \u306B\u5B58\u5728\u3057\u307E\u305B\u3093${suggest(e.to, nodeIds, "node")}
4875
4984
  \u2192 \u4FEE\u6B63\u4F8B: .node("${e.to}", { lane: "...", stack: 0, kind: "...", title: "..." }) \u3092 edge \u5BA3\u8A00\u524D\u306B\u8FFD\u52A0`
4876
4985
  );
4877
4986
  }
@@ -4888,6 +4997,16 @@ function validate(diag) {
4888
4997
  errors.push(`[cdl] unknown-ref: phase "${p.id}" \u306E activate "${id}" \u306F node \u3067\u3082 edge \u3067\u3082\u306A\u3044 (typo \u306E\u53EF\u80FD\u6027)`);
4889
4998
  }
4890
4999
  }
5000
+ for (const id of p.draw ?? []) {
5001
+ if (!nodeIds.has(id)) {
5002
+ errors.push(`[cdl] unknown-ref: phase "${p.id}" \u306E draw "${id}" \u306F node \u3067\u306F\u306A\u3044 (typo \u306E\u53EF\u80FD\u6027)${suggest(id, nodeIds, "node")}`);
5003
+ continue;
5004
+ }
5005
+ const kind = diag.nodes.find((n) => n.id === id)?.kind;
5006
+ if (kind !== "chart-line") {
5007
+ warnings.push(`phase "${p.id}" \u306E draw "${id}" \u306F kind "${kind}" \u3067\u3001 \u5DE6\u304B\u3089\u63CF\u304F\u52D5\u304D\u3092\u6301\u305F\u306A\u3044 (\u5BFE\u8C61\u306F chart-line)`);
5008
+ }
5009
+ }
4891
5010
  for (const t of p.tweens) {
4892
5011
  if (!stateIds.has(t.stateId)) {
4893
5012
  errors.push(`[cdl] unknown-ref: phase "${p.id}" tween \u5BFE\u8C61 state "${t.stateId}" \u672A\u5B9A\u7FA9`);
@@ -4906,10 +5025,7 @@ function validate(diag) {
4906
5025
  }
4907
5026
  }
4908
5027
  for (const n of diag.nodes) {
4909
- const checks = [];
4910
- if (n.value) checks.push(n.value);
4911
- if (n.rows) checks.push(...n.rows);
4912
- for (const text of checks) {
5028
+ for (const text of nodeTemplateTexts(n)) {
4913
5029
  for (const id of templateRefIds(text)) {
4914
5030
  if (!readableIds.has(id)) {
4915
5031
  warnings.push(`node "${n.id}" \u304C\u672A\u5B9A\u7FA9 state "${id}" \u3092\u53C2\u7167`);
@@ -4937,8 +5053,7 @@ function validate(diag) {
4937
5053
  for (const s of p.sets) stateUsed.add(s.stateId);
4938
5054
  }
4939
5055
  for (const n of diag.nodes) {
4940
- const texts2 = [n.value, ...n.rows ?? []].filter(Boolean);
4941
- for (const text of texts2) {
5056
+ for (const text of nodeTemplateTexts(n)) {
4942
5057
  for (const id of templateRefIds(text)) stateUsed.add(id);
4943
5058
  }
4944
5059
  }
@@ -5126,7 +5241,7 @@ function StateDiffCard({
5126
5241
  ] }) }),
5127
5242
  /* @__PURE__ */ jsxRuntime.jsx("tbody", { style: { color: "var(--cdl-text, #1a1f2a)" }, children: states.map((s) => {
5128
5243
  const initial = String(s.initial);
5129
- const current = stateValues[s.id] ?? initial;
5244
+ const current = Object.hasOwn(stateValues, s.id) ? stateValues[s.id] : initial;
5130
5245
  const changed = initial !== current;
5131
5246
  return /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
5132
5247
  /* @__PURE__ */ jsxRuntime.jsx("td", { className: "py-1 text-left text-[12px]", children: s.id }),
@@ -6436,21 +6551,32 @@ function resolveBoundText(raw, values) {
6436
6551
  return hasUnresolvedRef(resolved) ? { value: resolved, ok: false } : { value: resolved, ok: true };
6437
6552
  }
6438
6553
  function resolveTreeData(nodes, values) {
6439
- if (!nodes.some((n) => n.title.includes("{"))) return nodes;
6554
+ const \u8AAD\u3080 = nodes.some((n) => n.title.includes("{") || (n.subtitle?.includes("{") ?? false));
6555
+ if (!\u8AAD\u3080) return nodes;
6440
6556
  return nodes.map((n) => {
6441
6557
  const title = resolveBoundText(n.title, values);
6442
- const \u89E3\u6C7A\u5F8C = { ...n, title: title.value };
6443
- return title.ok ? \u89E3\u6C7A\u5F8C : { ...\u89E3\u6C7A\u5F8C, unresolved: true };
6558
+ const subtitle = n.subtitle === void 0 ? void 0 : resolveBoundText(n.subtitle, values);
6559
+ const \u89E3\u6C7A\u5F8C = {
6560
+ ...n,
6561
+ title: title.value,
6562
+ ...subtitle ? { subtitle: subtitle.value } : {}
6563
+ };
6564
+ return title.ok && (subtitle?.ok ?? true) ? \u89E3\u6C7A\u5F8C : { ...\u89E3\u6C7A\u5F8C, unresolved: true };
6444
6565
  });
6445
6566
  }
6446
6567
  function resolveMindData(data, values) {
6447
- const \u8AAD\u3080 = data.rootTitle.includes("{") || data.branches.some((b) => b.title.includes("{"));
6568
+ const \u8AAD\u3080 = data.rootTitle.includes("{") || data.branches.some((b) => b.title.includes("{") || (b.subtitle?.includes("{") ?? false));
6448
6569
  if (!\u8AAD\u3080) return data;
6449
6570
  const root = resolveBoundText(data.rootTitle, values);
6450
6571
  const branches = data.branches.map((b) => {
6451
6572
  const title = resolveBoundText(b.title, values);
6452
- const \u89E3\u6C7A\u5F8C2 = { ...b, title: title.value };
6453
- return title.ok ? \u89E3\u6C7A\u5F8C2 : { ...\u89E3\u6C7A\u5F8C2, unresolved: true };
6573
+ const subtitle = b.subtitle === void 0 ? void 0 : resolveBoundText(b.subtitle, values);
6574
+ const \u89E3\u6C7A\u5F8C2 = {
6575
+ ...b,
6576
+ title: title.value,
6577
+ ...subtitle ? { subtitle: subtitle.value } : {}
6578
+ };
6579
+ return title.ok && (subtitle?.ok ?? true) ? \u89E3\u6C7A\u5F8C2 : { ...\u89E3\u6C7A\u5F8C2, unresolved: true };
6454
6580
  });
6455
6581
  const \u89E3\u6C7A\u5F8C = { ...data, rootTitle: root.value, branches };
6456
6582
  return root.ok ? \u89E3\u6C7A\u5F8C : { ...\u89E3\u6C7A\u5F8C, unresolved: true };
@@ -6458,7 +6584,9 @@ function resolveMindData(data, values) {
6458
6584
  function ChartLineNode({
6459
6585
  node,
6460
6586
  active,
6461
- stateValues = {}
6587
+ stateValues = {},
6588
+ drawing = false,
6589
+ progress = 1
6462
6590
  }) {
6463
6591
  const x0 = node.cx - node.w / 2;
6464
6592
  const y0 = node.cy - node.h / 2;
@@ -6479,6 +6607,24 @@ function ChartLineNode({
6479
6607
  const xAt = (idx) => PAD_LEFT + (data.length <= 1 ? canvasW / 2 : canvasW * idx / (data.length - 1));
6480
6608
  const yAt = (v) => plotTop + plotH - (v - vMin) / vRange * plotH;
6481
6609
  const points = data.map((d, idx) => `${xAt(idx)},${yAt(d.value)}`).join(" ");
6610
+ const \u70B9\u307E\u3067\u306E\u9577\u3055 = [];
6611
+ let \u7DDA\u306E\u5168\u9577 = 0;
6612
+ let \u524D\u306E\u70B9;
6613
+ for (const [idx, datum] of data.entries()) {
6614
+ const \u4ECA\u306E\u70B9 = { x: xAt(idx), y: yAt(datum.value) };
6615
+ if (\u524D\u306E\u70B9) {
6616
+ \u7DDA\u306E\u5168\u9577 += Math.hypot(\u4ECA\u306E\u70B9.x - \u524D\u306E\u70B9.x, \u4ECA\u306E\u70B9.y - \u524D\u306E\u70B9.y);
6617
+ }
6618
+ \u70B9\u307E\u3067\u306E\u9577\u3055.push(\u7DDA\u306E\u5168\u9577);
6619
+ \u524D\u306E\u70B9 = \u4ECA\u306E\u70B9;
6620
+ }
6621
+ const \u4F38\u3073 = drawing ? Math.min(1, Math.max(0, Number.isFinite(progress) ? progress : 1)) : 1;
6622
+ const \u7DDA\u306E\u5148\u304C\u5C4A\u3044\u305F = (idx) => {
6623
+ if (!drawing) return true;
6624
+ if (data.length <= 1) return true;
6625
+ if (\u7DDA\u306E\u5168\u9577 === 0) return true;
6626
+ return \u4F38\u3073 >= (\u70B9\u307E\u3067\u306E\u9577\u3055[idx] ?? 0) / \u7DDA\u306E\u5168\u9577;
6627
+ };
6482
6628
  const gridCount = 4;
6483
6629
  const gridTicks = Array.from({ length: gridCount + 1 }, (_, i) => {
6484
6630
  const v = vMin + vRange * i / gridCount;
@@ -6556,7 +6702,8 @@ function ChartLineNode({
6556
6702
  stroke: "var(--cdl-tone-accent, #2d6a8f)",
6557
6703
  strokeWidth: 2.5,
6558
6704
  strokeLinejoin: "round",
6559
- strokeLinecap: "round"
6705
+ strokeLinecap: "round",
6706
+ ...drawing ? { pathLength: 1, strokeDasharray: 1, strokeDashoffset: 1 - \u4F38\u3073 } : {}
6560
6707
  }
6561
6708
  ),
6562
6709
  data.map((d, idx) => {
@@ -6567,40 +6714,43 @@ function ChartLineNode({
6567
6714
  const isValley = (prev === void 0 || d.value <= prev) && (next === void 0 || d.value <= next);
6568
6715
  const labelAbove = !isValley;
6569
6716
  const labelY = labelAbove ? cy - 12 : cy + 18;
6717
+ const \u51FA\u3059 = \u7DDA\u306E\u5148\u304C\u5C4A\u3044\u305F(idx);
6570
6718
  return /* @__PURE__ */ jsxRuntime.jsxs("g", { "data-cdl-unresolved": d.unresolved ? "true" : void 0, children: [
6571
- /* @__PURE__ */ jsxRuntime.jsx(
6572
- "circle",
6573
- {
6574
- cx,
6575
- cy,
6576
- r: 5,
6577
- fill: "var(--cdl-node-fill, #ffffff)",
6578
- stroke: "var(--cdl-tone-accent, #2d6a8f)",
6579
- strokeWidth: 2.5
6580
- }
6581
- ),
6582
- /* @__PURE__ */ jsxRuntime.jsx(
6583
- "circle",
6584
- {
6585
- cx,
6586
- cy,
6587
- r: 2.5,
6588
- fill: "var(--cdl-tone-accent, #2d6a8f)"
6589
- }
6590
- ),
6591
- /* @__PURE__ */ jsxRuntime.jsx(
6592
- "text",
6593
- {
6594
- "data-cdl-role": "chart-line-value",
6595
- x: cx,
6596
- y: labelY,
6597
- fontSize: 11,
6598
- fontWeight: 600,
6599
- textAnchor: "middle",
6600
- fill: "var(--cdl-text, #1a1f2a)",
6601
- children: d.value.toLocaleString()
6602
- }
6603
- ),
6719
+ \u51FA\u3059 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
6720
+ /* @__PURE__ */ jsxRuntime.jsx(
6721
+ "circle",
6722
+ {
6723
+ cx,
6724
+ cy,
6725
+ r: 5,
6726
+ fill: "var(--cdl-node-fill, #ffffff)",
6727
+ stroke: "var(--cdl-tone-accent, #2d6a8f)",
6728
+ strokeWidth: 2.5
6729
+ }
6730
+ ),
6731
+ /* @__PURE__ */ jsxRuntime.jsx(
6732
+ "circle",
6733
+ {
6734
+ cx,
6735
+ cy,
6736
+ r: 2.5,
6737
+ fill: "var(--cdl-tone-accent, #2d6a8f)"
6738
+ }
6739
+ ),
6740
+ /* @__PURE__ */ jsxRuntime.jsx(
6741
+ "text",
6742
+ {
6743
+ "data-cdl-role": "chart-line-value",
6744
+ x: cx,
6745
+ y: labelY,
6746
+ fontSize: 11,
6747
+ fontWeight: 600,
6748
+ textAnchor: "middle",
6749
+ fill: "var(--cdl-text, #1a1f2a)",
6750
+ children: d.value.toLocaleString()
6751
+ }
6752
+ )
6753
+ ] }),
6604
6754
  /* @__PURE__ */ jsxRuntime.jsx(
6605
6755
  "text",
6606
6756
  {
@@ -6861,16 +7011,53 @@ function GanttNode({
6861
7011
  const rowGap = 20;
6862
7012
  const rowCount = tasks.length || 1;
6863
7013
  const rowH = Math.max(28, (canvasH - rowGap * (rowCount + 1)) / rowCount);
6864
- const maxIdx = Math.max(1, Math.ceil(Math.max(...tasks.map((t) => t.endIdx + 1), 1)));
7014
+ const \u4F4D\u7F6E\u306E\u4E0A\u9650 = 1e6;
7015
+ const \u53CE\u3081\u305F = tasks.map((t) => {
7016
+ const \u59CB = Math.min(\u4F4D\u7F6E\u306E\u4E0A\u9650, Math.max(0, Number.isFinite(t.startIdx) ? t.startIdx : 0));
7017
+ const \u7D42 = Math.min(\u4F4D\u7F6E\u306E\u4E0A\u9650, Math.max(\u59CB, Number.isFinite(t.endIdx) ? t.endIdx : \u59CB));
7018
+ return { ...t, startIdx: \u59CB, endIdx: \u7D42 };
7019
+ });
7020
+ const \u5BA3\u8A00 = node.ganttAxisMax;
7021
+ const \u5BA3\u8A00\u3057\u305F\u5C3A = typeof \u5BA3\u8A00 === "number" && Number.isFinite(\u5BA3\u8A00) && \u5BA3\u8A00 > 0 ? Math.min(\u4F4D\u7F6E\u306E\u4E0A\u9650, Math.ceil(\u5BA3\u8A00)) : void 0;
7022
+ const maxIdx = \u5BA3\u8A00\u3057\u305F\u5C3A ?? Math.max(
7023
+ 1,
7024
+ Math.ceil(Math.max(...\u53CE\u3081\u305F.flatMap((t) => [t.startIdx + 1, t.endIdx + 1]), 1))
7025
+ );
6865
7026
  const cellW = canvasW / maxIdx;
7027
+ const \u6700\u5F8C\u306E\u76EE\u76DB\u308A = maxIdx - 1;
7028
+ const \u6574\u3048\u305F = \u5BA3\u8A00\u3057\u305F\u5C3A === void 0 ? \u53CE\u3081\u305F : \u53CE\u3081\u305F.map((t) => {
7029
+ const \u59CB = Math.min(t.startIdx, \u6700\u5F8C\u306E\u76EE\u76DB\u308A);
7030
+ const \u7D42 = Math.min(t.endIdx, \u6700\u5F8C\u306E\u76EE\u76DB\u308A);
7031
+ const \u5BC4\u305B\u305F = \u59CB !== t.startIdx || \u7D42 !== t.endIdx;
7032
+ return \u5BC4\u305B\u305F ? { ...t, startIdx: \u59CB, endIdx: \u7D42, clipped: true } : t;
7033
+ });
7034
+ const \u76EE\u76DB\u308A\u306E\u4E0A\u9650 = 200;
7035
+ const \u76EE\u76DB\u308A\u306E\u9593\u9694 = Math.max(1, Math.ceil(maxIdx / \u76EE\u76DB\u308A\u306E\u4E0A\u9650));
7036
+ const \u76EE\u76DB\u308A\u306E\u672C\u6570 = Math.ceil(maxIdx / \u76EE\u76DB\u308A\u306E\u9593\u9694);
6866
7037
  const xAt = (idx) => PAD_LEFT + idx * cellW;
6867
7038
  const yAt = (row) => PAD_TOP + rowGap + row * (rowH + rowGap);
6868
- const gridLabels = Array.from({ length: maxIdx }, (_, i) => {
6869
- const t = tasks.find((tt) => tt.startIdx === i);
6870
- return t?.startLabel ?? String(i);
6871
- });
6872
- const taskById = new Map(tasks.map((t) => [t.id, t]));
6873
- const rowOf = new Map(tasks.map((t, i) => [t.id, i]));
7039
+ const \u540D\u524D = { \u6574\u6570\u306E\u958B\u59CB: /* @__PURE__ */ new Map(), \u6574\u6570\u306E\u7D42\u4E86: /* @__PURE__ */ new Map(), \u7AEF\u6570\u306E\u958B\u59CB: /* @__PURE__ */ new Map(), \u7AEF\u6570\u306E\u7D42\u4E86: /* @__PURE__ */ new Map() };
7040
+ const \u7F6E\u304F = (\u8868, \u4F4D\u7F6E, label) => {
7041
+ if (label !== "" && Number.isFinite(\u4F4D\u7F6E) && !\u8868.has(\u4F4D\u7F6E)) \u8868.set(\u4F4D\u7F6E, label);
7042
+ };
7043
+ for (const t of \u53CE\u3081\u305F) {
7044
+ const \u6574\u6570\u306E\u958B\u59CB = Number.isInteger(t.startIdx);
7045
+ \u7F6E\u304F(\u6574\u6570\u306E\u958B\u59CB ? \u540D\u524D.\u6574\u6570\u306E\u958B\u59CB : \u540D\u524D.\u7AEF\u6570\u306E\u958B\u59CB, Math.round(t.startIdx), t.startLabel);
7046
+ const \u6574\u6570\u306E\u7D42\u4E86 = Number.isInteger(t.endIdx);
7047
+ \u7F6E\u304F(\u6574\u6570\u306E\u7D42\u4E86 ? \u540D\u524D.\u6574\u6570\u306E\u7D42\u4E86 : \u540D\u524D.\u7AEF\u6570\u306E\u7D42\u4E86, Math.round(t.endIdx), t.endLabel);
7048
+ }
7049
+ const \u540D\u524D\u3092\u5F15\u304F = (i) => \u540D\u524D.\u6574\u6570\u306E\u958B\u59CB.get(i) ?? \u540D\u524D.\u6574\u6570\u306E\u7D42\u4E86.get(i) ?? \u540D\u524D.\u7AEF\u6570\u306E\u958B\u59CB.get(i) ?? \u540D\u524D.\u7AEF\u6570\u306E\u7D42\u4E86.get(i) ?? "";
7050
+ const \u4F4D\u7F6E\u306E\u96C6\u5408 = /* @__PURE__ */ new Set();
7051
+ for (let n = 0; n < \u76EE\u76DB\u308A\u306E\u672C\u6570; n += 1) \u4F4D\u7F6E\u306E\u96C6\u5408.add(n * \u76EE\u76DB\u308A\u306E\u9593\u9694);
7052
+ for (const \u8868 of [\u540D\u524D.\u6574\u6570\u306E\u958B\u59CB, \u540D\u524D.\u6574\u6570\u306E\u7D42\u4E86, \u540D\u524D.\u7AEF\u6570\u306E\u958B\u59CB, \u540D\u524D.\u7AEF\u6570\u306E\u7D42\u4E86]) {
7053
+ for (const i of \u8868.keys()) if (i >= 0 && i < maxIdx) \u4F4D\u7F6E\u306E\u96C6\u5408.add(i);
7054
+ }
7055
+ const gridLabels = [...\u4F4D\u7F6E\u306E\u96C6\u5408].sort((a, b) => a - b).map((i) => ({ \u4F4D\u7F6E: i, label: \u540D\u524D\u3092\u5F15\u304F(i) }));
7056
+ const \u5E2F\u306E\u4F59\u767D = Math.min(6, cellW / 2);
7057
+ const \u5E2F\u306E\u5DE6 = (idx) => xAt(idx) + \u5E2F\u306E\u4F59\u767D / 2;
7058
+ const \u5E2F\u306E\u53F3 = (idx) => xAt(idx) + cellW - \u5E2F\u306E\u4F59\u767D / 2;
7059
+ const taskById = new Map(\u6574\u3048\u305F.map((t) => [t.id, t]));
7060
+ const rowOf = new Map(\u6574\u3048\u305F.map((t, i) => [t.id, i]));
6874
7061
  return /* @__PURE__ */ jsxRuntime.jsxs("g", { transform: `translate(${x0} ${y0})`, children: [
6875
7062
  /* @__PURE__ */ jsxRuntime.jsx(
6876
7063
  "rect",
@@ -6886,10 +7073,11 @@ function GanttNode({
6886
7073
  strokeWidth: active ? 3 : 1.25
6887
7074
  }
6888
7075
  ),
6889
- gridLabels.map((label, i) => /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
7076
+ gridLabels.map(({ \u4F4D\u7F6E: i, label }) => /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
6890
7077
  /* @__PURE__ */ jsxRuntime.jsx(
6891
7078
  "line",
6892
7079
  {
7080
+ "data-cdl-role": "gantt-grid",
6893
7081
  x1: xAt(i),
6894
7082
  y1: PAD_TOP,
6895
7083
  x2: xAt(i),
@@ -6900,9 +7088,10 @@ function GanttNode({
6900
7088
  opacity: 0.5
6901
7089
  }
6902
7090
  ),
6903
- /* @__PURE__ */ jsxRuntime.jsx(
7091
+ label === "" ? null : /* @__PURE__ */ jsxRuntime.jsx(
6904
7092
  "text",
6905
7093
  {
7094
+ "data-cdl-role": "gantt-tick",
6906
7095
  x: xAt(i) + cellW / 2,
6907
7096
  y: PAD_TOP + canvasH + 18,
6908
7097
  fontSize: 11,
@@ -6924,7 +7113,7 @@ function GanttNode({
6924
7113
  opacity: 0.5
6925
7114
  }
6926
7115
  ),
6927
- tasks.map((t, row) => /* @__PURE__ */ jsxRuntime.jsx(
7116
+ \u6574\u3048\u305F.map((t, row) => /* @__PURE__ */ jsxRuntime.jsx(
6928
7117
  "text",
6929
7118
  {
6930
7119
  x: PAD_LEFT - 12,
@@ -6937,16 +7126,17 @@ function GanttNode({
6937
7126
  },
6938
7127
  `label-${t.id}`
6939
7128
  )),
6940
- tasks.map((t, row) => {
6941
- const bx = xAt(t.startIdx) + 3;
7129
+ \u6574\u3048\u305F.map((t, row) => {
7130
+ const bx = \u5E2F\u306E\u5DE6(t.startIdx);
6942
7131
  const by = yAt(row);
6943
- const bw = Math.max(cellW - 6, (t.endIdx - t.startIdx + 1) * cellW - 6);
7132
+ const bw = Math.max(0, \u5E2F\u306E\u53F3(t.endIdx) - bx);
6944
7133
  return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
6945
7134
  /* @__PURE__ */ jsxRuntime.jsx(
6946
7135
  "rect",
6947
7136
  {
6948
7137
  "data-cdl-role": "gantt-bar",
6949
7138
  "data-cdl-unresolved": t.unresolved ? "true" : void 0,
7139
+ "data-cdl-clipped": "clipped" in t && t.clipped ? "true" : void 0,
6950
7140
  x: bx,
6951
7141
  y: by,
6952
7142
  width: bw,
@@ -6969,13 +7159,13 @@ function GanttNode({
6969
7159
  )
6970
7160
  ] }, `bar-${t.id}`);
6971
7161
  }),
6972
- tasks.filter((t) => t.dependsOn && taskById.has(t.dependsOn)).map((t) => {
7162
+ \u6574\u3048\u305F.filter((t) => t.dependsOn && taskById.has(t.dependsOn)).map((t) => {
6973
7163
  const parent = taskById.get(t.dependsOn);
6974
7164
  const parentRow = rowOf.get(parent.id);
6975
7165
  const childRow = rowOf.get(t.id);
6976
- const parentBarRight = xAt(parent.endIdx) + cellW - 3;
7166
+ const parentBarRight = \u5E2F\u306E\u53F3(parent.endIdx);
6977
7167
  const parentMidY = yAt(parentRow) + rowH / 2;
6978
- const childBarLeft = xAt(t.startIdx) + 3;
7168
+ const childBarLeft = \u5E2F\u306E\u5DE6(t.startIdx);
6979
7169
  const childMidY = yAt(childRow) + rowH / 2;
6980
7170
  const arrowHeadSize = 8;
6981
7171
  const arrowHeadTipGap = 10;
@@ -7017,6 +7207,49 @@ function toneColor3(tone) {
7017
7207
  if (!tone) return TONE.accent;
7018
7208
  return TONE[tone];
7019
7209
  }
7210
+
7211
+ // src/kinds/box-lines.ts
7212
+ var \u884C\u9001\u308A\u306E\u6BD4 = 1.15;
7213
+ var \u5B57\u306E\u9AD8\u3055\u306E\u6BD4 = 0.72;
7214
+ var \u4E0A\u4E0B\u306E\u4F59\u767D = 2;
7215
+ var \u5DE6\u53F3\u306E\u4F59\u767D = 8;
7216
+ function \u5E45\u3067\u5207\u308B(text, fontSize, \u4F7F\u3048\u308B\u5E45, \u6E2C\u308B = textWidth) {
7217
+ if (\u4F7F\u3048\u308B\u5E45 <= 0) return "";
7218
+ if (\u6E2C\u308B(text, fontSize) <= \u4F7F\u3048\u308B\u5E45) return text;
7219
+ const \u7701\u7565 = "\u2026";
7220
+ const \u7701\u7565\u306E\u5E45 = \u6E2C\u308B(\u7701\u7565, fontSize);
7221
+ if (\u7701\u7565\u306E\u5E45 > \u4F7F\u3048\u308B\u5E45) return "";
7222
+ let \u51FA = "";
7223
+ let \u5E45 = 0;
7224
+ for (const c of \u66F8\u8A18\u7D20\u306B\u5206\u3051\u308B(text)) {
7225
+ const \u5B57\u306E\u5E452 = \u6E2C\u308B(c, fontSize);
7226
+ if (\u5E45 + \u5B57\u306E\u5E452 + \u7701\u7565\u306E\u5E45 > \u4F7F\u3048\u308B\u5E45) break;
7227
+ \u5E45 += \u5B57\u306E\u5E452;
7228
+ \u51FA += c;
7229
+ }
7230
+ return `${\u51FA}${\u7701\u7565}`;
7231
+ }
7232
+ function \u66F8\u8A18\u7D20\u306B\u5206\u3051\u308B(text) {
7233
+ const \u533A\u5207\u308A = Intl.Segmenter;
7234
+ if (typeof \u533A\u5207\u308A !== "function") return [...text];
7235
+ return [...new \u533A\u5207\u308A(void 0, { granularity: "grapheme" }).segment(text)].map((g) => g.segment);
7236
+ }
7237
+ function \u7BB1\u306B\u7A4D\u3080(\u884C, \u7BB1\u306E\u5E45, \u7BB1\u306E\u9AD8\u3055) {
7238
+ const \u5019\u88DC = \u884C.filter((r, i) => i === 0 || r.text.trim() !== "");
7239
+ if (\u5019\u88DC.length === 0) return [];
7240
+ const \u4F7F\u3048\u308B\u9AD8\u3055 = \u7BB1\u306E\u9AD8\u3055 - \u4E0A\u4E0B\u306E\u4F59\u767D * 2;
7241
+ const \u9AD8\u3055 = (rs) => rs.reduce((a, r) => a + r.fontSize * \u884C\u9001\u308A\u306E\u6BD4, 0);
7242
+ const \u6B8B\u3059 = [...\u5019\u88DC];
7243
+ while (\u6B8B\u3059.length > 1 && \u9AD8\u3055(\u6B8B\u3059) > \u4F7F\u3048\u308B\u9AD8\u3055) \u6B8B\u3059.pop();
7244
+ const \u4F7F\u3048\u308B\u5E45 = \u7BB1\u306E\u5E45 - \u5DE6\u53F3\u306E\u4F59\u767D * 2;
7245
+ const \u7DCF\u9AD8 = \u9AD8\u3055(\u6B8B\u3059);
7246
+ let \u7A4D\u307F = -\u7DCF\u9AD8 / 2;
7247
+ return \u6B8B\u3059.map((r) => {
7248
+ const dy = \u7A4D\u307F + (\u884C\u9001\u308A\u306E\u6BD4 + \u5B57\u306E\u9AD8\u3055\u306E\u6BD4) / 2 * r.fontSize;
7249
+ \u7A4D\u307F += r.fontSize * \u884C\u9001\u308A\u306E\u6BD4;
7250
+ return { ...r, text: \u5E45\u3067\u5207\u308B(r.text, r.fontSize, \u4F7F\u3048\u308B\u5E45), dy };
7251
+ });
7252
+ }
7020
7253
  var MIND_TONES = ["accent", "teal", "success", "warning", "info", "error"];
7021
7254
  var MIND_BOX_GAP = 24;
7022
7255
  function MindMapNode({
@@ -7062,33 +7295,59 @@ function MindMapNode({
7062
7295
  },
7063
7296
  `edge-${i}`
7064
7297
  )),
7065
- layout2.nodes.map((n) => /* @__PURE__ */ jsxRuntime.jsxs("g", { "data-cdl-unresolved": n.unresolved ? "true" : void 0, children: [
7066
- /* @__PURE__ */ jsxRuntime.jsx(
7067
- "rect",
7068
- {
7069
- x: n.x - n.w / 2,
7070
- y: n.y - n.h / 2,
7071
- width: n.w,
7072
- height: n.h,
7073
- rx: n.isRoot ? 22 : 10,
7074
- fill: n.isRoot ? toneColor4(n.tone ?? "accent") : "var(--cdl-chip-fill, #f4f6fb)",
7075
- stroke: n.isRoot ? toneColor4(n.tone ?? "accent") : toneColor4(n.tone),
7076
- strokeWidth: n.isRoot ? 2 : 1.75
7077
- }
7078
- ),
7079
- /* @__PURE__ */ jsxRuntime.jsx(
7080
- "text",
7081
- {
7082
- x: n.x,
7083
- y: n.y + 5,
7084
- fontSize: n.isRoot ? 15 : 13,
7085
- fontWeight: n.isRoot ? 700 : 600,
7086
- textAnchor: "middle",
7087
- fill: n.isRoot ? "var(--cdl-text-on-tone, #ffffff)" : "var(--cdl-chip-text, #1a1f2a)",
7088
- children: n.title
7089
- }
7090
- )
7091
- ] }, `n-${n.id}`))
7298
+ layout2.nodes.map((n) => {
7299
+ const \u884C = \u7BB1\u306B\u7A4D\u3080(
7300
+ [
7301
+ { \u5F79\u5272: "title", text: n.title, fontSize: n.isRoot ? 15 : 13 },
7302
+ { \u5F79\u5272: "subtitle", text: n.subtitle ?? "", fontSize: 9 }
7303
+ ],
7304
+ n.w,
7305
+ n.h
7306
+ );
7307
+ const \u88DC\u8DB3\u3042\u308A = (n.subtitle ?? "").trim() !== "";
7308
+ const \u6587\u5B57\u306E\u8272 = n.isRoot ? "var(--cdl-text-on-tone, #ffffff)" : "var(--cdl-chip-text, #1a1f2a)";
7309
+ return /* @__PURE__ */ jsxRuntime.jsxs("g", { "data-cdl-unresolved": n.unresolved ? "true" : void 0, children: [
7310
+ /* @__PURE__ */ jsxRuntime.jsx(
7311
+ "rect",
7312
+ {
7313
+ x: n.x - n.w / 2,
7314
+ y: n.y - n.h / 2,
7315
+ width: n.w,
7316
+ height: n.h,
7317
+ rx: n.isRoot ? 22 : 10,
7318
+ fill: n.isRoot ? toneColor4(n.tone ?? "accent") : "var(--cdl-chip-fill, #f4f6fb)",
7319
+ stroke: n.isRoot ? toneColor4(n.tone ?? "accent") : toneColor4(n.tone),
7320
+ strokeWidth: n.isRoot ? 2 : 1.75
7321
+ }
7322
+ ),
7323
+ \u88DC\u8DB3\u3042\u308A ? \u884C.map((r) => /* @__PURE__ */ jsxRuntime.jsx(
7324
+ "text",
7325
+ {
7326
+ "data-cdl-role": r.\u5F79\u5272 === "subtitle" ? "mind-node-subtitle" : void 0,
7327
+ x: n.x,
7328
+ y: n.y + r.dy,
7329
+ fontSize: r.fontSize,
7330
+ fontWeight: r.\u5F79\u5272 === "title" ? n.isRoot ? 700 : 600 : 500,
7331
+ textAnchor: "middle",
7332
+ fill: \u6587\u5B57\u306E\u8272,
7333
+ opacity: r.\u5F79\u5272 === "subtitle" ? 0.85 : void 0,
7334
+ children: r.text
7335
+ },
7336
+ r.\u5F79\u5272
7337
+ )) : /* @__PURE__ */ jsxRuntime.jsx(
7338
+ "text",
7339
+ {
7340
+ x: n.x,
7341
+ y: n.y + 5,
7342
+ fontSize: n.isRoot ? 15 : 13,
7343
+ fontWeight: n.isRoot ? 700 : 600,
7344
+ textAnchor: "middle",
7345
+ fill: \u6587\u5B57\u306E\u8272,
7346
+ children: n.title
7347
+ }
7348
+ )
7349
+ ] }, `n-${n.id}`);
7350
+ })
7092
7351
  ] });
7093
7352
  }
7094
7353
  function computeMindMapLayout(mindData, cx, cy, canvasW, canvasH) {
@@ -7210,6 +7469,7 @@ function computeMindMapLayout(mindData, cx, cy, canvasW, canvasH) {
7210
7469
  nodes.push({
7211
7470
  id: node.id,
7212
7471
  title: node.title,
7472
+ ...node.subtitle !== void 0 ? { subtitle: node.subtitle } : {},
7213
7473
  x,
7214
7474
  y,
7215
7475
  w,
@@ -7414,6 +7674,9 @@ function QuadrantNode({
7414
7674
  const PAD_BOTTOM = 56;
7415
7675
  const AXIS_LABEL_FONT = 12;
7416
7676
  const PAD_MIN = 60;
7677
+ const ITEM_FONT = 13;
7678
+ const ITEM_PAD_X = 12;
7679
+ const ITEM_MIN_W = 48;
7417
7680
  const axisLabelPad = (text) => Math.max(PAD_MIN, measureTextWidth(`\u2190 ${text}`, { fontSize: AXIS_LABEL_FONT}) + 12);
7418
7681
  const padBudget = Math.max(0, node.w * 0.5 - PAD_MIN * 2);
7419
7682
  const wantLeft = axisLabelPad(data.xAxis.left) - PAD_MIN;
@@ -7509,6 +7772,12 @@ function QuadrantNode({
7509
7772
  const qxStart = (isLeft ? PAD_LEFT : cx) + 18;
7510
7773
  const qyStart = (isTop ? PAD_TOP : cy) + 44;
7511
7774
  const qHalfW = halfW - 36;
7775
+ const \u67A0\u306E\u4F59\u5730 = qHalfW;
7776
+ if (\u67A0\u306E\u4F59\u5730 <= 0) return null;
7777
+ const \u67A0\u306E\u5E45 = (title) => Math.min(
7778
+ \u67A0\u306E\u4F59\u5730,
7779
+ Math.max(ITEM_MIN_W, measureTextWidth(title, { fontSize: ITEM_FONT}) + ITEM_PAD_X * 2)
7780
+ );
7512
7781
  const qHalfH = halfH - 60;
7513
7782
  const gap = 10;
7514
7783
  const rowH = 34;
@@ -7523,7 +7792,7 @@ function QuadrantNode({
7523
7792
  "data-cdl-unresolved": it.unresolved ? "true" : void 0,
7524
7793
  x: qxStart,
7525
7794
  y: py,
7526
- width: qHalfW,
7795
+ width: \u67A0\u306E\u5E45(it.title),
7527
7796
  height: rowH,
7528
7797
  rx: 6,
7529
7798
  fill: "var(--cdl-node-fill, #ffffff)",
@@ -7531,12 +7800,35 @@ function QuadrantNode({
7531
7800
  strokeWidth: 1.5
7532
7801
  }
7533
7802
  ),
7534
- /* @__PURE__ */ jsxRuntime.jsx("text", { x: qxStart + 12, y: py + rowH / 2 + 5, fontSize: 13, fontWeight: 600, fill: "var(--cdl-text, #1a1f2a)", children: it.title })
7803
+ /* @__PURE__ */ jsxRuntime.jsx("text", { x: qxStart + ITEM_PAD_X, y: py + rowH / 2 + 5, fontSize: ITEM_FONT, fontWeight: 600, letterSpacing: 0, fill: "var(--cdl-text, #1a1f2a)", children: it.title })
7535
7804
  ] }, `item-${it.id}`);
7536
7805
  });
7537
7806
  })
7538
7807
  ] });
7539
7808
  }
7809
+ function \u7BB1\u306E\u6587\u5B57({
7810
+ \u884C,
7811
+ x,
7812
+ \u7BB1\u306E\u9AD8\u3055,
7813
+ fill,
7814
+ \u540D\u524D\u306E\u592A\u3055
7815
+ }) {
7816
+ return \u884C.map((r) => /* @__PURE__ */ jsxRuntime.jsx(
7817
+ "text",
7818
+ {
7819
+ "data-cdl-role": r.\u5F79\u5272 === "subtitle" ? "tree-node-subtitle" : void 0,
7820
+ x,
7821
+ y: \u7BB1\u306E\u9AD8\u3055 / 2 + r.dy,
7822
+ fontSize: r.fontSize,
7823
+ fontWeight: r.\u5F79\u5272 === "title" ? \u540D\u524D\u306E\u592A\u3055 : 500,
7824
+ textAnchor: "middle",
7825
+ fill,
7826
+ opacity: r.\u5F79\u5272 === "subtitle" ? 0.85 : void 0,
7827
+ children: r.text
7828
+ },
7829
+ r.\u5F79\u5272
7830
+ ));
7831
+ }
7540
7832
  function TreeNode({
7541
7833
  node,
7542
7834
  active,
@@ -7599,6 +7891,15 @@ function TreeNode({
7599
7891
  layout2.nodes.map((n) => {
7600
7892
  const accent = depthColors[Math.min(n.depth, depthColors.length - 1)] ?? depthColors[0];
7601
7893
  const isRoot = n.depth === 0;
7894
+ const \u884C = \u7BB1\u306B\u7A4D\u3080(
7895
+ [
7896
+ { \u5F79\u5272: "title", text: n.title, fontSize: isRoot ? 13 : 12 },
7897
+ { \u5F79\u5272: "subtitle", text: n.subtitle ?? "", fontSize: 9 }
7898
+ ],
7899
+ n.w,
7900
+ n.h
7901
+ );
7902
+ const \u88DC\u8DB3\u3042\u308A = (n.subtitle ?? "").trim() !== "";
7602
7903
  return /* @__PURE__ */ jsxRuntime.jsx(
7603
7904
  "g",
7604
7905
  {
@@ -7617,7 +7918,13 @@ function TreeNode({
7617
7918
  fill: `url(#${gradientId})`
7618
7919
  }
7619
7920
  ),
7620
- /* @__PURE__ */ jsxRuntime.jsx(
7921
+ \u88DC\u8DB3\u3042\u308A ? \u7BB1\u306E\u6587\u5B57({
7922
+ \u884C,
7923
+ x: n.w / 2,
7924
+ \u7BB1\u306E\u9AD8\u3055: n.h,
7925
+ fill: "var(--cdl-text-on-tone, #ffffff)",
7926
+ \u540D\u524D\u306E\u592A\u3055: 700
7927
+ }) : /* @__PURE__ */ jsxRuntime.jsx(
7621
7928
  "text",
7622
7929
  {
7623
7930
  x: n.w / 2,
@@ -7645,7 +7952,13 @@ function TreeNode({
7645
7952
  }
7646
7953
  ),
7647
7954
  /* @__PURE__ */ jsxRuntime.jsx("rect", { x: 0, y: 0, width: 4, height: n.h, rx: 2, fill: accent }),
7648
- /* @__PURE__ */ jsxRuntime.jsx(
7955
+ \u88DC\u8DB3\u3042\u308A ? \u7BB1\u306E\u6587\u5B57({
7956
+ \u884C,
7957
+ x: n.w / 2 + 2,
7958
+ \u7BB1\u306E\u9AD8\u3055: n.h,
7959
+ fill: "var(--cdl-chip-text, #1a1f2a)",
7960
+ \u540D\u524D\u306E\u592A\u3055: 600
7961
+ }) : /* @__PURE__ */ jsxRuntime.jsx(
7649
7962
  "text",
7650
7963
  {
7651
7964
  x: n.w / 2 + 2,
@@ -7726,6 +8039,7 @@ function computeTreeLayout(treeNodes, canvasW, canvasH) {
7726
8039
  nodes.push({
7727
8040
  id: n.id,
7728
8041
  title: n.title,
8042
+ ...n.subtitle !== void 0 ? { subtitle: n.subtitle } : {},
7729
8043
  x,
7730
8044
  y,
7731
8045
  w: nodeW,
@@ -8016,42 +8330,106 @@ var BOTTOM_PAD = 4;
8016
8330
  var ASCENT2 = 0.72;
8017
8331
  var DESCENT2 = 0.25;
8018
8332
  var ART_GAP = 4;
8333
+ var ART_MIN_BAND = 45;
8334
+ var \u843D\u3068\u3057\u3066\u3082\u63CF\u304F\u7A2E\u5225 = {
8335
+ \u80A9\u66F8: /* @__PURE__ */ new Set([
8336
+ // basement 系 = `commonLabel` が自前の帯に置く
8337
+ "shape-file",
8338
+ "shape-folder",
8339
+ "shape-cloud",
8340
+ "shape-cylinder",
8341
+ "shape-hexagon",
8342
+ "shape-diamond",
8343
+ "shape-stack",
8344
+ // software 系 = 固有 layout
8345
+ "shape-window",
8346
+ "shape-terminal",
8347
+ "shape-code-block",
8348
+ "shape-message-bubble",
8349
+ "shape-gear",
8350
+ // hardware 系のうち固有 layout を持つもの
8351
+ "shape-iot-sensor",
8352
+ "shape-robot-arm"
8353
+ ]),
8354
+ \u8AAC\u660E: /* @__PURE__ */ new Set([
8355
+ // basement 系 = `commonLabel` が自前の帯に置く
8356
+ "shape-file",
8357
+ "shape-folder",
8358
+ "shape-cloud",
8359
+ "shape-cylinder",
8360
+ "shape-hexagon",
8361
+ "shape-diamond",
8362
+ "shape-stack",
8363
+ // software 系 = 固有 layout
8364
+ "shape-window",
8365
+ "shape-message-bubble",
8366
+ "shape-gear",
8367
+ // hardware / blockchain 系のうち固有 layout を持つもの
8368
+ "shape-iot-sensor",
8369
+ "shape-robot-arm",
8370
+ "shape-blockchain-block"
8371
+ ])
8372
+ };
8373
+ function droppedLineIsHidden(kind, line) {
8374
+ return kind.startsWith("shape-") && !\u843D\u3068\u3057\u3066\u3082\u63CF\u304F\u7A2E\u5225[line].has(kind);
8375
+ }
8019
8376
  function labelPlan(node) {
8020
8377
  const \u9AD8\u3055 = Math.max(0, node.h);
8021
8378
  const \u5E95 = node.cy + \u9AD8\u3055 / 2;
8022
8379
  const \u5929 = node.cy - \u9AD8\u3055 / 2;
8023
- const \u7A4D\u3080 = (\u540D\u524D\u59272, \u8AAC\u660E\u3042\u308A2, \u80A9\u66F8\u3042\u308A2, \u4E0B\u4F59\u767D2) => {
8024
- const \u5C0F\u3055\u3044\u5B572 = Math.max(1, Math.round(\u540D\u524D\u59272 * SUB_RATIO));
8025
- let y = \u5E95 - \u4E0B\u4F59\u767D2;
8026
- const \u8AAC\u660E = \u8AAC\u660E\u3042\u308A2 ? { y, size: \u5C0F\u3055\u3044\u5B572 } : null;
8027
- if (\u8AAC\u660E !== null) y -= \u5C0F\u3055\u3044\u5B572 + LINE_GAP;
8028
- const \u540D\u524D = { y, size: \u540D\u524D\u59272 };
8029
- y -= \u540D\u524D\u59272 + LINE_GAP;
8030
- const \u80A9\u66F8 = \u80A9\u66F8\u3042\u308A2 ? { y, size: \u5C0F\u3055\u3044\u5B572 } : null;
8380
+ const \u7A4D\u3080 = (\u540D\u524D\u5927, \u8AAC\u660E\u3042\u308A2, \u80A9\u66F8\u3042\u308A2, \u4E0B\u4F59\u767D) => {
8381
+ const \u5C0F\u3055\u3044\u5B57 = Math.max(1, Math.round(\u540D\u524D\u5927 * SUB_RATIO));
8382
+ let y = \u5E95 - \u4E0B\u4F59\u767D;
8383
+ const \u8AAC\u660E = \u8AAC\u660E\u3042\u308A2 ? { y, size: \u5C0F\u3055\u3044\u5B57 } : null;
8384
+ if (\u8AAC\u660E !== null) y -= \u5C0F\u3055\u3044\u5B57 + LINE_GAP;
8385
+ const \u540D\u524D = { y, size: \u540D\u524D\u5927 };
8386
+ y -= \u540D\u524D\u5927 + LINE_GAP;
8387
+ const \u80A9\u66F8 = \u80A9\u66F8\u3042\u308A2 ? { y, size: \u5C0F\u3055\u3044\u5B57 } : null;
8031
8388
  const \u4E0A\u306E\u884C = \u80A9\u66F8 ?? \u540D\u524D;
8032
8389
  return { \u540D\u524D, \u8AAC\u660E, \u80A9\u66F8, \u4E0A\u7AEF: \u4E0A\u306E\u884C.y - \u4E0A\u306E\u884C.size * ASCENT2 };
8033
8390
  };
8034
8391
  const \u5165\u308B = (\u8AAC\u660E\u3042\u308A2, \u80A9\u66F8\u3042\u308A2) => \u7A4D\u3080(1, \u8AAC\u660E\u3042\u308A2, \u80A9\u66F8\u3042\u308A2, 1 * DESCENT2).\u4E0A\u7AEF >= \u5929;
8035
8392
  let \u8AAC\u660E\u3042\u308A = Boolean(node.subtitle);
8036
8393
  let \u80A9\u66F8\u3042\u308A = Boolean(node.eyebrow);
8037
- if (!\u5165\u308B(\u8AAC\u660E\u3042\u308A, \u80A9\u66F8\u3042\u308A)) \u80A9\u66F8\u3042\u308A = false;
8038
- if (!\u5165\u308B(\u8AAC\u660E\u3042\u308A, \u80A9\u66F8\u3042\u308A)) \u8AAC\u660E\u3042\u308A = false;
8039
- const \u4E0A\u9650 = Math.min(TITLE_MAX, Math.max(1, Math.round(\u9AD8\u3055 * TITLE_RATIO)));
8040
- let \u540D\u524D\u5927 = 0;
8041
- for (let size = \u4E0A\u9650; size >= 1; size--) {
8042
- const \u5C0F\u3055\u3044\u5B572 = Math.max(1, Math.round(size * SUB_RATIO));
8043
- const \u4E0B\u306E\u884C\u5927 = \u8AAC\u660E\u3042\u308A ? \u5C0F\u3055\u3044\u5B572 : size;
8044
- if (\u7A4D\u3080(size, \u8AAC\u660E\u3042\u308A, \u80A9\u66F8\u3042\u308A, \u4E0B\u306E\u884C\u5927 * DESCENT2).\u4E0A\u7AEF >= \u5929) {
8045
- \u540D\u524D\u5927 = size;
8046
- break;
8047
- }
8394
+ const \u843D\u3068\u3057\u305F = [];
8395
+ if (!\u5165\u308B(\u8AAC\u660E\u3042\u308A, \u80A9\u66F8\u3042\u308A) && \u80A9\u66F8\u3042\u308A) {
8396
+ \u80A9\u66F8\u3042\u308A = false;
8397
+ \u843D\u3068\u3057\u305F.push("\u80A9\u66F8");
8398
+ }
8399
+ if (!\u5165\u308B(\u8AAC\u660E\u3042\u308A, \u80A9\u66F8\u3042\u308A) && \u8AAC\u660E\u3042\u308A) {
8400
+ \u8AAC\u660E\u3042\u308A = false;
8401
+ \u843D\u3068\u3057\u305F.push("\u8AAC\u660E");
8402
+ }
8403
+ const \u7D44\u3080 = (\u8AAC\u660E\u3042\u308A2, \u80A9\u66F8\u3042\u308A2) => {
8404
+ const \u4E0A\u9650 = Math.min(TITLE_MAX, Math.max(1, Math.round(\u9AD8\u3055 * TITLE_RATIO)));
8405
+ let \u540D\u524D\u5927 = 0;
8406
+ for (let size = \u4E0A\u9650; size >= 1; size--) {
8407
+ const \u5C0F\u3055\u3044\u5B572 = Math.max(1, Math.round(size * SUB_RATIO));
8408
+ const \u4E0B\u306E\u884C\u5927 = \u8AAC\u660E\u3042\u308A2 ? \u5C0F\u3055\u3044\u5B572 : size;
8409
+ if (\u7A4D\u3080(size, \u8AAC\u660E\u3042\u308A2, \u80A9\u66F8\u3042\u308A2, \u4E0B\u306E\u884C\u5927 * DESCENT2).\u4E0A\u7AEF >= \u5929) {
8410
+ \u540D\u524D\u5927 = size;
8411
+ break;
8412
+ }
8413
+ }
8414
+ if (\u540D\u524D\u5927 === 0) \u540D\u524D\u5927 = \u9AD8\u3055 / (ASCENT2 + DESCENT2);
8415
+ const \u5C0F\u3055\u3044\u5B57 = Math.max(1, Math.round(\u540D\u524D\u5927 * SUB_RATIO));
8416
+ const \u5F35\u308A\u51FA\u3057 = (\u8AAC\u660E\u3042\u308A2 ? \u5C0F\u3055\u3044\u5B57 : \u540D\u524D\u5927) * DESCENT2;
8417
+ const \u4F59\u308A = Math.max(0, \u7A4D\u3080(\u540D\u524D\u5927, \u8AAC\u660E\u3042\u308A2, \u80A9\u66F8\u3042\u308A2, \u5F35\u308A\u51FA\u3057).\u4E0A\u7AEF - \u5929);
8418
+ const \u4E0B\u4F59\u767D = Math.min(Math.max(BOTTOM_PAD, \u5F35\u308A\u51FA\u3057), \u5F35\u308A\u51FA\u3057 + \u4F59\u308A);
8419
+ return \u7A4D\u3080(\u540D\u524D\u5927, \u8AAC\u660E\u3042\u308A2, \u80A9\u66F8\u3042\u308A2, \u4E0B\u4F59\u767D);
8420
+ };
8421
+ const \u7D75\u306E\u5E2F = (\u7D442) => Math.max(0, \u7D442.\u4E0A\u7AEF - ART_GAP - \u5929);
8422
+ let \u7D44 = \u7D44\u3080(\u8AAC\u660E\u3042\u308A, \u80A9\u66F8\u3042\u308A);
8423
+ if (\u7D75\u306E\u5E2F(\u7D44) < ART_MIN_BAND && \u80A9\u66F8\u3042\u308A) {
8424
+ \u80A9\u66F8\u3042\u308A = false;
8425
+ \u843D\u3068\u3057\u305F.push("\u80A9\u66F8");
8426
+ \u7D44 = \u7D44\u3080(\u8AAC\u660E\u3042\u308A, \u80A9\u66F8\u3042\u308A);
8427
+ }
8428
+ if (\u7D75\u306E\u5E2F(\u7D44) < ART_MIN_BAND && \u8AAC\u660E\u3042\u308A) {
8429
+ \u8AAC\u660E\u3042\u308A = false;
8430
+ \u843D\u3068\u3057\u305F.push("\u8AAC\u660E");
8431
+ \u7D44 = \u7D44\u3080(\u8AAC\u660E\u3042\u308A, \u80A9\u66F8\u3042\u308A);
8048
8432
  }
8049
- if (\u540D\u524D\u5927 === 0) \u540D\u524D\u5927 = \u9AD8\u3055 / (ASCENT2 + DESCENT2);
8050
- const \u5C0F\u3055\u3044\u5B57 = Math.max(1, Math.round(\u540D\u524D\u5927 * SUB_RATIO));
8051
- const \u5F35\u308A\u51FA\u3057 = (\u8AAC\u660E\u3042\u308A ? \u5C0F\u3055\u3044\u5B57 : \u540D\u524D\u5927) * DESCENT2;
8052
- const \u4F59\u308A = Math.max(0, \u7A4D\u3080(\u540D\u524D\u5927, \u8AAC\u660E\u3042\u308A, \u80A9\u66F8\u3042\u308A, \u5F35\u308A\u51FA\u3057).\u4E0A\u7AEF - \u5929);
8053
- const \u4E0B\u4F59\u767D = Math.min(Math.max(BOTTOM_PAD, \u5F35\u308A\u51FA\u3057), \u5F35\u308A\u51FA\u3057 + \u4F59\u308A);
8054
- const \u7D44 = \u7A4D\u3080(\u540D\u524D\u5927, \u8AAC\u660E\u3042\u308A, \u80A9\u66F8\u3042\u308A, \u4E0B\u4F59\u767D);
8055
8433
  const \u4E00\u756A\u5C0F\u3055\u3044\u884C = Math.min(
8056
8434
  ...[\u7D44.\u540D\u524D, \u7D44.\u8AAC\u660E, \u7D44.\u80A9\u66F8].filter((l) => l !== null).map((l) => l.size)
8057
8435
  );
@@ -8060,7 +8438,8 @@ function labelPlan(node) {
8060
8438
  \u8AAC\u660E: \u7D44.\u8AAC\u660E,
8061
8439
  \u80A9\u66F8: \u7D44.\u80A9\u66F8,
8062
8440
  top: \u7D44.\u4E0A\u7AEF,
8063
- \u8AAD\u3081\u308B: \u4E00\u756A\u5C0F\u3055\u3044\u884C >= TITLE_MIN
8441
+ \u8AAD\u3081\u308B: \u4E00\u756A\u5C0F\u3055\u3044\u884C >= TITLE_MIN,
8442
+ \u843D\u3068\u3057\u305F
8064
8443
  };
8065
8444
  }
8066
8445
  function shapeRegion(node) {
@@ -10328,6 +10707,15 @@ function ShapeSmartContractNode({ node, active }) {
10328
10707
  bottomLabel2(node, r.cx, r.label)
10329
10708
  ] });
10330
10709
  }
10710
+ var \u5B57\u306E\u5E45 = (text, size) => Math.max(displayWidth(text) * size * MONO_ADVANCE_RATIO, textWidth(text, size));
10711
+ var BLOCK_DEFAULT_FIELDS = [
10712
+ { label: "hash", value: "0xaf31c9d2..." },
10713
+ { label: "prev", value: "0x7b3f92c1..." },
10714
+ { label: "merkle", value: "0xc1892f4a..." },
10715
+ { label: "nonce", value: "82,914" },
10716
+ { label: "tx", value: "142 tx" }
10717
+ ];
10718
+ var BLOCK_DEFAULT_TITLE = "Block #421,873";
10331
10719
  function ShapeBlockchainBlockNode({ node, active }) {
10332
10720
  const frame = commonFrame4(active);
10333
10721
  const r = shapeRegion(node);
@@ -10337,21 +10725,22 @@ function ShapeBlockchainBlockNode({ node, active }) {
10337
10725
  const cy = r.cy;
10338
10726
  const x = cx - bw / 2;
10339
10727
  const y = cy - bh / 2;
10728
+ const \u6E21\u3055\u308C\u305F = (v) => (v ?? "").trim();
10729
+ const \u898B\u51FA\u3057 = \u6E21\u3055\u308C\u305F(node.title) || BLOCK_DEFAULT_TITLE;
10730
+ const fields = BLOCK_DEFAULT_FIELDS.map((f) => ({
10731
+ label: f.label,
10732
+ value: f.label === "hash" ? \u6E21\u3055\u308C\u305F(node.subtitle) || f.value : f.value
10733
+ }));
10734
+ const \u898B\u51FA\u3057\u306E\u5E45 = bw - 14 - 30;
10340
10735
  const art = /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
10341
10736
  /* @__PURE__ */ jsxRuntime.jsx("rect", { x: x + 6, y: y + 6, width: bw, height: bh, rx: 8, fill: frame.stroke, opacity: 0.2, stroke: "none" }),
10342
10737
  /* @__PURE__ */ jsxRuntime.jsx("rect", { "data-cdl-role": "node-body", x, y, width: bw, height: bh, rx: 8, ...frame }),
10343
10738
  /* @__PURE__ */ jsxRuntime.jsx("path", { d: `M ${x} ${y + 32} L ${x} ${y + 8} Q ${x} ${y}, ${x + 8} ${y} L ${x + bw - 8} ${y} Q ${x + bw} ${y}, ${x + bw} ${y + 8} L ${x + bw} ${y + 32} Z`, fill: "var(--cdl-tone-accent, #2d6a8f)", fillOpacity: 0.9, stroke: "none" }),
10344
- /* @__PURE__ */ jsxRuntime.jsx("text", { x: x + 14, y: y + 22, fontSize: 13, fontWeight: 700, fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fill: "#ffffff", children: "Block #421,873" }),
10739
+ /* @__PURE__ */ jsxRuntime.jsx("text", { "data-cdl-role": "blockchain-block-title", x: x + 14, y: y + 22, fontSize: 13, fontWeight: 700, fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fill: "#ffffff", children: \u5E45\u3067\u5207\u308B(\u898B\u51FA\u3057, 13, \u898B\u51FA\u3057\u306E\u5E45, \u5B57\u306E\u5E45) }),
10345
10740
  /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: x + bw - 16, cy: y + 16, r: 5.5, fill: "#7cd88f", stroke: "#ffffff", strokeWidth: 1.5 }),
10346
- [
10347
- { label: "hash", value: "0xaf31c9d2..." },
10348
- { label: "prev", value: "0x7b3f92c1..." },
10349
- { label: "merkle", value: "0xc1892f4a..." },
10350
- { label: "nonce", value: "82,914" },
10351
- { label: "tx", value: "142 tx" }
10352
- ].map((f, i) => /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
10741
+ fields.map((f, i) => /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
10353
10742
  /* @__PURE__ */ jsxRuntime.jsx("text", { x: x + 14, y: y + 54 + i * 22, fontSize: 10.5, fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fill: "var(--cdl-text-dim, #5a6270)", children: f.label }),
10354
- /* @__PURE__ */ jsxRuntime.jsx("text", { x: x + bw - 14, y: y + 54 + i * 22, fontSize: 11, fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontWeight: 600, textAnchor: "end", fill: "var(--cdl-text, #1a1f2a)", children: f.value }),
10743
+ /* @__PURE__ */ jsxRuntime.jsx("text", { "data-cdl-role": `blockchain-block-${f.label}`, x: x + bw - 14, y: y + 54 + i * 22, fontSize: 11, fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontWeight: 600, textAnchor: "end", fill: "var(--cdl-text, #1a1f2a)", children: \u5E45\u3067\u5207\u308B(f.value, 11, bw - 28 - \u5B57\u306E\u5E45(f.label, 10.5) - 8, \u5B57\u306E\u5E45) }),
10355
10744
  /* @__PURE__ */ jsxRuntime.jsx("line", { x1: x + 14, y1: y + 60 + i * 22, x2: x + bw - 14, y2: y + 60 + i * 22, stroke: "var(--cdl-divider, #d4d4d8)", strokeWidth: 0.5, opacity: 0.5 })
10356
10745
  ] }, i)),
10357
10746
  /* @__PURE__ */ jsxRuntime.jsxs("g", { transform: `translate(${cx - 32}, ${y + bh - 26})`, children: [
@@ -11837,6 +12226,7 @@ function ShapeCustomerServiceNode({ node, active }) {
11837
12226
  function CdlNodeView({
11838
12227
  node,
11839
12228
  active,
12229
+ drawing = false,
11840
12230
  progress,
11841
12231
  stateValues,
11842
12232
  currentPhaseId
@@ -11886,7 +12276,16 @@ function CdlNodeView({
11886
12276
  // 数と語を取る 7 種に対し、mind-map / tree-hierarchy は **名前だけ** を取る (#467)。
11887
12277
  // 親子の繋がりと、描かれない欄 (補足) は解決しない。
11888
12278
  case "chart-line":
11889
- return /* @__PURE__ */ jsxRuntime.jsx(ChartLineNode, { node: resolvedNode, active, stateValues });
12279
+ return /* @__PURE__ */ jsxRuntime.jsx(
12280
+ ChartLineNode,
12281
+ {
12282
+ node: resolvedNode,
12283
+ active,
12284
+ stateValues,
12285
+ drawing,
12286
+ progress
12287
+ }
12288
+ );
11890
12289
  case "chart-pie":
11891
12290
  return /* @__PURE__ */ jsxRuntime.jsx(ChartPieNode, { node: resolvedNode, active, stateValues });
11892
12291
  case "chart-bar":
@@ -12047,6 +12446,29 @@ function CdlNodeView({
12047
12446
  }
12048
12447
  );
12049
12448
  }
12449
+
12450
+ // src/layout/lifeline.ts
12451
+ function lifelineStartYs(nodes) {
12452
+ const topmost = /* @__PURE__ */ new Map();
12453
+ for (const n of nodes) {
12454
+ const cur = topmost.get(n.lane);
12455
+ if (cur === void 0 || n.cy < cur.cy) topmost.set(n.lane, n);
12456
+ }
12457
+ const starts = /* @__PURE__ */ new Map();
12458
+ for (const [laneId, n] of topmost) starts.set(laneId, n.cy + n.h / 2);
12459
+ return starts;
12460
+ }
12461
+ function uniformLifelineEndY(lanes, nodes, starts = lifelineStartYs(nodes)) {
12462
+ const lifelineLanes = lanes.filter((l) => l.lifeline);
12463
+ let deepestStart = Number.NEGATIVE_INFINITY;
12464
+ for (const lane of lifelineLanes) {
12465
+ const start = starts.get(lane.id);
12466
+ if (start !== void 0) deepestStart = Math.max(deepestStart, start);
12467
+ }
12468
+ const footerTops = lifelineFooterNodes(lanes, nodes).map((f) => footerShapeDrawTop(f)).filter((top) => top > deepestStart);
12469
+ if (footerTops.length > 0) return Math.min(...footerTops);
12470
+ return lifelineLanes[0] ? lifelineLanes[0].y + lifelineLanes[0].height : 0;
12471
+ }
12050
12472
  function CdlStage({
12051
12473
  laid,
12052
12474
  currentPhase,
@@ -12057,15 +12479,14 @@ function CdlStage({
12057
12479
  svgRef
12058
12480
  }) {
12059
12481
  const activeSet = new Set(currentPhase?.activate ?? []);
12060
- const lifelineLanes = laid.lanes.filter((l) => l.lifeline);
12061
- const { footerTops, footerDropById } = react.useMemo(() => {
12062
- const footers = lifelineFooterNodes(laid.lanes, laid.nodes);
12482
+ const drawSet = new Set(currentPhase?.draw ?? []);
12483
+ const { lifelineStarts, uniformFooterTop } = react.useMemo(() => {
12484
+ const starts = lifelineStartYs(laid.nodes);
12063
12485
  return {
12064
- footerTops: footers.map((f) => f.cy - f.h / 2),
12065
- footerDropById: footerShapeDropsOf(footers)
12486
+ lifelineStarts: starts,
12487
+ uniformFooterTop: uniformLifelineEndY(laid.lanes, laid.nodes, starts)
12066
12488
  };
12067
12489
  }, [laid]);
12068
- const uniformFooterTop = footerTops.length > 0 ? Math.min(...footerTops) : lifelineLanes[0] ? lifelineLanes[0].y + lifelineLanes[0].height : 0;
12069
12490
  const diagramScale = laid.diagramScale ?? 1;
12070
12491
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: compact ? "px-3 py-3" : "px-6 py-6", style: compact ? void 0 : { minHeight: 460 }, children: [
12071
12492
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -12152,11 +12573,8 @@ function CdlStage({
12152
12573
  }
12153
12574
  ),
12154
12575
  lane.lifeline && (() => {
12155
- const laneNodes = laid.nodes.filter((n) => n.lane === lane.id);
12156
- if (laneNodes.length === 0) return null;
12157
- const sortedByCy = laneNodes.slice().sort((a, b) => a.cy - b.cy);
12158
- const header = sortedByCy[0];
12159
- const y1 = header.cy + header.h / 2;
12576
+ const y1 = lifelineStarts.get(lane.id);
12577
+ if (y1 === void 0) return null;
12160
12578
  const y2 = uniformFooterTop;
12161
12579
  if (y2 <= y1) return null;
12162
12580
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -12198,13 +12616,13 @@ function CdlStage({
12198
12616
  {
12199
12617
  node,
12200
12618
  active: activeSet.has(node.id),
12619
+ drawing: drawSet.has(node.id),
12201
12620
  progress,
12202
12621
  stateValues,
12203
12622
  currentPhaseId: currentPhase?.id
12204
12623
  }
12205
12624
  );
12206
- const dy = footerDropById.get(node.id);
12207
- return dy === void 0 ? /* @__PURE__ */ jsxRuntime.jsx("g", { children: view }, node.id) : /* @__PURE__ */ jsxRuntime.jsx("g", { transform: `translate(0, ${dy})`, children: view }, node.id);
12625
+ return /* @__PURE__ */ jsxRuntime.jsx("g", { children: view }, node.id);
12208
12626
  }),
12209
12627
  laid.edges.filter((edge) => Boolean(edge.dThrough)).map((edge) => /* @__PURE__ */ jsxRuntime.jsx(CdlEdgeGlowView, { edge, active: activeSet.has(edge.id) }, `glow-${edge.id}`)),
12210
12628
  laid.edges.map((edge) => /* @__PURE__ */ jsxRuntime.jsx(
@@ -21086,6 +21504,7 @@ function emptyCounts() {
21086
21504
  "validate-performance-budget": 0,
21087
21505
  "node-inside-viewbox": 0,
21088
21506
  "node-inside-lane": 0,
21507
+ "shape-label-dropped": 0,
21089
21508
  "edge-inside-viewbox": 0,
21090
21509
  "lane-label-inside-viewbox": 0,
21091
21510
  "lane-label-overlap": 0,
@@ -21739,6 +22158,7 @@ function runAxes(laid, diag, violations, counts, push, profile, skippedAxes) {
21739
22158
  for (const n of laid.nodes) {
21740
22159
  if (n.id.endsWith("-spacer")) continue;
21741
22160
  if (!rendersTitleText(n.kind)) continue;
22161
+ if (fitsOwnTitleWidth(n.kind)) continue;
21742
22162
  if (!isRenderedNode(n)) continue;
21743
22163
  const titleTrimmed = n.title?.trim() ?? "";
21744
22164
  if (!titleTrimmed) continue;
@@ -21995,6 +22415,15 @@ function runAxes(laid, diag, violations, counts, push, profile, skippedAxes) {
21995
22415
  );
21996
22416
  }
21997
22417
  }
22418
+ for (const n of laid.nodes) {
22419
+ const \u843D\u3068\u3057\u305F = labelPlan(n).\u843D\u3068\u3057\u305F.filter((line) => droppedLineIsHidden(n.kind, line));
22420
+ if (\u843D\u3068\u3057\u305F.length === 0) continue;
22421
+ push(
22422
+ "shape-label-dropped",
22423
+ `node "${n.id}" (${n.kind}, ${n.w.toFixed(0)}x${n.h.toFixed(0)}) \u306F\u66F8\u3044\u305F ${\u843D\u3068\u3057\u305F.join(" / ")} \u304C\u63CF\u304B\u308C\u306A\u3044\u3002 \u7BB1\u3092\u9AD8\u304F\u3059\u308B\u304B\u3001 \u305D\u306E\u884C\u3092\u6D88\u3059`,
22424
+ "warn"
22425
+ );
22426
+ }
21998
22427
  for (const e of laid.edges) {
21999
22428
  const segs = flattenPathSegments(extractPathSegments(e.d));
22000
22429
  if (segs.length === 0) continue;
@@ -23184,14 +23613,7 @@ function CdlDiagramView({ diagram, laid: laidProp, hideHeader = false, hideMiniP
23184
23613
  );
23185
23614
  const stateValues = react.useMemo(() => {
23186
23615
  if (!isInteractive) return baseStateValues;
23187
- const merged = { ...baseStateValues };
23188
- for (const key of Object.keys(interactive.stateOverrides)) {
23189
- const v = interactive.stateOverrides[key];
23190
- if (typeof v === "number" || typeof v === "string" || typeof v === "boolean") {
23191
- merged[key] = String(v);
23192
- }
23193
- }
23194
- return merged;
23616
+ return mergeStateValues(baseStateValues, interactive.stateOverrides);
23195
23617
  }, [isInteractive, baseStateValues, interactive.stateOverrides]);
23196
23618
  return /* @__PURE__ */ jsxRuntime.jsxs(
23197
23619
  "div",