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