@cardenelabs/cdl 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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",
@@ -11804,6 +11813,7 @@ var GLYPH_CAP_RATIO = 0.72;
11804
11813
  var ROW_FONT_STORAGE = 26;
11805
11814
  var ROW_FONT_GENERIC = 21;
11806
11815
  var STORAGE_TITLE_BASELINE = 78;
11816
+ var SUBTITLE_ROW_SHIFT = 46;
11807
11817
  var STORAGE_ROW_DIVIDER_Y = Math.round(
11808
11818
  (STORAGE_TITLE_BASELINE + (ROW_TOP_STORAGE - ROW_FONT_STORAGE * GLYPH_CAP_RATIO)) / 2
11809
11819
  );
@@ -13407,7 +13417,10 @@ var TOKENS = {
13407
13417
  // 印は箱いっぱいに描く (線が箱の縁で止まるため) ので、この値が丸の直径になる。
13408
13418
  // 箱としての最小 (80x40) より大きく、名前を持つ箱 (card 320x150) よりは明らかに小さい
13409
13419
  "mark-start": { w: 96, h: 96 },
13410
- "mark-end": { w: 96, h: 96 }
13420
+ "mark-end": { w: 96, h: 96 },
13421
+ // 時系列のやり取り (#578)。 実寸は中身から決まる (`sequenceBoardMetrics`) ので、
13422
+ // ここは書かなかった時の受け皿
13423
+ "sequence-board": { w: 960, h: 640 }
13411
13424
  },
13412
13425
  // row gap (stack 間の vertical gap、 lane 内 / 跨ぎ共通)
13413
13426
  // edge label が node の上 36 px に配置されるため、 boxH 68 + 36 + 余裕 = 100 必要。
@@ -15070,8 +15083,12 @@ function requiredRowsWidth(rows, kind) {
15070
15083
  function autoStorageHeight(n, defaultH) {
15071
15084
  if (n.kind !== "storage") return n.h ?? defaultH;
15072
15085
  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);
15086
+ const \u5370\u3042\u308A = n.rowMarks !== void 0;
15087
+ const \u547C\u3073\u540D\u306E\u5206 = \u5370\u3042\u308A && n.subtitle ? SUBTITLE_ROW_SHIFT : 0;
15088
+ if (!n.rows || n.rows.length === 0) return 150 + \u547C\u3073\u540D\u306E\u5206;
15089
+ const \u5FC5\u8981 = requiredRowsHeight("storage", n.rows.length) ?? defaultH;
15090
+ if (\u5370\u3042\u308A) return \u5FC5\u8981 + \u547C\u3073\u540D\u306E\u5206;
15091
+ return Math.max(defaultH, \u5FC5\u8981);
15075
15092
  }
