@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.cjs CHANGED
@@ -462,7 +462,35 @@ function isCJK(code) {
462
462
  function isHalfwidthKana(code) {
463
463
  return code >= 65377 && code <= 65500;
464
464
  }
465
+ var EMOJI_ADVANCE_EM = 1.05;
466
+ var MONO_EMOJI_ADVANCE_EM = 1.16;
467
+ var WIDE_SYMBOL_ADVANCE_EM = 0.95;
468
+ function isEmoji(code) {
469
+ return code >= 126976 && code <= 129791 || // 絵文字の主要面
470
+ code >= 9728 && code <= 10175 || // 記号と絵文字 (♻ ✅ ❤ 等)
471
+ code >= 127462 && code <= 127487;
472
+ }
473
+ function isEmojiGrapheme(grapheme, firstCode) {
474
+ return isEmoji(firstCode) || grapheme.includes("\uFE0F");
475
+ }
476
+ function isWideSymbol(code) {
477
+ return code >= 8592 && code <= 8703 || // 矢印
478
+ code >= 9472 && code <= 9599 || // 罫線
479
+ code >= 9632 && code <= 9727 || // 幾何図形
480
+ code >= 11008 && code <= 11263;
481
+ }
482
+ var CLUSTER_JOINER_RE = /\u200d|\ufe0f|[\u{1f000}-\u{1faff}]|[\u{2600}-\u{27bf}]/u;
483
+ var \u66F8\u8A18\u7D20\u3067\u5206\u3051\u308B;
484
+ function segmentGraphemes(text) {
485
+ if (typeof Intl === "undefined" || typeof Intl.Segmenter !== "function") {
486
+ return [...text];
487
+ }
488
+ \u66F8\u8A18\u7D20\u3067\u5206\u3051\u308B ??= new Intl.Segmenter(void 0, { granularity: "grapheme" });
489
+ return [...\u66F8\u8A18\u7D20\u3067\u5206\u3051\u308B.segment(text)].map((s) => s.segment);
490
+ }
465
491
  function defaultAdvanceEm(ch, code) {
492
+ if (isEmoji(code)) return EMOJI_ADVANCE_EM;
493
+ if (isWideSymbol(code)) return WIDE_SYMBOL_ADVANCE_EM;
466
494
  if (isCJK(code)) return 1.08;
467
495
  if (isHalfwidthKana(code)) return 0.72;
468
496
  if (ch >= "a" && ch <= "z") return 0.73;
@@ -476,19 +504,23 @@ function measureTextWidth(text, opts = {}) {
476
504
  const fontFamily = opts.fontFamily ?? "sans";
477
505
  let advanceEmSum = 0;
478
506
  let charCount = 0;
479
- for (const ch of text) {
507
+ const \u6587\u5B57\u5217 = CLUSTER_JOINER_RE.test(text) ? segmentGraphemes(text) : [...text];
508
+ for (const ch of \u6587\u5B57\u5217) {
480
509
  charCount++;
510
+ const code = ch.codePointAt(0) ?? 0;
511
+ if (isEmojiGrapheme(ch, code)) {
512
+ advanceEmSum += fontFamily === "mono" ? MONO_EMOJI_ADVANCE_EM : EMOJI_ADVANCE_EM;
513
+ continue;
514
+ }
481
515
  if (fontFamily === "mono") {
482
- const code2 = ch.codePointAt(0) ?? 0;
483
- advanceEmSum += isCJK(code2) ? 1.08 : JETBRAINS_MONO_ADVANCE_EM;
516
+ advanceEmSum += isCJK(code) ? 1.08 : JETBRAINS_MONO_ADVANCE_EM;
484
517
  continue;
485
518
  }
486
- const code = ch.codePointAt(0) ?? 0;
487
519
  if (isCJK(code)) {
488
520
  advanceEmSum += 1.08;
489
521
  continue;
490
522
  }
491
- const table = INTER_BOLD_ADVANCE_EM[ch];
523
+ const table = ch.length === 1 ? INTER_BOLD_ADVANCE_EM[ch] : void 0;
492
524
  advanceEmSum += table !== void 0 ? table : defaultAdvanceEm(ch, code);
493
525
  }
494
526
  const w = advanceEmSum * fontSize + letterSpacing * charCount;
@@ -544,13 +576,15 @@ var KINDS_WITHOUT_TITLE_TEXT = /* @__PURE__ */ new Set([
544
576
  "funnel-stages",
545
577
  "quadrant-matrix",
546
578
  "tree-hierarchy",
547
- "journey-map",
548
- // 形の中に自前の文字を置く (title は使わない)
549
- "shape-blockchain-block"
579
+ "journey-map"
550
580
  ]);
551
581
  function rendersTitleText(kind) {
552
582
  return !KINDS_WITHOUT_TITLE_TEXT.has(kind ?? "");
553
583
  }
584
+ var KINDS_FITTING_OWN_TITLE = /* @__PURE__ */ new Set(["shape-blockchain-block"]);
585
+ function fitsOwnTitleWidth(kind) {
586
+ return KINDS_FITTING_OWN_TITLE.has(kind ?? "");
587
+ }
554
588
  var KINDS_WITH_ROW_TEXT = /* @__PURE__ */ new Set([
555
589
  // 専用の表組み (StorageNode)
556
590
  "storage",
@@ -737,6 +771,70 @@ function buildDiagramAltText(topic, phase) {
737
771
  return parts.join(" ");
738
772
  }
739
773
 
774
+ // src/layout/footer-shape.ts
775
+ var SHAPE_DRAW_UP_EXTENT = {
776
+ "shape-robot-arm": 201,
777
+ // 実測 上 129
778
+ "shape-iot-sensor": 120,
779
+ // 48
780
+ "shape-credit-card": 113,
781
+ // 40.8
782
+ "shape-exchange": 107,
783
+ // 35
784
+ "shape-storefront": 104,
785
+ // 32
786
+ "shape-cdn-edge": 103,
787
+ // 30.8
788
+ "shape-payment-provider": 101,
789
+ // 29
790
+ "shape-gear": 96,
791
+ // 23.9
792
+ "shape-rpc-node": 94,
793
+ // 22
794
+ "shape-wallet": 84
795
+ // 12.1
796
+ };
797
+ function lifelineFooterNodes(lanes, nodes) {
798
+ const \u6570 = /* @__PURE__ */ new Map();
799
+ for (const n of nodes) \u6570.set(n.lane, (\u6570.get(n.lane) ?? 0) + 1);
800
+ const lifelineLanes = /* @__PURE__ */ new Set();
801
+ for (const lane of lanes) {
802
+ if (lane.lifeline) lifelineLanes.add(lane.id);
803
+ }
804
+ return nodes.filter(
805
+ (n) => n.role === "lifeline-footer" && lifelineLanes.has(n.lane) && (\u6570.get(n.lane) ?? 0) >= 2
806
+ );
807
+ }
808
+ function footerShapeDropY(kind, h) {
809
+ const extent = SHAPE_DRAW_UP_EXTENT[kind];
810
+ if (extent === void 0) return 0;
811
+ if (!Number.isFinite(h) || h <= 0) return 0;
812
+ return Math.max(0, extent - h);
813
+ }
814
+ function footerShapeDrops(lanes, nodes) {
815
+ return footerShapeDropsOf(lifelineFooterNodes(lanes, nodes));
816
+ }
817
+ function footerShapeDropsOf(footers) {
818
+ const drops = /* @__PURE__ */ new Map();
819
+ for (const footer of footers) {
820
+ const dy = footerShapeDropY(footer.kind, footer.h);
821
+ if (dy > 0) drops.set(footer.id, dy);
822
+ }
823
+ return drops;
824
+ }
825
+ function applyFooterShapeDrops(lanes, nodes) {
826
+ const drops = footerShapeDrops(lanes, nodes);
827
+ if (drops.size === 0) return nodes;
828
+ return nodes.map((n) => {
829
+ if (n.posX !== void 0 && n.posY !== void 0) return n;
830
+ const dy = drops.get(n.id);
831
+ return dy === void 0 ? n : { ...n, cy: n.cy + dy };
832
+ });
833
+ }
834
+ function footerShapeDrawTop(node) {
835
+ return node.cy - node.h / 2 - footerShapeDropY(node.kind, node.h);
836
+ }
837
+
740
838
  // src/layout/collisions.ts
741
839
  var LANE_LABEL_CLEARANCE_MARGIN = 2;
742
840
  function collectBBoxes(lanes, nodes, edges) {
@@ -752,14 +850,16 @@ function collectBBoxes(lanes, nodes, edges) {
752
850
  h: 28
753
851
  });
754
852
  }
853
+ const footerDraw = footerShapeDrops(lanes, nodes);
755
854
  for (const n of nodes) {
855
+ const up = footerDraw.get(n.id) ?? 0;
756
856
  out.push({
757
857
  kind: "node",
758
858
  id: n.id,
759
859
  x: n.cx - n.w / 2,
760
- y: n.cy - n.h / 2,
860
+ y: n.cy - n.h / 2 - up,
761
861
  w: n.w,
762
- h: n.h
862
+ h: n.h + up
763
863
  });
764
864
  }
765
865
  for (const e of edges) {
@@ -3584,67 +3684,6 @@ function layoutNodes(diag, lanes) {
3584
3684
  return out;
3585
3685
  }
3586
3686
 
3587
- // src/layout/footer-shape.ts
3588
- var SHAPE_DRAW_UP_EXTENT = {
3589
- "shape-robot-arm": 201,
3590
- // 実測 上 129
3591
- "shape-iot-sensor": 120,
3592
- // 48
3593
- "shape-credit-card": 113,
3594
- // 40.8
3595
- "shape-exchange": 107,
3596
- // 35
3597
- "shape-storefront": 104,
3598
- // 32
3599
- "shape-cdn-edge": 103,
3600
- // 30.8
3601
- "shape-payment-provider": 101,
3602
- // 29
3603
- "shape-gear": 96,
3604
- // 23.9
3605
- "shape-rpc-node": 94,
3606
- // 22
3607
- "shape-wallet": 84
3608
- // 12.1
3609
- };
3610
- function lifelineFooterNodes(lanes, nodes) {
3611
- const byLane = /* @__PURE__ */ new Map();
3612
- for (const n of nodes) {
3613
- const inside = byLane.get(n.lane);
3614
- if (inside) inside.push(n);
3615
- else byLane.set(n.lane, [n]);
3616
- }
3617
- const footers = [];
3618
- for (const lane of lanes) {
3619
- if (!lane.lifeline) continue;
3620
- const inside = byLane.get(lane.id);
3621
- if (inside === void 0 || inside.length < 2) continue;
3622
- let footer = inside[0];
3623
- for (const n of inside) {
3624
- if (n.cy >= footer.cy) footer = n;
3625
- }
3626
- footers.push(footer);
3627
- }
3628
- return footers;
3629
- }
3630
- function footerShapeDropY(kind, h) {
3631
- const extent = SHAPE_DRAW_UP_EXTENT[kind];
3632
- if (extent === void 0) return 0;
3633
- if (!Number.isFinite(h) || h <= 0) return 0;
3634
- return Math.max(0, extent - h);
3635
- }
3636
- function footerShapeDrops(lanes, nodes) {
3637
- return footerShapeDropsOf(lifelineFooterNodes(lanes, nodes));
3638
- }
3639
- function footerShapeDropsOf(footers) {
3640
- const drops = /* @__PURE__ */ new Map();
3641
- for (const footer of footers) {
3642
- const dy = footerShapeDropY(footer.kind, footer.h);
3643
- if (dy > 0) drops.set(footer.id, dy);
3644
- }
3645
- return drops;
3646
- }
3647
-
3648
3687
  // src/layout/viewbox.ts
3649
3688
  function computeViewBox(lanes, nodes, edges, bboxes, viewport) {
3650
3689
  let maxX = 0;
@@ -3665,11 +3704,6 @@ function computeViewBox(lanes, nodes, edges, bboxes, viewport) {
3665
3704
  maxX = Math.max(maxX, n.cx + n.w / 2);
3666
3705
  maxY = Math.max(maxY, n.cy + n.h / 2);
3667
3706
  }
3668
- const footerDrops = footerShapeDrops(lanes, nodes);
3669
- for (const n of nodes) {
3670
- const dy = footerDrops.get(n.id);
3671
- if (dy !== void 0) maxY = Math.max(maxY, n.cy + n.h / 2 + dy);
3672
- }
3673
3707
  if (!Number.isFinite(minX)) minX = 0;
3674
3708
  if (!Number.isFinite(minY)) minY = 0;
3675
3709
  const x = minX - VIEW_PAD;
@@ -3747,7 +3781,7 @@ function applyRowLevelNodeShifts(nodes, lanes, nodeShifts) {
3747
3781
  }
3748
3782
  function layout(diag) {
3749
3783
  const lanes = layoutLanes(diag);
3750
- const rawNodes = layoutNodes(diag, lanes);
3784
+ const rawNodes = applyFooterShapeDrops(lanes, layoutNodes(diag, lanes));
3751
3785
  const expandedLanes = expandLanesForNodes(lanes, rawNodes);
3752
3786
  const gapExpandedLanes = expandLaneGapsForEdgeLabels(expandedLanes, rawNodes, diag.edges);
3753
3787
  const nodes = rawNodes.map((n) => {
@@ -4610,7 +4644,7 @@ function templateRefIds(text) {
4610
4644
  return out;
4611
4645
  }
4612
4646
  function computeStateValues(laid, phaseIdx, progress) {
4613
- const values = {};
4647
+ const values = /* @__PURE__ */ Object.create(null);
4614
4648
  for (const s of laid.states) values[s.id] = s.initial;
4615
4649
  for (let i = 0; i < laid.phases.length; i++) {
4616
4650
  const phase = laid.phases[i];
@@ -4628,10 +4662,20 @@ function computeStateValues(laid, phaseIdx, progress) {
4628
4662
  }
4629
4663
  }
4630
4664
  }
4631
- const out = {};
4665
+ const out = /* @__PURE__ */ Object.create(null);
4632
4666
  for (const [k, v] of Object.entries(values)) out[k] = String(v);
4633
4667
  return withDerivedValues(out, laid.derived);
4634
4668
  }
4669
+ function mergeStateValues(base, overrides) {
4670
+ const merged = Object.assign(/* @__PURE__ */ Object.create(null), base);
4671
+ for (const key of Object.keys(overrides)) {
4672
+ const v = overrides[key];
4673
+ if (typeof v === "number" || typeof v === "string" || typeof v === "boolean") {
4674
+ merged[key] = String(v);
4675
+ }
4676
+ }
4677
+ return merged;
4678
+ }
4635
4679
  function interpolate(template, values) {
4636
4680
  return template.replace(TEMPLATE_REF_RE, (_, id, accessor) => {
4637
4681
  const raw = Object.hasOwn(values, id) ? values[id] : void 0;
@@ -4781,6 +4825,65 @@ function pathSubpath(d, progress) {
4781
4825
  return d;
4782
4826
  }
4783
4827
 
4828
+ // src/render/template-fields.ts
4829
+ var TEXT_FIELDS = ["title", "eyebrow", "subtitle", "value", "wBind", "hBind", "visibleIf"];
4830
+ var LIST_FIELDS = ["rows"];
4831
+ var NUMERIC_FIELDS = ["opacity", "renderOffsetX", "renderOffsetY"];
4832
+ var SHAPE_TEMPLATE_FIELDS = ["source", "radius", "fillProgress", "angle", "level", "rotation", "fill"];
4833
+ function shapeTexts(shape) {
4834
+ if (!shape) return [];
4835
+ const out = [];
4836
+ const \u4E2D\u8EAB = shape;
4837
+ for (const f of SHAPE_TEMPLATE_FIELDS) {
4838
+ const v = \u4E2D\u8EAB[f];
4839
+ if (typeof v === "string" && v) out.push(v);
4840
+ }
4841
+ return out;
4842
+ }
4843
+ function payloadTexts(node) {
4844
+ const out = [];
4845
+ const \u8DB3\u3059 = (v) => {
4846
+ if (typeof v === "string" && v) out.push(v);
4847
+ };
4848
+ for (const d of node.chartData ?? []) \u8DB3\u3059(d.value);
4849
+ for (const s of node.funnelData ?? []) \u8DB3\u3059(s.count);
4850
+ for (const t of node.ganttData ?? []) {
4851
+ \u8DB3\u3059(t.startIdx);
4852
+ \u8DB3\u3059(t.endIdx);
4853
+ }
4854
+ for (const i of node.quadrantData?.items ?? []) \u8DB3\u3059(i.quadrant);
4855
+ for (const s of node.journeyData ?? []) \u8DB3\u3059(s.emotion);
4856
+ for (const n of node.treeData ?? []) {
4857
+ \u8DB3\u3059(n.title);
4858
+ \u8DB3\u3059(n.subtitle);
4859
+ }
4860
+ if (node.mindData) {
4861
+ \u8DB3\u3059(node.mindData.rootTitle);
4862
+ for (const b of node.mindData.branches) {
4863
+ \u8DB3\u3059(b.title);
4864
+ \u8DB3\u3059(b.subtitle);
4865
+ }
4866
+ }
4867
+ return out;
4868
+ }
4869
+ function nodeTemplateTexts(node) {
4870
+ const out = [];
4871
+ for (const f of TEXT_FIELDS) {
4872
+ const v = node[f];
4873
+ if (typeof v === "string" && v) out.push(v);
4874
+ }
4875
+ for (const f of LIST_FIELDS) {
4876
+ for (const v of node[f] ?? []) if (v) out.push(v);
4877
+ }
4878
+ for (const f of NUMERIC_FIELDS) {
4879
+ const v = node[f];
4880
+ if (typeof v === "string" && v) out.push(v);
4881
+ }
4882
+ out.push(...shapeTexts(node.shape));
4883
+ out.push(...payloadTexts(node));
4884
+ return out;
4885
+ }
4886
+
4784
4887
  // src/validate.ts
4785
4888
  var TONES2 = new Set(TONES);
4786
4889
  var KINDS = new Set(NODE_KINDS);
@@ -4839,7 +4942,13 @@ function validate(diag) {
4839
4942
  const nodeIds = new Set(diag.nodes.map((n) => n.id));
4840
4943
  const edgeIds = new Set(diag.edges.map((e) => e.id));
4841
4944
  const stateIds = new Set(diag.states.map((s) => s.id));
4842
- const readableIds = /* @__PURE__ */ new Set([...stateIds, ...(diag.derived ?? []).map((d) => d.id)]);
4945
+ const readableIds = /* @__PURE__ */ new Set([
4946
+ ...stateIds,
4947
+ ...(diag.derived ?? []).map((d) => d.id),
4948
+ ...(diag.inputs ?? []).map((i) => i.id),
4949
+ ...(diag.formulas ?? []).map((f) => f.id),
4950
+ ...(diag.scrollTriggers ?? []).map((t) => t.id)
4951
+ ]);
4843
4952
  const suggest = (target, candidates) => {
4844
4953
  const list = [...candidates];
4845
4954
  if (list.length === 0) return "";
@@ -4906,10 +5015,7 @@ function validate(diag) {
4906
5015
  }
4907
5016
  }
4908
5017
  for (const n of diag.nodes) {
4909
- const checks = [];
4910
- if (n.value) checks.push(n.value);
4911
- if (n.rows) checks.push(...n.rows);
4912
- for (const text of checks) {
5018
+ for (const text of nodeTemplateTexts(n)) {
4913
5019
  for (const id of templateRefIds(text)) {
4914
5020
  if (!readableIds.has(id)) {
4915
5021
  warnings.push(`node "${n.id}" \u304C\u672A\u5B9A\u7FA9 state "${id}" \u3092\u53C2\u7167`);
@@ -4937,8 +5043,7 @@ function validate(diag) {
4937
5043
  for (const s of p.sets) stateUsed.add(s.stateId);
4938
5044
  }
4939
5045
  for (const n of diag.nodes) {
4940
- const texts2 = [n.value, ...n.rows ?? []].filter(Boolean);
4941
- for (const text of texts2) {
5046
+ for (const text of nodeTemplateTexts(n)) {
4942
5047
  for (const id of templateRefIds(text)) stateUsed.add(id);
4943
5048
  }
4944
5049
  }
@@ -5126,7 +5231,7 @@ function StateDiffCard({
5126
5231
  ] }) }),
5127
5232
  /* @__PURE__ */ jsxRuntime.jsx("tbody", { style: { color: "var(--cdl-text, #1a1f2a)" }, children: states.map((s) => {
5128
5233
  const initial = String(s.initial);
5129
- const current = stateValues[s.id] ?? initial;
5234
+ const current = Object.hasOwn(stateValues, s.id) ? stateValues[s.id] : initial;
5130
5235
  const changed = initial !== current;
5131
5236
  return /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
5132
5237
  /* @__PURE__ */ jsxRuntime.jsx("td", { className: "py-1 text-left text-[12px]", children: s.id }),
@@ -6436,21 +6541,32 @@ function resolveBoundText(raw, values) {
6436
6541
  return hasUnresolvedRef(resolved) ? { value: resolved, ok: false } : { value: resolved, ok: true };
6437
6542
  }
6438
6543
  function resolveTreeData(nodes, values) {
6439
- if (!nodes.some((n) => n.title.includes("{"))) return nodes;
6544
+ const \u8AAD\u3080 = nodes.some((n) => n.title.includes("{") || (n.subtitle?.includes("{") ?? false));
6545
+ if (!\u8AAD\u3080) return nodes;
6440
6546
  return nodes.map((n) => {
6441
6547
  const title = resolveBoundText(n.title, values);
6442
- const \u89E3\u6C7A\u5F8C = { ...n, title: title.value };
6443
- return title.ok ? \u89E3\u6C7A\u5F8C : { ...\u89E3\u6C7A\u5F8C, unresolved: true };
6548
+ const subtitle = n.subtitle === void 0 ? void 0 : resolveBoundText(n.subtitle, values);
6549
+ const \u89E3\u6C7A\u5F8C = {
6550
+ ...n,
6551
+ title: title.value,
6552
+ ...subtitle ? { subtitle: subtitle.value } : {}
6553
+ };
6554
+ return title.ok && (subtitle?.ok ?? true) ? \u89E3\u6C7A\u5F8C : { ...\u89E3\u6C7A\u5F8C, unresolved: true };
6444
6555
  });
6445
6556
  }
6446
6557
  function resolveMindData(data, values) {
6447
- const \u8AAD\u3080 = data.rootTitle.includes("{") || data.branches.some((b) => b.title.includes("{"));
6558
+ const \u8AAD\u3080 = data.rootTitle.includes("{") || data.branches.some((b) => b.title.includes("{") || (b.subtitle?.includes("{") ?? false));
6448
6559
  if (!\u8AAD\u3080) return data;
6449
6560
  const root = resolveBoundText(data.rootTitle, values);
6450
6561
  const branches = data.branches.map((b) => {
6451
6562
  const title = resolveBoundText(b.title, values);
6452
- const \u89E3\u6C7A\u5F8C2 = { ...b, title: title.value };
6453
- return title.ok ? \u89E3\u6C7A\u5F8C2 : { ...\u89E3\u6C7A\u5F8C2, unresolved: true };
6563
+ const subtitle = b.subtitle === void 0 ? void 0 : resolveBoundText(b.subtitle, values);
6564
+ const \u89E3\u6C7A\u5F8C2 = {
6565
+ ...b,
6566
+ title: title.value,
6567
+ ...subtitle ? { subtitle: subtitle.value } : {}
6568
+ };
6569
+ return title.ok && (subtitle?.ok ?? true) ? \u89E3\u6C7A\u5F8C2 : { ...\u89E3\u6C7A\u5F8C2, unresolved: true };
6454
6570
  });
6455
6571
  const \u89E3\u6C7A\u5F8C = { ...data, rootTitle: root.value, branches };
6456
6572
  return root.ok ? \u89E3\u6C7A\u5F8C : { ...\u89E3\u6C7A\u5F8C, unresolved: true };
@@ -6861,16 +6977,53 @@ function GanttNode({
6861
6977
  const rowGap = 20;
6862
6978
  const rowCount = tasks.length || 1;
6863
6979
  const rowH = Math.max(28, (canvasH - rowGap * (rowCount + 1)) / rowCount);
6864
- const maxIdx = Math.max(1, Math.ceil(Math.max(...tasks.map((t) => t.endIdx + 1), 1)));
6980
+ const \u4F4D\u7F6E\u306E\u4E0A\u9650 = 1e6;
6981
+ const \u53CE\u3081\u305F = tasks.map((t) => {
6982
+ const \u59CB = Math.min(\u4F4D\u7F6E\u306E\u4E0A\u9650, Math.max(0, Number.isFinite(t.startIdx) ? t.startIdx : 0));
6983
+ const \u7D42 = Math.min(\u4F4D\u7F6E\u306E\u4E0A\u9650, Math.max(\u59CB, Number.isFinite(t.endIdx) ? t.endIdx : \u59CB));
6984
+ return { ...t, startIdx: \u59CB, endIdx: \u7D42 };
6985
+ });
6986
+ const \u5BA3\u8A00 = node.ganttAxisMax;
6987
+ 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;
6988
+ const maxIdx = \u5BA3\u8A00\u3057\u305F\u5C3A ?? Math.max(
6989
+ 1,
6990
+ Math.ceil(Math.max(...\u53CE\u3081\u305F.flatMap((t) => [t.startIdx + 1, t.endIdx + 1]), 1))
6991
+ );
6865
6992
  const cellW = canvasW / maxIdx;
6993
+ const \u6700\u5F8C\u306E\u76EE\u76DB\u308A = maxIdx - 1;
6994
+ const \u6574\u3048\u305F = \u5BA3\u8A00\u3057\u305F\u5C3A === void 0 ? \u53CE\u3081\u305F : \u53CE\u3081\u305F.map((t) => {
6995
+ const \u59CB = Math.min(t.startIdx, \u6700\u5F8C\u306E\u76EE\u76DB\u308A);
6996
+ const \u7D42 = Math.min(t.endIdx, \u6700\u5F8C\u306E\u76EE\u76DB\u308A);
6997
+ const \u5BC4\u305B\u305F = \u59CB !== t.startIdx || \u7D42 !== t.endIdx;
6998
+ return \u5BC4\u305B\u305F ? { ...t, startIdx: \u59CB, endIdx: \u7D42, clipped: true } : t;
6999
+ });
7000
+ const \u76EE\u76DB\u308A\u306E\u4E0A\u9650 = 200;
7001
+ const \u76EE\u76DB\u308A\u306E\u9593\u9694 = Math.max(1, Math.ceil(maxIdx / \u76EE\u76DB\u308A\u306E\u4E0A\u9650));
7002
+ const \u76EE\u76DB\u308A\u306E\u672C\u6570 = Math.ceil(maxIdx / \u76EE\u76DB\u308A\u306E\u9593\u9694);
6866
7003
  const xAt = (idx) => PAD_LEFT + idx * cellW;
6867
7004
  const yAt = (row) => PAD_TOP + rowGap + row * (rowH + rowGap);
6868
- const gridLabels = Array.from({ length: maxIdx }, (_, i) => {
6869
- const t = tasks.find((tt) => tt.startIdx === i);
6870
- return t?.startLabel ?? String(i);
6871
- });
6872
- const taskById = new Map(tasks.map((t) => [t.id, t]));
6873
- const rowOf = new Map(tasks.map((t, i) => [t.id, i]));
7005
+ 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() };
7006
+ const \u7F6E\u304F = (\u8868, \u4F4D\u7F6E, label) => {
7007
+ if (label !== "" && Number.isFinite(\u4F4D\u7F6E) && !\u8868.has(\u4F4D\u7F6E)) \u8868.set(\u4F4D\u7F6E, label);
7008
+ };
7009
+ for (const t of \u53CE\u3081\u305F) {
7010
+ const \u6574\u6570\u306E\u958B\u59CB = Number.isInteger(t.startIdx);
7011
+ \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);
7012
+ const \u6574\u6570\u306E\u7D42\u4E86 = Number.isInteger(t.endIdx);
7013
+ \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);
7014
+ }
7015
+ 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) ?? "";
7016
+ const \u4F4D\u7F6E\u306E\u96C6\u5408 = /* @__PURE__ */ new Set();
7017
+ 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);
7018
+ 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]) {
7019
+ for (const i of \u8868.keys()) if (i >= 0 && i < maxIdx) \u4F4D\u7F6E\u306E\u96C6\u5408.add(i);
7020
+ }
7021
+ const gridLabels = [...\u4F4D\u7F6E\u306E\u96C6\u5408].sort((a, b) => a - b).map((i) => ({ \u4F4D\u7F6E: i, label: \u540D\u524D\u3092\u5F15\u304F(i) }));
7022
+ const \u5E2F\u306E\u4F59\u767D = Math.min(6, cellW / 2);
7023
+ const \u5E2F\u306E\u5DE6 = (idx) => xAt(idx) + \u5E2F\u306E\u4F59\u767D / 2;
7024
+ const \u5E2F\u306E\u53F3 = (idx) => xAt(idx) + cellW - \u5E2F\u306E\u4F59\u767D / 2;
7025
+ const taskById = new Map(\u6574\u3048\u305F.map((t) => [t.id, t]));
7026
+ const rowOf = new Map(\u6574\u3048\u305F.map((t, i) => [t.id, i]));
6874
7027
  return /* @__PURE__ */ jsxRuntime.jsxs("g", { transform: `translate(${x0} ${y0})`, children: [
6875
7028
  /* @__PURE__ */ jsxRuntime.jsx(
6876
7029
  "rect",
@@ -6886,10 +7039,11 @@ function GanttNode({
6886
7039
  strokeWidth: active ? 3 : 1.25
6887
7040
  }
6888
7041
  ),
6889
- gridLabels.map((label, i) => /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
7042
+ gridLabels.map(({ \u4F4D\u7F6E: i, label }) => /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
6890
7043
  /* @__PURE__ */ jsxRuntime.jsx(
6891
7044
  "line",
6892
7045
  {
7046
+ "data-cdl-role": "gantt-grid",
6893
7047
  x1: xAt(i),
6894
7048
  y1: PAD_TOP,
6895
7049
  x2: xAt(i),
@@ -6900,9 +7054,10 @@ function GanttNode({
6900
7054
  opacity: 0.5
6901
7055
  }
6902
7056
  ),
6903
- /* @__PURE__ */ jsxRuntime.jsx(
7057
+ label === "" ? null : /* @__PURE__ */ jsxRuntime.jsx(
6904
7058
  "text",
6905
7059
  {
7060
+ "data-cdl-role": "gantt-tick",
6906
7061
  x: xAt(i) + cellW / 2,
6907
7062
  y: PAD_TOP + canvasH + 18,
6908
7063
  fontSize: 11,
@@ -6924,7 +7079,7 @@ function GanttNode({
6924
7079
  opacity: 0.5
6925
7080
  }
6926
7081
  ),
6927
- tasks.map((t, row) => /* @__PURE__ */ jsxRuntime.jsx(
7082
+ \u6574\u3048\u305F.map((t, row) => /* @__PURE__ */ jsxRuntime.jsx(
6928
7083
  "text",
6929
7084
  {
6930
7085
  x: PAD_LEFT - 12,
@@ -6937,16 +7092,17 @@ function GanttNode({
6937
7092
  },
6938
7093
  `label-${t.id}`
6939
7094
  )),
6940
- tasks.map((t, row) => {
6941
- const bx = xAt(t.startIdx) + 3;
7095
+ \u6574\u3048\u305F.map((t, row) => {
7096
+ const bx = \u5E2F\u306E\u5DE6(t.startIdx);
6942
7097
  const by = yAt(row);
6943
- const bw = Math.max(cellW - 6, (t.endIdx - t.startIdx + 1) * cellW - 6);
7098
+ const bw = Math.max(0, \u5E2F\u306E\u53F3(t.endIdx) - bx);
6944
7099
  return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
6945
7100
  /* @__PURE__ */ jsxRuntime.jsx(
6946
7101
  "rect",
6947
7102
  {
6948
7103
  "data-cdl-role": "gantt-bar",
6949
7104
  "data-cdl-unresolved": t.unresolved ? "true" : void 0,
7105
+ "data-cdl-clipped": "clipped" in t && t.clipped ? "true" : void 0,
6950
7106
  x: bx,
6951
7107
  y: by,
6952
7108
  width: bw,
@@ -6969,13 +7125,13 @@ function GanttNode({
6969
7125
  )
6970
7126
  ] }, `bar-${t.id}`);
6971
7127
  }),
6972
- tasks.filter((t) => t.dependsOn && taskById.has(t.dependsOn)).map((t) => {
7128
+ \u6574\u3048\u305F.filter((t) => t.dependsOn && taskById.has(t.dependsOn)).map((t) => {
6973
7129
  const parent = taskById.get(t.dependsOn);
6974
7130
  const parentRow = rowOf.get(parent.id);
6975
7131
  const childRow = rowOf.get(t.id);
6976
- const parentBarRight = xAt(parent.endIdx) + cellW - 3;
7132
+ const parentBarRight = \u5E2F\u306E\u53F3(parent.endIdx);
6977
7133
  const parentMidY = yAt(parentRow) + rowH / 2;
6978
- const childBarLeft = xAt(t.startIdx) + 3;
7134
+ const childBarLeft = \u5E2F\u306E\u5DE6(t.startIdx);
6979
7135
  const childMidY = yAt(childRow) + rowH / 2;
6980
7136
  const arrowHeadSize = 8;
6981
7137
  const arrowHeadTipGap = 10;
@@ -7017,6 +7173,49 @@ function toneColor3(tone) {
7017
7173
  if (!tone) return TONE.accent;
7018
7174
  return TONE[tone];
7019
7175
  }
7176
+
7177
+ // src/kinds/box-lines.ts
7178
+ var \u884C\u9001\u308A\u306E\u6BD4 = 1.15;
7179
+ var \u5B57\u306E\u9AD8\u3055\u306E\u6BD4 = 0.72;
7180
+ var \u4E0A\u4E0B\u306E\u4F59\u767D = 2;
7181
+ var \u5DE6\u53F3\u306E\u4F59\u767D = 8;
7182
+ function \u5E45\u3067\u5207\u308B(text, fontSize, \u4F7F\u3048\u308B\u5E45, \u6E2C\u308B = textWidth) {
7183
+ if (\u4F7F\u3048\u308B\u5E45 <= 0) return "";
7184
+ if (\u6E2C\u308B(text, fontSize) <= \u4F7F\u3048\u308B\u5E45) return text;
7185
+ const \u7701\u7565 = "\u2026";
7186
+ const \u7701\u7565\u306E\u5E45 = \u6E2C\u308B(\u7701\u7565, fontSize);
7187
+ if (\u7701\u7565\u306E\u5E45 > \u4F7F\u3048\u308B\u5E45) return "";
7188
+ let \u51FA = "";
7189
+ let \u5E45 = 0;
7190
+ for (const c of \u66F8\u8A18\u7D20\u306B\u5206\u3051\u308B(text)) {
7191
+ const \u5B57\u306E\u5E452 = \u6E2C\u308B(c, fontSize);
7192
+ if (\u5E45 + \u5B57\u306E\u5E452 + \u7701\u7565\u306E\u5E45 > \u4F7F\u3048\u308B\u5E45) break;
7193
+ \u5E45 += \u5B57\u306E\u5E452;
7194
+ \u51FA += c;
7195
+ }
7196
+ return `${\u51FA}${\u7701\u7565}`;
7197
+ }
7198
+ function \u66F8\u8A18\u7D20\u306B\u5206\u3051\u308B(text) {
7199
+ const \u533A\u5207\u308A = Intl.Segmenter;
7200
+ if (typeof \u533A\u5207\u308A !== "function") return [...text];
7201
+ return [...new \u533A\u5207\u308A(void 0, { granularity: "grapheme" }).segment(text)].map((g) => g.segment);
7202
+ }
7203
+ function \u7BB1\u306B\u7A4D\u3080(\u884C, \u7BB1\u306E\u5E45, \u7BB1\u306E\u9AD8\u3055) {
7204
+ const \u5019\u88DC = \u884C.filter((r, i) => i === 0 || r.text.trim() !== "");
7205
+ if (\u5019\u88DC.length === 0) return [];
7206
+ const \u4F7F\u3048\u308B\u9AD8\u3055 = \u7BB1\u306E\u9AD8\u3055 - \u4E0A\u4E0B\u306E\u4F59\u767D * 2;
7207
+ const \u9AD8\u3055 = (rs) => rs.reduce((a, r) => a + r.fontSize * \u884C\u9001\u308A\u306E\u6BD4, 0);
7208
+ const \u6B8B\u3059 = [...\u5019\u88DC];
7209
+ while (\u6B8B\u3059.length > 1 && \u9AD8\u3055(\u6B8B\u3059) > \u4F7F\u3048\u308B\u9AD8\u3055) \u6B8B\u3059.pop();
7210
+ const \u4F7F\u3048\u308B\u5E45 = \u7BB1\u306E\u5E45 - \u5DE6\u53F3\u306E\u4F59\u767D * 2;
7211
+ const \u7DCF\u9AD8 = \u9AD8\u3055(\u6B8B\u3059);
7212
+ let \u7A4D\u307F = -\u7DCF\u9AD8 / 2;
7213
+ return \u6B8B\u3059.map((r) => {
7214
+ const dy = \u7A4D\u307F + (\u884C\u9001\u308A\u306E\u6BD4 + \u5B57\u306E\u9AD8\u3055\u306E\u6BD4) / 2 * r.fontSize;
7215
+ \u7A4D\u307F += r.fontSize * \u884C\u9001\u308A\u306E\u6BD4;
7216
+ return { ...r, text: \u5E45\u3067\u5207\u308B(r.text, r.fontSize, \u4F7F\u3048\u308B\u5E45), dy };
7217
+ });
7218
+ }
7020
7219
  var MIND_TONES = ["accent", "teal", "success", "warning", "info", "error"];
7021
7220
  var MIND_BOX_GAP = 24;
7022
7221
  function MindMapNode({
@@ -7062,33 +7261,59 @@ function MindMapNode({
7062
7261
  },
7063
7262
  `edge-${i}`
7064
7263
  )),
7065
- layout2.nodes.map((n) => /* @__PURE__ */ jsxRuntime.jsxs("g", { "data-cdl-unresolved": n.unresolved ? "true" : void 0, children: [
7066
- /* @__PURE__ */ jsxRuntime.jsx(
7067
- "rect",
7068
- {
7069
- x: n.x - n.w / 2,
7070
- y: n.y - n.h / 2,
7071
- width: n.w,
7072
- height: n.h,
7073
- rx: n.isRoot ? 22 : 10,
7074
- fill: n.isRoot ? toneColor4(n.tone ?? "accent") : "var(--cdl-chip-fill, #f4f6fb)",
7075
- stroke: n.isRoot ? toneColor4(n.tone ?? "accent") : toneColor4(n.tone),
7076
- strokeWidth: n.isRoot ? 2 : 1.75
7077
- }
7078
- ),
7079
- /* @__PURE__ */ jsxRuntime.jsx(
7080
- "text",
7081
- {
7082
- x: n.x,
7083
- y: n.y + 5,
7084
- fontSize: n.isRoot ? 15 : 13,
7085
- fontWeight: n.isRoot ? 700 : 600,
7086
- textAnchor: "middle",
7087
- fill: n.isRoot ? "var(--cdl-text-on-tone, #ffffff)" : "var(--cdl-chip-text, #1a1f2a)",
7088
- children: n.title
7089
- }
7090
- )
7091
- ] }, `n-${n.id}`))
7264
+ layout2.nodes.map((n) => {
7265
+ const \u884C = \u7BB1\u306B\u7A4D\u3080(
7266
+ [
7267
+ { \u5F79\u5272: "title", text: n.title, fontSize: n.isRoot ? 15 : 13 },
7268
+ { \u5F79\u5272: "subtitle", text: n.subtitle ?? "", fontSize: 9 }
7269
+ ],
7270
+ n.w,
7271
+ n.h
7272
+ );
7273
+ const \u88DC\u8DB3\u3042\u308A = (n.subtitle ?? "").trim() !== "";
7274
+ const \u6587\u5B57\u306E\u8272 = n.isRoot ? "var(--cdl-text-on-tone, #ffffff)" : "var(--cdl-chip-text, #1a1f2a)";
7275
+ return /* @__PURE__ */ jsxRuntime.jsxs("g", { "data-cdl-unresolved": n.unresolved ? "true" : void 0, children: [
7276
+ /* @__PURE__ */ jsxRuntime.jsx(
7277
+ "rect",
7278
+ {
7279
+ x: n.x - n.w / 2,
7280
+ y: n.y - n.h / 2,
7281
+ width: n.w,
7282
+ height: n.h,
7283
+ rx: n.isRoot ? 22 : 10,
7284
+ fill: n.isRoot ? toneColor4(n.tone ?? "accent") : "var(--cdl-chip-fill, #f4f6fb)",
7285
+ stroke: n.isRoot ? toneColor4(n.tone ?? "accent") : toneColor4(n.tone),
7286
+ strokeWidth: n.isRoot ? 2 : 1.75
7287
+ }
7288
+ ),
7289
+ \u88DC\u8DB3\u3042\u308A ? \u884C.map((r) => /* @__PURE__ */ jsxRuntime.jsx(
7290
+ "text",
7291
+ {
7292
+ "data-cdl-role": r.\u5F79\u5272 === "subtitle" ? "mind-node-subtitle" : void 0,
7293
+ x: n.x,
7294
+ y: n.y + r.dy,
7295
+ fontSize: r.fontSize,
7296
+ fontWeight: r.\u5F79\u5272 === "title" ? n.isRoot ? 700 : 600 : 500,
7297
+ textAnchor: "middle",
7298
+ fill: \u6587\u5B57\u306E\u8272,
7299
+ opacity: r.\u5F79\u5272 === "subtitle" ? 0.85 : void 0,
7300
+ children: r.text
7301
+ },
7302
+ r.\u5F79\u5272
7303
+ )) : /* @__PURE__ */ jsxRuntime.jsx(
7304
+ "text",
7305
+ {
7306
+ x: n.x,
7307
+ y: n.y + 5,
7308
+ fontSize: n.isRoot ? 15 : 13,
7309
+ fontWeight: n.isRoot ? 700 : 600,
7310
+ textAnchor: "middle",
7311
+ fill: \u6587\u5B57\u306E\u8272,
7312
+ children: n.title
7313
+ }
7314
+ )
7315
+ ] }, `n-${n.id}`);
7316
+ })
7092
7317
  ] });
7093
7318
  }
7094
7319
  function computeMindMapLayout(mindData, cx, cy, canvasW, canvasH) {
@@ -7210,6 +7435,7 @@ function computeMindMapLayout(mindData, cx, cy, canvasW, canvasH) {
7210
7435
  nodes.push({
7211
7436
  id: node.id,
7212
7437
  title: node.title,
7438
+ ...node.subtitle !== void 0 ? { subtitle: node.subtitle } : {},
7213
7439
  x,
7214
7440
  y,
7215
7441
  w,
@@ -7414,6 +7640,9 @@ function QuadrantNode({
7414
7640
  const PAD_BOTTOM = 56;
7415
7641
  const AXIS_LABEL_FONT = 12;
7416
7642
  const PAD_MIN = 60;
7643
+ const ITEM_FONT = 13;
7644
+ const ITEM_PAD_X = 12;
7645
+ const ITEM_MIN_W = 48;
7417
7646
  const axisLabelPad = (text) => Math.max(PAD_MIN, measureTextWidth(`\u2190 ${text}`, { fontSize: AXIS_LABEL_FONT}) + 12);
7418
7647
  const padBudget = Math.max(0, node.w * 0.5 - PAD_MIN * 2);
7419
7648
  const wantLeft = axisLabelPad(data.xAxis.left) - PAD_MIN;
@@ -7509,6 +7738,12 @@ function QuadrantNode({
7509
7738
  const qxStart = (isLeft ? PAD_LEFT : cx) + 18;
7510
7739
  const qyStart = (isTop ? PAD_TOP : cy) + 44;
7511
7740
  const qHalfW = halfW - 36;
7741
+ const \u67A0\u306E\u4F59\u5730 = qHalfW;
7742
+ if (\u67A0\u306E\u4F59\u5730 <= 0) return null;
7743
+ const \u67A0\u306E\u5E45 = (title) => Math.min(
7744
+ \u67A0\u306E\u4F59\u5730,
7745
+ Math.max(ITEM_MIN_W, measureTextWidth(title, { fontSize: ITEM_FONT}) + ITEM_PAD_X * 2)
7746
+ );
7512
7747
  const qHalfH = halfH - 60;
7513
7748
  const gap = 10;
7514
7749
  const rowH = 34;
@@ -7523,7 +7758,7 @@ function QuadrantNode({
7523
7758
  "data-cdl-unresolved": it.unresolved ? "true" : void 0,
7524
7759
  x: qxStart,
7525
7760
  y: py,
7526
- width: qHalfW,
7761
+ width: \u67A0\u306E\u5E45(it.title),
7527
7762
  height: rowH,
7528
7763
  rx: 6,
7529
7764
  fill: "var(--cdl-node-fill, #ffffff)",
@@ -7531,12 +7766,35 @@ function QuadrantNode({
7531
7766
  strokeWidth: 1.5
7532
7767
  }
7533
7768
  ),
7534
- /* @__PURE__ */ jsxRuntime.jsx("text", { x: qxStart + 12, y: py + rowH / 2 + 5, fontSize: 13, fontWeight: 600, fill: "var(--cdl-text, #1a1f2a)", children: it.title })
7769
+ /* @__PURE__ */ jsxRuntime.jsx("text", { x: qxStart + ITEM_PAD_X, y: py + rowH / 2 + 5, fontSize: ITEM_FONT, fontWeight: 600, letterSpacing: 0, fill: "var(--cdl-text, #1a1f2a)", children: it.title })
7535
7770
  ] }, `item-${it.id}`);
7536
7771
  });
7537
7772
  })
7538
7773
  ] });
7539
7774
  }
7775
+ function \u7BB1\u306E\u6587\u5B57({
7776
+ \u884C,
7777
+ x,
7778
+ \u7BB1\u306E\u9AD8\u3055,
7779
+ fill,
7780
+ \u540D\u524D\u306E\u592A\u3055
7781
+ }) {
7782
+ return \u884C.map((r) => /* @__PURE__ */ jsxRuntime.jsx(
7783
+ "text",
7784
+ {
7785
+ "data-cdl-role": r.\u5F79\u5272 === "subtitle" ? "tree-node-subtitle" : void 0,
7786
+ x,
7787
+ y: \u7BB1\u306E\u9AD8\u3055 / 2 + r.dy,
7788
+ fontSize: r.fontSize,
7789
+ fontWeight: r.\u5F79\u5272 === "title" ? \u540D\u524D\u306E\u592A\u3055 : 500,
7790
+ textAnchor: "middle",
7791
+ fill,
7792
+ opacity: r.\u5F79\u5272 === "subtitle" ? 0.85 : void 0,
7793
+ children: r.text
7794
+ },
7795
+ r.\u5F79\u5272
7796
+ ));
7797
+ }
7540
7798
  function TreeNode({
7541
7799
  node,
7542
7800
  active,
@@ -7599,6 +7857,15 @@ function TreeNode({
7599
7857
  layout2.nodes.map((n) => {
7600
7858
  const accent = depthColors[Math.min(n.depth, depthColors.length - 1)] ?? depthColors[0];
7601
7859
  const isRoot = n.depth === 0;
7860
+ const \u884C = \u7BB1\u306B\u7A4D\u3080(
7861
+ [
7862
+ { \u5F79\u5272: "title", text: n.title, fontSize: isRoot ? 13 : 12 },
7863
+ { \u5F79\u5272: "subtitle", text: n.subtitle ?? "", fontSize: 9 }
7864
+ ],
7865
+ n.w,
7866
+ n.h
7867
+ );
7868
+ const \u88DC\u8DB3\u3042\u308A = (n.subtitle ?? "").trim() !== "";
7602
7869
  return /* @__PURE__ */ jsxRuntime.jsx(
7603
7870
  "g",
7604
7871
  {
@@ -7617,7 +7884,13 @@ function TreeNode({
7617
7884
  fill: `url(#${gradientId})`
7618
7885
  }
7619
7886
  ),
7620
- /* @__PURE__ */ jsxRuntime.jsx(
7887
+ \u88DC\u8DB3\u3042\u308A ? \u7BB1\u306E\u6587\u5B57({
7888
+ \u884C,
7889
+ x: n.w / 2,
7890
+ \u7BB1\u306E\u9AD8\u3055: n.h,
7891
+ fill: "var(--cdl-text-on-tone, #ffffff)",
7892
+ \u540D\u524D\u306E\u592A\u3055: 700
7893
+ }) : /* @__PURE__ */ jsxRuntime.jsx(
7621
7894
  "text",
7622
7895
  {
7623
7896
  x: n.w / 2,
@@ -7645,7 +7918,13 @@ function TreeNode({
7645
7918
  }
7646
7919
  ),
7647
7920
  /* @__PURE__ */ jsxRuntime.jsx("rect", { x: 0, y: 0, width: 4, height: n.h, rx: 2, fill: accent }),
7648
- /* @__PURE__ */ jsxRuntime.jsx(
7921
+ \u88DC\u8DB3\u3042\u308A ? \u7BB1\u306E\u6587\u5B57({
7922
+ \u884C,
7923
+ x: n.w / 2 + 2,
7924
+ \u7BB1\u306E\u9AD8\u3055: n.h,
7925
+ fill: "var(--cdl-chip-text, #1a1f2a)",
7926
+ \u540D\u524D\u306E\u592A\u3055: 600
7927
+ }) : /* @__PURE__ */ jsxRuntime.jsx(
7649
7928
  "text",
7650
7929
  {
7651
7930
  x: n.w / 2 + 2,
@@ -7726,6 +8005,7 @@ function computeTreeLayout(treeNodes, canvasW, canvasH) {
7726
8005
  nodes.push({
7727
8006
  id: n.id,
7728
8007
  title: n.title,
8008
+ ...n.subtitle !== void 0 ? { subtitle: n.subtitle } : {},
7729
8009
  x,
7730
8010
  y,
7731
8011
  w: nodeW,
@@ -8016,42 +8296,106 @@ var BOTTOM_PAD = 4;
8016
8296
  var ASCENT2 = 0.72;
8017
8297
  var DESCENT2 = 0.25;
8018
8298
  var ART_GAP = 4;
8299
+ var ART_MIN_BAND = 45;
8300
+ var \u843D\u3068\u3057\u3066\u3082\u63CF\u304F\u7A2E\u5225 = {
8301
+ \u80A9\u66F8: /* @__PURE__ */ new Set([
8302
+ // basement 系 = `commonLabel` が自前の帯に置く
8303
+ "shape-file",
8304
+ "shape-folder",
8305
+ "shape-cloud",
8306
+ "shape-cylinder",
8307
+ "shape-hexagon",
8308
+ "shape-diamond",
8309
+ "shape-stack",
8310
+ // software 系 = 固有 layout
8311
+ "shape-window",
8312
+ "shape-terminal",
8313
+ "shape-code-block",
8314
+ "shape-message-bubble",
8315
+ "shape-gear",
8316
+ // hardware 系のうち固有 layout を持つもの
8317
+ "shape-iot-sensor",
8318
+ "shape-robot-arm"
8319
+ ]),
8320
+ \u8AAC\u660E: /* @__PURE__ */ new Set([
8321
+ // basement 系 = `commonLabel` が自前の帯に置く
8322
+ "shape-file",
8323
+ "shape-folder",
8324
+ "shape-cloud",
8325
+ "shape-cylinder",
8326
+ "shape-hexagon",
8327
+ "shape-diamond",
8328
+ "shape-stack",
8329
+ // software 系 = 固有 layout
8330
+ "shape-window",
8331
+ "shape-message-bubble",
8332
+ "shape-gear",
8333
+ // hardware / blockchain 系のうち固有 layout を持つもの
8334
+ "shape-iot-sensor",
8335
+ "shape-robot-arm",
8336
+ "shape-blockchain-block"
8337
+ ])
8338
+ };
8339
+ function droppedLineIsHidden(kind, line) {
8340
+ return kind.startsWith("shape-") && !\u843D\u3068\u3057\u3066\u3082\u63CF\u304F\u7A2E\u5225[line].has(kind);
8341
+ }
8019
8342
  function labelPlan(node) {
8020
8343
  const \u9AD8\u3055 = Math.max(0, node.h);
8021
8344
  const \u5E95 = node.cy + \u9AD8\u3055 / 2;
8022
8345
  const \u5929 = node.cy - \u9AD8\u3055 / 2;
8023
- const \u7A4D\u3080 = (\u540D\u524D\u59272, \u8AAC\u660E\u3042\u308A2, \u80A9\u66F8\u3042\u308A2, \u4E0B\u4F59\u767D2) => {
8024
- const \u5C0F\u3055\u3044\u5B572 = Math.max(1, Math.round(\u540D\u524D\u59272 * SUB_RATIO));
8025
- let y = \u5E95 - \u4E0B\u4F59\u767D2;
8026
- const \u8AAC\u660E = \u8AAC\u660E\u3042\u308A2 ? { y, size: \u5C0F\u3055\u3044\u5B572 } : null;
8027
- if (\u8AAC\u660E !== null) y -= \u5C0F\u3055\u3044\u5B572 + LINE_GAP;
8028
- const \u540D\u524D = { y, size: \u540D\u524D\u59272 };
8029
- y -= \u540D\u524D\u59272 + LINE_GAP;
8030
- const \u80A9\u66F8 = \u80A9\u66F8\u3042\u308A2 ? { y, size: \u5C0F\u3055\u3044\u5B572 } : null;
8346
+ const \u7A4D\u3080 = (\u540D\u524D\u5927, \u8AAC\u660E\u3042\u308A2, \u80A9\u66F8\u3042\u308A2, \u4E0B\u4F59\u767D) => {
8347
+ const \u5C0F\u3055\u3044\u5B57 = Math.max(1, Math.round(\u540D\u524D\u5927 * SUB_RATIO));
8348
+ let y = \u5E95 - \u4E0B\u4F59\u767D;
8349
+ const \u8AAC\u660E = \u8AAC\u660E\u3042\u308A2 ? { y, size: \u5C0F\u3055\u3044\u5B57 } : null;
8350
+ if (\u8AAC\u660E !== null) y -= \u5C0F\u3055\u3044\u5B57 + LINE_GAP;
8351
+ const \u540D\u524D = { y, size: \u540D\u524D\u5927 };
8352
+ y -= \u540D\u524D\u5927 + LINE_GAP;
8353
+ const \u80A9\u66F8 = \u80A9\u66F8\u3042\u308A2 ? { y, size: \u5C0F\u3055\u3044\u5B57 } : null;
8031
8354
  const \u4E0A\u306E\u884C = \u80A9\u66F8 ?? \u540D\u524D;
8032
8355
  return { \u540D\u524D, \u8AAC\u660E, \u80A9\u66F8, \u4E0A\u7AEF: \u4E0A\u306E\u884C.y - \u4E0A\u306E\u884C.size * ASCENT2 };
8033
8356
  };
8034
8357
  const \u5165\u308B = (\u8AAC\u660E\u3042\u308A2, \u80A9\u66F8\u3042\u308A2) => \u7A4D\u3080(1, \u8AAC\u660E\u3042\u308A2, \u80A9\u66F8\u3042\u308A2, 1 * DESCENT2).\u4E0A\u7AEF >= \u5929;
8035
8358
  let \u8AAC\u660E\u3042\u308A = Boolean(node.subtitle);
8036
8359
  let \u80A9\u66F8\u3042\u308A = Boolean(node.eyebrow);
8037
- if (!\u5165\u308B(\u8AAC\u660E\u3042\u308A, \u80A9\u66F8\u3042\u308A)) \u80A9\u66F8\u3042\u308A = false;
8038
- if (!\u5165\u308B(\u8AAC\u660E\u3042\u308A, \u80A9\u66F8\u3042\u308A)) \u8AAC\u660E\u3042\u308A = false;
8039
- const \u4E0A\u9650 = Math.min(TITLE_MAX, Math.max(1, Math.round(\u9AD8\u3055 * TITLE_RATIO)));
8040
- let \u540D\u524D\u5927 = 0;
8041
- for (let size = \u4E0A\u9650; size >= 1; size--) {
8042
- const \u5C0F\u3055\u3044\u5B572 = Math.max(1, Math.round(size * SUB_RATIO));
8043
- const \u4E0B\u306E\u884C\u5927 = \u8AAC\u660E\u3042\u308A ? \u5C0F\u3055\u3044\u5B572 : size;
8044
- if (\u7A4D\u3080(size, \u8AAC\u660E\u3042\u308A, \u80A9\u66F8\u3042\u308A, \u4E0B\u306E\u884C\u5927 * DESCENT2).\u4E0A\u7AEF >= \u5929) {
8045
- \u540D\u524D\u5927 = size;
8046
- break;
8047
- }
8360
+ const \u843D\u3068\u3057\u305F = [];
8361
+ if (!\u5165\u308B(\u8AAC\u660E\u3042\u308A, \u80A9\u66F8\u3042\u308A) && \u80A9\u66F8\u3042\u308A) {
8362
+ \u80A9\u66F8\u3042\u308A = false;
8363
+ \u843D\u3068\u3057\u305F.push("\u80A9\u66F8");
8364
+ }
8365
+ if (!\u5165\u308B(\u8AAC\u660E\u3042\u308A, \u80A9\u66F8\u3042\u308A) && \u8AAC\u660E\u3042\u308A) {
8366
+ \u8AAC\u660E\u3042\u308A = false;
8367
+ \u843D\u3068\u3057\u305F.push("\u8AAC\u660E");
8368
+ }
8369
+ const \u7D44\u3080 = (\u8AAC\u660E\u3042\u308A2, \u80A9\u66F8\u3042\u308A2) => {
8370
+ const \u4E0A\u9650 = Math.min(TITLE_MAX, Math.max(1, Math.round(\u9AD8\u3055 * TITLE_RATIO)));
8371
+ let \u540D\u524D\u5927 = 0;
8372
+ for (let size = \u4E0A\u9650; size >= 1; size--) {
8373
+ const \u5C0F\u3055\u3044\u5B572 = Math.max(1, Math.round(size * SUB_RATIO));
8374
+ const \u4E0B\u306E\u884C\u5927 = \u8AAC\u660E\u3042\u308A2 ? \u5C0F\u3055\u3044\u5B572 : size;
8375
+ if (\u7A4D\u3080(size, \u8AAC\u660E\u3042\u308A2, \u80A9\u66F8\u3042\u308A2, \u4E0B\u306E\u884C\u5927 * DESCENT2).\u4E0A\u7AEF >= \u5929) {
8376
+ \u540D\u524D\u5927 = size;
8377
+ break;
8378
+ }
8379
+ }
8380
+ if (\u540D\u524D\u5927 === 0) \u540D\u524D\u5927 = \u9AD8\u3055 / (ASCENT2 + DESCENT2);
8381
+ const \u5C0F\u3055\u3044\u5B57 = Math.max(1, Math.round(\u540D\u524D\u5927 * SUB_RATIO));
8382
+ const \u5F35\u308A\u51FA\u3057 = (\u8AAC\u660E\u3042\u308A2 ? \u5C0F\u3055\u3044\u5B57 : \u540D\u524D\u5927) * DESCENT2;
8383
+ 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);
8384
+ const \u4E0B\u4F59\u767D = Math.min(Math.max(BOTTOM_PAD, \u5F35\u308A\u51FA\u3057), \u5F35\u308A\u51FA\u3057 + \u4F59\u308A);
8385
+ return \u7A4D\u3080(\u540D\u524D\u5927, \u8AAC\u660E\u3042\u308A2, \u80A9\u66F8\u3042\u308A2, \u4E0B\u4F59\u767D);
8386
+ };
8387
+ const \u7D75\u306E\u5E2F = (\u7D442) => Math.max(0, \u7D442.\u4E0A\u7AEF - ART_GAP - \u5929);
8388
+ let \u7D44 = \u7D44\u3080(\u8AAC\u660E\u3042\u308A, \u80A9\u66F8\u3042\u308A);
8389
+ if (\u7D75\u306E\u5E2F(\u7D44) < ART_MIN_BAND && \u80A9\u66F8\u3042\u308A) {
8390
+ \u80A9\u66F8\u3042\u308A = false;
8391
+ \u843D\u3068\u3057\u305F.push("\u80A9\u66F8");
8392
+ \u7D44 = \u7D44\u3080(\u8AAC\u660E\u3042\u308A, \u80A9\u66F8\u3042\u308A);
8393
+ }
8394
+ if (\u7D75\u306E\u5E2F(\u7D44) < ART_MIN_BAND && \u8AAC\u660E\u3042\u308A) {
8395
+ \u8AAC\u660E\u3042\u308A = false;
8396
+ \u843D\u3068\u3057\u305F.push("\u8AAC\u660E");
8397
+ \u7D44 = \u7D44\u3080(\u8AAC\u660E\u3042\u308A, \u80A9\u66F8\u3042\u308A);
8048
8398
  }
8049
- if (\u540D\u524D\u5927 === 0) \u540D\u524D\u5927 = \u9AD8\u3055 / (ASCENT2 + DESCENT2);
8050
- const \u5C0F\u3055\u3044\u5B57 = Math.max(1, Math.round(\u540D\u524D\u5927 * SUB_RATIO));
8051
- const \u5F35\u308A\u51FA\u3057 = (\u8AAC\u660E\u3042\u308A ? \u5C0F\u3055\u3044\u5B57 : \u540D\u524D\u5927) * DESCENT2;
8052
- const \u4F59\u308A = Math.max(0, \u7A4D\u3080(\u540D\u524D\u5927, \u8AAC\u660E\u3042\u308A, \u80A9\u66F8\u3042\u308A, \u5F35\u308A\u51FA\u3057).\u4E0A\u7AEF - \u5929);
8053
- const \u4E0B\u4F59\u767D = Math.min(Math.max(BOTTOM_PAD, \u5F35\u308A\u51FA\u3057), \u5F35\u308A\u51FA\u3057 + \u4F59\u308A);
8054
- const \u7D44 = \u7A4D\u3080(\u540D\u524D\u5927, \u8AAC\u660E\u3042\u308A, \u80A9\u66F8\u3042\u308A, \u4E0B\u4F59\u767D);
8055
8399
  const \u4E00\u756A\u5C0F\u3055\u3044\u884C = Math.min(
8056
8400
  ...[\u7D44.\u540D\u524D, \u7D44.\u8AAC\u660E, \u7D44.\u80A9\u66F8].filter((l) => l !== null).map((l) => l.size)
8057
8401
  );
@@ -8060,7 +8404,8 @@ function labelPlan(node) {
8060
8404
  \u8AAC\u660E: \u7D44.\u8AAC\u660E,
8061
8405
  \u80A9\u66F8: \u7D44.\u80A9\u66F8,
8062
8406
  top: \u7D44.\u4E0A\u7AEF,
8063
- \u8AAD\u3081\u308B: \u4E00\u756A\u5C0F\u3055\u3044\u884C >= TITLE_MIN
8407
+ \u8AAD\u3081\u308B: \u4E00\u756A\u5C0F\u3055\u3044\u884C >= TITLE_MIN,
8408
+ \u843D\u3068\u3057\u305F
8064
8409
  };
8065
8410
  }
8066
8411
  function shapeRegion(node) {
@@ -10328,6 +10673,15 @@ function ShapeSmartContractNode({ node, active }) {
10328
10673
  bottomLabel2(node, r.cx, r.label)
10329
10674
  ] });
10330
10675
  }
10676
+ var \u5B57\u306E\u5E45 = (text, size) => Math.max(displayWidth(text) * size * MONO_ADVANCE_RATIO, textWidth(text, size));
10677
+ var BLOCK_DEFAULT_FIELDS = [
10678
+ { label: "hash", value: "0xaf31c9d2..." },
10679
+ { label: "prev", value: "0x7b3f92c1..." },
10680
+ { label: "merkle", value: "0xc1892f4a..." },
10681
+ { label: "nonce", value: "82,914" },
10682
+ { label: "tx", value: "142 tx" }
10683
+ ];
10684
+ var BLOCK_DEFAULT_TITLE = "Block #421,873";
10331
10685
  function ShapeBlockchainBlockNode({ node, active }) {
10332
10686
  const frame = commonFrame4(active);
10333
10687
  const r = shapeRegion(node);
@@ -10337,21 +10691,22 @@ function ShapeBlockchainBlockNode({ node, active }) {
10337
10691
  const cy = r.cy;
10338
10692
  const x = cx - bw / 2;
10339
10693
  const y = cy - bh / 2;
10694
+ const \u6E21\u3055\u308C\u305F = (v) => (v ?? "").trim();
10695
+ const \u898B\u51FA\u3057 = \u6E21\u3055\u308C\u305F(node.title) || BLOCK_DEFAULT_TITLE;
10696
+ const fields = BLOCK_DEFAULT_FIELDS.map((f) => ({
10697
+ label: f.label,
10698
+ value: f.label === "hash" ? \u6E21\u3055\u308C\u305F(node.subtitle) || f.value : f.value
10699
+ }));
10700
+ const \u898B\u51FA\u3057\u306E\u5E45 = bw - 14 - 30;
10340
10701
  const art = /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
10341
10702
  /* @__PURE__ */ jsxRuntime.jsx("rect", { x: x + 6, y: y + 6, width: bw, height: bh, rx: 8, fill: frame.stroke, opacity: 0.2, stroke: "none" }),
10342
10703
  /* @__PURE__ */ jsxRuntime.jsx("rect", { "data-cdl-role": "node-body", x, y, width: bw, height: bh, rx: 8, ...frame }),
10343
10704
  /* @__PURE__ */ jsxRuntime.jsx("path", { d: `M ${x} ${y + 32} L ${x} ${y + 8} Q ${x} ${y}, ${x + 8} ${y} L ${x + bw - 8} ${y} Q ${x + bw} ${y}, ${x + bw} ${y + 8} L ${x + bw} ${y + 32} Z`, fill: "var(--cdl-tone-accent, #2d6a8f)", fillOpacity: 0.9, stroke: "none" }),
10344
- /* @__PURE__ */ jsxRuntime.jsx("text", { x: x + 14, y: y + 22, fontSize: 13, fontWeight: 700, fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fill: "#ffffff", children: "Block #421,873" }),
10705
+ /* @__PURE__ */ jsxRuntime.jsx("text", { "data-cdl-role": "blockchain-block-title", x: x + 14, y: y + 22, fontSize: 13, fontWeight: 700, fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fill: "#ffffff", children: \u5E45\u3067\u5207\u308B(\u898B\u51FA\u3057, 13, \u898B\u51FA\u3057\u306E\u5E45, \u5B57\u306E\u5E45) }),
10345
10706
  /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: x + bw - 16, cy: y + 16, r: 5.5, fill: "#7cd88f", stroke: "#ffffff", strokeWidth: 1.5 }),
10346
- [
10347
- { label: "hash", value: "0xaf31c9d2..." },
10348
- { label: "prev", value: "0x7b3f92c1..." },
10349
- { label: "merkle", value: "0xc1892f4a..." },
10350
- { label: "nonce", value: "82,914" },
10351
- { label: "tx", value: "142 tx" }
10352
- ].map((f, i) => /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
10707
+ fields.map((f, i) => /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
10353
10708
  /* @__PURE__ */ jsxRuntime.jsx("text", { x: x + 14, y: y + 54 + i * 22, fontSize: 10.5, fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fill: "var(--cdl-text-dim, #5a6270)", children: f.label }),
10354
- /* @__PURE__ */ jsxRuntime.jsx("text", { x: x + bw - 14, y: y + 54 + i * 22, fontSize: 11, fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontWeight: 600, textAnchor: "end", fill: "var(--cdl-text, #1a1f2a)", children: f.value }),
10709
+ /* @__PURE__ */ jsxRuntime.jsx("text", { "data-cdl-role": `blockchain-block-${f.label}`, x: x + bw - 14, y: y + 54 + i * 22, fontSize: 11, fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontWeight: 600, textAnchor: "end", fill: "var(--cdl-text, #1a1f2a)", children: \u5E45\u3067\u5207\u308B(f.value, 11, bw - 28 - \u5B57\u306E\u5E45(f.label, 10.5) - 8, \u5B57\u306E\u5E45) }),
10355
10710
  /* @__PURE__ */ jsxRuntime.jsx("line", { x1: x + 14, y1: y + 60 + i * 22, x2: x + bw - 14, y2: y + 60 + i * 22, stroke: "var(--cdl-divider, #d4d4d8)", strokeWidth: 0.5, opacity: 0.5 })
10356
10711
  ] }, i)),
10357
10712
  /* @__PURE__ */ jsxRuntime.jsxs("g", { transform: `translate(${cx - 32}, ${y + bh - 26})`, children: [
@@ -12047,6 +12402,29 @@ function CdlNodeView({
12047
12402
  }
12048
12403
  );
12049
12404
  }
12405
+
12406
+ // src/layout/lifeline.ts
12407
+ function lifelineStartYs(nodes) {
12408
+ const topmost = /* @__PURE__ */ new Map();
12409
+ for (const n of nodes) {
12410
+ const cur = topmost.get(n.lane);
12411
+ if (cur === void 0 || n.cy < cur.cy) topmost.set(n.lane, n);
12412
+ }
12413
+ const starts = /* @__PURE__ */ new Map();
12414
+ for (const [laneId, n] of topmost) starts.set(laneId, n.cy + n.h / 2);
12415
+ return starts;
12416
+ }
12417
+ function uniformLifelineEndY(lanes, nodes, starts = lifelineStartYs(nodes)) {
12418
+ const lifelineLanes = lanes.filter((l) => l.lifeline);
12419
+ let deepestStart = Number.NEGATIVE_INFINITY;
12420
+ for (const lane of lifelineLanes) {
12421
+ const start = starts.get(lane.id);
12422
+ if (start !== void 0) deepestStart = Math.max(deepestStart, start);
12423
+ }
12424
+ const footerTops = lifelineFooterNodes(lanes, nodes).map((f) => footerShapeDrawTop(f)).filter((top) => top > deepestStart);
12425
+ if (footerTops.length > 0) return Math.min(...footerTops);
12426
+ return lifelineLanes[0] ? lifelineLanes[0].y + lifelineLanes[0].height : 0;
12427
+ }
12050
12428
  function CdlStage({
12051
12429
  laid,
12052
12430
  currentPhase,
@@ -12057,15 +12435,13 @@ function CdlStage({
12057
12435
  svgRef
12058
12436
  }) {
12059
12437
  const activeSet = new Set(currentPhase?.activate ?? []);
12060
- const lifelineLanes = laid.lanes.filter((l) => l.lifeline);
12061
- const { footerTops, footerDropById } = react.useMemo(() => {
12062
- const footers = lifelineFooterNodes(laid.lanes, laid.nodes);
12438
+ const { lifelineStarts, uniformFooterTop } = react.useMemo(() => {
12439
+ const starts = lifelineStartYs(laid.nodes);
12063
12440
  return {
12064
- footerTops: footers.map((f) => f.cy - f.h / 2),
12065
- footerDropById: footerShapeDropsOf(footers)
12441
+ lifelineStarts: starts,
12442
+ uniformFooterTop: uniformLifelineEndY(laid.lanes, laid.nodes, starts)
12066
12443
  };
12067
12444
  }, [laid]);
12068
- const uniformFooterTop = footerTops.length > 0 ? Math.min(...footerTops) : lifelineLanes[0] ? lifelineLanes[0].y + lifelineLanes[0].height : 0;
12069
12445
  const diagramScale = laid.diagramScale ?? 1;
12070
12446
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: compact ? "px-3 py-3" : "px-6 py-6", style: compact ? void 0 : { minHeight: 460 }, children: [
12071
12447
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -12152,11 +12528,8 @@ function CdlStage({
12152
12528
  }
12153
12529
  ),
12154
12530
  lane.lifeline && (() => {
12155
- const laneNodes = laid.nodes.filter((n) => n.lane === lane.id);
12156
- if (laneNodes.length === 0) return null;
12157
- const sortedByCy = laneNodes.slice().sort((a, b) => a.cy - b.cy);
12158
- const header = sortedByCy[0];
12159
- const y1 = header.cy + header.h / 2;
12531
+ const y1 = lifelineStarts.get(lane.id);
12532
+ if (y1 === void 0) return null;
12160
12533
  const y2 = uniformFooterTop;
12161
12534
  if (y2 <= y1) return null;
12162
12535
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -12203,8 +12576,7 @@ function CdlStage({
12203
12576
  currentPhaseId: currentPhase?.id
12204
12577
  }
12205
12578
  );
12206
- const dy = footerDropById.get(node.id);
12207
- return dy === void 0 ? /* @__PURE__ */ jsxRuntime.jsx("g", { children: view }, node.id) : /* @__PURE__ */ jsxRuntime.jsx("g", { transform: `translate(0, ${dy})`, children: view }, node.id);
12579
+ return /* @__PURE__ */ jsxRuntime.jsx("g", { children: view }, node.id);
12208
12580
  }),
12209
12581
  laid.edges.filter((edge) => Boolean(edge.dThrough)).map((edge) => /* @__PURE__ */ jsxRuntime.jsx(CdlEdgeGlowView, { edge, active: activeSet.has(edge.id) }, `glow-${edge.id}`)),
12210
12582
  laid.edges.map((edge) => /* @__PURE__ */ jsxRuntime.jsx(
@@ -21086,6 +21458,7 @@ function emptyCounts() {
21086
21458
  "validate-performance-budget": 0,
21087
21459
  "node-inside-viewbox": 0,
21088
21460
  "node-inside-lane": 0,
21461
+ "shape-label-dropped": 0,
21089
21462
  "edge-inside-viewbox": 0,
21090
21463
  "lane-label-inside-viewbox": 0,
21091
21464
  "lane-label-overlap": 0,
@@ -21739,6 +22112,7 @@ function runAxes(laid, diag, violations, counts, push, profile, skippedAxes) {
21739
22112
  for (const n of laid.nodes) {
21740
22113
  if (n.id.endsWith("-spacer")) continue;
21741
22114
  if (!rendersTitleText(n.kind)) continue;
22115
+ if (fitsOwnTitleWidth(n.kind)) continue;
21742
22116
  if (!isRenderedNode(n)) continue;
21743
22117
  const titleTrimmed = n.title?.trim() ?? "";
21744
22118
  if (!titleTrimmed) continue;
@@ -21995,6 +22369,15 @@ function runAxes(laid, diag, violations, counts, push, profile, skippedAxes) {
21995
22369
  );
21996
22370
  }
21997
22371
  }
22372
+ for (const n of laid.nodes) {
22373
+ const \u843D\u3068\u3057\u305F = labelPlan(n).\u843D\u3068\u3057\u305F.filter((line) => droppedLineIsHidden(n.kind, line));
22374
+ if (\u843D\u3068\u3057\u305F.length === 0) continue;
22375
+ push(
22376
+ "shape-label-dropped",
22377
+ `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`,
22378
+ "warn"
22379
+ );
22380
+ }
21998
22381
  for (const e of laid.edges) {
21999
22382
  const segs = flattenPathSegments(extractPathSegments(e.d));
22000
22383
  if (segs.length === 0) continue;
@@ -23184,14 +23567,7 @@ function CdlDiagramView({ diagram, laid: laidProp, hideHeader = false, hideMiniP
23184
23567
  );
23185
23568
  const stateValues = react.useMemo(() => {
23186
23569
  if (!isInteractive) return baseStateValues;
23187
- const merged = { ...baseStateValues };
23188
- for (const key of Object.keys(interactive.stateOverrides)) {
23189
- const v = interactive.stateOverrides[key];
23190
- if (typeof v === "number" || typeof v === "string" || typeof v === "boolean") {
23191
- merged[key] = String(v);
23192
- }
23193
- }
23194
- return merged;
23570
+ return mergeStateValues(baseStateValues, interactive.stateOverrides);
23195
23571
  }, [isInteractive, baseStateValues, interactive.stateOverrides]);
23196
23572
  return /* @__PURE__ */ jsxRuntime.jsxs(
23197
23573
  "div",