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