@cardenelabs/cdl 0.15.0 → 0.17.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.
package/dist/react.cjs CHANGED
@@ -561,6 +561,9 @@ function hasRenderedLabel(e) {
561
561
  return Boolean(e.label?.trim() || e.sub?.trim());
562
562
  }
563
563
  var KINDS_WITHOUT_TITLE_TEXT = /* @__PURE__ */ new Set([
564
+ // 時系列のやり取り (#578)。 図を丸ごと描く種別で、描くのは面々の名前と言づての語。
565
+ // 箱の `title` は使わない
566
+ "sequence-board",
564
567
  // 形だけを描く (DynShapeNode)
565
568
  "dyn-rect",
566
569
  "dyn-circle",
@@ -649,6 +652,7 @@ var GLYPH_CAP_RATIO = 0.72;
649
652
  var ROW_FONT_STORAGE = 26;
650
653
  var ROW_FONT_GENERIC = 21;
651
654
  var STORAGE_TITLE_BASELINE = 78;
655
+ var SUBTITLE_ROW_SHIFT = 46;
652
656
  var STORAGE_ROW_DIVIDER_Y = Math.round(
653
657
  (STORAGE_TITLE_BASELINE + (ROW_TOP_STORAGE - ROW_FONT_STORAGE * GLYPH_CAP_RATIO)) / 2
654
658
  );
@@ -2079,7 +2083,10 @@ var TOKENS = {
2079
2083
  // 印は箱いっぱいに描く (線が箱の縁で止まるため) ので、この値が丸の直径になる。
2080
2084
  // 箱としての最小 (80x40) より大きく、名前を持つ箱 (card 320x150) よりは明らかに小さい
2081
2085
  "mark-start": { w: 96, h: 96 },
2082
- "mark-end": { w: 96, h: 96 }
2086
+ "mark-end": { w: 96, h: 96 },
2087
+ // 時系列のやり取り (#578)。 実寸は中身から決まる (`sequenceBoardMetrics`) ので、
2088
+ // ここは書かなかった時の受け皿
2089
+ "sequence-board": { w: 960, h: 640 }
2083
2090
  },
2084
2091
  // row gap (stack 間の vertical gap、 lane 内 / 跨ぎ共通)
2085
2092
  // edge label が node の上 36 px に配置されるため、 boxH 68 + 36 + 余裕 = 100 必要。
@@ -3742,8 +3749,12 @@ function requiredRowsWidth(rows, kind) {
3742
3749
  function autoStorageHeight(n, defaultH) {
3743
3750
  if (n.kind !== "storage") return n.h ?? defaultH;
3744
3751
  if (n.h != null) return n.h;
3745
- if (!n.rows || n.rows.length === 0) return 150;
3746
- return Math.max(defaultH, requiredRowsHeight("storage", n.rows.length) ?? defaultH);
3752
+ const \u5370\u3042\u308A = n.rowMarks !== void 0;
3753
+ const \u547C\u3073\u540D\u306E\u5206 = \u5370\u3042\u308A && n.subtitle ? SUBTITLE_ROW_SHIFT : 0;
3754
+ if (!n.rows || n.rows.length === 0) return 150 + \u547C\u3073\u540D\u306E\u5206;
3755
+ const \u5FC5\u8981 = requiredRowsHeight("storage", n.rows.length) ?? defaultH;
3756
+ if (\u5370\u3042\u308A) return \u5FC5\u8981 + \u547C\u3073\u540D\u306E\u5206;
3757
+ return Math.max(defaultH, \u5FC5\u8981);
3747
3758
  }
3748
3759
  function autoRowsWidth(n, defaultW) {
3749
3760
  if (n.w != null) return n.w;
@@ -3831,8 +3842,11 @@ function layoutNodes(diag, lanes) {
3831
3842
  continue;
3832
3843
  }
3833
3844
  const cx = lane.x + lane.width / 2;
3834
- const cy = rowCy.get(n.stack);
3835
- const h = n.h ?? rowH.get(n.stack);
3845
+ const \u6BB5\u306E\u82AF = rowCy.get(n.stack);
3846
+ const \u6BB5\u306E\u9AD8\u3055 = rowH.get(n.stack);
3847
+ const \u81EA\u524D\u306E\u9AD8\u3055 = n.rowMarks !== void 0;
3848
+ const h = n.h ?? (\u81EA\u524D\u306E\u9AD8\u3055 ? autoStorageHeight(n, size.h) : \u6BB5\u306E\u9AD8\u3055);
3849
+ const cy = \u81EA\u524D\u306E\u9AD8\u3055 ? \u6BB5\u306E\u82AF - \u6BB5\u306E\u9AD8\u3055 / 2 + h / 2 : \u6BB5\u306E\u82AF;
3836
3850
  const w = autoRowsWidth(n, size.w);
3837
3851
  out.push({
3838
3852
  ...n,
@@ -4161,10 +4175,24 @@ var NODE_KINDS = [
4161
4175
  // 流れの始まりと終わりの印 2 種 (#560) ... 塗った丸と輪で囲んだ丸。
4162
4176
  // 状態遷移図が使う。 印そのものが意味を持つので文字は描かない
4163
4177
  "mark-start",
4164
- "mark-end"
4178
+ "mark-end",
4179
+ // 時系列のやり取り (#578) ... 1 つの箱が図を丸ごと描く。 縦が時間で、参加者は箱を持たない
4180
+ "sequence-board"
4165
4181
  ];
4166
4182
  var EDGE_HEAD_DEFAULT = "triangle";
4167
- var EDGE_HEADS = ["triangle", "diamond", "open", "crow"];
4183
+ var EDGE_HEADS = [
4184
+ "none",
4185
+ "triangle",
4186
+ "diamond",
4187
+ "open",
4188
+ "crow",
4189
+ "one",
4190
+ "zero-one",
4191
+ "many",
4192
+ "zero-many"
4193
+ ];
4194
+ var EDGE_HEAD_FILL_DEFAULT = "solid";
4195
+ var EDGE_HEAD_FILLS = ["solid", "hollow"];
4168
4196
 
4169
4197
  // src/formula/evaluator.ts
4170
4198
  function evaluate(ast, ctxOrResolver) {
@@ -5003,7 +5031,7 @@ function pathSubpath(d, progress) {
5003
5031
  }
5004
5032
 
5005
5033
  // src/render/template-fields.ts
5006
- var TEXT_FIELDS = ["title", "eyebrow", "subtitle", "value", "wBind", "hBind", "visibleIf"];
5034
+ var TEXT_FIELDS = ["title", "eyebrow", "subtitle", "value", "wBind", "hBind", "visibleIf", "sequenceStep"];
5007
5035
  var LIST_FIELDS = ["rows"];
5008
5036
  var NUMERIC_FIELDS = ["opacity", "renderOffsetX", "renderOffsetY"];
5009
5037
  var SHAPE_TEMPLATE_FIELDS = ["source", "radius", "fillProgress", "angle", "level", "rotation", "fill"];
@@ -5459,14 +5487,42 @@ function StateDiffCard({
5459
5487
 
5460
5488
  // src/render/edge-head.ts
5461
5489
  var EDGE_HEAD_SHAPE = {
5462
- triangle: { d: "M 0 0 L 10 5 L 0 10 z", refX: 9, filled: true },
5463
- diamond: { d: "M 0 5 L 5 0 L 10 5 L 5 10 z", refX: 9, filled: true },
5464
- open: { d: "M 1 0 L 9 5 L 1 10", refX: 9, filled: false },
5465
- crow: { d: "M 10 0 L 0 5 M 10 5 L 0 5 M 10 10 L 0 5", refX: 10, filled: false }
5490
+ // 何も描かない端。 marker 自体を出さないので `d` は読まれない
5491
+ none: { d: "", w: 10, h: 10, refX: 0, filled: false },
5492
+ triangle: { d: "M 0 0 L 10 5 L 0 10 z", w: 10, h: 10, refX: 9, filled: true },
5493
+ diamond: { d: "M 0 5 L 5 0 L 10 5 L 5 10 z", w: 10, h: 10, refX: 9, filled: true },
5494
+ open: { d: "M 1 0 L 9 5 L 1 10", w: 10, h: 10, refX: 9, filled: false },
5495
+ crow: { d: "M 10 0 L 0 5 M 10 5 L 0 5 M 10 10 L 0 5", w: 10, h: 10, refX: 10, filled: false },
5496
+ // ER の端。 **箱に近い側 (x が大きい側) が個数、その外側が任意か** (#578)。
5497
+ //
5498
+ // 枠は 24 × 10 で、線の先端に合わせるのは x=24 = 箱の縁。 marker は経路の向きに回るので、
5499
+ // 行き先側でも出どころ側でも「箱に近い側」 が同じ端になる。
5500
+ //
5501
+ // 三又の真ん中の枝は **関係の線そのもの** が兼ねる。 別に引くと同じ所に 2 本重なって太く見える
5502
+ one: { d: "M 16 0 L 16 10 M 4 0 L 4 10", w: 24, h: 10, refX: 24, scale: 1.8, filled: false },
5503
+ "zero-one": { d: "M 16 0 L 16 10 M 7 5 a 3 3 0 1 0 -6 0 a 3 3 0 1 0 6 0", w: 24, h: 10, refX: 24, scale: 1.8, filled: false },
5504
+ many: { d: "M 24 0 L 13 5 L 24 10 M 4 0 L 4 10", w: 24, h: 10, refX: 24, scale: 1.8, filled: false },
5505
+ "zero-many": {
5506
+ d: "M 24 0 L 13 5 L 24 10 M 7 5 a 3 3 0 1 0 -6 0 a 3 3 0 1 0 6 0",
5507
+ w: 24,
5508
+ h: 10,
5509
+ refX: 24,
5510
+ scale: 1.8,
5511
+ filled: false
5512
+ }
5466
5513
  };
5467
- function edgeHeadMarkerId(tone, head, small) {
5514
+ function edgeHeadMarkerId(tone, head, small, fill = EDGE_HEAD_FILL_DEFAULT) {
5468
5515
  const \u5C0F = small ? "-sm" : "";
5469
- return head === EDGE_HEAD_DEFAULT ? `cdl-arrow-${tone}${\u5C0F}` : `cdl-arrow-${tone}-${head}${\u5C0F}`;
5516
+ const \u5857 = fill === EDGE_HEAD_FILL_DEFAULT || !EDGE_HEAD_SHAPE[head].filled ? "" : `-${fill}`;
5517
+ return head === EDGE_HEAD_DEFAULT && \u5857 === "" ? `cdl-arrow-${tone}${\u5C0F}` : `cdl-arrow-${tone}-${head}${\u5857}${\u5C0F}`;
5518
+ }
5519
+ function edgeHeadPaint(head, fill, color) {
5520
+ const \u5F62 = EDGE_HEAD_SHAPE[head];
5521
+ if (!\u5F62.filled) return { fill: "none", stroke: color, strokeWidth: 1.6 };
5522
+ if (fill === "hollow") {
5523
+ return { fill: "var(--cdl-node-fill, #ffffff)", stroke: color, strokeWidth: 1.4 };
5524
+ }
5525
+ return { fill: color, stroke: "none" };
5470
5526
  }
5471
5527
 
5472
5528
  // src/render/tone.ts
@@ -5520,7 +5576,13 @@ function CdlEdgeView({
5520
5576
  stateValues
5521
5577
  }) {
5522
5578
  const color = toneColor(edge.tone);
5523
- const marker = hasTone(edge.tone) ? edgeHeadMarkerId(edge.tone, edge.head ?? EDGE_HEAD_DEFAULT, !active) : "cdl-arrow-muted";
5579
+ const \u7AEF\u306E\u540D\u524D = (h, f) => {
5580
+ const \u5F62 = h ?? EDGE_HEAD_DEFAULT;
5581
+ if (\u5F62 === "none") return void 0;
5582
+ return hasTone(edge.tone) ? edgeHeadMarkerId(edge.tone, \u5F62, !active, f ?? EDGE_HEAD_FILL_DEFAULT) : "cdl-arrow-muted";
5583
+ };
5584
+ const marker = \u7AEF\u306E\u540D\u524D(edge.head, edge.headFill);
5585
+ const tailMarker = edge.tailHead === void 0 ? void 0 : \u7AEF\u306E\u540D\u524D(edge.tailHead, edge.tailHeadFill);
5524
5586
  const style = edge.style ?? "solid";
5525
5587
  const isDotted = style === "dotted-flow";
5526
5588
  const isPassThrough = isDotted && Boolean(edge.dThrough);
@@ -5568,6 +5630,8 @@ function CdlEdgeView({
5568
5630
  progress,
5569
5631
  color,
5570
5632
  marker,
5633
+ tailMarker,
5634
+ dashed: style === "dashed",
5571
5635
  stateValues
5572
5636
  }
5573
5637
  ),
@@ -5593,6 +5657,8 @@ function SolidEdgePath({
5593
5657
  progress,
5594
5658
  color,
5595
5659
  marker,
5660
+ tailMarker,
5661
+ dashed,
5596
5662
  stateValues
5597
5663
  }) {
5598
5664
  const visibleD = active ? pathSubpath(edge.d, progress) : edge.d;
@@ -5600,7 +5666,7 @@ function SolidEdgePath({
5600
5666
  const boundWidth = edge.widthBind && stateValues ? resolveNumericAttr(edge.widthBind, stateValues, defaultWidth) : defaultWidth;
5601
5667
  const boundStroke = edge.strokeBind && stateValues ? interpolate(edge.strokeBind, stateValues) : color;
5602
5668
  const boundDashOffset = edge.dashOffsetBind && stateValues ? resolveNumericAttr(edge.dashOffsetBind, stateValues, 0) : void 0;
5603
- const dashArray = active ? boundDashOffset !== void 0 ? "8 6" : void 0 : "4 5";
5669
+ const dashArray = dashed ? "10 8" : active ? boundDashOffset !== void 0 ? "8 6" : void 0 : "4 5";
5604
5670
  return /* @__PURE__ */ jsxRuntime.jsx(
5605
5671
  "path",
5606
5672
  {
@@ -5613,7 +5679,8 @@ function SolidEdgePath({
5613
5679
  strokeDasharray: dashArray,
5614
5680
  strokeDashoffset: boundDashOffset,
5615
5681
  strokeLinecap: "round",
5616
- markerEnd: `url(#${marker})`,
5682
+ markerEnd: marker === void 0 ? void 0 : `url(#${marker})`,
5683
+ markerStart: tailMarker === void 0 ? void 0 : `url(#${tailMarker})`,
5617
5684
  style: {
5618
5685
  transition: "stroke 280ms ease, stroke-opacity 280ms ease, stroke-width 280ms ease"
5619
5686
  }
@@ -5877,7 +5944,8 @@ function CardNode({ node, active }) {
5877
5944
  return /* @__PURE__ */ jsxRuntime.jsx("g", { transform: `translate(${x} ${y})` });
5878
5945
  }
5879
5946
  const compact = isCompactBox(node.h);
5880
- const titleY = compact ? node.h / 2 + 8 : 86;
5947
+ const \u5C0F\u578B\u306B\u547C\u3073\u540D = compact && Boolean(node.subtitle);
5948
+ const titleY = compact ? \u5C0F\u578B\u306B\u547C\u3073\u540D ? node.h / 2 - 2 : node.h / 2 + 8 : 86;
5881
5949
  const titleX = compact ? node.w / 2 : 26;
5882
5950
  const paddingX = compact ? 16 : 52;
5883
5951
  const availableW = node.w - paddingX;
@@ -5914,9 +5982,259 @@ function CardNode({ node, active }) {
5914
5982
  children: node.title
5915
5983
  }
5916
5984
  ),
5917
- !compact && node.subtitle && /* @__PURE__ */ jsxRuntime.jsx("text", { x: 26, y: 124, fontSize: subtitleSize, fill: "var(--cdl-text-dim, #5a6270)", children: node.subtitle })
5985
+ !compact && node.subtitle && /* @__PURE__ */ jsxRuntime.jsx("text", { x: 26, y: 124, fontSize: subtitleSize, fill: "var(--cdl-text-dim, #5a6270)", children: node.subtitle }),
5986
+ \u5C0F\u578B\u306B\u547C\u3073\u540D && /* @__PURE__ */ jsxRuntime.jsx(
5987
+ "text",
5988
+ {
5989
+ "data-cdl-role": "node-subtitle",
5990
+ x: node.w / 2,
5991
+ y: node.h / 2 + 24,
5992
+ fontSize: Math.min(subtitleSize, 18),
5993
+ textAnchor: "middle",
5994
+ fill: "var(--cdl-text-dim, #5a6270)",
5995
+ children: node.subtitle
5996
+ }
5997
+ )
5998
+ ] });
5999
+ }
6000
+
6001
+ // src/kinds/sequence-board-metrics.ts
6002
+ var SEQ_EDGE = 16;
6003
+ var SEQ_ROW = 64;
6004
+ var SEQ_GUTTER_PAD = 40;
6005
+ var SEQ_GUTTER_GAP = 40;
6006
+ var SEQ_HEAD = 96;
6007
+ var SEQ_ROW_PAD = 56;
6008
+ var SEQ_COL_PAD = 80;
6009
+ var SEQ_BAND_W = 52;
6010
+ var SEQ_BAND_PAD = 24;
6011
+ var SEQ_NAME = 24;
6012
+ var SEQ_SUB = 20;
6013
+ var SEQ_LABEL = 26;
6014
+ var SEQ_DOT = 7;
6015
+ var SEQ_SELF_W = 52;
6016
+ var SEQ_COL_MIN = 200;
6017
+ var g4 = (v) => Math.round(v / 4) * 4;
6018
+ function sequenceBoardMetrics(data) {
6019
+ const \u540D\u5E45 = data.actors.map(
6020
+ (a) => Math.max(textWidth(a.name, SEQ_NAME), a.subtitle ? textWidth(a.subtitle, SEQ_SUB) : 0)
6021
+ );
6022
+ const colW = g4(Math.max(SEQ_COL_MIN, Math.max(0, ...\u540D\u5E45) + SEQ_COL_PAD));
6023
+ const \u8A9E\u5E45 = Math.max(0, ...data.messages.map((m) => textWidth(m.label, SEQ_LABEL)));
6024
+ const gutter = g4(SEQ_EDGE + SEQ_GUTTER_PAD + \u8A9E\u5E45 + SEQ_GUTTER_GAP);
6025
+ const headY = SEQ_EDGE + SEQ_HEAD;
6026
+ const rowY = (i) => headY + SEQ_ROW_PAD + i * SEQ_ROW;
6027
+ const \u6700\u7D42 = data.messages.length > 0 ? rowY(data.messages.length - 1) : headY;
6028
+ const bottom = \u6700\u7D42 + SEQ_ROW_PAD;
6029
+ const cx = (i) => gutter + colW / 2 + i * colW;
6030
+ const w = g4(cx(Math.max(0, data.actors.length - 1)) + colW / 2 + SEQ_EDGE);
6031
+ const h = g4(bottom + SEQ_EDGE);
6032
+ return { w, h, gutter, colW, headY, cx, rowY, bottom };
6033
+ }
6034
+ function SequenceBoardNode({
6035
+ node,
6036
+ stateValues
6037
+ }) {
6038
+ const data = node.sequenceData;
6039
+ if (!data) return /* @__PURE__ */ jsxRuntime.jsx("g", { "data-cdl-role": "sequence-board" });
6040
+ const m = sequenceBoardMetrics(data);
6041
+ const x = node.cx - node.w / 2;
6042
+ const y = node.cy - node.h / 2;
6043
+ const \u73FE\u5728 = (() => {
6044
+ if (!node.sequenceStep) return null;
6045
+ const v = Number.parseInt(interpolate(node.sequenceStep, stateValues ?? {}).trim(), 10);
6046
+ return Number.isFinite(v) ? v : null;
6047
+ })();
6048
+ const \u6731 = "var(--cdl-tone-accent, #2d6a8f)";
6049
+ const \u58A8 = "var(--cdl-text-2, #4a5162)";
6050
+ const \u898B\u305F\u76EE = (i) => {
6051
+ if (\u73FE\u5728 === null) return { opacity: 1, color: \u6731 };
6052
+ if (i > \u73FE\u5728) return { opacity: 0, color: \u6731 };
6053
+ return i === \u73FE\u5728 ? { opacity: 1, color: \u6731 } : { opacity: 1, color: \u58A8 };
6054
+ };
6055
+ return /* @__PURE__ */ jsxRuntime.jsxs("g", { "data-cdl-role": "sequence-board", transform: `translate(${x} ${y})`, children: [
6056
+ data.bands.map((b, i) => {
6057
+ if (\u73FE\u5728 !== null && b.from > \u73FE\u5728) return null;
6058
+ const \u4E0B = \u73FE\u5728 === null ? b.to : Math.min(b.to, \u73FE\u5728);
6059
+ return /* @__PURE__ */ jsxRuntime.jsx(
6060
+ "rect",
6061
+ {
6062
+ "data-cdl-role": "sequence-band",
6063
+ x: m.cx(b.actor) - SEQ_BAND_W / 2,
6064
+ y: m.rowY(b.from) - SEQ_BAND_PAD / 2,
6065
+ width: SEQ_BAND_W,
6066
+ height: m.rowY(\u4E0B) - m.rowY(b.from) + SEQ_BAND_PAD,
6067
+ rx: 4,
6068
+ fill: \u6731,
6069
+ fillOpacity: 0.12
6070
+ },
6071
+ `band-${i}`
6072
+ );
6073
+ }),
6074
+ data.actors.map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(
6075
+ "line",
6076
+ {
6077
+ "data-cdl-role": "sequence-thread",
6078
+ x1: m.cx(i),
6079
+ y1: m.headY,
6080
+ x2: m.cx(i),
6081
+ y2: m.bottom,
6082
+ stroke: "var(--cdl-divider, #d4d4d8)",
6083
+ strokeWidth: 3
6084
+ },
6085
+ `thread-${i}`
6086
+ )),
6087
+ data.actors.map((a, i) => /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
6088
+ /* @__PURE__ */ jsxRuntime.jsx(
6089
+ "text",
6090
+ {
6091
+ "data-cdl-role": "sequence-actor",
6092
+ x: m.cx(i),
6093
+ y: SEQ_EDGE + 44,
6094
+ fontSize: SEQ_NAME,
6095
+ fontWeight: 700,
6096
+ textAnchor: "middle",
6097
+ fill: "var(--cdl-text, #1a1f2a)",
6098
+ style: { fontFamily: "'JetBrains Mono', monospace" },
6099
+ children: a.name
6100
+ }
6101
+ ),
6102
+ a.subtitle && /* @__PURE__ */ jsxRuntime.jsx(
6103
+ "text",
6104
+ {
6105
+ "data-cdl-role": "sequence-actor-subtitle",
6106
+ x: m.cx(i),
6107
+ y: SEQ_EDGE + 76,
6108
+ fontSize: SEQ_SUB,
6109
+ textAnchor: "middle",
6110
+ fill: "var(--cdl-text-dim, #5a6270)",
6111
+ children: a.subtitle
6112
+ }
6113
+ )
6114
+ ] }, `head-${i}`)),
6115
+ /* @__PURE__ */ jsxRuntime.jsx(
6116
+ "line",
6117
+ {
6118
+ "data-cdl-role": "sequence-rule",
6119
+ x1: SEQ_EDGE,
6120
+ y1: m.headY,
6121
+ x2: m.w - SEQ_EDGE,
6122
+ y2: m.headY,
6123
+ stroke: "var(--cdl-divider, #d4d4d8)",
6124
+ strokeWidth: 2
6125
+ }
6126
+ ),
6127
+ data.messages.map((msg, i) => /* @__PURE__ */ jsxRuntime.jsx(SequenceRow, { msg, yy: m.rowY(i), cx: m.cx, ...\u898B\u305F\u76EE(i) }, `msg-${i}`))
6128
+ ] });
6129
+ }
6130
+ function SequenceRow({
6131
+ msg,
6132
+ yy,
6133
+ cx,
6134
+ opacity,
6135
+ color
6136
+ }) {
6137
+ const \u7834 = msg.kind === "return";
6138
+ const \u5857\u308B = msg.kind === "call";
6139
+ const \u51FA = cx(msg.from);
6140
+ const \u81EA\u5206\u3078 = msg.from === msg.to;
6141
+ const dir = \u81EA\u5206\u3078 ? 1 : Math.sign(cx(msg.to) - \u51FA);
6142
+ return /* @__PURE__ */ jsxRuntime.jsxs("g", { "data-cdl-role": "sequence-message", "data-cdl-message-kind": msg.kind, opacity, children: [
6143
+ /* @__PURE__ */ jsxRuntime.jsx(
6144
+ "text",
6145
+ {
6146
+ "data-cdl-role": "sequence-label",
6147
+ x: SEQ_EDGE + SEQ_GUTTER_PAD,
6148
+ y: yy + 8,
6149
+ fontSize: SEQ_LABEL,
6150
+ fontWeight: 500,
6151
+ fill: "var(--cdl-text, #1a1f2a)",
6152
+ children: msg.label
6153
+ }
6154
+ ),
6155
+ \u81EA\u5206\u3078 ? (
6156
+ // 自分宛て。 相手がいないので糸の右へ小さく回して戻る
6157
+ /* @__PURE__ */ jsxRuntime.jsx(
6158
+ "path",
6159
+ {
6160
+ "data-cdl-role": "sequence-line",
6161
+ d: `M ${\u51FA + 10} ${yy - 16} H ${\u51FA + SEQ_SELF_W - 16} Q ${\u51FA + SEQ_SELF_W} ${yy - 16} ${\u51FA + SEQ_SELF_W} ${yy} Q ${\u51FA + SEQ_SELF_W} ${yy + 16} ${\u51FA + SEQ_SELF_W - 16} ${yy + 16} H ${\u51FA + 22}`,
6162
+ fill: "none",
6163
+ stroke: color,
6164
+ strokeWidth: 2.5,
6165
+ strokeLinecap: "round"
6166
+ }
6167
+ )
6168
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
6169
+ "path",
6170
+ {
6171
+ "data-cdl-role": "sequence-line",
6172
+ d: `M ${\u51FA + dir * 12} ${yy} H ${cx(msg.to) - dir * 14}`,
6173
+ fill: "none",
6174
+ stroke: color,
6175
+ strokeWidth: 2.5,
6176
+ strokeLinecap: "round",
6177
+ strokeDasharray: \u7834 ? "10 8" : void 0
6178
+ }
6179
+ ),
6180
+ /* @__PURE__ */ jsxRuntime.jsx(
6181
+ Arrow,
6182
+ {
6183
+ x: \u81EA\u5206\u3078 ? \u51FA + 14 : cx(msg.to),
6184
+ y: \u81EA\u5206\u3078 ? yy + 16 : yy,
6185
+ dir: \u81EA\u5206\u3078 ? -1 : dir,
6186
+ filled: \u5857\u308B,
6187
+ color
6188
+ }
6189
+ ),
6190
+ /* @__PURE__ */ jsxRuntime.jsx(
6191
+ "circle",
6192
+ {
6193
+ "data-cdl-role": "sequence-source",
6194
+ cx: \u51FA,
6195
+ cy: \u81EA\u5206\u3078 ? yy - 16 : yy,
6196
+ r: SEQ_DOT,
6197
+ fill: color
6198
+ }
6199
+ )
5918
6200
  ] });
5919
6201
  }
6202
+ function Arrow({
6203
+ x,
6204
+ y,
6205
+ dir,
6206
+ filled,
6207
+ color
6208
+ }) {
6209
+ const \u4E08 = 18;
6210
+ const \u534A = 8.5;
6211
+ const \u5148 = x;
6212
+ const \u80F4 = x - dir * \u4E08;
6213
+ return filled ? /* @__PURE__ */ jsxRuntime.jsx(
6214
+ "path",
6215
+ {
6216
+ "data-cdl-role": "sequence-head",
6217
+ "data-cdl-head-fill": "solid",
6218
+ d: `M ${\u5148} ${y} L ${\u80F4} ${y - \u534A} L ${\u80F4} ${y + \u534A} Z`,
6219
+ fill: color,
6220
+ stroke: color,
6221
+ strokeWidth: 3,
6222
+ strokeLinejoin: "round"
6223
+ }
6224
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
6225
+ "path",
6226
+ {
6227
+ "data-cdl-role": "sequence-head",
6228
+ "data-cdl-head-fill": "open",
6229
+ d: `M ${\u80F4} ${y - \u534A} L ${\u5148} ${y} L ${\u80F4} ${y + \u534A}`,
6230
+ fill: "none",
6231
+ stroke: color,
6232
+ strokeWidth: 2.5,
6233
+ strokeLinecap: "round",
6234
+ strokeLinejoin: "round"
6235
+ }
6236
+ );
6237
+ }
5920
6238
  function EventNode({
5921
6239
  node,
5922
6240
  active,
@@ -6018,6 +6336,29 @@ function FunctionNode({ node, active }) {
6018
6336
  node.subtitle && /* @__PURE__ */ jsxRuntime.jsx("text", { x: 26, y: 126, fontSize: 20, fill: "var(--cdl-text-dim, #5a6270)", children: node.subtitle })
6019
6337
  ] });
6020
6338
  }
6339
+ var ROW_GLYPH_HALF = 7;
6340
+ var ROW_GLYPH_CX = ROW_PAD_X_PX + ROW_GLYPH_HALF + 1;
6341
+ var ROW_GLYPH_LEAD = 40;
6342
+ var ROW_TYPE_FONT = 24;
6343
+ var SUBTITLE_BASELINE = 108;
6344
+ var SUBTITLE_FONT = 22;
6345
+ var UNDERLINE_DROP = 7;
6346
+ function RowGlyph({ mark, x, y, color }) {
6347
+ const \u534A = ROW_GLYPH_HALF;
6348
+ const d = mark.shape === "square" ? `M ${x - \u534A} ${y - \u534A} H ${x + \u534A} V ${y + \u534A} H ${x - \u534A} Z` : `M ${x - \u534A} ${y - \u534A} L ${x + \u534A} ${y} L ${x - \u534A} ${y + \u534A} Z`;
6349
+ return /* @__PURE__ */ jsxRuntime.jsx(
6350
+ "path",
6351
+ {
6352
+ "data-cdl-role": "node-row-glyph",
6353
+ "data-cdl-row-glyph": `${mark.shape}-${mark.filled ? "filled" : "hollow"}`,
6354
+ d,
6355
+ fill: mark.filled ? color : "var(--cdl-node-fill, #ffffff)",
6356
+ stroke: color,
6357
+ strokeWidth: mark.filled ? 0 : 2.4,
6358
+ strokeLinejoin: "round"
6359
+ }
6360
+ );
6361
+ }
6021
6362
  function StorageNode({
6022
6363
  node,
6023
6364
  active,
@@ -6035,6 +6376,8 @@ function StorageNode({
6035
6376
  const leftEndDrawn = ROW_PAD_X_PX + leftMaxWidth * STORAGE_CHAR_ADVANCE_PX;
6036
6377
  const columnDividerX = (leftEndDrawn + rightColX) / 2;
6037
6378
  const columnDividerFits = columnDividerX <= node.w - ROW_PAD_X_PX;
6379
+ const \u5370\u3067\u5206\u3051\u308B = node.rowMarks !== void 0;
6380
+ const \u884C\u306E\u4E0B\u3052 = \u5370\u3067\u5206\u3051\u308B && node.subtitle ? SUBTITLE_ROW_SHIFT : 0;
6038
6381
  const lastRowBottom = Math.min(
6039
6382
  ROW_TOP_STORAGE + (rows.length - 1) * ROW_PITCH_STORAGE + (rowGlyphDepth("storage") ?? 0),
6040
6383
  node.h - ROW_PAD_X_PX
@@ -6055,6 +6398,19 @@ function StorageNode({
6055
6398
  }
6056
6399
  ),
6057
6400
  node.eyebrow && /* @__PURE__ */ jsxRuntime.jsx("text", { x: 26, y: 38, fontSize: 18, fontWeight: 700, letterSpacing: 2, fill: accent, children: node.eyebrow }),
6401
+ \u5370\u3067\u5206\u3051\u308B && node.subtitle && // 呼び名は **印で分ける箱でだけ** 出す (#578)。 従来の箱は題の下に区切り線が来るので
6402
+ // 置き場が無い
6403
+ /* @__PURE__ */ jsxRuntime.jsx(
6404
+ "text",
6405
+ {
6406
+ "data-cdl-role": "node-subtitle",
6407
+ x: ROW_PAD_X_PX,
6408
+ y: SUBTITLE_BASELINE,
6409
+ fontSize: SUBTITLE_FONT,
6410
+ fill: "var(--cdl-text-dim, #5a6270)",
6411
+ children: node.subtitle
6412
+ }
6413
+ ),
6058
6414
  /* @__PURE__ */ jsxRuntime.jsx(
6059
6415
  "text",
6060
6416
  {
@@ -6069,7 +6425,7 @@ function StorageNode({
6069
6425
  children: node.title
6070
6426
  }
6071
6427
  ),
6072
- rows.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
6428
+ rows.length > 0 && !\u5370\u3067\u5206\u3051\u308B && /* @__PURE__ */ jsxRuntime.jsx(
6073
6429
  "line",
6074
6430
  {
6075
6431
  "data-cdl-role": "node-row-divider",
@@ -6081,7 +6437,7 @@ function StorageNode({
6081
6437
  strokeWidth: 1.25
6082
6438
  }
6083
6439
  ),
6084
- rows.length > 0 && hasRightColumn && columnDividerFits && /* @__PURE__ */ jsxRuntime.jsx(
6440
+ rows.length > 0 && hasRightColumn && columnDividerFits && !\u5370\u3067\u5206\u3051\u308B && /* @__PURE__ */ jsxRuntime.jsx(
6085
6441
  "line",
6086
6442
  {
6087
6443
  "data-cdl-role": "node-column-divider",
@@ -6094,15 +6450,37 @@ function StorageNode({
6094
6450
  }
6095
6451
  ),
6096
6452
  splits.map((s, idx) => {
6097
- return /* @__PURE__ */ jsxRuntime.jsxs("g", { transform: `translate(0 ${rowBaselineY("storage", idx)})`, children: [
6453
+ return /* @__PURE__ */ jsxRuntime.jsxs("g", { transform: `translate(0 ${(rowBaselineY("storage", idx) ?? 0) + \u884C\u306E\u4E0B\u3052})`, children: [
6454
+ \u5370\u3067\u5206\u3051\u308B && node.rowMarks?.[idx] != null && /* @__PURE__ */ jsxRuntime.jsx(
6455
+ RowGlyph,
6456
+ {
6457
+ mark: node.rowMarks[idx],
6458
+ x: ROW_GLYPH_CX,
6459
+ y: -8,
6460
+ color: "var(--cdl-text, #1a1f2a)"
6461
+ }
6462
+ ),
6463
+ \u5370\u3067\u5206\u3051\u308B && node.rowMarks?.[idx]?.underline === true && // 主キーの下線 (#578)。 印の 2 軸とは別の段に載るので、外部キーの山形と重ねられる
6464
+ /* @__PURE__ */ jsxRuntime.jsx(
6465
+ "line",
6466
+ {
6467
+ "data-cdl-role": "node-row-underline",
6468
+ x1: ROW_PAD_X_PX + ROW_GLYPH_LEAD,
6469
+ y1: UNDERLINE_DROP,
6470
+ x2: ROW_PAD_X_PX + ROW_GLYPH_LEAD + displayWidth(s.left) * STORAGE_CHAR_ADVANCE_PX,
6471
+ y2: UNDERLINE_DROP,
6472
+ stroke: "var(--cdl-text, #1a1f2a)",
6473
+ strokeWidth: 2
6474
+ }
6475
+ ),
6098
6476
  /* @__PURE__ */ jsxRuntime.jsx(
6099
6477
  "text",
6100
6478
  {
6101
6479
  "data-cdl-role": "node-row",
6102
6480
  "data-cdl-row-index": idx,
6103
- x: ROW_PAD_X_PX,
6481
+ x: \u5370\u3067\u5206\u3051\u308B ? ROW_PAD_X_PX + ROW_GLYPH_LEAD : ROW_PAD_X_PX,
6104
6482
  fontSize: 26,
6105
- fontWeight: 600,
6483
+ fontWeight: \u5370\u3067\u5206\u3051\u308B ? 500 : 600,
6106
6484
  fill: "var(--cdl-text, #1a1f2a)",
6107
6485
  style: { fontFamily: "'JetBrains Mono', monospace" },
6108
6486
  children: s.left
@@ -6113,11 +6491,11 @@ function StorageNode({
6113
6491
  {
6114
6492
  "data-cdl-role": "node-row",
6115
6493
  "data-cdl-row-index": idx,
6116
- x: rightColX,
6117
- fontSize: 26,
6118
- fontWeight: highlightRows ? 700 : 600,
6119
- textAnchor: "start",
6120
- fill: highlightRows ? accent : "var(--cdl-text, #1a1f2a)",
6494
+ x: \u5370\u3067\u5206\u3051\u308B ? node.w - ROW_PAD_X_PX : rightColX,
6495
+ fontSize: \u5370\u3067\u5206\u3051\u308B ? ROW_TYPE_FONT : 26,
6496
+ fontWeight: \u5370\u3067\u5206\u3051\u308B ? 500 : highlightRows ? 700 : 600,
6497
+ textAnchor: \u5370\u3067\u5206\u3051\u308B ? "end" : "start",
6498
+ fill: \u5370\u3067\u5206\u3051\u308B ? "var(--cdl-text-dim, #5a6270)" : highlightRows ? accent : "var(--cdl-text, #1a1f2a)",
6121
6499
  style: { fontFamily: "'JetBrains Mono', monospace" },
6122
6500
  children: s.right
6123
6501
  }
@@ -13515,6 +13893,9 @@ function CdlNodeView({
13515
13893
  return /* @__PURE__ */ jsxRuntime.jsx(EventNode, { node: resolvedNode, active, progress });
13516
13894
  case "card":
13517
13895
  return /* @__PURE__ */ jsxRuntime.jsx(CardNode, { node: resolvedNode, active });
13896
+ // 時系列のやり取り (#578)。 1 つの箱が図を丸ごと描く = 縦が時間で、参加者は箱を持たない
13897
+ case "sequence-board":
13898
+ return /* @__PURE__ */ jsxRuntime.jsx(SequenceBoardNode, { node: resolvedNode, stateValues });
13518
13899
  // 図表 9 種は payload が `{signal}` を取れるので stateValues を渡す
13519
13900
  // (`render/payload-binding.ts` が描画直前に解決する)。 取れる欄は種別で違い、
13520
13901
  // 数と語を取る 7 種に対し、mind-map / tree-hierarchy は **名前だけ** を取る (#467)。
@@ -13842,36 +14223,47 @@ function CdlStage({
13842
14223
  /* @__PURE__ */ jsxRuntime.jsxs("defs", { children: [
13843
14224
  Object.entries(TONE).flatMap(
13844
14225
  ([tone, color]) => [["", 14], ["-sm", 10]].flatMap(
13845
- ([suffix, size]) => EDGE_HEADS.map((head) => {
13846
- const \u5F62 = EDGE_HEAD_SHAPE[head];
13847
- return /* @__PURE__ */ jsxRuntime.jsx(
13848
- "marker",
13849
- {
13850
- id: edgeHeadMarkerId(tone, head, suffix === "-sm"),
13851
- viewBox: "0 0 10 10",
13852
- refX: \u5F62.refX,
13853
- refY: "5",
13854
- markerWidth: size,
13855
- markerHeight: size,
13856
- markerUnits: "userSpaceOnUse",
13857
- orient: "auto-start-reverse",
13858
- children: /* @__PURE__ */ jsxRuntime.jsx(
13859
- "path",
14226
+ ([suffix, size]) => EDGE_HEADS.flatMap(
14227
+ (head) => (
14228
+ // 塗り方は形と別の軸 (#578)。 塗らない形では 2 つが同じ名前に畳まれるので、
14229
+ // `edgeHeadMarkerId` が返す名前で重複を外す
14230
+ EDGE_HEAD_FILLS.map((fill) => {
14231
+ const \u5F62 = EDGE_HEAD_SHAPE[head];
14232
+ const id = edgeHeadMarkerId(tone, head, suffix === "-sm", fill);
14233
+ if (\u5F62.d === "") return null;
14234
+ if (fill !== EDGE_HEAD_FILL_DEFAULT && !\u5F62.filled) return null;
14235
+ const \u5857 = edgeHeadPaint(head, fill, color);
14236
+ return /* @__PURE__ */ jsxRuntime.jsx(
14237
+ "marker",
13860
14238
  {
13861
- "data-cdl-role": "edge-arrowhead",
13862
- "data-cdl-edge-head": head,
13863
- d: \u5F62.d,
13864
- fill: \u5F62.filled ? color : "none",
13865
- stroke: \u5F62.filled ? "none" : color,
13866
- strokeWidth: \u5F62.filled ? void 0 : 1.6,
13867
- strokeLinecap: \u5F62.filled ? void 0 : "round",
13868
- strokeLinejoin: \u5F62.filled ? void 0 : "round"
13869
- }
13870
- )
13871
- },
13872
- `${tone}${suffix}-${head}`
13873
- );
13874
- })
14239
+ id,
14240
+ viewBox: `0 0 ${\u5F62.w} ${\u5F62.h}`,
14241
+ refX: \u5F62.refX,
14242
+ refY: \u5F62.h / 2,
14243
+ markerWidth: size * (\u5F62.scale ?? 1) * \u5F62.w / \u5F62.h,
14244
+ markerHeight: size * (\u5F62.scale ?? 1),
14245
+ markerUnits: "userSpaceOnUse",
14246
+ orient: "auto-start-reverse",
14247
+ children: /* @__PURE__ */ jsxRuntime.jsx(
14248
+ "path",
14249
+ {
14250
+ "data-cdl-role": "edge-arrowhead",
14251
+ "data-cdl-edge-head": head,
14252
+ "data-cdl-edge-head-fill": fill,
14253
+ d: \u5F62.d,
14254
+ fill: \u5857.fill,
14255
+ stroke: \u5857.stroke,
14256
+ strokeWidth: \u5857.strokeWidth,
14257
+ strokeLinecap: \u5857.strokeWidth === void 0 ? void 0 : "round",
14258
+ strokeLinejoin: \u5857.strokeWidth === void 0 ? void 0 : "round"
14259
+ }
14260
+ )
14261
+ },
14262
+ id
14263
+ );
14264
+ })
14265
+ )
14266
+ )
13875
14267
  )
13876
14268
  ),
13877
14269
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -23551,6 +23943,7 @@ function runAxes(laid, diag, violations, counts, push, profile, skippedAxes) {
23551
23943
  if (!isRenderedNode(n)) continue;
23552
23944
  for (const row of n.rows) {
23553
23945
  if (DIVIDER_ONLY.test(row)) continue;
23946
+ if (n.rowMarks !== void 0) continue;
23554
23947
  if (!ROW_FORMAT.test(row)) {
23555
23948
  push("row-format", `node "${n.id}" row "${row}" \u304C "key: value" \u5F62\u5F0F\u3067\u306A\u3044`);
23556
23949
  }
@@ -23882,9 +24275,11 @@ function runAxes(laid, diag, violations, counts, push, profile, skippedAxes) {
23882
24275
  for (const [stack, group] of byStack) {
23883
24276
  if (group.length < 2) continue;
23884
24277
  const cy0 = group[0].cy;
24278
+ const top0 = cy0 - (group[0].h ?? 0) / 2;
23885
24279
  for (const n of group.slice(1)) {
23886
24280
  const diff = Math.abs(n.cy - cy0);
23887
- if (diff > MIN_ROW_ALIGNMENT_TOLERANCE) {
24281
+ const topDiff = Math.abs(n.cy - (n.h ?? 0) / 2 - top0);
24282
+ if (diff > MIN_ROW_ALIGNMENT_TOLERANCE && topDiff > MIN_ROW_ALIGNMENT_TOLERANCE) {
23888
24283
  push(
23889
24284
  "row-alignment",
23890
24285
  `stack=${stack} row \u5185 node "${n.id}" cy=${n.cy.toFixed(1)} \u304C row \u57FA\u6E96 cy=${cy0.toFixed(1)} \u3068 ${diff.toFixed(1)} world \u5DEE (\u8A31\u5BB9 ${MIN_ROW_ALIGNMENT_TOLERANCE})`
@@ -24933,15 +25328,18 @@ function runAxes(laid, diag, violations, counts, push, profile, skippedAxes) {
24933
25328
  if (group.length < 3) continue;
24934
25329
  const sorted = [...group].sort((a, b) => a.cy - b.cy);
24935
25330
  const gaps = [];
25331
+ const tops = [];
24936
25332
  for (let i = 1; i < sorted.length; i++) {
24937
25333
  const prev = sorted[i - 1];
24938
25334
  const cur = sorted[i];
24939
25335
  gaps.push(cur.cy - cur.h / 2 - (prev.cy + prev.h / 2));
25336
+ tops.push(cur.cy - cur.h / 2 - (prev.cy - prev.h / 2));
24940
25337
  }
24941
25338
  const maxGap = Math.max(...gaps);
24942
25339
  const minGap = Math.min(...gaps);
24943
25340
  const variance = maxGap - minGap;
24944
- if (variance > COLUMN_GAP_VARIANCE_TOL) {
25341
+ const topVariance = Math.max(...tops) - Math.min(...tops);
25342
+ if (variance > COLUMN_GAP_VARIANCE_TOL && topVariance > COLUMN_GAP_VARIANCE_TOL) {
24945
25343
  push(
24946
25344
  "column-gap-uniform",
24947
25345
  `lane "${laneId}" \u5185 node \u9593\u306E\u7AEF\u9593 gap variance ${variance.toFixed(1)} world \u304C spec ${COLUMN_GAP_VARIANCE_TOL} \u8D85\u904E (min=${minGap.toFixed(0)} max=${maxGap.toFixed(0)})`