@cardenelabs/cdl 0.16.0 → 0.17.1

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/index.cjs CHANGED
@@ -9494,6 +9494,9 @@ function diagram(id, options) {
9494
9494
  side: opts.side,
9495
9495
  style: opts.style,
9496
9496
  head: opts.head,
9497
+ headFill: opts.headFill,
9498
+ tailHead: opts.tailHead,
9499
+ tailHeadFill: opts.tailHeadFill,
9497
9500
  guard: opts.guard,
9498
9501
  cardinality: opts.cardinality,
9499
9502
  labelOffsetX: opts.labelOffsetX,
@@ -9617,6 +9620,9 @@ function diagram(id, options) {
9617
9620
  side: d.side,
9618
9621
  style: d.style,
9619
9622
  head: d.head,
9623
+ headFill: d.headFill,
9624
+ tailHead: d.tailHead,
9625
+ tailHeadFill: d.tailHeadFill,
9620
9626
  guard: d.guard,
9621
9627
  cardinality: d.cardinality,
9622
9628
  labelOffsetX: d.labelOffsetX,
@@ -11718,6 +11724,9 @@ function hasRenderedLabel(e) {
11718
11724
  return Boolean(e.label?.trim() || e.sub?.trim());
11719
11725
  }
11720
11726
  var KINDS_WITHOUT_TITLE_TEXT = /* @__PURE__ */ new Set([
11727
+ // 時系列のやり取り (#578)。 図を丸ごと描く種別で、描くのは面々の名前と言づての語。
11728
+ // 箱の `title` は使わない
11729
+ "sequence-board",
11721
11730
  // 形だけを描く (DynShapeNode)
11722
11731
  "dyn-rect",
11723
11732
  "dyn-circle",
@@ -11794,7 +11803,7 @@ function genericTextCentered(kind, hasRows) {
11794
11803
  function genericTitleBaselineY(kind, h, hasRows) {
11795
11804
  return genericTextCentered(kind, hasRows) ? h / 2 + GENERIC_TITLE_CENTER_OFFSET : GENERIC_TITLE_TOP + genericTextShift(kind);
11796
11805
  }
11797
- var ROW_TOP_STORAGE = 130;
11806
+ var ROW_TOP_STORAGE = 152;
11798
11807
  var ROW_PITCH_STORAGE = 56;
11799
11808
  var ROW_TOP_GENERIC = 100;
11800
11809
  var ROW_PITCH_GENERIC = 28;
@@ -11805,7 +11814,9 @@ var GENERIC_TITLE_FONT = 20;
11805
11814
  var GLYPH_CAP_RATIO = 0.72;
11806
11815
  var ROW_FONT_STORAGE = 26;
11807
11816
  var ROW_FONT_GENERIC = 21;
11808
- var STORAGE_TITLE_BASELINE = 78;
11817
+ var STORAGE_TITLE_BASELINE = 86;
11818
+ var STORAGE_TITLE_FONT = 34;
11819
+ var SUBTITLE_ROW_SHIFT = 46;
11809
11820
  var STORAGE_ROW_DIVIDER_Y = Math.round(
11810
11821
  (STORAGE_TITLE_BASELINE + (ROW_TOP_STORAGE - ROW_FONT_STORAGE * GLYPH_CAP_RATIO)) / 2
11811
11822
  );
@@ -13409,7 +13420,10 @@ var TOKENS = {
13409
13420
  // 印は箱いっぱいに描く (線が箱の縁で止まるため) ので、この値が丸の直径になる。
13410
13421
  // 箱としての最小 (80x40) より大きく、名前を持つ箱 (card 320x150) よりは明らかに小さい
13411
13422
  "mark-start": { w: 96, h: 96 },
13412
- "mark-end": { w: 96, h: 96 }
13423
+ "mark-end": { w: 96, h: 96 },
13424
+ // 時系列のやり取り (#578)。 実寸は中身から決まる (`sequenceBoardMetrics`) ので、
13425
+ // ここは書かなかった時の受け皿
13426
+ "sequence-board": { w: 960, h: 640 }
13413
13427
  },
13414
13428
  // row gap (stack 間の vertical gap、 lane 内 / 跨ぎ共通)
13415
13429
  // edge label が node の上 36 px に配置されるため、 boxH 68 + 36 + 余裕 = 100 必要。
@@ -15072,8 +15086,12 @@ function requiredRowsWidth(rows, kind) {
15072
15086
  function autoStorageHeight(n, defaultH) {
15073
15087
  if (n.kind !== "storage") return n.h ?? defaultH;
15074
15088
  if (n.h != null) return n.h;
15075
- if (!n.rows || n.rows.length === 0) return 150;
15076
- return Math.max(defaultH, requiredRowsHeight("storage", n.rows.length) ?? defaultH);
15089
+ const \u5370\u3042\u308A = n.rowMarks !== void 0;
15090
+ const \u547C\u3073\u540D\u306E\u5206 = \u5370\u3042\u308A && n.subtitle ? SUBTITLE_ROW_SHIFT : 0;
15091
+ if (!n.rows || n.rows.length === 0) return 150 + \u547C\u3073\u540D\u306E\u5206;
15092
+ const \u5FC5\u8981 = requiredRowsHeight("storage", n.rows.length) ?? defaultH;
15093
+ if (\u5370\u3042\u308A) return \u5FC5\u8981 + \u547C\u3073\u540D\u306E\u5206;
15094
+ return Math.max(defaultH, \u5FC5\u8981);
15077
15095
  }
15078
15096
  function autoRowsWidth(n, defaultW) {
15079
15097
  if (n.w != null) return n.w;
@@ -15161,8 +15179,11 @@ function layoutNodes(diag, lanes) {
15161
15179
  continue;
15162
15180
  }
15163
15181
  const cx = lane.x + lane.width / 2;
15164
- const cy = rowCy.get(n.stack);
15165
- const h = n.h ?? rowH.get(n.stack);
15182
+ const \u6BB5\u306E\u82AF = rowCy.get(n.stack);
15183
+ const \u6BB5\u306E\u9AD8\u3055 = rowH.get(n.stack);
15184
+ const \u81EA\u524D\u306E\u9AD8\u3055 = n.rowMarks !== void 0;
15185
+ const h = n.h ?? (\u81EA\u524D\u306E\u9AD8\u3055 ? autoStorageHeight(n, size.h) : \u6BB5\u306E\u9AD8\u3055);
15186
+ const cy = \u6BB5\u306E\u82AF;
15166
15187
  const w = autoRowsWidth(n, size.w);
15167
15188
  out.push({
15168
15189
  ...n,
@@ -15491,10 +15512,25 @@ var NODE_KINDS = [
15491
15512
  // 流れの始まりと終わりの印 2 種 (#560) ... 塗った丸と輪で囲んだ丸。
15492
15513
  // 状態遷移図が使う。 印そのものが意味を持つので文字は描かない
15493
15514
  "mark-start",
15494
- "mark-end"
15515
+ "mark-end",
15516
+ // 時系列のやり取り (#578) ... 1 つの箱が図を丸ごと描く。 縦が時間で、参加者は箱を持たない
15517
+ "sequence-board"
15495
15518
  ];
15519
+ var EDGE_STYLES = ["solid", "dotted-flow", "dashed"];
15496
15520
  var EDGE_HEAD_DEFAULT = "triangle";
15497
- var EDGE_HEADS = ["triangle", "diamond", "open", "crow"];
15521
+ var EDGE_HEADS = [
15522
+ "none",
15523
+ "triangle",
15524
+ "diamond",
15525
+ "open",
15526
+ "crow",
15527
+ "one",
15528
+ "zero-one",
15529
+ "many",
15530
+ "zero-many"
15531
+ ];
15532
+ var EDGE_HEAD_FILL_DEFAULT = "solid";
15533
+ var EDGE_HEAD_FILLS = ["solid", "hollow"];
15498
15534
 
15499
15535
  // src/anim/core/easing.ts
15500
15536
  function cubicBezier(x1, y1, x2, y2) {
@@ -15989,7 +16025,7 @@ function pathSubpath(d, progress) {
15989
16025
  }
15990
16026
 
15991
16027
  // src/render/template-fields.ts
15992
- var TEXT_FIELDS = ["title", "eyebrow", "subtitle", "value", "wBind", "hBind", "visibleIf"];
16028
+ var TEXT_FIELDS = ["title", "eyebrow", "subtitle", "value", "wBind", "hBind", "visibleIf", "sequenceStep"];
15993
16029
  var LIST_FIELDS = ["rows"];
15994
16030
  var NUMERIC_FIELDS = ["opacity", "renderOffsetX", "renderOffsetY"];
15995
16031
  var SHAPE_TEMPLATE_FIELDS = ["source", "radius", "fillProgress", "angle", "level", "rotation", "fill"];
@@ -18314,6 +18350,7 @@ function runAxes(laid, diag, violations, counts, push, profile, skippedAxes) {
18314
18350
  if (!isRenderedNode(n)) continue;
18315
18351
  for (const row of n.rows) {
18316
18352
  if (DIVIDER_ONLY.test(row)) continue;
18353
+ if (n.rowMarks !== void 0) continue;
18317
18354
  if (!ROW_FORMAT.test(row)) {
18318
18355
  push("row-format", `node "${n.id}" row "${row}" \u304C "key: value" \u5F62\u5F0F\u3067\u306A\u3044`);
18319
18356
  }
@@ -19704,7 +19741,16 @@ function runAxes(laid, diag, violations, counts, push, profile, skippedAxes) {
19704
19741
  const maxGap = Math.max(...gaps);
19705
19742
  const minGap = Math.min(...gaps);
19706
19743
  const variance = maxGap - minGap;
19707
- if (variance > COLUMN_GAP_VARIANCE_TOL) {
19744
+ const \u683C\u5B50\u306B\u7F6E\u304F = group.every((n) => n.rowMarks !== void 0);
19745
+ const \u7AEF\u304C\u8DB3\u308A\u3066\u3044\u308B = minGap >= requiredNearClearance("node", "node");
19746
+ if (\u683C\u5B50\u306B\u7F6E\u304F) {
19747
+ if (!\u7AEF\u304C\u8DB3\u308A\u3066\u3044\u308B) {
19748
+ push(
19749
+ "column-gap-uniform",
19750
+ `lane "${laneId}" \u5185 node \u9593\u306E\u7AEF\u9593 gap ${minGap.toFixed(0)} world \u304C\u4E0B\u9650 ${requiredNearClearance("node", "node")} \u3092\u5272\u308B`
19751
+ );
19752
+ }
19753
+ } else if (variance > COLUMN_GAP_VARIANCE_TOL) {
19708
19754
  push(
19709
19755
  "column-gap-uniform",
19710
19756
  `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)})`
@@ -21178,14 +21224,42 @@ function StateDiffCard({
21178
21224
 
21179
21225
  // src/render/edge-head.ts
21180
21226
  var EDGE_HEAD_SHAPE = {
21181
- triangle: { d: "M 0 0 L 10 5 L 0 10 z", refX: 9, filled: true },
21182
- diamond: { d: "M 0 5 L 5 0 L 10 5 L 5 10 z", refX: 9, filled: true },
21183
- open: { d: "M 1 0 L 9 5 L 1 10", refX: 9, filled: false },
21184
- crow: { d: "M 10 0 L 0 5 M 10 5 L 0 5 M 10 10 L 0 5", refX: 10, filled: false }
21227
+ // 何も描かない端。 marker 自体を出さないので `d` は読まれない
21228
+ none: { d: "", w: 10, h: 10, refX: 0, filled: false },
21229
+ triangle: { d: "M 0 0 L 10 5 L 0 10 z", w: 10, h: 10, refX: 9, filled: true },
21230
+ diamond: { d: "M 0 5 L 5 0 L 10 5 L 5 10 z", w: 10, h: 10, refX: 9, filled: true },
21231
+ open: { d: "M 1 0 L 9 5 L 1 10", w: 10, h: 10, refX: 9, filled: false },
21232
+ 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 },
21233
+ // ER の端。 **箱に近い側 (x が大きい側) が個数、その外側が任意か** (#578)。
21234
+ //
21235
+ // 枠は 24 × 10 で、線の先端に合わせるのは x=24 = 箱の縁。 marker は経路の向きに回るので、
21236
+ // 行き先側でも出どころ側でも「箱に近い側」 が同じ端になる。
21237
+ //
21238
+ // 三又の真ん中の枝は **関係の線そのもの** が兼ねる。 別に引くと同じ所に 2 本重なって太く見える
21239
+ 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 },
21240
+ "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 },
21241
+ 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 },
21242
+ "zero-many": {
21243
+ 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",
21244
+ w: 24,
21245
+ h: 10,
21246
+ refX: 24,
21247
+ scale: 1.8,
21248
+ filled: false
21249
+ }
21185
21250
  };
21186
- function edgeHeadMarkerId(tone, head, small) {
21251
+ function edgeHeadMarkerId(tone, head, small, fill = EDGE_HEAD_FILL_DEFAULT) {
21187
21252
  const \u5C0F = small ? "-sm" : "";
21188
- return head === EDGE_HEAD_DEFAULT ? `cdl-arrow-${tone}${\u5C0F}` : `cdl-arrow-${tone}-${head}${\u5C0F}`;
21253
+ const \u5857 = fill === EDGE_HEAD_FILL_DEFAULT || !EDGE_HEAD_SHAPE[head].filled ? "" : `-${fill}`;
21254
+ return head === EDGE_HEAD_DEFAULT && \u5857 === "" ? `cdl-arrow-${tone}${\u5C0F}` : `cdl-arrow-${tone}-${head}${\u5857}${\u5C0F}`;
21255
+ }
21256
+ function edgeHeadPaint(head, fill, color) {
21257
+ const \u5F62 = EDGE_HEAD_SHAPE[head];
21258
+ if (!\u5F62.filled) return { fill: "none", stroke: color, strokeWidth: 1.6 };
21259
+ if (fill === "hollow") {
21260
+ return { fill: "var(--cdl-node-fill, #ffffff)", stroke: color, strokeWidth: 1.4 };
21261
+ }
21262
+ return { fill: color, stroke: "none" };
21189
21263
  }
21190
21264
  function opacityAttr(opacity) {
21191
21265
  return opacity < 1 ? opacity : void 0;
@@ -21200,7 +21274,13 @@ function CdlEdgeView({
21200
21274
  stateValues
21201
21275
  }) {
21202
21276
  const color = toneColor(edge.tone);
21203
- const marker = hasTone(edge.tone) ? edgeHeadMarkerId(edge.tone, edge.head ?? EDGE_HEAD_DEFAULT, !active) : "cdl-arrow-muted";
21277
+ const \u7AEF\u306E\u540D\u524D = (h, f) => {
21278
+ const \u5F62 = h ?? EDGE_HEAD_DEFAULT;
21279
+ if (\u5F62 === "none") return void 0;
21280
+ return hasTone(edge.tone) ? edgeHeadMarkerId(edge.tone, \u5F62, !active, f ?? EDGE_HEAD_FILL_DEFAULT) : "cdl-arrow-muted";
21281
+ };
21282
+ const marker = \u7AEF\u306E\u540D\u524D(edge.head, edge.headFill);
21283
+ const tailMarker = edge.tailHead === void 0 ? void 0 : \u7AEF\u306E\u540D\u524D(edge.tailHead, edge.tailHeadFill);
21204
21284
  const style = edge.style ?? "solid";
21205
21285
  const isDotted = style === "dotted-flow";
21206
21286
  const isPassThrough = isDotted && Boolean(edge.dThrough);
@@ -21248,6 +21328,8 @@ function CdlEdgeView({
21248
21328
  progress,
21249
21329
  color,
21250
21330
  marker,
21331
+ tailMarker,
21332
+ dashed: style === "dashed",
21251
21333
  stateValues
21252
21334
  }
21253
21335
  ),
@@ -21273,6 +21355,8 @@ function SolidEdgePath({
21273
21355
  progress,
21274
21356
  color,
21275
21357
  marker,
21358
+ tailMarker,
21359
+ dashed,
21276
21360
  stateValues
21277
21361
  }) {
21278
21362
  const visibleD = active ? pathSubpath(edge.d, progress) : edge.d;
@@ -21280,7 +21364,7 @@ function SolidEdgePath({
21280
21364
  const boundWidth = edge.widthBind && stateValues ? resolveNumericAttr(edge.widthBind, stateValues, defaultWidth) : defaultWidth;
21281
21365
  const boundStroke = edge.strokeBind && stateValues ? interpolate(edge.strokeBind, stateValues) : color;
21282
21366
  const boundDashOffset = edge.dashOffsetBind && stateValues ? resolveNumericAttr(edge.dashOffsetBind, stateValues, 0) : void 0;
21283
- const dashArray = active ? boundDashOffset !== void 0 ? "8 6" : void 0 : "4 5";
21367
+ const dashArray = dashed ? "10 8" : active ? boundDashOffset !== void 0 ? "8 6" : void 0 : "4 5";
21284
21368
  return /* @__PURE__ */ jsxRuntime.jsx(
21285
21369
  "path",
21286
21370
  {
@@ -21293,7 +21377,8 @@ function SolidEdgePath({
21293
21377
  strokeDasharray: dashArray,
21294
21378
  strokeDashoffset: boundDashOffset,
21295
21379
  strokeLinecap: "round",
21296
- markerEnd: `url(#${marker})`,
21380
+ markerEnd: marker === void 0 ? void 0 : `url(#${marker})`,
21381
+ markerStart: tailMarker === void 0 ? void 0 : `url(#${tailMarker})`,
21297
21382
  style: {
21298
21383
  transition: "stroke 280ms ease, stroke-opacity 280ms ease, stroke-width 280ms ease"
21299
21384
  }
@@ -21505,7 +21590,8 @@ function CardNode({ node, active }) {
21505
21590
  return /* @__PURE__ */ jsxRuntime.jsx("g", { transform: `translate(${x} ${y})` });
21506
21591
  }
21507
21592
  const compact = isCompactBox(node.h);
21508
- const titleY = compact ? node.h / 2 + 8 : 86;
21593
+ const \u5C0F\u578B\u306B\u547C\u3073\u540D = compact && Boolean(node.subtitle);
21594
+ const titleY = compact ? \u5C0F\u578B\u306B\u547C\u3073\u540D ? node.h / 2 - 2 : node.h / 2 + 8 : 86;
21509
21595
  const titleX = compact ? node.w / 2 : 26;
21510
21596
  const paddingX = compact ? 16 : 52;
21511
21597
  const availableW = node.w - paddingX;
@@ -21542,9 +21628,262 @@ function CardNode({ node, active }) {
21542
21628
  children: node.title
21543
21629
  }
21544
21630
  ),
21545
- !compact && node.subtitle && /* @__PURE__ */ jsxRuntime.jsx("text", { x: 26, y: 124, fontSize: subtitleSize, fill: "var(--cdl-text-dim, #5a6270)", children: node.subtitle })
21631
+ !compact && node.subtitle && /* @__PURE__ */ jsxRuntime.jsx("text", { x: 26, y: 124, fontSize: subtitleSize, fill: "var(--cdl-text-dim, #5a6270)", children: node.subtitle }),
21632
+ \u5C0F\u578B\u306B\u547C\u3073\u540D && /* @__PURE__ */ jsxRuntime.jsx(
21633
+ "text",
21634
+ {
21635
+ "data-cdl-role": "node-subtitle",
21636
+ x: node.w / 2,
21637
+ y: node.h / 2 + 24,
21638
+ fontSize: Math.min(subtitleSize, 18),
21639
+ textAnchor: "middle",
21640
+ fill: "var(--cdl-text-dim, #5a6270)",
21641
+ children: node.subtitle
21642
+ }
21643
+ )
21546
21644
  ] });
21547
21645
  }
21646
+
21647
+ // src/kinds/sequence-board-metrics.ts
21648
+ var SEQ_EDGE = 16;
21649
+ var SEQ_ROW = 64;
21650
+ var SEQ_GUTTER_PAD = 40;
21651
+ var SEQ_GUTTER_GAP = 40;
21652
+ var SEQ_HEAD = 96;
21653
+ var SEQ_ROW_PAD = 56;
21654
+ var SEQ_COL_PAD = 80;
21655
+ var SEQ_BAND_W = 52;
21656
+ var SEQ_BAND_PAD = 24;
21657
+ var SEQ_NAME = 24;
21658
+ var SEQ_SUB = 20;
21659
+ var SEQ_LABEL = 26;
21660
+ var SEQ_DOT = 7;
21661
+ var SEQ_SELF_W = 52;
21662
+ var SEQ_COL_MIN = 200;
21663
+ var g4 = (v) => Math.round(v / 4) * 4;
21664
+ function sequenceStepId() {
21665
+ return "seq_step";
21666
+ }
21667
+ function sequenceBoardMetrics(data) {
21668
+ const \u540D\u5E45 = data.actors.map(
21669
+ (a) => Math.max(textWidth(a.name, SEQ_NAME), a.subtitle ? textWidth(a.subtitle, SEQ_SUB) : 0)
21670
+ );
21671
+ const colW = g4(Math.max(SEQ_COL_MIN, Math.max(0, ...\u540D\u5E45) + SEQ_COL_PAD));
21672
+ const \u8A9E\u5E45 = Math.max(0, ...data.messages.map((m) => textWidth(m.label, SEQ_LABEL)));
21673
+ const gutter = g4(SEQ_EDGE + SEQ_GUTTER_PAD + \u8A9E\u5E45 + SEQ_GUTTER_GAP);
21674
+ const headY = SEQ_EDGE + SEQ_HEAD;
21675
+ const rowY = (i) => headY + SEQ_ROW_PAD + i * SEQ_ROW;
21676
+ const \u6700\u7D42 = data.messages.length > 0 ? rowY(data.messages.length - 1) : headY;
21677
+ const bottom = \u6700\u7D42 + SEQ_ROW_PAD;
21678
+ const cx = (i) => gutter + colW / 2 + i * colW;
21679
+ const w = g4(cx(Math.max(0, data.actors.length - 1)) + colW / 2 + SEQ_EDGE);
21680
+ const h = g4(bottom + SEQ_EDGE);
21681
+ return { w, h, gutter, colW, headY, cx, rowY, bottom };
21682
+ }
21683
+ function SequenceBoardNode({
21684
+ node,
21685
+ stateValues
21686
+ }) {
21687
+ const data = node.sequenceData;
21688
+ if (!data) return /* @__PURE__ */ jsxRuntime.jsx("g", { "data-cdl-role": "sequence-board" });
21689
+ const m = sequenceBoardMetrics(data);
21690
+ const x = node.cx - node.w / 2;
21691
+ const y = node.cy - node.h / 2;
21692
+ const \u73FE\u5728 = (() => {
21693
+ if (!node.sequenceStep) return null;
21694
+ const v = Number.parseInt(interpolate(node.sequenceStep, stateValues ?? {}).trim(), 10);
21695
+ return Number.isFinite(v) ? v : null;
21696
+ })();
21697
+ const \u6731 = "var(--cdl-now, var(--cdl-tone-accent, #2d6a8f))";
21698
+ const \u58A8 = "var(--cdl-text-2, #4a5162)";
21699
+ const \u898B\u305F\u76EE = (i) => {
21700
+ if (\u73FE\u5728 === null) return { opacity: 1, color: \u6731 };
21701
+ if (i > \u73FE\u5728) return { opacity: 0, color: \u6731 };
21702
+ return i === \u73FE\u5728 ? { opacity: 1, color: \u6731 } : { opacity: 1, color: \u58A8 };
21703
+ };
21704
+ return /* @__PURE__ */ jsxRuntime.jsxs("g", { "data-cdl-role": "sequence-board", transform: `translate(${x} ${y})`, children: [
21705
+ data.bands.map((b, i) => {
21706
+ if (\u73FE\u5728 !== null && b.from > \u73FE\u5728) return null;
21707
+ const \u4E0B = \u73FE\u5728 === null ? b.to : Math.min(b.to, \u73FE\u5728);
21708
+ return /* @__PURE__ */ jsxRuntime.jsx(
21709
+ "rect",
21710
+ {
21711
+ "data-cdl-role": "sequence-band",
21712
+ x: m.cx(b.actor) - SEQ_BAND_W / 2,
21713
+ y: m.rowY(b.from) - SEQ_BAND_PAD / 2,
21714
+ width: SEQ_BAND_W,
21715
+ height: m.rowY(\u4E0B) - m.rowY(b.from) + SEQ_BAND_PAD,
21716
+ rx: 4,
21717
+ fill: \u6731,
21718
+ fillOpacity: 0.12
21719
+ },
21720
+ `band-${i}`
21721
+ );
21722
+ }),
21723
+ data.actors.map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(
21724
+ "line",
21725
+ {
21726
+ "data-cdl-role": "sequence-thread",
21727
+ x1: m.cx(i),
21728
+ y1: m.headY,
21729
+ x2: m.cx(i),
21730
+ y2: m.bottom,
21731
+ stroke: "var(--cdl-divider, #d4d4d8)",
21732
+ strokeWidth: 3
21733
+ },
21734
+ `thread-${i}`
21735
+ )),
21736
+ data.actors.map((a, i) => /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
21737
+ /* @__PURE__ */ jsxRuntime.jsx(
21738
+ "text",
21739
+ {
21740
+ "data-cdl-role": "sequence-actor",
21741
+ x: m.cx(i),
21742
+ y: SEQ_EDGE + 44,
21743
+ fontSize: SEQ_NAME,
21744
+ fontWeight: 700,
21745
+ textAnchor: "middle",
21746
+ fill: "var(--cdl-text, #1a1f2a)",
21747
+ style: { fontFamily: "'JetBrains Mono', monospace" },
21748
+ children: a.name
21749
+ }
21750
+ ),
21751
+ a.subtitle && /* @__PURE__ */ jsxRuntime.jsx(
21752
+ "text",
21753
+ {
21754
+ "data-cdl-role": "sequence-actor-subtitle",
21755
+ x: m.cx(i),
21756
+ y: SEQ_EDGE + 76,
21757
+ fontSize: SEQ_SUB,
21758
+ textAnchor: "middle",
21759
+ fill: "var(--cdl-text-dim, #5a6270)",
21760
+ children: a.subtitle
21761
+ }
21762
+ )
21763
+ ] }, `head-${i}`)),
21764
+ /* @__PURE__ */ jsxRuntime.jsx(
21765
+ "line",
21766
+ {
21767
+ "data-cdl-role": "sequence-rule",
21768
+ x1: SEQ_EDGE,
21769
+ y1: m.headY,
21770
+ x2: m.w - SEQ_EDGE,
21771
+ y2: m.headY,
21772
+ stroke: "var(--cdl-divider, #d4d4d8)",
21773
+ strokeWidth: 2
21774
+ }
21775
+ ),
21776
+ data.messages.map((msg, i) => /* @__PURE__ */ jsxRuntime.jsx(SequenceRow, { msg, yy: m.rowY(i), cx: m.cx, ...\u898B\u305F\u76EE(i) }, `msg-${i}`))
21777
+ ] });
21778
+ }
21779
+ function SequenceRow({
21780
+ msg,
21781
+ yy,
21782
+ cx,
21783
+ opacity,
21784
+ color
21785
+ }) {
21786
+ const \u7834 = msg.kind === "return";
21787
+ const \u5857\u308B = msg.kind === "call";
21788
+ const \u51FA = cx(msg.from);
21789
+ const \u81EA\u5206\u3078 = msg.from === msg.to;
21790
+ const dir = \u81EA\u5206\u3078 ? 1 : Math.sign(cx(msg.to) - \u51FA);
21791
+ return /* @__PURE__ */ jsxRuntime.jsxs("g", { "data-cdl-role": "sequence-message", "data-cdl-message-kind": msg.kind, opacity, children: [
21792
+ /* @__PURE__ */ jsxRuntime.jsx(
21793
+ "text",
21794
+ {
21795
+ "data-cdl-role": "sequence-label",
21796
+ x: SEQ_EDGE + SEQ_GUTTER_PAD,
21797
+ y: yy + 8,
21798
+ fontSize: SEQ_LABEL,
21799
+ fontWeight: 500,
21800
+ fill: "var(--cdl-text, #1a1f2a)",
21801
+ children: msg.label
21802
+ }
21803
+ ),
21804
+ \u81EA\u5206\u3078 ? (
21805
+ // 自分宛て。 相手がいないので糸の右へ小さく回して戻る
21806
+ /* @__PURE__ */ jsxRuntime.jsx(
21807
+ "path",
21808
+ {
21809
+ "data-cdl-role": "sequence-line",
21810
+ 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}`,
21811
+ fill: "none",
21812
+ stroke: color,
21813
+ strokeWidth: 2.5,
21814
+ strokeLinecap: "round"
21815
+ }
21816
+ )
21817
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
21818
+ "path",
21819
+ {
21820
+ "data-cdl-role": "sequence-line",
21821
+ d: `M ${\u51FA + dir * 12} ${yy} H ${cx(msg.to) - dir * 14}`,
21822
+ fill: "none",
21823
+ stroke: color,
21824
+ strokeWidth: 2.5,
21825
+ strokeLinecap: "round",
21826
+ strokeDasharray: \u7834 ? "10 8" : void 0
21827
+ }
21828
+ ),
21829
+ /* @__PURE__ */ jsxRuntime.jsx(
21830
+ Arrow,
21831
+ {
21832
+ x: \u81EA\u5206\u3078 ? \u51FA + 14 : cx(msg.to),
21833
+ y: \u81EA\u5206\u3078 ? yy + 16 : yy,
21834
+ dir: \u81EA\u5206\u3078 ? -1 : dir,
21835
+ filled: \u5857\u308B,
21836
+ color
21837
+ }
21838
+ ),
21839
+ /* @__PURE__ */ jsxRuntime.jsx(
21840
+ "circle",
21841
+ {
21842
+ "data-cdl-role": "sequence-source",
21843
+ cx: \u51FA,
21844
+ cy: \u81EA\u5206\u3078 ? yy - 16 : yy,
21845
+ r: SEQ_DOT,
21846
+ fill: color
21847
+ }
21848
+ )
21849
+ ] });
21850
+ }
21851
+ function Arrow({
21852
+ x,
21853
+ y,
21854
+ dir,
21855
+ filled,
21856
+ color
21857
+ }) {
21858
+ const \u4E08 = 18;
21859
+ const \u534A = 8.5;
21860
+ const \u5148 = x;
21861
+ const \u80F4 = x - dir * \u4E08;
21862
+ return filled ? /* @__PURE__ */ jsxRuntime.jsx(
21863
+ "path",
21864
+ {
21865
+ "data-cdl-role": "sequence-head",
21866
+ "data-cdl-head-fill": "solid",
21867
+ d: `M ${\u5148} ${y} L ${\u80F4} ${y - \u534A} L ${\u80F4} ${y + \u534A} Z`,
21868
+ fill: color,
21869
+ stroke: color,
21870
+ strokeWidth: 3,
21871
+ strokeLinejoin: "round"
21872
+ }
21873
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
21874
+ "path",
21875
+ {
21876
+ "data-cdl-role": "sequence-head",
21877
+ "data-cdl-head-fill": "open",
21878
+ d: `M ${\u80F4} ${y - \u534A} L ${\u5148} ${y} L ${\u80F4} ${y + \u534A}`,
21879
+ fill: "none",
21880
+ stroke: color,
21881
+ strokeWidth: 2.5,
21882
+ strokeLinecap: "round",
21883
+ strokeLinejoin: "round"
21884
+ }
21885
+ );
21886
+ }
21548
21887
  function EventNode({
21549
21888
  node,
21550
21889
  active,
@@ -21646,6 +21985,29 @@ function FunctionNode({ node, active }) {
21646
21985
  node.subtitle && /* @__PURE__ */ jsxRuntime.jsx("text", { x: 26, y: 126, fontSize: 20, fill: "var(--cdl-text-dim, #5a6270)", children: node.subtitle })
21647
21986
  ] });
21648
21987
  }
21988
+ var ROW_GLYPH_HALF = 7;
21989
+ var ROW_GLYPH_CX = ROW_PAD_X_PX + ROW_GLYPH_HALF + 1;
21990
+ var ROW_GLYPH_LEAD = 40;
21991
+ var ROW_TYPE_FONT = 22;
21992
+ var SUBTITLE_BASELINE = 118;
21993
+ var SUBTITLE_FONT = 20;
21994
+ var UNDERLINE_DROP = 7;
21995
+ function RowGlyph({ mark, x, y, color }) {
21996
+ const \u534A = ROW_GLYPH_HALF;
21997
+ 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`;
21998
+ return /* @__PURE__ */ jsxRuntime.jsx(
21999
+ "path",
22000
+ {
22001
+ "data-cdl-role": "node-row-glyph",
22002
+ "data-cdl-row-glyph": `${mark.shape}-${mark.filled ? "filled" : "hollow"}`,
22003
+ d,
22004
+ fill: mark.filled ? color : "var(--cdl-node-fill, #ffffff)",
22005
+ stroke: color,
22006
+ strokeWidth: mark.filled ? 0 : 2.4,
22007
+ strokeLinejoin: "round"
22008
+ }
22009
+ );
22010
+ }
21649
22011
  function StorageNode({
21650
22012
  node,
21651
22013
  active,
@@ -21655,7 +22017,7 @@ function StorageNode({
21655
22017
  const accent = nodeAccent(node, "var(--cdl-tone-accent, #2d6a8f)");
21656
22018
  const x = node.cx - node.w / 2;
21657
22019
  const y = node.cy - node.h / 2;
21658
- const \u540D\u524D = titlePlacement(node, { x: 26, y: STORAGE_TITLE_BASELINE, size: 22 }, { hasEyebrow: Boolean(node.eyebrow) });
22020
+ const \u540D\u524D = titlePlacement(node, { x: 26, y: STORAGE_TITLE_BASELINE, size: STORAGE_TITLE_FONT }, { hasEyebrow: Boolean(node.eyebrow) });
21659
22021
  const splits = rows.map((r) => splitRow(r));
21660
22022
  const leftMaxWidth = splits.reduce((m, s) => Math.max(m, displayWidth(s.left)), 0);
21661
22023
  const rightColX = ROW_PAD_X_PX + leftMaxWidth * STORAGE_CHAR_ADVANCE_PX + STORAGE_COL_GAP_PX;
@@ -21663,6 +22025,8 @@ function StorageNode({
21663
22025
  const leftEndDrawn = ROW_PAD_X_PX + leftMaxWidth * STORAGE_CHAR_ADVANCE_PX;
21664
22026
  const columnDividerX = (leftEndDrawn + rightColX) / 2;
21665
22027
  const columnDividerFits = columnDividerX <= node.w - ROW_PAD_X_PX;
22028
+ const \u5370\u3067\u5206\u3051\u308B = node.rowMarks !== void 0;
22029
+ const \u884C\u306E\u4E0B\u3052 = \u5370\u3067\u5206\u3051\u308B && node.subtitle ? SUBTITLE_ROW_SHIFT : 0;
21666
22030
  const lastRowBottom = Math.min(
21667
22031
  ROW_TOP_STORAGE + (rows.length - 1) * ROW_PITCH_STORAGE + (rowGlyphDepth("storage") ?? 0),
21668
22032
  node.h - ROW_PAD_X_PX
@@ -21683,6 +22047,19 @@ function StorageNode({
21683
22047
  }
21684
22048
  ),
21685
22049
  node.eyebrow && /* @__PURE__ */ jsxRuntime.jsx("text", { x: 26, y: 38, fontSize: 18, fontWeight: 700, letterSpacing: 2, fill: accent, children: node.eyebrow }),
22050
+ \u5370\u3067\u5206\u3051\u308B && node.subtitle && // 呼び名は **印で分ける箱でだけ** 出す (#578)。 従来の箱は題の下に区切り線が来るので
22051
+ // 置き場が無い
22052
+ /* @__PURE__ */ jsxRuntime.jsx(
22053
+ "text",
22054
+ {
22055
+ "data-cdl-role": "node-subtitle",
22056
+ x: ROW_PAD_X_PX,
22057
+ y: SUBTITLE_BASELINE,
22058
+ fontSize: SUBTITLE_FONT,
22059
+ fill: "var(--cdl-text-dim, #5a6270)",
22060
+ children: node.subtitle
22061
+ }
22062
+ ),
21686
22063
  /* @__PURE__ */ jsxRuntime.jsx(
21687
22064
  "text",
21688
22065
  {
@@ -21697,7 +22074,7 @@ function StorageNode({
21697
22074
  children: node.title
21698
22075
  }
21699
22076
  ),
21700
- rows.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
22077
+ rows.length > 0 && !\u5370\u3067\u5206\u3051\u308B && /* @__PURE__ */ jsxRuntime.jsx(
21701
22078
  "line",
21702
22079
  {
21703
22080
  "data-cdl-role": "node-row-divider",
@@ -21709,7 +22086,7 @@ function StorageNode({
21709
22086
  strokeWidth: 1.25
21710
22087
  }
21711
22088
  ),
21712
- rows.length > 0 && hasRightColumn && columnDividerFits && /* @__PURE__ */ jsxRuntime.jsx(
22089
+ rows.length > 0 && hasRightColumn && columnDividerFits && !\u5370\u3067\u5206\u3051\u308B && /* @__PURE__ */ jsxRuntime.jsx(
21713
22090
  "line",
21714
22091
  {
21715
22092
  "data-cdl-role": "node-column-divider",
@@ -21722,15 +22099,37 @@ function StorageNode({
21722
22099
  }
21723
22100
  ),
21724
22101
  splits.map((s, idx) => {
21725
- return /* @__PURE__ */ jsxRuntime.jsxs("g", { transform: `translate(0 ${rowBaselineY("storage", idx)})`, children: [
22102
+ return /* @__PURE__ */ jsxRuntime.jsxs("g", { transform: `translate(0 ${(rowBaselineY("storage", idx) ?? 0) + \u884C\u306E\u4E0B\u3052})`, children: [
22103
+ \u5370\u3067\u5206\u3051\u308B && node.rowMarks?.[idx] != null && /* @__PURE__ */ jsxRuntime.jsx(
22104
+ RowGlyph,
22105
+ {
22106
+ mark: node.rowMarks[idx],
22107
+ x: ROW_GLYPH_CX,
22108
+ y: -8,
22109
+ color: "var(--cdl-text, #1a1f2a)"
22110
+ }
22111
+ ),
22112
+ \u5370\u3067\u5206\u3051\u308B && node.rowMarks?.[idx]?.underline === true && // 主キーの下線 (#578)。 印の 2 軸とは別の段に載るので、外部キーの山形と重ねられる
22113
+ /* @__PURE__ */ jsxRuntime.jsx(
22114
+ "line",
22115
+ {
22116
+ "data-cdl-role": "node-row-underline",
22117
+ x1: ROW_PAD_X_PX + ROW_GLYPH_LEAD,
22118
+ y1: UNDERLINE_DROP,
22119
+ x2: ROW_PAD_X_PX + ROW_GLYPH_LEAD + displayWidth(s.left) * STORAGE_CHAR_ADVANCE_PX,
22120
+ y2: UNDERLINE_DROP,
22121
+ stroke: "var(--cdl-text, #1a1f2a)",
22122
+ strokeWidth: 2
22123
+ }
22124
+ ),
21726
22125
  /* @__PURE__ */ jsxRuntime.jsx(
21727
22126
  "text",
21728
22127
  {
21729
22128
  "data-cdl-role": "node-row",
21730
22129
  "data-cdl-row-index": idx,
21731
- x: ROW_PAD_X_PX,
22130
+ x: \u5370\u3067\u5206\u3051\u308B ? ROW_PAD_X_PX + ROW_GLYPH_LEAD : ROW_PAD_X_PX,
21732
22131
  fontSize: 26,
21733
- fontWeight: 600,
22132
+ fontWeight: \u5370\u3067\u5206\u3051\u308B ? 500 : 600,
21734
22133
  fill: "var(--cdl-text, #1a1f2a)",
21735
22134
  style: { fontFamily: "'JetBrains Mono', monospace" },
21736
22135
  children: s.left
@@ -21741,11 +22140,11 @@ function StorageNode({
21741
22140
  {
21742
22141
  "data-cdl-role": "node-row",
21743
22142
  "data-cdl-row-index": idx,
21744
- x: rightColX,
21745
- fontSize: 26,
21746
- fontWeight: highlightRows ? 700 : 600,
21747
- textAnchor: "start",
21748
- fill: highlightRows ? accent : "var(--cdl-text, #1a1f2a)",
22143
+ x: \u5370\u3067\u5206\u3051\u308B ? node.w - ROW_PAD_X_PX : rightColX,
22144
+ fontSize: \u5370\u3067\u5206\u3051\u308B ? ROW_TYPE_FONT : 26,
22145
+ fontWeight: \u5370\u3067\u5206\u3051\u308B ? 500 : highlightRows ? 700 : 600,
22146
+ textAnchor: \u5370\u3067\u5206\u3051\u308B ? "end" : "start",
22147
+ fill: \u5370\u3067\u5206\u3051\u308B ? "var(--cdl-text-dim, #5a6270)" : highlightRows ? accent : "var(--cdl-text, #1a1f2a)",
21749
22148
  style: { fontFamily: "'JetBrains Mono', monospace" },
21750
22149
  children: s.right
21751
22150
  }
@@ -28101,6 +28500,9 @@ function CdlNodeView({
28101
28500
  return /* @__PURE__ */ jsxRuntime.jsx(EventNode, { node: resolvedNode, active, progress });
28102
28501
  case "card":
28103
28502
  return /* @__PURE__ */ jsxRuntime.jsx(CardNode, { node: resolvedNode, active });
28503
+ // 時系列のやり取り (#578)。 1 つの箱が図を丸ごと描く = 縦が時間で、参加者は箱を持たない
28504
+ case "sequence-board":
28505
+ return /* @__PURE__ */ jsxRuntime.jsx(SequenceBoardNode, { node: resolvedNode, stateValues });
28104
28506
  // 図表 9 種は payload が `{signal}` を取れるので stateValues を渡す
28105
28507
  // (`render/payload-binding.ts` が描画直前に解決する)。 取れる欄は種別で違い、
28106
28508
  // 数と語を取る 7 種に対し、mind-map / tree-hierarchy は **名前だけ** を取る (#467)。
@@ -28428,36 +28830,47 @@ function CdlStage({
28428
28830
  /* @__PURE__ */ jsxRuntime.jsxs("defs", { children: [
28429
28831
  Object.entries(TONE).flatMap(
28430
28832
  ([tone, color]) => [["", 14], ["-sm", 10]].flatMap(
28431
- ([suffix, size]) => EDGE_HEADS.map((head) => {
28432
- const \u5F62 = EDGE_HEAD_SHAPE[head];
28433
- return /* @__PURE__ */ jsxRuntime.jsx(
28434
- "marker",
28435
- {
28436
- id: edgeHeadMarkerId(tone, head, suffix === "-sm"),
28437
- viewBox: "0 0 10 10",
28438
- refX: \u5F62.refX,
28439
- refY: "5",
28440
- markerWidth: size,
28441
- markerHeight: size,
28442
- markerUnits: "userSpaceOnUse",
28443
- orient: "auto-start-reverse",
28444
- children: /* @__PURE__ */ jsxRuntime.jsx(
28445
- "path",
28833
+ ([suffix, size]) => EDGE_HEADS.flatMap(
28834
+ (head) => (
28835
+ // 塗り方は形と別の軸 (#578)。 塗らない形では 2 つが同じ名前に畳まれるので、
28836
+ // `edgeHeadMarkerId` が返す名前で重複を外す
28837
+ EDGE_HEAD_FILLS.map((fill) => {
28838
+ const \u5F62 = EDGE_HEAD_SHAPE[head];
28839
+ const id = edgeHeadMarkerId(tone, head, suffix === "-sm", fill);
28840
+ if (\u5F62.d === "") return null;
28841
+ if (fill !== EDGE_HEAD_FILL_DEFAULT && !\u5F62.filled) return null;
28842
+ const \u5857 = edgeHeadPaint(head, fill, color);
28843
+ return /* @__PURE__ */ jsxRuntime.jsx(
28844
+ "marker",
28446
28845
  {
28447
- "data-cdl-role": "edge-arrowhead",
28448
- "data-cdl-edge-head": head,
28449
- d: \u5F62.d,
28450
- fill: \u5F62.filled ? color : "none",
28451
- stroke: \u5F62.filled ? "none" : color,
28452
- strokeWidth: \u5F62.filled ? void 0 : 1.6,
28453
- strokeLinecap: \u5F62.filled ? void 0 : "round",
28454
- strokeLinejoin: \u5F62.filled ? void 0 : "round"
28455
- }
28456
- )
28457
- },
28458
- `${tone}${suffix}-${head}`
28459
- );
28460
- })
28846
+ id,
28847
+ viewBox: `0 0 ${\u5F62.w} ${\u5F62.h}`,
28848
+ refX: \u5F62.refX,
28849
+ refY: \u5F62.h / 2,
28850
+ markerWidth: size * (\u5F62.scale ?? 1) * \u5F62.w / \u5F62.h,
28851
+ markerHeight: size * (\u5F62.scale ?? 1),
28852
+ markerUnits: "userSpaceOnUse",
28853
+ orient: "auto-start-reverse",
28854
+ children: /* @__PURE__ */ jsxRuntime.jsx(
28855
+ "path",
28856
+ {
28857
+ "data-cdl-role": "edge-arrowhead",
28858
+ "data-cdl-edge-head": head,
28859
+ "data-cdl-edge-head-fill": fill,
28860
+ d: \u5F62.d,
28861
+ fill: \u5857.fill,
28862
+ stroke: \u5857.stroke,
28863
+ strokeWidth: \u5857.strokeWidth,
28864
+ strokeLinecap: \u5857.strokeWidth === void 0 ? void 0 : "round",
28865
+ strokeLinejoin: \u5857.strokeWidth === void 0 ? void 0 : "round"
28866
+ }
28867
+ )
28868
+ },
28869
+ id
28870
+ );
28871
+ })
28872
+ )
28873
+ )
28461
28874
  )
28462
28875
  ),
28463
28876
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -29146,65 +29559,58 @@ function flow(preset) {
29146
29559
  };
29147
29560
  return api;
29148
29561
  }
29562
+ var SEQUENCE_MESSAGE_LOOK = {
29563
+ call: { style: "solid", head: "triangle" },
29564
+ return: { style: "dashed", head: "open" },
29565
+ fire: { style: "solid", head: "open" }
29566
+ };
29149
29567
  function sequence(preset) {
29150
- const laneW = preset.laneWidth ?? 340;
29151
29568
  const tone = preset.defaultTone ?? "accent";
29152
- const style = preset.defaultStyle ?? "solid";
29153
29569
  const b = diagram(preset.id, { topic: preset.topic });
29154
- const actorIds = /* @__PURE__ */ new Map();
29155
- const headerNodeIds = [];
29156
- preset.actors.forEach((label, i) => {
29157
- const id = slugify(label) || `actor-${i}`;
29158
- actorIds.set(label, id);
29159
- actorIds.set(id, id);
29160
- b.lane(id, { width: laneW, label, lifeline: true });
29161
- const actorW = Math.max(140, label.length * 22 + 52);
29162
- const headerId = `${id}-header`;
29163
- b.node(headerId, { lane: id, stack: 0, kind: "card", title: label, w: actorW, h: 72 });
29164
- headerNodeIds.push(headerId);
29165
- const spacerId = `${id}-spacer`;
29166
- b.node(spacerId, { lane: id, stack: 1, kind: "card", title: "", w: 2, h: 40 });
29570
+ const \u9762\u3005 = preset.actors.map(
29571
+ (a) => typeof a === "string" ? { name: a } : { name: a.name, ...a.subtitle ? { subtitle: a.subtitle } : {} }
29572
+ );
29573
+ const \u756A\u53F7 = /* @__PURE__ */ new Map();
29574
+ \u9762\u3005.forEach((a, i) => {
29575
+ \u756A\u53F7.set(a.name, i);
29576
+ \u756A\u53F7.set(slugify(a.name) || `actor-${i}`, i);
29167
29577
  });
29168
- const phaseActivate = [...headerNodeIds];
29169
- let stepIdx = 0;
29578
+ const \u8A00\u3065\u3066 = [];
29579
+ const \u8A9E = /* @__PURE__ */ new Map();
29170
29580
  const api = {
29171
- step(s) {
29172
- const fromLaneId = actorIds.get(s.from) ?? s.from;
29173
- const toLaneId = actorIds.get(s.to) ?? s.to;
29174
- const stack = stepIdx + 2;
29175
- const fromBoxId = `s${stepIdx}-${fromLaneId}`;
29176
- const toBoxId = `s${stepIdx}-${toLaneId}`;
29177
- b.node(fromBoxId, { lane: fromLaneId, stack, kind: "card", title: "", w: 2, h: 2 });
29178
- if (fromLaneId !== toLaneId) {
29179
- b.node(toBoxId, { lane: toLaneId, stack, kind: "card", title: "", w: 2, h: 2 });
29180
- }
29181
- const edgeId = `e${stepIdx}-${fromLaneId}-${toLaneId}`;
29182
- b.edge(fromBoxId, fromLaneId === toLaneId ? fromBoxId : toBoxId, {
29183
- id: edgeId,
29184
- label: s.label,
29185
- ...s.sub ? { sub: s.sub } : {},
29186
- tone: s.tone ?? tone,
29187
- style: s.style ?? style
29188
- });
29189
- phaseActivate.push(fromBoxId);
29190
- if (fromLaneId !== toLaneId) phaseActivate.push(toBoxId);
29191
- phaseActivate.push(edgeId);
29192
- stepIdx += 1;
29581
+ step(s2) {
29582
+ const from = \u756A\u53F7.get(s2.from) ?? 0;
29583
+ const to = \u756A\u53F7.get(s2.to) ?? 0;
29584
+ const kind = s2.kind ?? ((s2.style ?? preset.defaultStyle) === "dotted-flow" ? "return" : "call");
29585
+ \u8A00\u3065\u3066.push({ label: s2.label, from, to, kind });
29586
+ if (s2.sub) \u8A9E.set(\u8A00\u3065\u3066.length - 1, s2.sub);
29193
29587
  return api;
29194
29588
  },
29195
29589
  build() {
29196
- const footerStack = stepIdx + 2;
29197
- preset.actors.forEach((label) => {
29198
- const laneId = actorIds.get(label) ?? slugify(label);
29199
- const footerId = `${laneId}-footer`;
29200
- const actorW = Math.max(140, label.length * 22 + 52);
29201
- b.node(footerId, { lane: laneId, stack: footerStack, kind: "card", title: label, w: actorW, h: 72, role: "lifeline-footer" });
29202
- phaseActivate.push(footerId);
29590
+ const \u5E2F = preset.bands ? preset.bands.map((x) => ({ actor: \u756A\u53F7.get(x.actor) ?? 0, from: x.from, to: x.to })) : \u9762\u3005.map((_, i) => {
29591
+ const \u51FA\u756A = \u8A00\u3065\u3066.map((m2, idx) => m2.from === i || m2.to === i ? idx : -1).filter((idx) => idx >= 0);
29592
+ return \u51FA\u756A.length > 0 ? { actor: i, from: \u51FA\u756A[0], to: \u51FA\u756A[\u51FA\u756A.length - 1] } : null;
29593
+ }).filter((x) => x !== null);
29594
+ const data = { actors: \u9762\u3005, messages: \u8A00\u3065\u3066, bands: \u5E2F };
29595
+ const m = sequenceBoardMetrics(data);
29596
+ const laneId = "seq";
29597
+ b.lane(laneId, { width: m.w + LANE_PAD_MIN * 2 });
29598
+ b.node(preset.id, {
29599
+ lane: laneId,
29600
+ stack: 0,
29601
+ kind: "sequence-board",
29602
+ w: m.w,
29603
+ h: m.h,
29604
+ title: "",
29605
+ sequenceData: data,
29606
+ // 段が進むと濃さが移る。 書かない図では全部を濃く出す
29607
+ sequenceStep: `{${sequenceStepId()}}`,
29608
+ tone
29203
29609
  });
29204
29610
  b.phase(
29205
- "seq",
29611
+ "sequence",
29206
29612
  { duration: 2400, title: preset.topic, body: "sequence \u306E\u5168 message \u3092\u6642\u7CFB\u5217\u5C55\u958B\u3002" },
29207
- (p) => p.activate(...phaseActivate).badge("sequence")
29613
+ (p) => p.activate(preset.id).badge("sequence")
29208
29614
  );
29209
29615
  return b.build();
29210
29616
  }
@@ -29272,6 +29678,14 @@ function topology(preset) {
29272
29678
  };
29273
29679
  return api;
29274
29680
  }
29681
+ var ER_CARDINALITY_HEAD = {
29682
+ "1:1": { tail: "one", head: "one" },
29683
+ "1:N": { tail: "one", head: "many" },
29684
+ "N:1": { tail: "many", head: "one" },
29685
+ "N:M": { tail: "many", head: "many" },
29686
+ "0..1": { tail: "one", head: "zero-one" },
29687
+ "1..*": { tail: "one", head: "many" }
29688
+ };
29275
29689
  function er(preset) {
29276
29690
  const entityW = resolveStorageBoxW(preset.entityWidth, "er", "entityWidth");
29277
29691
  const tone = preset.defaultTone ?? "info";
@@ -29282,7 +29696,27 @@ function er(preset) {
29282
29696
  const api = {
29283
29697
  entity(e) {
29284
29698
  const laneId = `lane-${e.id}`;
29285
- const boxW = fitRowsWidth(entityW, e.rows);
29699
+ const \u9375 = (e.columns ?? []).filter((c) => c.pk === true);
29700
+ const \u5024 = (e.columns ?? []).filter((c) => c.pk !== true);
29701
+ const \u884C = [];
29702
+ const \u5370 = [];
29703
+ const \u8DB3\u3059 = (c) => {
29704
+ \u884C.push(`${c.name}: ${c.type}`);
29705
+ \u5370.push({
29706
+ shape: c.fk === true ? "chevron" : "square",
29707
+ filled: c.optional !== true,
29708
+ ...c.pk === true ? { underline: true } : {}
29709
+ });
29710
+ };
29711
+ \u9375.forEach(\u8DB3\u3059);
29712
+ if (\u9375.length > 0 && \u5024.length > 0) {
29713
+ \u884C.push("");
29714
+ \u5370.push(null);
29715
+ }
29716
+ \u5024.forEach(\u8DB3\u3059);
29717
+ const \u8A2D\u8A08\u306E\u5F62 = \u884C.length > 0;
29718
+ const rows = \u8A2D\u8A08\u306E\u5F62 ? \u884C : e.rows ?? [];
29719
+ const boxW = fitRowsWidth(entityW, rows);
29286
29720
  b.lane(laneId, { width: boxLaneW(boxW) });
29287
29721
  b.node(e.id, {
29288
29722
  lane: laneId,
@@ -29290,8 +29724,12 @@ function er(preset) {
29290
29724
  kind: "storage",
29291
29725
  w: boxW,
29292
29726
  title: e.title,
29293
- eyebrow: "\u30A8\u30F3\u30C6\u30A3\u30C6\u30A3",
29294
- rows: e.rows
29727
+ // 種類の札は設計の形では出さない (#578)。 「エンティティ」 と書いた札は全ての箱に
29728
+ // 同じ字が並ぶだけで、代わりに表の呼び名を題の下へ置く
29729
+ ...\u8A2D\u8A08\u306E\u5F62 ? {} : { eyebrow: "\u30A8\u30F3\u30C6\u30A3\u30C6\u30A3" },
29730
+ ...\u8A2D\u8A08\u306E\u5F62 && e.subtitle ? { subtitle: e.subtitle } : {},
29731
+ rows,
29732
+ ...\u8A2D\u8A08\u306E\u5F62 ? { rowMarks: \u5370 } : {}
29295
29733
  });
29296
29734
  entities.push(e.id);
29297
29735
  phaseActivate.push(e.id);
@@ -29300,14 +29738,19 @@ function er(preset) {
29300
29738
  relation(r) {
29301
29739
  const currentIdx = relIdx++;
29302
29740
  const edgeId = `rel-${currentIdx}-${r.from}-${r.to}`;
29303
- const label = r.label ?? r.cardinality;
29304
- const sub = r.label ? r.cardinality : void 0;
29741
+ const label = r.label ?? r.cardinality ?? "";
29742
+ const \u5370\u304C\u793A\u3059 = r.tailHead !== void 0 || r.head !== void 0;
29743
+ const sub = r.label && r.cardinality && !\u5370\u304C\u793A\u3059 ? r.cardinality : void 0;
29744
+ const \u65E2\u5B9A = r.cardinality ? ER_CARDINALITY_HEAD[r.cardinality] : void 0;
29305
29745
  b.edge(r.from, r.to, {
29306
29746
  id: edgeId,
29307
29747
  label,
29308
29748
  ...sub ? { sub } : {},
29309
29749
  tone: r.tone ?? tone,
29310
- style: "solid"
29750
+ style: r.style ?? "solid",
29751
+ // 端の印は両端に立つ (#578)。 箱に近い側が個数、その外側が任意か
29752
+ ...r.head ?? \u65E2\u5B9A?.head ? { head: r.head ?? \u65E2\u5B9A.head } : {},
29753
+ ...r.tailHead ?? \u65E2\u5B9A?.tail ? { tailHead: r.tailHead ?? \u65E2\u5B9A.tail } : {}
29311
29754
  });
29312
29755
  phaseActivate.push(edgeId);
29313
29756
  return api;
@@ -29323,49 +29766,114 @@ function er(preset) {
29323
29766
  };
29324
29767
  return api;
29325
29768
  }
29769
+ var MARK_BOX = 96;
29770
+ var FSM_ACTION_MARK = {
29771
+ entry: { shape: "chevron", filled: true },
29772
+ exit: { shape: "chevron", filled: false },
29773
+ do: { shape: "square", filled: true },
29774
+ internal: { shape: "square", filled: false }
29775
+ };
29326
29776
  function stateMachine(preset) {
29327
29777
  const stateW = resolveStateW(preset.stateWidth, "stateMachine");
29328
- const laneW = boxLaneW(stateW);
29329
29778
  const tone = preset.defaultTone ?? "accent";
29330
29779
  const b = diagram(preset.id, { topic: preset.topic });
29331
29780
  const phaseActivate = [];
29332
- let transIdx = 0;
29333
29781
  const stateOrder = /* @__PURE__ */ new Map();
29782
+ const \u72B6\u614B = [];
29783
+ const \u7BC0\u70B9 = [];
29784
+ const \u9077\u79FB = [];
29785
+ const \u4E26\u3073 = [];
29334
29786
  const api = {
29335
29787
  state(s) {
29336
- const laneId = `lane-${s.id}`;
29337
- b.lane(laneId, { width: laneW });
29338
- const eyebrow = s.initial ? "\u521D\u671F" : s.final ? "\u6700\u7D42" : "\u72B6\u614B";
29339
- b.node(s.id, {
29340
- lane: laneId,
29341
- stack: 0,
29342
- kind: "card",
29343
- w: stateW,
29344
- title: s.title,
29345
- eyebrow
29346
- });
29788
+ \u4E26\u3073.push({ \u7A2E: "\u72B6\u614B", i: \u72B6\u614B.length });
29789
+ \u72B6\u614B.push(s);
29347
29790
  stateOrder.set(s.id, stateOrder.size);
29348
- phaseActivate.push(s.id);
29791
+ return api;
29792
+ },
29793
+ mark(m) {
29794
+ \u4E26\u3073.push({ \u7A2E: "\u7BC0\u70B9", i: \u7BC0\u70B9.length });
29795
+ \u7BC0\u70B9.push(m);
29796
+ stateOrder.set(m.id, stateOrder.size);
29349
29797
  return api;
29350
29798
  },
29351
29799
  transition(t) {
29352
- const currentIdx = transIdx++;
29353
- const edgeId = `t${currentIdx}-${t.from}-${t.to}`;
29354
- const fromIdx = stateOrder.get(t.from) ?? 0;
29355
- const toIdx = stateOrder.get(t.to) ?? 0;
29356
- const isBack = toIdx < fromIdx;
29357
- b.edge(t.from, t.to, {
29358
- id: edgeId,
29359
- label: t.trigger,
29360
- ...t.guard ? { sub: t.guard } : {},
29361
- tone: t.tone ?? tone,
29362
- style: "solid",
29363
- ...isBack ? { routing: "back-detour" } : {}
29364
- });
29365
- phaseActivate.push(edgeId);
29800
+ \u9077\u79FB.push(t);
29366
29801
  return api;
29367
29802
  },
29368
29803
  build() {
29804
+ const \u8A2D\u8A08\u306E\u5F62 = \u72B6\u614B.some((s) => s.actions !== void 0 || s.subtitle !== void 0) || \u7BC0\u70B9.length > 0;
29805
+ const \u683C\u5B50 = \u72B6\u614B.some((s) => s.col !== void 0) || \u7BC0\u70B9.some((m) => m.col !== void 0);
29806
+ const \u4E2D\u8EAB = \u72B6\u614B.map((s) => {
29807
+ const \u884C = (s.actions ?? []).map((a) => a.note ? `${a.label}: ${a.note}` : a.label);
29808
+ const \u5370 = (s.actions ?? []).map((a) => FSM_ACTION_MARK[a.when]);
29809
+ const laneId = \u683C\u5B50 && s.col !== void 0 ? `col-${s.col}` : `lane-${s.id}`;
29810
+ const boxW = \u8A2D\u8A08\u306E\u5F62 ? fitRowsWidth(stateW, \u884C) : stateW;
29811
+ return { s, \u884C, \u5370, laneId, boxW, stack: \u683C\u5B50 ? s.row ?? 0 : 0 };
29812
+ });
29813
+ const \u7BC0\u70B9\u306E\u4E2D\u8EAB = \u7BC0\u70B9.map((m) => ({
29814
+ m,
29815
+ laneId: \u683C\u5B50 && m.col !== void 0 ? `col-${m.col}` : `lane-${m.id}`,
29816
+ stack: \u683C\u5B50 ? m.row ?? 0 : 0
29817
+ }));
29818
+ const \u5E2F\u5E45 = /* @__PURE__ */ new Map();
29819
+ for (const c of \u4E2D\u8EAB) \u5E2F\u5E45.set(c.laneId, Math.max(\u5E2F\u5E45.get(c.laneId) ?? 0, c.boxW));
29820
+ for (const c of \u7BC0\u70B9\u306E\u4E2D\u8EAB) \u5E2F\u5E45.set(c.laneId, Math.max(\u5E2F\u5E45.get(c.laneId) ?? 0, MARK_BOX));
29821
+ const \u6E08 = /* @__PURE__ */ new Set();
29822
+ const \u5E2F\u3092\u4F5C\u308B = (laneId) => {
29823
+ if (\u6E08.has(laneId)) return;
29824
+ \u6E08.add(laneId);
29825
+ b.lane(laneId, { width: boxLaneW(\u5E2F\u5E45.get(laneId) ?? stateW) });
29826
+ };
29827
+ for (const c of \u7BC0\u70B9\u306E\u4E2D\u8EAB) if (c.stack === 0) \u5E2F\u3092\u4F5C\u308B(c.laneId);
29828
+ for (const c of \u4E2D\u8EAB) \u5E2F\u3092\u4F5C\u308B(c.laneId);
29829
+ for (const c of \u7BC0\u70B9\u306E\u4E2D\u8EAB) \u5E2F\u3092\u4F5C\u308B(c.laneId);
29830
+ for (const o of \u4E26\u3073) {
29831
+ if (o.\u7A2E === "\u7BC0\u70B9") {
29832
+ const c2 = \u7BC0\u70B9\u306E\u4E2D\u8EAB[o.i];
29833
+ b.node(c2.m.id, {
29834
+ lane: c2.laneId,
29835
+ stack: c2.stack,
29836
+ kind: c2.m.kind === "start" ? "mark-start" : "mark-end",
29837
+ w: MARK_BOX,
29838
+ h: MARK_BOX,
29839
+ title: ""
29840
+ });
29841
+ phaseActivate.push(c2.m.id);
29842
+ continue;
29843
+ }
29844
+ const c = \u4E2D\u8EAB[o.i];
29845
+ const eyebrow = \u7BC0\u70B9.length > 0 ? void 0 : c.s.initial ? "\u521D\u671F" : c.s.final ? "\u6700\u7D42" : "\u72B6\u614B";
29846
+ b.node(c.s.id, {
29847
+ lane: c.laneId,
29848
+ stack: c.stack,
29849
+ kind: \u8A2D\u8A08\u306E\u5F62 ? "storage" : "card",
29850
+ w: c.boxW,
29851
+ title: c.s.title,
29852
+ ...eyebrow ? { eyebrow } : {},
29853
+ ...c.s.subtitle ? { subtitle: c.s.subtitle } : {},
29854
+ // 行を持たない状態でも欄を書く (#578)。 書かないとその箱だけ従来の意匠に落ち、
29855
+ // 呼び名が消える
29856
+ ...\u8A2D\u8A08\u306E\u5F62 ? { rows: c.\u884C, rowMarks: c.\u5370 } : {}
29857
+ });
29858
+ phaseActivate.push(c.s.id);
29859
+ }
29860
+ \u9077\u79FB.forEach((t, currentIdx) => {
29861
+ const edgeId = `t${currentIdx}-${t.from}-${t.to}`;
29862
+ const fromIdx = stateOrder.get(t.from) ?? 0;
29863
+ const toIdx = stateOrder.get(t.to) ?? 0;
29864
+ const isBack = toIdx < fromIdx;
29865
+ b.edge(t.from, t.to, {
29866
+ id: edgeId,
29867
+ label: t.trigger,
29868
+ ...t.guard ? { sub: t.guard } : {},
29869
+ tone: t.tone ?? tone,
29870
+ style: "solid",
29871
+ // 遷移の印は 1 種類だけ = 実線に開いた矢 (#578)。 線と印が分かれず、違いは語の中に入る
29872
+ head: t.head ?? "open",
29873
+ ...isBack ? { routing: "back-detour" } : {}
29874
+ });
29875
+ phaseActivate.push(edgeId);
29876
+ });
29369
29877
  b.phase(
29370
29878
  "fsm",
29371
29879
  { duration: 2400, title: preset.topic, body: "FSM \u306E\u5168 state + transition \u3092 visible \u5316\u3002" },
@@ -29429,72 +29937,103 @@ function infrastructure(preset) {
29429
29937
  };
29430
29938
  return api;
29431
29939
  }
29432
- var CLASS_RELATION_HEAD = {
29433
- extends: "triangle",
29434
- implements: "triangle",
29435
- uses: "open",
29436
- aggregates: "diamond",
29437
- composes: "diamond"
29940
+ var CLASS_RELATION_LOOK = {
29941
+ extends: { head: "triangle", fill: "hollow", tailHead: "none", tailFill: "solid", style: "solid" },
29942
+ implements: { head: "triangle", fill: "hollow", tailHead: "none", tailFill: "solid", style: "dashed" },
29943
+ aggregates: { head: "none", fill: "solid", tailHead: "diamond", tailFill: "hollow", style: "solid" },
29944
+ composes: { head: "none", fill: "solid", tailHead: "diamond", tailFill: "solid", style: "solid" },
29945
+ associates: { head: "open", fill: "solid", tailHead: "none", tailFill: "solid", style: "solid" },
29946
+ uses: { head: "open", fill: "solid", tailHead: "none", tailFill: "solid", style: "dashed" }
29438
29947
  };
29948
+ Object.fromEntries(
29949
+ Object.entries(CLASS_RELATION_LOOK).map(([k, v]) => [k, v.head])
29950
+ );
29951
+ function isPublic(row) {
29952
+ return !row.startsWith("-") && !row.startsWith("#");
29953
+ }
29954
+ function stripAccess(row) {
29955
+ const \u672C\u4F53 = isPublic(row) && !row.startsWith("+") ? row : row.slice(1);
29956
+ return \u672C\u4F53.replace("()", "");
29957
+ }
29439
29958
  function classDiagram(preset) {
29440
29959
  const classW = resolveStorageBoxW(preset.classWidth, "classDiagram", "classWidth");
29441
29960
  const tone = preset.defaultTone ?? "info";
29442
29961
  const b = diagram(preset.id, { topic: preset.topic });
29443
29962
  const classes = [];
29444
29963
  const phaseActivate = [];
29445
- let relIdx = 0;
29964
+ const \u7BB1 = [];
29965
+ const \u95A2\u4FC2 = [];
29446
29966
  const api = {
29447
29967
  class(c) {
29448
- const laneId = `lane-${c.id}`;
29449
- const rows = [];
29450
- if (c.attributes && c.attributes.length > 0) {
29451
- rows.push(...c.attributes);
29452
- }
29453
- if (c.methods && c.methods.length > 0) {
29454
- if (rows.length > 0) rows.push("\u2500\u2500\u2500");
29455
- rows.push(...c.methods);
29456
- }
29457
- const boxW = fitRowsWidth(classW, rows);
29458
- b.lane(laneId, { width: boxLaneW(boxW) });
29459
- b.node(c.id, {
29460
- lane: laneId,
29461
- stack: 0,
29462
- kind: "storage",
29463
- w: boxW,
29464
- title: c.title,
29465
- eyebrow: c.stereotype ?? "\u30AF\u30E9\u30B9",
29466
- ...rows.length > 0 ? { rows } : {}
29467
- });
29468
- classes.push(c.id);
29469
- phaseActivate.push(c.id);
29968
+ \u7BB1.push(c);
29470
29969
  return api;
29471
29970
  },
29472
29971
  relation(r) {
29473
- const currentIdx = relIdx++;
29474
- const edgeId = `cr-${currentIdx}-${r.from}-${r.to}`;
29475
- const label = r.label ?? r.type;
29476
- const sub = r.cardinality;
29477
- b.edge(r.from, r.to, {
29478
- id: edgeId,
29479
- label,
29480
- ...sub ? { sub } : {},
29481
- tone: r.tone ?? tone,
29482
- style: "solid",
29483
- // 関係の種類から端の形を決める (cdl#574)。 書いた形が勝つ
29484
- head: r.head ?? CLASS_RELATION_HEAD[r.type]
29485
- // CAR-531 SSOT ... 旧実装は `labelOffsetY: currentIdx % 2 === 0 ? -60 : 60` で
29486
- // 複数 relation label の path 沿い集中を強制分散していた。 CAR-430 で導入した
29487
- // engine 側 fan offset (FAN_STEP 40 world) が同 from / to 群を自動分散する現在、
29488
- // author 指定 ±60 は engine 分散と二重発火して extends label が SSOT
29489
- // LABEL_INIT_CLEARANCE (34、 CAR-630 で 26 → 34 復帰) を大幅に超える 114 world 離れる bug の直接原因になっていた。
29490
- // author 指定 offset を削除、 engine 側の LABEL_INIT_CLEARANCE + fan offset に一任する。
29491
- // 個別 relation で offset を再導入したい author は future extension (UmlRelation に
29492
- // labelOffsetY? を追加) で明示的に指定する経路を残す。
29493
- });
29494
- phaseActivate.push(edgeId);
29972
+ \u95A2\u4FC2.push(r);
29495
29973
  return api;
29496
29974
  },
29497
29975
  build() {
29976
+ const \u4E2D\u8EAB = \u7BB1.map((c) => {
29977
+ const rows = [];
29978
+ const rowMarks = [];
29979
+ for (const a of c.attributes ?? []) {
29980
+ rows.push(stripAccess(a));
29981
+ rowMarks.push({ shape: "square", filled: isPublic(a) });
29982
+ }
29983
+ if ((c.attributes?.length ?? 0) > 0 && (c.methods?.length ?? 0) > 0) {
29984
+ rows.push("");
29985
+ rowMarks.push(null);
29986
+ }
29987
+ for (const m of c.methods ?? []) {
29988
+ rows.push(stripAccess(m));
29989
+ rowMarks.push({ shape: "chevron", filled: isPublic(m) });
29990
+ }
29991
+ const boxW = fitRowsWidth(classW, rows);
29992
+ const laneId = c.col !== void 0 ? `col-${c.col}` : `lane-${c.id}`;
29993
+ return { c, rows, rowMarks, boxW, laneId, stack: c.col !== void 0 ? c.row ?? 0 : 0 };
29994
+ });
29995
+ const \u5E2F\u5E45 = /* @__PURE__ */ new Map();
29996
+ for (const m of \u4E2D\u8EAB) \u5E2F\u5E45.set(m.laneId, Math.max(\u5E2F\u5E45.get(m.laneId) ?? 0, m.boxW));
29997
+ const \u6E08 = /* @__PURE__ */ new Set();
29998
+ for (const m of \u4E2D\u8EAB) {
29999
+ if (\u6E08.has(m.laneId)) continue;
30000
+ \u6E08.add(m.laneId);
30001
+ b.lane(m.laneId, { width: boxLaneW(\u5E2F\u5E45.get(m.laneId) ?? m.boxW) });
30002
+ }
30003
+ for (const m of \u4E2D\u8EAB) {
30004
+ b.node(m.c.id, {
30005
+ lane: m.laneId,
30006
+ stack: m.stack,
30007
+ kind: "storage",
30008
+ w: m.boxW,
30009
+ title: m.c.title,
30010
+ // 種類の札は **書いた時だけ** 出す (#578)。 「クラス」 と書いた札は箱がクラスで
30011
+ // あることしか言わず、図の中の全ての箱に同じ字が並ぶ
30012
+ ...m.c.stereotype ? { eyebrow: m.c.stereotype } : {},
30013
+ ...m.rows.length > 0 ? { rows: m.rows, rowMarks: m.rowMarks } : {}
30014
+ });
30015
+ classes.push(m.c.id);
30016
+ phaseActivate.push(m.c.id);
30017
+ }
30018
+ \u95A2\u4FC2.forEach((r, currentIdx) => {
30019
+ const edgeId = `cr-${currentIdx}-${r.from}-${r.to}`;
30020
+ const \u898B\u305F\u76EE = CLASS_RELATION_LOOK[r.type];
30021
+ b.edge(r.from, r.to, {
30022
+ id: edgeId,
30023
+ label: r.label ?? r.type,
30024
+ ...r.cardinality ? { sub: r.cardinality } : {},
30025
+ tone: r.tone ?? tone,
30026
+ // 関係の種類から線と端の形を決める (cdl#574 / #578)。 書いた値が勝つ
30027
+ style: r.style ?? \u898B\u305F\u76EE.style,
30028
+ head: r.head ?? \u898B\u305F\u76EE.head,
30029
+ headFill: r.headFill ?? \u898B\u305F\u76EE.fill,
30030
+ tailHead: r.tailHead ?? \u898B\u305F\u76EE.tailHead,
30031
+ tailHeadFill: \u898B\u305F\u76EE.tailFill
30032
+ // CAR-531 SSOT ... label の分散は engine 側 (LABEL_INIT_CLEARANCE + fan offset) に
30033
+ // 一任する。 author 指定 ±60 は engine 分散と二重発火して label が大きく離れる
30034
+ });
30035
+ phaseActivate.push(edgeId);
30036
+ });
29498
30037
  b.phase(
29499
30038
  "class",
29500
30039
  { duration: 2400, title: preset.topic, body: "UML class \u5168 class + relation \u3092 visible \u5316\u3002" },
@@ -30009,6 +30548,7 @@ exports.ARROW_ENDPOINT_CENTER_TOL = ARROW_ENDPOINT_CENTER_TOL;
30009
30548
  exports.CHAIN_MIN_CLEARANCE = CHAIN_MIN_CLEARANCE;
30010
30549
  exports.CHAIN_SHIFT_MAX_ACCUMULATED = CHAIN_SHIFT_MAX_ACCUMULATED;
30011
30550
  exports.CHAIN_SHIFT_MAX_ITER = CHAIN_SHIFT_MAX_ITER;
30551
+ exports.CLASS_RELATION_LOOK = CLASS_RELATION_LOOK;
30012
30552
  exports.CLEARANCE_LABEL_LABEL = CLEARANCE_LABEL_LABEL;
30013
30553
  exports.CLEARANCE_LANE_LABEL = CLEARANCE_LANE_LABEL;
30014
30554
  exports.CLEARANCE_NODE_LABEL = CLEARANCE_NODE_LABEL;
@@ -30022,10 +30562,15 @@ exports.DIST_LABEL_PATH_MAX = DIST_LABEL_PATH_MAX;
30022
30562
  exports.DIST_LABEL_PATH_MIN = DIST_LABEL_PATH_MIN;
30023
30563
  exports.EDGE_HEADS = EDGE_HEADS;
30024
30564
  exports.EDGE_HEAD_DEFAULT = EDGE_HEAD_DEFAULT;
30565
+ exports.EDGE_HEAD_FILLS = EDGE_HEAD_FILLS;
30566
+ exports.EDGE_HEAD_FILL_DEFAULT = EDGE_HEAD_FILL_DEFAULT;
30025
30567
  exports.EDGE_LABEL_TEXT = EDGE_LABEL_TEXT;
30026
30568
  exports.EDGE_STUB_OUT = EDGE_STUB_OUT;
30569
+ exports.EDGE_STYLES = EDGE_STYLES;
30570
+ exports.ER_CARDINALITY_HEAD = ER_CARDINALITY_HEAD;
30027
30571
  exports.FAN_GAP = FAN_GAP;
30028
30572
  exports.FONT_RENDER_TOLERANCE_WORLD = FONT_RENDER_TOLERANCE_WORLD;
30573
+ exports.FSM_ACTION_MARK = FSM_ACTION_MARK;
30029
30574
  exports.LABEL_INIT_CLEARANCE = LABEL_INIT_CLEARANCE;
30030
30575
  exports.LABEL_INIT_CLEARANCE_DOTTED_FLOW = LABEL_INIT_CLEARANCE_DOTTED_FLOW;
30031
30576
  exports.LABEL_SIZE_TOLERANCE_WORLD = LABEL_SIZE_TOLERANCE_WORLD;
@@ -30039,6 +30584,7 @@ exports.NEAR_COLLISION_POLICY = NEAR_COLLISION_POLICY;
30039
30584
  exports.NODE_FRAME_SELECTOR = NODE_FRAME_SELECTOR;
30040
30585
  exports.NODE_KINDS = NODE_KINDS;
30041
30586
  exports.ROW_GAP_VARIANCE_TOL = ROW_GAP_VARIANCE_TOL;
30587
+ exports.SEQUENCE_MESSAGE_LOOK = SEQUENCE_MESSAGE_LOOK;
30042
30588
  exports.SPEC_CLEARANCE_POLICY = SPEC_CLEARANCE_POLICY;
30043
30589
  exports.STAGE_SVG_SELECTOR = STAGE_SVG_SELECTOR;
30044
30590
  exports.TARGET_LABEL_PATH_DIST = TARGET_LABEL_PATH_DIST;
@@ -30113,6 +30659,8 @@ exports.resolveTarget = resolveTarget;
30113
30659
  exports.resolveTargetAll = resolveTargetAll;
30114
30660
  exports.segmentsIntersect = segmentsIntersect2;
30115
30661
  exports.sequence = sequence;
30662
+ exports.sequenceBoardMetrics = sequenceBoardMetrics;
30663
+ exports.sequenceStepId = sequenceStepId;
30116
30664
  exports.signal = signal;
30117
30665
  exports.stateMachine = stateMachine;
30118
30666
  exports.stateMachine2 = stateMachine2;