@cardenelabs/cdl 0.7.0 → 0.9.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 (42) hide show
  1. package/CHANGELOG.md +401 -0
  2. package/SPEC.md +32 -17
  3. package/dist/index.cjs +700 -306
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.d.cts +68 -14
  6. package/dist/index.d.ts +68 -14
  7. package/dist/index.js +700 -306
  8. package/dist/index.js.map +1 -1
  9. package/dist/react.cjs +572 -196
  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 +572 -196
  14. package/dist/react.js.map +1 -1
  15. package/dist/{render-DquCvgOB.d.cts → render-hmFRAabt.d.cts} +33 -2
  16. package/dist/{render-DquCvgOB.d.ts → render-hmFRAabt.d.ts} +33 -2
  17. package/package.json +6 -3
  18. package/src/kinds/box-lines.ts +120 -0
  19. package/src/kinds/gantt.tsx +152 -24
  20. package/src/kinds/mind-map.tsx +50 -12
  21. package/src/kinds/quadrant.tsx +28 -2
  22. package/src/kinds/shape-blockchain.tsx +65 -12
  23. package/src/kinds/shape-region.tsx +152 -34
  24. package/src/kinds/tree.tsx +92 -20
  25. package/src/layout/collisions.ts +10 -2
  26. package/src/layout/footer-shape.ts +86 -24
  27. package/src/layout/lifeline.ts +80 -0
  28. package/src/layout/spec.ts +21 -2
  29. package/src/layout/text-width.ts +92 -4
  30. package/src/layout/viewbox.ts +0 -8
  31. package/src/layout.ts +8 -1
  32. package/src/presets.ts +120 -22
  33. package/src/render/header.tsx +3 -1
  34. package/src/render/payload-binding.ts +31 -8
  35. package/src/render/stage.tsx +17 -34
  36. package/src/render/template-fields.ts +212 -0
  37. package/src/render/utils.ts +32 -2
  38. package/src/render.tsx +2 -9
  39. package/src/types.ts +33 -2
  40. package/src/validate.ts +27 -9
  41. package/src/visual-validate.ts +34 -1
  42. 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,7 +4940,13 @@ 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)]);
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
+ ]);
4841
4950
  const suggest = (target, candidates) => {
4842
4951
  const list = [...candidates];
4843
4952
  if (list.length === 0) return "";
@@ -4904,10 +5013,7 @@ function validate(diag) {
4904
5013
  }
4905
5014
  }
