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