@cardenelabs/cdl 0.53.0 → 0.55.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.
@@ -105,7 +105,7 @@ type Side = "top" | "right" | "bottom" | "left";
105
105
  * - "dashed" ... 刻んだ線。 **光っていても刻みのまま**。
106
106
  *
107
107
  * `"dashed"` が要るのは、`"solid"` の刻みが「光っていない」 を表す合図に使われているため。
108
- * 光ると実線に戻るので、線の種類として刻みを持たせられない。 UML の「満たす」 と「使う」、
108
+ * 光ると実線に戻るので、線の種類として刻みを持たせられない。 UML の「実装」 と「依存」、
109
109
  * ER の「識別しない」 のように **刻みが意味を持つ関係** はこちらを使う。
110
110
  */
111
111
  type EdgeStyle = "solid" | "dotted-flow" | "dashed";
@@ -139,8 +139,8 @@ declare const EDGE_STYLES: readonly EdgeStyle[];
139
139
  * 4 通りを 2 つの欄に分けないのは、**端の印は 1 つの記号として読まれる**ため。 欄を分けると
140
140
  * 片方だけ書いた形 (個数はあるが任意かが無い) を許すことになり、描けない組み合わせが増える。
141
141
  *
142
- * 線の種類と分けて持つのは、両者が独立に組み合わさるため。 実線 × 三角 (継ぐ) と
143
- * 破線 × 三角 (満たす) は線種で分かれ、実線 × 三角 (継ぐ) と実線 × 菱 (持つ) は端で分かれる。
142
+ * 線の種類と分けて持つのは、両者が独立に組み合わさるため。 実線 × 三角 (継承) と
143
+ * 破線 × 三角 (実装) は線種で分かれ、実線 × 三角 (継承) と実線 × 菱 (集約) は端で分かれる。
144
144
  * 1 つの欄に畳むと 2 軸の積を名前で列挙することになり、組合せが増えるたびに語が増える。
145
145
  */
146
146
  type EdgeHead = "none" | "triangle" | "diamond" | "open" | "crow" | "one" | "zero-one" | "many" | "zero-many";
@@ -154,7 +154,8 @@ declare const EDGE_HEADS: readonly EdgeHead[];
154
154
  * - `"solid"` ... 墨で塗る。 **既定**。 書かなければ形ごとの既定 (`EDGE_HEAD_SHAPE.filled`) に従う
155
155
  * - `"hollow"` ... 紙の色で塗り、輪郭だけを線で描く
156
156
  *
157
- * UML は同じ形を塗りで分ける = 白抜きの菱が「持つ」、塗った菱が「抱える」 (命が同じ)。
157
+ * UML は同じ形を塗りで分ける = 白抜きの菱が「集約」、塗った菱が「コンポジション」
158
+ * (部分が全体と寿命を共にする)。
158
159
  * 形を増やして表そうとすると、塗りの軸を持つ全ての形について語が倍になる。
159
160
  *
160
161
  * 塗らない形 (`"open"` / `"crow"`) には効かない。 元から輪郭しか無いため。
