@cardenelabs/dragon 0.16.0 → 0.18.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.cjs CHANGED
@@ -11,6 +11,17 @@ var HEADERS = {
11
11
  animate: ["\u30A2\u30CB\u30E1\u30FC\u30B7\u30E7\u30F3", "animate", "animation"]
12
12
  };
13
13
  var PRESET_NAMES = ["sequence", "flow", "swimlane", "er", "state", "topology"];
14
+ var DIRECTIONS = ["\u7E26", "\u6A2A"];
15
+ var DIRECTION_ALIAS = {
16
+ \u7E26: "\u7E26",
17
+ \u6A2A: "\u6A2A",
18
+ vertical: "\u7E26",
19
+ horizontal: "\u6A2A"
20
+ };
21
+ function resolveDirection(s) {
22
+ const k = s.trim().toLowerCase();
23
+ return Object.hasOwn(DIRECTION_ALIAS, k) ? DIRECTION_ALIAS[k] : null;
24
+ }
14
25
  var NODE_KIND_ALIAS = {
15
26
  // 日本語
16
27
  \u4EBA: "actor",
@@ -1885,6 +1896,10 @@ var \u3064\u307E\u307F\u306E\u8868 = {
1885
1896
 
1886
1897
  // src/v05/parser.ts
1887
1898
  var EDGE_SIDE_VALUES = ["top", "right", "bottom", "left"];
1899
+ var EDGE_HEAD_VALUES = cdl.EDGE_HEADS;
1900
+ var EDGE_HEAD_FILL_VALUES = cdl.EDGE_HEAD_FILLS;
1901
+ var CLASS_RELATION_VALUES = Object.keys(cdl.CLASS_RELATION_LOOK);
1902
+ var SEQ_MESSAGE_VALUES = Object.keys(cdl.SEQUENCE_MESSAGE_LOOK);
1888
1903
  var TOP_LEVEL_KEYS = [
1889
1904
  "title",
1890
1905
  "type",
@@ -1909,7 +1924,22 @@ var TOP_LEVEL_KEYS = [
1909
1924
  // 押下などの出来事で動く仕掛け (#1393)
1910
1925
  "events",
1911
1926
  // 巻き上げに応じて進む値 (#1393)
1912
- "scrolls"
1927
+ "scrolls",
1928
+ // 動いている間の帯 (#1466)。 順序図だけが読む
1929
+ "bands",
1930
+ // 矢印をいつ出すか (#1470)
1931
+ "reveal",
1932
+ /*
1933
+ * 図の並ぶ向き (#1494)。
1934
+ *
1935
+ * **最上位の語は英語にする**。 日本語の見出し (`タイトル:` / `種類:` 等) は v0.4 の
1936
+ * 書き方で、2026-12-31 に廃止する側にある。 ここで日本語の語を足すと、消す予定の形を
1937
+ * 増やすことになる。
1938
+ *
1939
+ * 値は日本語と英語の両方を受ける。 箱の項目 (`色` / `種類` / `位置` / `大きさ`) は v0.5 でも
1940
+ * 日本語で書けるので、書き手が自然に言う語 (`縦` / `横`) を残せる。
1941
+ */
1942
+ "direction"
1913
1943
  ];
1914
1944
  var LANE_ID_ENTRY = /^([\p{L}\p{N}_-]+)\s*:\s*\{([^}]*)\}\s*$/u;
1915
1945
  function \u540D\u524D\u3068\u4E2D\u62EC\u5F27\u306B\u5272\u308B(\u884C) {
@@ -2049,7 +2079,7 @@ var NODE_KIND_VALID = /* @__PURE__ */ new Set([
2049
2079
  ...Object.keys(INFRA_KIND_ALIAS)
2050
2080
  ]);
2051
2081
  var TONE_VALID = new Set(cdl.TONES);
2052
- var STYLE_VALID = /* @__PURE__ */ new Set(["solid", "dotted-flow"]);
2082
+ var STYLE_VALID = new Set(cdl.EDGE_STYLES);
2053
2083
  function \u66F8\u3051\u308B\u8272\u540D() {
2054
2084
  return [.../* @__PURE__ */ new Set([...cdl.TONES, ...Object.keys(TONE_ALIAS)])];
2055
2085
  }
@@ -2069,6 +2099,9 @@ function parseTextDslV05(src) {
2069
2099
  let type = null;
2070
2100
  let eyebrow = null;
2071
2101
  let eyebrowLine = 0;
2102
+ let reveal = null;
2103
+ let direction = null;
2104
+ let directionLine = 0;
2072
2105
  let axes = void 0;
2073
2106
  let axesLine = 0;
2074
2107
  let actors = [];
@@ -2077,6 +2110,7 @@ function parseTextDslV05(src) {
2077
2110
  const values = [];
2078
2111
  let viewport = void 0;
2079
2112
  let lanesMap = void 0;
2113
+ let bands = void 0;
2080
2114
  let readoutsList = void 0;
2081
2115
  let inputsList = void 0;
2082
2116
  let formulasList = void 0;
@@ -2109,6 +2143,40 @@ function parseTextDslV05(src) {
2109
2143
  i += 1;
2110
2144
  continue;
2111
2145
  }
2146
+ if (head.key === "reveal") {
2147
+ const v = (head.value ?? "").trim();
2148
+ if (v.length > 0) {
2149
+ if (cdl.EDGE_REVEALS.includes(v)) {
2150
+ reveal = v;
2151
+ } else {
2152
+ errors.push({
2153
+ line: line.no,
2154
+ message: `reveal \u304C\u8AAD\u3081\u307E\u305B\u3093 (\u66F8\u3044\u305F\u5024: ${v})`,
2155
+ hint: `\u4F7F\u3048\u308B\u8A9E = ${cdl.EDGE_REVEALS.join(" / ")}`
2156
+ });
2157
+ }
2158
+ }
2159
+ i += 1;
2160
+ continue;
2161
+ }
2162
+ if (head.key === "direction") {
2163
+ const v = (head.value ?? "").trim();
2164
+ if (v.length > 0) {
2165
+ const \u89E3\u3051\u305F = resolveDirection(v);
2166
+ if (\u89E3\u3051\u305F !== null) {
2167
+ direction = \u89E3\u3051\u305F;
2168
+ directionLine = line.no;
2169
+ } else {
2170
+ errors.push({
2171
+ line: line.no,
2172
+ message: `direction \u304C\u8AAD\u3081\u307E\u305B\u3093 (\u66F8\u3044\u305F\u5024: ${v})`,
2173
+ hint: `\u4F7F\u3048\u308B\u8A9E = ${DIRECTIONS.join(" / ")} / vertical / horizontal`
2174
+ });
2175
+ }
2176
+ }
2177
+ i += 1;
2178
+ continue;
2179
+ }
2112
2180
  if (head.key === "eyebrow") {
2113
2181
  const v = (head.value ?? "").trim();
2114
2182
  eyebrow = v.length > 0 ? v : null;
@@ -2289,6 +2357,24 @@ function parseTextDslV05(src) {
2289
2357
  i = next;
2290
2358
  continue;
2291
2359
  }
2360
+ if (head.key === "bands") {
2361
+ const { items, next } = collectIndentedList(lines, i + 1, line.indent);
2362
+ bands = [];
2363
+ for (const it of items) {
2364
+ const m = it.trimmed.match(/^(?:-\s*)?(.+?)\s*:\s*(\d+)\s*\.\.\s*(\d+)\s*$/);
2365
+ if (!m) {
2366
+ errors.push({
2367
+ line: it.no,
2368
+ message: `\u5E2F\u306E\u66F8\u304D\u65B9\u304C\u8AAD\u3081\u307E\u305B\u3093: "${it.trimmed}"`,
2369
+ hint: "use `- DB: 1..2` (\u9762\u306E\u540D\u524D\u3068\u3001\u6BB5\u306E\u756A\u53F7\u306E\u533A\u9593)"
2370
+ });
2371
+ continue;
2372
+ }
2373
+ bands.push({ actor: m[1], from: Number(m[2]), to: Number(m[3]) });
2374
+ }
2375
+ i = next;
2376
+ continue;
2377
+ }
2292
2378
  if (head.key === "lanes") {
2293
2379
  const { items, next } = collectIndentedList(lines, i + 1, line.indent);
2294
2380
  lanesMap = {};
@@ -2503,6 +2589,8 @@ function parseTextDslV05(src) {
2503
2589
  title,
2504
2590
  type,
2505
2591
  ...eyebrow !== null ? { eyebrow, eyebrowPos: { line: eyebrowLine } } : {},
2592
+ ...reveal !== null ? { reveal } : {},
2593
+ ...direction !== null ? { direction, directionPos: { line: directionLine } } : {},
2506
2594
  ...axes !== void 0 ? { axes, axesPos: { line: axesLine } } : {},
2507
2595
  actors,
2508
2596
  flow: flow2,
@@ -2510,6 +2598,7 @@ function parseTextDslV05(src) {
2510
2598
  ...values.length > 0 ? { values } : {},
2511
2599
  viewport,
2512
2600
  lanes: lanesMap,
2601
+ ...bands && bands.length > 0 ? { bands } : {},
2513
2602
  readouts: readoutsList,
2514
2603
  inputs: inputsList,
2515
2604
  formulas: formulasList,
@@ -3472,6 +3561,19 @@ function applyContinuationLines(actor, rest, errors) {
3472
3561
  case "\u884C":
3473
3562
  out.rows = raw.replace(/^\[|\]$/g, "").split(/,(?![^[]*\])/).map((x) => stripQuotes(x.trim())).filter(Boolean);
3474
3563
  break;
3564
+ /*
3565
+ * 行頭の印 (#1466)。 `rows` と同じ並びで、空文字はその行に印を付けない。
3566
+ *
3567
+ * **語の意味は図の種類が決める**。 ER は `pk` / `fk` / `opt`、状態遷移は
3568
+ * `entry` / `exit` / `do` / `internal`。 印の 2 軸 (形 × 塗り) は共通だが、その軸が
3569
+ * 何を指すかは種類ごとに違う = 1 つの語彙に畳むと、どの図でも意味が合わない語が残る。
3570
+ *
3571
+ * 空の要素を捨てない (`filter(Boolean)` を掛けない) = 並びが `rows` とずれる。
3572
+ */
3573
+ case "marks":
3574
+ case "\u5370":
3575
+ out.marks = raw.replace(/^\[|\]$/g, "").split(/,(?![^[]*\])/).map((x) => stripQuotes(x.trim()));
3576
+ break;
3475
3577
  case "\u4F4D\u7F6E":
3476
3578
  case "pos": {
3477
3579
  const value = stripQuotes(raw);
@@ -3591,6 +3693,9 @@ var ACTOR_ITEM_KEYS = /* @__PURE__ */ new Set([
3591
3693
  "\u524D\u306E\u5024",
3592
3694
  "rows",
3593
3695
  "\u884C",
3696
+ // 行頭の印 (#1466)。 `rows` と同じ並びで、形 × 塗り の 2 軸を語で書く
3697
+ "marks",
3698
+ "\u5370",
3594
3699
  // 箱の中に描く図形 (#1374)
3595
3700
  "shape",
3596
3701
  "\u56F3\u5F62",
@@ -3716,6 +3821,7 @@ var ACTOR_RESERVED_FIELDS = /* @__PURE__ */ new Set([
3716
3821
  "value",
3717
3822
  "previous",
3718
3823
  "rows",
3824
+ "marks",
3719
3825
  "lane",
3720
3826
  "stack",
3721
3827
  "initial",
@@ -3818,7 +3924,8 @@ var INLINE_ACTOR_ALIASES = {
3818
3924
  \u88DC\u8DB3: "subtitle",
3819
3925
  \u5024: "value",
3820
3926
  \u524D\u306E\u5024: "previous",
3821
- \u884C: "rows"
3927
+ \u884C: "rows",
3928
+ \u5370: "marks"
3822
3929
  };
3823
3930
  function \u4E2D\u62EC\u5F27\u306E\u5225\u540D\u3092\u5BC4\u305B\u308B(opts) {
3824
3931
  let \u89E6\u3063\u305F = false;
@@ -3838,6 +3945,7 @@ var INLINE_ACTOR_KEYS = /* @__PURE__ */ new Set([
3838
3945
  "value",
3839
3946
  "previous",
3840
3947
  "rows",
3948
+ "marks",
3841
3949
  "lane",
3842
3950
  "stack",
3843
3951
  "initial",
@@ -3884,6 +3992,27 @@ var FLOW_INLINE_READERS = {
3884
3992
  cardinality: (v) => v,
3885
3993
  // 矢印がどの辺から出るか (#1385)。 描画側は 4 方向を取り、書かなければ自動で選ぶ
3886
3994
  side: (v) => v !== void 0 && EDGE_SIDE_VALUES.includes(v) ? v : void 0,
3995
+ // 矢印の先の形 (#1462)。 書かなければ描画側の既定 (塗った三角) になる
3996
+ head: (v) => v !== void 0 && EDGE_HEAD_VALUES.includes(v) ? v : void 0,
3997
+ // 出どころ側の端の形 (#1466)。 ER は端ごとに違う個数を示すので両端に要る
3998
+ tailHead: (v) => v !== void 0 && EDGE_HEAD_VALUES.includes(v) ? v : void 0,
3999
+ // 端の印の塗り (#1466)。 白抜きの菱が「持つ」、塗った菱が「抱える」
4000
+ headFill: (v) => v !== void 0 && EDGE_HEAD_FILL_VALUES.includes(v) ? v : void 0,
4001
+ tailHeadFill: (v) => v !== void 0 && EDGE_HEAD_FILL_VALUES.includes(v) ? v : void 0,
4002
+ /*
4003
+ * クラス図の関係の種類 (#1466)。 書くと **線と端の形と塗りと付く側** がまとめて決まる
4004
+ * (`CLASS_RELATION_LOOK`)。
4005
+ *
4006
+ * 4 つを個別に書かせない = 組合せは 6 通りしか無く、1 つでも書き違えると読み手に別の意味で
4007
+ * 伝わる (菱を逆に置くと持ち主が入れ替わる)。 種類で書けばその 6 通りから外れない。
4008
+ */
4009
+ relation: (v) => v !== void 0 && CLASS_RELATION_VALUES.includes(v) ? v : void 0,
4010
+ /*
4011
+ * 順序図の言づての種類 (#1466)。 書くと線と矢の形がまとめて決まる。
4012
+ *
4013
+ * 呼ぶ (実線 + 塗った矢) / 返す (破線 + 開いた矢) / 投げる (実線 + 開いた矢) の 3 つ。
4014
+ */
4015
+ kind: (v) => v !== void 0 && SEQ_MESSAGE_VALUES.includes(v) ? v : void 0,
3887
4016
  /*
3888
4017
  * 矢印を値に追随させる 3 欄 (#1396)。 箱の `wBind` (#1392) と同じく文字列だけを取る。
3889
4018
  *
@@ -3950,6 +4079,8 @@ function parseActor2(line, errors) {
3950
4079
  value: opts.value,
3951
4080
  previous: opts.previous,
3952
4081
  rows: opts.rows ? opts.rows.replace(/^\[|\]$/g, "").split(/,(?![^[]*\])/).map((x) => stripQuotes(x.trim())).filter(Boolean) : void 0,
4082
+ // 行頭の印 (#1466)。 `rows` と同じ並びなので **空の要素を捨てない**
4083
+ marks: opts.marks ? opts.marks.replace(/^\[|\]$/g, "").split(/,(?![^[]*\])/).map((x) => stripQuotes(x.trim())) : void 0,
3953
4084
  lane: opts.lane,
3954
4085
  // 箱の中に描く図形 (#1374)。 パーツでは状態の上書きとして意味を持つため横取りしない
3955
4086
  shape: isPart || opts.shape === void 0 ? void 0 : \u56F3\u5F62\u3068\u3057\u3066\u8AAD\u3080(opts.shape, line.no, errors),
@@ -4045,6 +4176,22 @@ function parseFlowStep(line, no, errors) {
4045
4176
  hint: `\u4F7F\u3048\u308B\u5024 = ${EDGE_SIDE_VALUES.join(", ")}`
4046
4177
  });
4047
4178
  }
4179
+ for (const [\u6B04, \u4F7F\u3048\u308B] of [
4180
+ ["head", EDGE_HEAD_VALUES],
4181
+ ["tailHead", EDGE_HEAD_VALUES],
4182
+ ["headFill", EDGE_HEAD_FILL_VALUES],
4183
+ ["tailHeadFill", EDGE_HEAD_FILL_VALUES],
4184
+ ["relation", CLASS_RELATION_VALUES],
4185
+ ["kind", SEQ_MESSAGE_VALUES]
4186
+ ]) {
4187
+ if (opts[\u6B04] !== void 0 && \u4E2D\u62EC\u5F27[\u6B04] === void 0) {
4188
+ errors.push({
4189
+ line: line.no,
4190
+ message: `\u77E2\u5370\u306E ${\u6B04} \u304C\u8AAD\u3081\u307E\u305B\u3093: "${opts[\u6B04]}"`,
4191
+ hint: `\u4F7F\u3048\u308B\u5024 = ${\u4F7F\u3048\u308B.join(", ")}`
4192
+ });
4193
+ }
4194
+ }
4048
4195
  \u6570\u3068\u771F\u507D = \u8868\u3067\u8AAD\u3080(FLOW_INLINE_VALUE_KINDS, opts, "\u77E2\u5370\u306E ", line.no, errors);
4049
4196
  rest = \u584A.\u524D.trim();
4050
4197
  }
@@ -4052,6 +4199,12 @@ function parseFlowStep(line, no, errors) {
4052
4199
  const guard = \u4E2D\u62EC\u5F27.guard;
4053
4200
  const cardinality = \u4E2D\u62EC\u5F27.cardinality;
4054
4201
  const side = \u4E2D\u62EC\u5F27.side;
4202
+ const head = \u4E2D\u62EC\u5F27.head;
4203
+ const tailHead = \u4E2D\u62EC\u5F27.tailHead;
4204
+ const headFill = \u4E2D\u62EC\u5F27.headFill;
4205
+ const tailHeadFill = \u4E2D\u62EC\u5F27.tailHeadFill;
4206
+ const relation = \u4E2D\u62EC\u5F27.relation;
4207
+ const msgKind = \u4E2D\u62EC\u5F27.kind;
4055
4208
  const widthBind = \u8FFD\u968F\u3059\u308B\u5927\u304D\u3055\u3068\u3057\u3066\u8AAD\u3080(
4056
4209
  \u4E2D\u62EC\u5F27.widthBind,
4057
4210
  "\u77E2\u5370\u306E widthBind ",
@@ -4121,6 +4274,12 @@ function parseFlowStep(line, no, errors) {
4121
4274
  guard,
4122
4275
  cardinality,
4123
4276
  side,
4277
+ head,
4278
+ tailHead,
4279
+ headFill,
4280
+ tailHeadFill,
4281
+ relation,
4282
+ msgKind,
4124
4283
  widthBind,
4125
4284
  strokeBind,
4126
4285
  dashOffsetBind,
@@ -4810,7 +4969,6 @@ function compileToCdl(doc, opts) {
4810
4969
  doc = canonicalizeFlowActors(doc);
4811
4970
  const \u66F8\u3044\u305F\u307E\u307E = doc;
4812
4971
  doc = dropUnresolvedFlow(doc);
4813
- doc = dropSelfLoopFlow(doc);
4814
4972
  const \u5206\u3051\u305F = disambiguateActorIds(doc, opts?.onNotice);
4815
4973
  doc = \u5206\u3051\u305F.doc;
4816
4974
  let diagram2;
@@ -4894,15 +5052,17 @@ function compileToCdl(doc, opts) {
4894
5052
  applyNodeTones(diagram2, doc);
4895
5053
  reportMissingFocusTargets(\u66F8\u3044\u305F\u307E\u307E, opts?.onNotice);
4896
5054
  reportMissingFlowActors(\u66F8\u3044\u305F\u307E\u307E, opts?.onNotice);
4897
- reportSelfLoopFlow(\u66F8\u3044\u305F\u307E\u307E, opts?.onNotice);
4898
5055
  reportFlowEndpointNotHonored(doc, \u5206\u3051\u305F.\u5143\u306E\u540D\u524D, opts?.onNotice);
4899
5056
  reportLaneNotHonored(\u66F8\u3044\u305F\u307E\u307E, opts?.onNotice);
5057
+ reportActorKindNotHonored(\u66F8\u3044\u305F\u307E\u307E, opts?.onNotice);
5058
+ reportMessageOptionNotHonored(\u66F8\u3044\u305F\u307E\u307E, opts?.onNotice);
4900
5059
  reportDocEyebrowNotHonored(\u66F8\u3044\u305F\u307E\u307E, opts?.onNotice);
5060
+ reportDirectionNotHonored(\u66F8\u3044\u305F\u307E\u307E, opts?.onNotice);
4901
5061
  reportDrawNotHonored(\u66F8\u3044\u305F\u307E\u307E, opts?.onNotice);
4902
5062
  reportChartFieldsNotHonored(\u66F8\u3044\u305F\u307E\u307E, opts?.onNotice);
4903
5063
  reportAxesNotHonored(\u66F8\u3044\u305F\u307E\u307E, opts?.onNotice);
4904
5064
  const placed = resolveRelativeDoc(diagram2, doc, opts?.onNotice, opts?.partsCatalog);
4905
- applyCanvasPivotPositions(diagram2, placed);
5065
+ applyCanvasPivotPositions(diagram2, placed, opts?.onNotice);
4906
5066
  const \u8FFD\u52A0\u3057\u305F\u7E26\u5217 = [];
4907
5067
  const extended = applyV05Extensions(diagram2, placed, \u8FFD\u52A0\u3057\u305F\u7E26\u5217);
4908
5068
  const inheritedDerivedSourceLines = opts?.onNotice ? /* @__PURE__ */ new Map() : void 0;
@@ -4925,6 +5085,7 @@ function compileToCdl(doc, opts) {
4925
5085
  }
4926
5086
  injectStaticPhase(merged);
4927
5087
  materializeStates(merged, doc);
5088
+ if (doc.reveal !== void 0) merged.edgeReveal = doc.reveal;
4928
5089
  \u8A9E\u306E\u72B6\u614B\u3092\u56F3\u306E\u8A9E\u3078\u76F4\u3059(merged);
4929
5090
  const \u7573\u3093\u3060\u304D\u3063\u304B\u3051 = foldValueTriggers(merged, doc, opts?.onNotice);
4930
5091
  attachDerivedValues(merged, doc, opts?.onNotice, inheritedDerivedSourceLines, \u7573\u3093\u3060\u304D\u3063\u304B\u3051);
@@ -5141,27 +5302,6 @@ function dropUnresolvedFlow(doc) {
5141
5302
  const flow2 = doc.flow.filter((s) => \u8868.has(s.from) && \u8868.has(s.to));
5142
5303
  return flow2.length === doc.flow.length ? doc : { ...doc, flow: flow2 };
5143
5304
  }
5144
- function dropSelfLoopFlow(doc) {
5145
- if (\u56F3\u7A2E\u306E\u4F5C\u308A[doc.type] === "\u56F3\u5168\u4F53\u3092 1 \u7BB1") return doc;
5146
- const flow2 = doc.flow.filter((s) => s.from !== s.to);
5147
- return flow2.length === doc.flow.length ? doc : { ...doc, flow: flow2 };
5148
- }
5149
- function reportSelfLoopFlow(doc, onNotice) {
5150
- if (!onNotice) return;
5151
- if (\u56F3\u7A2E\u306E\u4F5C\u308A[doc.type] === "\u56F3\u5168\u4F53\u3092 1 \u7BB1") return;
5152
- const \u77E5\u3089\u305B\u305F = /* @__PURE__ */ new Set();
5153
- for (const s of doc.flow) {
5154
- if (s.from !== s.to || \u77E5\u3089\u305B\u305F.has(s.from)) continue;
5155
- \u77E5\u3089\u305B\u305F.add(s.from);
5156
- onNotice({
5157
- kind: "flow-self-loop",
5158
- actor: s.from,
5159
- line: s.pos.line,
5160
- message: `"${truncateForMessage(s.from)}" \u304B\u3089\u81EA\u5206\u3078\u623B\u308B\u77E2\u5370\u306F\u63CF\u3051\u307E\u305B\u3093 (\u7D44\u307F\u7ACB\u3066\u304B\u3089\u5916\u3057\u307E\u3057\u305F)`,
5161
- hint: "\u9014\u4E2D\u306E\u7BB1\u3092 1 \u3064\u8DB3\u3057\u3066 2 \u672C\u306B\u5206\u3051\u308B\u304B\u3001 \u6BB5 (animation) \u3067\u72B6\u614B\u304C\u5909\u308F\u308B\u69D8\u5B50\u3068\u3057\u3066\u898B\u305B\u308B"
5162
- });
5163
- }
5164
- }
5165
5305
  function \u56F3\u306E\u5C0F\u898B\u51FA\u3057(doc) {
5166
5306
  return doc.eyebrow === void 0 ? {} : { eyebrow: doc.eyebrow };
5167
5307
  }
@@ -5240,6 +5380,59 @@ function reportFlowEndpointNotHonored(doc, \u5143\u306E\u540D\u524D, onNotice) {
5240
5380
  });
5241
5381
  }
5242
5382
  }
5383
+ function reportMessageOptionNotHonored(doc, onNotice) {
5384
+ if (!onNotice) return;
5385
+ if (doc.type !== "sequence" && doc.type !== "solidity") return;
5386
+ for (const s of doc.flow) {
5387
+ const \u52B9\u304B\u306A\u3044 = [
5388
+ s.tone !== void 0 ? "\u8272\u5473" : "",
5389
+ s.sub !== void 0 ? "\u6DFB\u3048\u5B57" : "",
5390
+ s.side !== void 0 ? "\u5BC4\u305B" : ""
5391
+ ].filter((x) => x !== "");
5392
+ if (\u52B9\u304B\u306A\u3044.length === 0) continue;
5393
+ onNotice({
5394
+ kind: "message-option-not-honored",
5395
+ actor: s.from,
5396
+ line: s.pos?.line ?? 0,
5397
+ message: `"${truncateForMessage(s.label)}" \u306B\u66F8\u3044\u305F ${\u52B9\u304B\u306A\u3044.join(" / ")} \u306F\u52B9\u304D\u307E\u305B\u3093 (type: ${doc.type} \u306E\u677F\u306F\u8A9E\u3068\u5411\u304D\u3068\u7A2E\u985E\u3060\u3051\u3092\u63CF\u304D\u307E\u3059)`,
5398
+ hint: "\u677F\u306B\u306F\u77E2\u5370\u304C\u7121\u3044\u305F\u3081\u98FE\u308A\u3092\u8F09\u305B\u308B\u5148\u304C\u3042\u308A\u307E\u305B\u3093\u3002 \u8A00\u3065\u3066\u306E\u7A2E\u985E (kind: call / return / fire) \u3067\u63CF\u304D\u5206\u3051\u3066\u304F\u3060\u3055\u3044"
5399
+ });
5400
+ }
5401
+ }
5402
+ function reportActorKindNotHonored(doc, onNotice) {
5403
+ if (!onNotice) return;
5404
+ if (doc.type !== "sequence" && doc.type !== "solidity") return;
5405
+ for (const a of doc.actors) {
5406
+ if (a.partId !== void 0) continue;
5407
+ const \u52B9\u304B\u306A\u3044 = [
5408
+ /*
5409
+ * 種類は `sequence` でだけ落ちる。
5410
+ *
5411
+ * `solidity` は種類で **面の並びを決める** (`compileSolidity`) ので、絵にならなくても
5412
+ * 書いた意味は figure に出ている。 落ちたと伝えると、正しく効いている指定に毎回鳴る。
5413
+ *
5414
+ * 記法を通すと既定の `actor` が必ず入るため、値ではなく書いたかどうかの印で見る。
5415
+ * 記法を通さず直接組み立てた場合はこの印が無いので、種類を置いたこと自体を「書いた」 とみなす
5416
+ */
5417
+ doc.type === "sequence" && a.kindWritten !== false && a.kind !== void 0 ? "\u7A2E\u985E" : "",
5418
+ a.posW !== void 0 || a.posH !== void 0 ? "\u5927\u304D\u3055" : "",
5419
+ a.posX !== void 0 || a.posY !== void 0 || a.posRel !== void 0 ? "\u4F4D\u7F6E" : "",
5420
+ a.rows !== void 0 ? "\u884C" : "",
5421
+ a.tone !== void 0 ? "\u8272" : "",
5422
+ a.eyebrow !== void 0 ? "\u5C0F\u898B\u51FA\u3057" : "",
5423
+ a.value !== void 0 ? "\u5024" : "",
5424
+ a.shape !== void 0 ? "\u56F3\u5F62" : ""
5425
+ ].filter((x) => x !== "");
5426
+ if (\u52B9\u304B\u306A\u3044.length === 0) continue;
5427
+ onNotice({
5428
+ kind: "actor-kind-not-honored",
5429
+ actor: a.name,
5430
+ line: a.pos?.line ?? 0,
5431
+ message: `"${truncateForMessage(a.name)}" \u306B\u66F8\u3044\u305F ${\u52B9\u304B\u306A\u3044.join(" / ")} \u306F\u52B9\u304D\u307E\u305B\u3093 (type: ${doc.type} \u306E\u677F\u306F\u540D\u524D\u3068\u547C\u3073\u540D\u3060\u3051\u3092\u63CF\u304D\u307E\u3059)`,
5432
+ hint: "\u677F\u306B\u306F\u9762\u3054\u3068\u306E\u7BB1\u304C\u7121\u3044\u305F\u3081\u98FE\u308A\u3092\u8F09\u305B\u308B\u5148\u304C\u3042\u308A\u307E\u305B\u3093\u3002 \u547C\u3073\u540D (subtitle) \u306B\u66F8\u304F\u304B\u3001\u7BB1\u3092\u6301\u3064\u56F3\u7A2E\u3092\u4F7F\u3063\u3066\u304F\u3060\u3055\u3044"
5433
+ });
5434
+ }
5435
+ }
5243
5436
  function reportLaneNotHonored(doc, onNotice) {
5244
5437
  if (!onNotice) return;
5245
5438
  if (doc.type === "mind") return;
@@ -5639,7 +5832,7 @@ function applyNodeTones(diagram2, doc) {
5639
5832
  }
5640
5833
  }
5641
5834
  }
5642
- function applyCanvasPivotPositions(diagram2, doc) {
5835
+ function applyCanvasPivotPositions(diagram2, doc, onNotice) {
5643
5836
  for (const actor of doc.actors) {
5644
5837
  if (actor.partId !== void 0) continue;
5645
5838
  const aliasSlug = slugify(actor.name);
@@ -5664,14 +5857,25 @@ function applyCanvasPivotPositions(diagram2, doc) {
5664
5857
  if (actor.nodes) {
5665
5858
  for (const [subKey, override] of Object.entries(actor.nodes)) {
5666
5859
  if (override.posX === void 0 || override.posY === void 0) continue;
5860
+ let \u5F53\u305F\u3063\u305F = 0;
5667
5861
  for (const node of diagram2.nodes) {
5668
5862
  if (node.id === `${aliasSlug}-${subKey}` || node.id === `${subKey}-${aliasSlug}`) {
5669
5863
  node.posX = override.posX;
5670
5864
  node.posY = override.posY;
5671
5865
  if (override.posW !== void 0) node.posW = override.posW;
5672
5866
  if (override.posH !== void 0) node.posH = override.posH;
5867
+ \u5F53\u305F\u3063\u305F += 1;
5673
5868
  }
5674
5869
  }
5870
+ if (\u5F53\u305F\u3063\u305F === 0) {
5871
+ onNotice?.({
5872
+ kind: "sub-node-not-found",
5873
+ actor: actor.name,
5874
+ line: actor.pos?.line ?? 0,
5875
+ message: `"${truncateForMessage(actor.name)}" \u306E nodes \u306B\u66F8\u3044\u305F "${truncateForMessage(subKey)}" \u306B\u5F53\u305F\u308B\u7BB1\u304C\u56F3\u306B\u3042\u308A\u307E\u305B\u3093`,
5876
+ hint: "1 \u4EBA\u306B\u8907\u6570\u306E\u7BB1\u3092\u4F5C\u308B\u56F3\u7A2E\u3067\u3060\u3051\u52B9\u304D\u307E\u3059\u3002 \u7BB1\u305D\u306E\u3082\u306E\u306E\u4F4D\u7F6E\u306F actor \u5074\u306E \u4F4D\u7F6E: \u306B\u66F8\u3044\u3066\u304F\u3060\u3055\u3044"
5877
+ });
5878
+ }
5675
5879
  }
5676
5880
  }
5677
5881
  }
@@ -5894,10 +6098,13 @@ function partVisualSize(part, targetW, targetH) {
5894
6098
  var PARTS_PER_ROW = 3;
5895
6099
  var PARTS_GAP = 120;
5896
6100
  var STACK_PITCH = 280;
5897
- function partsGridCenters(baseNodeCount, items) {
6101
+ function partsBaseRows(nodes) {
6102
+ return nodes.reduce((acc, n) => acc + Math.max(1, Math.ceil(positiveOr(n.h, 0) / STACK_PITCH)), 0);
6103
+ }
6104
+ function partsGridCenters(baseRows, items) {
5898
6105
  const out = /* @__PURE__ */ new Map();
5899
6106
  if (items.length === 0) return out;
5900
- const safeCount = Number.isSafeInteger(baseNodeCount) && baseNodeCount >= 0 ? baseNodeCount : 0;
6107
+ const safeCount = Number.isSafeInteger(baseRows) && baseRows >= 0 ? baseRows : 0;
5901
6108
  const top = safeCount * STACK_PITCH + PARTS_GAP * 2;
5902
6109
  const seen = /* @__PURE__ */ new Set();
5903
6110
  const unique = items.filter((i) => {
@@ -5980,7 +6187,7 @@ function partGridCenters(target, doc, partsCatalog, accepted, acceptedFrom) {
5980
6187
  extents.set(a.name, partFrameExtent(part, t.w, t.h));
5981
6188
  }
5982
6189
  const centers = partsGridCenters(
5983
- baseNodes.length,
6190
+ partsBaseRows(baseNodes),
5984
6191
  placedActors.map((a) => ({ id: a.name, ...extents.get(a.name) }))
5985
6192
  );
5986
6193
  const out = /* @__PURE__ */ new Map();
@@ -6030,7 +6237,11 @@ function cleanupPlaceholderActor(target, doc, a) {
6030
6237
  for (const n of target.nodes) {
6031
6238
  if (ownedLaneIds.has(n.lane)) ownedNodeIds.add(n.id);
6032
6239
  }
6240
+ const \u7D20\u306E\u7BB1\u306Eid = new Set(
6241
+ doc.actors.filter((x) => x.partId === void 0).map((x) => slugify(x.name))
6242
+ );
6033
6243
  const matchesAliasSlug = (id) => {
6244
+ if (\u7D20\u306E\u7BB1\u306Eid.has(id)) return false;
6034
6245
  if (id === aliasSlug) return true;
6035
6246
  if (id.startsWith(`${aliasSlug}-`)) return true;
6036
6247
  if (/^s\d+-/.test(id) && id.endsWith(`-${aliasSlug}`)) return true;
@@ -6044,14 +6255,14 @@ function cleanupPlaceholderActor(target, doc, a) {
6044
6255
  if (drop) removedEdgeIds.add(e.id);
6045
6256
  return !drop;
6046
6257
  });
6047
- if (doc.type === "sequence" || doc.type === "solidity") {
6048
- target.lanes = target.lanes.filter((l) => {
6049
- if (a.lane !== void 0 && l.id === a.lane) return true;
6050
- if (l.label === a.name) return false;
6051
- if (l.id === aliasSlug) return false;
6052
- return true;
6053
- });
6054
- }
6258
+ const \u6B8B\u308B\u7BB1\u3092\u6301\u3064 = new Set(target.nodes.map((n) => n.lane));
6259
+ target.lanes = target.lanes.filter((l) => {
6260
+ if (a.lane !== void 0 && l.id === a.lane) return true;
6261
+ if (\u6B8B\u308B\u7BB1\u3092\u6301\u3064.has(l.id)) return true;
6262
+ if (l.label === a.name) return false;
6263
+ if (l.id === aliasSlug) return false;
6264
+ return true;
6265
+ });
6055
6266
  for (const phase of target.phases) {
6056
6267
  phase.activate = phase.activate.filter((id) => !relatedToActor(id) && !removedEdgeIds.has(id));
6057
6268
  }
@@ -6413,15 +6624,11 @@ function applyEdgeInlineOptions(diagram2, doc, sourceLines) {
6413
6624
  return;
6414
6625
  }
6415
6626
  const used = /* @__PURE__ */ new Set();
6416
- const isSeqLike = doc.type === "sequence" || doc.type === "solidity";
6417
- doc.flow.forEach((s, stepIdx) => {
6627
+ doc.flow.forEach((s) => {
6418
6628
  const fromId = slugify(s.from);
6419
6629
  const toId = slugify(s.to);
6420
6630
  const target = diagram2.edges.find((e) => {
6421
6631
  if (used.has(e.id)) return false;
6422
- if (isSeqLike) {
6423
- return (e.from === `s${stepIdx}-${fromId}` || e.from === fromId) && (e.to === `s${stepIdx}-${toId}` || e.from === e.to);
6424
- }
6425
6632
  return e.from === fromId && e.to === toId;
6426
6633
  });
6427
6634
  if (!target) return;
@@ -6472,6 +6679,10 @@ function \u77E2\u5370\u3078\u66F8\u304D\u5199\u3059(target, s, doc) {
6472
6679
  }
6473
6680
  }
6474
6681
  if (s.side !== void 0) target.side = s.side;
6682
+ if (s.head !== void 0) target.head = s.head;
6683
+ if (s.tailHead !== void 0) target.tailHead = s.tailHead;
6684
+ if (s.headFill !== void 0) target.headFill = s.headFill;
6685
+ if (s.tailHeadFill !== void 0) target.tailHeadFill = s.tailHeadFill;
6475
6686
  if (s.labelOffsetX !== void 0) target.labelOffsetX = s.labelOffsetX;
6476
6687
  if (s.labelOffsetY !== void 0) target.labelOffsetY = s.labelOffsetY;
6477
6688
  if (s.overlay !== void 0) target.overlay = s.overlay;
@@ -6489,16 +6700,6 @@ function \u9396\u306E\u3069\u306E\u884C\u304B\u3089\u6765\u305F\u304B(doc, edgeI
6489
6700
  if (to === void 0) return void 0;
6490
6701
  return doc.flow.find((s) => s.to === to.name);
6491
6702
  }
6492
- var DRAWABLE_KINDS = new Set(cdl.NODE_KINDS);
6493
- var KIND_ALIAS = {
6494
- eoa: "shape-wallet",
6495
- wallet: "shape-wallet",
6496
- multisig: "signer",
6497
- contract: "shape-smart-contract",
6498
- proxy: "shape-smart-contract",
6499
- library: "shape-code-block",
6500
- interface: "shape-code-block"
6501
- };
6502
6703
  function \u5F0F\u306B\u66F8\u304F\u6570(n) {
6503
6704
  if (!Number.isFinite(n)) return "0";
6504
6705
  const text = String(n);
@@ -6826,99 +7027,6 @@ var SINGLE_BOX_KINDS = /* @__PURE__ */ new Set([
6826
7027
  "tree-hierarchy",
6827
7028
  "journey-map"
6828
7029
  ]);
6829
- function drawableKind(kind) {
6830
- if (kind === void 0) return void 0;
6831
- const mapped = KIND_ALIAS[kind] ?? kind;
6832
- return DRAWABLE_KINDS.has(mapped) ? mapped : void 0;
6833
- }
6834
- function alignSeqHeaderHeights(diagram2, doc) {
6835
- if (doc.type !== "sequence" && doc.type !== "solidity") return;
6836
- const isEnd = (n) => n.id === `${n.lane}-header` || n.id === `${n.lane}-footer`;
6837
- const ends = diagram2.nodes.filter(isEnd);
6838
- if (ends.length === 0) return;
6839
- const auto = ends.filter((n) => n.posH === void 0);
6840
- if (auto.length === 0) return;
6841
- const tallest = Math.max(...auto.map((n) => n.h ?? 0));
6842
- if (tallest <= 0) return;
6843
- for (const n of auto) n.h = tallest;
6844
- }
6845
- var LABEL_MIN_H = {
6846
- // `shape-` のうち、 高さを上げれば下のはみ出しが消える 4 種 (#1067)。 値は「収まる最小の高さ」
6847
- // で、 1 手前 (値 - 1) では 0.9-1 はみ出すことを実測した
6848
- "shape-person": 228,
6849
- // 名札 72 で下へ 155.9
6850
- "shape-server-rack": 166,
6851
- // 94
6852
- "shape-website": 98,
6853
- // 26
6854
- "shape-warehouse": 79
6855
- // 7
6856
- };
6857
- var LABEL_NEVER_FITS = /* @__PURE__ */ new Set([
6858
- // 左右にはみ出す 24 種。 横幅は高さで変わらないため直らない
6859
- "shape-api-gateway",
6860
- "shape-atm",
6861
- "shape-auditor",
6862
- "shape-bank",
6863
- "shape-bitcoin-chain",
6864
- "shape-blockchain",
6865
- "shape-blockchain-block",
6866
- "shape-blockchain-node",
6867
- "shape-brokerage",
6868
- "shape-code-block",
6869
- "shape-customer-service",
6870
- "shape-ethereum-chain",
6871
- "shape-hexagon",
6872
- "shape-kanban-card",
6873
- "shape-lawyer",
6874
- "shape-network-node",
6875
- "shape-nft",
6876
- "shape-notary",
6877
- "shape-regulator",
6878
- "shape-satellite",
6879
- "shape-smart-contract",
6880
- "shape-terminal",
6881
- "shape-trader",
6882
- "shape-trust-bank",
6883
- // 下のはみ出しが高さに依らない 6 種
6884
- "shape-cylinder",
6885
- "shape-diamond",
6886
- "shape-file",
6887
- "shape-folder",
6888
- "shape-mobile-device",
6889
- "shape-stack",
6890
- // 上へ出る絵が名札の大きさでは読めない。 `#1067` では「上は何ともぶつからない」 として残したが、
6891
- // 実際には絵が小さく潰れて名前と重なり、 横に並べた時も 1 本だけ頭が浮く (user 実機確認)
6892
- "shape-wallet"
6893
- ]);
6894
- function hasAuthoredText(n) {
6895
- if (n.subtitle !== void 0 || n.eyebrow !== void 0 || n.value !== void 0) return true;
6896
- return cdl.rendersRows(n.kind) && (n.rows?.length ?? 0) > 0;
6897
- }
6898
- function dropUnfittableEndKinds(diagram2, doc) {
6899
- if (doc.type !== "sequence" && doc.type !== "solidity") return;
6900
- const ends = /* @__PURE__ */ new Map();
6901
- for (const n of diagram2.nodes) {
6902
- if (n.id !== `${n.lane}-header` && n.id !== `${n.lane}-footer`) continue;
6903
- const pair = ends.get(n.lane);
6904
- if (pair === void 0) ends.set(n.lane, [n]);
6905
- else pair.push(n);
6906
- }
6907
- for (const pair of ends.values()) {
6908
- if (pair.some(hasAuthoredText)) continue;
6909
- const \u53CE\u307E\u3089\u306A\u3044 = pair.some((n) => {
6910
- if (LABEL_NEVER_FITS.has(n.kind)) return true;
6911
- const need = LABEL_MIN_H[n.kind];
6912
- if (need === void 0) return false;
6913
- const h = (n.posX !== void 0 && n.posY !== void 0 ? n.posH : void 0) ?? n.h;
6914
- return h !== void 0 && h < need;
6915
- });
6916
- if (!\u53CE\u307E\u3089\u306A\u3044) continue;
6917
- for (const n of pair) {
6918
- if (LABEL_NEVER_FITS.has(n.kind) || LABEL_MIN_H[n.kind] !== void 0) n.kind = "card";
6919
- }
6920
- }
6921
- }
6922
7030
  function applyGroupContainers(diagram2, doc) {
6923
7031
  if (!doc.groups || Object.keys(doc.groups).length === 0) return;
6924
7032
  for (const [id, g] of Object.entries(doc.groups)) {
@@ -7086,34 +7194,46 @@ function compileGantt(doc) {
7086
7194
  return b.build();
7087
7195
  }
7088
7196
  function compileClass(doc) {
7089
- const b = cdl.diagram(slugify(doc.title), { topic: doc.title });
7090
- const CLASS_W = 400;
7091
- const CLASS_LANE_W = 450;
7092
- if (doc.actors.length === 0) return b.build();
7093
- doc.actors.forEach((a, idx) => {
7094
- const nodeId = slugify(a.name);
7095
- const laneId = `lane-${nodeId}`;
7096
- b.lane(laneId, { width: CLASS_LANE_W });
7097
- b.node(nodeId, {
7098
- lane: laneId,
7099
- stack: 0,
7100
- kind: "storage",
7197
+ const b = cdl.classDiagram({ id: slugify(doc.title), topic: doc.title });
7198
+ if (doc.actors.length === 0) return cdl.diagram(slugify(doc.title), { topic: doc.title }).build();
7199
+ const \u5217\u756A\u53F7 = /* @__PURE__ */ new Map();
7200
+ for (const a of doc.actors) {
7201
+ if (a.lane === void 0) continue;
7202
+ if (!\u5217\u756A\u53F7.has(a.lane)) \u5217\u756A\u53F7.set(a.lane, \u5217\u756A\u53F7.size);
7203
+ }
7204
+ for (const a of doc.actors) {
7205
+ const rows = (a.rows ?? []).filter((r) => !/^[─-]+$/.test(r.trim()));
7206
+ const \u632F\u308B\u821E\u3044 = (r) => r.includes("(");
7207
+ const attributes = rows.filter((r) => !\u632F\u308B\u821E\u3044(r));
7208
+ const methods = rows.filter(\u632F\u308B\u821E\u3044);
7209
+ b.class({
7210
+ id: slugify(a.name),
7101
7211
  title: \u7BB1\u306E\u984C(a),
7102
- w: CLASS_W
7212
+ ...attributes.length > 0 ? { attributes: [...attributes] } : {},
7213
+ ...methods.length > 0 ? { methods: [...methods] } : {},
7214
+ ...a.eyebrow ? { stereotype: a.eyebrow } : {},
7215
+ ...a.lane !== void 0 ? { col: \u5217\u756A\u53F7.get(a.lane) ?? 0 } : {},
7216
+ ...a.stack !== void 0 ? { row: a.stack } : {}
7103
7217
  });
7104
- });
7105
- for (const s of doc.flow) {
7106
- const fromId = slugify(s.from);
7107
- const toId = slugify(s.to);
7108
- b.edge(fromId, toId, {
7109
- label: s.label,
7110
- ...s.sub ? { sub: s.sub } : {},
7111
- ...s.side ? { side: s.side } : {},
7112
- ...s.tone ? { tone: s.tone } : {},
7113
- ...s.style ? { style: s.style } : {}
7218
+ }
7219
+ for (const s2 of doc.flow) {
7220
+ b.relation({
7221
+ from: slugify(s2.from),
7222
+ to: slugify(s2.to),
7223
+ // 種類を書かない矢印は「使う」 扱い = 端が開いた矢になり、線と印の組が最も素直
7224
+ type: s2.relation ?? "uses",
7225
+ ...s2.label ? { label: s2.label } : {},
7226
+ ...s2.sub ? { cardinality: s2.sub } : {},
7227
+ ...s2.tone ? { tone: s2.tone } : {},
7228
+ ...s2.style ? { style: s2.style } : {},
7229
+ ...s2.head ? { head: s2.head } : {},
7230
+ ...s2.headFill ? { headFill: s2.headFill } : {},
7231
+ ...s2.tailHead ? { tailHead: s2.tailHead } : {}
7114
7232
  });
7115
7233
  }
7116
- return b.build();
7234
+ const built = b.build();
7235
+ const \u66F8\u3044\u305F\u6BB5\u304C\u3042\u308B = (doc.animate?.phases.length ?? 0) > 0;
7236
+ return \u66F8\u3044\u305F\u6BB5\u304C\u3042\u308B ? { ...built, phases: [] } : built;
7117
7237
  }
7118
7238
  function parseShareValue(raw) {
7119
7239
  if (raw === void 0) return null;
@@ -7695,9 +7815,50 @@ function compileC4(doc) {
7695
7815
  }
7696
7816
  return b.build();
7697
7817
  }
7818
+ var \u984C\u3092\u6301\u305F\u306A\u3044\u7A2E\u985E = /* @__PURE__ */ new Set(["mark-start", "mark-end"]);
7698
7819
  function \u7BB1\u306E\u984C(a) {
7820
+ if (a.title === void 0 && a.kind !== void 0 && \u984C\u3092\u6301\u305F\u306A\u3044\u7A2E\u985E.has(a.kind)) return "";
7699
7821
  return a.title ?? a.name;
7700
7822
  }
7823
+ function \u884C\u3092\u7D44\u307F\u7ACB\u3066\u5668\u304C\u6301\u3064(type) {
7824
+ return type === "class";
7825
+ }
7826
+ function \u884C\u3068\u5370\u3092\u7D44\u3080(type, rows, marks) {
7827
+ const \u5370 = \u884C\u982D\u306E\u5370\u306B\u3059\u308B(type, marks);
7828
+ if (\u5370 === null) return null;
7829
+ if (type !== "er") {
7830
+ return { rows: [...rows], rowMarks: rows.map((_, i) => \u5370[i] ?? null) };
7831
+ }
7832
+ const \u9375 = [];
7833
+ const \u5024 = [];
7834
+ rows.forEach((_, i) => (\u5370[i]?.underline === true ? \u9375 : \u5024).push(i));
7835
+ const \u4E26\u3073 = \u9375.length > 0 && \u5024.length > 0 ? [...\u9375, -1, ...\u5024] : [...\u9375, ...\u5024];
7836
+ return {
7837
+ rows: \u4E26\u3073.map((i) => i < 0 ? "" : rows[i] ?? ""),
7838
+ rowMarks: \u4E26\u3073.map((i) => i < 0 ? null : \u5370[i] ?? null)
7839
+ };
7840
+ }
7841
+ function \u884C\u982D\u306E\u5370\u306B\u3059\u308B(type, marks) {
7842
+ if (type === "er") {
7843
+ return marks.map((m) => {
7844
+ const \u8A9E = m.trim().split(/\s+/).filter(Boolean);
7845
+ return {
7846
+ shape: \u8A9E.includes("fk") ? "chevron" : "square",
7847
+ filled: !\u8A9E.includes("opt"),
7848
+ ...\u8A9E.includes("pk") ? { underline: true } : {}
7849
+ };
7850
+ });
7851
+ }
7852
+ if (type === "state") {
7853
+ return marks.map((m) => {
7854
+ const \u8A9E = m.trim();
7855
+ if (\u8A9E === "") return null;
7856
+ const \u8868 = cdl.FSM_ACTION_MARK;
7857
+ return \u8868[\u8A9E] ?? null;
7858
+ });
7859
+ }
7860
+ return null;
7861
+ }
7701
7862
  var \u653E\u5C04\u3067\u63CF\u3051\u306A\u3044\u6B04\u306E\u540D\u524D = {
7702
7863
  kind: "\u7A2E\u985E",
7703
7864
  eyebrow: "\u4E0A\u306E\u5C0F\u898B\u51FA\u3057",
@@ -7706,6 +7867,7 @@ var \u653E\u5C04\u3067\u63CF\u3051\u306A\u3044\u6B04\u306E\u540D\u524D = {
7706
7867
  owner: "\u62C5\u5F53 (\u5DE5\u7A0B\u306E\u4E26\u3073\u306E\u6B04)",
7707
7868
  end: "\u7D42\u308F\u308B\u6642\u671F (\u5DE5\u7A0B\u306E\u4E26\u3073\u306E\u6B04)",
7708
7869
  rows: "\u884C",
7870
+ marks: "\u5370",
7709
7871
  lane: "\u67A0\u306E\u6307\u5B9A",
7710
7872
  stack: "\u7A4D\u3080\u9806",
7711
7873
  initial: "\u59CB\u307E\u308A / \u7D42\u308F\u308A \u306E\u5370",
@@ -7905,75 +8067,35 @@ function reportEmptyDeclaredLanes(diagram2, \u8FFD\u52A0\u3057\u305F\u7E26\u5217
7905
8067
  }
7906
8068
  }
7907
8069
  function applyV05Extensions(diagram2, doc, \u8FFD\u52A0\u3057\u305F\u7E26\u5217out) {
7908
- const isSeqLike = doc.type === "sequence" || doc.type === "solidity";
7909
8070
  for (const a of doc.actors) {
7910
8071
  const dragonSlug = slugify(a.name);
7911
- let primaryNodes;
7912
- if (isSeqLike) {
7913
- const ownedLaneIds = new Set(
7914
- diagram2.lanes.filter((l) => l.label === a.name).map((l) => l.id)
7915
- );
7916
- primaryNodes = ownedLaneIds.size > 0 ? diagram2.nodes.filter((n) => ownedLaneIds.has(n.lane) && n.id === `${n.lane}-header`) : diagram2.nodes.filter((n) => n.id === `${dragonSlug}-header`);
7917
- } else {
7918
- primaryNodes = diagram2.nodes.filter((n) => n.id === dragonSlug);
7919
- }
8072
+ const primaryNodes = diagram2.nodes.filter((n) => n.id === dragonSlug);
7920
8073
  for (const node of primaryNodes) {
7921
- if (a.title !== void 0) {
7922
- node.title = a.title;
7923
- if (isSeqLike) {
7924
- const footer = diagram2.nodes.find((n) => n.id === `${node.lane}-footer`);
7925
- if (footer) footer.title = a.title;
7926
- if (a.posW === void 0) {
7927
- const titleW = Math.max(140, a.title.length * 22 + 52);
7928
- node.w = titleW;
7929
- if (footer) footer.w = titleW;
7930
- }
7931
- }
7932
- }
8074
+ if (a.title !== void 0) node.title = a.title;
7933
8075
  const \u8AAC\u660E = doc.type === "c4" ? \u6BB5\u3092\u8AAD\u307F\u53D6\u308B(a.subtitle).\u8AAC\u660E : a.subtitle;
7934
8076
  if (\u8AAC\u660E !== void 0) node.subtitle = \u8AAC\u660E;
7935
8077
  if (a.eyebrow !== void 0) node.eyebrow = a.eyebrow;
7936
8078
  if (a.value !== void 0) node.value = a.value;
7937
- if (a.rows !== void 0) node.rows = a.rows;
7938
- if (!isSeqLike) {
7939
- if (a.posW !== void 0) node.w = a.posW;
7940
- if (a.posH !== void 0) node.h = a.posH;
7941
- }
7942
- const drawn = isSeqLike && a.kindWritten !== false ? drawableKind(a.kind) : void 0;
7943
- if (drawn !== void 0) {
7944
- node.kind = drawn;
7945
- const footer = diagram2.nodes.find((n) => n.id === `${node.lane}-footer`);
7946
- if (footer) footer.kind = drawn;
7947
- }
7948
- if (isSeqLike && a.rows !== void 0 && a.rows.length > 0 && cdl.rendersRows(a.kind)) {
7949
- node.h = Math.max(node.h ?? 0, cdl.requiredRowsHeight(a.kind, a.rows.length) ?? 0);
7950
- node.w = Math.max(node.w ?? 0, cdl.requiredRowsWidth(a.rows, a.kind));
7951
- }
7952
- if (isSeqLike) {
7953
- if (a.posW !== void 0) node.w = a.posW;
7954
- if (a.posH !== void 0) node.h = a.posH;
7955
- const footer = diagram2.nodes.find((n) => n.id === `${node.lane}-footer`);
7956
- if (footer && a.posW !== void 0) footer.w = a.posW;
8079
+ if (a.rows !== void 0 && !\u884C\u3092\u7D44\u307F\u7ACB\u3066\u5668\u304C\u6301\u3064(doc.type)) node.rows = a.rows;
8080
+ if (a.marks !== void 0 && a.rows !== void 0) {
8081
+ const \u7D44 = \u884C\u3068\u5370\u3092\u7D44\u3080(doc.type, a.rows, a.marks);
8082
+ if (\u7D44 !== null) {
8083
+ node.rows = \u7D44.rows;
8084
+ node.rowMarks = \u7D44.rowMarks;
8085
+ }
7957
8086
  }
8087
+ if (doc.type === "state" && node.kind === "storage" && node.rowMarks === void 0) {
8088
+ node.rows = a.rows ?? [];
8089
+ node.rowMarks = [];
8090
+ }
8091
+ if (a.posW !== void 0) node.w = a.posW;
8092
+ if (a.posH !== void 0) node.h = a.posH;
7958
8093
  if (a.shape !== void 0) {
7959
8094
  const dynamicKind = `dyn-${a.shape.kind}`;
7960
8095
  node.kind = dynamicKind;
7961
8096
  node.shape = { ...a.shape };
7962
- if (isSeqLike) {
7963
- const footer = diagram2.nodes.find((n) => n.id === `${node.lane}-footer`);
7964
- if (footer) {
7965
- footer.kind = dynamicKind;
7966
- footer.shape = { ...a.shape };
7967
- }
7968
- }
7969
- }
7970
- if (a.visibleIf !== void 0) {
7971
- node.visibleIf = a.visibleIf;
7972
- if (isSeqLike) {
7973
- const footer = diagram2.nodes.find((n) => n.id === `${node.lane}-footer`);
7974
- if (footer) footer.visibleIf = a.visibleIf;
7975
- }
7976
8097
  }
8098
+ if (a.visibleIf !== void 0) node.visibleIf = a.visibleIf;
7977
8099
  if (a.wBind !== void 0) node.wBind = a.wBind;
7978
8100
  if (a.hBind !== void 0) node.hBind = a.hBind;
7979
8101
  if (a.opacity !== void 0) node.opacity = a.opacity;
@@ -7981,8 +8103,6 @@ function applyV05Extensions(diagram2, doc, \u8FFD\u52A0\u3057\u305F\u7E26\u5217o
7981
8103
  if (a.renderOffsetY !== void 0) node.renderOffsetY = a.renderOffsetY;
7982
8104
  }
7983
8105
  }
7984
- alignSeqHeaderHeights(diagram2, doc);
7985
- dropUnfittableEndKinds(diagram2, doc);
7986
8106
  if (doc.animate && doc.animate.phases.length > 0 && diagram2.phases.length === 0) {
7987
8107
  injectPhasesFallback(diagram2, doc);
7988
8108
  }
@@ -8088,13 +8208,17 @@ function injectPhasesFallback(diagram2, doc) {
8088
8208
  }
8089
8209
  }
8090
8210
  function compileSequence(doc) {
8091
- if (doc.animate && doc.animate.phases.length > 0) {
8092
- return compileSequenceWithAnimate(doc);
8093
- }
8094
8211
  const seqBuilder = cdl.sequence({
8095
8212
  id: slugify(doc.title),
8096
8213
  topic: doc.title,
8097
- actors: doc.actors.map((a) => a.name)
8214
+ /*
8215
+ * 見出しに出すのは **書いた題** (#1466)。 名前は矢印の端として指すためのもので、
8216
+ * `title:` を書いたらそちらを出す (`箱の題`)。 板でも他の図種と同じ規約にする。
8217
+ */
8218
+ actors: doc.actors.map(
8219
+ (a) => a.subtitle ? { name: \u7BB1\u306E\u984C(a), subtitle: a.subtitle } : \u7BB1\u306E\u984C(a)
8220
+ ),
8221
+ ...doc.bands && doc.bands.length > 0 ? { bands: doc.bands } : {}
8098
8222
  });
8099
8223
  for (const s of doc.flow) {
8100
8224
  seqBuilder.step({
@@ -8104,140 +8228,43 @@ function compileSequence(doc) {
8104
8228
  ...s.sub ? { sub: s.sub } : {},
8105
8229
  ...s.side ? { side: s.side } : {},
8106
8230
  ...s.tone ? { tone: s.tone } : {},
8107
- ...s.style ? { style: s.style } : {}
8231
+ ...s.style ? { style: s.style } : {},
8232
+ ...s.msgKind ? { kind: s.msgKind } : {}
8108
8233
  });
8109
8234
  }
8110
- return seqBuilder.build();
8111
- }
8112
- function compileSequenceWithAnimate(doc) {
8113
- const b = cdl.diagram(slugify(doc.title), { topic: doc.title });
8114
- const laneW = 340;
8115
- const actorIds = /* @__PURE__ */ new Map();
8116
- doc.actors.forEach((a, i) => {
8117
- const id = slugify(a.name);
8118
- actorIds.set(a.name, id);
8119
- actorIds.set(id, id);
8120
- const laneOpts = { width: laneW, label: a.name, lifeline: true };
8121
- if (a.posX !== void 0 && a.posY !== void 0) {
8122
- laneOpts.posX = a.posX;
8123
- laneOpts.posY = a.posY;
8124
- if (a.posW !== void 0) laneOpts.posW = a.posW;
8125
- if (a.posH !== void 0) laneOpts.posH = a.posH;
8126
- }
8127
- b.lane(id, laneOpts);
8128
- const headerId = `${id}-header`;
8129
- const actorW = Math.max(140, \u7BB1\u306E\u984C(a).length * 22 + 52);
8130
- b.node(headerId, { lane: id, stack: 0, kind: "card", title: \u7BB1\u306E\u984C(a), w: actorW, h: 72 });
8131
- const spacerId = `${id}-spacer`;
8132
- b.node(spacerId, { lane: id, stack: 1, kind: "card", title: "", w: 2, h: 40 });
8133
- });
8134
- doc.flow.forEach((s, idx) => {
8135
- const fromLaneId = actorIds.get(s.from);
8136
- const toLaneId = actorIds.get(s.to);
8137
- if (fromLaneId === void 0 || toLaneId === void 0) return;
8138
- const stack = idx + 2;
8139
- const fromBoxId = `s${idx}-${fromLaneId}`;
8140
- const toBoxId = `s${idx}-${toLaneId}`;
8141
- b.node(fromBoxId, { lane: fromLaneId, stack, kind: "card", title: "", w: 2, h: 2 });
8142
- if (fromLaneId !== toLaneId) {
8143
- b.node(toBoxId, { lane: toLaneId, stack, kind: "card", title: "", w: 2, h: 2 });
8144
- }
8145
- const edgeId = `e${idx}-${fromLaneId}-${toLaneId}`;
8146
- b.edge(fromBoxId, fromLaneId === toLaneId ? fromBoxId : toBoxId, {
8147
- id: edgeId,
8148
- label: s.label,
8149
- ...s.sub ? { sub: s.sub } : {},
8150
- ...s.side ? { side: s.side } : {},
8151
- ...s.tone ? { tone: s.tone } : {},
8152
- ...s.style ? { style: s.style } : {}
8153
- });
8154
- });
8155
- const footerStack = doc.flow.length + 2;
8156
- doc.actors.forEach((a) => {
8157
- const laneId = actorIds.get(a.name) ?? slugify(a.name);
8158
- const footerId = `${laneId}-footer`;
8159
- const actorW = Math.max(140, \u7BB1\u306E\u984C(a).length * 22 + 52);
8160
- b.node(footerId, {
8161
- lane: laneId,
8162
- stack: footerStack,
8163
- kind: "card",
8164
- title: \u7BB1\u306E\u984C(a),
8165
- w: actorW,
8166
- h: 72,
8167
- role: "lifeline-footer"
8168
- });
8169
- });
8170
- for (const st of doc.animate?.states ?? []) {
8171
- b.state(st.name, { initial: st.initial });
8172
- }
8173
- for (const p of doc.animate?.phases ?? []) {
8174
- b.phase(
8175
- slugify(p.name),
8176
- {
8235
+ const built = seqBuilder.build();
8236
+ const \u6BB5 = doc.animate?.phases ?? [];
8237
+ if (\u6BB5.length === 0) return built;
8238
+ const \u756A\u53F7 = /* @__PURE__ */ new Map();
8239
+ doc.flow.forEach((f, i) => \u756A\u53F7.set(`${slugify(f.from)} -> ${slugify(f.to)}`, i));
8240
+ const \u72B6\u614B\u540D = cdl.sequenceStepId();
8241
+ return {
8242
+ ...built,
8243
+ states: [...built.states, { id: \u72B6\u614B\u540D, initial: "0" }],
8244
+ phases: \u6BB5.map((p, i) => {
8245
+ const \u756A = Math.max(
8246
+ 0,
8247
+ ...(p.highlight ?? []).map(
8248
+ (f) => \u756A\u53F7.get(f.split("->").map((x) => slugify(x.trim())).join(" -> ")) ?? -1
8249
+ )
8250
+ );
8251
+ return {
8252
+ id: `p${i}`,
8177
8253
  duration: p.durationMs,
8178
8254
  title: p.name,
8179
- body: p.body ?? ""
8180
- },
8181
- (pb) => {
8182
- const activateIds = resolveHighlight(p, doc, actorIds);
8183
- if (activateIds.length > 0) {
8184
- pb.activate(...activateIds);
8185
- }
8186
- for (const t of p.tweens ?? []) {
8187
- pb.tween(t.state, t.from, t.to);
8188
- }
8189
- for (const s of p.sets ?? []) {
8190
- pb.set(s.state, s.value);
8191
- }
8192
- if (p.badge) {
8193
- pb.badge(p.badge);
8194
- }
8195
- return pb;
8196
- }
8197
- );
8198
- }
8199
- return b.build();
8200
- }
8201
- function resolveHighlight(phase, doc, actorIds, _stepEdgeIds) {
8202
- const out = [];
8203
- const knownNames = new Set(actorIds.keys());
8204
- for (const raw of phase.highlight ?? []) {
8205
- const entry = parseFocusEntry(raw, knownNames);
8206
- if (entry.kind === "edge") {
8207
- const fromLaneId = actorIds.get(entry.from) ?? slugify(entry.from);
8208
- const toLaneId = actorIds.get(entry.to) ?? slugify(entry.to);
8209
- doc.flow.forEach((s, idx) => {
8210
- const sFromId = actorIds.get(s.from) ?? slugify(s.from);
8211
- const sToId = actorIds.get(s.to) ?? slugify(s.to);
8212
- if (sFromId === fromLaneId && sToId === toLaneId) {
8213
- out.push(`e${idx}-${fromLaneId}-${toLaneId}`);
8214
- }
8215
- });
8216
- const stackIdx = doc.flow.findIndex((s) => {
8217
- const sFromId = actorIds.get(s.from) ?? slugify(s.from);
8218
- const sToId = actorIds.get(s.to) ?? slugify(s.to);
8219
- return sFromId === fromLaneId && sToId === toLaneId;
8220
- });
8221
- if (stackIdx >= 0) {
8222
- out.push(`s${stackIdx}-${fromLaneId}`);
8223
- if (fromLaneId !== toLaneId) out.push(`s${stackIdx}-${toLaneId}`);
8224
- }
8225
- continue;
8226
- }
8227
- const laneId = actorIds.get(entry.name) ?? slugLookup(actorIds, entry.name);
8228
- if (laneId) {
8229
- out.push(`${laneId}-header`);
8230
- out.push(`${laneId}-footer`);
8231
- doc.flow.forEach((s, idx) => {
8232
- const sFromId = actorIds.get(s.from) ?? slugify(s.from);
8233
- const sToId = actorIds.get(s.to) ?? slugify(s.to);
8234
- if (sFromId === laneId || sToId === laneId) {
8235
- out.push(`s${idx}-${laneId}`);
8236
- }
8237
- });
8238
- }
8239
- }
8240
- return out;
8255
+ body: p.body ?? "",
8256
+ activate: [slugify(doc.title)],
8257
+ ...p.badge ? { badge: p.badge } : {},
8258
+ /*
8259
+ * **書いた状態の動きも一緒に運ぶ** (#1466)。 板の段は「今どの言づてか」 を状態に
8260
+ * 書くが、記法は同じ段に `遷移:` / `切替:` も書ける。 板の分だけを載せると、
8261
+ * 書いた動きが黙って落ちる (実測で `tweens` が 0 件になっていた)。
8262
+ */
8263
+ sets: [...(p.sets ?? []).map((x) => ({ stateId: x.state, value: x.value })), { stateId: \u72B6\u614B\u540D, value: \u756A }],
8264
+ tweens: (p.tweens ?? []).map((t) => ({ stateId: t.state, from: t.from, to: t.to }))
8265
+ };
8266
+ })
8267
+ };
8241
8268
  }
8242
8269
  function slugLookup(byName, wanted) {
8243
8270
  let hit;
@@ -8249,7 +8276,7 @@ function slugLookup(byName, wanted) {
8249
8276
  return hit;
8250
8277
  }
8251
8278
  function compileFlow(doc) {
8252
- if (doc.animate && doc.animate.phases.length > 0 || \u66F8\u3044\u305F\u7E26\u5217\u306B\u7F6E\u304F("flow", doc)) {
8279
+ if (doc.animate && doc.animate.phases.length > 0 || \u66F8\u3044\u305F\u7E26\u5217\u306B\u7F6E\u304F("flow", doc) || doc.direction !== void 0) {
8253
8280
  return compileGenericWithAnimate(doc, { kind: "flow", laneId: "main", laneWidth: 400 });
8254
8281
  }
8255
8282
  if (doc.actors.length === 0) {
@@ -8275,7 +8302,7 @@ function compileFlow(doc) {
8275
8302
  return flowBuilder.build();
8276
8303
  }
8277
8304
  function compileSwimlane(doc) {
8278
- if (doc.animate && doc.animate.phases.length > 0 || \u66F8\u3044\u305F\u7E26\u5217\u306B\u7F6E\u304F("swimlane", doc)) {
8305
+ if (doc.animate && doc.animate.phases.length > 0 || \u66F8\u3044\u305F\u7E26\u5217\u306B\u7F6E\u304F("swimlane", doc) || doc.direction !== void 0) {
8279
8306
  return compileGenericWithAnimate(doc, { kind: "swimlane", laneWidth: 400 });
8280
8307
  }
8281
8308
  const swim = cdl.swimlane({
@@ -8435,7 +8462,46 @@ function \u5F8C\u308D\u3078\u623B\u308B\u77E2\u5370\u304B(kind, fromId, toId, \u
8435
8462
  if (\u5143 === void 0 || \u5148 === void 0) return false;
8436
8463
  return \u5148 < \u5143;
8437
8464
  }
8438
- var \u7E26\u5217\u3092\u9078\u3079\u308B\u56F3\u7A2E = /* @__PURE__ */ new Set(["flow", "topology", "swimlane"]);
8465
+ var \u5411\u304D\u3092\u9078\u3079\u308B\u56F3\u7A2E = /* @__PURE__ */ new Set(["flow", "swimlane"]);
8466
+ function \u65E2\u5B9A\u306E\u5411\u304D(kind) {
8467
+ return kind === "flow" || kind === "topology" ? "\u7E26" : "\u6A2A";
8468
+ }
8469
+ function \u4E26\u3079\u308B\u5411\u304D(kind, doc) {
8470
+ if (doc.direction === void 0) return \u65E2\u5B9A\u306E\u5411\u304D(kind);
8471
+ if (!\u5411\u304D\u3092\u9078\u3079\u308B\u56F3\u7A2E.has(kind)) return \u65E2\u5B9A\u306E\u5411\u304D(kind);
8472
+ return doc.direction;
8473
+ }
8474
+ function reportDirectionNotHonored(doc, onNotice) {
8475
+ if (!onNotice) return;
8476
+ if (doc.direction === void 0) return;
8477
+ const \u884C = doc.directionPos?.line ?? doc.pos?.line ?? 0;
8478
+ if (!\u5411\u304D\u3092\u9078\u3079\u308B\u56F3\u7A2E.has(doc.type)) {
8479
+ onNotice({
8480
+ kind: "direction-not-honored",
8481
+ actor: doc.title,
8482
+ line: \u884C,
8483
+ message: `\u66F8\u3044\u305F direction \u306F\u52B9\u304D\u307E\u305B\u3093 (type: ${doc.type} \u306F\u4E26\u3073\u65B9\u305D\u306E\u3082\u306E\u304C\u8AAD\u307F\u65B9\u3092\u6C7A\u3081\u307E\u3059)`,
8484
+ hint: `direction \u3092\u66F8\u3051\u308B\u306E\u306F ${[...\u5411\u304D\u3092\u9078\u3079\u308B\u56F3\u7A2E].join(" / ")} \u3067\u3059`
8485
+ });
8486
+ return;
8487
+ }
8488
+ if (\u66F8\u3044\u305F\u7E26\u5217\u306B\u7F6E\u304F(doc.type, doc)) {
8489
+ onNotice({
8490
+ kind: "direction-not-honored",
8491
+ actor: doc.title,
8492
+ line: \u884C,
8493
+ message: "\u66F8\u3044\u305F direction \u306F\u52B9\u304D\u307E\u305B\u3093 (\u5168\u3066\u306E\u7BB1\u304C\u7E26\u5217\u3092\u66F8\u3044\u3066\u3044\u308B\u306E\u3067\u3001\u305D\u3061\u3089\u304C\u512A\u5148\u3055\u308C\u307E\u3059)",
8494
+ hint: "direction \u3067\u4E26\u3079\u305F\u3044\u6642\u306F\u7BB1\u306E `lane` \u3092\u5916\u3057\u3066\u304F\u3060\u3055\u3044"
8495
+ });
8496
+ }
8497
+ }
8498
+ var \u7E26\u5217\u3092\u9078\u3079\u308B\u56F3\u7A2E = /* @__PURE__ */ new Set([
8499
+ "flow",
8500
+ "topology",
8501
+ "swimlane",
8502
+ "class",
8503
+ "state"
8504
+ ]);
8439
8505
  function \u66F8\u3044\u305F\u7E26\u5217\u306B\u7F6E\u304F(kind, doc) {
8440
8506
  if (!\u7E26\u5217\u3092\u9078\u3079\u308B\u56F3\u7A2E.has(kind)) return false;
8441
8507
  const \u5BFE\u8C61 = doc.actors.filter((a) => a.partId === void 0);
@@ -8486,11 +8552,17 @@ function compileGenericWithAnimate(doc, opts) {
8486
8552
  title: \u7BB1\u306E\u984C(a)
8487
8553
  });
8488
8554
  });
8489
- } else if (kind === "flow" || kind === "topology") {
8555
+ } else if (\u4E26\u3079\u308B\u5411\u304D(kind, doc) === "\u7E26") {
8490
8556
  const lid = opts.laneId ?? "main";
8491
8557
  b.lane(lid, {
8492
8558
  width: laneWidth,
8493
- label: doc.title,
8559
+ /*
8560
+ * **見出しは自然に縦へ積む図種だけ** (#1494)。
8561
+ *
8562
+ * `direction: 縦` を書いた泳法図がここへ来るようになった。 その図に題を渡すと、図の題が
8563
+ * 縦列の見出しとしてもう 1 度出る (実測 = 「認証の流れ」 が題と見出しの 2 箇所に並んだ)。
8564
+ */
8565
+ ...kind === "flow" || kind === "topology" ? { label: doc.title } : {},
8494
8566
  ...kind === "topology" ? { contain: true } : {}
8495
8567
  });
8496
8568
  doc.actors.forEach((a, idx) => {
@@ -9063,7 +9135,7 @@ function writeVerticalForm(seg, headIdx, lineCount, headIndent, x, y) {
9063
9135
  else seg.splice(insertAt * 2, 0, newLine, sep);
9064
9136
  return seg.join("");
9065
9137
  }
9066
- var \u7DDA\u7A2E = ["solid", "dotted-flow"];
9138
+ var \u7DDA\u7A2E = cdl.EDGE_STYLES;
9067
9139
  var \u8272\u540D\u306E\u8868 = new Map([
9068
9140
  ...cdl.TONES.map((t) => [t.toLowerCase(), t]),
9069
9141
  ...Object.entries(TONE_ALIAS).map(([k, v]) => [k.toLowerCase(), v])
@@ -9230,7 +9302,13 @@ var ACCEPTED_KEYS = {
9230
9302
  "formulas",
9231
9303
  // 押下などの出来事で動く仕掛けと、巻き上げに応じて進む値 (#1393)
9232
9304
  "events",
9233
- "scrolls"
9305
+ "scrolls",
9306
+ // 順序図で面が動いている間の帯 (#1466)
9307
+ "bands",
9308
+ // 矢印をいつ出すか (#1470)
9309
+ "reveal",
9310
+ // 図の並ぶ向き (#1494)
9311
+ "direction"
9234
9312
  ],
9235
9313
  actor: [
9236
9314
  "name",
@@ -9270,7 +9348,9 @@ var ACCEPTED_KEYS = {
9270
9348
  "hBind",
9271
9349
  "opacity",
9272
9350
  "renderOffsetX",
9273
- "renderOffsetY"
9351
+ "renderOffsetY",
9352
+ // 行頭の印 (#1466)。 行ごとに 1 つ、図の種類ごとの語で書く
9353
+ "marks"
9274
9354
  ],
9275
9355
  step: [
9276
9356
  "from",
@@ -9279,6 +9359,14 @@ var ACCEPTED_KEYS = {
9279
9359
  "sub",
9280
9360
  // 矢印がどの辺から出るか (#1385)
9281
9361
  "side",
9362
+ // 矢印の先の形 (#1462)
9363
+ "head",
9364
+ // 端の印の残り 3 欄と、関係の語 / 言づての種類 (#1466)
9365
+ "tailHead",
9366
+ "headFill",
9367
+ "tailHeadFill",
9368
+ "relation",
9369
+ "kind",
9282
9370
  "tone",
9283
9371
  "style",
9284
9372
  "guard",
@@ -9324,7 +9412,13 @@ var \u6B04\u306E\u578B\u8868 = {
9324
9412
  formulas: "object",
9325
9413
  // 押下と巻き上げ (#1393)。 中身は下の検査が 1 件ずつ見る
9326
9414
  events: "\u4E26\u3073",
9327
- scrolls: "object"
9415
+ scrolls: "object",
9416
+ // 順序図で面が動いている間の帯 (#1466)
9417
+ bands: "\u4E26\u3073",
9418
+ // 矢印をいつ出すか (#1470)
9419
+ reveal: "\u975E\u7A7A\u306E\u6587\u5B57\u5217",
9420
+ // 図の並ぶ向き (#1494)
9421
+ direction: "\u975E\u7A7A\u306E\u6587\u5B57\u5217"
9328
9422
  },
9329
9423
  actor: {
9330
9424
  name: "\u5FC5\u9808\u306E\u975E\u7A7A\u6587\u5B57\u5217",
@@ -9363,7 +9457,9 @@ var \u6B04\u306E\u578B\u8868 = {
9363
9457
  hBind: "\u975E\u7A7A\u306E\u6587\u5B57\u5217",
9364
9458
  opacity: "\u6570\u304B\u6587\u5B57\u5217",
9365
9459
  renderOffsetX: "\u6570\u304B\u6587\u5B57\u5217",
9366
- renderOffsetY: "\u6570\u304B\u6587\u5B57\u5217"
9460
+ renderOffsetY: "\u6570\u304B\u6587\u5B57\u5217",
9461
+ // 行頭の印 (#1466)
9462
+ marks: "\u6587\u5B57\u5217\u306E\u4E26\u3073"
9367
9463
  },
9368
9464
  step: {
9369
9465
  from: "\u5FC5\u9808\u306E\u6587\u5B57\u5217",
@@ -9372,6 +9468,13 @@ var \u6B04\u306E\u578B\u8868 = {
9372
9468
  sub: "\u6587\u5B57\u5217",
9373
9469
  // 矢印がどの辺から出るか (#1385)
9374
9470
  side: "\u8FBA",
9471
+ head: "\u7AEF\u306E\u5F62",
9472
+ // 端の印の残り 3 欄と、関係の語 / 言づての種類 (#1466)
9473
+ tailHead: "\u7AEF\u306E\u5F62",
9474
+ headFill: "\u975E\u7A7A\u306E\u6587\u5B57\u5217",
9475
+ tailHeadFill: "\u975E\u7A7A\u306E\u6587\u5B57\u5217",
9476
+ relation: "\u975E\u7A7A\u306E\u6587\u5B57\u5217",
9477
+ kind: "\u975E\u7A7A\u306E\u6587\u5B57\u5217",
9375
9478
  tone: "\u8272",
9376
9479
  style: "\u7DDA\u7A2E",
9377
9480
  guard: "\u6587\u5B57\u5217",
@@ -9553,6 +9656,16 @@ function \u5024\u3092\u691C\u67FB(v, \u578B, path, \u540D\u524D, errors) {
9553
9656
  });
9554
9657
  }
9555
9658
  return;
9659
+ case "\u7AEF\u306E\u5F62":
9660
+ if (v === void 0) return;
9661
+ if (typeof v !== "string" || !EDGE_HEAD_VALUES.includes(v)) {
9662
+ errors.push({
9663
+ path,
9664
+ message: `${\u540D\u524D} must be one of: ${EDGE_HEAD_VALUES.join(", ")}`,
9665
+ hint: typeof v === "string" ? `got "${v}"` : `got ${typeof v}`
9666
+ });
9667
+ }
9668
+ return;
9556
9669
  case "\u63CF\u304F\u3082\u306E":
9557
9670
  if (v === void 0) return;
9558
9671
  if (typeof v !== "string" || !DRAW_WORDS.has(v)) {
@@ -9737,6 +9850,33 @@ function \u8868\u3067\u4E2D\u8EAB\u3092\u691C\u67FB\u3059\u308B(o, \u8868, path,
9737
9850
  }
9738
9851
  }
9739
9852
  }
9853
+ function validateBands(v, errors) {
9854
+ if (v === void 0) return;
9855
+ if (!Array.isArray(v)) {
9856
+ errors.push({
9857
+ path: "$.bands",
9858
+ message: "bands must be an array of band objects",
9859
+ hint: `got ${v === null ? "null" : typeof v}`
9860
+ });
9861
+ return;
9862
+ }
9863
+ v.forEach((b, i) => {
9864
+ const path = `$.bands[${i}]`;
9865
+ if (typeof b !== "object" || b === null || Array.isArray(b)) {
9866
+ errors.push({ path, message: "band must be an object", hint: "{ actor, from, to } \u306E\u5F62\u3067\u66F8\u304F" });
9867
+ return;
9868
+ }
9869
+ const o = b;
9870
+ if (typeof o.actor !== "string" || o.actor === "") {
9871
+ errors.push({ path: `${path}.actor`, message: "band.actor must be a non-empty string" });
9872
+ }
9873
+ for (const k of ["from", "to"]) {
9874
+ if (typeof o[k] !== "number" || !Number.isInteger(o[k]) || o[k] < 0) {
9875
+ errors.push({ path: `${path}.${k}`, message: `band.${k} must be a non-negative integer` });
9876
+ }
9877
+ }
9878
+ });
9879
+ }
9740
9880
  function validateReadouts(v, errors) {
9741
9881
  if (v === void 0) return;
9742
9882
  if (!Array.isArray(v)) {
@@ -10201,6 +10341,7 @@ function validateJson(json) {
10201
10341
  const j = \u5199\u3057.value;
10202
10342
  checkUnknownKeys(j, "root", "$", errors);
10203
10343
  validateReadouts(j.readouts, errors);
10344
+ validateBands(j.bands, errors);
10204
10345
  validateInputs(j.inputs, errors);
10205
10346
  validateFormulas(j.formulas, j.inputs, errors);
10206
10347
  validateEvents(j.events, errors);
@@ -10434,6 +10575,8 @@ function jsonToDoc(json) {
10434
10575
  value: a.value,
10435
10576
  previous: a.previous,
10436
10577
  rows: a.rows,
10578
+ // 行頭の印 (#1466)。 行と対で読む
10579
+ marks: a.marks,
10437
10580
  lane: a.lane,
10438
10581
  stack: a.stack,
10439
10582
  initial: a.initial,
@@ -10489,6 +10632,14 @@ function jsonToDoc(json) {
10489
10632
  label: s.label,
10490
10633
  sub: s.sub,
10491
10634
  side: s.side,
10635
+ // 矢印の先の形 (#1462)。 読めない語は組み立てが落とす
10636
+ head: s.head,
10637
+ // 端の印の残り 3 欄と、関係の語 / 言づての種類 (#1466)
10638
+ tailHead: s.tailHead,
10639
+ headFill: s.headFill,
10640
+ tailHeadFill: s.tailHeadFill,
10641
+ relation: s.relation,
10642
+ msgKind: s.kind,
10492
10643
  // 箱と同じ読み替えを通す (#1304)。 通さないと `tone: "成功"` が色名として解決されないまま
10493
10644
  // 図に届き、同じ値が箱では色になり矢印では色にならない
10494
10645
  tone: resolveTone(s.tone),
@@ -10584,6 +10735,12 @@ function jsonToDoc(json) {
10584
10735
  formulas,
10585
10736
  events,
10586
10737
  scrolls,
10738
+ // 順序図で面が動いている間の帯 (#1466)
10739
+ bands: json.bands,
10740
+ // 矢印をいつ出すか (#1470)
10741
+ reveal: json.reveal,
10742
+ // 図の並ぶ向き (#1494)。 JSON は英語で書くので、記法と同じ語に直してから渡す
10743
+ ...json.direction !== void 0 ? { direction: json.direction === "horizontal" ? "\u6A2A" : "\u7E26" } : {},
10587
10744
  pos: p0
10588
10745
  };
10589
10746
  }
@@ -10727,6 +10884,13 @@ var diagram_default = {
10727
10884
  },
10728
10885
  description: "storage \u5185\u306E column \u5217 (kind: storage \u7528)"
10729
10886
  },
10887
+ marks: {
10888
+ type: "array",
10889
+ items: {
10890
+ type: "string"
10891
+ },
10892
+ description: "\u884C\u982D\u306E\u5370\u3002 rows \u3068\u540C\u3058\u6570\u3060\u3051\u4E26\u3079\u308B\u3002 \u8A9E\u306E\u610F\u5473\u306F\u56F3\u306E\u7A2E\u985E\u304C\u6C7A\u3081\u308B = er \u306F pk / fk / opt\u3001class \u306F + / - \u3068 () \u306E\u6709\u7121\u3001state \u306F entry / do / exit / internal"
10893
+ },
10730
10894
  lane: {
10731
10895
  type: "string",
10732
10896
  description: "swimlane / topology \u3067\u5C5E\u3059\u308B lane id"
@@ -11061,7 +11225,7 @@ var diagram_default = {
11061
11225
  },
11062
11226
  style: {
11063
11227
  type: "string",
11064
- enum: ["solid", "dotted-flow"],
11228
+ enum: ["solid", "dotted-flow", "dashed"],
11065
11229
  description: "\u77E2\u5370\u306E\u7DDA\u7A2E"
11066
11230
  },
11067
11231
  guard: {
@@ -11077,6 +11241,36 @@ var diagram_default = {
11077
11241
  enum: ["top", "right", "bottom", "left"],
11078
11242
  description: "\u77E2\u5370\u304C\u3069\u306E\u8FBA\u304B\u3089\u51FA\u308B\u304B\u3002 \u66F8\u304B\u306A\u3051\u308C\u3070\u63CF\u753B\u5074\u304C\u81EA\u52D5\u3067\u9078\u3076\u3002"
11079
11243
  },
11244
+ head: {
11245
+ type: "string",
11246
+ enum: ["none", "triangle", "diamond", "open", "crow", "one", "zero-one", "many", "zero-many"],
11247
+ description: "\u7740\u304D\u5148\u5074\u306E\u7AEF\u306E\u5F62\u3002 \u7AEF\u306E\u5F62\u3067\u95A2\u4FC2\u306E\u7A2E\u985E\u3092\u793A\u3059 = triangle (\u7D99\u3050) / diamond (\u6301\u3064) / open (\u4F7F\u3046) / crow (\u591A) / one / zero-one / many / zero-many (\u500B\u6570)\u3002 \u66F8\u304B\u306A\u3051\u308C\u3070\u5857\u3063\u305F\u4E09\u89D2\u306B\u306A\u308B\u3002"
11248
+ },
11249
+ tailHead: {
11250
+ type: "string",
11251
+ enum: ["none", "triangle", "diamond", "open", "crow", "one", "zero-one", "many", "zero-many"],
11252
+ description: "\u51FA\u3069\u3053\u308D\u5074\u306E\u7AEF\u306E\u5F62\u3002 \u4E21\u7AEF\u306B\u5225\u3005\u306E\u5370\u3092\u7ACB\u3066\u308B\u6642\u306B\u66F8\u304F\u3002"
11253
+ },
11254
+ headFill: {
11255
+ type: "string",
11256
+ enum: ["solid", "hollow"],
11257
+ description: "\u7740\u304D\u5148\u5074\u306E\u7AEF\u306E\u5857\u308A\u3002 \u4E2D\u7A7A\u306F\u300C\u5F31\u3044\u95A2\u4FC2\u300D \u3092\u8868\u3059\u3002"
11258
+ },
11259
+ tailHeadFill: {
11260
+ type: "string",
11261
+ enum: ["solid", "hollow"],
11262
+ description: "\u51FA\u3069\u3053\u308D\u5074\u306E\u7AEF\u306E\u5857\u308A\u3002"
11263
+ },
11264
+ relation: {
11265
+ type: "string",
11266
+ enum: ["extends", "implements", "aggregates", "composes", "associates", "uses"],
11267
+ description: "\u30AF\u30E9\u30B9\u56F3\u306E\u95A2\u4FC2\u306E\u8A9E\u3002 \u66F8\u304F\u3068\u7AEF\u306E\u5F62 / \u5857\u308A / \u7DDA\u7A2E\u304C\u307E\u3068\u3081\u3066\u6C7A\u307E\u308B\u3002"
11268
+ },
11269
+ kind: {
11270
+ type: "string",
11271
+ enum: ["call", "return", "fire"],
11272
+ description: "\u9806\u5E8F\u56F3\u306E\u8A00\u3065\u3066\u306E\u7A2E\u985E\u3002 call (\u8FD4\u4E8B\u3092\u5F85\u3064) / return (\u8FD4\u3057) / fire (\u8FD4\u4E8B\u3092\u5F85\u305F\u306A\u3044)\u3002"
11273
+ },
11080
11274
  labelOffsetX: {
11081
11275
  type: "number",
11082
11276
  description: "\u30E9\u30D9\u30EB\u4F4D\u7F6E x \u8ABF\u6574"
@@ -12363,6 +12557,30 @@ var diagram_default = {
12363
12557
  pattern: "^[A-Za-z_][A-Za-z0-9_]*$"
12364
12558
  }
12365
12559
  },
12560
+ direction: {
12561
+ type: "string",
12562
+ enum: ["vertical", "horizontal"],
12563
+ description: "\u56F3\u306E\u4E26\u3076\u5411\u304D\u3002 vertical = 1 \u3064\u306E\u7E26\u5217\u306B\u7A4D\u3080\u3002 horizontal = 1 \u4EBA\u305A\u3064\u7E26\u5217\u3092\u4F5C\u308B\u3002 \u52B9\u304F\u306E\u306F flow \u3068 swimlane \u3060\u3051\u3002"
12564
+ },
12565
+ reveal: {
12566
+ type: "string",
12567
+ enum: ["phase", "all"],
12568
+ description: "\u77E2\u5370\u3092\u3044\u3064\u51FA\u3059\u304B\u3002 phase (\u65E2\u5B9A) = \u6BB5\u304C\u540D\u6307\u3057\u3059\u308B\u77E2\u5370\u306F\u305D\u306E\u6BB5\u304C\u6765\u308B\u307E\u3067\u63CF\u304B\u306A\u3044\u3002 all = \u6BB5\u306B\u95A2\u308F\u3089\u305A\u6700\u521D\u304B\u3089\u5168\u90E8\u63CF\u304F\u3002"
12569
+ },
12570
+ bands: {
12571
+ type: "array",
12572
+ description: "\u9806\u5E8F\u56F3\u3067\u9762\u304C\u52D5\u3044\u3066\u3044\u308B\u9593\u306E\u5E2F\u3002 \u66F8\u304B\u306A\u3051\u308C\u3070\u9762\u3054\u3068\u306B\u300C\u6700\u521D\u306B\u95A2\u308F\u3063\u305F\u6BB5\u304B\u3089\u6700\u5F8C\u307E\u3067\u300D \u306E 1 \u672C\u306B\u306A\u308B\u3002",
12573
+ items: {
12574
+ type: "object",
12575
+ required: ["actor", "from", "to"],
12576
+ additionalProperties: false,
12577
+ properties: {
12578
+ actor: { type: "string", description: "\u5E2F\u3092\u51FA\u3059\u9762\u306E\u540D\u524D" },
12579
+ from: { type: "integer", minimum: 0, description: "\u59CB\u307E\u308A\u306E\u8A00\u3065\u3066\u306E\u756A\u53F7 (0 \u8D77\u70B9)" },
12580
+ to: { type: "integer", minimum: 0, description: "\u7D42\u308F\u308A\u306E\u8A00\u3065\u3066\u306E\u756A\u53F7 (0 \u8D77\u70B9)" }
12581
+ }
12582
+ }
12583
+ },
12366
12584
  events: {
12367
12585
  type: "array",
12368
12586
  description: "\u62BC\u4E0B\u306A\u3069\u306E\u51FA\u6765\u4E8B\u3067\u52D5\u304F\u4ED5\u639B\u3051\u3002 \u76F8\u624B\u306F\u540D\u524D\u3067\u6307\u3059 (box / lane / arrow / diagram \u306E\u3069\u308C\u304B 1 \u3064)\u3002",
@@ -12579,6 +12797,7 @@ exports.partScaleFactor = partScaleFactor;
12579
12797
  exports.partTargetScale = partTargetScale;
12580
12798
  exports.partTargetSize = partTargetSize;
12581
12799
  exports.partVisualSize = partVisualSize;
12800
+ exports.partsBaseRows = partsBaseRows;
12582
12801
  exports.partsGridCenters = partsGridCenters;
12583
12802
  exports.pointsOutside = pointsOutside;
12584
12803
  exports.rectsOverlap = rectsOverlap;