@cardenelabs/dragon 0.15.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/dist/index.cjs +628 -379
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +217 -7
- package/dist/index.d.ts +217 -7
- package/dist/index.js +629 -381
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/compile.ts +567 -725
- package/src/index.ts +1 -0
- package/src/json-parser.ts +145 -2
- package/src/schemas/diagram.json +74 -3
- package/src/tokenize.ts +5 -5
- package/src/types.ts +84 -1
- package/src/v05/parser.ts +216 -6
package/src/compile.ts
CHANGED
|
@@ -26,6 +26,7 @@ import type {
|
|
|
26
26
|
FormulaAst,
|
|
27
27
|
LaidDiagram,
|
|
28
28
|
NodeKind,
|
|
29
|
+
RowMark,
|
|
29
30
|
} from "@cardenelabs/cdl";
|
|
30
31
|
import {
|
|
31
32
|
sequence,
|
|
@@ -33,13 +34,12 @@ import {
|
|
|
33
34
|
swimlane,
|
|
34
35
|
er,
|
|
35
36
|
stateMachine,
|
|
37
|
+
classDiagram,
|
|
38
|
+
FSM_ACTION_MARK,
|
|
39
|
+
sequenceStepId,
|
|
36
40
|
topology,
|
|
37
41
|
diagram,
|
|
38
42
|
layout,
|
|
39
|
-
rendersRows,
|
|
40
|
-
requiredRowsHeight,
|
|
41
|
-
requiredRowsWidth,
|
|
42
|
-
NODE_KINDS,
|
|
43
43
|
applyDerivedValues,
|
|
44
44
|
parseFormula,
|
|
45
45
|
extractIdentifiers,
|
|
@@ -162,8 +162,6 @@ export type CompileNotice = {
|
|
|
162
162
|
| "flow-actor-missing"
|
|
163
163
|
// きっかけ形の値を段に畳めなかった (段が無い / 相手が境目を通らない / 段からはみ出す、 #1161)
|
|
164
164
|
| "value-trigger-unresolved"
|
|
165
|
-
// 矢印の両端が同じ登場人物だった (#1227)
|
|
166
|
-
| "flow-self-loop"
|
|
167
165
|
// 箱に `lane:` を書いたが、 縦列は図種が決めるため効かなかった (#1246)
|
|
168
166
|
| "lane-not-honored"
|
|
169
167
|
// `lanes:` に書いた縦列に箱が 1 つも入らなかった (#1241)
|
|
@@ -179,7 +177,13 @@ export type CompileNotice = {
|
|
|
179
177
|
// 式が、どこにも書かれていない名前を読んだ (#1391)
|
|
180
178
|
| "formula-unresolved"
|
|
181
179
|
// 出来事が指す相手が図に無い (#1393)
|
|
182
|
-
| "event-target-missing"
|
|
180
|
+
| "event-target-missing"
|
|
181
|
+
// 順序図で面に種類を書いたが、板は名前と呼び名しか描かない (#1466)
|
|
182
|
+
| "actor-kind-not-honored"
|
|
183
|
+
// 箱の中の小さな箱に位置を書いたが、その名前の箱が図に無かった (#1466)
|
|
184
|
+
| "sub-node-not-found"
|
|
185
|
+
// 順序図の言づてに、板が描かない飾り (色味 / 添え字 / 寄せ) を書いた (#1466)
|
|
186
|
+
| "message-option-not-honored";
|
|
183
187
|
/** 対象の名前。 光らせる相手なら書かれた指定そのまま */
|
|
184
188
|
actor: string;
|
|
185
189
|
/** 書かれていた行 */
|
|
@@ -208,9 +212,6 @@ export function compileToCdl(doc: DslDocument, opts?: CompileToCdlOpts): CdlDiag
|
|
|
208
212
|
// 解決できない矢印を組み立てから外す (#1219)。 残すと、 存在しない箱や枠を指す図ができて
|
|
209
213
|
// 描画の直前で落ちる (実測 = 8 図種)
|
|
210
214
|
doc = dropUnresolvedFlow(doc);
|
|
211
|
-
// 自分へ戻る矢印を組み立てから外す (#1227)。 残すと描画側の検査が図ごと落とし、
|
|
212
|
-
// 本文のどの行が原因かも出ない
|
|
213
|
-
doc = dropSelfLoopFlow(doc);
|
|
214
215
|
|
|
215
216
|
// 名前から作る id が重なる分を解く (#1220)。 **矢印を落とした後**に見る = 落とした矢印の
|
|
216
217
|
// 端にしか出てこない名前で id を分けても、 その箱は作られない
|
|
@@ -255,6 +256,21 @@ export function compileToCdl(doc: DslDocument, opts?: CompileToCdlOpts): CdlDiag
|
|
|
255
256
|
case "line":
|
|
256
257
|
diagram = compileValueChart(doc, "line", "chart-line", opts?.onNotice);
|
|
257
258
|
break;
|
|
259
|
+
case "gauge":
|
|
260
|
+
diagram = compileValueChart(doc, "gauge", "chart-gauge", opts?.onNotice);
|
|
261
|
+
break;
|
|
262
|
+
case "radial":
|
|
263
|
+
diagram = compileValueChart(doc, "radial", "chart-radial", opts?.onNotice);
|
|
264
|
+
break;
|
|
265
|
+
case "stat":
|
|
266
|
+
diagram = compileValueChart(doc, "stat", "chart-stat", opts?.onNotice);
|
|
267
|
+
break;
|
|
268
|
+
case "waffle":
|
|
269
|
+
diagram = compileValueChart(doc, "waffle", "chart-waffle", opts?.onNotice);
|
|
270
|
+
break;
|
|
271
|
+
case "stacked":
|
|
272
|
+
diagram = compileValueChart(doc, "stacked", "chart-stacked-bar", opts?.onNotice);
|
|
273
|
+
break;
|
|
258
274
|
case "funnel":
|
|
259
275
|
diagram = compileFunnel(doc, opts?.onNotice);
|
|
260
276
|
break;
|
|
@@ -294,11 +310,12 @@ export function compileToCdl(doc: DslDocument, opts?: CompileToCdlOpts): CdlDiag
|
|
|
294
310
|
// 矢印が指す名前が actors に居るかを確かめる。 図種ごとの解決より前に、 記述だけで決まる
|
|
295
311
|
reportMissingFlowActors(書いたまま, opts?.onNotice);
|
|
296
312
|
// 両端が同じ矢印を伝える (#1227)。 落とす前の `flow` を見る
|
|
297
|
-
reportSelfLoopFlow(書いたまま, opts?.onNotice);
|
|
298
313
|
// 静止した `type: flow` で書いた矢印の端が使われないことを伝える (#1269)。
|
|
299
314
|
// 自分へ戻る形と居ない名前を指す形は既に落ちた後の `doc` を見る = 上の 2 件と重ねない
|
|
300
315
|
reportFlowEndpointNotHonored(doc, 分けた.元の名前, opts?.onNotice);
|
|
301
316
|
reportLaneNotHonored(書いたまま, opts?.onNotice);
|
|
317
|
+
reportActorKindNotHonored(書いたまま, opts?.onNotice);
|
|
318
|
+
reportMessageOptionNotHonored(書いたまま, opts?.onNotice);
|
|
302
319
|
reportDocEyebrowNotHonored(書いたまま, opts?.onNotice);
|
|
303
320
|
reportDrawNotHonored(書いたまま, opts?.onNotice);
|
|
304
321
|
reportChartFieldsNotHonored(書いたまま, opts?.onNotice);
|
|
@@ -306,7 +323,7 @@ export function compileToCdl(doc: DslDocument, opts?: CompileToCdlOpts): CdlDiag
|
|
|
306
323
|
// `位置: Web の右` を実際の配置から絶対座標に直す。 以降は座標を直接書いた時と同じ経路
|
|
307
324
|
const placed = resolveRelativeDoc(diagram, doc, opts?.onNotice, opts?.partsCatalog);
|
|
308
325
|
// canvas pivot 新 spec = 全 preset 共通の post-process で actor.posX/Y を CDL lane / node に伝播
|
|
309
|
-
applyCanvasPivotPositions(diagram, placed);
|
|
326
|
+
applyCanvasPivotPositions(diagram, placed, opts?.onNotice);
|
|
310
327
|
// CAR-1657 = parts kind actor を merge (opts.partsCatalog 経由)、 applyV05Extensions 後段で実行
|
|
311
328
|
const 追加した縦列: DslLane[] = [];
|
|
312
329
|
const extended = applyV05Extensions(diagram, placed, 追加した縦列);
|
|
@@ -355,6 +372,13 @@ export function compileToCdl(doc: DslDocument, opts?: CompileToCdlOpts): CdlDiag
|
|
|
355
372
|
// 書いた状態を図に載せる (#1162)。 段を書かない図でも値が届くようにする。
|
|
356
373
|
// **値を載せるより先に呼ぶ**。 状態が空のまま式を解くと、参照が全て「無い名前」 になる。
|
|
357
374
|
materializeStates(merged, doc);
|
|
375
|
+
/*
|
|
376
|
+
* 矢印をいつ出すか (#1470)。 描き手がそのまま読む欄なので、書いた語を素通しする。
|
|
377
|
+
*
|
|
378
|
+
* **出口で 1 度だけ載せる**。 図の種類ごとに組み立てが分かれており、経路ごとに書くと
|
|
379
|
+
* どれかを見落とす (`injectStaticPhase` / `materializeStates` と同じ理由)。
|
|
380
|
+
*/
|
|
381
|
+
if (doc.reveal !== undefined) merged.edgeReveal = doc.reveal;
|
|
358
382
|
// 語の欄が状態を読むとき、その状態には記法の語が入っている。 図の語へ直す (#1201)
|
|
359
383
|
語の状態を図の語へ直す(merged);
|
|
360
384
|
// きっかけ形の値を段の時計を読む式へ畳む (#1161 段 2)。 **値を載せるより先に呼ぶ** =
|
|
@@ -700,6 +724,11 @@ const 図種の作り: Record<PresetType, "登場人物ごとに箱" | "図全
|
|
|
700
724
|
pie: "図全体を 1 箱",
|
|
701
725
|
bar: "図全体を 1 箱",
|
|
702
726
|
line: "図全体を 1 箱",
|
|
727
|
+
gauge: "図全体を 1 箱",
|
|
728
|
+
radial: "図全体を 1 箱",
|
|
729
|
+
stat: "図全体を 1 箱",
|
|
730
|
+
waffle: "図全体を 1 箱",
|
|
731
|
+
stacked: "図全体を 1 箱",
|
|
703
732
|
funnel: "図全体を 1 箱",
|
|
704
733
|
tree: "図全体を 1 箱",
|
|
705
734
|
journey: "図全体を 1 箱",
|
|
@@ -720,56 +749,6 @@ function dropUnresolvedFlow(doc: DslDocument): DslDocument {
|
|
|
720
749
|
return flow.length === doc.flow.length ? doc : { ...doc, flow };
|
|
721
750
|
}
|
|
722
751
|
|
|
723
|
-
/**
|
|
724
|
-
* 自分へ戻る矢印を落とした `flow` を返す (#1227)。
|
|
725
|
-
*
|
|
726
|
-
* 描画側は両端が同じ矢印を受けない (`validate` が `self-loop` で落とす)。 記法の側は通すため、
|
|
727
|
-
* 書けてしまって描画の直前で図ごと落ちていた。 本文のどの行が原因かも出ない。
|
|
728
|
-
*
|
|
729
|
-
* `#1219` が「解決できない矢印は組み立てから外し、 知らせは元の `flow` を見る」 という形を
|
|
730
|
-
* 決めているので、 それに揃える。 落とすのは **組み立てに渡す分だけ** で、 書いた人には
|
|
731
|
-
* `reportSelfLoopFlow` が行番号付きで伝える。
|
|
732
|
-
*
|
|
733
|
-
* ## `type: state` の自己遷移をどう扱うか
|
|
734
|
-
*
|
|
735
|
-
* 矢印としては描けない。 描画側に自分へ戻る矢印を足すのは別 repo の判断で、 本 repo の
|
|
736
|
-
* 記法から決められない。 代わりに 2 通りの書き方が残る = 途中の箱を 1 つ足して 2 本の矢印に
|
|
737
|
-
* 分けるか、 段 (`animation`) で状態が変わる様子として見せる。 知らせの `hint` がこの 2 つを
|
|
738
|
-
* 案内する。
|
|
739
|
-
*
|
|
740
|
-
* 図全体を 1 箱で描く種別は矢印を作らないため対象にしない (本数を数えて別に伝えている)。
|
|
741
|
-
*/
|
|
742
|
-
function dropSelfLoopFlow(doc: DslDocument): DslDocument {
|
|
743
|
-
if (図種の作り[doc.type] === "図全体を 1 箱") return doc;
|
|
744
|
-
const flow = doc.flow.filter((s) => s.from !== s.to);
|
|
745
|
-
return flow.length === doc.flow.length ? doc : { ...doc, flow };
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
/**
|
|
749
|
-
* 自分へ戻る矢印を書いた人に伝える (#1227)。
|
|
750
|
-
*
|
|
751
|
-
* **落とす前の `flow` を見る**。 落とした後を渡すと、 図が壊れないように外した矢印が
|
|
752
|
-
* 書いた人に届かない (`reportMissingFlowActors` と同じ理由)。
|
|
753
|
-
*
|
|
754
|
-
* 同じ登場人物に何本書いても 1 件にまとめる = 本数だけ知らせが並んでも直し方は変わらない。
|
|
755
|
-
*/
|
|
756
|
-
function reportSelfLoopFlow(doc: DslDocument, onNotice?: (n: CompileNotice) => void): void {
|
|
757
|
-
if (!onNotice) return;
|
|
758
|
-
if (図種の作り[doc.type] === "図全体を 1 箱") return;
|
|
759
|
-
const 知らせた = new Set<string>();
|
|
760
|
-
for (const s of doc.flow) {
|
|
761
|
-
if (s.from !== s.to || 知らせた.has(s.from)) continue;
|
|
762
|
-
知らせた.add(s.from);
|
|
763
|
-
onNotice({
|
|
764
|
-
kind: "flow-self-loop",
|
|
765
|
-
actor: s.from,
|
|
766
|
-
line: s.pos.line,
|
|
767
|
-
message: `"${truncateForMessage(s.from)}" から自分へ戻る矢印は描けません (組み立てから外しました)`,
|
|
768
|
-
hint: "途中の箱を 1 つ足して 2 本に分けるか、 段 (animation) で状態が変わる様子として見せる",
|
|
769
|
-
});
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
|
|
773
752
|
/**
|
|
774
753
|
* 図全体を 1 箱にする図種で、 その箱の上に出す小見出しを渡す (#1247)。
|
|
775
754
|
*
|
|
@@ -893,8 +872,10 @@ function reportLaneMixed(doc: DslDocument, onNotice: (n: CompileNotice) => void)
|
|
|
893
872
|
*
|
|
894
873
|
* ## 既に落ちた行は見ない
|
|
895
874
|
*
|
|
896
|
-
*
|
|
897
|
-
*
|
|
875
|
+
* 居ない名前を指す形 (`flow-actor-missing`) は別の知らせが担う。 落とした後の `doc` を
|
|
876
|
+
* 見ることで、同じ 1 行に知らせが 2 件並ぶのを避ける。
|
|
877
|
+
*
|
|
878
|
+
* 自分へ戻る形は #1462 で落とさなくなった (描画側が輪として描ける)。
|
|
898
879
|
*/
|
|
899
880
|
function reportFlowEndpointNotHonored(
|
|
900
881
|
doc: DslDocument,
|
|
@@ -951,12 +932,83 @@ function reportFlowEndpointNotHonored(
|
|
|
951
932
|
* (`compileMind` の「名前と副題 / 値、 枝の色しか描けません」)、 そこに `枠の指定` が既に
|
|
952
933
|
* 入っている。 二重に伝えると同じ 1 行について知らせが 2 件並ぶ。
|
|
953
934
|
*/
|
|
935
|
+
/**
|
|
936
|
+
* 順序図で面に書いた飾りが使われないことを伝える (#1466)。
|
|
937
|
+
*
|
|
938
|
+
* 順序図は 1 つの板が図を丸ごと描く形になり、面は上端の見出しに **名前と呼び名だけ** で並ぶ。
|
|
939
|
+
* 面ごとの箱が無いので、種類 / 大きさ / 位置 / 行 / 色 / 小見出し / 値 / 図形を載せる先も無い。
|
|
940
|
+
* 黙って落とすと、書いた側は効いていると思い込む。
|
|
941
|
+
*
|
|
942
|
+
* 見本 (`parts`) を重ねた面は対象外 = 見本は別経路で図に取り込まれ、板の見出しには並ばない。
|
|
943
|
+
*/
|
|
944
|
+
/**
|
|
945
|
+
* 順序図の言づてに書いた飾りが使われないことを伝える (#1466)。
|
|
946
|
+
*
|
|
947
|
+
* 板は言づてを **語と向きと種類** で描く。 色味 (`tone`) / 添え字 (`sub`) / 寄せ (`side`) を
|
|
948
|
+
* 載せる場所が無い = 矢印だった頃はその 3 つが矢印に付いていたが、板では行になった。
|
|
949
|
+
* 黙って落とすと、書いた側は効いていると思い込む。
|
|
950
|
+
*/
|
|
951
|
+
function reportMessageOptionNotHonored(doc: DslDocument, onNotice?: (n: CompileNotice) => void): void {
|
|
952
|
+
if (!onNotice) return;
|
|
953
|
+
if (doc.type !== "sequence" && doc.type !== "solidity") return;
|
|
954
|
+
for (const s of doc.flow) {
|
|
955
|
+
const 効かない = [
|
|
956
|
+
s.tone !== undefined ? "色味" : "",
|
|
957
|
+
s.sub !== undefined ? "添え字" : "",
|
|
958
|
+
s.side !== undefined ? "寄せ" : "",
|
|
959
|
+
].filter((x) => x !== "");
|
|
960
|
+
if (効かない.length === 0) continue;
|
|
961
|
+
onNotice({
|
|
962
|
+
kind: "message-option-not-honored",
|
|
963
|
+
actor: s.from,
|
|
964
|
+
line: s.pos?.line ?? 0,
|
|
965
|
+
message: `"${truncateForMessage(s.label)}" に書いた ${効かない.join(" / ")} は効きません (type: ${doc.type} の板は語と向きと種類だけを描きます)`,
|
|
966
|
+
hint: "板には矢印が無いため飾りを載せる先がありません。 言づての種類 (kind: call / return / fire) で描き分けてください",
|
|
967
|
+
});
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
function reportActorKindNotHonored(doc: DslDocument, onNotice?: (n: CompileNotice) => void): void {
|
|
972
|
+
if (!onNotice) return;
|
|
973
|
+
if (doc.type !== "sequence" && doc.type !== "solidity") return;
|
|
974
|
+
for (const a of doc.actors) {
|
|
975
|
+
if (a.partId !== undefined) continue;
|
|
976
|
+
const 効かない = [
|
|
977
|
+
/*
|
|
978
|
+
* 種類は `sequence` でだけ落ちる。
|
|
979
|
+
*
|
|
980
|
+
* `solidity` は種類で **面の並びを決める** (`compileSolidity`) ので、絵にならなくても
|
|
981
|
+
* 書いた意味は figure に出ている。 落ちたと伝えると、正しく効いている指定に毎回鳴る。
|
|
982
|
+
*
|
|
983
|
+
* 記法を通すと既定の `actor` が必ず入るため、値ではなく書いたかどうかの印で見る。
|
|
984
|
+
* 記法を通さず直接組み立てた場合はこの印が無いので、種類を置いたこと自体を「書いた」 とみなす
|
|
985
|
+
*/
|
|
986
|
+
doc.type === "sequence" && a.kindWritten !== false && a.kind !== undefined ? "種類" : "",
|
|
987
|
+
a.posW !== undefined || a.posH !== undefined ? "大きさ" : "",
|
|
988
|
+
a.posX !== undefined || a.posY !== undefined || a.posRel !== undefined ? "位置" : "",
|
|
989
|
+
a.rows !== undefined ? "行" : "",
|
|
990
|
+
a.tone !== undefined ? "色" : "",
|
|
991
|
+
a.eyebrow !== undefined ? "小見出し" : "",
|
|
992
|
+
a.value !== undefined ? "値" : "",
|
|
993
|
+
a.shape !== undefined ? "図形" : "",
|
|
994
|
+
].filter((x) => x !== "");
|
|
995
|
+
if (効かない.length === 0) continue;
|
|
996
|
+
onNotice({
|
|
997
|
+
kind: "actor-kind-not-honored",
|
|
998
|
+
actor: a.name,
|
|
999
|
+
line: a.pos?.line ?? 0,
|
|
1000
|
+
message: `"${truncateForMessage(a.name)}" に書いた ${効かない.join(" / ")} は効きません (type: ${doc.type} の板は名前と呼び名だけを描きます)`,
|
|
1001
|
+
hint: "板には面ごとの箱が無いため飾りを載せる先がありません。 呼び名 (subtitle) に書くか、箱を持つ図種を使ってください",
|
|
1002
|
+
});
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
|
|
954
1006
|
function reportLaneNotHonored(doc: DslDocument, onNotice?: (n: CompileNotice) => void): void {
|
|
955
1007
|
if (!onNotice) return;
|
|
956
1008
|
if (doc.type === "mind") return;
|
|
957
1009
|
// 縦列を選べる図種では、全ての箱が書いていれば効く (#1263)。 効く形では知らせない
|
|
958
|
-
const 効く図種 = 縦列を選べる図種.has(doc.type
|
|
959
|
-
if (効く図種 && 書いた縦列に置く(doc.type
|
|
1010
|
+
const 効く図種 = 縦列を選べる図種.has(doc.type);
|
|
1011
|
+
if (効く図種 && 書いた縦列に置く(doc.type, doc)) return;
|
|
960
1012
|
if (効く図種) {
|
|
961
1013
|
reportLaneMixed(doc, onNotice);
|
|
962
1014
|
return;
|
|
@@ -1062,6 +1114,11 @@ const 空の形の逃げ先: Record<PresetType, string[]> = {
|
|
|
1062
1114
|
pie: [],
|
|
1063
1115
|
bar: [],
|
|
1064
1116
|
line: [],
|
|
1117
|
+
gauge: [],
|
|
1118
|
+
radial: [],
|
|
1119
|
+
stat: [],
|
|
1120
|
+
waffle: [],
|
|
1121
|
+
stacked: [],
|
|
1065
1122
|
funnel: [],
|
|
1066
1123
|
tree: [],
|
|
1067
1124
|
journey: [],
|
|
@@ -1684,7 +1741,12 @@ function applyNodeTones(diagram: CdlDiagram, doc: DslDocument): void {
|
|
|
1684
1741
|
* slugify で actor 名 → lane id / node id の逆引き、 posX/Y set 済 actor に対応する lane / node に
|
|
1685
1742
|
* 座標を書込む。 未指定 actor は従来 auto layout 経路そのまま。
|
|
1686
1743
|
*/
|
|
1687
|
-
function applyCanvasPivotPositions(
|
|
1744
|
+
function applyCanvasPivotPositions(
|
|
1745
|
+
diagram: CdlDiagram,
|
|
1746
|
+
doc: DslDocument,
|
|
1747
|
+
// 下見 (`probe`) の呼出では渡さない = 同じ知らせが 2 度出る
|
|
1748
|
+
onNotice?: (notice: CompileNotice) => void,
|
|
1749
|
+
): void {
|
|
1688
1750
|
for (const actor of doc.actors) {
|
|
1689
1751
|
if (actor.partId !== undefined) continue; // parts actor は別経路 (mergePartsFromActors) で処理
|
|
1690
1752
|
const aliasSlug = slugify(actor.name);
|
|
@@ -1718,14 +1780,32 @@ function applyCanvasPivotPositions(diagram: CdlDiagram, doc: DslDocument): void
|
|
|
1718
1780
|
if (actor.nodes) {
|
|
1719
1781
|
for (const [subKey, override] of Object.entries(actor.nodes)) {
|
|
1720
1782
|
if (override.posX === undefined || override.posY === undefined) continue;
|
|
1783
|
+
let 当たった = 0;
|
|
1721
1784
|
for (const node of diagram.nodes) {
|
|
1722
1785
|
if (node.id === `${aliasSlug}-${subKey}` || node.id === `${subKey}-${aliasSlug}`) {
|
|
1723
1786
|
node.posX = override.posX;
|
|
1724
1787
|
node.posY = override.posY;
|
|
1725
1788
|
if (override.posW !== undefined) node.posW = override.posW;
|
|
1726
1789
|
if (override.posH !== undefined) node.posH = override.posH;
|
|
1790
|
+
当たった += 1;
|
|
1727
1791
|
}
|
|
1728
1792
|
}
|
|
1793
|
+
/*
|
|
1794
|
+
* **当たらなかったことを伝える** (#1466)。
|
|
1795
|
+
*
|
|
1796
|
+
* この経路が動くのは、図種が 1 人につき複数の箱を作る時だけ。 順序図が名札 / 余白 /
|
|
1797
|
+
* 足を作っていた頃はそこに当たっていたが、板になって作らなくなった = いまはどの図種も
|
|
1798
|
+
* この形の箱を作らない。 黙って落とすと、書いた側は効いていると思い込む。
|
|
1799
|
+
*/
|
|
1800
|
+
if (当たった === 0) {
|
|
1801
|
+
onNotice?.({
|
|
1802
|
+
kind: "sub-node-not-found",
|
|
1803
|
+
actor: actor.name,
|
|
1804
|
+
line: actor.pos?.line ?? 0,
|
|
1805
|
+
message: `"${truncateForMessage(actor.name)}" の nodes に書いた "${truncateForMessage(subKey)}" に当たる箱が図にありません`,
|
|
1806
|
+
hint: "1 人に複数の箱を作る図種でだけ効きます。 箱そのものの位置は actor 側の 位置: に書いてください",
|
|
1807
|
+
});
|
|
1808
|
+
}
|
|
1729
1809
|
}
|
|
1730
1810
|
}
|
|
1731
1811
|
}
|
|
@@ -2250,6 +2330,20 @@ const PARTS_GAP = 120;
|
|
|
2250
2330
|
*/
|
|
2251
2331
|
const STACK_PITCH = 280;
|
|
2252
2332
|
|
|
2333
|
+
/**
|
|
2334
|
+
* 既存の図が縦に何段ぶんを占めるかの目安 (#1481)。
|
|
2335
|
+
*
|
|
2336
|
+
* **箱の数では数えない**。 数で数えると、図を丸ごと 1 つの箱で描く種別が 1 段に潰れる。
|
|
2337
|
+
* 順序図は `#1466` で 1 枚の板になり、高さ 432 の箱 1 つになった = 1 段 (280) と見積もられ、
|
|
2338
|
+
* その下に置いたパーツが板に 31px 重なっていた (実測)。
|
|
2339
|
+
*
|
|
2340
|
+
* 大きさを自分で持つ箱は、その高さから段数を出す。 持たない箱は今までどおり 1 段と数えるので、
|
|
2341
|
+
* 高さを書かない図の並び方は変わらない。
|
|
2342
|
+
*/
|
|
2343
|
+
export function partsBaseRows(nodes: ReadonlyArray<{ h?: number }>): number {
|
|
2344
|
+
return nodes.reduce((acc, n) => acc + Math.max(1, Math.ceil(positiveOr(n.h, 0) / STACK_PITCH)), 0);
|
|
2345
|
+
}
|
|
2346
|
+
|
|
2253
2347
|
/**
|
|
2254
2348
|
* 位置を書かなかったパーツを格子に並べた時の、 矩形の中心。
|
|
2255
2349
|
*
|
|
@@ -2260,17 +2354,17 @@ const STACK_PITCH = 280;
|
|
|
2260
2354
|
* 隣と重なる (実測 = 400 の次に 200 を置くと 280 重なった)。 段の高さも段内の最大高で揃える。
|
|
2261
2355
|
* 縦は自分の高さの半分だけ段の上端から下げて、 段内で上端を揃える。
|
|
2262
2356
|
*
|
|
2263
|
-
* @param
|
|
2357
|
+
* @param baseRows 既存の図が占める段数の目安 (`partsBaseRows`)。 図の下から並べ始めるために使う
|
|
2264
2358
|
*/
|
|
2265
2359
|
export function partsGridCenters(
|
|
2266
|
-
|
|
2360
|
+
baseRows: number,
|
|
2267
2361
|
items: ReadonlyArray<{ id: string; w: number; h: number }>,
|
|
2268
2362
|
): Map<string, { cx: number; cy: number }> {
|
|
2269
2363
|
const out = new Map<string, { cx: number; cy: number }>();
|
|
2270
2364
|
if (items.length === 0) return out;
|
|
2271
2365
|
// 公開している関数なので、 呼出側が渡す値を入口で閉じる。 数でない箱の数や桁溢れを
|
|
2272
2366
|
// そのまま計算に入れると、 描けない座標を返すことになる
|
|
2273
|
-
const safeCount = Number.isSafeInteger(
|
|
2367
|
+
const safeCount = Number.isSafeInteger(baseRows) && baseRows >= 0 ? baseRows : 0;
|
|
2274
2368
|
const top = safeCount * STACK_PITCH + PARTS_GAP * 2;
|
|
2275
2369
|
// 同じ名前が 2 度来たら先の方だけを見る。 後の分を残すと、 どちらを指したか決められない
|
|
2276
2370
|
// まま列の送り幅にも影響する
|
|
@@ -2418,7 +2512,7 @@ function partGridCenters(
|
|
|
2418
2512
|
extents.set(a.name, partFrameExtent(part, t.w, t.h));
|
|
2419
2513
|
}
|
|
2420
2514
|
const centers = partsGridCenters(
|
|
2421
|
-
baseNodes
|
|
2515
|
+
partsBaseRows(baseNodes),
|
|
2422
2516
|
placedActors.map((a) => ({ id: a.name, ...extents.get(a.name)! })),
|
|
2423
2517
|
);
|
|
2424
2518
|
// merge に渡すのは段の中心。 矩形の中心とのずれを引く。 引かないと、 段ごとに箱の高さが
|
|
@@ -2505,9 +2599,24 @@ function cleanupPlaceholderActor(
|
|
|
2505
2599
|
for (const n of target.nodes) {
|
|
2506
2600
|
if (ownedLaneIds.has(n.lane)) ownedNodeIds.add(n.id);
|
|
2507
2601
|
}
|
|
2602
|
+
/*
|
|
2603
|
+
* 素の登場人物が持つ id は消さない (#1466)。
|
|
2604
|
+
*
|
|
2605
|
+
* 下の頭一致は、見本のために作られた仮の箱 (`a-header` / `s0-a`) を拾うためのもの。
|
|
2606
|
+
* ところが名前が重なった素の登場人物は `重なりを解く` 側で `a-c40bf6` のような id に
|
|
2607
|
+
* 作り替えられるため、同じ頭で始まり **本体の箱まで巻き込む**。
|
|
2608
|
+
*
|
|
2609
|
+
* 見本と重なる名前を書いた図で、素の箱が黙って消えていた (実測で `flow` / `swimlane` /
|
|
2610
|
+
* `er` / `state` / `topology` / `class` / `c4` の 7 図種すべて)。 順序図だけは面ごとの
|
|
2611
|
+
* 縦列で引けたため免れており、#1466 で板になって縦列が無くなると同じ穴に落ちる。
|
|
2612
|
+
*/
|
|
2613
|
+
const 素の箱のid = new Set(
|
|
2614
|
+
doc.actors.filter((x) => x.partId === undefined).map((x) => slugify(x.name)),
|
|
2615
|
+
);
|
|
2508
2616
|
// actor 専用 lane を引き当てられない経路 (flow / topology 等の共有 lane preset) は従来どおり dragon
|
|
2509
2617
|
// slug の prefix match に fallback する。 これらは 1 actor = 1 node (id = slug) の生成規則。
|
|
2510
2618
|
const matchesAliasSlug = (id: string): boolean => {
|
|
2619
|
+
if (素の箱のid.has(id)) return false;
|
|
2511
2620
|
if (id === aliasSlug) return true;
|
|
2512
2621
|
if (id.startsWith(`${aliasSlug}-`)) return true;
|
|
2513
2622
|
// sequence step anchor = `s{N}-{aliasSlug}` pattern
|
|
@@ -2540,15 +2649,24 @@ function cleanupPlaceholderActor(
|
|
|
2540
2649
|
// (`_`/全角の扱い等)、 aliasSlug (dragon slugify) と lane.id が不一致になる actor 名がある。 lane.label
|
|
2541
2650
|
// は両経路とも a.name 生値なので slug 差の影響を受けず確実に一致する。 id === aliasSlug は
|
|
2542
2651
|
// label 未設定 preset への fallback (exact match のみ、 prefix は false match risk のため付けない)。
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2652
|
+
/*
|
|
2653
|
+
* **図種で分けない** (#1466)。 以前は順序図系だけを掃除していたが、順序図が板になって
|
|
2654
|
+
* 面ごとの縦列を作らなくなり、この分岐は誰も通らなくなった。 一方で縦列を作る他の図種
|
|
2655
|
+
* (`swimlane` 等) では見本の仮の縦列が空のまま残っていた (実測)。
|
|
2656
|
+
*
|
|
2657
|
+
* **中身が残っている縦列は消さない**。 1 本の縦列を全員で共有する図種 (`flow` / `topology`)
|
|
2658
|
+
* では、その縦列の名札がたまたま登場人物の名前と一致することがある。 消すと本体の箱が
|
|
2659
|
+
* 行き場を失う。
|
|
2660
|
+
*/
|
|
2661
|
+
const 残る箱を持つ = new Set(target.nodes.map((n) => n.lane));
|
|
2662
|
+
target.lanes = target.lanes.filter((l) => {
|
|
2663
|
+
// 明示 lane mapping (a.lane) 先は part の張替え先なので保持する。
|
|
2664
|
+
if (a.lane !== undefined && l.id === a.lane) return true;
|
|
2665
|
+
if (残る箱を持つ.has(l.id)) return true;
|
|
2666
|
+
if (l.label === a.name) return false;
|
|
2667
|
+
if (l.id === aliasSlug) return false;
|
|
2668
|
+
return true;
|
|
2669
|
+
});
|
|
2552
2670
|
// 削除された node / edge を activate 参照している既存 phase の cleanup (node 削除と同じ判定経路
|
|
2553
2671
|
// = 取りこぼすと存在しない id が activate に残り dangling 参照になる、 #873)
|
|
2554
2672
|
for (const phase of target.phases) {
|
|
@@ -3226,23 +3344,14 @@ function applyEdgeInlineOptions(
|
|
|
3226
3344
|
return;
|
|
3227
3345
|
}
|
|
3228
3346
|
const used = new Set<string>();
|
|
3229
|
-
//
|
|
3230
|
-
//
|
|
3231
|
-
//
|
|
3232
|
-
|
|
3233
|
-
const isSeqLike = doc.type === "sequence" || doc.type === "solidity";
|
|
3234
|
-
doc.flow.forEach((s, stepIdx) => {
|
|
3347
|
+
// edge.from / edge.to は plain slug (slugify(actor 名))。
|
|
3348
|
+
//
|
|
3349
|
+
// 順序図系 (`sequence` / `solidity`) はここに来ない = #1466 で板になり矢印を作らない。
|
|
3350
|
+
doc.flow.forEach((s) => {
|
|
3235
3351
|
const fromId = slugify(s.from);
|
|
3236
3352
|
const toId = slugify(s.to);
|
|
3237
3353
|
const target = diagram.edges.find((e) => {
|
|
3238
3354
|
if (used.has(e.id)) return false;
|
|
3239
|
-
if (isSeqLike) {
|
|
3240
|
-
// edge.from / edge.to は `s{stepIdx}-{laneId}` 命名規則
|
|
3241
|
-
return (
|
|
3242
|
-
(e.from === `s${stepIdx}-${fromId}` || e.from === fromId) &&
|
|
3243
|
-
(e.to === `s${stepIdx}-${toId}` || e.from === e.to)
|
|
3244
|
-
);
|
|
3245
|
-
}
|
|
3246
3355
|
return e.from === fromId && e.to === toId;
|
|
3247
3356
|
});
|
|
3248
3357
|
if (!target) return;
|
|
@@ -3315,6 +3424,15 @@ function 矢印へ書き写す(target: CdlEdge, s: DslStep, doc: DslDocument): v
|
|
|
3315
3424
|
}
|
|
3316
3425
|
// 矢印がどの辺から出るか (#1385)。 書かなければ描画側が自動で選ぶ
|
|
3317
3426
|
if (s.side !== undefined) target.side = s.side;
|
|
3427
|
+
// 矢印の先の形 (#1462)。 書かない矢印には値を入れない = 既存の図が変わらない。
|
|
3428
|
+
//
|
|
3429
|
+
// **矢印を作る 9 図種すべてがここを通る**。 図種ごとの組み立てにも同じ形を置いたが、
|
|
3430
|
+
// 外しても 9 図種とも渡っていた (変異試験で実測) = 余分だったので消した
|
|
3431
|
+
if (s.head !== undefined) target.head = s.head;
|
|
3432
|
+
// 端の残り 3 欄 (#1466)。 出どころ側の印と、両端の塗り。 ER は端ごとに違う個数を示す
|
|
3433
|
+
if (s.tailHead !== undefined) target.tailHead = s.tailHead;
|
|
3434
|
+
if (s.headFill !== undefined) target.headFill = s.headFill;
|
|
3435
|
+
if (s.tailHeadFill !== undefined) target.tailHeadFill = s.tailHeadFill;
|
|
3318
3436
|
if (s.labelOffsetX !== undefined) target.labelOffsetX = s.labelOffsetX;
|
|
3319
3437
|
if (s.labelOffsetY !== undefined) target.labelOffsetY = s.labelOffsetY;
|
|
3320
3438
|
if (s.overlay !== undefined) target.overlay = s.overlay;
|
|
@@ -3352,34 +3470,6 @@ function 鎖のどの行から来たか(doc: DslDocument, edgeIndex: number): Ds
|
|
|
3352
3470
|
return doc.flow.find((s) => s.to === to.name);
|
|
3353
3471
|
}
|
|
3354
3472
|
|
|
3355
|
-
/**
|
|
3356
|
-
* 描画側が大きさを持つ種別。
|
|
3357
|
-
*
|
|
3358
|
-
* 記法の `kind` は描画の種別より広い。 そのまま渡すと大きさを引けずに描画が落ちる
|
|
3359
|
-
* (実測 = solidity の golden 4 件が `Cannot read properties of undefined`)。
|
|
3360
|
-
*/
|
|
3361
|
-
const DRAWABLE_KINDS: ReadonlySet<string> = new Set(NODE_KINDS);
|
|
3362
|
-
|
|
3363
|
-
/**
|
|
3364
|
-
* 描画側に無い記法の種別を、 意味の近い描画の種別に読み替える。
|
|
3365
|
-
*
|
|
3366
|
-
* Solidity の記法は `eoa` / `contract` のように領域固有の語を使う。 描画側に同じ名前は無いが、
|
|
3367
|
-
* 意味の対応する形はある (`shape-wallet` / `shape-smart-contract`)。 読み替えないと名札が
|
|
3368
|
-
* 一律 `card` になり、 「書いたとおりの形になる」 が Solidity の図だけ成立しない。
|
|
3369
|
-
*
|
|
3370
|
-
* 並び順 (`compileSolidity` の `kindOrder`) はこの読み替えの前の値で決まる = 読み替えても
|
|
3371
|
-
* 縦線の並びは変わらない。
|
|
3372
|
-
*/
|
|
3373
|
-
const KIND_ALIAS: Readonly<Record<string, string>> = {
|
|
3374
|
-
eoa: "shape-wallet",
|
|
3375
|
-
wallet: "shape-wallet",
|
|
3376
|
-
multisig: "signer",
|
|
3377
|
-
contract: "shape-smart-contract",
|
|
3378
|
-
proxy: "shape-smart-contract",
|
|
3379
|
-
library: "shape-code-block",
|
|
3380
|
-
interface: "shape-code-block",
|
|
3381
|
-
};
|
|
3382
|
-
|
|
3383
3473
|
/**
|
|
3384
3474
|
* 記法の `values:` を図に載せる (#1162)。
|
|
3385
3475
|
*
|
|
@@ -3883,6 +3973,7 @@ const VALUE_NOTICE_HINT: Readonly<Record<string, string>> = {
|
|
|
3883
3973
|
"invalid-id": "名前に使えるのは英数字と _ だけ",
|
|
3884
3974
|
};
|
|
3885
3975
|
|
|
3976
|
+
|
|
3886
3977
|
/**
|
|
3887
3978
|
* 図全体を 1 つの箱で描く種別。
|
|
3888
3979
|
*
|
|
@@ -3911,253 +4002,6 @@ const SINGLE_BOX_KINDS: ReadonlySet<string> = new Set([
|
|
|
3911
4002
|
"journey-map",
|
|
3912
4003
|
]);
|
|
3913
4004
|
|
|
3914
|
-
/** 記法の種別を描画の種別に直す。 描けない種別のままなら `undefined`。 */
|
|
3915
|
-
function drawableKind(kind: string | undefined): string | undefined {
|
|
3916
|
-
if (kind === undefined) return undefined;
|
|
3917
|
-
const mapped = KIND_ALIAS[kind] ?? kind;
|
|
3918
|
-
return DRAWABLE_KINDS.has(mapped) ? mapped : undefined;
|
|
3919
|
-
}
|
|
3920
|
-
|
|
3921
|
-
/**
|
|
3922
|
-
* 順序図の名札 (lifeline 上端 / 下端) の高さを揃える。
|
|
3923
|
-
*
|
|
3924
|
-
* `kind` を書いたとおりに載せると、 種別ごとに要る高さが変わる (行を持つ storage は 206、
|
|
3925
|
-
* card は 72)。 揃えないと縦線の始まる位置がばらけ、 「同じ高さから下りる」 読み方が崩れる。
|
|
3926
|
-
*
|
|
3927
|
-
* 上端は最も高いものに合わせる。 下端も同じ値にする = 上下で形が違うと、 同じ登場人物が
|
|
3928
|
-
* 別物に見える。
|
|
3929
|
-
*/
|
|
3930
|
-
function alignSeqHeaderHeights(diagram: CdlDiagram, doc: DslDocument): void {
|
|
3931
|
-
if (doc.type !== "sequence" && doc.type !== "solidity") return;
|
|
3932
|
-
// 名札の id は `{laneId}-header` / `{laneId}-footer` の構造。 末尾の一致だけで見ると、
|
|
3933
|
-
// 登場人物名が `Auth Header` の時に step の目印 `s0-auth-header` を拾い、 見えない 2px の
|
|
3934
|
-
// 箱を名札の高さまで広げてしまう (#883 と同根)。
|
|
3935
|
-
const isEnd = (n: CdlDiagram["nodes"][number]): boolean =>
|
|
3936
|
-
n.id === `${n.lane}-header` || n.id === `${n.lane}-footer`;
|
|
3937
|
-
const ends = diagram.nodes.filter(isEnd);
|
|
3938
|
-
if (ends.length === 0) return;
|
|
3939
|
-
// `posH` を書いた名札は揃えの外に置く。 「その名札だけを指定の大きさにし、 他には影響させない」
|
|
3940
|
-
// という指定なので (`types.ts` の `nodes` override)、 値を変えるのも、 他の名札を引きずるのも
|
|
3941
|
-
// 契約に反する (実測 = `posH: 400` を 1 つ書くと、 無関係な名札まで 72 → 400 になった)。
|
|
3942
|
-
const auto = ends.filter((n) => n.posH === undefined);
|
|
3943
|
-
if (auto.length === 0) return;
|
|
3944
|
-
const tallest = Math.max(...auto.map((n) => n.h ?? 0));
|
|
3945
|
-
if (tallest <= 0) return;
|
|
3946
|
-
for (const n of auto) n.h = tallest;
|
|
3947
|
-
}
|
|
3948
|
-
|
|
3949
|
-
/**
|
|
3950
|
-
* 名札に載せるのに要る高さ (world 単位)。
|
|
3951
|
-
*
|
|
3952
|
-
* 絵が箱の外に出る `shape-` のうち、 **高さを上げれば下のはみ出しが消える** 4 種だけを持つ。
|
|
3953
|
-
*
|
|
3954
|
-
* `actor` / `function` / `storage` / `event` の 4 種は `#1066` までここに載っていた。 描画側が
|
|
3955
|
-
* 名前を箱の高さに関係なく固定の位置に置いていたため、 名札 (高さ 72) に載せると名前が下端を
|
|
3956
|
-
* またいだ。 `cardene777/cdl#416` が小型用の配置を足して収まるようになったので外した
|
|
3957
|
-
* (実測 = 名札の高さで下へ 52.3 から 63.8 の余裕、 目印がある場合でも 9.5 以上)。
|
|
3958
|
-
*
|
|
3959
|
-
* 値は実測 = 高さを 1 ずつ変えて描き、 絵の下端が箱の下端を越えなくなる最小の整数を取った
|
|
3960
|
-
* (`type: flow` で `大きさ:` を書いて掃いた)。 **絵を変えたら測り直す**。
|
|
3961
|
-
* 表が実際の描画と合っているかは `apps/playground-spa/tests/node-label-fit.spec.ts` が
|
|
3962
|
-
* 両側 (この高さで収まる / 2 低いとはみ出す) を実 render で測って見る。
|
|
3963
|
-
*/
|
|
3964
|
-
const LABEL_MIN_H: Readonly<Record<string, number>> = {
|
|
3965
|
-
// `shape-` のうち、 高さを上げれば下のはみ出しが消える 4 種 (#1067)。 値は「収まる最小の高さ」
|
|
3966
|
-
// で、 1 手前 (値 - 1) では 0.9-1 はみ出すことを実測した
|
|
3967
|
-
"shape-person": 228, // 名札 72 で下へ 155.9
|
|
3968
|
-
"shape-server-rack": 166, // 94
|
|
3969
|
-
"shape-website": 98, // 26
|
|
3970
|
-
"shape-warehouse": 79, // 7
|
|
3971
|
-
};
|
|
3972
|
-
|
|
3973
|
-
/**
|
|
3974
|
-
* 名札の高さをどれだけ上げても収まらない種別 (#1067)。
|
|
3975
|
-
*
|
|
3976
|
-
* `shape-` を名札に書くと絵が箱の外に描かれる。 49 種すべてを実測したところ、 完全に収まるのは
|
|
3977
|
-
* 5 種 (`shape-cloud` / `shape-window` / `shape-message-bubble` / `shape-token` /
|
|
3978
|
-
* `shape-online-shop`) だけだった。
|
|
3979
|
-
*
|
|
3980
|
-
* ## 分ける基準は「名前が読めるか」 (#1106 で変わった)
|
|
3981
|
-
*
|
|
3982
|
-
* | 向き | 実害 | 扱い |
|
|
3983
|
-
* |---|---|---|
|
|
3984
|
-
* | 下 | 縦線が絵を貫く | 落とす |
|
|
3985
|
-
* | 左右 | 隣の本とぶつかる (`shape-code-block` は右へ 132) | 落とす |
|
|
3986
|
-
* | 上のみ | 何ともぶつからず図の外にも出ない | **原則残すが例外あり** |
|
|
3987
|
-
*
|
|
3988
|
-
* `#1067` は向きだけで分け、 上だけのはみ出しは 10 種すべて残した。 実測で viewBox の内側に
|
|
3989
|
-
* 収まることを確かめており (最大の `shape-robot-arm` は上へ 129 だが余裕が 23)、 箱から出ても
|
|
3990
|
-
* 読み手には壊れて見えないと判断したため。
|
|
3991
|
-
*
|
|
3992
|
-
* **その判断が実機で崩れた**。 `shape-wallet` は上へ 12.1 しか出ないのに、 絵が小さく潰れて
|
|
3993
|
-
* 名前と重なり `EOA` が読めない状態だった。 はみ出し量では説明できない = 12.1 の
|
|
3994
|
-
* `shape-wallet` を落とし、 129 の `shape-robot-arm` を残す。
|
|
3995
|
-
*
|
|
3996
|
-
* したがって **基準は「名前が読めるか」** で、 向きは目安にすぎない。 上だけに出る 10 種のうち
|
|
3997
|
-
* 残るのは 9 種で、 分ける根拠は目視のみ (機械的な基準は無い)。
|
|
3998
|
-
*
|
|
3999
|
-
* ## ここに載るのは「高さで直らない」 種別だけ
|
|
4000
|
-
*
|
|
4001
|
-
* 下のはみ出しは高さで直ることがある。 4 種は `LABEL_MIN_H` に最小の高さを持たせ、 `rows` 等で
|
|
4002
|
-
* 名札が高くなった図では書いたとおりの形で載る (`#1061` の「収まる高さがある時は書いたとおりに
|
|
4003
|
-
* 載せる」 と同じ扱い)。
|
|
4004
|
-
*
|
|
4005
|
-
* こちらに載るのは 3 種類。 **左右にはみ出す 24 種** は横幅が高さで変わらないため直らない
|
|
4006
|
-
* (実測 = `shape-smart-contract` は h=72 でも h=430 でも右へ 15.2)。 **下のはみ出しが高さに
|
|
4007
|
-
* 依らない 6 種** は h を 72 から 600 まで上げても値が変わらない (実測 = `shape-stack` は
|
|
4008
|
-
* 常に 36、 `shape-cylinder` は 150 以上で常に 1)。 **`shape-wallet`** は上のはみ出しが
|
|
4009
|
-
* 高さで変わらず、 絵が潰れて名前と重なる (#1106)。
|
|
4010
|
-
*
|
|
4011
|
-
* ## 失うもの
|
|
4012
|
-
*
|
|
4013
|
-
* Solidity の読み替え (`#975`) 4 組のうち 3 組がここに入るため、 名札では `card` になる
|
|
4014
|
-
* (`contract` / `proxy` → `shape-smart-contract`、 `library` / `interface` →
|
|
4015
|
-
* `shape-code-block`、 `eoa` / `wallet` → `shape-wallet`)。 名札で形が残るのは
|
|
4016
|
-
* `multisig` → `signer` だけ。
|
|
4017
|
-
*/
|
|
4018
|
-
const LABEL_NEVER_FITS: ReadonlySet<string> = new Set([
|
|
4019
|
-
// 左右にはみ出す 24 種。 横幅は高さで変わらないため直らない
|
|
4020
|
-
"shape-api-gateway",
|
|
4021
|
-
"shape-atm",
|
|
4022
|
-
"shape-auditor",
|
|
4023
|
-
"shape-bank",
|
|
4024
|
-
"shape-bitcoin-chain",
|
|
4025
|
-
"shape-blockchain",
|
|
4026
|
-
"shape-blockchain-block",
|
|
4027
|
-
"shape-blockchain-node",
|
|
4028
|
-
"shape-brokerage",
|
|
4029
|
-
"shape-code-block",
|
|
4030
|
-
"shape-customer-service",
|
|
4031
|
-
"shape-ethereum-chain",
|
|
4032
|
-
"shape-hexagon",
|
|
4033
|
-
"shape-kanban-card",
|
|
4034
|
-
"shape-lawyer",
|
|
4035
|
-
"shape-network-node",
|
|
4036
|
-
"shape-nft",
|
|
4037
|
-
"shape-notary",
|
|
4038
|
-
"shape-regulator",
|
|
4039
|
-
"shape-satellite",
|
|
4040
|
-
"shape-smart-contract",
|
|
4041
|
-
"shape-terminal",
|
|
4042
|
-
"shape-trader",
|
|
4043
|
-
"shape-trust-bank",
|
|
4044
|
-
// 下のはみ出しが高さに依らない 6 種
|
|
4045
|
-
"shape-cylinder",
|
|
4046
|
-
"shape-diamond",
|
|
4047
|
-
"shape-file",
|
|
4048
|
-
"shape-folder",
|
|
4049
|
-
"shape-mobile-device",
|
|
4050
|
-
"shape-stack",
|
|
4051
|
-
// 上へ出る絵が名札の大きさでは読めない。 `#1067` では「上は何ともぶつからない」 として残したが、
|
|
4052
|
-
// 実際には絵が小さく潰れて名前と重なり、 横に並べた時も 1 本だけ頭が浮く (user 実機確認)
|
|
4053
|
-
"shape-wallet",
|
|
4054
|
-
]);
|
|
4055
|
-
|
|
4056
|
-
/**
|
|
4057
|
-
* 名札が、 小型の `card` では描かれない文字を持つか。
|
|
4058
|
-
*
|
|
4059
|
-
* 小型の `card` (`h < 100`) が描くのは名前だけ。 `subtitle` と `eyebrow` は分岐で外れ、
|
|
4060
|
-
* `value` は `card` が元から描かない。 `rows` を描くのは種別が限られる。
|
|
4061
|
-
* どれか 1 つでも持つ名札を `card` に落とすと、 著者が書いた文字が画面から消える。
|
|
4062
|
-
*/
|
|
4063
|
-
function hasAuthoredText(n: CdlDiagram["nodes"][number]): boolean {
|
|
4064
|
-
if (n.subtitle !== undefined || n.eyebrow !== undefined || n.value !== undefined) return true;
|
|
4065
|
-
return rendersRows(n.kind) && (n.rows?.length ?? 0) > 0;
|
|
4066
|
-
}
|
|
4067
|
-
|
|
4068
|
-
/**
|
|
4069
|
-
* 絵が箱に収まらない `shape-` を名札から外す (#1061 / #1067)。
|
|
4070
|
-
*
|
|
4071
|
-
* `#975` が「書いた種別を名札に載せる」 挙動を入れ、 `#1058` が「書かなかった時は載せない」
|
|
4072
|
-
* を直した。 残っていたのは **書いた時にはみ出す** 側で、 名札は小型の箱 (`h: 72`) なのに
|
|
4073
|
-
* `shape-` は絵を自分の大きさで描くため、 絵が箱の外に出て縦線に貫かれる。
|
|
4074
|
-
*
|
|
4075
|
-
* `actor` / `function` / `storage` / `event` は `#1066` まで対象だった (名前を固定位置に置く
|
|
4076
|
-
* ため名前が下端をまたいだ = actor 21.6 / function 21.6 / storage 13.6 / event 24.2 world px)。
|
|
4077
|
-
* 描画側 (`cardene777/cdl#416`) が小さい箱で名前を中央に置くようになったので外した。
|
|
4078
|
-
* **いま落とす理由は絵のはみ出しだけ**。
|
|
4079
|
-
*
|
|
4080
|
-
* **収まる高さがある時は書いたとおりに載せる**。 `rows` を書いた名札は
|
|
4081
|
-
* `requiredRowsHeight` で 206 以上になり、 揃え (`alignSeqHeaderHeights`) がその高さを
|
|
4082
|
-
* 全本に配る。 判定を揃えの後に置くのはこのため。
|
|
4083
|
-
*
|
|
4084
|
-
* ただし **揃えで届くのは表の値が 206 以下の種別だけ**。 `shape-warehouse` (79) /
|
|
4085
|
-
* `shape-website` (98) / `shape-server-rack` (166) は収まるが、 `shape-person` (228) は
|
|
4086
|
-
* 届かず `card` に落ちる (`rows` 1 件では 206 まで)。
|
|
4087
|
-
*
|
|
4088
|
-
* **著者が書いた文字を持つ名札は落とさない**。 小型の `card` は名前しか描かない
|
|
4089
|
-
* (`subtitle` / `eyebrow` は `h < 100` の分岐で外れ、 `value` は元から描かない)。 落とすと
|
|
4090
|
-
* 書いた文字が画面から消える = 絵がはみ出すより悪い。 `rows` と同じ扱いにする。
|
|
4091
|
-
*
|
|
4092
|
-
* 落とす時に失うものは、 種別ごとの絵と、 既定の配色での枠線の色。 本 app の配色は枠線の色を
|
|
4093
|
-
* 上書きするため見た目は変わらないが、 既定の配色で使う利用者には差が出る。
|
|
4094
|
-
* それでも落とすのは、 絵が箱の外に出る方が読み手に与える誤りが大きいため。
|
|
4095
|
-
*
|
|
4096
|
-
* 高さを上げる方向は採らない。 名札の高さは全本で揃える規約があるため 1 本の指定が全体に
|
|
4097
|
-
* 伝播し、 全名札が 2-3 倍になる (`#1058` で実測、 golden 25 件が変化)。
|
|
4098
|
-
*
|
|
4099
|
-
* **判定は上下 1 組でする**。 `nodes` override で上端だけ大きさを書くと (`posH: 120`)、
|
|
4100
|
-
* 上端は収まり下端 (72) は収まらないため、 1 つずつ見ると同じ登場人物の上下で形が変わる。
|
|
4101
|
-
* 上下で形が違うと別物に見えるので、 どちらかが収まらなければ両方落とす。
|
|
4102
|
-
*
|
|
4103
|
-
* ## `shape-` も対象に含める (#1067)
|
|
4104
|
-
*
|
|
4105
|
-
* 当初は対象外にしていた。 これらは名前を箱ではなく自分の絵に対して置くため、 箱を基準に測ると
|
|
4106
|
-
* 収まっていないように見えるだけだと考えたため。 49 種を実測すると **絵そのものが箱の外に出て
|
|
4107
|
-
* 縦線に貫かれ、 隣の本ともぶつかって** いた。 どの種別をどう扱うかは `LABEL_NEVER_FITS` の
|
|
4108
|
-
* 説明が SSOT。
|
|
4109
|
-
*
|
|
4110
|
-
* ## 覆っていない範囲
|
|
4111
|
-
*
|
|
4112
|
-
* **上だけにはみ出す `shape-` は 9 種を残す**。 何ともぶつからず図の外にも出ないため
|
|
4113
|
-
* (`LABEL_NEVER_FITS` の説明を参照)。 箱の外に絵があること自体は直っておらず、 縦線が絵を貫く。
|
|
4114
|
-
* 縦線の終点は描画側が箱の下端で決めており、 組み立て側からは変えられない。
|
|
4115
|
-
*
|
|
4116
|
-
* 10 種のうち `shape-wallet` だけは落とす (#1106)。 上へ 12.1 しか出ないのに絵が潰れて名前と
|
|
4117
|
-
* 重なるため = 分ける基準ははみ出し量ではなく「名前が読めるか」。
|
|
4118
|
-
*
|
|
4119
|
-
* **著者が文字を書いた名札は落とさない**。 小型の `card` は名前しか描かないため、 落とすと
|
|
4120
|
-
* 書いた文字が画面から消える。 この保護によって `shape-` は落ちずに残るが、 描画側が絵を箱に
|
|
4121
|
-
* 収めるようになったので **下と左右には出ない** (`#1105`、 49 種を実測して 0)。 残るのは上だけで、
|
|
4122
|
-
* 上の扱いはこの節の 1 つ目と同じ。
|
|
4123
|
-
*
|
|
4124
|
-
* `actor` / `function` / `storage` / `event` は `#1066` まで落とす対象だった。 描画側
|
|
4125
|
-
* (`cardene777/cdl#416`) が小さい箱で名前を中央に置くようになったので外した = 名前がはみ出す
|
|
4126
|
-
* 理由で落とす経路はもう無い。 いま落とすのは `shape-` だけで、 理由は絵のはみ出し。
|
|
4127
|
-
*/
|
|
4128
|
-
function dropUnfittableEndKinds(diagram: CdlDiagram, doc: DslDocument): void {
|
|
4129
|
-
if (doc.type !== "sequence" && doc.type !== "solidity") return;
|
|
4130
|
-
const ends = new Map<string, CdlDiagram["nodes"]>();
|
|
4131
|
-
for (const n of diagram.nodes) {
|
|
4132
|
-
if (n.id !== `${n.lane}-header` && n.id !== `${n.lane}-footer`) continue;
|
|
4133
|
-
const pair = ends.get(n.lane);
|
|
4134
|
-
if (pair === undefined) ends.set(n.lane, [n]);
|
|
4135
|
-
else pair.push(n);
|
|
4136
|
-
}
|
|
4137
|
-
for (const pair of ends.values()) {
|
|
4138
|
-
// 著者が書いた文字を持つ名札は落とさない。 小型の `card` は名前しか描かないため、
|
|
4139
|
-
// 落とすと書いた文字が画面から消える (`#387` と同じ壊れ方になる)。 これらは上端にしか
|
|
4140
|
-
// 載らないため 1 組で見る。
|
|
4141
|
-
if (pair.some(hasAuthoredText)) continue;
|
|
4142
|
-
const 収まらない = pair.some((n) => {
|
|
4143
|
-
// 高さを上げても直らない種別 (#1067)。 高さを見ずに落とす
|
|
4144
|
-
if (LABEL_NEVER_FITS.has(n.kind)) return true;
|
|
4145
|
-
const need = LABEL_MIN_H[n.kind];
|
|
4146
|
-
if (need === undefined) return false;
|
|
4147
|
-
// 描画で使う高さを見る。 `posH` が効くのは `posX` と `posY` が揃った node だけ
|
|
4148
|
-
// (`layout/nodes.ts`)。 揃っていない node の `posH` を見ると、 描画では使われない値で
|
|
4149
|
-
// 判定することになる。
|
|
4150
|
-
const h = (n.posX !== undefined && n.posY !== undefined ? n.posH : undefined) ?? n.h;
|
|
4151
|
-
// 高さを書いていない名札は描画側の既定 (`NODE_SIZE`、 4 種とも 170 以上) で描かれるので
|
|
4152
|
-
// 収まる。 名札は必ず高さを持つため通常ここには来ない。
|
|
4153
|
-
return h !== undefined && h < need;
|
|
4154
|
-
});
|
|
4155
|
-
if (!収まらない) continue;
|
|
4156
|
-
for (const n of pair) {
|
|
4157
|
-
if (LABEL_NEVER_FITS.has(n.kind) || LABEL_MIN_H[n.kind] !== undefined) n.kind = "card";
|
|
4158
|
-
}
|
|
4159
|
-
}
|
|
4160
|
-
}
|
|
4161
4005
|
|
|
4162
4006
|
/**
|
|
4163
4007
|
* `(from, to)` の一致では取れない preset について、 edge と DSL の行の対応を埋める。
|
|
@@ -4470,44 +4314,78 @@ function compileGantt(doc: DslDocument): CdlDiagram {
|
|
|
4470
4314
|
* 矢印は継承や保有を表す (`extends` / `aggregates` 等を書き手が説明に書く)。
|
|
4471
4315
|
*/
|
|
4472
4316
|
function compileClass(doc: DslDocument): CdlDiagram {
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4317
|
+
/*
|
|
4318
|
+
* **組み立て器 (`classDiagram`) に渡す** (#1466)。
|
|
4319
|
+
*
|
|
4320
|
+
* 以前は箱と矢印を直に組んでいたため、行頭の印・端の塗り・印が付く側・段の配置が
|
|
4321
|
+
* 1 つも出なかった = 画面の図と記法の図が別物になっていた。 組み立て器へ渡せば、
|
|
4322
|
+
* 意匠の決まり (`CLASS_RELATION_LOOK`) を 1 箇所から引ける。
|
|
4323
|
+
*/
|
|
4324
|
+
const b = classDiagram({ id: slugify(doc.title), topic: doc.title });
|
|
4477
4325
|
// 登場人物が 0 人なら枠も作らない。 先に作ると中身の無い枠が 1 つ残る (#1096)
|
|
4478
|
-
if (doc.actors.length === 0) return
|
|
4326
|
+
if (doc.actors.length === 0) return diagram(slugify(doc.title), { topic: doc.title }).build();
|
|
4479
4327
|
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4328
|
+
/*
|
|
4329
|
+
* 縦列は `lane:` の順、段は `stack:` で決まる (#1466)。
|
|
4330
|
+
*
|
|
4331
|
+
* 書かない図は宣言した順に横 1 列 = 従来どおり。 1 つでも書けば格子に置く =
|
|
4332
|
+
* **箱の 1 つの辺には関係を 1 本まで** を守るには段が要る。
|
|
4333
|
+
*/
|
|
4334
|
+
const 列番号 = new Map<string, number>();
|
|
4335
|
+
for (const a of doc.actors) {
|
|
4336
|
+
if (a.lane === undefined) continue;
|
|
4337
|
+
if (!列番号.has(a.lane)) 列番号.set(a.lane, 列番号.size);
|
|
4338
|
+
}
|
|
4339
|
+
|
|
4340
|
+
for (const a of doc.actors) {
|
|
4341
|
+
/*
|
|
4342
|
+
* 行を持ち物と振る舞いに割る (#1466)。 **1 行ずつ括弧の有無で見る**。
|
|
4343
|
+
*
|
|
4344
|
+
* 区切りの行 (`───`) の位置では割らない = 振る舞いしか持たない箱は区切りを書けず、
|
|
4345
|
+
* 全部が持ち物に落ちる (実測 = `Auditable` の `audit()` が四角の印で出た)。
|
|
4346
|
+
* 区切りの行そのものは、組み立て器が群の間を空の行で作るので捨てる。
|
|
4347
|
+
*/
|
|
4348
|
+
const rows = (a.rows ?? []).filter((r) => !/^[─-]+$/.test(r.trim()));
|
|
4349
|
+
const 振る舞い = (r: string): boolean => r.includes("(");
|
|
4350
|
+
const attributes = rows.filter((r) => !振る舞い(r));
|
|
4351
|
+
const methods = rows.filter(振る舞い);
|
|
4352
|
+
b.class({
|
|
4353
|
+
id: slugify(a.name),
|
|
4493
4354
|
title: 箱の題(a),
|
|
4494
|
-
|
|
4355
|
+
...(attributes.length > 0 ? { attributes: [...attributes] } : {}),
|
|
4356
|
+
...(methods.length > 0 ? { methods: [...methods] } : {}),
|
|
4357
|
+
...(a.eyebrow ? { stereotype: a.eyebrow } : {}),
|
|
4358
|
+
...(a.lane !== undefined ? { col: 列番号.get(a.lane) ?? 0 } : {}),
|
|
4359
|
+
...(a.stack !== undefined ? { row: a.stack } : {}),
|
|
4495
4360
|
});
|
|
4496
|
-
}
|
|
4361
|
+
}
|
|
4497
4362
|
|
|
4498
|
-
for (const
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
|
|
4504
|
-
...(
|
|
4505
|
-
...(
|
|
4506
|
-
...(
|
|
4363
|
+
for (const s2 of doc.flow) {
|
|
4364
|
+
b.relation({
|
|
4365
|
+
from: slugify(s2.from),
|
|
4366
|
+
to: slugify(s2.to),
|
|
4367
|
+
// 種類を書かない矢印は「使う」 扱い = 端が開いた矢になり、線と印の組が最も素直
|
|
4368
|
+
type: s2.relation ?? "uses",
|
|
4369
|
+
...(s2.label ? { label: s2.label } : {}),
|
|
4370
|
+
...(s2.sub ? { cardinality: s2.sub } : {}),
|
|
4371
|
+
...(s2.tone ? { tone: s2.tone } : {}),
|
|
4372
|
+
...(s2.style ? { style: s2.style } : {}),
|
|
4373
|
+
...(s2.head ? { head: s2.head } : {}),
|
|
4374
|
+
...(s2.headFill ? { headFill: s2.headFill } : {}),
|
|
4375
|
+
...(s2.tailHead ? { tailHead: s2.tailHead } : {}),
|
|
4507
4376
|
});
|
|
4508
4377
|
}
|
|
4509
4378
|
|
|
4510
|
-
|
|
4379
|
+
const built = b.build();
|
|
4380
|
+
/*
|
|
4381
|
+
* 記法が段を書いた図では、組み立て器が作る 1 つの段を捨てる (#1466)。
|
|
4382
|
+
*
|
|
4383
|
+
* 段の注入 (`injectPhasesFallback`) は「段が 1 つも無い」 図にだけ効く。 組み立て器へ
|
|
4384
|
+
* 渡すようにしたことで自動の段が 1 つ付き、書いた段が届かなくなった (実測 = 6 段書いた
|
|
4385
|
+
* 図が 1 段で出た)。
|
|
4386
|
+
*/
|
|
4387
|
+
const 書いた段がある = (doc.animate?.phases.length ?? 0) > 0;
|
|
4388
|
+
return 書いた段がある ? { ...built, phases: [] } : built;
|
|
4511
4389
|
}
|
|
4512
4390
|
|
|
4513
4391
|
/**
|
|
@@ -4591,8 +4469,16 @@ function 参照する名前(value: number | string | null): string | null {
|
|
|
4591
4469
|
*/
|
|
4592
4470
|
function compileValueChart(
|
|
4593
4471
|
doc: DslDocument,
|
|
4594
|
-
型: "pie" | "bar" | "line",
|
|
4595
|
-
kind:
|
|
4472
|
+
型: "pie" | "bar" | "line" | "gauge" | "radial" | "stat" | "waffle" | "stacked",
|
|
4473
|
+
kind:
|
|
4474
|
+
| "chart-pie"
|
|
4475
|
+
| "chart-bar"
|
|
4476
|
+
| "chart-line"
|
|
4477
|
+
| "chart-gauge"
|
|
4478
|
+
| "chart-radial"
|
|
4479
|
+
| "chart-stat"
|
|
4480
|
+
| "chart-waffle"
|
|
4481
|
+
| "chart-stacked-bar",
|
|
4596
4482
|
onNotice?: (notice: CompileNotice) => void,
|
|
4597
4483
|
): CdlDiagram {
|
|
4598
4484
|
const b = diagram(slugify(doc.title), { topic: doc.title });
|
|
@@ -4600,11 +4486,19 @@ function compileValueChart(
|
|
|
4600
4486
|
// **高さは型で違い、 格子に載せる**。 描画側 (`cdl` の `chart()` preset) は `pie` を 320、
|
|
4601
4487
|
// 棒と折れ線を 360 とした上で **16 の倍数へ切り上げる** (360 は 16 で割り切れないので 368)。
|
|
4602
4488
|
// 切り上げないと下端が格子から外れ、 全図で位置の警告が出る (review 指摘)
|
|
4603
|
-
|
|
4489
|
+
// 半円と弧は縦を使わないので円と同じ 320。 描画側 (`cdl` の `chart()` preset) が
|
|
4490
|
+
// `pie` / `gauge` / `radial` を 320、棒と折れ線を 360 とし、16 の倍数へ切り上げる
|
|
4491
|
+
// 縦に余白が要らない型。 描画側 (`cdl` の `chart()` preset) と揃える。
|
|
4492
|
+
// 半円 / 弧 / 割合の印 は縦を使わず、値 1 つを大きく示す図も縦に伸びない
|
|
4493
|
+
const 低い型 =
|
|
4494
|
+
型 === "pie" || 型 === "gauge" || 型 === "radial" || 型 === "waffle" || 型 === "stat";
|
|
4495
|
+
const CHART_H = 低い型 ? 320 : 368;
|
|
4604
4496
|
b.lane("chart", { width: CHART_W + 64 });
|
|
4605
4497
|
|
|
4606
4498
|
const data: NonNullable<CdlDiagram["nodes"][number]["chartData"]> = [];
|
|
4607
4499
|
const 読めない: string[] = [];
|
|
4500
|
+
const 前が読めない: string[] = [];
|
|
4501
|
+
let 前が読めない行 = 0;
|
|
4608
4502
|
const 未宣言: string[] = [];
|
|
4609
4503
|
let 未宣言行 = 0;
|
|
4610
4504
|
const 参照できる = 数の欄から参照できる名前(doc);
|
|
@@ -4631,16 +4525,37 @@ function compileValueChart(
|
|
|
4631
4525
|
continue;
|
|
4632
4526
|
}
|
|
4633
4527
|
// 色はそのまま渡す。 箱が 1 つになっても、 書いた色が消えないようにする
|
|
4634
|
-
|
|
4528
|
+
// 前の時点の値 (#1450)。 読めない形は黙って捨てず知らせる = 書いたのに 2 本目の帯が
|
|
4529
|
+
// 出ない状態になり、手掛かりが残らない
|
|
4530
|
+
const 前 = a.previous === undefined ? null : parseChartValue(a.previous);
|
|
4531
|
+
if (a.previous !== undefined && 前 === null) {
|
|
4532
|
+
if (前が読めない.length === 0) 前が読めない行 = a.pos?.line ?? 0;
|
|
4533
|
+
前が読めない.push(a.name);
|
|
4534
|
+
}
|
|
4535
|
+
data.push({
|
|
4536
|
+
label: 箱の題(a),
|
|
4537
|
+
value,
|
|
4538
|
+
...(前 === null ? {} : { previous: 前 }),
|
|
4539
|
+
...(a.tone !== undefined ? { tone: a.tone } : {}),
|
|
4540
|
+
});
|
|
4635
4541
|
}
|
|
4636
4542
|
// 案内の言葉は型ごとに変える。 共通化した時に `pie` の「割合 / 円 / 45%」 が「値 / 図 / 45」 に
|
|
4637
4543
|
// 薄まり、 既存の案内が後退した (review 指摘)。 何を書けばよいかは型ごとに違う
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
|
|
4643
|
-
|
|
4544
|
+
// 何を書けばよいかは型ごとに違う。 まとめると「割合 / 円 / 45%」 が「値 / 図 / 45」 に薄まる
|
|
4545
|
+
//
|
|
4546
|
+
// **表で持つ**。 三項の連鎖にすると、型が増えるたびに深さが増え、最後の枝が
|
|
4547
|
+
// 「それ以外」 になるため型検査が新しい型の漏れを教えてくれない
|
|
4548
|
+
const 語の表: Record<typeof 型, { 量: string; 図: string; 例: string }> = {
|
|
4549
|
+
pie: { 量: "割合", 図: "円", 例: '"45%"' },
|
|
4550
|
+
bar: { 量: "値", 図: "棒", 例: '"420"' },
|
|
4551
|
+
line: { 量: "値", 図: "折れ線", 例: '"180"' },
|
|
4552
|
+
gauge: { 量: "値", 図: "半円", 例: '"680"' },
|
|
4553
|
+
radial: { 量: "値", 図: "弧", 例: '"72"' },
|
|
4554
|
+
stat: { 量: "値", 図: "大きな数字", 例: '"1200"' },
|
|
4555
|
+
waffle: { 量: "割合", 図: "100 個の印", 例: '"45%"' },
|
|
4556
|
+
stacked: { 量: "内訳の値", 図: "帯", 例: '"320"' },
|
|
4557
|
+
};
|
|
4558
|
+
const 語 = 語の表[型];
|
|
4644
4559
|
|
|
4645
4560
|
/**
|
|
4646
4561
|
* 利用者に伝える。 **`console.warn` だけにしない**。 エディタは受け取った notice を画面に
|
|
@@ -4669,6 +4584,15 @@ function compileValueChart(
|
|
|
4669
4584
|
未宣言行,
|
|
4670
4585
|
);
|
|
4671
4586
|
}
|
|
4587
|
+
if (前が読めない.length > 0) {
|
|
4588
|
+
伝える(
|
|
4589
|
+
"chart-value-unreadable",
|
|
4590
|
+
前が読めない[0]!,
|
|
4591
|
+
`type: ${型} で前の時点の値を読めない項目があります (前の値を使いません): ${前が読めない.join(", ")}。` +
|
|
4592
|
+
" `- 名前: { value: " + 語.例 + ", previous: " + 語.例 + " }` の形で書いてください",
|
|
4593
|
+
前が読めない行,
|
|
4594
|
+
);
|
|
4595
|
+
}
|
|
4672
4596
|
if (doc.flow.length > 0) {
|
|
4673
4597
|
伝える(
|
|
4674
4598
|
"chart-edge-dropped",
|
|
@@ -5393,6 +5317,7 @@ function compileC4(doc: DslDocument): CdlDiagram {
|
|
|
5393
5317
|
label: s.label,
|
|
5394
5318
|
...(s.sub ? { sub: s.sub } : {}),
|
|
5395
5319
|
...(s.side ? { side: s.side } : {}),
|
|
5320
|
+
|
|
5396
5321
|
...(s.tone ? { tone: s.tone } : {}),
|
|
5397
5322
|
...(s.style ? { style: s.style } : {}),
|
|
5398
5323
|
});
|
|
@@ -5475,11 +5400,101 @@ type 放射で描ける欄 =
|
|
|
5475
5400
|
*
|
|
5476
5401
|
* 名前は図の中で 1 つに決まる必要がある (`focus:` と `flow:` が名前で指す) 一方、題は
|
|
5477
5402
|
* 重なってよい。 同じ題の箱を並べる図と、題を持たない箱は、名前と切り離さないと書けない。
|
|
5403
|
+
*
|
|
5404
|
+
* **始まりと終わりの印は題を持たない** (#1466)。 塗った丸と輪で描くもので、名前を出す場所が
|
|
5405
|
+
* 無い。 名前は矢印の端として指すために要るので、名前をそのまま題にすると `begin` の字が
|
|
5406
|
+
* 丸の上に乗る (組み立て API 側の `.mark()` は題を空で作る)。 書いた題があればそれを使う。
|
|
5478
5407
|
*/
|
|
5408
|
+
const 題を持たない種類: ReadonlySet<string> = new Set(["mark-start", "mark-end"]);
|
|
5409
|
+
|
|
5479
5410
|
function 箱の題(a: DslActor): string {
|
|
5411
|
+
if (a.title === undefined && a.kind !== undefined && 題を持たない種類.has(a.kind)) return "";
|
|
5480
5412
|
return a.title ?? a.name;
|
|
5481
5413
|
}
|
|
5482
5414
|
|
|
5415
|
+
/**
|
|
5416
|
+
* 行を組み立て器が加工する図の種類 (#1466)。
|
|
5417
|
+
*
|
|
5418
|
+
* ここに載る種類では、記法に書いた行をそのまま箱へ載せ直さない = 組み立て器が行頭の印に
|
|
5419
|
+
* 合わせて字を落としているため。
|
|
5420
|
+
*/
|
|
5421
|
+
function 行を組み立て器が持つ(type: DslDocument["type"]): boolean {
|
|
5422
|
+
return type === "class";
|
|
5423
|
+
}
|
|
5424
|
+
|
|
5425
|
+
/**
|
|
5426
|
+
* 書いた語を行頭の印に読み替える (#1466)。
|
|
5427
|
+
*
|
|
5428
|
+
* **軸の意味は図の種類が決める**。 印そのものは 形 (四角 / 山形) × 塗り (塗る / 中空) の
|
|
5429
|
+
* 2 軸で共通だが、その軸が何を指すかは種類ごとに違う。
|
|
5430
|
+
*
|
|
5431
|
+
* | 種類 | 山形 | 塗り |
|
|
5432
|
+
* |---|---|---|
|
|
5433
|
+
* | `er` | 外を指す列 (`fk`) | 空にできない (`opt` を書かない) |
|
|
5434
|
+
* | `state` | 出入りの瞬間 (`entry` / `exit`) | 続く・入る側 (`entry` / `do`) |
|
|
5435
|
+
*
|
|
5436
|
+
* ER の `pk` は印の 2 軸とは別の段 (名前の下線) に載るので、`fk` と重ねて書ける。
|
|
5437
|
+
*
|
|
5438
|
+
* 語を 1 つも知らない図の種類では `null` を返す = 印を付けない。
|
|
5439
|
+
*/
|
|
5440
|
+
/**
|
|
5441
|
+
* 行と印を組む (#1466)。 群の分け方は図の種類が決める。
|
|
5442
|
+
*
|
|
5443
|
+
* ER は **鍵の群を上にまとめ、間を空の行 1 つで開ける** (組み立て器 `er()` と同じ形)。
|
|
5444
|
+
* 記法に空の行を書かせないのは、`rows` が空の要素を捨てるため = 書いても消える。
|
|
5445
|
+
* 状態遷移は群を分けないので、書いた並びのまま。
|
|
5446
|
+
*/
|
|
5447
|
+
function 行と印を組む(
|
|
5448
|
+
type: DslDocument["type"],
|
|
5449
|
+
rows: readonly string[],
|
|
5450
|
+
marks: readonly string[],
|
|
5451
|
+
): { rows: string[]; rowMarks: (RowMark | null)[] } | null {
|
|
5452
|
+
const 印 = 行頭の印にする(type, marks);
|
|
5453
|
+
if (印 === null) return null;
|
|
5454
|
+
if (type !== "er") {
|
|
5455
|
+
return { rows: [...rows], rowMarks: rows.map((_, i) => 印[i] ?? null) };
|
|
5456
|
+
}
|
|
5457
|
+
const 鍵: number[] = [];
|
|
5458
|
+
const 値: number[] = [];
|
|
5459
|
+
rows.forEach((_, i) => ((印[i]?.underline === true ? 鍵 : 値).push(i)));
|
|
5460
|
+
const 並び = 鍵.length > 0 && 値.length > 0 ? [...鍵, -1, ...値] : [...鍵, ...値];
|
|
5461
|
+
return {
|
|
5462
|
+
rows: 並び.map((i) => (i < 0 ? "" : (rows[i] ?? ""))),
|
|
5463
|
+
rowMarks: 並び.map((i) => (i < 0 ? null : (印[i] ?? null))),
|
|
5464
|
+
};
|
|
5465
|
+
}
|
|
5466
|
+
|
|
5467
|
+
function 行頭の印にする(
|
|
5468
|
+
type: DslDocument["type"],
|
|
5469
|
+
marks: readonly string[],
|
|
5470
|
+
): (RowMark | null)[] | null {
|
|
5471
|
+
if (type === "er") {
|
|
5472
|
+
return marks.map((m) => {
|
|
5473
|
+
const 語 = m.trim().split(/\s+/).filter(Boolean);
|
|
5474
|
+
/*
|
|
5475
|
+
* **語を書かない行は「ただの値」**。 印を付けない行にはしない (#1466)。
|
|
5476
|
+
*
|
|
5477
|
+
* ER の印は 2 軸とも既定を持つ = 四角 (外を指さない) で塗る (空にできない)。
|
|
5478
|
+
* 印なしにすると、書かなかった列だけ行頭が空いて群の間と見分けが付かなくなる。
|
|
5479
|
+
*/
|
|
5480
|
+
return {
|
|
5481
|
+
shape: 語.includes("fk") ? ("chevron" as const) : ("square" as const),
|
|
5482
|
+
filled: !語.includes("opt"),
|
|
5483
|
+
...(語.includes("pk") ? { underline: true } : {}),
|
|
5484
|
+
};
|
|
5485
|
+
});
|
|
5486
|
+
}
|
|
5487
|
+
if (type === "state") {
|
|
5488
|
+
return marks.map((m) => {
|
|
5489
|
+
const 語 = m.trim();
|
|
5490
|
+
if (語 === "") return null;
|
|
5491
|
+
const 表 = FSM_ACTION_MARK as Record<string, RowMark>;
|
|
5492
|
+
return 表[語] ?? null;
|
|
5493
|
+
});
|
|
5494
|
+
}
|
|
5495
|
+
return null;
|
|
5496
|
+
}
|
|
5497
|
+
|
|
5483
5498
|
/** 放射では描けない欄。 書かれていたら伝える */
|
|
5484
5499
|
type 放射で描けない欄 =
|
|
5485
5500
|
| "kind"
|
|
@@ -5489,10 +5504,13 @@ type 放射で描けない欄 =
|
|
|
5489
5504
|
| "owner"
|
|
5490
5505
|
| "end"
|
|
5491
5506
|
| "rows"
|
|
5507
|
+
| "marks"
|
|
5492
5508
|
| "lane"
|
|
5493
5509
|
| "stack"
|
|
5494
5510
|
| "initial"
|
|
5495
5511
|
| "final"
|
|
5512
|
+
// 前の時点の値 (#1450)。 放射の枝は 1 時点しか描かない = 2 本目の帯に当たるものが無い
|
|
5513
|
+
| "previous"
|
|
5496
5514
|
| "colorHex"
|
|
5497
5515
|
| "stateOverride"
|
|
5498
5516
|
| "posX"
|
|
@@ -5552,10 +5570,12 @@ const 放射で描けない欄の名前: Record<放射で描けない欄, string
|
|
|
5552
5570
|
owner: "担当 (工程の並びの欄)",
|
|
5553
5571
|
end: "終わる時期 (工程の並びの欄)",
|
|
5554
5572
|
rows: "行",
|
|
5573
|
+
marks: "印",
|
|
5555
5574
|
lane: "枠の指定",
|
|
5556
5575
|
stack: "積む順",
|
|
5557
5576
|
initial: "始まり / 終わり の印",
|
|
5558
5577
|
final: "始まり / 終わり の印",
|
|
5578
|
+
previous: "前の時点の値",
|
|
5559
5579
|
colorHex: "色番号",
|
|
5560
5580
|
stateOverride: "状態の上書き",
|
|
5561
5581
|
// 位置は「登場人物ごとの箱をどこに置くか」 の指定で、 箱が 1 つの図では置く先が無い
|
|
@@ -5853,54 +5873,20 @@ function applyV05Extensions(
|
|
|
5853
5873
|
// seq-like の非 animate 経路は実 node id を CDL preset 側 slugify (`_` → `-` 置換 + 全角正規化) で
|
|
5854
5874
|
// 生成する。 dragon slugify (`_` / 全角 保持) で `{slug}-header` を決め打つと、 actor `A_B` の
|
|
5855
5875
|
// primaryNodeId `a_b-header` が実 node `a-b-header` と食い違い、 inline option (subtitle / eyebrow /
|
|
5856
|
-
// value / rows) が drop する (#881、 #873 / #877 と同根の dragon⇔CDL slug 不一致)。
|
|
5857
|
-
// 両 slug 経路とも actor.name の生値なので (#877)、 actor 専用 lane を label 一致で引き当て、 その
|
|
5858
|
-
// lane 内の `-header` node を権威 primary として回収する。 slug 決め打ちを廃して実装差を構造的に吸収。
|
|
5876
|
+
// value / rows) が drop する (#881、 #873 / #877 と同根の dragon⇔CDL slug 不一致)。
|
|
5859
5877
|
//
|
|
5860
|
-
//
|
|
5861
|
-
//
|
|
5862
|
-
//
|
|
5863
|
-
const isSeqLike = doc.type === "sequence" || doc.type === "solidity";
|
|
5878
|
+
// **順序図系はここを通らない** (#1466)。 `sequence` / `solidity` は 1 枚の板になり、面ごとの
|
|
5879
|
+
// 箱も縦列も作らなくなった = 書いた欄を写す相手が無い。 面に書いた内容が効かないことは
|
|
5880
|
+
// `reportActorKindNotHonored` が伝える。
|
|
5864
5881
|
// actor inline option → node merge
|
|
5865
5882
|
for (const a of doc.actors) {
|
|
5866
5883
|
const dragonSlug = slugify(a.name);
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
const ownedLaneIds = new Set(
|
|
5870
|
-
diagram.lanes.filter((l) => l.label === a.name).map((l) => l.id),
|
|
5871
|
-
);
|
|
5872
|
-
// lane.label で actor 専用 lane を引けた場合はその lane の header node を回収する。 引けない
|
|
5873
|
-
// (label 未設定等の) preset は従来どおり dragon slug の `{slug}-header` 決め打ちに fallback する。
|
|
5874
|
-
//
|
|
5875
|
-
// header node id は `{laneId}-header` の構造。 `endsWith("-header")` で判定すると step box
|
|
5876
|
-
// `s{idx}-{laneId}` が actor 名末尾 "Header" (slug `...-header`) で誤マッチし、 option が invisible
|
|
5877
|
-
// な step anchor にも copy される (cc-codex #883 MAJOR)。 lane id との構造 exact 一致で header だけを
|
|
5878
|
-
// 引くことで step box / spacer / footer を排除する。
|
|
5879
|
-
primaryNodes =
|
|
5880
|
-
ownedLaneIds.size > 0
|
|
5881
|
-
? diagram.nodes.filter((n) => ownedLaneIds.has(n.lane) && n.id === `${n.lane}-header`)
|
|
5882
|
-
: diagram.nodes.filter((n) => n.id === `${dragonSlug}-header`);
|
|
5883
|
-
} else {
|
|
5884
|
-
// 非 seq preset は 1 actor = 1 node (id = dragon slug) で node id と dragon slug が一致する。
|
|
5885
|
-
primaryNodes = diagram.nodes.filter((n) => n.id === dragonSlug);
|
|
5886
|
-
}
|
|
5884
|
+
// 1 actor = 1 node (id = dragon slug) で node id と dragon slug が一致する。
|
|
5885
|
+
const primaryNodes = diagram.nodes.filter((n) => n.id === dragonSlug);
|
|
5887
5886
|
for (const node of primaryNodes) {
|
|
5888
5887
|
// 識別に使う名前と、箱に出す題を分ける (#1381)。 preset が actor 名で
|
|
5889
5888
|
// node を作る経路 (sequence / solidity / swimlane / c4) もここで書き換える。
|
|
5890
|
-
if (a.title !== undefined)
|
|
5891
|
-
node.title = a.title;
|
|
5892
|
-
if (isSeqLike) {
|
|
5893
|
-
const footer = diagram.nodes.find((n) => n.id === `${node.lane}-footer`);
|
|
5894
|
-
if (footer) footer.title = a.title;
|
|
5895
|
-
|
|
5896
|
-
// 名前で決めた preset の幅を題に合わせる。 明示の大きさは後段が優先する。
|
|
5897
|
-
if (a.posW === undefined) {
|
|
5898
|
-
const titleW = Math.max(140, a.title.length * 22 + 52);
|
|
5899
|
-
node.w = titleW;
|
|
5900
|
-
if (footer) footer.w = titleW;
|
|
5901
|
-
}
|
|
5902
|
-
}
|
|
5903
|
-
}
|
|
5889
|
+
if (a.title !== undefined) node.title = a.title;
|
|
5904
5890
|
// `type: c4` では説明の先頭に段の目印 (`L1` / `L2` / `L3`) を書く。 目印は組み立てに
|
|
5905
5891
|
// 段を伝えるためのもので読む人に意味を持たず、 段の名前は枠のラベルが既に出している。
|
|
5906
5892
|
// ここで落とさないと、 組み立てが読み取った目印がそのまま箱の説明として出る (#1098)
|
|
@@ -5908,74 +5894,42 @@ function applyV05Extensions(
|
|
|
5908
5894
|
if (説明 !== undefined) node.subtitle = 説明;
|
|
5909
5895
|
if (a.eyebrow !== undefined) node.eyebrow = a.eyebrow;
|
|
5910
5896
|
if (a.value !== undefined) node.value = a.value;
|
|
5911
|
-
|
|
5897
|
+
/*
|
|
5898
|
+
* 行は **組み立て器が持つ図では上書きしない** (#1466)。
|
|
5899
|
+
*
|
|
5900
|
+
* クラス図は行頭の印を出すために、公開の記号 (`+` / `-`) と呼び出しの括弧を字から
|
|
5901
|
+
* 落とし、群の区切り (`───`) を空の行に置き換える。 書いた字をそのまま載せ直すと
|
|
5902
|
+
* その加工が消え、印と字が同じことを 2 度言う形に戻る (実測)。
|
|
5903
|
+
*/
|
|
5904
|
+
if (a.rows !== undefined && !行を組み立て器が持つ(doc.type)) node.rows = a.rows;
|
|
5905
|
+
// 行頭の印 (#1466)。 書いた語を図の種類ごとの意味で読み、群の分け方も種類が決める
|
|
5906
|
+
if (a.marks !== undefined && a.rows !== undefined) {
|
|
5907
|
+
const 組 = 行と印を組む(doc.type, a.rows, a.marks);
|
|
5908
|
+
if (組 !== null) {
|
|
5909
|
+
node.rows = 組.rows;
|
|
5910
|
+
node.rowMarks = 組.rowMarks;
|
|
5911
|
+
}
|
|
5912
|
+
}
|
|
5913
|
+
/*
|
|
5914
|
+
* **行を持たない状態でも欄を置く** (#1466)。
|
|
5915
|
+
*
|
|
5916
|
+
* 描き手は `rowMarks` の有無で新しい意匠かどうかを決める (`storage.tsx`)。 行の無い箱で
|
|
5917
|
+
* 欄ごと省くと、その箱だけ従来の意匠に落ちて呼び名が消える (組み立て API 側の
|
|
5918
|
+
* `stateMachine` は同じ理由で空の欄を置いている)。
|
|
5919
|
+
*/
|
|
5920
|
+
if (doc.type === "state" && node.kind === "storage" && node.rowMarks === undefined) {
|
|
5921
|
+
node.rows = a.rows ?? [];
|
|
5922
|
+
node.rowMarks = [];
|
|
5923
|
+
}
|
|
5912
5924
|
// 箱の大きさを反映する (#1259)。 **animation の有無に関係なく** = 動く図専用の
|
|
5913
5925
|
// 組み立てだけで渡すと、同じ記法でも静止図では指定が消える。
|
|
5914
5926
|
//
|
|
5915
|
-
// 順序図は名札 / 余白 / 足を組で作り、大きさが縦線の並びと結びつくため対象外。
|
|
5916
|
-
//
|
|
5917
5927
|
// **幅が図に出るかは縦列との大小で決まる**。 縦列に収まれば箱だけが変わり、
|
|
5918
5928
|
// 縦列より広ければ縦列ごと押し広げる (実測 = ステート図の 320 は縦列 370 に収まって
|
|
5919
5929
|
// 図が変わらないが、拡張ステート図の 280 に対し既定 640 は縦列 330 を押し広げた)。
|
|
5920
5930
|
// #1260 で 1 件だけ見て「見た目に出ない」 と判断し配線を外した = 同じ誤りを繰り返さない
|
|
5921
|
-
if (
|
|
5922
|
-
|
|
5923
|
-
if (a.posH !== undefined) node.h = a.posH;
|
|
5924
|
-
}
|
|
5925
|
-
// seq-like preset の header / footer は kind を card 固定で作る。 書いた kind を載せる
|
|
5926
|
-
// (#975)。 載せないと「書いたのに効かない項目」 が残り、 `rows` を書いた時は行が card に
|
|
5927
|
-
// 付いて画面から消える (#387、 cdl 側 Axis 67 rows-not-rendered が検知する)。
|
|
5928
|
-
//
|
|
5929
|
-
// 以前は「行を描く kind かつ rows あり」 に絞っていた。 header の見た目を kind ごとに
|
|
5930
|
-
// 変えると読み方が変わることを懸念したためだが、 **書いたとおりにならない方が読み手を
|
|
5931
|
-
// 惑わせる**。 見本 412 図で影響を受けるのは 1 図 (4 actor) だけと実測した。
|
|
5932
|
-
// 書いた種別を名札に載せる。 描画側に無い語は意味の近い形に読み替える (#975)。
|
|
5933
|
-
//
|
|
5934
|
-
// **書いた時だけ載せる** (#1058)。 `kind` は書かなくても既定の `actor` が入るため、
|
|
5935
|
-
// 値だけを見ると「書かなかった」 が「`actor` と書いた」 に化ける。 名札は小型の箱
|
|
5936
|
-
// (`h: 72`) で作られ、 描画側は `card` に小型用の分岐を持つが `actor` には無い。
|
|
5937
|
-
// 既定値で上書きすると小型の分岐が外れ、 名前の文字が箱の下端をはみ出す。
|
|
5938
|
-
//
|
|
5939
|
-
// 判定は `!== false` で行う。 記法の parse は書かなかった時に `false` を明示するので
|
|
5940
|
-
// これで区別できる。 `=== true` にすると、 **記法を通さず `DslActor` を直接組み立てて
|
|
5941
|
-
// `compileToCdl()` を呼ぶ経路** (公開 API) が既定の `undefined` で全て「書かなかった」
|
|
5942
|
-
// に倒れ、 書いた種類が消える (#975 の挙動が壊れる)。
|
|
5943
|
-
const drawn = isSeqLike && a.kindWritten !== false ? drawableKind(a.kind) : undefined;
|
|
5944
|
-
if (drawn !== undefined) {
|
|
5945
|
-
node.kind = drawn as typeof node.kind;
|
|
5946
|
-
// 下端の名札も同じ形にする。 上下で形が違うと、 同じ登場人物が別物に見える。
|
|
5947
|
-
// `rows` は上端にだけ載る (`primaryNodes` が上端しか拾わない) ので、 行は 2 度出ない。
|
|
5948
|
-
const footer = diagram.nodes.find((n) => n.id === `${node.lane}-footer`);
|
|
5949
|
-
if (footer) footer.kind = drawn as typeof node.kind;
|
|
5950
|
-
}
|
|
5951
|
-
// 行を書いた時は枠に収まる高さと幅にする。 header は w / h を固定値で作られ、 cdl 側は
|
|
5952
|
-
// `n.w` / `n.h` を明示した node の自動拡張を尊重する (著者指定を壊さない) 設計なので、
|
|
5953
|
-
// preset が置いた固定値がそのまま残る。
|
|
5954
|
-
//
|
|
5955
|
-
// 必要な寸法は cdl の SSOT (`requiredRowsHeight` / `requiredRowsWidth`) から引く。
|
|
5956
|
-
// 式を dragon 側に写すと、 描画を変えた時に片方だけ古くなる。
|
|
5957
|
-
if (isSeqLike && a.rows !== undefined && a.rows.length > 0 && rendersRows(a.kind)) {
|
|
5958
|
-
node.h = Math.max(node.h ?? 0, requiredRowsHeight(a.kind, a.rows.length) ?? 0);
|
|
5959
|
-
// **種別も渡す**。 省くと cdl 側は「どちらで描かれるか分からない」 として広い方を返す
|
|
5960
|
-
// ため、 実際に描かれる書式より名札が広くなる。 高さと同じく種別を渡す。
|
|
5961
|
-
//
|
|
5962
|
-
// 差が出るのは比例の書式の方が広くなる行 = 比例は 1 字の最大が字の大きさの 1.038 倍
|
|
5963
|
-
// (`W`) で 18 なら 18.7、 等幅の 26 は 1 字 15.6。 実測 = `W` を 10 個並べた行を持つ
|
|
5964
|
-
// `storage` の名札が、 種別なしで 268 / 種別あり で 256。
|
|
5965
|
-
node.w = Math.max(node.w ?? 0, requiredRowsWidth(a.rows, a.kind));
|
|
5966
|
-
}
|
|
5967
|
-
// 名札の大きさも書いたとおりにする (#975)。 縦線の位置は `位置:` の x が lane に効く
|
|
5968
|
-
// (実測) が、 大きさはどこにも載っていなかった。
|
|
5969
|
-
//
|
|
5970
|
-
// 高さは指定をそのまま使わず、 揃える側 (`alignSeqHeaderHeights`) に渡す候補にする。
|
|
5971
|
-
// 1 本だけ高い名札を作ると、 縦線の始まる位置がばらける。
|
|
5972
|
-
if (isSeqLike) {
|
|
5973
|
-
// 書いた値をそのまま使う。 大きい方を採ると、 縮める指定 (`大きさ: 80,60`) が効かない。
|
|
5974
|
-
if (a.posW !== undefined) node.w = a.posW;
|
|
5975
|
-
if (a.posH !== undefined) node.h = a.posH;
|
|
5976
|
-
const footer = diagram.nodes.find((n) => n.id === `${node.lane}-footer`);
|
|
5977
|
-
if (footer && a.posW !== undefined) footer.w = a.posW;
|
|
5978
|
-
}
|
|
5931
|
+
if (a.posW !== undefined) node.w = a.posW;
|
|
5932
|
+
if (a.posH !== undefined) node.h = a.posH;
|
|
5979
5933
|
// 箱の中に描く図形 (#1374)。 renderer が shape を描くのは dyn-* kind だけなので、
|
|
5980
5934
|
// shape 自身を SSOT にして対応する kind へ揃える。 card 等のまま shape だけ渡すと、指定を
|
|
5981
5935
|
// 保持しているのに画面には何も出ない。 paint 検査が入力を mutate しないよう object も写す。
|
|
@@ -5983,29 +5937,13 @@ function applyV05Extensions(
|
|
|
5983
5937
|
const dynamicKind = `dyn-${a.shape.kind}` as typeof node.kind;
|
|
5984
5938
|
node.kind = dynamicKind;
|
|
5985
5939
|
node.shape = { ...a.shape };
|
|
5986
|
-
if (isSeqLike) {
|
|
5987
|
-
const footer = diagram.nodes.find((n) => n.id === `${node.lane}-footer`);
|
|
5988
|
-
if (footer) {
|
|
5989
|
-
footer.kind = dynamicKind;
|
|
5990
|
-
footer.shape = { ...a.shape };
|
|
5991
|
-
}
|
|
5992
|
-
}
|
|
5993
|
-
}
|
|
5994
|
-
// その箱を出すかどうかの条件 (#1381)。 名札と足にも同じ条件を渡す = 片方だけ隠すと
|
|
5995
|
-
// 順序図で縦線の頭と足が食い違う
|
|
5996
|
-
if (a.visibleIf !== undefined) {
|
|
5997
|
-
node.visibleIf = a.visibleIf;
|
|
5998
|
-
if (isSeqLike) {
|
|
5999
|
-
const footer = diagram.nodes.find((n) => n.id === `${node.lane}-footer`);
|
|
6000
|
-
if (footer) footer.visibleIf = a.visibleIf;
|
|
6001
|
-
}
|
|
6002
5940
|
}
|
|
5941
|
+
// その箱を出すかどうかの条件 (#1381)
|
|
5942
|
+
if (a.visibleIf !== undefined) node.visibleIf = a.visibleIf;
|
|
6003
5943
|
/*
|
|
6004
5944
|
* 値に追随する 5 欄 (#1392)。
|
|
6005
5945
|
*
|
|
6006
|
-
*
|
|
6007
|
-
* 食い違うため揃えるが、こちらは見た目の大きさ / 濃さ / ずらしで、名札まで同じだけ
|
|
6008
|
-
* 動かすと縦線の頭が本体から離れる。 書いた箱にだけ効かせる。
|
|
5946
|
+
* 見た目の大きさ / 濃さ / ずらしを、書いた箱にだけ効かせる。
|
|
6009
5947
|
*/
|
|
6010
5948
|
if (a.wBind !== undefined) node.wBind = a.wBind;
|
|
6011
5949
|
if (a.hBind !== undefined) node.hBind = a.hBind;
|
|
@@ -6014,13 +5952,6 @@ function applyV05Extensions(
|
|
|
6014
5952
|
if (a.renderOffsetY !== undefined) node.renderOffsetY = a.renderOffsetY;
|
|
6015
5953
|
}
|
|
6016
5954
|
}
|
|
6017
|
-
// 名札の高さを揃える。 kind ごとに高さが変わると縦線の始まる位置がばらけ、 順序図の
|
|
6018
|
-
// 「同じ高さから下りる」 読み方が崩れる (実測 = 行を持つ名札だけ 134px 下にずれた)。
|
|
6019
|
-
alignSeqHeaderHeights(diagram, doc);
|
|
6020
|
-
// 揃えた後の高さで、 絵が箱に収まらない `shape-` を名札から外す (#1061 / #1067、 #1066 で
|
|
6021
|
-
// 4 種を対象から外した)。 揃えは高さを上げる方向にしか動かないので、 ここで見れば
|
|
6022
|
-
// 「行を書いた図では書いた種別が残る」 が成立する。
|
|
6023
|
-
dropUnfittableEndKinds(diagram, doc);
|
|
6024
5955
|
// v0.5+ animation phase 後段注入 (CAR-1657 fix、 元 dragon PR #413 report user)。
|
|
6025
5956
|
// preset (class / pie / c4 / mind / gantt) が doc.animate を無視して build するケースを補償。
|
|
6026
5957
|
// 既に preset が phase を生成済 (sequence / flow / swimlane / er / state / topology 経由 = compileGenericWithAnimate) なら skip。
|
|
@@ -6174,6 +6105,9 @@ function injectPhasesFallback(diagram: CdlDiagram, doc: DslDocument): void {
|
|
|
6174
6105
|
body: p.body ?? "",
|
|
6175
6106
|
activate: activateIds,
|
|
6176
6107
|
...(drawIds.length > 0 ? { draw: drawIds } : {}),
|
|
6108
|
+
// 描く速さ (#1441)。 **描く相手が決まった段にだけ載せる** = 語が効かない図種
|
|
6109
|
+
// (`drawIds` が空) で割合だけ渡すと、描画側が「draw が空なのに割合がある」 と知らせる
|
|
6110
|
+
...(drawIds.length > 0 && p.drawRatio !== undefined ? { drawRatio: p.drawRatio } : {}),
|
|
6177
6111
|
tweens: (p.tweens ?? []).map((t) => ({ stateId: t.state, from: t.from, to: t.to })),
|
|
6178
6112
|
sets: (p.sets ?? []).map((s) => ({ stateId: s.state, value: s.value })),
|
|
6179
6113
|
...(p.badge ? { badge: p.badge } : {}),
|
|
@@ -6184,14 +6118,23 @@ function injectPhasesFallback(diagram: CdlDiagram, doc: DslDocument): void {
|
|
|
6184
6118
|
function compileSequence(doc: DslDocument): CdlDiagram {
|
|
6185
6119
|
// v0.3 ... アニメーション 有無で経路を分岐。
|
|
6186
6120
|
// 有り = builder 直接経路で state / 複数 phase を注入。
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
|
|
6190
|
-
|
|
6121
|
+
/*
|
|
6122
|
+
* **段があっても組み立て器へ渡す** (#1466)。
|
|
6123
|
+
*
|
|
6124
|
+
* 順序図は 1 つの箱が図を丸ごと描く形になり、言づては箱の中の行になった。 段ごとに
|
|
6125
|
+
* 別経路で箱と縦線を組む形 (`compileSequenceWithAnimate`) では、その骨格が出ない。
|
|
6126
|
+
*/
|
|
6191
6127
|
const seqBuilder = sequence({
|
|
6192
6128
|
id: slugify(doc.title),
|
|
6193
6129
|
topic: doc.title,
|
|
6194
|
-
|
|
6130
|
+
/*
|
|
6131
|
+
* 見出しに出すのは **書いた題** (#1466)。 名前は矢印の端として指すためのもので、
|
|
6132
|
+
* `title:` を書いたらそちらを出す (`箱の題`)。 板でも他の図種と同じ規約にする。
|
|
6133
|
+
*/
|
|
6134
|
+
actors: doc.actors.map((a) =>
|
|
6135
|
+
a.subtitle ? { name: 箱の題(a), subtitle: a.subtitle } : 箱の題(a),
|
|
6136
|
+
),
|
|
6137
|
+
...(doc.bands && doc.bands.length > 0 ? { bands: doc.bands } : {}),
|
|
6195
6138
|
});
|
|
6196
6139
|
for (const s of doc.flow) {
|
|
6197
6140
|
seqBuilder.step({
|
|
@@ -6202,192 +6145,52 @@ function compileSequence(doc: DslDocument): CdlDiagram {
|
|
|
6202
6145
|
...(s.side ? { side: s.side } : {}),
|
|
6203
6146
|
...(s.tone ? { tone: s.tone } : {}),
|
|
6204
6147
|
...(s.style ? { style: s.style } : {}),
|
|
6148
|
+
...(s.msgKind ? { kind: s.msgKind } : {}),
|
|
6205
6149
|
});
|
|
6206
6150
|
}
|
|
6207
|
-
|
|
6208
|
-
|
|
6209
|
-
|
|
6210
|
-
|
|
6211
|
-
|
|
6212
|
-
|
|
6213
|
-
|
|
6214
|
-
|
|
6215
|
-
|
|
6216
|
-
|
|
6217
|
-
|
|
6218
|
-
|
|
6219
|
-
const
|
|
6220
|
-
|
|
6221
|
-
|
|
6222
|
-
|
|
6223
|
-
|
|
6224
|
-
|
|
6225
|
-
|
|
6226
|
-
|
|
6227
|
-
|
|
6228
|
-
|
|
6229
|
-
|
|
6230
|
-
|
|
6231
|
-
|
|
6232
|
-
if (a.posX !== undefined && a.posY !== undefined) {
|
|
6233
|
-
laneOpts.posX = a.posX;
|
|
6234
|
-
laneOpts.posY = a.posY;
|
|
6235
|
-
if (a.posW !== undefined) laneOpts.posW = a.posW;
|
|
6236
|
-
if (a.posH !== undefined) laneOpts.posH = a.posH;
|
|
6237
|
-
}
|
|
6238
|
-
b.lane(id, laneOpts);
|
|
6239
|
-
const headerId = `${id}-header`;
|
|
6240
|
-
// header/footer 幅を title 長に応じて auto-size (text-readability warning 解消)。
|
|
6241
|
-
// formula = 22px/char + 52px padding (visualValidate text-readability と完全一致)、 min 140 で従来 sample 互換維持。
|
|
6242
|
-
const actorW = Math.max(140, 箱の題(a).length * 22 + 52);
|
|
6243
|
-
b.node(headerId, { lane: id, stack: 0, kind: "card", title: 箱の題(a), w: actorW, h: 72 });
|
|
6244
|
-
headerNodeIds.push(headerId);
|
|
6245
|
-
const spacerId = `${id}-spacer`;
|
|
6246
|
-
b.node(spacerId, { lane: id, stack: 1, kind: "card", title: "", w: 2, h: 40 });
|
|
6247
|
-
});
|
|
6248
|
-
|
|
6249
|
-
// step boxes (sequence preset と同じ命名 ... `s${idx}-${laneId}` / `e${idx}-${from}-${to}`)
|
|
6250
|
-
// step ごとに DSL flow item に対応、 actor 名 → lane id の slugify を活用。
|
|
6251
|
-
const stepEdgeIds: string[] = [];
|
|
6252
|
-
doc.flow.forEach((s, idx) => {
|
|
6253
|
-
// 解決できない名前の矢印は落とす (#1209)。 以前は名前をそのまま lane id として使い、
|
|
6254
|
-
// 存在しない lane に箱を置いた図ができていた
|
|
6255
|
-
const fromLaneId = actorIds.get(s.from);
|
|
6256
|
-
const toLaneId = actorIds.get(s.to);
|
|
6257
|
-
if (fromLaneId === undefined || toLaneId === undefined) return;
|
|
6258
|
-
const stack = idx + 2;
|
|
6259
|
-
const fromBoxId = `s${idx}-${fromLaneId}`;
|
|
6260
|
-
const toBoxId = `s${idx}-${toLaneId}`;
|
|
6261
|
-
b.node(fromBoxId, { lane: fromLaneId, stack, kind: "card", title: "", w: 2, h: 2 });
|
|
6262
|
-
if (fromLaneId !== toLaneId) {
|
|
6263
|
-
b.node(toBoxId, { lane: toLaneId, stack, kind: "card", title: "", w: 2, h: 2 });
|
|
6264
|
-
}
|
|
6265
|
-
const edgeId = `e${idx}-${fromLaneId}-${toLaneId}`;
|
|
6266
|
-
b.edge(fromBoxId, fromLaneId === toLaneId ? fromBoxId : toBoxId, {
|
|
6267
|
-
id: edgeId,
|
|
6268
|
-
label: s.label,
|
|
6269
|
-
...(s.sub ? { sub: s.sub } : {}),
|
|
6270
|
-
...(s.side ? { side: s.side } : {}),
|
|
6271
|
-
...(s.tone ? { tone: s.tone } : {}),
|
|
6272
|
-
...(s.style ? { style: s.style } : {}),
|
|
6273
|
-
});
|
|
6274
|
-
stepEdgeIds.push(edgeId);
|
|
6275
|
-
});
|
|
6276
|
-
|
|
6277
|
-
// footer (sequence preset と整合)
|
|
6278
|
-
const footerStack = doc.flow.length + 2;
|
|
6279
|
-
doc.actors.forEach((a) => {
|
|
6280
|
-
const laneId = actorIds.get(a.name) ?? slugify(a.name);
|
|
6281
|
-
const footerId = `${laneId}-footer`;
|
|
6282
|
-
const actorW = Math.max(140, 箱の題(a).length * 22 + 52);
|
|
6283
|
-
// `role` を付ける (#1273)。 付けないと生命線の終わりが footer より 100 下まで伸びる
|
|
6284
|
-
// (実測 = 組立て API は `y2=848`、記法は `y2=948`)。 枠の大きさは同じなので
|
|
6285
|
-
// 描いた図の大きさの比較では捕まらない
|
|
6286
|
-
b.node(footerId, {
|
|
6287
|
-
lane: laneId,
|
|
6288
|
-
stack: footerStack,
|
|
6289
|
-
kind: "card",
|
|
6290
|
-
title: 箱の題(a),
|
|
6291
|
-
w: actorW,
|
|
6292
|
-
h: 72,
|
|
6293
|
-
role: "lifeline-footer",
|
|
6294
|
-
});
|
|
6295
|
-
});
|
|
6296
|
-
|
|
6297
|
-
// state を builder に登録
|
|
6298
|
-
for (const st of doc.animate?.states ?? []) {
|
|
6299
|
-
b.state(st.name, { initial: st.initial });
|
|
6300
|
-
}
|
|
6301
|
-
|
|
6302
|
-
// phase を順次注入 ... highlight / tween / set / badge / body 全反映
|
|
6303
|
-
for (const p of doc.animate?.phases ?? []) {
|
|
6304
|
-
b.phase(
|
|
6305
|
-
slugify(p.name) || p.name,
|
|
6306
|
-
{
|
|
6151
|
+
const built = seqBuilder.build();
|
|
6152
|
+
const 段 = doc.animate?.phases ?? [];
|
|
6153
|
+
if (段.length === 0) return built;
|
|
6154
|
+
/*
|
|
6155
|
+
* 書いた段を「今どの言づてか」 に読み替える (#1466)。
|
|
6156
|
+
*
|
|
6157
|
+
* 記法は段ごとに光らせる矢印を並べる (`focus: [A -> B, ...]`) が、言づては矢印ではなく
|
|
6158
|
+
* 箱の中の行になった = 光らせる先が無い。 代わりに **その段までに出た言づての番号** を
|
|
6159
|
+
* 状態へ書き、箱がそこまでを描く。
|
|
6160
|
+
*/
|
|
6161
|
+
const 番号 = new Map<string, number>();
|
|
6162
|
+
doc.flow.forEach((f, i) => 番号.set(`${slugify(f.from)} -> ${slugify(f.to)}`, i));
|
|
6163
|
+
const 状態名 = sequenceStepId();
|
|
6164
|
+
return {
|
|
6165
|
+
...built,
|
|
6166
|
+
states: [...built.states, { id: 状態名, initial: "0" }],
|
|
6167
|
+
phases: 段.map((p, i) => {
|
|
6168
|
+
const 番 = Math.max(
|
|
6169
|
+
0,
|
|
6170
|
+
...(p.highlight ?? []).map(
|
|
6171
|
+
(f) => 番号.get(f.split("->").map((x) => slugify(x.trim())).join(" -> ")) ?? -1,
|
|
6172
|
+
),
|
|
6173
|
+
);
|
|
6174
|
+
return {
|
|
6175
|
+
id: `p${i}`,
|
|
6307
6176
|
duration: p.durationMs,
|
|
6308
6177
|
title: p.name,
|
|
6309
6178
|
body: p.body ?? "",
|
|
6310
|
-
|
|
6311
|
-
|
|
6312
|
-
|
|
6313
|
-
|
|
6314
|
-
|
|
6315
|
-
|
|
6316
|
-
|
|
6317
|
-
|
|
6318
|
-
|
|
6319
|
-
|
|
6320
|
-
|
|
6321
|
-
|
|
6322
|
-
for (const s of p.sets ?? []) {
|
|
6323
|
-
pb.set(s.state, s.value);
|
|
6324
|
-
}
|
|
6325
|
-
// badge
|
|
6326
|
-
if (p.badge) {
|
|
6327
|
-
pb.badge(p.badge);
|
|
6328
|
-
}
|
|
6329
|
-
return pb;
|
|
6330
|
-
},
|
|
6331
|
-
);
|
|
6332
|
-
}
|
|
6333
|
-
|
|
6334
|
-
return b.build();
|
|
6179
|
+
activate: [slugify(doc.title)],
|
|
6180
|
+
...(p.badge ? { badge: p.badge } : {}),
|
|
6181
|
+
/*
|
|
6182
|
+
* **書いた状態の動きも一緒に運ぶ** (#1466)。 板の段は「今どの言づてか」 を状態に
|
|
6183
|
+
* 書くが、記法は同じ段に `遷移:` / `切替:` も書ける。 板の分だけを載せると、
|
|
6184
|
+
* 書いた動きが黙って落ちる (実測で `tweens` が 0 件になっていた)。
|
|
6185
|
+
*/
|
|
6186
|
+
sets: [...(p.sets ?? []).map((x) => ({ stateId: x.state, value: x.value })), { stateId: 状態名, value: 番 }],
|
|
6187
|
+
tweens: (p.tweens ?? []).map((t) => ({ stateId: t.state, from: t.from, to: t.to })),
|
|
6188
|
+
};
|
|
6189
|
+
}),
|
|
6190
|
+
};
|
|
6335
6191
|
}
|
|
6336
6192
|
|
|
6337
|
-
|
|
6338
|
-
* DSL の highlight item (actor 名 or "A→B" or "A-B" 等) を実 node/edge id に解決する。
|
|
6339
|
-
*/
|
|
6340
|
-
function resolveHighlight(
|
|
6341
|
-
phase: DslPhase,
|
|
6342
|
-
doc: DslDocument,
|
|
6343
|
-
actorIds: Map<string, string>,
|
|
6344
|
-
_stepEdgeIds: string[],
|
|
6345
|
-
): string[] {
|
|
6346
|
-
const out: string[] = [];
|
|
6347
|
-
const knownNames = new Set(actorIds.keys());
|
|
6348
|
-
for (const raw of phase.highlight ?? []) {
|
|
6349
|
-
const entry = parseFocusEntry(raw, knownNames);
|
|
6350
|
-
// 矢印つき → 該当 step edge を全部探して active
|
|
6351
|
-
if (entry.kind === "edge") {
|
|
6352
|
-
const fromLaneId = actorIds.get(entry.from) ?? slugify(entry.from);
|
|
6353
|
-
const toLaneId = actorIds.get(entry.to) ?? slugify(entry.to);
|
|
6354
|
-
// 該当 edge を flow から検索
|
|
6355
|
-
doc.flow.forEach((s, idx) => {
|
|
6356
|
-
const sFromId = actorIds.get(s.from) ?? slugify(s.from);
|
|
6357
|
-
const sToId = actorIds.get(s.to) ?? slugify(s.to);
|
|
6358
|
-
if (sFromId === fromLaneId && sToId === toLaneId) {
|
|
6359
|
-
out.push(`e${idx}-${fromLaneId}-${toLaneId}`);
|
|
6360
|
-
}
|
|
6361
|
-
});
|
|
6362
|
-
// 関連する step box も active 化
|
|
6363
|
-
const stackIdx = doc.flow.findIndex((s) => {
|
|
6364
|
-
const sFromId = actorIds.get(s.from) ?? slugify(s.from);
|
|
6365
|
-
const sToId = actorIds.get(s.to) ?? slugify(s.to);
|
|
6366
|
-
return sFromId === fromLaneId && sToId === toLaneId;
|
|
6367
|
-
});
|
|
6368
|
-
if (stackIdx >= 0) {
|
|
6369
|
-
out.push(`s${stackIdx}-${fromLaneId}`);
|
|
6370
|
-
if (fromLaneId !== toLaneId) out.push(`s${stackIdx}-${toLaneId}`);
|
|
6371
|
-
}
|
|
6372
|
-
continue;
|
|
6373
|
-
}
|
|
6374
|
-
// actor 名 → header + footer + 全 step box を active
|
|
6375
|
-
const laneId = actorIds.get(entry.name) ?? slugLookup(actorIds, entry.name);
|
|
6376
|
-
if (laneId) {
|
|
6377
|
-
out.push(`${laneId}-header`);
|
|
6378
|
-
out.push(`${laneId}-footer`);
|
|
6379
|
-
// この lane の全 step box
|
|
6380
|
-
doc.flow.forEach((s, idx) => {
|
|
6381
|
-
const sFromId = actorIds.get(s.from) ?? slugify(s.from);
|
|
6382
|
-
const sToId = actorIds.get(s.to) ?? slugify(s.to);
|
|
6383
|
-
if (sFromId === laneId || sToId === laneId) {
|
|
6384
|
-
out.push(`s${idx}-${laneId}`);
|
|
6385
|
-
}
|
|
6386
|
-
});
|
|
6387
|
-
}
|
|
6388
|
-
}
|
|
6389
|
-
return out;
|
|
6390
|
-
}
|
|
6193
|
+
|
|
6391
6194
|
|
|
6392
6195
|
/**
|
|
6393
6196
|
* 名前が見つからない時に、 slug の形でも探す。
|
|
@@ -6487,6 +6290,7 @@ function compileSwimlane(doc: DslDocument): CdlDiagram {
|
|
|
6487
6290
|
label: s.label,
|
|
6488
6291
|
...(s.sub ? { sub: s.sub } : {}),
|
|
6489
6292
|
...(s.side ? { side: s.side } : {}),
|
|
6293
|
+
|
|
6490
6294
|
...(s.tone ? { tone: s.tone } : {}),
|
|
6491
6295
|
...(s.style ? { style: s.style } : {}),
|
|
6492
6296
|
});
|
|
@@ -6570,9 +6374,35 @@ function 始まりと終わりの決め方(doc: DslDocument): {
|
|
|
6570
6374
|
};
|
|
6571
6375
|
}
|
|
6572
6376
|
|
|
6377
|
+
/** 状態の図で `stateMachine` preset が全ての箱に使う種類 */
|
|
6378
|
+
const 状態の図の既定の種類: NodeKind = "card";
|
|
6379
|
+
|
|
6380
|
+
/**
|
|
6381
|
+
* 状態の図で、既定と違う種類を書いた登場人物がいるか (#1450)。
|
|
6382
|
+
*
|
|
6383
|
+
* `stateMachine` preset は全ての状態を `card` で描くため、書いた `kind:` が黙って消える。
|
|
6384
|
+
* 始点終点の印 (`mark-start` / `mark-end`) を書けるようにするには、書いた形を効かせる必要がある。
|
|
6385
|
+
*
|
|
6386
|
+
* **「書いたか」 だけでは広すぎる**。 記法だけの種類 (`kind: state` 等) は `card` に読み替わり、
|
|
6387
|
+
* preset の出す形と同じになる = 書いても何も変わらないので経路を切り替える理由が無い。
|
|
6388
|
+
* 切り替えると既存の図の id と枠の作りが変わる (実測で golden 8 件が落ちた)。
|
|
6389
|
+
*
|
|
6390
|
+
* 読み替えた後の種類が既定と違う時だけ切り替える。 こうすると「書いたのに効かない」 は消え、
|
|
6391
|
+
* 「書いたが結果が同じ」 は従来の経路に留まる。
|
|
6392
|
+
*/
|
|
6393
|
+
function 既定と違う種類を書いた(doc: DslDocument): boolean {
|
|
6394
|
+
return doc.actors.some(
|
|
6395
|
+
(a) => a.kindWritten === true && 描ける種別(a.kind) !== 状態の図の既定の種類,
|
|
6396
|
+
);
|
|
6397
|
+
}
|
|
6398
|
+
|
|
6573
6399
|
function compileState(doc: DslDocument): CdlDiagram {
|
|
6574
6400
|
// v0.4 ... animation あり時 builder 直接経路 (各 state を lane で配置、 transition を edge)
|
|
6575
|
-
|
|
6401
|
+
//
|
|
6402
|
+
// **種類を書いた形は動きの有無に関わらず generic 経路へ** (#1450、 #1263 と同じ理由)。
|
|
6403
|
+
// 動く図だけで効かせると、同じ記法でも静止図では指定が黙って消える
|
|
6404
|
+
// (実測 = `kind: mark-start` を書いた箱が `card` になり知らせも出ない)
|
|
6405
|
+
if ((doc.animate && doc.animate.phases.length > 0) || 既定と違う種類を書いた(doc)) {
|
|
6576
6406
|
return compileGenericWithAnimate(doc, { kind: "state", laneWidth: 360 });
|
|
6577
6407
|
}
|
|
6578
6408
|
// stateMachine preset ... actors を state に、 流れ を transition に
|
|
@@ -6637,6 +6467,7 @@ function compileTopology(doc: DslDocument): CdlDiagram {
|
|
|
6637
6467
|
label: s.label,
|
|
6638
6468
|
...(s.sub ? { sub: s.sub } : {}),
|
|
6639
6469
|
...(s.side ? { side: s.side } : {}),
|
|
6470
|
+
|
|
6640
6471
|
...(s.tone ? { tone: s.tone } : {}),
|
|
6641
6472
|
...(s.style ? { style: s.style } : {}),
|
|
6642
6473
|
});
|
|
@@ -6689,10 +6520,20 @@ function 後ろへ戻る矢印か(
|
|
|
6689
6520
|
* 縦列を **箱を並べるための入れ物** として使う図種だけを許す。 順序図と solidity は
|
|
6690
6521
|
* 縦列がそのまま生命線として描かれる骨格なので許さない (#1248 の判断はこちらに当たる)。
|
|
6691
6522
|
*
|
|
6692
|
-
* `er`
|
|
6693
|
-
*
|
|
6523
|
+
* `er` は「1 縦列 1 箱」 が図の読み方そのもの (表が横に並ぶ) なので、2 つの箱を同じ縦列へ
|
|
6524
|
+
* 入れられる形にはしない。
|
|
6525
|
+
*
|
|
6526
|
+
* **クラス図と状態遷移図は外した** (#1466)。 設計が格子に置く形になり (クラス 3 列 3 段 /
|
|
6527
|
+
* 状態 2 列 5 段)、「箱の 1 つの辺には関係を 1 本まで」 を守るには 1 つの縦列に複数の箱が要る。
|
|
6528
|
+
* 「1 縦列 1 箱」 が読み方だった前提はここで崩れている。
|
|
6694
6529
|
*/
|
|
6695
|
-
const 縦列を選べる図種: ReadonlySet<
|
|
6530
|
+
const 縦列を選べる図種: ReadonlySet<PresetType> = new Set<PresetType>([
|
|
6531
|
+
"flow",
|
|
6532
|
+
"topology",
|
|
6533
|
+
"swimlane",
|
|
6534
|
+
"class",
|
|
6535
|
+
"state",
|
|
6536
|
+
]);
|
|
6696
6537
|
|
|
6697
6538
|
/**
|
|
6698
6539
|
* 書いた縦列に箱を置く形か (#1263)。
|
|
@@ -6703,7 +6544,7 @@ const 縦列を選べる図種: ReadonlySet<GenericKind> = new Set(["flow", "top
|
|
|
6703
6544
|
*
|
|
6704
6545
|
* 見本 (parts) は縦列を張替え先として使うため、この判定からは外す。
|
|
6705
6546
|
*/
|
|
6706
|
-
function 書いた縦列に置く(kind:
|
|
6547
|
+
function 書いた縦列に置く(kind: PresetType, doc: DslDocument): boolean {
|
|
6707
6548
|
if (!縦列を選べる図種.has(kind)) return false;
|
|
6708
6549
|
const 対象 = doc.actors.filter((a) => a.partId === undefined);
|
|
6709
6550
|
return 対象.length > 0 && 対象.every((a) => a.lane !== undefined);
|
|
@@ -6855,6 +6696,7 @@ function compileGenericWithAnimate(doc: DslDocument, opts: GenericOpts): CdlDiag
|
|
|
6855
6696
|
: {}),
|
|
6856
6697
|
...(s.sub ? { sub: s.sub } : {}),
|
|
6857
6698
|
...(s.side ? { side: s.side } : {}),
|
|
6699
|
+
|
|
6858
6700
|
...(s.tone ? { tone: s.tone } : {}),
|
|
6859
6701
|
...(s.style ? { style: s.style } : {}),
|
|
6860
6702
|
});
|