@cardenelabs/dragon 0.34.0 → 0.35.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/README.md +39 -720
  2. package/dist/cli.cjs +4529 -0
  3. package/dist/cli.cjs.map +1 -0
  4. package/dist/cli.js +4523 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/export.cjs +3912 -0
  7. package/dist/export.cjs.map +1 -0
  8. package/dist/export.d.cts +36 -0
  9. package/dist/export.d.ts +36 -0
  10. package/dist/export.js +3907 -0
  11. package/dist/export.js.map +1 -0
  12. package/dist/fonts.cjs +7 -0
  13. package/dist/fonts.cjs.map +1 -0
  14. package/dist/fonts.js +4 -0
  15. package/dist/fonts.js.map +1 -0
  16. package/dist/index.cjs +4105 -20356
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +227 -5522
  19. package/dist/index.d.ts +227 -5522
  20. package/dist/index.js +4091 -20283
  21. package/dist/index.js.map +1 -1
  22. package/dist/player.global.js +2 -0
  23. package/dist/schema-BBWfniwH.d.cts +112 -0
  24. package/dist/schema-BBWfniwH.d.ts +112 -0
  25. package/package.json +53 -27
  26. package/src/cli.ts +237 -0
  27. package/src/export/capture.ts +277 -0
  28. package/src/export/fonts.ts +68 -0
  29. package/src/export/html.ts +32 -0
  30. package/src/export/index.ts +146 -0
  31. package/src/field-messages.ts +104 -0
  32. package/src/figure-text.ts +42 -0
  33. package/src/index.ts +30 -283
  34. package/src/layout/architecture.ts +381 -0
  35. package/src/layout/measure.ts +21 -0
  36. package/src/layout/scene.ts +353 -0
  37. package/src/layout/sequence.ts +189 -0
  38. package/src/load-fonts.ts +8 -0
  39. package/src/look/fonts.ts +33 -0
  40. package/src/look/palettes.json +209 -0
  41. package/src/look/values.ts +155 -0
  42. package/src/markdown.ts +35 -0
  43. package/src/messages.ts +122 -0
  44. package/src/parser.ts +342 -310
  45. package/src/player-global.ts +4 -0
  46. package/src/player.ts +271 -0
  47. package/src/printer.ts +76 -0
  48. package/src/render/icons.ts +41 -0
  49. package/src/render/motion.ts +106 -0
  50. package/src/render/parts.ts +279 -0
  51. package/src/render/svg.ts +341 -0
  52. package/src/schema.ts +161 -16
  53. package/src/schemas/diagram.schema.json +150 -0
  54. package/src/schemas/validate-diagram.d.ts +12 -0
  55. package/src/schemas/validate-diagram.js +8 -0
  56. package/src/timeline.ts +166 -0
  57. package/src/tokenize.ts +20 -281
  58. package/src/version.ts +55 -0
  59. package/src/view.ts +16 -0
  60. package/src/words.ts +79 -0
  61. package/examples/quick-start.md +0 -142
  62. package/src/canvas-bounds.ts +0 -52
  63. package/src/color.ts +0 -197
  64. package/src/compile/actor-ids.ts +0 -297
  65. package/src/compile/actor-option-notice.ts +0 -379
  66. package/src/compile/actors.ts +0 -103
  67. package/src/compile/c4.ts +0 -74
  68. package/src/compile/chain.ts +0 -93
  69. package/src/compile/chart-fields.ts +0 -193
  70. package/src/compile/deep-rewrite.ts +0 -30
  71. package/src/compile/derived-source.ts +0 -18
  72. package/src/compile/direction.ts +0 -102
  73. package/src/compile/edge-option-notice.ts +0 -112
  74. package/src/compile/er-relation.ts +0 -200
  75. package/src/compile/flow.ts +0 -54
  76. package/src/compile/flowchart.ts +0 -344
  77. package/src/compile/funnel.ts +0 -87
  78. package/src/compile/gantt.ts +0 -337
  79. package/src/compile/generic.ts +0 -696
  80. package/src/compile/hierarchy.ts +0 -92
  81. package/src/compile/journey.ts +0 -93
  82. package/src/compile/kinds.ts +0 -139
  83. package/src/compile/lanes.ts +0 -138
  84. package/src/compile/metro.ts +0 -271
  85. package/src/compile/mind.ts +0 -393
  86. package/src/compile/node-title.ts +0 -24
  87. package/src/compile/notice.ts +0 -141
  88. package/src/compile/order.ts +0 -57
  89. package/src/compile/parts-lookup.ts +0 -17
  90. package/src/compile/parts.ts +0 -2709
  91. package/src/compile/placement-apply.ts +0 -689
  92. package/src/compile/placement.ts +0 -15
  93. package/src/compile/quadrant.ts +0 -265
  94. package/src/compile/record.ts +0 -75
  95. package/src/compile/row-marks.ts +0 -128
  96. package/src/compile/rows.ts +0 -40
  97. package/src/compile/sequence.ts +0 -305
  98. package/src/compile/slug.ts +0 -37
  99. package/src/compile/stages.ts +0 -247
  100. package/src/compile/subtitle.ts +0 -35
  101. package/src/compile/swimlane-marks.ts +0 -87
  102. package/src/compile/swimlane.ts +0 -82
  103. package/src/compile/timeline.ts +0 -213
  104. package/src/compile/topology.ts +0 -44
  105. package/src/compile/tree.ts +0 -69
  106. package/src/compile/value-chart.ts +0 -337
  107. package/src/compile/values.ts +0 -432
  108. package/src/compile/word-state.ts +0 -82
  109. package/src/compile.ts +0 -3168
  110. package/src/focus.ts +0 -46
  111. package/src/input-size.ts +0 -181
  112. package/src/json-line-map.ts +0 -30
  113. package/src/json-parser.ts +0 -4306
  114. package/src/keywords.ts +0 -417
  115. package/src/near-name.ts +0 -42
  116. package/src/notation-lint.ts +0 -342
  117. package/src/relative-pos.ts +0 -251
  118. package/src/schemas/diagram.json +0 -3088
  119. package/src/types.ts +0 -1009
  120. package/src/v05/index.ts +0 -9
  121. package/src/v05/input-table.generated.ts +0 -138
  122. package/src/v05/parser-types.ts +0 -32
  123. package/src/v05/parser.ts +0 -6668
  124. package/src/v05/readout-table.generated.ts +0 -1088
  125. package/src/v05/removed-syntax.ts +0 -115
  126. package/src/value-syntax.ts +0 -313
  127. package/src/write-position.ts +0 -270