@@ -664,8 +665,8 @@ type CdlEdge = {
664
665
  /**
665
666
  * 端の印の塗り方 (#578)。 書かなければ形ごとの既定。 塗らない形には効かない。
666
667
  *
667
- * 形と分けて持つのは、UML が同じ形を塗りで分けるため (白抜きの菱 = 持つ /
668
- * 塗った菱 = 抱える)。
668
+ * 形と分けて持つのは、UML が同じ形を塗りで分けるため (白抜きの菱 = 集約 /
669
+ * 塗った菱 = コンポジション)。
669
670
  */
670
671
  headFill?: EdgeHeadFill;
671
672
  /**
@@ -105,7 +105,7 @@ type Side = "top" | "right" | "bottom" | "left";
105
105
  * - "dashed" ... 刻んだ線。 **光っていても刻みのまま**。
106
106
  *
107
107
  * `"dashed"` が要るのは、`"solid"` の刻みが「光っていない」 を表す合図に使われているため。
108
- * 光ると実線に戻るので、線の種類として刻みを持たせられない。 UML の「満たす」 と「使う」、
108
+ * 光ると実線に戻るので、線の種類として刻みを持たせられない。 UML の「実装」 と「依存」、
109
109
  * ER の「識別しない」 のように **刻みが意味を持つ関係** はこちらを使う。
110
110
  */
111
111
  type EdgeStyle = "solid" | "dotted-flow" | "dashed";
@@ -139,8 +139,8 @@ declare const EDGE_STYLES: readonly EdgeStyle[];
139
139
  * 4 通りを 2 つの欄に分けないのは、**端の印は 1 つの記号として読まれる**ため。 欄を分けると
140
140
  * 片方だけ書いた形 (個数はあるが任意かが無い) を許すことになり、描けない組み合わせが増える。
141
141
  *
142
- * 線の種類と分けて持つのは、両者が独立に組み合わさるため。 実線 × 三角 (継ぐ) と
143
- * 破線 × 三角 (満たす) は線種で分かれ、実線 × 三角 (継ぐ) と実線 × 菱 (持つ) は端で分かれる。
142
+ * 線の種類と分けて持つのは、両者が独立に組み合わさるため。 実線 × 三角 (継承) と
143
+ * 破線 × 三角 (実装) は線種で分かれ、実線 × 三角 (継承) と実線 × 菱 (集約) は端で分かれる。
144
144
  * 1 つの欄に畳むと 2 軸の積を名前で列挙することになり、組合せが増えるたびに語が増える。
145
145
  */
146
146
  type EdgeHead = "none" | "triangle" | "diamond" | "open" | "crow" | "one" | "zero-one" | "many" | "zero-many";
@@ -154,7 +154,8 @@ declare const EDGE_HEADS: readonly EdgeHead[];
154
154
  * - `"solid"` ... 墨で塗る。 **既定**。 書かなければ形ごとの既定 (`EDGE_HEAD_SHAPE.filled`) に従う
155
155
  * - `"hollow"` ... 紙の色で塗り、輪郭だけを線で描く
156
156
  *
157
- * UML は同じ形を塗りで分ける = 白抜きの菱が「持つ」、塗った菱が「抱える」 (命が同じ)。
157
+ * UML は同じ形を塗りで分ける = 白抜きの菱が「集約」、塗った菱が「コンポジション」
158
+ * (部分が全体と寿命を共にする)。
158
159
  * 形を増やして表そうとすると、塗りの軸を持つ全ての形について語が倍になる。
159
160
  *
160
161
  * 塗らない形 (`"open"` / `"crow"`) には効かない。 元から輪郭しか無いため。
@@ -664,8 +665,8 @@ type CdlEdge = {
664
665
  /**
665
666
  * 端の印の塗り方 (#578)。 書かなければ形ごとの既定。 塗らない形には効かない。
666
667
  *
667
- * 形と分けて持つのは、UML が同じ形を塗りで分けるため (白抜きの菱 = 持つ /
668
- * 塗った菱 = 抱える)。
668
+ * 形と分けて持つのは、UML が同じ形を塗りで分けるため (白抜きの菱 = 集約 /
669
+ * 塗った菱 = コンポジション)。
669
670
  */
670
671
  headFill?: EdgeHeadFill;
671
672
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cardenelabs/cdl",
3
- "version": "0.53.0",
3
+ "version": "0.55.0",
4
4
  "description": "CDL (Chainome Diagram Language). Mermaid-like declarative DSL that compiles to animated SVG diagrams. Built for blockchain / Solidity flows, generic enough for sequence / flow / state / ER / topology diagrams.",
5
5
  "license": "MIT",
6
6
  "author": "cardene777",
@@ -7,7 +7,7 @@
7
7
  * 端での線の向きで決める。 端から `端の字を戻す距離` だけ線を戻った点を基準に、線の脇へ置く。
8
8
  * 字は端に近い側の縁を基準点に揃える = 字の長さが変わっても、端から字までの隙間は変わらない。
9
9
  *
10
- * **線の札の反対側に置く**。 札と同じ側に置くと、札の語と多重度が続けて読まれる (「持つ」 の
10
+ * **線の札の反対側に置く**。 札と同じ側に置くと、札の語と多重度が続けて読まれる (「集約」 の
11
11
  * 隣に `1..*`、 英字の頃は「HAS1」)。 実測で、札と同じ側 (横向きなら上、縦向きなら右) に
12
12
  * 置く形は見本帳のクラス図で 1 本が札と重なり、 もう 1 本が札と 2 world まで寄った。
13
13
  * 札を持たない線は、横向きなら下、縦向きなら左に置く (engine が札を置く既定の側の反対)。
@@ -25,7 +25,7 @@ import { measureTextWidth } from "./text-width";
25
25
  * 端から字までの、線に沿った距離 (world)。
26
26
  *
27
27
  * 端の印の長さの最大 (光っている時の 14、 `render/stage.tsx` の marker の大きさ) に隙間 8 を足す。
28
- * 印を持たない端 (菱が出どころ側にある「持つ」 「抱える」 の行き先) でも同じ距離にする = 端ごとに
28
+ * 印を持たない端 (菱が出どころ側にある「集約」 「コンポジション」 の行き先) でも同じ距離にする = 端ごとに
29
29
  * 距離が変わると、図の中で多重度の位置が揃わない。
30
30
  */
31
31
  export const 端の字を戻す距離 = 22;
package/src/presets.ts CHANGED
@@ -345,7 +345,7 @@ export function flow(preset: FlowPreset): FlowBuilder {
345
345
  build() {
346
346
  b.phase(
347
347
  "flow",
348
- { duration: 2400, title: preset.topic, body: "全 step 順次実行。" },
348
+ { duration: 2400, title: preset.topic, body: "すべての段を上から順に通った流れ。" },
349
349
  (p) => p.activate(...phaseActivate).badge("flow"),
350
350
  );
351
351
  return b.build();
@@ -366,7 +366,7 @@ export function flow(preset: FlowPreset): FlowBuilder {
366
366
  * | `return` | 破線 | 開いた矢 | 呼ばれた側から戻る。 新しい仕事ではないので線を切る |
367
367
  * | `fire` | 実線 | 開いた矢 | 返事を待たない。 実線だが矢を閉じないことで待たないと示す |
368
368
  *
369
- * 印の形はクラス図の「継ぐ」 と「結ぶ」 で使った形そのもの。 **意味は違う** (あちらは親を
369
+ * 印の形はクラス図の「継承」 と「関連」 で使った形そのもの。 **意味は違う** (あちらは親を
370
370
  * 指す / こちらは返事を待つか) が、形の並びは共有できる。
371
371
  */
372
372
  export type SequenceMessageKind = "call" | "return" | "fire";
@@ -517,7 +517,7 @@ export function sequence(preset: SequencePreset): SequenceBuilder {
517
517
  });
518
518
  b.phase(
519
519
  "sequence",
520
- { duration: 2400, title: preset.topic, body: "sequence の全 message を時系列展開。" },
520
+ { duration: 2400, title: preset.topic, body: "すべてのやり取りを時間の順に並べた姿。" },
521
521
  (p) => p.activate(preset.id).badge("sequence"),
522
522
  );
523
523
  return b.build();
@@ -653,7 +653,7 @@ export function topology(preset: TopologyPreset): TopologyBuilder {
653
653
  build() {
654
654
  b.phase(
655
655
  "topo",
656
- { duration: 2400, title: preset.topic, body: "topology の全 container + connection を visible 化。" },
656
+ { duration: 2400, title: preset.topic, body: "すべての構成要素と接続を並べた構成。" },
657
657
  (p) => p.activate(...phaseActivate).badge("topology"),
658
658
  );
659
659
  return b.build();
@@ -883,7 +883,7 @@ export function er(preset: ErPreset): ErBuilder {
883
883
  build() {
884
884
  b.phase(
885
885
  "er",
886
- { duration: 2400, title: preset.topic, body: "ER 図の全 entity + relation を visible 化。" },
886
+ { duration: 2400, title: preset.topic, body: "すべての表と関係を並べた構造。" },
887
887
  (p) => p.activate(...phaseActivate).badge("er"),
888
888
  );
889
889
  return b.build();
@@ -1158,7 +1158,7 @@ export function stateMachine(preset: StateMachinePreset): StateMachineBuilder {
1158
1158
 
1159
1159
  b.phase(
1160
1160
  "fsm",
1161
- { duration: 2400, title: preset.topic, body: "FSM の全 state + transition を visible 化。" },
1161
+ { duration: 2400, title: preset.topic, body: "すべての状態と遷移を並べた姿。" },
1162
1162
  (p) => p.activate(...phaseActivate).badge("fsm"),
1163
1163
  );
1164
1164
  return b.build();
@@ -1291,7 +1291,7 @@ export function infrastructure(preset: InfrastructurePreset): InfrastructureBuil
1291
1291
  build() {
1292
1292
  b.phase(
1293
1293
  "infra",
1294
- { duration: 2400, title: preset.topic, body: "infrastructure 全 node + connection を visible 化。" },
1294
+ { duration: 2400, title: preset.topic, body: "すべての構成要素と接続を並べた構成。" },
1295
1295
  (p) => p.activate(...phaseActivate).badge("infrastructure"),
1296
1296
  );
1297
1297
  return b.build();
@@ -1322,12 +1322,12 @@ export type ClassRelationType =
1322
1322
  *
1323
1323
  * | 種類 | 線 | 形 | 塗り | 側 | 意味 |
1324
1324
  * |---|---|---|---|---|---|
1325
- * | `extends` | 実線 | 三角 | 白抜き | 行き先 | 継ぐ |
1326
- * | `implements` | 破線 | 三角 | 白抜き | 行き先 | 満たす |
1327
- * | `aggregates` | 実線 | 菱 | 白抜き | 出どころ | 持つ |
1328
- * | `composes` | 実線 | 菱 | 塗る | 出どころ | 抱える |
1329
- * | `associates` | 実線 | 開いた矢 | — | 行き先 | 結ぶ |
1330
- * | `uses` | 破線 | 開いた矢 | — | 行き先 | 使う |
1325
+ * | `extends` | 実線 | 三角 | 白抜き | 行き先 | 継承 |
1326
+ * | `implements` | 破線 | 三角 | 白抜き | 行き先 | 実装 |
1327
+ * | `aggregates` | 実線 | 菱 | 白抜き | 出どころ | 集約 |
1328
+ * | `composes` | 実線 | 菱 | 塗る | 出どころ | コンポジション |
1329
+ * | `associates` | 実線 | 開いた矢 | — | 行き先 | 関連 |
1330
+ * | `uses` | 破線 | 開いた矢 | — | 行き先 | 依存 |
1331
1331
  *
1332
1332
  * **側が種で割れるのが要点**。 三角は「この先が親」 を指すので行き先に立ち、菱は
1333
1333
  * 「この箱が持っている」 を指すので出どころに立つ。 1 つの決まりで書こうとするとどちらかが
@@ -1594,7 +1594,7 @@ export function classDiagram(preset: ClassDiagramPreset): ClassDiagramBuilder {
1594
1594
 
1595
1595
  b.phase(
1596
1596
  "class",
1597
- { duration: 2400, title: preset.topic, body: "UML class 全 class + relation を visible 化。" },
1597
+ { duration: 2400, title: preset.topic, body: "すべてのクラスと関係を並べた構造。" },
1598
1598
  (p) => p.activate(...phaseActivate).badge("class"),
1599
1599
  );
1600
1600
  return b.build();
@@ -1673,7 +1673,7 @@ export function tree(preset: TreePreset): TreeBuilder {
1673
1673
  });
1674
1674
  b.phase(
1675
1675
  "tree",
1676
- { duration: 2400, title: preset.topic, body: "tree の全 node + parent-child edge を hierarchical layout で描画。" },
1676
+ { duration: 2400, title: preset.topic, body: "すべての項目を親子の階層で並べた木。" },
1677
1677
  (p) => p.activate(nodeId).badge("tree"),
1678
1678
  );
1679
1679
  return b.build();
@@ -1756,7 +1756,7 @@ export function userJourney(preset: UserJourneyPreset): UserJourneyBuilder {
1756
1756
  });
1757
1757
  b.phase(
1758
1758
  "journey",
1759
- { duration: 2400, title: preset.topic, body: "userJourney 全 step を感情曲線 + touchpoint で描画。" },
1759
+ { duration: 2400, title: preset.topic, body: "すべての段階を、気持ちの曲線と接点で並べた体験。" },
1760
1760
  (p) => p.activate(nodeId).badge("journey"),
1761
1761
  );
1762
1762
  return b.build();
@@ -1844,7 +1844,7 @@ export function mindMap(preset: MindMapPreset): MindMapBuilder {
1844
1844
  });
1845
1845
  b.phase(
1846
1846
  "mindmap",
1847
- { duration: 2400, title: preset.topic, body: "mindMap 全 branch を visible 化、 中心 + 放射で描画。" },
1847
+ { duration: 2400, title: preset.topic, body: "中心の主題から、すべての枝を放射状に広げた姿。" },
1848
1848
  (p) => p.activate(nodeId).badge("mindmap"),
1849
1849
  );
1850
1850
  return b.build();
@@ -1920,7 +1920,7 @@ export function funnel(preset: FunnelPreset): FunnelBuilder {
1920
1920
  });
1921
1921
  b.phase(
1922
1922
  "funnel",
1923
- { duration: 2400, title: preset.topic, body: "funnel 全 stage を逆三角形 + count で描画。" },
1923
+ { duration: 2400, title: preset.topic, body: "すべての段階を、人数とともに上から絞り込んだ姿。" },
1924
1924
  (p) => p.activate(nodeId).badge("funnel"),
1925
1925
  );
1926
1926
  return b.build();
@@ -2018,7 +2018,7 @@ export function quadrant(preset: QuadrantPreset): QuadrantBuilder {
2018
2018
  });
2019
2019
  b.phase(
2020
2020
  "quadrant",
2021
- { duration: 2400, title: preset.topic, body: "quadrant 十字軸 + 4 象限 + item を描画。" },
2021
+ { duration: 2400, title: preset.topic, body: "2 本の軸で分けた 4 つの枠に、すべての項目を置いた姿。" },
2022
2022
  (p) => p.activate(nodeId).badge("quadrant"),
2023
2023
  );
2024
2024
  return b.build();
@@ -2134,7 +2134,7 @@ export function chart(preset: ChartPreset): ChartBuilder {
2134
2134
  });
2135
2135
  b.phase(
2136
2136
  "chart",
2137
- { duration: 2400, title: preset.topic, body: `${preset.type} chart の全 datum を visible 化。` },
2137
+ { duration: 2400, title: preset.topic, body: "すべての値を描いたグラフ。" },
2138
2138
  (p) => p.activate(nodeId).badge(preset.type),
2139
2139
  );
2140
2140
  return b.build();
@@ -2254,7 +2254,7 @@ export function gantt(preset: GanttPreset): GanttBuilder {
2254
2254
  });
2255
2255
  b.phase(
2256
2256
  "gantt",
2257
- { duration: 2400, title: preset.topic, body: "gantt 全 task を visible 化、 帯状 timeline + 依存 arrow で描画。" },
2257
+ { duration: 2400, title: preset.topic, body: "すべての作業を、期間の帯と前後関係の矢印で並べた工程。" },
2258
2258
  (p) => p.activate(nodeId).badge("gantt"),
2259
2259
  );
2260
2260
  return b.build();
@@ -2307,12 +2307,16 @@ const SHAPE_KIND: Record<FlowchartNodeShape, NodeKind> = {
2307
2307
  loop: "card",
2308
2308
  };
2309
2309
 
2310
+ /**
2311
+ * 箱の上に出す形の名前 (#835)。 流れ図の記号の呼び方 (JIS X 0121 の 処理 / 判断 / 端子) に合わせる。
2312
+ * 端子は始まりと終わりで分けて書く = 同じ「端子」 では、どちらの端かが読めない。
2313
+ */
2310
2314
  const SHAPE_EYEBROW: Record<FlowchartNodeShape, string> = {
2311
- process: "process",
2312
- decision: "decision",
2313
- start: "start",
2314
- end: "end",
2315
- loop: "loop",
2315
+ process: "処理",
2316
+ decision: "判断",
2317
+ start: "開始",
2318
+ end: "終了",
2319
+ loop: "繰り返し",
2316
2320
  };
2317
2321
 
2318
2322
  /**
@@ -2365,9 +2369,10 @@ export function flowchart(preset: FlowchartPreset): FlowchartBuilder {
2365
2369
  const edgeId = `fc-${edgeIdx++}-${e.from}-${e.to}`;
2366
2370
  // CAR-549 SSOT ... flowchart の decision condition label (true / false / yes / no) は
2367
2371
  // edge の semantic (どちらの分岐か) を強調するため path 上に重ねて表示する。
2372
+ // 日本語で分岐を書いた「はい」「いいえ」 も同じ扱いにする (#839)。 一致は完全一致に限る。
2368
2373
  // 他 label (通常の process 遷移名) は default (34 world 離す) で読みやすさ優先。
2369
2374
  const label = e.label ?? "";
2370
- const isConditionLabel = /^(true|false|yes|no)$/i.test(label.trim());
2375
+ const isConditionLabel = /^(true|false|yes|no|はい|いいえ)$/i.test(label.trim());
2371
2376
  b.edge(e.from, e.to, {
2372
2377
  id: edgeId,
2373
2378
  label,
@@ -2381,7 +2386,7 @@ export function flowchart(preset: FlowchartPreset): FlowchartBuilder {
2381
2386
  build() {
2382
2387
  b.phase(
2383
2388
  "flowchart",
2384
- { duration: 2400, title: preset.topic, body: "flowchart 全 node + edge を visible 化、 swimlane + decision を表現。" },
2389
+ { duration: 2400, title: preset.topic, body: "すべての処理と分岐を、担当ごとのレーンに並べた流れ。" },
2385
2390
  (p) => p.activate(...phaseActivate).badge("flowchart"),
2386
2391
  );
2387
2392
  return b.build();
@@ -2434,6 +2439,15 @@ const NET_KIND_MAP: Record<NetworkDeviceKind, NodeKind> = {
2434
2439
  client: "actor",
2435
2440
  };
2436
2441
 
2442
+ /** 箱の上に出す機器の呼び名 (#835)。 種類の英語をそのまま出すと、日本語の図の中で浮く */
2443
+ const NET_KIND_LABEL: Record<NetworkDeviceKind, string> = {
2444
+ router: "ルーター",
2445
+ switch: "スイッチ",
2446
+ firewall: "ファイアウォール",
2447
+ server: "サーバー",
2448
+ client: "端末",
2449
+ };
2450
+
2437
2451
  /**
2438
2452
  * network preset ... NW topology (router / switch / firewall / server / client) を col × row 配置、
2439
2453
  * link で接続 (protocol / VLAN を label)。 On-prem / hybrid NW 構成図向け。
@@ -2464,7 +2478,7 @@ export function network(preset: NetworkPreset): NetworkBuilder {
2464
2478
  b.lane(laneId, { width: laneW });
2465
2479
  colLaneIds.set(d.col, laneId);
2466
2480
  }
2467
- const eyebrowParts: string[] = [d.kind];
2481
+ const eyebrowParts: string[] = [NET_KIND_LABEL[d.kind]];
2468
2482
  if (d.segment) eyebrowParts.push(d.segment);
2469
2483
  b.node(d.id, {
2470
2484
  lane: laneId,
@@ -2490,7 +2504,7 @@ export function network(preset: NetworkPreset): NetworkBuilder {
2490
2504
  build() {
2491
2505
  b.phase(
2492
2506
  "network",
2493
- { duration: 2400, title: preset.topic, body: "network 全 device + link を visible 化。" },
2507
+ { duration: 2400, title: preset.topic, body: "すべての機器と接続を並べた構成。" },
2494
2508
  (p) => p.activate(...phaseActivate).badge("network"),
2495
2509
  );
2496
2510
  return b.build();
@@ -2572,19 +2586,25 @@ export function stateMachine2(preset: StateMachine2Preset): StateMachine2Builder
2572
2586
  const stateOrder = new Map<string, number>();
2573
2587
  const phaseActivate: string[] = [];
2574
2588
  let transIdx = 0;
2589
+ // 状態の題と親 (#835)。 入れ子の札は親の題で書くので、親が後から宣言されても引けるよう
2590
+ // 札は `build()` で決める
2591
+ const 題 = new Map<string, string>();
2592
+ const 親 = new Map<string, string>();
2575
2593
 
2576
2594
  const api: StateMachine2Builder = {
2577
2595
  state(s) {
2578
2596
  const laneId = `lane-${s.id}`;
2579
2597
  b.lane(laneId, { width: laneW });
2598
+ 題.set(s.id, s.title);
2599
+ if (s.parent) 親.set(s.id, s.parent);
2580
2600
  const eyebrowParts: string[] = [];
2581
2601
  if (s.initial) eyebrowParts.push("初期");
2582
2602
  else if (s.final) eyebrowParts.push("最終");
2583
2603
  else eyebrowParts.push("状態");
2584
- if (s.parent) eyebrowParts.push(`nested in ${s.parent}`);
2604
+ // 入る時と出る時の呼び方は `types.ts` の行頭の印の説明と揃える
2585
2605
  const subtitleParts: string[] = [];
2586
- if (s.entry) subtitleParts.push(`entry: ${s.entry}`);
2587
- if (s.exit) subtitleParts.push(`exit: ${s.exit}`);
2606
+ if (s.entry) subtitleParts.push(`入る時: ${s.entry}`);
2607
+ if (s.exit) subtitleParts.push(`出る時: ${s.exit}`);
2588
2608
  b.node(s.id, {
2589
2609
  lane: laneId,
2590
2610
  stack: 0,
@@ -2620,10 +2640,24 @@ export function stateMachine2(preset: StateMachine2Preset): StateMachine2Builder
2620
2640
  build() {
2621
2641
  b.phase(
2622
2642
  "sm2",
2623
- { duration: 2400, title: preset.topic, body: "拡張 FSM (nested + action) を visible 化。" },
2643
+ {
2644
+ duration: 2400,
2645
+ title: preset.topic,
2646
+ body: "入れ子の状態と、入る時・出る時の処理を含めた、すべての状態と遷移。",
2647
+ },
2624
2648
  (p) => p.activate(...phaseActivate).badge("statemachine2"),
2625
2649
  );
2626
- return b.build();
2650
+ const d = b.build();
2651
+ // 入れ子の札に親の題を足す。 識別子 (`active`) を出すと、図のどこにも無い字になる
2652
+ return {
2653
+ ...d,
2654
+ nodes: d.nodes.map((n) => {
2655
+ const 親の識別子 = 親.get(n.id);
2656
+ if (親の識別子 === undefined) return n;
2657
+ const 親の題 = 題.get(親の識別子) ?? 親の識別子;
2658
+ return { ...n, eyebrow: `${n.eyebrow ?? "状態"} / ${親の題}の中` };
2659
+ }),
2660
+ };
2627
2661
  },
2628
2662
  };
2629
2663
 
@@ -6,7 +6,7 @@
6
6
  * 問いにその場で答えられる。
7
7
  *
8
8
  * 集合は 3 つで作る = 触れた箱 / その箱に繋がる線 / 線の相手の箱。
9
- * **線の向きは問わない** = クラス図の「継ぐ」 は子から親へ、 ER の「持つ」 は親から子へ向くが、
9
+ * **線の向きは問わない** = クラス図の「継承」 は子から親へ、 ER の「注文する」 は利用者から注文へ向くが、
10
10
  * 読み手にとってはどちらも「この箱の関係」 で、向きで見え方が変わる理由が無い。
11
11
  */
12
12
 
package/src/types.ts CHANGED
@@ -130,7 +130,7 @@ export type Side = "top" | "right" | "bottom" | "left";
130
130
  * - "dashed" ... 刻んだ線。 **光っていても刻みのまま**。
131
131
  *
132
132
  * `"dashed"` が要るのは、`"solid"` の刻みが「光っていない」 を表す合図に使われているため。
133
- * 光ると実線に戻るので、線の種類として刻みを持たせられない。 UML の「満たす」 と「使う」、
133
+ * 光ると実線に戻るので、線の種類として刻みを持たせられない。 UML の「実装」 と「依存」、
134
134
  * ER の「識別しない」 のように **刻みが意味を持つ関係** はこちらを使う。
135
135
  */
136
136
  export type EdgeStyle = "solid" | "dotted-flow" | "dashed";
@@ -167,8 +167,8 @@ export const EDGE_STYLES: readonly EdgeStyle[] = ["solid", "dotted-flow", "dashe
167
167
  * 4 通りを 2 つの欄に分けないのは、**端の印は 1 つの記号として読まれる**ため。 欄を分けると
168
168
  * 片方だけ書いた形 (個数はあるが任意かが無い) を許すことになり、描けない組み合わせが増える。
169
169
  *
170
- * 線の種類と分けて持つのは、両者が独立に組み合わさるため。 実線 × 三角 (継ぐ) と
171
- * 破線 × 三角 (満たす) は線種で分かれ、実線 × 三角 (継ぐ) と実線 × 菱 (持つ) は端で分かれる。
170
+ * 線の種類と分けて持つのは、両者が独立に組み合わさるため。 実線 × 三角 (継承) と
171
+ * 破線 × 三角 (実装) は線種で分かれ、実線 × 三角 (継承) と実線 × 菱 (集約) は端で分かれる。
172
172
  * 1 つの欄に畳むと 2 軸の積を名前で列挙することになり、組合せが増えるたびに語が増える。
173
173
  */
174
174
  export type EdgeHead =
@@ -204,7 +204,8 @@ export const EDGE_HEADS: readonly EdgeHead[] = [
204
204
  * - `"solid"` ... 墨で塗る。 **既定**。 書かなければ形ごとの既定 (`EDGE_HEAD_SHAPE.filled`) に従う
205
205
  * - `"hollow"` ... 紙の色で塗り、輪郭だけを線で描く
206
206
  *
207
- * UML は同じ形を塗りで分ける = 白抜きの菱が「持つ」、塗った菱が「抱える」 (命が同じ)。
207
+ * UML は同じ形を塗りで分ける = 白抜きの菱が「集約」、塗った菱が「コンポジション」
208
+ * (部分が全体と寿命を共にする)。
208
209
  * 形を増やして表そうとすると、塗りの軸を持つ全ての形について語が倍になる。
209
210
  *
210
211
  * 塗らない形 (`"open"` / `"crow"`) には効かない。 元から輪郭しか無いため。
@@ -736,8 +737,8 @@ export type CdlEdge = {
736
737
  /**
737
738
  * 端の印の塗り方 (#578)。 書かなければ形ごとの既定。 塗らない形には効かない。
738
739
  *
739
- * 形と分けて持つのは、UML が同じ形を塗りで分けるため (白抜きの菱 = 持つ /
740
- * 塗った菱 = 抱える)。
740
+ * 形と分けて持つのは、UML が同じ形を塗りで分けるため (白抜きの菱 = 集約 /
741
+ * 塗った菱 = コンポジション)。
741
742
  */
742
743
  headFill?: EdgeHeadFill;
743
744
  /**