15076
15093
  function autoRowsWidth(n, defaultW) {
15077
15094
  if (n.w != null) return n.w;
@@ -15159,8 +15176,11 @@ function layoutNodes(diag, lanes) {
15159
15176
  continue;
15160
15177
  }
15161
15178
  const cx = lane.x + lane.width / 2;
15162
- const cy = rowCy.get(n.stack);
15163
- const h = n.h ?? rowH.get(n.stack);
15179
+ const \u6BB5\u306E\u82AF = rowCy.get(n.stack);
15180
+ const \u6BB5\u306E\u9AD8\u3055 = rowH.get(n.stack);
15181
+ const \u81EA\u524D\u306E\u9AD8\u3055 = n.rowMarks !== void 0;
15182
+ const h = n.h ?? (\u81EA\u524D\u306E\u9AD8\u3055 ? autoStorageHeight(n, size.h) : \u6BB5\u306E\u9AD8\u3055);
15183
+ const cy = \u81EA\u524D\u306E\u9AD8\u3055 ? \u6BB5\u306E\u82AF - \u6BB5\u306E\u9AD8\u3055 / 2 + h / 2 : \u6BB5\u306E\u82AF;
15164
15184
  const w = autoRowsWidth(n, size.w);
15165
15185
  out.push({
15166
15186
  ...n,
@@ -15489,10 +15509,25 @@ var NODE_KINDS = [
15489
15509
  // 流れの始まりと終わりの印 2 種 (#560) ... 塗った丸と輪で囲んだ丸。
15490
15510
  // 状態遷移図が使う。 印そのものが意味を持つので文字は描かない
15491
15511
  "mark-start",
15492
- "mark-end"
15512
+ "mark-end",
15513
+ // 時系列のやり取り (#578) ... 1 つの箱が図を丸ごと描く。 縦が時間で、参加者は箱を持たない
15514
+ "sequence-board"
15493
15515
  ];
15516
+ var EDGE_STYLES = ["solid", "dotted-flow", "dashed"];
15494
15517
  var EDGE_HEAD_DEFAULT = "triangle";
15495
- var EDGE_HEADS = ["triangle", "diamond", "open", "crow"];
15518
+ var EDGE_HEADS = [
15519
+ "none",
15520
+ "triangle",
15521
+ "diamond",
15522
+ "open",
15523
+ "crow",
15524
+ "one",
15525
+ "zero-one",
15526
+ "many",
15527
+ "zero-many"
15528
+ ];
15529
+ var EDGE_HEAD_FILL_DEFAULT = "solid";
15530
+ var EDGE_HEAD_FILLS = ["solid", "hollow"];
15496
15531
 
15497
15532
  // src/anim/core/easing.ts
15498
15533
  function cubicBezier(x1, y1, x2, y2) {
@@ -15987,7 +16022,7 @@ function pathSubpath(d, progress) {
15987
16022
  }
15988
16023
 
15989
16024
  // src/render/template-fields.ts
15990
- var TEXT_FIELDS = ["title", "eyebrow", "subtitle", "value", "wBind", "hBind", "visibleIf"];
16025
+ var TEXT_FIELDS = ["title", "eyebrow", "subtitle", "value", "wBind", "hBind", "visibleIf", "sequenceStep"];
15991
16026
  var LIST_FIELDS = ["rows"];
15992
16027
  var NUMERIC_FIELDS = ["opacity", "renderOffsetX", "renderOffsetY"];
15993
16028
  var SHAPE_TEMPLATE_FIELDS = ["source", "radius", "fillProgress", "angle", "level", "rotation", "fill"];
@@ -18312,6 +18347,7 @@ function runAxes(laid, diag, violations, counts, push, profile, skippedAxes) {
18312
18347
  if (!isRenderedNode(n)) continue;
18313
18348
  for (const row of n.rows) {
18314
18349
  if (DIVIDER_ONLY.test(row)) continue;
18350
+ if (n.rowMarks !== void 0) continue;
18315
18351
  if (!ROW_FORMAT.test(row)) {
18316
18352
  push("row-format", `node "${n.id}" row "${row}" \u304C "key: value" \u5F62\u5F0F\u3067\u306A\u3044`);
18317
18353
  }
@@ -18643,9 +18679,11 @@ function runAxes(laid, diag, violations, counts, push, profile, skippedAxes) {
18643
18679
  for (const [stack, group] of byStack) {
18644
18680
  if (group.length < 2) continue;
18645
18681
  const cy0 = group[0].cy;
18682
+ const top0 = cy0 - (group[0].h ?? 0) / 2;
18646
18683
  for (const n of group.slice(1)) {
18647
18684
  const diff = Math.abs(n.cy - cy0);
18648
- if (diff > MIN_ROW_ALIGNMENT_TOLERANCE) {
18685
+ const topDiff = Math.abs(n.cy - (n.h ?? 0) / 2 - top0);
18686
+ if (diff > MIN_ROW_ALIGNMENT_TOLERANCE && topDiff > MIN_ROW_ALIGNMENT_TOLERANCE) {
18649
18687
  push(
18650
18688
  "row-alignment",
18651
18689
  `stack=${stack} row \u5185 node "${n.id}" cy=${n.cy.toFixed(1)} \u304C row \u57FA\u6E96 cy=${cy0.toFixed(1)} \u3068 ${diff.toFixed(1)} world \u5DEE (\u8A31\u5BB9 ${MIN_ROW_ALIGNMENT_TOLERANCE})`
@@ -19694,15 +19732,18 @@ function runAxes(laid, diag, violations, counts, push, profile, skippedAxes) {
19694
19732
  if (group.length < 3) continue;
19695
19733
  const sorted = [...group].sort((a, b) => a.cy - b.cy);
19696
19734
  const gaps = [];
19735
+ const tops = [];
19697
19736
  for (let i = 1; i < sorted.length; i++) {
19698
19737
  const prev = sorted[i - 1];
19699
19738
  const cur = sorted[i];
19700
19739
  gaps.push(cur.cy - cur.h / 2 - (prev.cy + prev.h / 2));
19740
+ tops.push(cur.cy - cur.h / 2 - (prev.cy - prev.h / 2));
19701
19741
  }
19702
19742
  const maxGap = Math.max(...gaps);
19703
19743
  const minGap = Math.min(...gaps);
19704
19744
  const variance = maxGap - minGap;
19705
- if (variance > COLUMN_GAP_VARIANCE_TOL) {
19745
+ const topVariance = Math.max(...tops) - Math.min(...tops);
19746
+ if (variance > COLUMN_GAP_VARIANCE_TOL && topVariance > COLUMN_GAP_VARIANCE_TOL) {
19706
19747
  push(
19707
19748
  "column-gap-uniform",
19708
19749
  `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 +21217,42 @@ function StateDiffCard({
21176
21217
 
21177
21218
  // src/render/edge-head.ts
21178
21219
  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 }
21220
+ // 何も描かない端。 marker 自体を出さないので `d` は読まれない
21221
+ none: { d: "", w: 10, h: 10, refX: 0, filled: false },
21222
+ triangle: { d: "M 0 0 L 10 5 L 0 10 z", w: 10, h: 10, refX: 9, filled: true },
21223
+ diamond: { d: "M 0 5 L 5 0 L 10 5 L 5 10 z", w: 10, h: 10, refX: 9, filled: true },
21224
+ open: { d: "M 1 0 L 9 5 L 1 10", w: 10, h: 10, refX: 9, filled: false },
21225
+ 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 },
21226
+ // ER の端。 **箱に近い側 (x が大きい側) が個数、その外側が任意か** (#578)。
21227
+ //
21228
+ // 枠は 24 × 10 で、線の先端に合わせるのは x=24 = 箱の縁。 marker は経路の向きに回るので、
21229
+ // 行き先側でも出どころ側でも「箱に近い側」 が同じ端になる。
21230
+ //
21231
+ // 三又の真ん中の枝は **関係の線そのもの** が兼ねる。 別に引くと同じ所に 2 本重なって太く見える
21232
+ 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 },
21233
+ "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 },
21234
+ 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 },
21235
+ "zero-many": {
21236
+ 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",
21237
+ w: 24,
21238
+ h: 10,
21239
+ refX: 24,
21240
+ scale: 1.8,
21241
+ filled: false
21242
+ }
21183
21243
  };
21184
- function edgeHeadMarkerId(tone, head, small) {
21244
+ function edgeHeadMarkerId(tone, head, small, fill = EDGE_HEAD_FILL_DEFAULT) {
21185
21245
  const \u5C0F = small ? "-sm" : "";
21186
- return head === EDGE_HEAD_DEFAULT ? `cdl-arrow-${tone}${\u5C0F}` : `cdl-arrow-${tone}-${head}${\u5C0F}`;
21246
+ const \u5857 = fill === EDGE_HEAD_FILL_DEFAULT || !EDGE_HEAD_SHAPE[head].filled ? "" : `-${fill}`;
21247
+ return head === EDGE_HEAD_DEFAULT && \u5857 === "" ? `cdl-arrow-${tone}${\u5C0F}` : `cdl-arrow-${tone}-${head}${\u5857}${\u5C0F}`;
21248
+ }
21249
+ function edgeHeadPaint(head, fill, color) {
21250
+ const \u5F62 = EDGE_HEAD_SHAPE[head];
21251
+ if (!\u5F62.filled) return { fill: "none", stroke: color, strokeWidth: 1.6 };
21252
+ if (fill === "hollow") {
21253
+ return { fill: "var(--cdl-node-fill, #ffffff)", stroke: color, strokeWidth: 1.4 };
21254
+ }
21255
+ return { fill: color, stroke: "none" };
21187
21256
  }
21188
21257
  function opacityAttr(opacity) {
21189
21258
  return opacity < 1 ? opacity : void 0;
@@ -21198,7 +21267,13 @@ function CdlEdgeView({
21198
21267
  stateValues
21199
21268
  }) {
21200
21269
  const color = toneColor(edge.tone);
21201
- const marker = hasTone(edge.tone) ? edgeHeadMarkerId(edge.tone, edge.head ?? EDGE_HEAD_DEFAULT, !active) : "cdl-arrow-muted";
21270
+ const \u7AEF\u306E\u540D\u524D = (h, f) => {
21271
+ const \u5F62 = h ?? EDGE_HEAD_DEFAULT;
21272
+ if (\u5F62 === "none") return void 0;
21273
+ return hasTone(edge.tone) ? edgeHeadMarkerId(edge.tone, \u5F62, !active, f ?? EDGE_HEAD_FILL_DEFAULT) : "cdl-arrow-muted";
21274
+ };
21275
+ const marker = \u7AEF\u306E\u540D\u524D(edge.head, edge.headFill);
21276
+ const tailMarker = edge.tailHead === void 0 ? void 0 : \u7AEF\u306E\u540D\u524D(edge.tailHead, edge.tailHeadFill);
21202
21277
  const style = edge.style ?? "solid";
21203
21278
  const isDotted = style === "dotted-flow";
21204
21279
  const isPassThrough = isDotted && Boolean(edge.dThrough);
@@ -21246,6 +21321,8 @@ function CdlEdgeView({
21246
21321
  progress,
21247
21322
  color,
21248
21323
  marker,
21324
+ tailMarker,
21325
+ dashed: style === "dashed",
21249
21326
  stateValues
21250
21327
  }
21251
21328
  ),
@@ -21271,6 +21348,8 @@ function SolidEdgePath({
21271
21348
  progress,
21272
21349
  color,
21273
21350
  marker,
21351
+ tailMarker,
21352
+ dashed,
21274
21353
  stateValues
21275
21354
  }) {
21276
21355
  const visibleD = active ? pathSubpath(edge.d, progress) : edge.d;
@@ -21278,7 +21357,7 @@ function SolidEdgePath({
21278
21357
  const boundWidth = edge.widthBind && stateValues ? resolveNumericAttr(edge.widthBind, stateValues, defaultWidth) : defaultWidth;
21279
21358
  const boundStroke = edge.strokeBind && stateValues ? interpolate(edge.strokeBind, stateValues) : color;
21280
21359
  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";
21360
+ const dashArray = dashed ? "10 8" : active ? boundDashOffset !== void 0 ? "8 6" : void 0 : "4 5";
21282
21361
  return /* @__PURE__ */ jsx(
21283
21362
  "path",
21284
21363
  {
@@ -21291,7 +21370,8 @@ function SolidEdgePath({
21291
21370
  strokeDasharray: dashArray,
21292
21371
  strokeDashoffset: boundDashOffset,
21293
21372
  strokeLinecap: "round",
21294
- markerEnd: `url(#${marker})`,
21373
+ markerEnd: marker === void 0 ? void 0 : `url(#${marker})`,
21374
+ markerStart: tailMarker === void 0 ? void 0 : `url(#${tailMarker})`,
21295
21375
  style: {
21296
21376
  transition: "stroke 280ms ease, stroke-opacity 280ms ease, stroke-width 280ms ease"
21297
21377
  }
@@ -21503,7 +21583,8 @@ function CardNode({ node, active }) {
21503
21583
  return /* @__PURE__ */ jsx("g", { transform: `translate(${x} ${y})` });
21504
21584
  }
21505
21585
  const compact = isCompactBox(node.h);
21506
- const titleY = compact ? node.h / 2 + 8 : 86;
21586
+ const \u5C0F\u578B\u306B\u547C\u3073\u540D = compact && Boolean(node.subtitle);
21587
+ const titleY = compact ? \u5C0F\u578B\u306B\u547C\u3073\u540D ? node.h / 2 - 2 : node.h / 2 + 8 : 86;
21507
21588
  const titleX = compact ? node.w / 2 : 26;
21508
21589
  const paddingX = compact ? 16 : 52;
21509
21590
  const availableW = node.w - paddingX;
@@ -21540,9 +21621,262 @@ function CardNode({ node, active }) {
21540
21621
  children: node.title
21541
21622
  }
21542
21623
  ),
21543
- !compact && node.subtitle && /* @__PURE__ */ jsx("text", { x: 26, y: 124, fontSize: subtitleSize, fill: "var(--cdl-text-dim, #5a6270)", children: node.subtitle })
21624
+ !compact && node.subtitle && /* @__PURE__ */ jsx("text", { x: 26, y: 124, fontSize: subtitleSize, fill: "var(--cdl-text-dim, #5a6270)", children: node.subtitle }),
21625
+ \u5C0F\u578B\u306B\u547C\u3073\u540D && /* @__PURE__ */ jsx(
21626
+ "text",
21627
+ {
21628
+ "data-cdl-role": "node-subtitle",
21629
+ x: node.w / 2,
21630
+ y: node.h / 2 + 24,
21631
+ fontSize: Math.min(subtitleSize, 18),
21632
+ textAnchor: "middle",
21633
+ fill: "var(--cdl-text-dim, #5a6270)",
21634
+ children: node.subtitle
21635
+ }
21636
+ )
21637
+ ] });
21638
+ }
21639
+
21640
+ // src/kinds/sequence-board-metrics.ts
21641
+ var SEQ_EDGE = 16;
21642
+ var SEQ_ROW = 64;
21643
+ var SEQ_GUTTER_PAD = 40;
21644
+ var SEQ_GUTTER_GAP = 40;
21645
+ var SEQ_HEAD = 96;
21646
+ var SEQ_ROW_PAD = 56;
21647
+ var SEQ_COL_PAD = 80;
21648
+ var SEQ_BAND_W = 52;
21649
+ var SEQ_BAND_PAD = 24;
21650
+ var SEQ_NAME = 24;
21651
+ var SEQ_SUB = 20;
21652
+ var SEQ_LABEL = 26;
21653
+ var SEQ_DOT = 7;
21654
+ var SEQ_SELF_W = 52;
21655
+ var SEQ_COL_MIN = 200;
21656
+ var g4 = (v) => Math.round(v / 4) * 4;
21657
+ function sequenceStepId() {
21658
+ return "seq_step";
21659
+ }
21660
+ function sequenceBoardMetrics(data) {
21661
+ const \u540D\u5E45 = data.actors.map(
21662
+ (a) => Math.max(textWidth(a.name, SEQ_NAME), a.subtitle ? textWidth(a.subtitle, SEQ_SUB) : 0)
21663
+ );
21664
+ const colW = g4(Math.max(SEQ_COL_MIN, Math.max(0, ...\u540D\u5E45) + SEQ_COL_PAD));
21665
+ const \u8A9E\u5E45 = Math.max(0, ...data.messages.map((m) => textWidth(m.label, SEQ_LABEL)));
21666
+ const gutter = g4(SEQ_EDGE + SEQ_GUTTER_PAD + \u8A9E\u5E45 + SEQ_GUTTER_GAP);
21667
+ const headY = SEQ_EDGE + SEQ_HEAD;
21668
+ const rowY = (i) => headY + SEQ_ROW_PAD + i * SEQ_ROW;
21669
+ const \u6700\u7D42 = data.messages.length > 0 ? rowY(data.messages.length - 1) : headY;
21670
+ const bottom = \u6700\u7D42 + SEQ_ROW_PAD;
21671
+ const cx = (i) => gutter + colW / 2 + i * colW;
21672
+ const w = g4(cx(Math.max(0, data.actors.length - 1)) + colW / 2 + SEQ_EDGE);
21673
+ const h = g4(bottom + SEQ_EDGE);
21674
+ return { w, h, gutter, colW, headY, cx, rowY, bottom };
21675
+ }
21676
+ function SequenceBoardNode({
21677
+ node,
21678
+ stateValues
21679
+ }) {
21680
+ const data = node.sequenceData;
21681
+ if (!data) return /* @__PURE__ */ jsx("g", { "data-cdl-role": "sequence-board" });
21682
+ const m = sequenceBoardMetrics(data);
21683
+ const x = node.cx - node.w / 2;
21684
+ const y = node.cy - node.h / 2;
21685
+ const \u73FE\u5728 = (() => {
21686
+ if (!node.sequenceStep) return null;
21687
+ const v = Number.parseInt(interpolate(node.sequenceStep, stateValues ?? {}).trim(), 10);
21688
+ return Number.isFinite(v) ? v : null;
21689
+ })();
21690
+ const \u6731 = "var(--cdl-tone-accent, #2d6a8f)";
21691
+ const \u58A8 = "var(--cdl-text-2, #4a5162)";
21692
+ const \u898B\u305F\u76EE = (i) => {
21693
+ if (\u73FE\u5728 === null) return { opacity: 1, color: \u6731 };
21694
+ if (i > \u73FE\u5728) return { opacity: 0, color: \u6731 };
21695
+ return i === \u73FE\u5728 ? { opacity: 1, color: \u6731 } : { opacity: 1, color: \u58A8 };
21696
+ };
21697
+ return /* @__PURE__ */ jsxs("g", { "data-cdl-role": "sequence-board", transform: `translate(${x} ${y})`, children: [
21698
+ data.bands.map((b, i) => {
21699
+ if (\u73FE\u5728 !== null && b.from > \u73FE\u5728) return null;
21700
+ const \u4E0B = \u73FE\u5728 === null ? b.to : Math.min(b.to, \u73FE\u5728);
21701
+ return /* @__PURE__ */ jsx(
21702
+ "rect",
21703
+ {
21704
+ "data-cdl-role": "sequence-band",
21705
+ x: m.cx(b.actor) - SEQ_BAND_W / 2,
21706
+ y: m.rowY(b.from) - SEQ_BAND_PAD / 2,
21707
+ width: SEQ_BAND_W,
21708
+ height: m.rowY(\u4E0B) - m.rowY(b.from) + SEQ_BAND_PAD,
21709
+ rx: 4,
21710
+ fill: \u6731,
21711
+ fillOpacity: 0.12
21712
+ },
21713
+ `band-${i}`
21714
+ );
21715
+ }),
21716
+ data.actors.map((_, i) => /* @__PURE__ */ jsx(
21717
+ "line",
21718
+ {
21719
+ "data-cdl-role": "sequence-thread",
21720
+ x1: m.cx(i),
21721
+ y1: m.headY,
21722
+ x2: m.cx(i),
21723
+ y2: m.bottom,
21724
+ stroke: "var(--cdl-divider, #d4d4d8)",
21725
+ strokeWidth: 3
21726
+ },
21727
+ `thread-${i}`
21728
+ )),
21729
+ data.actors.map((a, i) => /* @__PURE__ */ jsxs("g", { children: [
21730
+ /* @__PURE__ */ jsx(
21731
+ "text",
21732
+ {
21733
+ "data-cdl-role": "sequence-actor",
21734
+ x: m.cx(i),
21735
+ y: SEQ_EDGE + 44,
21736
+ fontSize: SEQ_NAME,
21737
+ fontWeight: 700,
21738
+ textAnchor: "middle",
21739
+ fill: "var(--cdl-text, #1a1f2a)",
21740
+ style: { fontFamily: "'JetBrains Mono', monospace" },
21741
+ children: a.name
21742
+ }
21743
+ ),
21744
+ a.subtitle && /* @__PURE__ */ jsx(
21745
+ "text",
21746
+ {
21747
+ "data-cdl-role": "sequence-actor-subtitle",
21748
+ x: m.cx(i),
21749
+ y: SEQ_EDGE + 76,
21750
+ fontSize: SEQ_SUB,
21751
+ textAnchor: "middle",
21752
+ fill: "var(--cdl-text-dim, #5a6270)",
21753
+ children: a.subtitle
21754
+ }
21755
+ )
21756
+ ] }, `head-${i}`)),
21757
+ /* @__PURE__ */ jsx(
21758
+ "line",
21759
+ {
21760
+ "data-cdl-role": "sequence-rule",
21761
+ x1: SEQ_EDGE,
21762
+ y1: m.headY,
21763
+ x2: m.w - SEQ_EDGE,
21764
+ y2: m.headY,
21765
+ stroke: "var(--cdl-divider, #d4d4d8)",
21766
+ strokeWidth: 2
21767
+ }
21768
+ ),
21769
+ data.messages.map((msg, i) => /* @__PURE__ */ jsx(SequenceRow, { msg, yy: m.rowY(i), cx: m.cx, ...\u898B\u305F\u76EE(i) }, `msg-${i}`))
21770
+ ] });
21771
+ }
21772
+ function SequenceRow({
21773
+ msg,
21774
+ yy,
21775
+ cx,
21776
+ opacity,
21777
+ color
21778
+ }) {
21779
+ const \u7834 = msg.kind === "return";
21780
+ const \u5857\u308B = msg.kind === "call";
21781
+ const \u51FA = cx(msg.from);
21782
+ const \u81EA\u5206\u3078 = msg.from === msg.to;
21783
+ const dir = \u81EA\u5206\u3078 ? 1 : Math.sign(cx(msg.to) - \u51FA);
21784
+ return /* @__PURE__ */ jsxs("g", { "data-cdl-role": "sequence-message", "data-cdl-message-kind": msg.kind, opacity, children: [
21785
+ /* @__PURE__ */ jsx(
21786
+ "text",
21787
+ {
21788
+ "data-cdl-role": "sequence-label",
21789
+ x: SEQ_EDGE + SEQ_GUTTER_PAD,
21790
+ y: yy + 8,
21791
+ fontSize: SEQ_LABEL,
21792
+ fontWeight: 500,
21793
+ fill: "var(--cdl-text, #1a1f2a)",
21794
+ children: msg.label
21795
+ }
21796
+ ),
21797
+ \u81EA\u5206\u3078 ? (
21798
+ // 自分宛て。 相手がいないので糸の右へ小さく回して戻る
21799
+ /* @__PURE__ */ jsx(
21800
+ "path",
21801
+ {
21802
+ "data-cdl-role": "sequence-line",
21803
+ 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}`,
21804
+ fill: "none",
21805
+ stroke: color,
21806
+ strokeWidth: 2.5,
21807
+ strokeLinecap: "round"
21808
+ }
21809
+ )
21810
+ ) : /* @__PURE__ */ jsx(
21811
+ "path",
21812
+ {
21813
+ "data-cdl-role": "sequence-line",
21814
+ d: `M ${\u51FA + dir * 12} ${yy} H ${cx(msg.to) - dir * 14}`,
21815
+ fill: "none",
21816
+ stroke: color,
21817
+ strokeWidth: 2.5,
21818
+ strokeLinecap: "round",
21819
+ strokeDasharray: \u7834 ? "10 8" : void 0
21820
+ }
21821
+ ),
21822
+ /* @__PURE__ */ jsx(
21823
+ Arrow,
21824
+ {
21825
+ x: \u81EA\u5206\u3078 ? \u51FA + 14 : cx(msg.to),
21826
+ y: \u81EA\u5206\u3078 ? yy + 16 : yy,
21827
+ dir: \u81EA\u5206\u3078 ? -1 : dir,
21828
+ filled: \u5857\u308B,
21829
+ color
21830
+ }
21831
+ ),
21832
+ /* @__PURE__ */ jsx(
21833
+ "circle",
21834
+ {
21835
+ "data-cdl-role": "sequence-source",
21836
+ cx: \u51FA,
21837
+ cy: \u81EA\u5206\u3078 ? yy - 16 : yy,
21838
+ r: SEQ_DOT,
21839
+ fill: color
21840
+ }
21841
+ )
21544
21842
  ] });
21545
21843
  }
21844
+ function Arrow({
21845
+ x,
21846
+ y,
21847
+ dir,
21848
+ filled,
21849
+ color
21850
+ }) {
21851
+ const \u4E08 = 18;
21852
+ const \u534A = 8.5;
21853
+ const \u5148 = x;
21854
+ const \u80F4 = x - dir * \u4E08;
21855
+ return filled ? /* @__PURE__ */ jsx(
21856
+ "path",
21857
+ {
21858
+ "data-cdl-role": "sequence-head",
21859
+ "data-cdl-head-fill": "solid",
21860
+ d: `M ${\u5148} ${y} L ${\u80F4} ${y - \u534A} L ${\u80F4} ${y + \u534A} Z`,
21861
+ fill: color,
21862
+ stroke: color,
21863
+ strokeWidth: 3,
21864
+ strokeLinejoin: "round"
21865
+ }
21866
+ ) : /* @__PURE__ */ jsx(
21867
+ "path",
21868
+ {
21869
+ "data-cdl-role": "sequence-head",
21870
+ "data-cdl-head-fill": "open",
21871
+ d: `M ${\u80F4} ${y - \u534A} L ${\u5148} ${y} L ${\u80F4} ${y + \u534A}`,
21872
+ fill: "none",
21873
+ stroke: color,
21874
+ strokeWidth: 2.5,
21875
+ strokeLinecap: "round",
21876
+ strokeLinejoin: "round"
21877
+ }
21878
+ );
21879
+ }
21546
21880
  function EventNode({
21547
21881
  node,
21548
21882
  active,
@@ -21644,6 +21978,29 @@ function FunctionNode({ node, active }) {
21644
21978
  node.subtitle && /* @__PURE__ */ jsx("text", { x: 26, y: 126, fontSize: 20, fill: "var(--cdl-text-dim, #5a6270)", children: node.subtitle })
21645
21979
  ] });
21646
21980
  }
21981
+ var ROW_GLYPH_HALF = 7;
21982
+ var ROW_GLYPH_CX = ROW_PAD_X_PX + ROW_GLYPH_HALF + 1;
21983
+ var ROW_GLYPH_LEAD = 40;
21984
+ var ROW_TYPE_FONT = 24;
21985
+ var SUBTITLE_BASELINE = 108;
21986
+ var SUBTITLE_FONT = 22;
21987
+ var UNDERLINE_DROP = 7;
21988
+ function RowGlyph({ mark, x, y, color }) {
21989
+ const \u534A = ROW_GLYPH_HALF;
21990
+ 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`;
21991
+ return /* @__PURE__ */ jsx(
21992
+ "path",
21993
+ {
21994
+ "data-cdl-role": "node-row-glyph",
21995
+ "data-cdl-row-glyph": `${mark.shape}-${mark.filled ? "filled" : "hollow"}`,
21996
+ d,
21997
+ fill: mark.filled ? color : "var(--cdl-node-fill, #ffffff)",
21998
+ stroke: color,
21999
+ strokeWidth: mark.filled ? 0 : 2.4,
22000
+ strokeLinejoin: "round"
22001
+ }
22002
+ );
22003
+ }
21647
22004
  function StorageNode({
21648
22005
  node,
21649
22006
  active,
@@ -21661,6 +22018,8 @@ function StorageNode({
21661
22018
  const leftEndDrawn = ROW_PAD_X_PX + leftMaxWidth * STORAGE_CHAR_ADVANCE_PX;
21662
22019
  const columnDividerX = (leftEndDrawn + rightColX) / 2;
21663
22020
  const columnDividerFits = columnDividerX <= node.w - ROW_PAD_X_PX;
22021
+ const \u5370\u3067\u5206\u3051\u308B = node.rowMarks !== void 0;
22022
+ const \u884C\u306E\u4E0B\u3052 = \u5370\u3067\u5206\u3051\u308B && node.subtitle ? SUBTITLE_ROW_SHIFT : 0;
21664
22023
  const lastRowBottom = Math.min(
21665
22024
  ROW_TOP_STORAGE + (rows.length - 1) * ROW_PITCH_STORAGE + (rowGlyphDepth("storage") ?? 0),
21666
22025
  node.h - ROW_PAD_X_PX
@@ -21681,6 +22040,19 @@ function StorageNode({
21681
22040
  }
21682
22041
  ),
21683
22042
  node.eyebrow && /* @__PURE__ */ jsx("text", { x: 26, y: 38, fontSize: 18, fontWeight: 700, letterSpacing: 2, fill: accent, children: node.eyebrow }),
22043
+ \u5370\u3067\u5206\u3051\u308B && node.subtitle && // 呼び名は **印で分ける箱でだけ** 出す (#578)。 従来の箱は題の下に区切り線が来るので
22044
+ // 置き場が無い
22045
+ /* @__PURE__ */ jsx(
22046
+ "text",
22047
+ {
22048
+ "data-cdl-role": "node-subtitle",
22049
+ x: ROW_PAD_X_PX,
22050
+ y: SUBTITLE_BASELINE,
22051
+ fontSize: SUBTITLE_FONT,
22052
+ fill: "var(--cdl-text-dim, #5a6270)",
22053
+ children: node.subtitle
22054
+ }
22055
+ ),
21684
22056
  /* @__PURE__ */ jsx(
21685
22057
  "text",
21686
22058
  {
@@ -21695,7 +22067,7 @@ function StorageNode({
21695
22067
  children: node.title
21696
22068
  }
21697
22069
  ),
21698
- rows.length > 0 && /* @__PURE__ */ jsx(
22070
+ rows.length > 0 && !\u5370\u3067\u5206\u3051\u308B && /* @__PURE__ */ jsx(
21699
22071
  "line",
21700
22072
  {
21701
22073
  "data-cdl-role": "node-row-divider",
@@ -21707,7 +22079,7 @@ function StorageNode({
21707
22079
  strokeWidth: 1.25
21708
22080
  }
21709
22081
  ),
21710
- rows.length > 0 && hasRightColumn && columnDividerFits && /* @__PURE__ */ jsx(
22082
+ rows.length > 0 && hasRightColumn && columnDividerFits && !\u5370\u3067\u5206\u3051\u308B && /* @__PURE__ */ jsx(
21711
22083
  "line",
21712
22084
  {
21713
22085
  "data-cdl-role": "node-column-divider",
@@ -21720,15 +22092,37 @@ function StorageNode({
21720
22092
  }
21721
22093
  ),
21722
22094
  splits.map((s, idx) => {
21723
- return /* @__PURE__ */ jsxs("g", { transform: `translate(0 ${rowBaselineY("storage", idx)})`, children: [
22095
+ return /* @__PURE__ */ jsxs("g", { transform: `translate(0 ${(rowBaselineY("storage", idx) ?? 0) + \u884C\u306E\u4E0B\u3052})`, children: [
22096
+ \u5370\u3067\u5206\u3051\u308B && node.rowMarks?.[idx] != null && /* @__PURE__ */ jsx(
22097
+ RowGlyph,
22098
+ {
22099
+ mark: node.rowMarks[idx],
22100
+ x: ROW_GLYPH_CX,
22101
+ y: -8,
22102
+ color: "var(--cdl-text, #1a1f2a)"
22103
+ }
22104
+ ),
22105
+ \u5370\u3067\u5206\u3051\u308B && node.rowMarks?.[idx]?.underline === true && // 主キーの下線 (#578)。 印の 2 軸とは別の段に載るので、外部キーの山形と重ねられる
22106
+ /* @__PURE__ */ jsx(
22107
+ "line",
22108
+ {
22109
+ "data-cdl-role": "node-row-underline",
22110
+ x1: ROW_PAD_X_PX + ROW_GLYPH_LEAD,
22111
+ y1: UNDERLINE_DROP,
22112
+ x2: ROW_PAD_X_PX + ROW_GLYPH_LEAD + displayWidth(s.left) * STORAGE_CHAR_ADVANCE_PX,
22113
+ y2: UNDERLINE_DROP,
22114
+ stroke: "var(--cdl-text, #1a1f2a)",
22115
+ strokeWidth: 2
22116
+ }
22117
+ ),
21724
22118
  /* @__PURE__ */ jsx(
21725
22119
  "text",
21726
22120
  {
21727
22121
  "data-cdl-role": "node-row",
21728
22122
  "data-cdl-row-index": idx,
21729
- x: ROW_PAD_X_PX,
22123
+ x: \u5370\u3067\u5206\u3051\u308B ? ROW_PAD_X_PX + ROW_GLYPH_LEAD : ROW_PAD_X_PX,
21730
22124
  fontSize: 26,
21731
- fontWeight: 600,
22125
+ fontWeight: \u5370\u3067\u5206\u3051\u308B ? 500 : 600,
21732
22126
  fill: "var(--cdl-text, #1a1f2a)",
21733
22127
  style: { fontFamily: "'JetBrains Mono', monospace" },
21734
22128
  children: s.left
@@ -21739,11 +22133,11 @@ function StorageNode({
21739
22133
  {
21740
22134
  "data-cdl-role": "node-row",
21741
22135
  "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)",
22136
+ x: \u5370\u3067\u5206\u3051\u308B ? node.w - ROW_PAD_X_PX : rightColX,
22137
+ fontSize: \u5370\u3067\u5206\u3051\u308B ? ROW_TYPE_FONT : 26,
22138
+ fontWeight: \u5370\u3067\u5206\u3051\u308B ? 500 : highlightRows ? 700 : 600,
22139
+ textAnchor: \u5370\u3067\u5206\u3051\u308B ? "end" : "start",
22140
+ fill: \u5370\u3067\u5206\u3051\u308B ? "var(--cdl-text-dim, #5a6270)" : highlightRows ? accent : "var(--cdl-text, #1a1f2a)",
21747
22141
  style: { fontFamily: "'JetBrains Mono', monospace" },
21748
22142
  children: s.right
21749
22143
  }
@@ -28099,6 +28493,9 @@ function CdlNodeView({
28099
28493
  return /* @__PURE__ */ jsx(EventNode, { node: resolvedNode, active, progress });
28100
28494
  case "card":
28101
28495
  return /* @__PURE__ */ jsx(CardNode, { node: resolvedNode, active });
28496
+ // 時系列のやり取り (#578)。 1 つの箱が図を丸ごと描く = 縦が時間で、参加者は箱を持たない
28497
+ case "sequence-board":
28498
+ return /* @__PURE__ */ jsx(SequenceBoardNode, { node: resolvedNode, stateValues });
28102
28499
  // 図表 9 種は payload が `{signal}` を取れるので stateValues を渡す
28103
28500
  // (`render/payload-binding.ts` が描画直前に解決する)。 取れる欄は種別で違い、
28104
28501
  // 数と語を取る 7 種に対し、mind-map / tree-hierarchy は **名前だけ** を取る (#467)。
@@ -28426,36 +28823,47 @@ function CdlStage({
28426
28823
  /* @__PURE__ */ jsxs("defs", { children: [
28427
28824
  Object.entries(TONE).flatMap(
28428
28825
  ([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",
28826
+ ([suffix, size]) => EDGE_HEADS.flatMap(
28827
+ (head) => (
28828
+ // 塗り方は形と別の軸 (#578)。 塗らない形では 2 つが同じ名前に畳まれるので、
28829
+ // `edgeHeadMarkerId` が返す名前で重複を外す
28830
+ EDGE_HEAD_FILLS.map((fill) => {
28831
+ const \u5F62 = EDGE_HEAD_SHAPE[head];
28832
+ const id = edgeHeadMarkerId(tone, head, suffix === "-sm", fill);
28833
+ if (\u5F62.d === "") return null;
28834
+ if (fill !== EDGE_HEAD_FILL_DEFAULT && !\u5F62.filled) return null;
28835
+ const \u5857 = edgeHeadPaint(head, fill, color);
28836
+ return /* @__PURE__ */ jsx(
28837
+ "marker",
28444
28838
  {
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
- })
28839
+ id,
28840
+ viewBox: `0 0 ${\u5F62.w} ${\u5F62.h}`,
28841
+ refX: \u5F62.refX,
28842
+ refY: \u5F62.h / 2,
28843
+ markerWidth: size * (\u5F62.scale ?? 1) * \u5F62.w / \u5F62.h,
28844
+ markerHeight: size * (\u5F62.scale ?? 1),
28845
+ markerUnits: "userSpaceOnUse",
28846
+ orient: "auto-start-reverse",
28847
+ children: /* @__PURE__ */ jsx(
28848
+ "path",
28849
+ {
28850
+ "data-cdl-role": "edge-arrowhead",
28851
+ "data-cdl-edge-head": head,
28852
+ "data-cdl-edge-head-fill": fill,
28853
+ d: \u5F62.d,
28854
+ fill: \u5857.fill,
28855
+ stroke: \u5857.stroke,
28856
+ strokeWidth: \u5857.strokeWidth,
28857
+ strokeLinecap: \u5857.strokeWidth === void 0 ? void 0 : "round",
28858
+ strokeLinejoin: \u5857.strokeWidth === void 0 ? void 0 : "round"
28859
+ }
28860
+ )
28861
+ },
28862
+ id
28863
+ );
28864
+ })
28865
+ )
28866
+ )
28459
28867
  )
28460
28868
  ),
28461
28869
  /* @__PURE__ */ jsx(
@@ -29144,65 +29552,58 @@ function flow(preset) {
29144
29552
  };
29145
29553
  return api;
29146
29554
  }
29555
+ var SEQUENCE_MESSAGE_LOOK = {
29556
+ call: { style: "solid", head: "triangle" },
29557
+ return: { style: "dashed", head: "open" },
29558
+ fire: { style: "solid", head: "open" }
29559
+ };
29147
29560
  function sequence(preset) {
29148
- const laneW = preset.laneWidth ?? 340;
29149
29561
  const tone = preset.defaultTone ?? "accent";
29150
- const style = preset.defaultStyle ?? "solid";
29151
29562
  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 });
29563
+ const \u9762\u3005 = preset.actors.map(
29564
+ (a) => typeof a === "string" ? { name: a } : { name: a.name, ...a.subtitle ? { subtitle: a.subtitle } : {} }
29565
+ );
29566
+ const \u756A\u53F7 = /* @__PURE__ */ new Map();
29567
+ \u9762\u3005.forEach((a, i) => {
29568
+ \u756A\u53F7.set(a.name, i);
29569
+ \u756A\u53F7.set(slugify(a.name) || `actor-${i}`, i);
29165
29570
  });
29166
- const phaseActivate = [...headerNodeIds];
29167
- let stepIdx = 0;
29571
+ const \u8A00\u3065\u3066 = [];
29572
+ const \u8A9E = /* @__PURE__ */ new Map();
29168
29573
  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;
29574
+ step(s2) {
29575
+ const from = \u756A\u53F7.get(s2.from) ?? 0;
29576
+ const to = \u756A\u53F7.get(s2.to) ?? 0;
29577
+ const kind = s2.kind ?? ((s2.style ?? preset.defaultStyle) === "dotted-flow" ? "return" : "call");
29578
+ \u8A00\u3065\u3066.push({ label: s2.label, from, to, kind });
29579
+ if (s2.sub) \u8A9E.set(\u8A00\u3065\u3066.length - 1, s2.sub);
29191
29580
  return api;
29192
29581
  },
29193
29582
  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);
29583
+ 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) => {
29584
+ const \u51FA\u756A = \u8A00\u3065\u3066.map((m2, idx) => m2.from === i || m2.to === i ? idx : -1).filter((idx) => idx >= 0);
29585
+ return \u51FA\u756A.length > 0 ? { actor: i, from: \u51FA\u756A[0], to: \u51FA\u756A[\u51FA\u756A.length - 1] } : null;
29586
+ }).filter((x) => x !== null);
29587
+ const data = { actors: \u9762\u3005, messages: \u8A00\u3065\u3066, bands: \u5E2F };
29588
+ const m = sequenceBoardMetrics(data);
29589
+ const laneId = "seq";
29590
+ b.lane(laneId, { width: m.w + LANE_PAD_MIN * 2 });
29591
+ b.node(preset.id, {
29592
+ lane: laneId,
29593
+ stack: 0,
29594
+ kind: "sequence-board",
29595
+ w: m.w,
29596
+ h: m.h,
29597
+ title: "",
29598
+ sequenceData: data,
29599
+ // 段が進むと濃さが移る。 書かない図では全部を濃く出す
29600
+ sequenceStep: `{${sequenceStepId()}}`,
29601
+ tone
29201
29602
  });
29202
29603
  b.phase(
29203
- "seq",
29604
+ "sequence",
29204
29605
  { duration: 2400, title: preset.topic, body: "sequence \u306E\u5168 message \u3092\u6642\u7CFB\u5217\u5C55\u958B\u3002" },
29205
- (p) => p.activate(...phaseActivate).badge("sequence")
29606
+ (p) => p.activate(preset.id).badge("sequence")
29206
29607
  );
29207
29608
  return b.build();
29208
29609
  }
@@ -29270,6 +29671,14 @@ function topology(preset) {
29270
29671
  };
29271
29672
  return api;
29272
29673
  }
29674
+ var ER_CARDINALITY_HEAD = {
29675
+ "1:1": { tail: "one", head: "one" },
29676
+ "1:N": { tail: "one", head: "many" },
29677
+ "N:1": { tail: "many", head: "one" },
29678
+ "N:M": { tail: "many", head: "many" },
29679
+ "0..1": { tail: "one", head: "zero-one" },
29680
+ "1..*": { tail: "one", head: "many" }
29681
+ };
29273
29682
  function er(preset) {
29274
29683
  const entityW = resolveStorageBoxW(preset.entityWidth, "er", "entityWidth");
29275
29684
  const tone = preset.defaultTone ?? "info";
@@ -29280,7 +29689,27 @@ function er(preset) {
29280
29689
  const api = {
29281
29690
  entity(e) {
29282
29691
  const laneId = `lane-${e.id}`;
29283
- const boxW = fitRowsWidth(entityW, e.rows);
29692
+ const \u9375 = (e.columns ?? []).filter((c) => c.pk === true);
29693
+ const \u5024 = (e.columns ?? []).filter((c) => c.pk !== true);
29694
+ const \u884C = [];
29695
+ const \u5370 = [];
29696
+ const \u8DB3\u3059 = (c) => {
29697
+ \u884C.push(`${c.name}: ${c.type}`);
29698
+ \u5370.push({
29699
+ shape: c.fk === true ? "chevron" : "square",
29700
+ filled: c.optional !== true,
29701
+ ...c.pk === true ? { underline: true } : {}
29702
+ });
29703
+ };
29704
+ \u9375.forEach(\u8DB3\u3059);
29705
+ if (\u9375.length > 0 && \u5024.length > 0) {
29706
+ \u884C.push("");
29707
+ \u5370.push(null);
29708
+ }
29709
+ \u5024.forEach(\u8DB3\u3059);
29710
+ const \u8A2D\u8A08\u306E\u5F62 = \u884C.length > 0;
29711
+ const rows = \u8A2D\u8A08\u306E\u5F62 ? \u884C : e.rows ?? [];
29712
+ const boxW = fitRowsWidth(entityW, rows);
29284
29713
  b.lane(laneId, { width: boxLaneW(boxW) });
29285
29714
  b.node(e.id, {
29286
29715
  lane: laneId,
@@ -29288,8 +29717,12 @@ function er(preset) {
29288
29717
  kind: "storage",
29289
29718
  w: boxW,
29290
29719
  title: e.title,
29291
- eyebrow: "\u30A8\u30F3\u30C6\u30A3\u30C6\u30A3",
29292
- rows: e.rows
29720
+ // 種類の札は設計の形では出さない (#578)。 「エンティティ」 と書いた札は全ての箱に
29721
+ // 同じ字が並ぶだけで、代わりに表の呼び名を題の下へ置く
29722
+ ...\u8A2D\u8A08\u306E\u5F62 ? {} : { eyebrow: "\u30A8\u30F3\u30C6\u30A3\u30C6\u30A3" },
29723
+ ...\u8A2D\u8A08\u306E\u5F62 && e.subtitle ? { subtitle: e.subtitle } : {},
29724
+ rows,
29725
+ ...\u8A2D\u8A08\u306E\u5F62 ? { rowMarks: \u5370 } : {}
29293
29726
  });
29294
29727
  entities.push(e.id);
29295
29728
  phaseActivate.push(e.id);
@@ -29298,14 +29731,19 @@ function er(preset) {
29298
29731
  relation(r) {
29299
29732
  const currentIdx = relIdx++;
29300
29733
  const edgeId = `rel-${currentIdx}-${r.from}-${r.to}`;
29301
- const label = r.label ?? r.cardinality;
29302
- const sub = r.label ? r.cardinality : void 0;
29734
+ const label = r.label ?? r.cardinality ?? "";
29735
+ const \u5370\u304C\u793A\u3059 = r.tailHead !== void 0 || r.head !== void 0;
29736
+ const sub = r.label && r.cardinality && !\u5370\u304C\u793A\u3059 ? r.cardinality : void 0;
29737
+ const \u65E2\u5B9A = r.cardinality ? ER_CARDINALITY_HEAD[r.cardinality] : void 0;
29303
29738
  b.edge(r.from, r.to, {
29304
29739
  id: edgeId,
29305
29740
  label,
29306
29741
  ...sub ? { sub } : {},
29307
29742
  tone: r.tone ?? tone,
29308
- style: "solid"
29743
+ style: r.style ?? "solid",
29744
+ // 端の印は両端に立つ (#578)。 箱に近い側が個数、その外側が任意か
29745
+ ...r.head ?? \u65E2\u5B9A?.head ? { head: r.head ?? \u65E2\u5B9A.head } : {},
29746
+ ...r.tailHead ?? \u65E2\u5B9A?.tail ? { tailHead: r.tailHead ?? \u65E2\u5B9A.tail } : {}
29309
29747
  });
29310
29748
  phaseActivate.push(edgeId);
29311
29749
  return api;
@@ -29321,49 +29759,114 @@ function er(preset) {
29321
29759
  };
29322
29760
  return api;
29323
29761
  }
29762
+ var MARK_BOX = 96;
29763
+ var FSM_ACTION_MARK = {
29764
+ entry: { shape: "chevron", filled: true },
29765
+ exit: { shape: "chevron", filled: false },
29766
+ do: { shape: "square", filled: true },
29767
+ internal: { shape: "square", filled: false }
29768
+ };
29324
29769
  function stateMachine(preset) {
29325
29770
  const stateW = resolveStateW(preset.stateWidth, "stateMachine");
29326
- const laneW = boxLaneW(stateW);
29327
29771
  const tone = preset.defaultTone ?? "accent";
29328
29772
  const b = diagram(preset.id, { topic: preset.topic });
29329
29773
  const phaseActivate = [];
29330
- let transIdx = 0;
29331
29774
  const stateOrder = /* @__PURE__ */ new Map();
29775
+ const \u72B6\u614B = [];
29776
+ const \u7BC0\u70B9 = [];
29777
+ const \u9077\u79FB = [];
29778
+ const \u4E26\u3073 = [];
29332
29779
  const api = {
29333
29780
  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
- });
29781
+ \u4E26\u3073.push({ \u7A2E: "\u72B6\u614B", i: \u72B6\u614B.length });
29782
+ \u72B6\u614B.push(s);
29345
29783
  stateOrder.set(s.id, stateOrder.size);
29346
- phaseActivate.push(s.id);
29784
+ return api;
29785
+ },
29786
+ mark(m) {
29787
+ \u4E26\u3073.push({ \u7A2E: "\u7BC0\u70B9", i: \u7BC0\u70B9.length });
29788
+ \u7BC0\u70B9.push(m);
29789
+ stateOrder.set(m.id, stateOrder.size);
29347
29790
  return api;
29348
29791
  },
29349
29792
  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);
29793
+ \u9077\u79FB.push(t);
29364
29794
  return api;
29365
29795
  },
29366
29796
  build() {
29797
+ const \u8A2D\u8A08\u306E\u5F62 = \u72B6\u614B.some((s) => s.actions !== void 0 || s.subtitle !== void 0) || \u7BC0\u70B9.length > 0;
29798
+ const \u683C\u5B50 = \u72B6\u614B.some((s) => s.col !== void 0) || \u7BC0\u70B9.some((m) => m.col !== void 0);
29799
+ const \u4E2D\u8EAB = \u72B6\u614B.map((s) => {
29800
+ const \u884C = (s.actions ?? []).map((a) => a.note ? `${a.label}: ${a.note}` : a.label);
29801
+ const \u5370 = (s.actions ?? []).map((a) => FSM_ACTION_MARK[a.when]);
29802
+ const laneId = \u683C\u5B50 && s.col !== void 0 ? `col-${s.col}` : `lane-${s.id}`;
29803
+ const boxW = \u8A2D\u8A08\u306E\u5F62 ? fitRowsWidth(stateW, \u884C) : stateW;
29804
+ return { s, \u884C, \u5370, laneId, boxW, stack: \u683C\u5B50 ? s.row ?? 0 : 0 };
29805
+ });
29806
+ const \u7BC0\u70B9\u306E\u4E2D\u8EAB = \u7BC0\u70B9.map((m) => ({
29807
+ m,
29808
+ laneId: \u683C\u5B50 && m.col !== void 0 ? `col-${m.col}` : `lane-${m.id}`,
29809
+ stack: \u683C\u5B50 ? m.row ?? 0 : 0
29810
+ }));
29811
+ const \u5E2F\u5E45 = /* @__PURE__ */ new Map();
29812
+ for (const c of \u4E2D\u8EAB) \u5E2F\u5E45.set(c.laneId, Math.max(\u5E2F\u5E45.get(c.laneId) ?? 0, c.boxW));
29813
+ for (const c of \u7BC0\u70B9\u306E\u4E2D\u8EAB) \u5E2F\u5E45.set(c.laneId, Math.max(\u5E2F\u5E45.get(c.laneId) ?? 0, MARK_BOX));
29814
+ const \u6E08 = /* @__PURE__ */ new Set();
29815
+ const \u5E2F\u3092\u4F5C\u308B = (laneId) => {
29816
+ if (\u6E08.has(laneId)) return;
29817
+ \u6E08.add(laneId);
29818
+ b.lane(laneId, { width: boxLaneW(\u5E2F\u5E45.get(laneId) ?? stateW) });
29819
+ };
29820
+ for (const c of \u7BC0\u70B9\u306E\u4E2D\u8EAB) if (c.stack === 0) \u5E2F\u3092\u4F5C\u308B(c.laneId);
29821
+ for (const c of \u4E2D\u8EAB) \u5E2F\u3092\u4F5C\u308B(c.laneId);
29822
+ for (const c of \u7BC0\u70B9\u306E\u4E2D\u8EAB) \u5E2F\u3092\u4F5C\u308B(c.laneId);
29823
+ for (const o of \u4E26\u3073) {
29824
+ if (o.\u7A2E === "\u7BC0\u70B9") {
29825
+ const c2 = \u7BC0\u70B9\u306E\u4E2D\u8EAB[o.i];
29826
+ b.node(c2.m.id, {
29827
+ lane: c2.laneId,
29828
+ stack: c2.stack,
29829
+ kind: c2.m.kind === "start" ? "mark-start" : "mark-end",
29830
+ w: MARK_BOX,
29831
+ h: MARK_BOX,
29832
+ title: ""
29833
+ });
29834
+ phaseActivate.push(c2.m.id);
29835
+ continue;
29836
+ }
29837
+ const c = \u4E2D\u8EAB[o.i];
29838
+ const eyebrow = \u7BC0\u70B9.length > 0 ? void 0 : c.s.initial ? "\u521D\u671F" : c.s.final ? "\u6700\u7D42" : "\u72B6\u614B";
29839
+ b.node(c.s.id, {
29840
+ lane: c.laneId,
29841
+ stack: c.stack,
29842
+ kind: \u8A2D\u8A08\u306E\u5F62 ? "storage" : "card",
29843
+ w: c.boxW,
29844
+ title: c.s.title,
29845
+ ...eyebrow ? { eyebrow } : {},
29846
+ ...c.s.subtitle ? { subtitle: c.s.subtitle } : {},
29847
+ // 行を持たない状態でも欄を書く (#578)。 書かないとその箱だけ従来の意匠に落ち、
29848
+ // 呼び名が消える
29849
+ ...\u8A2D\u8A08\u306E\u5F62 ? { rows: c.\u884C, rowMarks: c.\u5370 } : {}
29850
+ });
29851
+ phaseActivate.push(c.s.id);
29852
+ }
29853
+ \u9077\u79FB.forEach((t, currentIdx) => {
29854
+ const edgeId = `t${currentIdx}-${t.from}-${t.to}`;
29855
+ const fromIdx = stateOrder.get(t.from) ?? 0;
29856
+ const toIdx = stateOrder.get(t.to) ?? 0;
29857
+ const isBack = toIdx < fromIdx;
29858
+ b.edge(t.from, t.to, {
29859
+ id: edgeId,
29860
+ label: t.trigger,
29861
+ ...t.guard ? { sub: t.guard } : {},
29862
+ tone: t.tone ?? tone,
29863
+ style: "solid",
29864
+ // 遷移の印は 1 種類だけ = 実線に開いた矢 (#578)。 線と印が分かれず、違いは語の中に入る
29865
+ head: t.head ?? "open",
29866
+ ...isBack ? { routing: "back-detour" } : {}
29867
+ });
29868
+ phaseActivate.push(edgeId);
29869
+ });
29367
29870
  b.phase(
29368
29871
  "fsm",
29369
29872
  { duration: 2400, title: preset.topic, body: "FSM \u306E\u5168 state + transition \u3092 visible \u5316\u3002" },
@@ -29427,72 +29930,103 @@ function infrastructure(preset) {
29427
29930
  };
29428
29931
  return api;
29429
29932
  }
29430
- var CLASS_RELATION_HEAD = {
29431
- extends: "triangle",
29432
- implements: "triangle",
29433
- uses: "open",
29434
- aggregates: "diamond",
29435
- composes: "diamond"
29933
+ var CLASS_RELATION_LOOK = {
29934
+ extends: { head: "triangle", fill: "hollow", tailHead: "none", tailFill: "solid", style: "solid" },
29935
+ implements: { head: "triangle", fill: "hollow", tailHead: "none", tailFill: "solid", style: "dashed" },
29936
+ aggregates: { head: "none", fill: "solid", tailHead: "diamond", tailFill: "hollow", style: "solid" },
29937
+ composes: { head: "none", fill: "solid", tailHead: "diamond", tailFill: "solid", style: "solid" },
29938
+ associates: { head: "open", fill: "solid", tailHead: "none", tailFill: "solid", style: "solid" },
29939
+ uses: { head: "open", fill: "solid", tailHead: "none", tailFill: "solid", style: "dashed" }
29436
29940
  };
29941
+ Object.fromEntries(
29942
+ Object.entries(CLASS_RELATION_LOOK).map(([k, v]) => [k, v.head])
29943
+ );
29944
+ function isPublic(row) {
29945
+ return !row.startsWith("-") && !row.startsWith("#");
29946
+ }
29947
+ function stripAccess(row) {
29948
+ const \u672C\u4F53 = isPublic(row) && !row.startsWith("+") ? row : row.slice(1);
29949
+ return \u672C\u4F53.replace("()", "");
29950
+ }
29437
29951
  function classDiagram(preset) {
29438
29952
  const classW = resolveStorageBoxW(preset.classWidth, "classDiagram", "classWidth");
29439
29953
  const tone = preset.defaultTone ?? "info";
29440
29954
  const b = diagram(preset.id, { topic: preset.topic });
29441
29955
  const classes = [];
29442
29956
  const phaseActivate = [];
29443
- let relIdx = 0;
29957
+ const \u7BB1 = [];
29958
+ const \u95A2\u4FC2 = [];
29444
29959
  const api = {
29445
29960
  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);
29961
+ \u7BB1.push(c);
29468
29962
  return api;
29469
29963
  },
29470
29964
  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);
29965
+ \u95A2\u4FC2.push(r);
29493
29966
  return api;
29494
29967
  },
29495
29968
  build() {
29969
+ const \u4E2D\u8EAB = \u7BB1.map((c) => {
29970
+ const rows = [];
29971
+ const rowMarks = [];
29972
+ for (const a of c.attributes ?? []) {
29973
+ rows.push(stripAccess(a));
29974
+ rowMarks.push({ shape: "square", filled: isPublic(a) });
29975
+ }
29976
+ if ((c.attributes?.length ?? 0) > 0 && (c.methods?.length ?? 0) > 0) {
29977
+ rows.push("");
29978
+ rowMarks.push(null);
29979
+ }
29980
+ for (const m of c.methods ?? []) {
29981
+ rows.push(stripAccess(m));
29982
+ rowMarks.push({ shape: "chevron", filled: isPublic(m) });
29983
+ }
29984
+ const boxW = fitRowsWidth(classW, rows);
29985
+ const laneId = c.col !== void 0 ? `col-${c.col}` : `lane-${c.id}`;
29986
+ return { c, rows, rowMarks, boxW, laneId, stack: c.col !== void 0 ? c.row ?? 0 : 0 };
29987
+ });
29988
+ const \u5E2F\u5E45 = /* @__PURE__ */ new Map();
29989
+ for (const m of \u4E2D\u8EAB) \u5E2F\u5E45.set(m.laneId, Math.max(\u5E2F\u5E45.get(m.laneId) ?? 0, m.boxW));
29990
+ const \u6E08 = /* @__PURE__ */ new Set();
29991
+ for (const m of \u4E2D\u8EAB) {
29992
+ if (\u6E08.has(m.laneId)) continue;
29993
+ \u6E08.add(m.laneId);
29994
+ b.lane(m.laneId, { width: boxLaneW(\u5E2F\u5E45.get(m.laneId) ?? m.boxW) });
29995
+ }
29996
+ for (const m of \u4E2D\u8EAB) {
29997
+ b.node(m.c.id, {
29998
+ lane: m.laneId,
29999
+ stack: m.stack,
30000
+ kind: "storage",
30001
+ w: m.boxW,
30002
+ title: m.c.title,
30003
+ // 種類の札は **書いた時だけ** 出す (#578)。 「クラス」 と書いた札は箱がクラスで
30004
+ // あることしか言わず、図の中の全ての箱に同じ字が並ぶ
30005
+ ...m.c.stereotype ? { eyebrow: m.c.stereotype } : {},
30006
+ ...m.rows.length > 0 ? { rows: m.rows, rowMarks: m.rowMarks } : {}
30007
+ });
30008
+ classes.push(m.c.id);
30009
+ phaseActivate.push(m.c.id);
30010
+ }
30011
+ \u95A2\u4FC2.forEach((r, currentIdx) => {
30012
+ const edgeId = `cr-${currentIdx}-${r.from}-${r.to}`;
30013
+ const \u898B\u305F\u76EE = CLASS_RELATION_LOOK[r.type];
30014
+ b.edge(r.from, r.to, {
30015
+ id: edgeId,
30016
+ label: r.label ?? r.type,
30017
+ ...r.cardinality ? { sub: r.cardinality } : {},
30018
+ tone: r.tone ?? tone,
30019
+ // 関係の種類から線と端の形を決める (cdl#574 / #578)。 書いた値が勝つ
30020
+ style: r.style ?? \u898B\u305F\u76EE.style,
30021
+ head: r.head ?? \u898B\u305F\u76EE.head,
30022
+ headFill: r.headFill ?? \u898B\u305F\u76EE.fill,
30023
+ tailHead: r.tailHead ?? \u898B\u305F\u76EE.tailHead,
30024
+ tailHeadFill: \u898B\u305F\u76EE.tailFill
30025
+ // CAR-531 SSOT ... label の分散は engine 側 (LABEL_INIT_CLEARANCE + fan offset) に
30026
+ // 一任する。 author 指定 ±60 は engine 分散と二重発火して label が大きく離れる
30027
+ });
30028
+ phaseActivate.push(edgeId);
30029
+ });
29496
30030
  b.phase(
29497
30031
  "class",
29498
30032
  { duration: 2400, title: preset.topic, body: "UML class \u5168 class + relation \u3092 visible \u5316\u3002" },
@@ -30002,6 +30536,6 @@ function slugify(s) {
30002
30536
  return s.toLowerCase().replace(/[^a-z0-9぀-ゟ゠-ヿ一-龯]+/g, "-").replace(/^-|-$/g, "");
30003
30537
  }
30004
30538
 
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 };
30539
+ 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
30540
  //# sourceMappingURL=index.js.map
30007
30541
  //# sourceMappingURL=index.js.map