@@ -1,103 +0,0 @@
1
- import type { DslActor, DslDocument } from "../types";
2
- import { slugify } from "./slug";
3
- /**
4
- * 登場人物の参照表と、並びの整え (#2030 で `compile.ts` から移した)。
5
- */
6
-
7
- /**
8
- * 矢印の指す先を `actors` に書いた **正規の名前** へ解決する表 (#1209)。
9
- *
10
- * 名前そのものに加えて **一意な slug も受ける**。 動きを書いた図の組み立ては `slugify` に
11
- * 落として引くため、 `API Gateway` を `api-gateway` と書いた形が届く。 2 つ以上の名前が
12
- * 同じ slug になる時は受けない = どちらを指したか決められない。
13
- *
14
- * **返すのは正規の名前で、 slug ではない**。 slug を返すと、 動きを書いていない図の組み立て
15
- * (名前の完全一致で引く) と食い違う = 知らせは出ないのに label が消える / 題が slug に化ける /
16
- * 依存が切れる、 という形になる (Round 1 で実測)。 中央で名前へ揃えれば全経路が同じ相手を指す。
17
- */
18
- export function actorRefTable(doc: DslDocument): Map<string, string> {
19
- const 表 = new Map<string, string>();
20
- const slug数 = new Map<string, number>();
21
- for (const a of doc.actors) {
22
- const sl = slugify(a.name);
23
- slug数.set(sl, (slug数.get(sl) ?? 0) + 1);
24
- }
25
- for (const a of doc.actors) {
26
- 表.set(a.name, a.name);
27
- const sl = slugify(a.name);
28
- if ((slug数.get(sl) ?? 0) === 1) 表.set(sl, a.name);
29
- }
30
- return 表;
31
- }
32
-
33
- /**
34
- * 矢印の指す先を正規の名前へ揃えた `flow` を返す (#1209)。
35
- *
36
- * 解決できない矢印はそのまま残す = 図種ごとに扱いが違う (木は独自の知らせを出し、 値で描く図は
37
- * 落とす)。 中央で消すとその扱いが効かなくなる。 残した分は `reportMissingFlowActors` が
38
- * 知らせ、 動きを書いた図の組み立てが落とす。
39
- */
40
- export function canonicalizeFlowActors(doc: DslDocument): DslDocument {
41
- const 表 = actorRefTable(doc);
42
- let 変えた = false;
43
- const flow = doc.flow.map((s) => {
44
- const from = 表.get(s.from) ?? s.from;
45
- const to = 表.get(s.to) ?? s.to;
46
- if (from === s.from && to === s.to) return s;
47
- 変えた = true;
48
- return { ...s, from, to };
49
- });
50
- return 変えた ? { ...doc, flow } : doc;
51
- }
52
-
53
- /**
54
- * 状態遷移図で、どの箱を始まり / 終わりとみなすか (#1275)。
55
- *
56
- * **書いた値が勝つ**。 `initial:` / `final:` は記法で書けるのに 1 度も読まれておらず、
57
- * 位置だけで決まっていた (実測 = 中央の箱に `final: true` を書いても、最後に書いた箱が
58
- * 「最終」 になった)。
59
- *
60
- * 1 つも書いていなければ従来どおり位置で決める = 書かない記法の図は変わらない。
61
- * 片方だけ書いた形も、書いた側だけが切り替わる。
62
- *
63
- * ## 打ち消し (`initial: false`) は書いた箱にだけ効く (#2418)
64
- *
65
- * 書いたかどうかを `true` だけで数えていた間、`false` しか書かれていない図は
66
- * 「1 つも書いていない」 扱いになって位置で決める既定へ落ちていた。 その既定は
67
- * 「1 番目の箱が始まり」 なので、**打ち消しを書いた当の箱に札が出ていた** (知らせも 0 件)。
68
- *
69
- * 位置で決める既定から、打ち消しを書いた箱だけを外す。
70
- *
71
- * **「どれか 1 つでも書いたら既定を捨てる」 形にはしない** = 途中の箱に書いた
72
- * `initial: false` が 1 番目の箱の札まで消す。 書き手は途中の箱のことしか言っていない。
73
- */
74
- export function 始まりと終わりの決め方(
75
- doc: DslDocument,
76
- /**
77
- * 並びで決める既定を使うか (#2349)。
78
- *
79
- * **格子に置く形では並びが始まりと終わりを意味しない**。 縦列と段を書いた図は
80
- * 書いた順に左から並ぶわけではないので、「最初に書いた箱」 を初期とみなす既定は
81
- * 図の見た目と合わない (実測 = 印の箱を持つ見本で、既に形が始点を示している箱へ
82
- * 「初期」 の字が重ねて出た)。 書いた値はどの並べ方でも効く。
83
- */
84
- opts: { 並びで決める?: boolean } = {},
85
- ): {
86
- 始まり: (a: DslActor, idx: number) => boolean;
87
- 終わり: (a: DslActor, idx: number) => boolean;
88
- } {
89
- const 並びで決める = opts.並びで決める ?? true;
90
- const 書いた始まり = doc.actors.some((a) => a.initial === true);
91
- const 書いた終わり = doc.actors.some((a) => a.final === true);
92
- return {
93
- 始まり: (a, idx) =>
94
- 書いた始まり ? a.initial === true : 並びで決める && idx === 0 && a.initial !== false,
95
- 終わり: (a, idx) =>
96
- 書いた終わり
97
- ? a.final === true
98
- : 並びで決める &&
99
- idx === doc.actors.length - 1 &&
100
- doc.actors.length > 1 &&
101
- a.final !== false,
102
- };
103
- }
package/src/compile/c4.ts DELETED
@@ -1,74 +0,0 @@
1
- import { diagram } from "@cardenelabs/cdl";
2
- import type { CdlDiagram } from "@cardenelabs/cdl";
3
- import type { DslDocument } from "../types";
4
- import { 描ける種別 } from "./kinds";
5
- import { 段を読み取る } from "./rows";
6
- import { slugify } from "./slug";
7
- /**
8
- * C4 preset (C4 model 階層 system context 専用 layout)
9
- *
10
- * 設計 ... actor.subtitle の先頭に "L1" / "L2" / "L3" を置き、 階層 lane を生成。
11
- * - L1 = 全体の見取り図 (C4 model の System Context)
12
- * - L2 = 動かす単位 (同 Container = 単体で動かす application や保管庫)
13
- * - L3 = 部品 (同 Component)
14
- *
15
- * 実装 ... **中身のある段だけ** lane を作り、 使う段を左から順に詰めて横並び (contain: true で
16
- * 囲む) 配置する。 3 lane を常に作ると中身のない枠が画面に残り、 描かれ損ねたように見える (#1078)。
17
- * 同 lane 内の actor は内部 stack で縦並びになる (横並びは layout 制約上不可、
18
- * 段の区別が視覚的に最重要)。 subtitle marker 未指定なら L1 fallback。
19
- *
20
- * flow ... actor 間の関係を edge で表現。
21
- */
22
- export function compileC4(doc: DslDocument): CdlDiagram {
23
- const b = diagram(slugify(doc.title), { topic: doc.title, type: "infrastructure" });
24
- const LANE_W = 400;
25
- const LANE_GAP = 80;
26
- // 段の名前は **枠に描く字** で、記法に打ち込む識別子ではない (#1886)。 打ち込む字は目印
27
- // (`L1` / `L2` / `L3`) の側で、そちらは綴りを変えない
28
- const 段の名前: Record<number, string> = { 1: "全体の見取り図", 2: "動かす単位", 3: "部品" };
29
-
30
- // 段は **先頭一致** で読み、 読んだ目印は説明から落とす (`段を読み取る` の説明を参照)
31
- const 割当 = doc.actors.map((a, idx) => {
32
- const { 段, 説明 } = 段を読み取る(a.subtitle);
33
- return { 段, 説明, id: slugify(a.name) || `n${idx}`, actor: a };
34
- });
35
-
36
- // **中身のある段だけ枠を作る**。 3 段を必ず作ると、 書いていない段が空の点線枠として残り、
37
- // 見た人には「何かが描かれ損ねた」 ようにしか見えない
38
- const 使う段 = [1, 2, 3].filter((lv) => 割当.some((x) => x.段 === lv));
39
- 使う段.forEach((lv, i) => {
40
- b.lane(`c4-l${lv}`, {
41
- // 空の段を飛ばした分だけ左に詰める。 飛ばした位置に隙間を残すと、 やはり
42
- // 「何かが抜けている」 ように見える
43
- x: i * (LANE_W + LANE_GAP),
44
- width: LANE_W,
45
- label: 段の名前[lv]!,
46
- contain: true,
47
- });
48
- });
49
-
50
- const stackPerLane: Record<string, number> = { "c4-l1": 0, "c4-l2": 0, "c4-l3": 0 };
51
- for (const x of 割当) {
52
- const lid = `c4-l${x.段}`;
53
- const stack = stackPerLane[lid]!;
54
- stackPerLane[lid] = stack + 1;
55
- b.node(x.id, {
56
- lane: lid,
57
- stack,
58
- kind: 描ける種別(x.actor.kind),
59
- title: x.actor.name,
60
- });
61
- }
62
-
63
- for (const s of doc.flow) {
64
- const fromId = slugify(s.from);
65
- const toId = slugify(s.to);
66
- b.edge(fromId, toId, {
67
- label: s.label,
68
- ...(s.sub ? { sub: s.sub } : {}),
69
- ...(s.side ? { side: s.side } : {}),
70
- });
71
- }
72
-
73
- return b.build();
74
- }
@@ -1,93 +0,0 @@
1
- import type { CdlDiagram } from "@cardenelabs/cdl";
2
- import type { DslActor, DslDocument } from "../types";
3
- import { 書いた縦列に置く } from "./lanes";
4
- import { lookupPartRaw } from "./parts-lookup";
5
- /**
6
- * 箱を鎖のように 1 本につなぐ形か (#2030 で `compile.ts` から移した)。
7
- *
8
- * 図種ごとの file と、`compile.ts` に残る部品の取り込みの両方が呼ぶ。
9
- */
10
-
11
- /**
12
- * 静止した `type: flow` かどうか。 この形だけ矢印を鎖状に作る (`compileFlow`)。
13
- *
14
- * 段を持つ形・縦列を書いた形・向きを書いた形は generic 経路へ回るため鎖にならない。
15
- *
16
- * **`compileFlow` の分岐もこの判定を使う**。 判定を 2 か所に書くと、組み立てだけが別経路へ回り、
17
- * 行の対応と端の知らせが鎖のまま残る。 向きを書いた形 (#1494) は組み立てにだけ足され、`A -> C` に
18
- * 書いた `head` が `C -> B` に載り、書いた端のとおりの矢印に「端は使われません」 が出ていた (#1986)。
19
- */
20
- export function 鎖でつなぐ形か(doc: DslDocument): boolean {
21
- if (doc.type !== "flow") return false;
22
- return !鎖にしない書き方.some((x) => x.あてはまる(doc));
23
- }
24
-
25
- /**
26
- * 鎖をやめて、書いた端どおりに繋ぐ書き方 (#2374)。
27
- *
28
- * **判定と案内を組にして持つ**。 以前は判定が 3 つの条件を見るのに、知らせの案内は
29
- * 手で並べた 2 つだった (実測 = 動きを書く経路が案内に無く、読み手はその道を知れなかった)。
30
- *
31
- * 表から判定を導き、案内も同じ表から作る。 条件を足した日に案内が自動で追随する。
32
- *
33
- * どれも図の見た目を変えるので、**優先順位は付けない**。 向きを書けば並びが変わり、
34
- * 縦列を書けば列ができ、動きを書けば段が付く。 どれを選ぶかは書き手が決める。
35
- */
36
- export const 鎖にしない書き方: readonly {
37
- /** 検査と読み手のための短い呼び名 */
38
- 名: string;
39
- /** その書き方をしているか */
40
- あてはまる: (doc: DslDocument) => boolean;
41
- /** 知らせの案内に出す文 */
42
- 案内: string;
43
- }[] = [
44
- {
45
- 名: "動き",
46
- あてはまる: (doc) => doc.animate !== undefined && doc.animate.phases.length > 0,
47
- 案内: "動き (animation:) を書く",
48
- },
49
- {
50
- 名: "向き",
51
- あてはまる: (doc) => doc.direction !== undefined,
52
- 案内: "向き (direction: 縦) を書く",
53
- },
54
- {
55
- 名: "縦列",
56
- あてはまる: (doc) => 書いた縦列に置く("flow", doc),
57
- 案内: "箱に縦列 (lane:) を書く",
58
- },
59
- ];
60
-
61
- /** 知らせの案内に出す、鎖をやめる書き方の並び (#2374) */
62
- export function 鎖にしない書き方の案内(): string {
63
- return 鎖にしない書き方.map((x) => x.案内).join(" / ");
64
- }
65
-
66
- /**
67
- * 静止したフローの鎖に並べる登場人物 (#1987)。
68
- *
69
- * **どの行にも書かれていない部品は鎖に並べない**。 並べると、部品の前後の並び順の矢印は書き手が
70
- * 部品へ引いたものではないため部品へ繋がずに外され (#1979)、部品を途中に置いた図から矢印が全て
71
- * 消えていた (実測 = `受付 / 印 / 出荷` で矢印 0 本、知らせ 0 件)。 編集画面は部品を本文から抜いてから
72
- * 鎖を作るので `受付 -> 出荷` を描き、カタログと絵が食い違っていた。 部品は図の下の格子に置くため、
73
- * 鎖から外しても流れの列に空きは生まれない。
74
- *
75
- * - 部品の一覧に無い部品は仮の箱のまま描かれるので残す (一覧を渡さない組み立ても同じ)
76
- * - 大きすぎて取り込まない部品は一覧にあるので外す (図に入らない場所へ鎖を通さない)
77
- * - 行の端に書かれた部品は残し、#1979 の繋ぎ方に従う
78
- *
79
- * 鎖を作る `compileFlow`、行との対応 (`鎖のどの行から来たか`)、端の知らせ
80
- * (`reportFlowEndpointNotHonored`) の 3 つがこの並びを使う。 1 つでも `doc.actors` を見ると、
81
- * N 本目の矢印と行の対応がずれる。
82
- */
83
- export function 鎖に並べる登場人物(
84
- doc: DslDocument,
85
- partsCatalog: Record<string, CdlDiagram> | undefined,
86
- ): DslActor[] {
87
- if (!partsCatalog) return doc.actors;
88
- const 行に書かれた = new Set(doc.flow.flatMap((s) => [s.from, s.to]));
89
- // 部品でない箱は `partId` を持たず一覧を引けないので、一覧に無い部品と同じく残る
90
- return doc.actors.filter(
91
- (a) => 行に書かれた.has(a.name) || lookupPartRaw(partsCatalog, a.partId) === undefined,
92
- );
93
- }
@@ -1,193 +0,0 @@
1
- import type { DslDocument } from "../types";
2
- /**
3
- * 図表の欄を読む小道具 (#2030 で `compile.ts` から移した)。
4
- *
5
- * 値の図 (`pie` / `bar` / `line` ほか 6 種) ・ 漏斗 ・ 道のり ・ 四象限 の 4 系統が使う。
6
- * 数と語の読み方を 1 箇所に置くのは、図種ごとに別の読み方をすると同じ本文が図種で
7
- * 別の値に化けるため。
8
- *
9
- * **葉に置く** = ここが `compile.ts` を取り込まないので、どちらから呼んでも輪にならない。
10
- */
11
-
12
- /**
13
- * 割合の書き方から数値を読む。 読めなければ `null`。
14
- *
15
- * 受けるのは `"45%"` / `"45"` / `"45.5%"` と、 前後の空白。 `"四割"` や `"0.45"` のような
16
- * 別の言い方は読まない = **黙って 0 にすると、 その分だけ欠けた円が「正しい図」 として出る**。
17
- * 読めなかったことは呼出側が警告に出す。
18
- */
19
- export function parseShareValue(raw: string | undefined): number | null {
20
- if (raw === undefined) return null;
21
- const m = raw.trim().match(/^(-?\d+(?:\.\d+)?)\s*%?$/);
22
- if (m === null) return null;
23
- const v = Number(m[1]);
24
- return Number.isFinite(v) ? v : null;
25
- }
26
-
27
- /**
28
- * 状態を読む欄かどうか (`{名前}`)。
29
- *
30
- * **`{名前}` そのものだけを受ける**。 `{v} 件` のような混ざった形は、描画側が数として
31
- * 読めず既定値に落ちて印が付くだけになる (`render/payload-binding.ts` は解いた文字列を
32
- * そのまま数にする)。 書けたのに効かない形を作らない。
33
- *
34
- * `%` を付けた形も受けない。 同じ理由で `"45%"` は数に直せるが `"{v}%"` は直せない。
35
- *
36
- * 名前に使えるのは英数字と `_` で、読む側 (cdl の `interpolate`) と同じ範囲に合わせる。
37
- * 決まった accessor (`.sum` 等) は付けてよい。
38
- */
39
- export function parseBoundValue(raw: string | undefined): string | null {
40
- if (raw === undefined) return null;
41
- const t = raw.trim();
42
- return /^\{\w+(?:\.(?:length|sum|max|min|avg)|\[\d+\])?\}$/.test(t) ? t : null;
43
- }
44
-
45
- /**
46
- * 図表の数の欄を読む。 数そのものか、状態を読む `{名前}` を返す。
47
- *
48
- * 数として解けない `{名前}` は、そのまま図表の中身に渡して描画側が段ごとに解く。
49
- * 受け取る側の型 (`BoundNumber`) は元から 2 通りを想定している = 入口だけが塞がっていた。
50
- */
51
- export function parseChartValue(raw: string | undefined): number | string | null {
52
- const n = parseShareValue(raw);
53
- if (n !== null) return n;
54
- return parseBoundValue(raw);
55
- }
56
-
57
- /** 状態を読む欄が指している名前 (`{v.sum}` なら `v`)。 欄でなければ null */
58
- export function 参照する名前(value: number | string | null): string | null {
59
- if (typeof value !== "string") return null;
60
- const m = value.match(/^\{(\w+)/);
61
- return m ? m[1]! : null;
62
- }
63
-
64
- /**
65
- * 図表の箱の大きさ (#1260)。
66
- *
67
- * **組立て API と同じ値を使う**。 別の値にすると、同じ内容を書いても描いた図の大きさが変わる
68
- * (実測 = 記法の `funnel` は 640x368、組立て API は 560x480 で、描いた図の viewBox が
69
- * 785x488 対 712x600 になっていた)。
70
- *
71
- * 組立て API 側は中身の件数で変えない (実測 = 2 / 4 / 8 件のどれでも同じ値)。 そのため
72
- * こちらも定数で持つ。 `gantt` だけは件数で高さを変える = 8 件目から最後の帯が枠の外に
73
- * 出るため (`compileGantt` の実測)、組立て API の固定 360 より正しい。
74
- *
75
- * **格子 (16) の倍数にする**。 描画側 (`cdl` の `chart()` preset) は高さを 16 の倍数へ切り上げる。
76
- * 揃えないと下端が格子から外れ、 正しい記法でも位置の警告が出る (review 指摘、 360 のまま
77
- * 5 型が該当していた)。
78
- */
79
- export const 図表の大きさ = {
80
- funnel: { w: 560, h: 480 },
81
- // 木は幅を持たない (cdl#970)。 必要な幅は葉の数で変わるので、描画側の `木の札の幅` が出す。
82
- // 値を残すと、描画側が割付を変えた時にここだけが古い幅を持つ
83
- tree: { h: 480 },
84
- mind: { w: 720, h: 480 },
85
- journey: { w: 720, h: 480 },
86
- quadrant: { w: 640, h: 480 },
87
- } as const;
88
-
89
- /**
90
- * 記法に札の寸法があればそれを使い、無ければ図種ごとの既定をそのまま返す (#2854 C13)。
91
- * `図表の大きさ` 自体は変更しない。欄を書かない既存の図が byte-level で同じ寸法を保つため。
92
- */
93
- export function 図表の大きさを読む(
94
- doc: DslDocument,
95
- fallback: { w: number; h: number },
96
- ): { w: number; h: number } {
97
- return doc.figureSize === undefined
98
- ? fallback
99
- : { w: doc.figureSize.width, h: doc.figureSize.height };
100
- }
101
-
102
- /**
103
- * 図表の欄が `{名前}` で読む値を、**1 か所で** 確かめる (#1200)。
104
- *
105
- * 図表には数の欄 (割合 / 段の人数) と語の欄 (気持ち / 区画) があり、どちらも `{名前}` で
106
- * 状態を読める。 確かめることは欄の種類で違うが、**土台は同じ** = 同じ名前を 2 回書いた時に
107
- * 後ろが効くこと、段で状態に入る値 (切り替え / 補間) も見ること、の 2 つ。
108
- *
109
- * #1198 と #1201 では欄ごとに検査を書き足しており、同じ土台を 3 度書いていた。 3 度とも
110
- * review で同じ形の穴を指摘されている (最初の宣言で判定する / 段で入る値を見落とす)。
111
- * 土台を 1 つにして、欄ごとの違いだけを外から渡す。
112
- *
113
- * ## 確かめること
114
- *
115
- * | 欄 | 通す値 | 段で入る値 |
116
- * |---|---|---|
117
- * | 数 | 数として読める (空文字は弾く、`Number("")` が 0 を返すため) | 切り替え先が数 |
118
- * | 語 | 語表にある語 | 切り替え先が語表にあり、補間されない (補間の行き先は数) |
119
- *
120
- * 自動で決まる値 (`values:`) は式の評価結果で必ず数になるため、数の欄からは参照できて
121
- * 語の欄からは参照できない。 式そのものの不備 (語を読む / 名前が無い) は
122
- * `value-unresolved` の警告が別に出る (実測で確認済)。
123
- *
124
- * ## 責務境界 (#1198 / #1200)
125
- *
126
- * **見るのは組み立ての時点で決まっている範囲だけ**。 記法の値は実行時に決まるため、ここで
127
- * 全部を判定しようとすると式の評価を組み立て側で再現することになる。 実際に #1199 の review で
128
- * 4 round 続けて同じ形の指摘が出て収束せず、境界を決めて切り分けた (穴を 1 つ塞ぐと別の形が
129
- * 出る = 塞ぎ方ではなく責務の置き場所の問題だった)。
130
- *
131
- * | 見る | 見ない | 見ない理由 |
132
- * |---|---|---|
133
- * | 名前が宣言されているか | 段の行き先が負になる形 | 描画側が問題なく描く (負の大きさも `NaN` も出ないことを実測) |
134
- * | 宣言の時点で読める値か | 式が実行時に返す値 | 式の不備は `value-unresolved` の警告が別に出る (実測で確認) |
135
- * | 段で状態に入る値 | | |
136
- *
137
- * 見ない範囲は描画側が受け持つ = 解けない値は既定値で描いて `data-cdl-unresolved` を付ける。
138
- */
139
- export function 図表の欄から参照できる名前(
140
- doc: DslDocument,
141
- 欄: {
142
- 読めるか: (v: number | string) => boolean;
143
- 補間で壊れるか: boolean;
144
- 自動の値を許すか: boolean;
145
- },
146
- ): Set<string> {
147
- // 同じ名前を 2 回宣言した時は後ろが効く。 描画側が後の宣言を有効値として扱うため、
148
- // 前の宣言で判定すると「読めると判定したのに読めない値が入る」 状態になる (実測)
149
- const 実効 = new Map<string, number | string>();
150
- for (const s of doc.animate?.states ?? []) 実効.set(s.name, s.initial);
151
-
152
- const 壊れる = new Set<string>();
153
- for (const p of doc.animate?.phases ?? []) {
154
- for (const st of p.sets ?? []) if (!欄.読めるか(st.value)) 壊れる.add(st.state);
155
- if (欄.補間で壊れるか) for (const tw of p.tweens ?? []) 壊れる.add(tw.state);
156
- }
157
-
158
- const out = new Set<string>();
159
- for (const [名前, 値] of 実効) {
160
- if (!欄.読めるか(値)) continue;
161
- if (壊れる.has(名前)) continue;
162
- out.add(名前);
163
- }
164
- if (欄.自動の値を許すか) for (const v of doc.values ?? []) out.add(v.name);
165
- return out;
166
- }
167
-
168
- /** 数の欄が読める値か。 空文字と空白だけは弾く (`Number("")` は 0 を返す) */
169
- export function 数として読めるか(v: number | string): boolean {
170
- if (typeof v === "number") return Number.isFinite(v);
171
- const t = v.trim();
172
- if (t === "") return false;
173
- return Number.isFinite(Number(t));
174
- }
175
-
176
- export function 数の欄から参照できる名前(doc: DslDocument): Set<string> {
177
- return 図表の欄から参照できる名前(doc, {
178
- 読めるか: 数として読めるか,
179
- // 補間の行き先は数なので、数の欄では壊れない
180
- 補間で壊れるか: false,
181
- 自動の値を許すか: true,
182
- });
183
- }
184
-
185
- export function 語の欄から参照できる名前(doc: DslDocument, 語表: Map<string, string>): Set<string> {
186
- return 図表の欄から参照できる名前(doc, {
187
- 読めるか: (v) => 語表.has(String(v).trim()),
188
- // 補間の行き先は数。 語の欄が読む状態を補間すると、その段で語が数に変わる
189
- 補間で壊れるか: true,
190
- // 式の評価結果は数になるため、語の欄からは読めない
191
- 自動の値を許すか: false,
192
- });
193
- }
@@ -1,30 +0,0 @@
1
- /**
2
- * 入れ子の中の文字だけを書き換える (#2034 で `compile.ts` から移した)。
3
- *
4
- * **葉に置く** = 入口 (`compileToCdl`) と部品の取り込み (`mergePartIntoDiagram`) の両方が呼ぶ。
5
- */
6
-
7
- /**
8
- * 形 (`shape`) と数値の表示 (`readout`) の入れ子にある文字の葉を、全て書き換え関数に通す。
9
- * 文字でない葉 (数 / 真偽 / null) はそのまま残す。
10
- * 同じ object を 2 度辿らないよう `WeakSet` で覚える = 今の形は木なので輪にはならないが、
11
- * 輪ができた時に戻らなくなるのを防ぐため。
12
- */
13
- export function deepRewriteStrings(
14
- value: unknown,
15
- rewrite: (s: string | undefined) => string | undefined,
16
- seen: WeakSet<object> = new WeakSet(),
17
- ): unknown {
18
- if (typeof value === "string") return rewrite(value) ?? value;
19
- if (value === null || typeof value !== "object") return value;
20
- if (seen.has(value)) return value;
21
- seen.add(value);
22
- if (Array.isArray(value)) {
23
- return value.map((v) => deepRewriteStrings(v, rewrite, seen));
24
- }
25
- const out: Record<string, unknown> = {};
26
- for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
27
- out[k] = deepRewriteStrings(v, rewrite, seen);
28
- }
29
- return out;
30
- }
@@ -1,18 +0,0 @@
1
- /**
2
- * 値の宣言がどの行から来たかを控える (#2034 で `compile.ts` から移した)。
3
- *
4
- * **葉に置く** = 入口 (`compileToCdl`) と部品の取り込み (`mergePartIntoDiagram`) の両方が呼ぶ。
5
- * 見本を重ねる間だけ別表で宣言元を持ち回るため、どちらの側も同じ表へ書き足す。
6
- */
7
-
8
- /** `derived` の同名宣言を、engine が読む順のまま行番号の列として残す。 */
9
- export function recordDerivedSourceLine(
10
- sourceLines: Map<string, number[]> | undefined,
11
- id: string,
12
- line: number,
13
- ): void {
14
- if (!sourceLines) return;
15
- const lines = sourceLines.get(id) ?? [];
16
- lines.push(line);
17
- sourceLines.set(id, lines);
18
- }
@@ -1,102 +0,0 @@
1
- import type { DslDocument, PresetType } from "../types";
2
- /**
3
- * 図を並べる向きを決める小道具 (#2030 で `compile.ts` から移した)。
4
- *
5
- * 向きを書ける図種と、その既定、書いた向きの読み取り、後ろへ戻る矢印の判定。
6
- *
7
- * **葉に置く** = 図種ごとの組み立て器と、`compile.ts` に残る「書いたのに効かない」 の知らせの
8
- * 両方が呼ぶ。 どちらかの側に置くと、もう片方から逆向きの取り込みが生まれる。
9
- */
10
-
11
- /** 段の有無で経路が分かれる図種。 組み立ての入口が渡す種類と同じ綴り */
12
- export type GenericKind = "flow" | "swimlane" | "record" | "topology";
13
-
14
- /**
15
- * 向きを選べる図種 (#1494、 #2524 で `flowchart` を足した)。
16
- *
17
- * **並び方そのものが読み方を担う図種は外す**。 行を持つ図は「1 縦列 1 箱」、クラス図は
18
- * 設計が格子に置く形、順序図は 1 枚の板で、どれも向きを入れ替えると図の意味が変わる。
19
- *
20
- * `topology` も外す = 入れ物 (`contain`) を持つ図で、縦列の中に箱を囲む作りが向きと結びついている。
21
- *
22
- * 分かれ道の図は #2513 で足した時に外していた。 描く側の入口が向きを受けなかったためで、
23
- * 図の意味が向きと結びついているからではない。 入口が受けるようになったので足す
24
- * ([cardene777/cdl#880](https://github.com/cardene777/cdl/issues/880))。
25
- */
26
- export const 向きを選べる図種: ReadonlySet<PresetType> = new Set<PresetType>([
27
- "flow",
28
- "swimlane",
29
- "flowchart",
30
- ]);
31
-
32
- /**
33
- * 縦列を書いた図でも向きが勝つ図種 (#2524)。
34
- *
35
- * 他の図種は、全ての箱が縦列を書いた時に **縦列のほうが勝つ**。 書いた縦列に箱を置く形が
36
- * 向きより具体的な指定だからで、その時は書いた向きが捨てられたと知らせる。
37
- *
38
- * 分かれ道の図だけは逆にする。 この図種は **縦列が役割** で、書く人はほぼ必ず全ての箱に
39
- * 役割を書く。 縦列が勝つ形にすると向きの行が 1 度も効かず、選べるようにした意味が消える。
40
- */
41
- export const 縦列より向きが勝つ図種: ReadonlySet<PresetType> = new Set<PresetType>(["flowchart"]);
42
-
43
- /**
44
- * その図種の既定の向き (#2424 で `flow` を横にした)。
45
- *
46
- * **フローは横に並べる**。 縦に積むと描く広さが幅より高くなり、一覧の台は幅に合わせて
47
- * 伸ばすので縦に長い図になる (実測 = 見本の頁の「フロー」 が 576×920 で、器 1150×630px に
48
- * 収まらなかった)。 横に並べると 1 人 1 縦列になり、幅の側へ伸びる。
49
- *
50
- * `topology` だけ縦のまま = 入れ物 (`contain`) を持つ図で、縦列の中に箱を囲む作りが
51
- * 向きと結びついている。
52
- *
53
- * **動きを書かないフローはここを読まない**。 その形は箱を鎖のように繋ぐ別の組み立てを通る
54
- * (`鎖でつなぐ形か`)。 既定を変えても鎖の並びは 1 件も動かない。
55
- */
56
- export function 既定の向き(kind: PresetType): "縦" | "横" {
57
- return kind === "topology" ? "縦" : "横";
58
- }
59
-
60
- /**
61
- * 実際に使う向き (#1494)。
62
- *
63
- * 書いていない図と、効かない図種に書いた図は既定のまま = **書かない図の並びは 1 つも動かない**。
64
- */
65
- export function 並べる向き(kind: PresetType, doc: DslDocument): "縦" | "横" {
66
- if (doc.direction === undefined) return 既定の向き(kind);
67
- if (!向きを選べる図種.has(kind)) return 既定の向き(kind);
68
- return doc.direction;
69
- }
70
-
71
- /**
72
- * 後ろへ戻る矢印か (#1260 / #2782)。
73
- *
74
- * 前の箱へ戻る矢印は **箱の上を回して** 描く (`routing: "back-detour"`)。 組立て API 側が
75
- * そうしており、記法側で付けないと **描いた図の高さが変わる**
76
- * (実測 = viewBox が 404 対 486 で、戻る矢印が箱の右横を回っていた)。
77
- *
78
- * 判定は並び順。 指す先が指す元より前にあれば戻る矢印
79
- * (実測 = `stateMachine()` は c -> a と c -> b の 2 本だけに付け、a -> b と b -> c には付けない)。
80
- *
81
- * **縦列を書いた図には付けない** (#2782)。 回すのは「書いた順に並ぶから、戻る線が箱を
82
- * 貫く」 ための手当てで、縦列と段を書いた図は書いた人が置き場所を決めている。
83
- * 畳む前の表の図は縦列を書く形が多く、そこへ回し始めると **線が往復して戻る弧になり**
84
- * 関係の無い箱を貫いた (実測 = `利用者 -> 会員証` が同じ点へ 2 度戻る形になり、
85
- * 配置の知らせに `overlap node ∩ edge-path` が出た)。
86
- *
87
- * **行を持つ図だけに付ける**。 他の図種の組立て API は付けない。
88
- */
89
- export function 後ろへ戻る矢印か(
90
- kind: GenericKind,
91
- fromId: string,
92
- toId: string,
93
- 箱の並び: ReadonlyMap<string, number>,
94
- 縦列を書いた: boolean,
95
- ): boolean {
96
- if (kind !== "record") return false;
97
- if (縦列を書いた) return false;
98
- const 元 = 箱の並び.get(fromId);
99
- const 先 = 箱の並び.get(toId);
100
- if (元 === undefined || 先 === undefined) return false;
101
- return 先 < 元;
102
- }