4906
5015
  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) {
5016
+ for (const text of nodeTemplateTexts(n)) {
4911
5017
  for (const id of templateRefIds(text)) {
4912
5018
  if (!readableIds.has(id)) {
4913
5019
  warnings.push(`node "${n.id}" \u304C\u672A\u5B9A\u7FA9 state "${id}" \u3092\u53C2\u7167`);
@@ -4935,8 +5041,7 @@ function validate(diag) {
4935
5041
  for (const s of p.sets) stateUsed.add(s.stateId);
4936
5042
  }
4937
5043
  for (const n of diag.nodes) {
4938
- const texts2 = [n.value, ...n.rows ?? []].filter(Boolean);
4939
- for (const text of texts2) {
5044
+ for (const text of nodeTemplateTexts(n)) {
4940
5045
  for (const id of templateRefIds(text)) stateUsed.add(id);
4941
5046
  }
4942
5047
  }
@@ -5124,7 +5229,7 @@ function StateDiffCard({
5124
5229
  ] }) }),
5125
5230
  /* @__PURE__ */ jsx("tbody", { style: { color: "var(--cdl-text, #1a1f2a)" }, children: states.map((s) => {
5126
5231
  const initial = String(s.initial);
5127
- const current = stateValues[s.id] ?? initial;
5232
+ const current = Object.hasOwn(stateValues, s.id) ? stateValues[s.id] : initial;
5128
5233
  const changed = initial !== current;
5129
5234
  return /* @__PURE__ */ jsxs("tr", { children: [
5130
5235
  /* @__PURE__ */ jsx("td", { className: "py-1 text-left text-[12px]", children: s.id }),
@@ -6434,21 +6539,32 @@ function resolveBoundText(raw, values) {
6434
6539
  return hasUnresolvedRef(resolved) ? { value: resolved, ok: false } : { value: resolved, ok: true };
6435
6540
  }
6436
6541
  function resolveTreeData(nodes, values) {
6437
- if (!nodes.some((n) => n.title.includes("{"))) return nodes;
6542
+ const \u8AAD\u3080 = nodes.some((n) => n.title.includes("{") || (n.subtitle?.includes("{") ?? false));
6543
+ if (!\u8AAD\u3080) return nodes;
6438
6544
  return nodes.map((n) => {
6439
6545
  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 };
6546
+ const subtitle = n.subtitle === void 0 ? void 0 : resolveBoundText(n.subtitle, values);
6547
+ const \u89E3\u6C7A\u5F8C = {
6548
+ ...n,
6549
+ title: title.value,
6550
+ ...subtitle ? { subtitle: subtitle.value } : {}
6551
+ };
6552
+ return title.ok && (subtitle?.ok ?? true) ? \u89E3\u6C7A\u5F8C : { ...\u89E3\u6C7A\u5F8C, unresolved: true };
6442
6553
  });
6443
6554
  }
6444
6555
  function resolveMindData(data, values) {
6445
- const \u8AAD\u3080 = data.rootTitle.includes("{") || data.branches.some((b) => b.title.includes("{"));
6556
+ const \u8AAD\u3080 = data.rootTitle.includes("{") || data.branches.some((b) => b.title.includes("{") || (b.subtitle?.includes("{") ?? false));
6446
6557
  if (!\u8AAD\u3080) return data;
6447
6558
  const root = resolveBoundText(data.rootTitle, values);
6448
6559
  const branches = data.branches.map((b) => {
6449
6560
  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 };
6561
+ const subtitle = b.subtitle === void 0 ? void 0 : resolveBoundText(b.subtitle, values);
6562
+ const \u89E3\u6C7A\u5F8C2 = {
6563
+ ...b,
6564
+ title: title.value,
6565
+ ...subtitle ? { subtitle: subtitle.value } : {}
6566
+ };
6567
+ return title.ok && (subtitle?.ok ?? true) ? \u89E3\u6C7A\u5F8C2 : { ...\u89E3\u6C7A\u5F8C2, unresolved: true };
6452
6568
  });
6453
6569
  const \u89E3\u6C7A\u5F8C = { ...data, rootTitle: root.value, branches };
6454
6570
  return root.ok ? \u89E3\u6C7A\u5F8C : { ...\u89E3\u6C7A\u5F8C, unresolved: true };
@@ -6859,16 +6975,53 @@ function GanttNode({
6859
6975
  const rowGap = 20;
6860
6976
  const rowCount = tasks.length || 1;
6861
6977
  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)));
6978
+ const \u4F4D\u7F6E\u306E\u4E0A\u9650 = 1e6;
6979
+ const \u53CE\u3081\u305F = tasks.map((t) => {
6980
+ const \u59CB = Math.min(\u4F4D\u7F6E\u306E\u4E0A\u9650, Math.max(0, Number.isFinite(t.startIdx) ? t.startIdx : 0));
6981
+ const \u7D42 = Math.min(\u4F4D\u7F6E\u306E\u4E0A\u9650, Math.max(\u59CB, Number.isFinite(t.endIdx) ? t.endIdx : \u59CB));
6982
+ return { ...t, startIdx: \u59CB, endIdx: \u7D42 };
6983
+ });
6984
+ const \u5BA3\u8A00 = node.ganttAxisMax;
6985
+ 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;
6986
+ const maxIdx = \u5BA3\u8A00\u3057\u305F\u5C3A ?? Math.max(
6987
+ 1,
6988
+ Math.ceil(Math.max(...\u53CE\u3081\u305F.flatMap((t) => [t.startIdx + 1, t.endIdx + 1]), 1))
6989
+ );
6863
6990
  const cellW = canvasW / maxIdx;
6991
+ const \u6700\u5F8C\u306E\u76EE\u76DB\u308A = maxIdx - 1;
6992
+ const \u6574\u3048\u305F = \u5BA3\u8A00\u3057\u305F\u5C3A === void 0 ? \u53CE\u3081\u305F : \u53CE\u3081\u305F.map((t) => {
6993
+ const \u59CB = Math.min(t.startIdx, \u6700\u5F8C\u306E\u76EE\u76DB\u308A);
6994
+ const \u7D42 = Math.min(t.endIdx, \u6700\u5F8C\u306E\u76EE\u76DB\u308A);
6995
+ const \u5BC4\u305B\u305F = \u59CB !== t.startIdx || \u7D42 !== t.endIdx;
6996
+ return \u5BC4\u305B\u305F ? { ...t, startIdx: \u59CB, endIdx: \u7D42, clipped: true } : t;
6997
+ });
6998
+ const \u76EE\u76DB\u308A\u306E\u4E0A\u9650 = 200;
6999
+ const \u76EE\u76DB\u308A\u306E\u9593\u9694 = Math.max(1, Math.ceil(maxIdx / \u76EE\u76DB\u308A\u306E\u4E0A\u9650));
7000
+ const \u76EE\u76DB\u308A\u306E\u672C\u6570 = Math.ceil(maxIdx / \u76EE\u76DB\u308A\u306E\u9593\u9694);
6864
7001
  const xAt = (idx) => PAD_LEFT + idx * cellW;
6865
7002
  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]));
7003
+ 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() };
7004
+ const \u7F6E\u304F = (\u8868, \u4F4D\u7F6E, label) => {
7005
+ if (label !== "" && Number.isFinite(\u4F4D\u7F6E) && !\u8868.has(\u4F4D\u7F6E)) \u8868.set(\u4F4D\u7F6E, label);
7006
+ };
7007
+ for (const t of \u53CE\u3081\u305F) {
7008
+ const \u6574\u6570\u306E\u958B\u59CB = Number.isInteger(t.startIdx);
7009
+ \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);
7010
+ const \u6574\u6570\u306E\u7D42\u4E86 = Number.isInteger(t.endIdx);
7011
+ \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);
7012
+ }
7013
+ 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) ?? "";
7014
+ const \u4F4D\u7F6E\u306E\u96C6\u5408 = /* @__PURE__ */ new Set();
7015
+ 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);
7016
+ 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]) {
7017
+ for (const i of \u8868.keys()) if (i >= 0 && i < maxIdx) \u4F4D\u7F6E\u306E\u96C6\u5408.add(i);
7018
+ }
7019
+ const gridLabels = [...\u4F4D\u7F6E\u306E\u96C6\u5408].sort((a, b) => a - b).map((i) => ({ \u4F4D\u7F6E: i, label: \u540D\u524D\u3092\u5F15\u304F(i) }));
7020
+ const \u5E2F\u306E\u4F59\u767D = Math.min(6, cellW / 2);
7021
+ const \u5E2F\u306E\u5DE6 = (idx) => xAt(idx) + \u5E2F\u306E\u4F59\u767D / 2;
7022
+ const \u5E2F\u306E\u53F3 = (idx) => xAt(idx) + cellW - \u5E2F\u306E\u4F59\u767D / 2;
7023
+ const taskById = new Map(\u6574\u3048\u305F.map((t) => [t.id, t]));
7024
+ const rowOf = new Map(\u6574\u3048\u305F.map((t, i) => [t.id, i]));
6872
7025
  return /* @__PURE__ */ jsxs("g", { transform: `translate(${x0} ${y0})`, children: [
6873
7026
  /* @__PURE__ */ jsx(
6874
7027
  "rect",
@@ -6884,10 +7037,11 @@ function GanttNode({
6884
7037
  strokeWidth: active ? 3 : 1.25
6885
7038
  }
6886
7039
  ),
6887
- gridLabels.map((label, i) => /* @__PURE__ */ jsxs("g", { children: [
7040
+ gridLabels.map(({ \u4F4D\u7F6E: i, label }) => /* @__PURE__ */ jsxs("g", { children: [
6888
7041
  /* @__PURE__ */ jsx(
6889
7042
  "line",
6890
7043
  {
7044
+ "data-cdl-role": "gantt-grid",
6891
7045
  x1: xAt(i),
6892
7046
  y1: PAD_TOP,
6893
7047
  x2: xAt(i),
@@ -6898,9 +7052,10 @@ function GanttNode({
6898
7052
  opacity: 0.5
6899
7053
  }
6900
7054
  ),
6901
- /* @__PURE__ */ jsx(
7055
+ label === "" ? null : /* @__PURE__ */ jsx(
6902
7056
  "text",
6903
7057
  {
7058
+ "data-cdl-role": "gantt-tick",
6904
7059
  x: xAt(i) + cellW / 2,
6905
7060
  y: PAD_TOP + canvasH + 18,
6906
7061
  fontSize: 11,
@@ -6922,7 +7077,7 @@ function GanttNode({
6922
7077
  opacity: 0.5
6923
7078
  }
6924
7079
  ),
6925
- tasks.map((t, row) => /* @__PURE__ */ jsx(
7080
+ \u6574\u3048\u305F.map((t, row) => /* @__PURE__ */ jsx(
6926
7081
  "text",
6927
7082
  {
6928
7083
  x: PAD_LEFT - 12,
@@ -6935,16 +7090,17 @@ function GanttNode({
6935
7090
  },
6936
7091
  `label-${t.id}`
6937
7092
  )),
6938
- tasks.map((t, row) => {
6939
- const bx = xAt(t.startIdx) + 3;
7093
+ \u6574\u3048\u305F.map((t, row) => {
7094
+ const bx = \u5E2F\u306E\u5DE6(t.startIdx);
6940
7095
  const by = yAt(row);
6941
- const bw = Math.max(cellW - 6, (t.endIdx - t.startIdx + 1) * cellW - 6);
7096
+ const bw = Math.max(0, \u5E2F\u306E\u53F3(t.endIdx) - bx);
6942
7097
  return /* @__PURE__ */ jsxs("g", { children: [
6943
7098
  /* @__PURE__ */ jsx(
6944
7099
  "rect",
6945
7100
  {
6946
7101
  "data-cdl-role": "gantt-bar",
6947
7102
  "data-cdl-unresolved": t.unresolved ? "true" : void 0,
7103
+ "data-cdl-clipped": "clipped" in t && t.clipped ? "true" : void 0,
6948
7104
  x: bx,
6949
7105
  y: by,
6950
7106
  width: bw,
@@ -6967,13 +7123,13 @@ function GanttNode({
6967
7123
  )
6968
7124
  ] }, `bar-${t.id}`);
6969
7125
  }),
6970
- tasks.filter((t) => t.dependsOn && taskById.has(t.dependsOn)).map((t) => {
7126
+ \u6574\u3048\u305F.filter((t) => t.dependsOn && taskById.has(t.dependsOn)).map((t) => {
6971
7127
  const parent = taskById.get(t.dependsOn);
6972
7128
  const parentRow = rowOf.get(parent.id);
6973
7129
  const childRow = rowOf.get(t.id);
6974
- const parentBarRight = xAt(parent.endIdx) + cellW - 3;
7130
+ const parentBarRight = \u5E2F\u306E\u53F3(parent.endIdx);
6975
7131
  const parentMidY = yAt(parentRow) + rowH / 2;
6976
- const childBarLeft = xAt(t.startIdx) + 3;
7132
+ const childBarLeft = \u5E2F\u306E\u5DE6(t.startIdx);
6977
7133
  const childMidY = yAt(childRow) + rowH / 2;
6978
7134
  const arrowHeadSize = 8;
6979
7135
  const arrowHeadTipGap = 10;
@@ -7015,6 +7171,49 @@ function toneColor3(tone) {
7015
7171
  if (!tone) return TONE.accent;
7016
7172
  return TONE[tone];
7017
7173
  }
7174
+
7175
+ // src/kinds/box-lines.ts
7176
+ var \u884C\u9001\u308A\u306E\u6BD4 = 1.15;
7177
+ var \u5B57\u306E\u9AD8\u3055\u306E\u6BD4 = 0.72;
7178
+ var \u4E0A\u4E0B\u306E\u4F59\u767D = 2;
7179
+ var \u5DE6\u53F3\u306E\u4F59\u767D = 8;
7180
+ function \u5E45\u3067\u5207\u308B(text, fontSize, \u4F7F\u3048\u308B\u5E45, \u6E2C\u308B = textWidth) {
7181
+ if (\u4F7F\u3048\u308B\u5E45 <= 0) return "";
7182
+ if (\u6E2C\u308B(text, fontSize) <= \u4F7F\u3048\u308B\u5E45) return text;
7183
+ const \u7701\u7565 = "\u2026";
7184
+ const \u7701\u7565\u306E\u5E45 = \u6E2C\u308B(\u7701\u7565, fontSize);
7185
+ if (\u7701\u7565\u306E\u5E45 > \u4F7F\u3048\u308B\u5E45) return "";
7186
+ let \u51FA = "";
7187
+ let \u5E45 = 0;
7188
+ for (const c of \u66F8\u8A18\u7D20\u306B\u5206\u3051\u308B(text)) {
7189
+ const \u5B57\u306E\u5E452 = \u6E2C\u308B(c, fontSize);
7190
+ if (\u5E45 + \u5B57\u306E\u5E452 + \u7701\u7565\u306E\u5E45 > \u4F7F\u3048\u308B\u5E45) break;
7191
+ \u5E45 += \u5B57\u306E\u5E452;
7192
+ \u51FA += c;
7193
+ }
7194
+ return `${\u51FA}${\u7701\u7565}`;
7195
+ }
7196
+ function \u66F8\u8A18\u7D20\u306B\u5206\u3051\u308B(text) {
7197
+ const \u533A\u5207\u308A = Intl.Segmenter;
7198
+ if (typeof \u533A\u5207\u308A !== "function") return [...text];
7199
+ return [...new \u533A\u5207\u308A(void 0, { granularity: "grapheme" }).segment(text)].map((g) => g.segment);
7200
+ }
7201
+ function \u7BB1\u306B\u7A4D\u3080(\u884C, \u7BB1\u306E\u5E45, \u7BB1\u306E\u9AD8\u3055) {
7202
+ const \u5019\u88DC = \u884C.filter((r, i) => i === 0 || r.text.trim() !== "");
7203
+ if (\u5019\u88DC.length === 0) return [];
7204
+ const \u4F7F\u3048\u308B\u9AD8\u3055 = \u7BB1\u306E\u9AD8\u3055 - \u4E0A\u4E0B\u306E\u4F59\u767D * 2;
7205
+ const \u9AD8\u3055 = (rs) => rs.reduce((a, r) => a + r.fontSize * \u884C\u9001\u308A\u306E\u6BD4, 0);
7206
+ const \u6B8B\u3059 = [...\u5019\u88DC];
7207
+ while (\u6B8B\u3059.length > 1 && \u9AD8\u3055(\u6B8B\u3059) > \u4F7F\u3048\u308B\u9AD8\u3055) \u6B8B\u3059.pop();
7208
+ const \u4F7F\u3048\u308B\u5E45 = \u7BB1\u306E\u5E45 - \u5DE6\u53F3\u306E\u4F59\u767D * 2;
7209
+ const \u7DCF\u9AD8 = \u9AD8\u3055(\u6B8B\u3059);
7210
+ let \u7A4D\u307F = -\u7DCF\u9AD8 / 2;
7211
+ return \u6B8B\u3059.map((r) => {
7212
+ const dy = \u7A4D\u307F + (\u884C\u9001\u308A\u306E\u6BD4 + \u5B57\u306E\u9AD8\u3055\u306E\u6BD4) / 2 * r.fontSize;
7213
+ \u7A4D\u307F += r.fontSize * \u884C\u9001\u308A\u306E\u6BD4;
7214
+ return { ...r, text: \u5E45\u3067\u5207\u308B(r.text, r.fontSize, \u4F7F\u3048\u308B\u5E45), dy };
7215
+ });
7216
+ }
7018
7217
  var MIND_TONES = ["accent", "teal", "success", "warning", "info", "error"];
7019
7218
  var MIND_BOX_GAP = 24;
7020
7219
  function MindMapNode({
@@ -7060,33 +7259,59 @@ function MindMapNode({
7060
7259
  },
7061
7260
  `edge-${i}`
7062
7261
  )),
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}`))
7262
+ layout2.nodes.map((n) => {
7263
+ const \u884C = \u7BB1\u306B\u7A4D\u3080(
7264
+ [
7265
+ { \u5F79\u5272: "title", text: n.title, fontSize: n.isRoot ? 15 : 13 },
7266
+ { \u5F79\u5272: "subtitle", text: n.subtitle ?? "", fontSize: 9 }
7267
+ ],
7268
+ n.w,
7269
+ n.h
7270
+ );
7271
+ const \u88DC\u8DB3\u3042\u308A = (n.subtitle ?? "").trim() !== "";
7272
+ const \u6587\u5B57\u306E\u8272 = n.isRoot ? "var(--cdl-text-on-tone, #ffffff)" : "var(--cdl-chip-text, #1a1f2a)";
7273
+ return /* @__PURE__ */ jsxs("g", { "data-cdl-unresolved": n.unresolved ? "true" : void 0, children: [
7274
+ /* @__PURE__ */ jsx(
7275
+ "rect",
7276
+ {
7277
+ x: n.x - n.w / 2,
7278
+ y: n.y - n.h / 2,
7279
+ width: n.w,
7280
+ height: n.h,
7281
+ rx: n.isRoot ? 22 : 10,
7282
+ fill: n.isRoot ? toneColor4(n.tone ?? "accent") : "var(--cdl-chip-fill, #f4f6fb)",
7283
+ stroke: n.isRoot ? toneColor4(n.tone ?? "accent") : toneColor4(n.tone),
7284
+ strokeWidth: n.isRoot ? 2 : 1.75
7285
+ }
7286
+ ),
7287
+ \u88DC\u8DB3\u3042\u308A ? \u884C.map((r) => /* @__PURE__ */ jsx(
7288
+ "text",
7289
+ {
7290
+ "data-cdl-role": r.\u5F79\u5272 === "subtitle" ? "mind-node-subtitle" : void 0,
7291
+ x: n.x,
7292
+ y: n.y + r.dy,
7293
+ fontSize: r.fontSize,
7294
+ fontWeight: r.\u5F79\u5272 === "title" ? n.isRoot ? 700 : 600 : 500,
7295
+ textAnchor: "middle",
7296
+ fill: \u6587\u5B57\u306E\u8272,
7297
+ opacity: r.\u5F79\u5272 === "subtitle" ? 0.85 : void 0,
7298
+ children: r.text
7299
+ },
7300
+ r.\u5F79\u5272
7301
+ )) : /* @__PURE__ */ jsx(
7302
+ "text",
7303
+ {
7304
+ x: n.x,
7305
+ y: n.y + 5,
7306
+ fontSize: n.isRoot ? 15 : 13,
7307
+ fontWeight: n.isRoot ? 700 : 600,
7308
+ textAnchor: "middle",
7309
+ fill: \u6587\u5B57\u306E\u8272,
7310
+ children: n.title
7311
+ }
7312
+ )
7313
+ ] }, `n-${n.id}`);
7314
+ })
7090
7315
  ] });
7091
7316
  }
7092
7317
  function computeMindMapLayout(mindData, cx, cy, canvasW, canvasH) {
@@ -7208,6 +7433,7 @@ function computeMindMapLayout(mindData, cx, cy, canvasW, canvasH) {
7208
7433
  nodes.push({
7209
7434
  id: node.id,
7210
7435
  title: node.title,
7436
+ ...node.subtitle !== void 0 ? { subtitle: node.subtitle } : {},
7211
7437
  x,
7212
7438
  y,
7213
7439
  w,
@@ -7412,6 +7638,9 @@ function QuadrantNode({
7412
7638
  const PAD_BOTTOM = 56;
7413
7639
  const AXIS_LABEL_FONT = 12;
7414
7640
  const PAD_MIN = 60;
7641
+ const ITEM_FONT = 13;
7642
+ const ITEM_PAD_X = 12;
7643
+ const ITEM_MIN_W = 48;
7415
7644
  const axisLabelPad = (text) => Math.max(PAD_MIN, measureTextWidth(`\u2190 ${text}`, { fontSize: AXIS_LABEL_FONT}) + 12);
7416
7645
  const padBudget = Math.max(0, node.w * 0.5 - PAD_MIN * 2);
7417
7646
  const wantLeft = axisLabelPad(data.xAxis.left) - PAD_MIN;
@@ -7507,6 +7736,12 @@ function QuadrantNode({
7507
7736
  const qxStart = (isLeft ? PAD_LEFT : cx) + 18;
7508
7737
  const qyStart = (isTop ? PAD_TOP : cy) + 44;
7509
7738
  const qHalfW = halfW - 36;
7739
+ const \u67A0\u306E\u4F59\u5730 = qHalfW;
7740
+ if (\u67A0\u306E\u4F59\u5730 <= 0) return null;
7741
+ const \u67A0\u306E\u5E45 = (title) => Math.min(
7742
+ \u67A0\u306E\u4F59\u5730,
7743
+ Math.max(ITEM_MIN_W, measureTextWidth(title, { fontSize: ITEM_FONT}) + ITEM_PAD_X * 2)
7744
+ );
7510
7745
  const qHalfH = halfH - 60;
7511
7746
  const gap = 10;
7512
7747
  const rowH = 34;
@@ -7521,7 +7756,7 @@ function QuadrantNode({
7521
7756
  "data-cdl-unresolved": it.unresolved ? "true" : void 0,
7522
7757
  x: qxStart,
7523
7758
  y: py,
7524
- width: qHalfW,
7759
+ width: \u67A0\u306E\u5E45(it.title),
7525
7760
  height: rowH,
7526
7761
  rx: 6,
7527
7762
  fill: "var(--cdl-node-fill, #ffffff)",
@@ -7529,12 +7764,35 @@ function QuadrantNode({
7529
7764
  strokeWidth: 1.5
7530
7765
  }
7531
7766
  ),
7532
- /* @__PURE__ */ jsx("text", { x: qxStart + 12, y: py + rowH / 2 + 5, fontSize: 13, fontWeight: 600, fill: "var(--cdl-text, #1a1f2a)", children: it.title })
7767
+ /* @__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
7768
  ] }, `item-${it.id}`);
7534
7769
  });
7535
7770
  })
7536
7771
  ] });
7537
7772
  }
7773
+ function \u7BB1\u306E\u6587\u5B57({
7774
+ \u884C,
7775
+ x,
7776
+ \u7BB1\u306E\u9AD8\u3055,
7777
+ fill,
7778
+ \u540D\u524D\u306E\u592A\u3055
7779
+ }) {
7780
+ return \u884C.map((r) => /* @__PURE__ */ jsx(
7781
+ "text",
7782
+ {
7783
+ "data-cdl-role": r.\u5F79\u5272 === "subtitle" ? "tree-node-subtitle" : void 0,
7784
+ x,
7785
+ y: \u7BB1\u306E\u9AD8\u3055 / 2 + r.dy,
7786
+ fontSize: r.fontSize,
7787
+ fontWeight: r.\u5F79\u5272 === "title" ? \u540D\u524D\u306E\u592A\u3055 : 500,
7788
+ textAnchor: "middle",
7789
+ fill,
7790
+ opacity: r.\u5F79\u5272 === "subtitle" ? 0.85 : void 0,
7791
+ children: r.text
7792
+ },
7793
+ r.\u5F79\u5272
7794
+ ));
7795
+ }
7538
7796
  function TreeNode({
7539
7797
  node,
7540
7798
  active,
@@ -7597,6 +7855,15 @@ function TreeNode({
7597
7855
  layout2.nodes.map((n) => {
7598
7856
  const accent = depthColors[Math.min(n.depth, depthColors.length - 1)] ?? depthColors[0];
7599
7857
  const isRoot = n.depth === 0;
7858
+ const \u884C = \u7BB1\u306B\u7A4D\u3080(
7859
+ [
7860
+ { \u5F79\u5272: "title", text: n.title, fontSize: isRoot ? 13 : 12 },
7861
+ { \u5F79\u5272: "subtitle", text: n.subtitle ?? "", fontSize: 9 }
7862
+ ],
7863
+ n.w,
7864
+ n.h
7865
+ );
7866
+ const \u88DC\u8DB3\u3042\u308A = (n.subtitle ?? "").trim() !== "";
7600
7867
  return /* @__PURE__ */ jsx(
7601
7868
  "g",
7602
7869
  {
@@ -7615,7 +7882,13 @@ function TreeNode({
7615
7882
  fill: `url(#${gradientId})`
7616
7883
  }
7617
7884
  ),
7618
- /* @__PURE__ */ jsx(
7885
+ \u88DC\u8DB3\u3042\u308A ? \u7BB1\u306E\u6587\u5B57({
7886
+ \u884C,
7887
+ x: n.w / 2,
7888
+ \u7BB1\u306E\u9AD8\u3055: n.h,
7889
+ fill: "var(--cdl-text-on-tone, #ffffff)",
7890
+ \u540D\u524D\u306E\u592A\u3055: 700
7891
+ }) : /* @__PURE__ */ jsx(
7619
7892
  "text",
7620
7893
  {
7621
7894
  x: n.w / 2,
@@ -7643,7 +7916,13 @@ function TreeNode({
7643
7916
  }
7644
7917
  ),
7645
7918
  /* @__PURE__ */ jsx("rect", { x: 0, y: 0, width: 4, height: n.h, rx: 2, fill: accent }),
7646
- /* @__PURE__ */ jsx(
7919
+ \u88DC\u8DB3\u3042\u308A ? \u7BB1\u306E\u6587\u5B57({
7920
+ \u884C,
7921
+ x: n.w / 2 + 2,
7922
+ \u7BB1\u306E\u9AD8\u3055: n.h,
7923
+ fill: "var(--cdl-chip-text, #1a1f2a)",
7924
+ \u540D\u524D\u306E\u592A\u3055: 600
7925
+ }) : /* @__PURE__ */ jsx(
7647
7926
  "text",
7648
7927
  {
7649
7928
  x: n.w / 2 + 2,
@@ -7724,6 +8003,7 @@ function computeTreeLayout(treeNodes, canvasW, canvasH) {
7724
8003
  nodes.push({
7725
8004
  id: n.id,
7726
8005
  title: n.title,
8006
+ ...n.subtitle !== void 0 ? { subtitle: n.subtitle } : {},
7727
8007
  x,
7728
8008
  y,
7729
8009
  w: nodeW,
@@ -8014,42 +8294,106 @@ var BOTTOM_PAD = 4;
8014
8294
  var ASCENT2 = 0.72;
8015
8295
  var DESCENT2 = 0.25;
8016
8296
  var ART_GAP = 4;
8297
+ var ART_MIN_BAND = 45;
8298
+ var \u843D\u3068\u3057\u3066\u3082\u63CF\u304F\u7A2E\u5225 = {
8299
+ \u80A9\u66F8: /* @__PURE__ */ new Set([
8300
+ // basement 系 = `commonLabel` が自前の帯に置く
8301
+ "shape-file",
8302
+ "shape-folder",
8303
+ "shape-cloud",
8304
+ "shape-cylinder",
8305
+ "shape-hexagon",
8306
+ "shape-diamond",
8307
+ "shape-stack",
8308
+ // software 系 = 固有 layout
8309
+ "shape-window",
8310
+ "shape-terminal",
8311
+ "shape-code-block",
8312
+ "shape-message-bubble",
8313
+ "shape-gear",
8314
+ // hardware 系のうち固有 layout を持つもの
8315
+ "shape-iot-sensor",
8316
+ "shape-robot-arm"
8317
+ ]),
8318
+ \u8AAC\u660E: /* @__PURE__ */ new Set([
8319
+ // basement 系 = `commonLabel` が自前の帯に置く
8320
+ "shape-file",
8321
+ "shape-folder",
8322
+ "shape-cloud",
8323
+ "shape-cylinder",
8324
+ "shape-hexagon",
8325
+ "shape-diamond",
8326
+ "shape-stack",
8327
+ // software 系 = 固有 layout
8328
+ "shape-window",
8329
+ "shape-message-bubble",
8330
+ "shape-gear",
8331
+ // hardware / blockchain 系のうち固有 layout を持つもの
8332
+ "shape-iot-sensor",
8333
+ "shape-robot-arm",
8334
+ "shape-blockchain-block"
8335
+ ])
8336
+ };
8337
+ function droppedLineIsHidden(kind, line) {
8338
+ return kind.startsWith("shape-") && !\u843D\u3068\u3057\u3066\u3082\u63CF\u304F\u7A2E\u5225[line].has(kind);
8339
+ }
8017
8340
  function labelPlan(node) {
8018
8341
  const \u9AD8\u3055 = Math.max(0, node.h);
8019
8342
  const \u5E95 = node.cy + \u9AD8\u3055 / 2;
8020
8343
  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;
8344
+ const \u7A4D\u3080 = (\u540D\u524D\u5927, \u8AAC\u660E\u3042\u308A2, \u80A9\u66F8\u3042\u308A2, \u4E0B\u4F59\u767D) => {
8345
+ const \u5C0F\u3055\u3044\u5B57 = Math.max(1, Math.round(\u540D\u524D\u5927 * SUB_RATIO));
8346
+ let y = \u5E95 - \u4E0B\u4F59\u767D;
8347
+ const \u8AAC\u660E = \u8AAC\u660E\u3042\u308A2 ? { y, size: \u5C0F\u3055\u3044\u5B57 } : null;
8348
+ if (\u8AAC\u660E !== null) y -= \u5C0F\u3055\u3044\u5B57 + LINE_GAP;
8349
+ const \u540D\u524D = { y, size: \u540D\u524D\u5927 };
8350
+ y -= \u540D\u524D\u5927 + LINE_GAP;
8351
+ const \u80A9\u66F8 = \u80A9\u66F8\u3042\u308A2 ? { y, size: \u5C0F\u3055\u3044\u5B57 } : null;
8029
8352
  const \u4E0A\u306E\u884C = \u80A9\u66F8 ?? \u540D\u524D;
8030
8353
  return { \u540D\u524D, \u8AAC\u660E, \u80A9\u66F8, \u4E0A\u7AEF: \u4E0A\u306E\u884C.y - \u4E0A\u306E\u884C.size * ASCENT2 };
8031
8354
  };
8032
8355
  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
8356
  let \u8AAC\u660E\u3042\u308A = Boolean(node.subtitle);
8034
8357
  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
- }
8358
+ const \u843D\u3068\u3057\u305F = [];
8359
+ if (!\u5165\u308B(\u8AAC\u660E\u3042\u308A, \u80A9\u66F8\u3042\u308A) && \u80A9\u66F8\u3042\u308A) {
8360
+ \u80A9\u66F8\u3042\u308A = false;
8361
+ \u843D\u3068\u3057\u305F.push("\u80A9\u66F8");
8362
+ }
8363
+ if (!\u5165\u308B(\u8AAC\u660E\u3042\u308A, \u80A9\u66F8\u3042\u308A) && \u8AAC\u660E\u3042\u308A) {
8364
+ \u8AAC\u660E\u3042\u308A = false;
8365
+ \u843D\u3068\u3057\u305F.push("\u8AAC\u660E");
8366
+ }
8367
+ const \u7D44\u3080 = (\u8AAC\u660E\u3042\u308A2, \u80A9\u66F8\u3042\u308A2) => {
8368
+ const \u4E0A\u9650 = Math.min(TITLE_MAX, Math.max(1, Math.round(\u9AD8\u3055 * TITLE_RATIO)));
8369
+ let \u540D\u524D\u5927 = 0;
8370
+ for (let size = \u4E0A\u9650; size >= 1; size--) {
8371
+ const \u5C0F\u3055\u3044\u5B572 = Math.max(1, Math.round(size * SUB_RATIO));
8372
+ const \u4E0B\u306E\u884C\u5927 = \u8AAC\u660E\u3042\u308A2 ? \u5C0F\u3055\u3044\u5B572 : size;
8373
+ if (\u7A4D\u3080(size, \u8AAC\u660E\u3042\u308A2, \u80A9\u66F8\u3042\u308A2, \u4E0B\u306E\u884C\u5927 * DESCENT2).\u4E0A\u7AEF >= \u5929) {
8374
+ \u540D\u524D\u5927 = size;
8375
+ break;
8376
+ }
8377
+ }
8378
+ if (\u540D\u524D\u5927 === 0) \u540D\u524D\u5927 = \u9AD8\u3055 / (ASCENT2 + DESCENT2);
8379
+ const \u5C0F\u3055\u3044\u5B57 = Math.max(1, Math.round(\u540D\u524D\u5927 * SUB_RATIO));
8380
+ const \u5F35\u308A\u51FA\u3057 = (\u8AAC\u660E\u3042\u308A2 ? \u5C0F\u3055\u3044\u5B57 : \u540D\u524D\u5927) * DESCENT2;
8381
+ 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);
8382
+ const \u4E0B\u4F59\u767D = Math.min(Math.max(BOTTOM_PAD, \u5F35\u308A\u51FA\u3057), \u5F35\u308A\u51FA\u3057 + \u4F59\u308A);
8383
+ return \u7A4D\u3080(\u540D\u524D\u5927, \u8AAC\u660E\u3042\u308A2, \u80A9\u66F8\u3042\u308A2, \u4E0B\u4F59\u767D);
8384
+ };
8385
+ const \u7D75\u306E\u5E2F = (\u7D442) => Math.max(0, \u7D442.\u4E0A\u7AEF - ART_GAP - \u5929);
8386
+ let \u7D44 = \u7D44\u3080(\u8AAC\u660E\u3042\u308A, \u80A9\u66F8\u3042\u308A);
8387
+ if (\u7D75\u306E\u5E2F(\u7D44) < ART_MIN_BAND && \u80A9\u66F8\u3042\u308A) {
8388
+ \u80A9\u66F8\u3042\u308A = false;
8389
+ \u843D\u3068\u3057\u305F.push("\u80A9\u66F8");
8390
+ \u7D44 = \u7D44\u3080(\u8AAC\u660E\u3042\u308A, \u80A9\u66F8\u3042\u308A);
8391
+ }
8392
+ if (\u7D75\u306E\u5E2F(\u7D44) < ART_MIN_BAND && \u8AAC\u660E\u3042\u308A) {
8393
+ \u8AAC\u660E\u3042\u308A = false;
8394
+ \u843D\u3068\u3057\u305F.push("\u8AAC\u660E");
8395
+ \u7D44 = \u7D44\u3080(\u8AAC\u660E\u3042\u308A, \u80A9\u66F8\u3042\u308A);
8046
8396
  }
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
8397
  const \u4E00\u756A\u5C0F\u3055\u3044\u884C = Math.min(
8054
8398
  ...[\u7D44.\u540D\u524D, \u7D44.\u8AAC\u660E, \u7D44.\u80A9\u66F8].filter((l) => l !== null).map((l) => l.size)
8055
8399
  );
@@ -8058,7 +8402,8 @@ function labelPlan(node) {
8058
8402
  \u8AAC\u660E: \u7D44.\u8AAC\u660E,
8059
8403
  \u80A9\u66F8: \u7D44.\u80A9\u66F8,
8060
8404
  top: \u7D44.\u4E0A\u7AEF,
8061
- \u8AAD\u3081\u308B: \u4E00\u756A\u5C0F\u3055\u3044\u884C >= TITLE_MIN
8405
+ \u8AAD\u3081\u308B: \u4E00\u756A\u5C0F\u3055\u3044\u884C >= TITLE_MIN,
8406
+ \u843D\u3068\u3057\u305F
8062
8407
  };
8063
8408
  }
8064
8409
  function shapeRegion(node) {
@@ -10326,6 +10671,15 @@ function ShapeSmartContractNode({ node, active }) {
10326
10671
  bottomLabel2(node, r.cx, r.label)
10327
10672
  ] });
10328
10673
  }
10674
+ var \u5B57\u306E\u5E45 = (text, size) => Math.max(displayWidth(text) * size * MONO_ADVANCE_RATIO, textWidth(text, size));
10675
+ var BLOCK_DEFAULT_FIELDS = [
10676
+ { label: "hash", value: "0xaf31c9d2..." },
10677
+ { label: "prev", value: "0x7b3f92c1..." },
10678
+ { label: "merkle", value: "0xc1892f4a..." },
10679
+ { label: "nonce", value: "82,914" },
10680
+ { label: "tx", value: "142 tx" }
10681
+ ];
10682
+ var BLOCK_DEFAULT_TITLE = "Block #421,873";
10329
10683
  function ShapeBlockchainBlockNode({ node, active }) {
10330
10684
  const frame = commonFrame4(active);
10331
10685
  const r = shapeRegion(node);
@@ -10335,21 +10689,22 @@ function ShapeBlockchainBlockNode({ node, active }) {
10335
10689
  const cy = r.cy;
10336
10690
  const x = cx - bw / 2;
10337
10691
  const y = cy - bh / 2;
10692
+ const \u6E21\u3055\u308C\u305F = (v) => (v ?? "").trim();
10693
+ const \u898B\u51FA\u3057 = \u6E21\u3055\u308C\u305F(node.title) || BLOCK_DEFAULT_TITLE;
10694
+ const fields = BLOCK_DEFAULT_FIELDS.map((f) => ({
10695
+ label: f.label,
10696
+ value: f.label === "hash" ? \u6E21\u3055\u308C\u305F(node.subtitle) || f.value : f.value
10697
+ }));
10698
+ const \u898B\u51FA\u3057\u306E\u5E45 = bw - 14 - 30;
10338
10699
  const art = /* @__PURE__ */ jsxs("g", { children: [
10339
10700
  /* @__PURE__ */ jsx("rect", { x: x + 6, y: y + 6, width: bw, height: bh, rx: 8, fill: frame.stroke, opacity: 0.2, stroke: "none" }),
10340
10701
  /* @__PURE__ */ jsx("rect", { "data-cdl-role": "node-body", x, y, width: bw, height: bh, rx: 8, ...frame }),
10341
10702
  /* @__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" }),
10703
+ /* @__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
10704
  /* @__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: [
10705
+ fields.map((f, i) => /* @__PURE__ */ jsxs("g", { children: [
10351
10706
  /* @__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 }),
10707
+ /* @__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
10708
  /* @__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
10709
  ] }, i)),
10355
10710
  /* @__PURE__ */ jsxs("g", { transform: `translate(${cx - 32}, ${y + bh - 26})`, children: [
@@ -12045,6 +12400,29 @@ function CdlNodeView({
12045
12400
  }
12046
12401
  );
12047
12402
  }
12403
+
12404
+ // src/layout/lifeline.ts
12405
+ function lifelineStartYs(nodes) {
12406
+ const topmost = /* @__PURE__ */ new Map();
12407
+ for (const n of nodes) {
12408
+ const cur = topmost.get(n.lane);
12409
+ if (cur === void 0 || n.cy < cur.cy) topmost.set(n.lane, n);
12410
+ }
12411
+ const starts = /* @__PURE__ */ new Map();
12412
+ for (const [laneId, n] of topmost) starts.set(laneId, n.cy + n.h / 2);
12413
+ return starts;
12414
+ }
12415
+ function uniformLifelineEndY(lanes, nodes, starts = lifelineStartYs(nodes)) {
12416
+ const lifelineLanes = lanes.filter((l) => l.lifeline);
12417
+ let deepestStart = Number.NEGATIVE_INFINITY;
12418
+ for (const lane of lifelineLanes) {
12419
+ const start = starts.get(lane.id);
12420
+ if (start !== void 0) deepestStart = Math.max(deepestStart, start);
12421
+ }
12422
+ const footerTops = lifelineFooterNodes(lanes, nodes).map((f) => footerShapeDrawTop(f)).filter((top) => top > deepestStart);
12423
+ if (footerTops.length > 0) return Math.min(...footerTops);
12424
+ return lifelineLanes[0] ? lifelineLanes[0].y + lifelineLanes[0].height : 0;
12425
+ }
12048
12426
  function CdlStage({
12049
12427
  laid,
12050
12428
  currentPhase,
@@ -12055,15 +12433,13 @@ function CdlStage({
12055
12433
  svgRef
12056
12434
  }) {
12057
12435
  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);
12436
+ const { lifelineStarts, uniformFooterTop } = useMemo(() => {
12437
+ const starts = lifelineStartYs(laid.nodes);
12061
12438
  return {
12062
- footerTops: footers.map((f) => f.cy - f.h / 2),
12063
- footerDropById: footerShapeDropsOf(footers)
12439
+ lifelineStarts: starts,
12440
+ uniformFooterTop: uniformLifelineEndY(laid.lanes, laid.nodes, starts)
12064
12441
  };
12065
12442
  }, [laid]);
12066
- const uniformFooterTop = footerTops.length > 0 ? Math.min(...footerTops) : lifelineLanes[0] ? lifelineLanes[0].y + lifelineLanes[0].height : 0;
12067
12443
  const diagramScale = laid.diagramScale ?? 1;
12068
12444
  return /* @__PURE__ */ jsxs("div", { className: compact ? "px-3 py-3" : "px-6 py-6", style: compact ? void 0 : { minHeight: 460 }, children: [
12069
12445
  /* @__PURE__ */ jsxs(
@@ -12150,11 +12526,8 @@ function CdlStage({
12150
12526
  }
12151
12527
  ),
12152
12528
  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;
12529
+ const y1 = lifelineStarts.get(lane.id);
12530
+ if (y1 === void 0) return null;
12158
12531
  const y2 = uniformFooterTop;
12159
12532
  if (y2 <= y1) return null;
12160
12533
  return /* @__PURE__ */ jsx(
@@ -12201,8 +12574,7 @@ function CdlStage({
12201
12574
  currentPhaseId: currentPhase?.id
12202
12575
  }
12203
12576
  );
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);
12577
+ return /* @__PURE__ */ jsx("g", { children: view }, node.id);
12206
12578
  }),
12207
12579
  laid.edges.filter((edge) => Boolean(edge.dThrough)).map((edge) => /* @__PURE__ */ jsx(CdlEdgeGlowView, { edge, active: activeSet.has(edge.id) }, `glow-${edge.id}`)),
12208
12580
  laid.edges.map((edge) => /* @__PURE__ */ jsx(
@@ -21084,6 +21456,7 @@ function emptyCounts() {
21084
21456
  "validate-performance-budget": 0,
21085
21457
  "node-inside-viewbox": 0,
21086
21458
  "node-inside-lane": 0,
21459
+ "shape-label-dropped": 0,
21087
21460
  "edge-inside-viewbox": 0,
21088
21461
  "lane-label-inside-viewbox": 0,
21089
21462
  "lane-label-overlap": 0,
@@ -21737,6 +22110,7 @@ function runAxes(laid, diag, violations, counts, push, profile, skippedAxes) {
21737
22110
  for (const n of laid.nodes) {
21738
22111
  if (n.id.endsWith("-spacer")) continue;
21739
22112
  if (!rendersTitleText(n.kind)) continue;
22113
+ if (fitsOwnTitleWidth(n.kind)) continue;
21740
22114
  if (!isRenderedNode(n)) continue;
21741
22115
  const titleTrimmed = n.title?.trim() ?? "";
21742
22116
  if (!titleTrimmed) continue;
@@ -21993,6 +22367,15 @@ function runAxes(laid, diag, violations, counts, push, profile, skippedAxes) {
21993
22367
  );
21994
22368
  }
21995
22369
  }
22370
+ for (const n of laid.nodes) {
22371
+ const \u843D\u3068\u3057\u305F = labelPlan(n).\u843D\u3068\u3057\u305F.filter((line) => droppedLineIsHidden(n.kind, line));
22372
+ if (\u843D\u3068\u3057\u305F.length === 0) continue;
22373
+ push(
22374
+ "shape-label-dropped",
22375
+ `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`,
22376
+ "warn"
22377
+ );
22378
+ }
21996
22379
  for (const e of laid.edges) {
21997
22380
  const segs = flattenPathSegments(extractPathSegments(e.d));
21998
22381
  if (segs.length === 0) continue;
@@ -23182,14 +23565,7 @@ function CdlDiagramView({ diagram, laid: laidProp, hideHeader = false, hideMiniP
23182
23565
  );
23183
23566
  const stateValues = useMemo(() => {
23184
23567
  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;
23568
+ return mergeStateValues(baseStateValues, interactive.stateOverrides);
23193
23569
  }, [isInteractive, baseStateValues, interactive.stateOverrides]);
23194
23570
  return /* @__PURE__ */ jsxs(
23195
23571
  "div",