@cardenelabs/dragon 0.7.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/LICENSE +21 -0
- package/README.md +173 -0
- package/dist/index.cjs +4916 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1451 -0
- package/dist/index.d.ts +1451 -0
- package/dist/index.js +4870 -0
- package/dist/index.js.map +1 -0
- package/examples/quick-start.md +142 -0
- package/package.json +79 -0
- package/src/canvas-bounds.ts +52 -0
- package/src/color.ts +172 -0
- package/src/compile.ts +3739 -0
- package/src/focus.ts +46 -0
- package/src/index.ts +226 -0
- package/src/input-size.ts +154 -0
- package/src/json-parser.ts +434 -0
- package/src/keywords.ts +114 -0
- package/src/notation-lint.ts +304 -0
- package/src/parser.ts +354 -0
- package/src/relative-pos.ts +182 -0
- package/src/schema.ts +24 -0
- package/src/schemas/diagram.json +133 -0
- package/src/types.ts +294 -0
- package/src/v05/index.ts +9 -0
- package/src/v05/parser.ts +1678 -0
- package/src/write-position.ts +270 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 位置を他の要素からの相対で書くための解決。
|
|
3
|
+
*
|
|
4
|
+
* `位置: Web の右 200` のように、 座標の代わりに「誰の」「どちら側に」「どれだけ離して」 を書く。
|
|
5
|
+
* 書く人も LLM も座標を知らないので、 数値を当てさせない形を用意する。
|
|
6
|
+
*
|
|
7
|
+
* 解決結果は絶対座標 (`posX` / `posY`) で、 座標を直接書いた時と同じ経路を通る。
|
|
8
|
+
* 書き方が増えるだけで、 効き方は変わらない。
|
|
9
|
+
*
|
|
10
|
+
* 座標を決める場所が組み立て側 (`compile.ts`) と画面側 (playground の `overlay-dsl.ts`) の
|
|
11
|
+
* 2 つあるため、 解決の規則は本 file に 1 つだけ置いて両方から呼ぶ。 片方だけ直すと画面が
|
|
12
|
+
* 直らない事故を、 規則を共有することで構造的に防ぐ。
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/** 基準からどちら側に置くか。 */
|
|
16
|
+
export type RelativeDirection = "right" | "left" | "above" | "below";
|
|
17
|
+
|
|
18
|
+
/** 相対で書かれた位置の指定。 */
|
|
19
|
+
export type RelativePos = {
|
|
20
|
+
/** 基準にする相手の名前。 `actors:` に書かれた名前をそのまま持つ。 */
|
|
21
|
+
anchor: string;
|
|
22
|
+
dir: RelativeDirection;
|
|
23
|
+
/** 相手との間隔。 書かなかった時は `RELATIVE_GAP_DEFAULT`。 */
|
|
24
|
+
gap?: number;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/** 位置を決めるのに必要な、 基準の中心と大きさ。 */
|
|
28
|
+
export type AnchorBox = { cx: number; cy: number; w: number; h: number };
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 間隔を書かなかった時の既定値。
|
|
32
|
+
*
|
|
33
|
+
* 8 図種 × 4 向きで間隔を変えながら、 cdl が出す近すぎ系の指摘の数を数えて決めた。
|
|
34
|
+
* 100 で 17 件、 130 で 10 件、 140 で 4 件と減り、 160 で 2 件に落ちて以降は変わらない。
|
|
35
|
+
* 残る 2 件は間隔と無関係な指摘なので、 減らなくなる 160 を既定にする。
|
|
36
|
+
*
|
|
37
|
+
* 自動配置が空ける隙間も実測では縦 100 / 横 256-406 で、 160 はその間に収まる。
|
|
38
|
+
* 隣に置いたと読める近さと、 詰まって見えない広さの両方を満たす。
|
|
39
|
+
*/
|
|
40
|
+
export const RELATIVE_GAP_DEFAULT = 160;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 書かれた向きの語。 日本語と英語の両方を受け付ける。
|
|
44
|
+
*
|
|
45
|
+
* 項目名が日本語でも英語でもよい (`種類` / `kind`) のと揃える。
|
|
46
|
+
*/
|
|
47
|
+
const DIRECTION_WORDS: Readonly<Record<string, RelativeDirection>> = {
|
|
48
|
+
右: "right",
|
|
49
|
+
左: "left",
|
|
50
|
+
上: "above",
|
|
51
|
+
下: "below",
|
|
52
|
+
right: "right",
|
|
53
|
+
left: "left",
|
|
54
|
+
above: "above",
|
|
55
|
+
below: "below",
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* 間隔として受け付ける数の形。
|
|
60
|
+
*
|
|
61
|
+
* 負の数は含めない (向きが裏返るため)。 小数と指数表記は受け付ける。 整数だけに絞ると、
|
|
62
|
+
* 有効な数を書いたのに「書き方が読めません」 と返す (実測 = `1e2` / `.5` が弾かれた)。
|
|
63
|
+
*/
|
|
64
|
+
const GAP_NUM = String.raw`(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?`;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 日本語の形。 `Web の右` / `Web の右 200` / `Web の右200`。
|
|
68
|
+
*
|
|
69
|
+
* 相手の名前は控えめ (`.+?`) に取る。 名前自体が `の右` で終わる場合 (`Aの右 の左`) でも、
|
|
70
|
+
* 後戻りして末尾の向きを先に確定するため取り違えない。
|
|
71
|
+
*/
|
|
72
|
+
const RE_JA = new RegExp(String.raw`^(.+?)\s*の\s*(右|左|上|下)(?:\s*(${GAP_NUM}))?$`);
|
|
73
|
+
|
|
74
|
+
/** 英語の形。 `Web right` / `Web right 200`。 向きの前後は空白で区切る。 */
|
|
75
|
+
const RE_EN = new RegExp(String.raw`^(.+?)\s+(right|left|above|below)(?:\s+(${GAP_NUM}))?$`, "i");
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* `位置:` に書かれた値を相対指定として読む。 相対の形でなければ null。
|
|
79
|
+
*
|
|
80
|
+
* 座標の形 (`300,200`) は呼ぶ側が先に判定する。 ここは相対だけを見る。
|
|
81
|
+
*/
|
|
82
|
+
export function parseRelativePos(raw: string): RelativePos | null {
|
|
83
|
+
const s = raw.trim();
|
|
84
|
+
if (s === "") return null;
|
|
85
|
+
const m = s.match(RE_JA) ?? s.match(RE_EN);
|
|
86
|
+
if (!m) return null;
|
|
87
|
+
const anchor = m[1]!.trim();
|
|
88
|
+
if (anchor === "") return null;
|
|
89
|
+
const dir = DIRECTION_WORDS[m[2]!.toLowerCase()];
|
|
90
|
+
if (dir === undefined) return null;
|
|
91
|
+
const gapRaw = m[3];
|
|
92
|
+
if (gapRaw === undefined) return { anchor, dir };
|
|
93
|
+
const gap = Number(gapRaw);
|
|
94
|
+
// 数として読めない間隔は書かなかった扱いにする。 既定の間隔で置く方が、
|
|
95
|
+
// 図から消えるより書いた人が気付きやすい。
|
|
96
|
+
return Number.isFinite(gap) ? { anchor, dir, gap } : { anchor, dir };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* 相対指定を絶対座標に直す。
|
|
101
|
+
*
|
|
102
|
+
* `posX` / `posY` は箱の中心。 間隔は箱の縁と縁の間の距離として扱う。 中心間の距離にすると、
|
|
103
|
+
* 大きさの違う箱を並べた時に見た目の隙間が揃わない。
|
|
104
|
+
*/
|
|
105
|
+
export function resolveRelativePos(
|
|
106
|
+
rel: RelativePos,
|
|
107
|
+
anchor: AnchorBox,
|
|
108
|
+
target: { w: number; h: number },
|
|
109
|
+
): { posX: number; posY: number } {
|
|
110
|
+
// 負の間隔は向きを裏返す。 `Web の右 -1000` が Web の左に置かれ、 確かめる側も同じ値で
|
|
111
|
+
// 期待を作るので矛盾に気付けない (実測 = 右と書いて左に出た)。 間隔は 0 以上として扱う。
|
|
112
|
+
// 記法から来る値は parser が弾くが、 本関数は公開しているので入口で閉じる
|
|
113
|
+
const raw = rel.gap ?? RELATIVE_GAP_DEFAULT;
|
|
114
|
+
const gap = Number.isFinite(raw) && raw >= 0 ? raw : RELATIVE_GAP_DEFAULT;
|
|
115
|
+
const dx = anchor.w / 2 + gap + target.w / 2;
|
|
116
|
+
const dy = anchor.h / 2 + gap + target.h / 2;
|
|
117
|
+
switch (rel.dir) {
|
|
118
|
+
case "right":
|
|
119
|
+
return { posX: anchor.cx + dx, posY: anchor.cy };
|
|
120
|
+
case "left":
|
|
121
|
+
return { posX: anchor.cx - dx, posY: anchor.cy };
|
|
122
|
+
case "below":
|
|
123
|
+
return { posX: anchor.cx, posY: anchor.cy + dy };
|
|
124
|
+
case "above":
|
|
125
|
+
return { posX: anchor.cx, posY: anchor.cy - dy };
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* 相対指定を解く順番を決める。
|
|
131
|
+
*
|
|
132
|
+
* 基準にした相手がまた相対で書かれていることがある (`B は A の右`、 `C は B の右`)。
|
|
133
|
+
* 先に相手が決まっていないと座標を出せないので、 依存の浅い順に並べ替える。
|
|
134
|
+
*
|
|
135
|
+
* 輪になっている分 (`A は B の右`、 `B は A の右`) は解けないため、 順番からは外して
|
|
136
|
+
* 名前だけを返す。 呼ぶ側が誤りとして扱う。
|
|
137
|
+
*/
|
|
138
|
+
export function orderByDependency(
|
|
139
|
+
items: ReadonlyArray<{ name: string; rel?: RelativePos }>,
|
|
140
|
+
): { order: string[]; cyclic: string[] } {
|
|
141
|
+
const relOf = new Map<string, RelativePos>();
|
|
142
|
+
const known = new Set<string>();
|
|
143
|
+
for (const it of items) {
|
|
144
|
+
known.add(it.name);
|
|
145
|
+
if (it.rel) relOf.set(it.name, it.rel);
|
|
146
|
+
}
|
|
147
|
+
const order: string[] = [];
|
|
148
|
+
const done = new Set<string>();
|
|
149
|
+
const cyclic = new Set<string>();
|
|
150
|
+
|
|
151
|
+
// 再帰にすると基準の連鎖の長さだけ stack を積む。 連鎖の長さは書く人が決めるので
|
|
152
|
+
// 上限を置けない。 明示的な配列で辿る。
|
|
153
|
+
for (const it of items) {
|
|
154
|
+
if (done.has(it.name) || cyclic.has(it.name)) continue;
|
|
155
|
+
// path は [自分, 基準, 基準の基準, ...] の順に伸びる
|
|
156
|
+
const path: string[] = [];
|
|
157
|
+
const onPath = new Set<string>();
|
|
158
|
+
let cur: string | undefined = it.name;
|
|
159
|
+
while (cur !== undefined) {
|
|
160
|
+
if (done.has(cur) || cyclic.has(cur)) break;
|
|
161
|
+
if (onPath.has(cur)) {
|
|
162
|
+
// 輪を見つけた。 輪に含まれる分だけを外す。 輪に入る手前の分は解けるので残す
|
|
163
|
+
for (const n of path.slice(path.indexOf(cur))) cyclic.add(n);
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
path.push(cur);
|
|
167
|
+
onPath.add(cur);
|
|
168
|
+
const rel = relOf.get(cur);
|
|
169
|
+
// 相対で書かれていない、 または相手が居ない = ここで辿り終わり
|
|
170
|
+
if (rel === undefined || !known.has(rel.anchor)) break;
|
|
171
|
+
cur = rel.anchor;
|
|
172
|
+
}
|
|
173
|
+
// 奥 (基準側) から順に並べる。 基準が先に決まっていないと座標を出せない
|
|
174
|
+
for (let i = path.length - 1; i >= 0; i -= 1) {
|
|
175
|
+
const n = path[i]!;
|
|
176
|
+
if (cyclic.has(n) || done.has(n)) continue;
|
|
177
|
+
done.add(n);
|
|
178
|
+
order.push(n);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return { order, cyclic: [...cyclic] };
|
|
182
|
+
}
|
package/src/schema.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON Schema を TypeScript から使えるように export。
|
|
3
|
+
*
|
|
4
|
+
* 使い方 (LLM 呼出):
|
|
5
|
+
* import Anthropic from "@anthropic-ai/sdk";
|
|
6
|
+
* import { diagramJsonSchema, jsonToDiagram } from "@cardenelabs/dragon";
|
|
7
|
+
* const client = new Anthropic();
|
|
8
|
+
* const res = await client.messages.create({
|
|
9
|
+
* model: "claude-sonnet-5",
|
|
10
|
+
* max_tokens: 4096,
|
|
11
|
+
* tools: [{ name: "create_diagram", description: "...", input_schema: diagramJsonSchema }],
|
|
12
|
+
* messages: [{ role: "user", content: "..." }],
|
|
13
|
+
* });
|
|
14
|
+
* const diagram = jsonToDiagram(res.content[0].input);
|
|
15
|
+
*
|
|
16
|
+
* schema JSON は `packages/dragon/schemas/diagram.json` が SSOT、 本 file は import して再 export するだけ。
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import schemaJson from "./schemas/diagram.json" with { type: "json" };
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Dragon DSL JSON Schema (Draft 7)。 Anthropic / OpenAI の tool schema にそのまま注入可能。
|
|
23
|
+
*/
|
|
24
|
+
export const diagramJsonSchema = schemaJson;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://github.com/cardene777/dragon/schemas/diagram.json",
|
|
4
|
+
"title": "Dragon DSL Diagram",
|
|
5
|
+
"description": "LLM 向け Dragon DSL の JSON Schema。 Anthropic Claude tool use / OpenAI GPT structured output の schema field に注入して使う。 生成された JSON は jsonToDiagram() に渡して CdlDiagram に変換する。",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["title", "type", "actors", "flow"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"title": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"minLength": 1,
|
|
13
|
+
"description": "図の title (画面上部に表示される見出し)"
|
|
14
|
+
},
|
|
15
|
+
"type": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"enum": ["sequence", "flow", "swimlane", "er", "state", "topology", "solidity", "gantt", "class", "pie", "c4", "mind"],
|
|
18
|
+
"description": "preset type = 図の種類。 sequence (時系列 API 呼び出し) / flow (処理 flow) / swimlane (責務分担 flow) / er (DB schema) / state (状態遷移) / topology (network) / solidity (contract) / gantt (schedule) / class (UML class) / pie (円グラフ) / c4 (architecture) / mind (mind map)"
|
|
19
|
+
},
|
|
20
|
+
"actors": {
|
|
21
|
+
"type": "array",
|
|
22
|
+
"minItems": 1,
|
|
23
|
+
"description": "登場人物 (node) の配列。 文字列で name のみ、 または object で kind / subtitle 等を指定。",
|
|
24
|
+
"items": {
|
|
25
|
+
"oneOf": [
|
|
26
|
+
{
|
|
27
|
+
"type": "string",
|
|
28
|
+
"description": "actor 名のみ (kind は default 'actor')"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"type": "object",
|
|
32
|
+
"required": ["name"],
|
|
33
|
+
"additionalProperties": false,
|
|
34
|
+
"properties": {
|
|
35
|
+
"name": {"type": "string", "minLength": 1, "description": "actor 名 (flow の from / to で参照される)"},
|
|
36
|
+
"kind": {
|
|
37
|
+
"type": "string",
|
|
38
|
+
"description": "node kind。 shape-* を指定して 49 shape 中から選ぶ。 例: 'shape-wallet' / 'shape-smart-contract' / 'shape-cloud' / 'shape-file' / 'actor' (default) / 'function' / 'storage' / 'event' 等"
|
|
39
|
+
},
|
|
40
|
+
"subtitle": {"type": "string", "description": "node 下部の補足テキスト"},
|
|
41
|
+
"eyebrow": {"type": "string", "description": "node 上部の分類ラベル (accent 色)"},
|
|
42
|
+
"value": {"type": "string", "description": "actor 数値表示 (kind: actor 用)"},
|
|
43
|
+
"rows": {"type": "array", "items": {"type": "string"}, "description": "storage 内の column 列 (kind: storage 用)"},
|
|
44
|
+
"lane": {"type": "string", "description": "swimlane / topology で属する lane id"},
|
|
45
|
+
"stack": {"type": "integer", "minimum": 0, "description": "lane 内の縦位置 (0-based)"},
|
|
46
|
+
"initial": {"type": "boolean", "description": "state 開始点 (kind: state 用)"},
|
|
47
|
+
"final": {"type": "boolean", "description": "state 終了点 (kind: state 用)"}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"flow": {
|
|
54
|
+
"type": "array",
|
|
55
|
+
"description": "step の配列 (矢印で actor 間を繋ぐ)。 sequence では順序が意味を持つ。",
|
|
56
|
+
"items": {
|
|
57
|
+
"type": "object",
|
|
58
|
+
"required": ["from", "to", "label"],
|
|
59
|
+
"additionalProperties": false,
|
|
60
|
+
"properties": {
|
|
61
|
+
"from": {"type": "string", "description": "起点 actor 名"},
|
|
62
|
+
"to": {"type": "string", "description": "終点 actor 名"},
|
|
63
|
+
"label": {"type": "string", "description": "矢印上のラベル (関数呼出し名 / メッセージ等)"},
|
|
64
|
+
"sub": {"type": "string", "description": "ラベル下の補足"},
|
|
65
|
+
"tone": {"type": "string", "enum": ["accent", "success", "warning", "danger"], "description": "矢印の色調"},
|
|
66
|
+
"style": {"type": "string", "enum": ["solid", "dashed", "dotted", "dotted-flow"], "description": "矢印の線種"},
|
|
67
|
+
"guard": {"type": "string", "description": "state 遷移の条件 (kind: state 用)"},
|
|
68
|
+
"cardinality": {"type": "string", "description": "ER の多重度 (1..N 等、 kind: er 用)"},
|
|
69
|
+
"labelOffsetX": {"type": "number", "description": "ラベル位置 x 調整"},
|
|
70
|
+
"labelOffsetY": {"type": "number", "description": "ラベル位置 y 調整"}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
"animation": {
|
|
75
|
+
"type": "array",
|
|
76
|
+
"description": "アニメーション phase 配列。 各 phase で focus 対象を highlight する。 optional。",
|
|
77
|
+
"items": {
|
|
78
|
+
"type": "object",
|
|
79
|
+
"required": ["step"],
|
|
80
|
+
"additionalProperties": false,
|
|
81
|
+
"properties": {
|
|
82
|
+
"step": {"type": "string", "minLength": 1, "description": "phase 名"},
|
|
83
|
+
"duration": {"type": "number", "minimum": 0.1, "description": "phase 時間 (秒、 default 1.4)"},
|
|
84
|
+
"focus": {"type": "array", "items": {"type": "string"}, "description": "この phase で active 化する actor 名 or edge 'A -> B' の配列"},
|
|
85
|
+
"body": {"type": "string", "description": "phase 説明文"},
|
|
86
|
+
"badge": {"type": "string", "description": "phase 中に表示する badge label"}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
"viewport": {
|
|
91
|
+
"type": "object",
|
|
92
|
+
"description": "全体 canvas サイズ + gap の調整。 optional。",
|
|
93
|
+
"additionalProperties": false,
|
|
94
|
+
"properties": {
|
|
95
|
+
"width": {"type": "number", "minimum": 100},
|
|
96
|
+
"height": {"type": "number", "minimum": 100},
|
|
97
|
+
"laneWidth": {"type": "number", "minimum": 100},
|
|
98
|
+
"gap": {"type": "number", "minimum": 0},
|
|
99
|
+
"laneGap": {"type": "number", "minimum": 0},
|
|
100
|
+
"nodeGap": {"type": "number", "minimum": 0},
|
|
101
|
+
"labelMargin": {"type": "number", "minimum": 0}
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
"lanes": {
|
|
105
|
+
"type": "object",
|
|
106
|
+
"description": "swimlane / topology で使う lane 宣言。 key = lane id、 value = lane 定義。",
|
|
107
|
+
"additionalProperties": {
|
|
108
|
+
"type": "object",
|
|
109
|
+
"additionalProperties": false,
|
|
110
|
+
"properties": {
|
|
111
|
+
"x": {"type": "number"},
|
|
112
|
+
"width": {"type": "number", "minimum": 50},
|
|
113
|
+
"label": {"type": "string"},
|
|
114
|
+
"contain": {"type": "boolean"},
|
|
115
|
+
"lifeline": {"type": "boolean"}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
"groups": {
|
|
120
|
+
"type": "object",
|
|
121
|
+
"description": "topology で使う group 宣言。 key = group id、 value = { label, lanes }。",
|
|
122
|
+
"additionalProperties": {
|
|
123
|
+
"type": "object",
|
|
124
|
+
"required": ["lanes"],
|
|
125
|
+
"additionalProperties": false,
|
|
126
|
+
"properties": {
|
|
127
|
+
"label": {"type": "string"},
|
|
128
|
+
"lanes": {"type": "array", "items": {"type": "string"}, "minItems": 1}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Text DSL の AST 型定義
|
|
3
|
+
* docs/cdl/text-dsl-spec.md の文法を AST に変換した中間表現
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { NodeKind, Tone, EdgeStyle } from "@cardenelabs/cdl";
|
|
7
|
+
import type { RelativePos } from "./relative-pos";
|
|
8
|
+
|
|
9
|
+
export type PresetType =
|
|
10
|
+
| "sequence"
|
|
11
|
+
| "flow"
|
|
12
|
+
| "swimlane"
|
|
13
|
+
| "er"
|
|
14
|
+
| "state"
|
|
15
|
+
| "topology"
|
|
16
|
+
| "solidity"
|
|
17
|
+
| "gantt"
|
|
18
|
+
| "class"
|
|
19
|
+
| "pie"
|
|
20
|
+
| "c4"
|
|
21
|
+
| "mind";
|
|
22
|
+
|
|
23
|
+
export type Position = {
|
|
24
|
+
line: number;
|
|
25
|
+
column?: number;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* canvas pivot (CAR-1693 Phase 1) の DSL 表面 `pos: {x, y}` を保持する型。
|
|
30
|
+
* auto layout の compute value からの offset (dx, dy) を表す。 element の `layoutPos:` が
|
|
31
|
+
* undefined なら auto layout の値をそのまま採用 (catalog 100+ backward compat)、 set 済なら
|
|
32
|
+
* Phase 2 の applyPosOffset pass が offset として適用する。
|
|
33
|
+
*
|
|
34
|
+
* naming = DSL 表面 syntax は user 提案 wording (`pos:`) を維持、 内部 AST は既存 `pos: Position`
|
|
35
|
+
* (source line/column) との collision 回避のため `layoutPos:` に rename する 2 層設計。
|
|
36
|
+
*/
|
|
37
|
+
export type LayoutPos = {
|
|
38
|
+
x: number;
|
|
39
|
+
y: number;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* diagram-level layout mode (CAR-1693 Phase 1)。 未指定は "auto" default で catalog 100+ は
|
|
44
|
+
* byte-identical 動作。 "manual" は Phase 4 で drag interaction が「auto layout を skip して
|
|
45
|
+
* pos: 値をそのまま採用する」 mode として使う予定。
|
|
46
|
+
*/
|
|
47
|
+
export type LayoutMode = "auto" | "manual";
|
|
48
|
+
|
|
49
|
+
/** トップレベル AST */
|
|
50
|
+
export type DslDocument = {
|
|
51
|
+
title: string;
|
|
52
|
+
type: PresetType;
|
|
53
|
+
actors: DslActor[];
|
|
54
|
+
flow: DslStep[];
|
|
55
|
+
animate?: DslAnimate;
|
|
56
|
+
/** v0.5+ 拡張 ... viewport / lanes / groups */
|
|
57
|
+
viewport?: DslViewport;
|
|
58
|
+
lanes?: Record<string, DslLane>;
|
|
59
|
+
groups?: Record<string, DslGroup>;
|
|
60
|
+
/**
|
|
61
|
+
* canvas pivot (CAR-1693 Phase 1) diagram-level layout mode。 未指定は "auto" default で
|
|
62
|
+
* catalog 100+ backward compat。 "manual" は Phase 4 で drag → pos: 保存の完全 manual mode。
|
|
63
|
+
*/
|
|
64
|
+
layout?: LayoutMode;
|
|
65
|
+
pos: Position;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/** 登場人物 (v0.5+ ... inline option 拡張) */
|
|
69
|
+
export type DslActor = {
|
|
70
|
+
name: string;
|
|
71
|
+
kind: NodeKind;
|
|
72
|
+
/**
|
|
73
|
+
* 著者が種類を書いたか。 書かなかった時 `kind` には既定の `actor` が入るため、
|
|
74
|
+
* `kind` の値だけでは「書いた `actor`」 と「書かなかった」 を区別できない。
|
|
75
|
+
*
|
|
76
|
+
* 順序図の名札は小型の箱 (`h: 72`) で作られる。 描画側は `card` に小型用の分岐を持つが
|
|
77
|
+
* `actor` には無く、 名札の文字が箱の下端をはみ出す。 書いた時だけ種類を名札に載せ、
|
|
78
|
+
* 書かなかった時は小型に耐える形のまま残すために、 この 2 つを区別する (#1058)。
|
|
79
|
+
*/
|
|
80
|
+
kindWritten?: boolean;
|
|
81
|
+
/** v0.5+ inline option */
|
|
82
|
+
subtitle?: string;
|
|
83
|
+
eyebrow?: string;
|
|
84
|
+
value?: string;
|
|
85
|
+
rows?: string[];
|
|
86
|
+
lane?: string;
|
|
87
|
+
stack?: number;
|
|
88
|
+
initial?: boolean;
|
|
89
|
+
final?: boolean;
|
|
90
|
+
/**
|
|
91
|
+
* 箱の色。 未指定なら種類ごとの既定色。
|
|
92
|
+
*
|
|
93
|
+
* 矢印 (`DslStep.tone`) と同じ名前と別名を受け付ける (`成功` / `success` 等)。
|
|
94
|
+
* 効く種類は cdl 側の 26 種で、 それ以外は指定しても色が変わらない。
|
|
95
|
+
*/
|
|
96
|
+
tone?: Tone;
|
|
97
|
+
/**
|
|
98
|
+
* CAR-1657 parts unified syntax = kind が既存 NODE_KIND_VALID に無い値 (parts identifier 候補)
|
|
99
|
+
* だった時、 parser は partId に格納して compile 側に委譲する。 compile 時に partsCatalog から
|
|
100
|
+
* 対応する CdlDiagram を lookup + merge する経路。 partId set 時は kind = "actor" (default) fallback。
|
|
101
|
+
*/
|
|
102
|
+
partId?: string;
|
|
103
|
+
/**
|
|
104
|
+
* `色:` に色番号を書いた時の値。 どの状態に入れるかは組み立て時に決める。
|
|
105
|
+
*
|
|
106
|
+
* 色を保持する状態の名前はパーツごとに違う (`bg` / `stFill` / `gFill` / `hue` など 17 種)。
|
|
107
|
+
* 解析の時点ではパーツの定義を知らないため、 名前を決めずに持っておく。
|
|
108
|
+
*/
|
|
109
|
+
colorHex?: string;
|
|
110
|
+
/**
|
|
111
|
+
* parts state override (partId set 時のみ有効)。 kind + 既存 reserved fields を除いた
|
|
112
|
+
* inline option の残り (`v: 50` / `count: 100` 等) を state 名 → initial 値 map として保持。
|
|
113
|
+
* compile 時に parts.states[i].initial を上書きする。
|
|
114
|
+
*/
|
|
115
|
+
stateOverride?: Record<string, number | string | boolean>;
|
|
116
|
+
/**
|
|
117
|
+
* canvas pivot 新 spec (dragon canvas pivot spec §layout-role-conversion)。
|
|
118
|
+
* user drag / resize で明示的に固定した絶対座標 / サイズ。 4 field set 済なら CDL layout が
|
|
119
|
+
* 該当 actor 由来 lane / node の位置計算を skip、 posX / posY / posW / posH をそのまま採用する。
|
|
120
|
+
* 未指定なら従来の auto layout (catalog 100+ backward compat 保証)。
|
|
121
|
+
*/
|
|
122
|
+
posX?: number;
|
|
123
|
+
posY?: number;
|
|
124
|
+
posW?: number;
|
|
125
|
+
posH?: number;
|
|
126
|
+
/**
|
|
127
|
+
* 見本を何倍で描くか (`倍率: 2` / `scale: 2`、 #1026)。 partId set 時のみ有効。
|
|
128
|
+
*
|
|
129
|
+
* `大きさ:` (`posW` / `posH`) とは掛け合わさる。 画面側も同じ意味で読むため、
|
|
130
|
+
* `scale` は状態の名前としては使えない (予約語)。 状態を上書きしたい時は
|
|
131
|
+
* `state: { scale: 2 }` と明示するか、別の名前を使う。
|
|
132
|
+
*/
|
|
133
|
+
scale?: number;
|
|
134
|
+
/**
|
|
135
|
+
* 倍率として書かれた項目名 (`scale` / `倍率`、 #1026)。
|
|
136
|
+
*
|
|
137
|
+
* 値が読めない形 (`scale: x`) と書いていない形を見分けるために持つ。 見本が同じ名前の
|
|
138
|
+
* 状態を持つ時の知らせ (`scale-reserved`) が、値の読めなさに左右されないようにする。
|
|
139
|
+
*/
|
|
140
|
+
scaleKeys?: string[];
|
|
141
|
+
/**
|
|
142
|
+
* 位置を他の要素からの相対で書いた時の指定 (`位置: Web の右 200`)。
|
|
143
|
+
*
|
|
144
|
+
* 組み立ての段階で 1 度配置を計算し、 基準の実座標から `posX` / `posY` に直す。 解決後は
|
|
145
|
+
* 座標を直接書いた時と同じ経路を通るため、 効き方は書き方によって変わらない。
|
|
146
|
+
*/
|
|
147
|
+
posRel?: RelativePos;
|
|
148
|
+
/**
|
|
149
|
+
* canvas pivot UX 修正 (B1 individual node isolation)。 actor 1 件が生成する複数 sub-node
|
|
150
|
+
* (sequence の header / spacer / footer / s{N} 等) の中で「特定 sub-node だけを固定 / resize」
|
|
151
|
+
* するための nested override map。 key = sub-node id 相当の short key (`header` / `footer` /
|
|
152
|
+
* `spacer` / `s0` 等)、 value = posX/Y/W/H の 4 field。 compile 側は対応 CDL node に単独反映、
|
|
153
|
+
* 同 actor の他 sub-node は影響を受けない (lane 全体 posX とは独立経路)。
|
|
154
|
+
*/
|
|
155
|
+
nodes?: Record<string, DslActorNodeOverride>;
|
|
156
|
+
/**
|
|
157
|
+
* canvas pivot (CAR-1693 Phase 1) DSL 表面 `pos: {x, y}` 由来の layout offset。 未指定は auto
|
|
158
|
+
* layout の compute value そのまま (backward compat)、 set 済なら Phase 2 の applyPosOffset で
|
|
159
|
+
* (auto x + layoutPos.x, auto y + layoutPos.y) に shift される。 既存 posX/posY (絶対座標) は
|
|
160
|
+
* 別 mechanism で、 layoutPos は auto layout からの nudge (dx, dy)。
|
|
161
|
+
*/
|
|
162
|
+
layoutPos?: LayoutPos;
|
|
163
|
+
pos: Position;
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* canvas pivot UX 修正 (B1) = actor 内 sub-node 単位で「絶対座標 / サイズ」 を固定するための
|
|
168
|
+
* override 値。 全 field optional、 posX / posY が両方 set 済なら CDL 側で該当 sub-node の
|
|
169
|
+
* auto layout を skip、 明示座標をそのまま採用する。 posW / posH は width / height の上書き。
|
|
170
|
+
*/
|
|
171
|
+
export type DslActorNodeOverride = {
|
|
172
|
+
posX?: number;
|
|
173
|
+
posY?: number;
|
|
174
|
+
posW?: number;
|
|
175
|
+
posH?: number;
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
/** 流れ (1 行 = 1 step) (v0.5+ ... inline option 拡張) */
|
|
179
|
+
export type DslStep = {
|
|
180
|
+
no: number;
|
|
181
|
+
from: string;
|
|
182
|
+
to: string;
|
|
183
|
+
label: string;
|
|
184
|
+
sub?: string;
|
|
185
|
+
tone?: Tone;
|
|
186
|
+
style?: EdgeStyle;
|
|
187
|
+
/** v0.5+ inline option */
|
|
188
|
+
guard?: string;
|
|
189
|
+
cardinality?: string;
|
|
190
|
+
labelOffsetX?: number;
|
|
191
|
+
labelOffsetY?: number;
|
|
192
|
+
/**
|
|
193
|
+
* canvas pivot (CAR-1693 Phase 1) DSL 表面 `pos: {x, y}` 由来の layout offset。 step の edge
|
|
194
|
+
* label 位置を auto layout compute から (dx, dy) shift する。 未指定は auto、 set 済は Phase 2 で適用。
|
|
195
|
+
*/
|
|
196
|
+
layoutPos?: LayoutPos;
|
|
197
|
+
pos: Position;
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
/** lane 宣言 (v0.5+ top-level lanes section) */
|
|
201
|
+
export type DslLane = {
|
|
202
|
+
id: string;
|
|
203
|
+
x?: number;
|
|
204
|
+
width?: number;
|
|
205
|
+
label?: string;
|
|
206
|
+
contain?: boolean;
|
|
207
|
+
lifeline?: boolean;
|
|
208
|
+
/**
|
|
209
|
+
* canvas pivot (CAR-1693 Phase 1) DSL 表面 `pos: {x, y}` 由来の layout offset。 lane の x 座標を
|
|
210
|
+
* auto layout compute から (dx, dy) shift する。 未指定は auto、 set 済は Phase 2 で適用。
|
|
211
|
+
*/
|
|
212
|
+
layoutPos?: LayoutPos;
|
|
213
|
+
pos: Position;
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
/** group 宣言 (v0.5+ top-level groups section、 topology preset 専用) */
|
|
217
|
+
export type DslGroup = {
|
|
218
|
+
id: string;
|
|
219
|
+
label?: string;
|
|
220
|
+
lanes: string[]; // 内包する lane id
|
|
221
|
+
pos: Position;
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
/** viewport 全体仕様 (v0.5+ top-level viewport section) */
|
|
225
|
+
export type DslViewport = {
|
|
226
|
+
width?: number;
|
|
227
|
+
height?: number;
|
|
228
|
+
/**
|
|
229
|
+
* 図全体の倍率 (default 1)。 箱 / 文字 / 線 / 間隔のすべてが等比で拡大縮小される。
|
|
230
|
+
*
|
|
231
|
+
* `laneWidth` / `laneGap` / `nodeGap` は **間隔だけ**を動かすため、 箱の大きさは変わらず
|
|
232
|
+
* 図に占める割合はむしろ下がる。 本 field は cdl 側で座標系ごと拡大するので、
|
|
233
|
+
* 見た目の比率が完全に保たれる (SVG user unit 固定の font-size も追従する)。
|
|
234
|
+
*/
|
|
235
|
+
scale?: number;
|
|
236
|
+
laneWidth?: number;
|
|
237
|
+
/** 全体 default gap (互換維持、 個別 laneGap / nodeGap / labelMargin の fallback) */
|
|
238
|
+
gap?: number;
|
|
239
|
+
/** lanes 間 horizontal gap */
|
|
240
|
+
laneGap?: number;
|
|
241
|
+
/** nodes 間 vertical gap within lane */
|
|
242
|
+
nodeGap?: number;
|
|
243
|
+
/** edge label 周辺余白 */
|
|
244
|
+
labelMargin?: number;
|
|
245
|
+
pos: Position;
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
/** アニメーション ブロック */
|
|
249
|
+
export type DslAnimate = {
|
|
250
|
+
states: DslState[];
|
|
251
|
+
phases: DslPhase[];
|
|
252
|
+
pos: Position;
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
/** 状態宣言 */
|
|
256
|
+
export type DslState = {
|
|
257
|
+
name: string;
|
|
258
|
+
initial: number | string;
|
|
259
|
+
pos: Position;
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
/** ステップ (phase) */
|
|
263
|
+
export type DslPhase = {
|
|
264
|
+
name: string;
|
|
265
|
+
durationMs: number;
|
|
266
|
+
highlight?: string[]; // active 化対象 (node 名 / edge 名)
|
|
267
|
+
tweens?: DslTween[]; // state lerp
|
|
268
|
+
sets?: DslSet[]; // state 即時遷移
|
|
269
|
+
body?: string;
|
|
270
|
+
badge?: string;
|
|
271
|
+
pos: Position;
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
/** state lerp (遷移) */
|
|
275
|
+
export type DslTween = {
|
|
276
|
+
state: string;
|
|
277
|
+
from: number;
|
|
278
|
+
to: number;
|
|
279
|
+
pos: Position;
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
/** state 即時遷移 (切替) */
|
|
283
|
+
export type DslSet = {
|
|
284
|
+
state: string;
|
|
285
|
+
value: string | number;
|
|
286
|
+
pos: Position;
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
/** Parser error (行番号付き) */
|
|
290
|
+
export type DslError = {
|
|
291
|
+
line: number;
|
|
292
|
+
message: string;
|
|
293
|
+
hint?: string;
|
|
294
|
+
};
|
package/src/v05/index.ts
ADDED