@cardenelabs/dragon 0.28.0 → 0.30.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 +78 -5
- package/dist/index.cjs +9246 -8698
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +186 -66
- package/dist/index.d.ts +186 -66
- package/dist/index.js +9247 -8700
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/compile/actor-ids.ts +306 -0
- package/src/compile/actors.ts +74 -0
- package/src/compile/c4.ts +77 -0
- package/src/compile/chain.ts +54 -0
- package/src/compile/chart-fields.ts +178 -0
- package/src/compile/class.ts +100 -0
- package/src/compile/deep-rewrite.ts +30 -0
- package/src/compile/derived-source.ts +18 -0
- package/src/compile/direction.ts +63 -0
- package/src/compile/er-relation.ts +200 -0
- package/src/compile/er.ts +44 -0
- package/src/compile/flow.ts +54 -0
- package/src/compile/funnel.ts +75 -0
- package/src/compile/gantt.ts +232 -0
- package/src/compile/generic.ts +284 -0
- package/src/compile/hierarchy.ts +92 -0
- package/src/compile/journey.ts +84 -0
- package/src/compile/kinds.ts +156 -0
- package/src/compile/lanes.ts +56 -0
- package/src/compile/mind.ts +343 -0
- package/src/compile/node-title.ts +24 -0
- package/src/compile/notice.ts +94 -0
- package/src/compile/parts-lookup.ts +17 -0
- package/src/compile/parts.ts +2635 -0
- package/src/compile/placement-apply.ts +673 -0
- package/src/compile/placement.ts +15 -0
- package/src/compile/quadrant.ts +110 -0
- package/src/compile/rows.ts +40 -0
- package/src/compile/sequence.ts +226 -0
- package/src/compile/slug.ts +29 -0
- package/src/compile/solidity.ts +39 -0
- package/src/compile/state.ts +71 -0
- package/src/compile/subtitle.ts +22 -0
- package/src/compile/swimlane.ts +83 -0
- package/src/compile/topology.ts +50 -0
- package/src/compile/tree.ts +60 -0
- package/src/compile/value-chart.ts +183 -0
- package/src/compile/values.ts +432 -0
- package/src/compile/word-state.ts +82 -0
- package/src/compile.ts +513 -6858
- package/src/index.ts +8 -7
- package/src/json-line-map.ts +30 -0
- package/src/json-parser.ts +356 -123
- package/src/near-name.ts +42 -0
- package/src/relative-pos.ts +71 -2
- package/src/schema.ts +1 -1
- package/src/schemas/diagram.json +38 -4
- package/src/types.ts +1 -1
- package/src/v05/parser.ts +91 -72
package/src/near-name.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 書き間違えた名前に綴りの近い候補を探す (#1295 で JSON の項目名に作り、#2113 で部品の名前と共有した)。
|
|
3
|
+
*
|
|
4
|
+
* 「知らない名前です」 だけだと、書いた人は正しい綴りを探しに行く必要がある。
|
|
5
|
+
* 2 文字の違い (足りない / 多い / 別の字) までを候補とする。 隣どうしの入れ替わりは 2 文字の違いに数える。
|
|
6
|
+
*
|
|
7
|
+
* 遠い名前は勧めない。 無関係な名前を勧めると、書いた人がそちらへ直して二度手間になる。
|
|
8
|
+
* 大文字と小文字は区別しない。
|
|
9
|
+
*/
|
|
10
|
+
export function 近い名前(語: string, 候補: Iterable<string>): string | undefined {
|
|
11
|
+
const 小文字 = 語.toLowerCase();
|
|
12
|
+
let 最短: { 名: string; 距離: number } | undefined;
|
|
13
|
+
for (const c of 候補) {
|
|
14
|
+
const d = 編集距離(小文字, c.toLowerCase(), 2);
|
|
15
|
+
if (d <= 2 && (最短 === undefined || d < 最短.距離)) 最短 = { 名: c, 距離: d };
|
|
16
|
+
}
|
|
17
|
+
return 最短?.名;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 2 つの語の編集距離 (上限付き)。
|
|
22
|
+
*
|
|
23
|
+
* 上限を持つのは、長い語どうしで表を全部埋めないため。 上限を超えた時点で打ち切る。
|
|
24
|
+
*/
|
|
25
|
+
function 編集距離(a: string, b: string, 上限: number): number {
|
|
26
|
+
if (Math.abs(a.length - b.length) > 上限) return 上限 + 1;
|
|
27
|
+
let 前 = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
28
|
+
for (let i = 1; i <= a.length; i += 1) {
|
|
29
|
+
const 今: number[] = [i];
|
|
30
|
+
let 行の最小 = i;
|
|
31
|
+
for (let j = 1; j <= b.length; j += 1) {
|
|
32
|
+
const 費用 = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
33
|
+
const v = Math.min(今[j - 1]! + 1, 前[j]! + 1, 前[j - 1]! + 費用);
|
|
34
|
+
今.push(v);
|
|
35
|
+
if (v < 行の最小) 行の最小 = v;
|
|
36
|
+
}
|
|
37
|
+
// その行の最小が上限を超えたら、以降どう進んでも上限以下にはならない
|
|
38
|
+
if (行の最小 > 上限) return 上限 + 1;
|
|
39
|
+
前 = 今;
|
|
40
|
+
}
|
|
41
|
+
return 前[b.length]!;
|
|
42
|
+
}
|
package/src/relative-pos.ts
CHANGED
|
@@ -12,8 +12,20 @@
|
|
|
12
12
|
* 直らない事故を、 規則を共有することで構造的に防ぐ。
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
/**
|
|
16
|
-
|
|
15
|
+
/**
|
|
16
|
+
* 基準からどちら側に置くか。
|
|
17
|
+
*
|
|
18
|
+
* 語の一覧は下の `RELATIVE_DIRECTIONS` が持つ。 型を並べ直さず、一覧から導く。
|
|
19
|
+
*/
|
|
20
|
+
export type RelativeDirection = (typeof RELATIVE_DIRECTIONS)[number];
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 向きとして受ける語の一覧。
|
|
24
|
+
*
|
|
25
|
+
* 読み取りの入口が 2 つある (本文の `位置: Web の右 200` と JSON の `posRel.dir`) ため、
|
|
26
|
+
* 一覧を 1 箇所に置いて両方から引く。 2 度書くと、向きを足した時に片方だけが取り残される。
|
|
27
|
+
*/
|
|
28
|
+
export const RELATIVE_DIRECTIONS = ["right", "left", "above", "below"] as const;
|
|
17
29
|
|
|
18
30
|
/** 相対で書かれた位置の指定。 */
|
|
19
31
|
export type RelativePos = {
|
|
@@ -180,3 +192,60 @@ export function orderByDependency(
|
|
|
180
192
|
}
|
|
181
193
|
return { order, cyclic: [...cyclic] };
|
|
182
194
|
}
|
|
195
|
+
|
|
196
|
+
/** 解けない書き方の種類。 誤りの文は入口ごとに作るので、ここでは種類だけを返す */
|
|
197
|
+
export type RelativeProblemKind = "self" | "missing-anchor" | "cyclic";
|
|
198
|
+
|
|
199
|
+
/** 解けない書き方 1 件。 `anchor` は相手が居ない時だけ持つ */
|
|
200
|
+
export type RelativeProblem = {
|
|
201
|
+
name: string;
|
|
202
|
+
kind: RelativeProblemKind;
|
|
203
|
+
anchor?: string;
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* 解けない相対の指定を挙げる (#2039)。
|
|
208
|
+
*
|
|
209
|
+
* 解けない書き方は 3 通りある。 相手が居ない / 自分を基準にした / 基準が輪になっている。
|
|
210
|
+
* どれも「書いたのに図が変わらない」 形で表に出るため、図を出す前に知らせる。
|
|
211
|
+
*
|
|
212
|
+
* **判定は本 file に 1 つだけ置く**。 読み取りの入口が 2 つある (本文と JSON) ため、
|
|
213
|
+
* 片方だけに判定を持つと、同じ書き方が入口によって通ったり通らなかったりする。
|
|
214
|
+
* 実際 JSON 側には判定が無く、相対で置く欄そのものが無かった。
|
|
215
|
+
*
|
|
216
|
+
* 誤りの文は返さない。 本文側は行番号を、JSON 側は欄の場所を添えるため、文の組み立ては
|
|
217
|
+
* 入口に任せる。 ここが返すのは「誰の」「どの種類の」 解けなさか だけ。
|
|
218
|
+
*
|
|
219
|
+
* 呼ぶ側は、挙がった名前から相対の指定を外す。 残したままだと、誤りを無視して読み込んだ
|
|
220
|
+
* 経路で解決できない指定が組み立てまで届く。
|
|
221
|
+
*/
|
|
222
|
+
export function findRelativeProblems(
|
|
223
|
+
items: ReadonlyArray<{ name: string; rel?: RelativePos }>,
|
|
224
|
+
): RelativeProblem[] {
|
|
225
|
+
const named = new Set(items.map((i) => i.name));
|
|
226
|
+
const broken = new Set<string>();
|
|
227
|
+
const out: RelativeProblem[] = [];
|
|
228
|
+
|
|
229
|
+
for (const it of items) {
|
|
230
|
+
const rel = it.rel;
|
|
231
|
+
if (!rel) continue;
|
|
232
|
+
if (rel.anchor === it.name) {
|
|
233
|
+
out.push({ name: it.name, kind: "self" });
|
|
234
|
+
broken.add(it.name);
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
if (!named.has(rel.anchor)) {
|
|
238
|
+
out.push({ name: it.name, kind: "missing-anchor", anchor: rel.anchor });
|
|
239
|
+
broken.add(it.name);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// 輪は、既に解けないと判った分を外してから探す。 外さないと「相手が居ない」 1 件が
|
|
244
|
+
// 「輪になっている」 としても挙がり、同じ 1 つの誤りが 2 通りの直し方で出る
|
|
245
|
+
const { cyclic } = orderByDependency(
|
|
246
|
+
items.map((i) => ({ name: i.name, rel: broken.has(i.name) ? undefined : i.rel })),
|
|
247
|
+
);
|
|
248
|
+
for (const name of cyclic) out.push({ name, kind: "cyclic" });
|
|
249
|
+
|
|
250
|
+
return out;
|
|
251
|
+
}
|
package/src/schema.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* });
|
|
14
14
|
* const diagram = jsonToDiagram(res.content[0].input);
|
|
15
15
|
*
|
|
16
|
-
* schema JSON は `packages/dragon/schemas/diagram.json` が SSOT、 本 file は import して再 export するだけ。
|
|
16
|
+
* schema JSON は `packages/dragon/src/schemas/diagram.json` が SSOT、 本 file は import して再 export するだけ。
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import schemaJson from "./schemas/diagram.json" with { type: "json" };
|
package/src/schemas/diagram.json
CHANGED
|
@@ -106,7 +106,7 @@
|
|
|
106
106
|
},
|
|
107
107
|
"kind": {
|
|
108
108
|
"type": "string",
|
|
109
|
-
"description": "node kind。 shape-* を指定して 49 shape 中から選ぶ。 例: 'shape-wallet' / 'shape-smart-contract' / 'shape-cloud' / 'shape-file' / 'actor' (default) / 'function' / 'storage' / 'event'
|
|
109
|
+
"description": "node kind。 shape-* を指定して 49 shape 中から選ぶ。 例: 'shape-wallet' / 'shape-smart-contract' / 'shape-cloud' / 'shape-file' / 'actor' (default) / 'function' / 'storage' / 'event' 等。 種別に無い名前は部品の名前として部品の一覧 (partsCatalog) から引き、一覧にも無ければ種別を書かなかった箱になる。 部品を持つ一覧を渡した時は知らせを出し、綴りの近い名前を案内する。"
|
|
110
110
|
},
|
|
111
111
|
"subtitle": {
|
|
112
112
|
"type": "string",
|
|
@@ -215,7 +215,7 @@
|
|
|
215
215
|
},
|
|
216
216
|
"end": {
|
|
217
217
|
"type": "string",
|
|
218
|
-
"description": "gantt の工程が終わる時期。"
|
|
218
|
+
"description": "gantt の工程が終わる時期。 始まりより前の時期は始まりと同じに倒し、知らせを出す。"
|
|
219
219
|
},
|
|
220
220
|
"touchpoint": {
|
|
221
221
|
"type": "string",
|
|
@@ -245,6 +245,10 @@
|
|
|
245
245
|
"type": "number",
|
|
246
246
|
"description": "parts の描画倍率。 kind に parts identifier を指定した時だけ使える。"
|
|
247
247
|
},
|
|
248
|
+
"phase": {
|
|
249
|
+
"type": "boolean",
|
|
250
|
+
"description": "parts が持つ段を使うか。 既定は書いた段と番号ごとに並べて再生する。 false を書くと parts の段の中身を空にし、値は animation に書いた段だけが動かす。 kind に parts identifier を指定した時だけ使える。"
|
|
251
|
+
},
|
|
248
252
|
"pos": {
|
|
249
253
|
"type": "object",
|
|
250
254
|
"additionalProperties": false,
|
|
@@ -262,6 +266,36 @@
|
|
|
262
266
|
}
|
|
263
267
|
}
|
|
264
268
|
},
|
|
269
|
+
"posRel": {
|
|
270
|
+
"type": "object",
|
|
271
|
+
"additionalProperties": false,
|
|
272
|
+
"description": "位置を他の要素からの相対で書く指定。 座標を知らなくても「誰の」「どちら側に」「どれだけ離して」 で置ける。 解決した後は posX / posY を書いた時と同じ経路を通る。",
|
|
273
|
+
"required": [
|
|
274
|
+
"anchor",
|
|
275
|
+
"dir"
|
|
276
|
+
],
|
|
277
|
+
"properties": {
|
|
278
|
+
"anchor": {
|
|
279
|
+
"type": "string",
|
|
280
|
+
"minLength": 1,
|
|
281
|
+
"description": "基準にする相手の名前。 actors に書かれた名前をそのまま書く。"
|
|
282
|
+
},
|
|
283
|
+
"dir": {
|
|
284
|
+
"type": "string",
|
|
285
|
+
"enum": [
|
|
286
|
+
"right",
|
|
287
|
+
"left",
|
|
288
|
+
"above",
|
|
289
|
+
"below"
|
|
290
|
+
],
|
|
291
|
+
"description": "基準からどちら側に置くか。"
|
|
292
|
+
},
|
|
293
|
+
"gap": {
|
|
294
|
+
"type": "number",
|
|
295
|
+
"description": "相手の縁との間隔。 書かなければ 160 で置く。"
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
},
|
|
265
299
|
"visibleIf": {
|
|
266
300
|
"type": "string",
|
|
267
301
|
"description": "その箱を出すかどうかの条件。 状態を差し込める文字列で、\"0\" / \"false\" / 空文字が偽、それ以外は真。"
|
|
@@ -538,7 +572,7 @@
|
|
|
538
572
|
},
|
|
539
573
|
"cardinality": {
|
|
540
574
|
"type": "string",
|
|
541
|
-
"description": "ER の多重度 (1
|
|
575
|
+
"description": "ER の多重度 (type: er 用)。 1:1 / 1:N / N:1 / N:M / 0..1 / 1..* のどれかを書くと、 両端の形がその語から決まり (tailHead / head を書けばそちらが勝つ)、 語は説明文の下の行に出る。 説明文に書いた同じ語は説明文から外す。 6 語以外を書くと説明文に (語) と添えるだけで、 端の形は決まらない (tailHead と head を両方書かなければ、 描かない端を知らせる)。 type: er 以外の図種では描かず、 そのことを知らせる"
|
|
542
576
|
},
|
|
543
577
|
"side": {
|
|
544
578
|
"type": "string",
|
|
@@ -2321,7 +2355,7 @@
|
|
|
2321
2355
|
},
|
|
2322
2356
|
"events": {
|
|
2323
2357
|
"type": "array",
|
|
2324
|
-
"description": "押下などの出来事で動く仕掛け。
|
|
2358
|
+
"description": "押下などの出来事で動く仕掛け。 相手は名前で指し、指し方の項目をちょうど 1 つだけ書く。",
|
|
2325
2359
|
"items": {
|
|
2326
2360
|
"type": "object",
|
|
2327
2361
|
"required": [
|
package/src/types.ts
CHANGED
|
@@ -637,10 +637,10 @@ export type DslValue = {
|
|
|
637
637
|
}
|
|
638
638
|
);
|
|
639
639
|
|
|
640
|
-
/** ステップ (phase) */
|
|
641
640
|
/** 動いている間の帯 (#1466)。 順序図で、面がいつ動いているかを段の番号で持つ */
|
|
642
641
|
export type DslBand = { actor: string; from: number; to: number };
|
|
643
642
|
|
|
643
|
+
/** ステップ (phase) */
|
|
644
644
|
export type DslPhase = {
|
|
645
645
|
name: string;
|
|
646
646
|
durationMs: number;
|
package/src/v05/parser.ts
CHANGED
|
@@ -47,7 +47,7 @@ import type { EdgeReveal, RelationFocus } from "@cardenelabs/cdl";
|
|
|
47
47
|
import { TONE_ALIAS, NODE_KIND_ALIAS, DIRECTIONS, resolveDirection, PALETTES, resolvePalette } from "../keywords";
|
|
48
48
|
import type { DslPalette } from "../keywords";
|
|
49
49
|
import type { DslDirection } from "../keywords";
|
|
50
|
-
import { parseRelativePos,
|
|
50
|
+
import { parseRelativePos, findRelativeProblems, type RelativeProblem } from "../relative-pos";
|
|
51
51
|
import {
|
|
52
52
|
checkValueExpression,
|
|
53
53
|
isTriggerBody,
|
|
@@ -383,14 +383,12 @@ const INFRA_KIND_ALIAS: Record<string, NodeKind> = {
|
|
|
383
383
|
};
|
|
384
384
|
|
|
385
385
|
/**
|
|
386
|
-
*
|
|
386
|
+
* 記法が受理する種類の全体。 これに載っていない種類は見本 (パーツ) の候補になる。
|
|
387
387
|
*
|
|
388
|
-
*
|
|
389
|
-
*
|
|
388
|
+
* 描画できる種類 (cdl の `NODE_KINDS`) に、 記法だけが持つ種類と基盤の別名を足す。
|
|
389
|
+
* 手書きの一覧だった頃は、 描画側に種類が増えても記法から書けず、 部品名として扱われて
|
|
390
|
+
* 「そんな部品は無い」 と警告が出るだけだった。 描画側を出所に加えることで
|
|
390
391
|
* 「描画できるものは書ける」 が成立する。
|
|
391
|
-
*/
|
|
392
|
-
/**
|
|
393
|
-
* 記法が受理する種類の全体。 これに載っていない種類は見本 (パーツ) の候補になる。
|
|
394
392
|
*
|
|
395
393
|
* 画面側が「本文が見本を使っているか」 を判定するのに使う (#1022)。 手書きの一覧を
|
|
396
394
|
* 別に持つと、種類が増えた時にそちらだけ取り残されて余分な読み込みが起きる。
|
|
@@ -1553,7 +1551,7 @@ export const AXIS_Y_KEYS = ["bottom", "top"] as const satisfies readonly (keyof
|
|
|
1553
1551
|
// `parser.ts` に置いたままだと 生成した表 → parser → 生成した表 の輪ができる
|
|
1554
1552
|
export type { 欄の形, 図形の定義 } from "./parser-types";
|
|
1555
1553
|
|
|
1556
|
-
/** 描ける図形と、その欄 (`CdlDynShape`
|
|
1554
|
+
/** 描ける図形と、その欄 (`CdlDynShape` の全種を覆う) */
|
|
1557
1555
|
export const 図形の表: Record<string, 図形の定義> = {
|
|
1558
1556
|
rect: {
|
|
1559
1557
|
必須: ["source", "fillMax"],
|
|
@@ -1608,9 +1606,9 @@ export const 図形の表: Record<string, 図形の定義> = {
|
|
|
1608
1606
|
/**
|
|
1609
1607
|
* 記法が受ける部品と、その欄。
|
|
1610
1608
|
*
|
|
1611
|
-
* **手で書かず、描画側の型定義から生成する** (#1385)。
|
|
1612
|
-
*
|
|
1613
|
-
* 人手で書かない`)。
|
|
1609
|
+
* **手で書かず、描画側の型定義から生成する** (#1385)。 描画側が持つ種類も、種類ごとの欄も
|
|
1610
|
+
* 多く、手で写すと写し間違いと描画側の変更への drift が残る (`rules/quality.md § 導出可能記述は
|
|
1611
|
+
* 人手で書かない`)。 いくつあるかは `readout-table.generated.ts` を数えれば出る。
|
|
1614
1612
|
*
|
|
1615
1613
|
* 作り直す = `node packages/dragon/scripts/gen-readout-table.mjs`
|
|
1616
1614
|
* ずれの検知 = `packages/dragon/test/readout-table-generated.test.ts`
|
|
@@ -1621,8 +1619,9 @@ import { 部品の表, 部品の組の表 } from "./readout-table.generated";
|
|
|
1621
1619
|
/**
|
|
1622
1620
|
* 記法が受けるつまみと、その欄 (#1389)。
|
|
1623
1621
|
*
|
|
1624
|
-
* **部品の表と同じく、描画側の型定義から生成する**。
|
|
1622
|
+
* **部品の表と同じく、描画側の型定義から生成する**。 種類ごとに欄が違い、手で写すと
|
|
1625
1623
|
* 描画側が種類を足した時に drift が残る (`rules/quality.md § 導出可能記述は人手で書かない`)。
|
|
1624
|
+
* いくつあるかは `input-table.generated.ts` を数えれば出る。
|
|
1626
1625
|
*
|
|
1627
1626
|
* 作り直す = `node packages/dragon/scripts/gen-input-table.mjs`
|
|
1628
1627
|
* ずれの検知 = `packages/dragon/test/input-table-generated.test.ts`
|
|
@@ -2081,8 +2080,26 @@ export const EVENT_KINDS = [
|
|
|
2081
2080
|
"blur",
|
|
2082
2081
|
] as const;
|
|
2083
2082
|
|
|
2084
|
-
/**
|
|
2085
|
-
|
|
2083
|
+
/**
|
|
2084
|
+
* 出来事の相手を指す書き方。 ちょうど 1 つだけ書く。
|
|
2085
|
+
*
|
|
2086
|
+
* **ここが唯一の置き場所** (#2256)。 同じ 4 語が JSON の記法にも書かれていた間、
|
|
2087
|
+
* 1:1 を名乗りながら受ける項目が両側で別々に決まっており、片方に 5 つ目を足すと
|
|
2088
|
+
* 記法では通るのに JSON では弾かれる状態が黙って生まれる形だった。
|
|
2089
|
+
* 公開 schema の項目の鍵とは `event-target-keys-parity.test.ts` が突き合わせる。
|
|
2090
|
+
*/
|
|
2091
|
+
export const EVENT_TARGET_KEYS = ["box", "lane", "arrow", "diagram"] as const;
|
|
2092
|
+
|
|
2093
|
+
/** 出来事に必ず書く項目。 相手の指し方と合わせて schema の項目の鍵になる */
|
|
2094
|
+
export const EVENT_REQUIRED_KEYS = ["on", "handler"] as const;
|
|
2095
|
+
|
|
2096
|
+
/**
|
|
2097
|
+
* 出来事が受ける項目の全体 (#2258)。
|
|
2098
|
+
*
|
|
2099
|
+
* **判定と知らせが同じ集合を読む**。 知らせが先頭 2 語だけを手で並べていた間、
|
|
2100
|
+
* 必ず書く項目を増やしても知らせは古い一覧を教え続ける形だった。
|
|
2101
|
+
*/
|
|
2102
|
+
const EVENT_ITEM_KEYS: readonly string[] = [...EVENT_REQUIRED_KEYS, ...EVENT_TARGET_KEYS];
|
|
2086
2103
|
|
|
2087
2104
|
/**
|
|
2088
2105
|
* 押下などの出来事で動く仕掛けを 1 件読む (#1393)。
|
|
@@ -2109,12 +2126,11 @@ function 出来事として読む(
|
|
|
2109
2126
|
}
|
|
2110
2127
|
const opts = parseInlineMapping(t.slice(1, -1));
|
|
2111
2128
|
for (const k of Object.keys(opts)) {
|
|
2112
|
-
if (
|
|
2113
|
-
continue;
|
|
2129
|
+
if (EVENT_ITEM_KEYS.includes(k)) continue;
|
|
2114
2130
|
errors.push({
|
|
2115
2131
|
line,
|
|
2116
2132
|
message: `出来事の項目名が読めません: "${k}"`,
|
|
2117
|
-
hint: `使える項目 =
|
|
2133
|
+
hint: `使える項目 = ${EVENT_ITEM_KEYS.join(", ")}`,
|
|
2118
2134
|
});
|
|
2119
2135
|
return undefined;
|
|
2120
2136
|
}
|
|
@@ -2622,20 +2638,6 @@ function collectIndentedList(
|
|
|
2622
2638
|
return { items, next: i };
|
|
2623
2639
|
}
|
|
2624
2640
|
|
|
2625
|
-
/**
|
|
2626
|
-
* 登場人物を 1 件ずつ集める。 続く字下げ行は同じ 1 件にまとめる。
|
|
2627
|
-
*
|
|
2628
|
-
* 項目が少なければ 1 行で書け、 多ければ縦に並べられる。 縦に並べた方が、 何を指定できるかが
|
|
2629
|
-
* 見える。
|
|
2630
|
-
*
|
|
2631
|
-
* ```
|
|
2632
|
-
* - Client
|
|
2633
|
-
* - API: service
|
|
2634
|
-
* - Web:
|
|
2635
|
-
* kind: service
|
|
2636
|
-
* 色: 失敗
|
|
2637
|
-
* ```
|
|
2638
|
-
*/
|
|
2639
2641
|
/**
|
|
2640
2642
|
* 色の指定を振り分ける。
|
|
2641
2643
|
*
|
|
@@ -3091,51 +3093,68 @@ export const ACTOR_ITEM_KEYS: ReadonlySet<string> = new Set([
|
|
|
3091
3093
|
*/
|
|
3092
3094
|
function validateRelativePositions(actors: DslActor[], errors: DslError[]): void {
|
|
3093
3095
|
const named = new Set(actors.map((a) => a.name));
|
|
3094
|
-
|
|
3096
|
+
// 解けない書き方の判定は `relative-pos.ts` が持つ。 JSON の入口も同じ判定を呼ぶ (#2039)
|
|
3097
|
+
const problems = findRelativeProblems(actors.map((a) => ({ name: a.name, rel: a.posRel })));
|
|
3095
3098
|
|
|
3096
|
-
for (const
|
|
3097
|
-
const
|
|
3098
|
-
if (!rel) continue;
|
|
3099
|
-
if (rel.anchor === a.name) {
|
|
3100
|
-
errors.push({
|
|
3101
|
-
line: a.pos.line,
|
|
3102
|
-
message: `位置の基準が自分自身です: "${a.name}"`,
|
|
3103
|
-
hint: "別の登場人物の名前を書く",
|
|
3104
|
-
});
|
|
3105
|
-
broken.add(a.name);
|
|
3106
|
-
continue;
|
|
3107
|
-
}
|
|
3108
|
-
if (!named.has(rel.anchor)) {
|
|
3109
|
-
errors.push({
|
|
3110
|
-
line: a.pos.line,
|
|
3111
|
-
message: `位置の基準が見つかりません: "${rel.anchor}"`,
|
|
3112
|
-
hint:
|
|
3113
|
-
named.size > 0
|
|
3114
|
-
? `actors: に書かれている名前 = ${[...named].join(", ")}`
|
|
3115
|
-
: "actors: に基準にする登場人物を書く",
|
|
3116
|
-
});
|
|
3117
|
-
broken.add(a.name);
|
|
3118
|
-
}
|
|
3119
|
-
}
|
|
3120
|
-
|
|
3121
|
-
const { cyclic } = orderByDependency(
|
|
3122
|
-
actors.map((a) => ({ name: a.name, rel: broken.has(a.name) ? undefined : a.posRel })),
|
|
3123
|
-
);
|
|
3124
|
-
for (const name of cyclic) {
|
|
3125
|
-
const a = actors.find((x) => x.name === name);
|
|
3099
|
+
for (const p of problems) {
|
|
3100
|
+
const a = actors.find((x) => x.name === p.name);
|
|
3126
3101
|
errors.push({
|
|
3127
3102
|
line: a?.pos.line ?? 1,
|
|
3128
|
-
|
|
3129
|
-
hint: "どれか 1 つは座標 (`位置: 300,200`) か自動配置にする",
|
|
3103
|
+
...相対の誤り(p, named),
|
|
3130
3104
|
});
|
|
3131
|
-
broken.add(name);
|
|
3132
3105
|
}
|
|
3133
3106
|
|
|
3107
|
+
const broken = new Set(problems.map((p) => p.name));
|
|
3134
3108
|
for (const a of actors) {
|
|
3135
3109
|
if (broken.has(a.name)) a.posRel = undefined;
|
|
3136
3110
|
}
|
|
3137
3111
|
}
|
|
3138
3112
|
|
|
3113
|
+
/**
|
|
3114
|
+
* 解けない相対の指定に対する、記法側の誤りの文 (#2039)。
|
|
3115
|
+
*
|
|
3116
|
+
* 判定は `relative-pos.ts` が持ち、文はここが持つ。 JSON 側は欄の場所を添えた別の文を作る。
|
|
3117
|
+
*/
|
|
3118
|
+
function 相対の誤り(
|
|
3119
|
+
p: RelativeProblem,
|
|
3120
|
+
named: ReadonlySet<string>,
|
|
3121
|
+
): { message: string; hint: string } {
|
|
3122
|
+
switch (p.kind) {
|
|
3123
|
+
case "self":
|
|
3124
|
+
return {
|
|
3125
|
+
message: `位置の基準が自分自身です: "${p.name}"`,
|
|
3126
|
+
hint: "別の登場人物の名前を書く",
|
|
3127
|
+
};
|
|
3128
|
+
case "missing-anchor":
|
|
3129
|
+
return {
|
|
3130
|
+
message: `位置の基準が見つかりません: "${p.anchor ?? ""}"`,
|
|
3131
|
+
hint:
|
|
3132
|
+
named.size > 0
|
|
3133
|
+
? `actors: に書かれている名前 = ${[...named].join(", ")}`
|
|
3134
|
+
: "actors: に基準にする登場人物を書く",
|
|
3135
|
+
};
|
|
3136
|
+
case "cyclic":
|
|
3137
|
+
return {
|
|
3138
|
+
message: `位置の基準が互いを指しています: "${p.name}"`,
|
|
3139
|
+
hint: "どれか 1 つは座標 (`位置: 300,200`) か自動配置にする",
|
|
3140
|
+
};
|
|
3141
|
+
}
|
|
3142
|
+
}
|
|
3143
|
+
|
|
3144
|
+
/**
|
|
3145
|
+
* 登場人物を 1 件ずつ集める。 続く字下げ行は同じ 1 件にまとめる。
|
|
3146
|
+
*
|
|
3147
|
+
* 項目が少なければ 1 行で書け、 多ければ縦に並べられる。 縦に並べた方が、 何を指定できるかが
|
|
3148
|
+
* 見える。
|
|
3149
|
+
*
|
|
3150
|
+
* ```
|
|
3151
|
+
* - Client
|
|
3152
|
+
* - API: service
|
|
3153
|
+
* - Web:
|
|
3154
|
+
* kind: service
|
|
3155
|
+
* 色: 失敗
|
|
3156
|
+
* ```
|
|
3157
|
+
*/
|
|
3139
3158
|
function collectActorEntries(
|
|
3140
3159
|
lines: Line[],
|
|
3141
3160
|
start: number,
|
|
@@ -3300,13 +3319,6 @@ function reportScaleOnNonPart(
|
|
|
3300
3319
|
});
|
|
3301
3320
|
}
|
|
3302
3321
|
|
|
3303
|
-
/**
|
|
3304
|
-
* 中括弧の形で読める項目名。
|
|
3305
|
-
*
|
|
3306
|
-
* ここに無い名前は、 パーツでない箱ではどこにも入らずに消える。 `ACTOR_ITEM_KEYS` (縦に
|
|
3307
|
-
* 並べた形) とは別に持つ = 中括弧の形は位置や大きさを未対応にしてあり、 同じ集合にすると
|
|
3308
|
-
* 「知らせない」 側がずれる。
|
|
3309
|
-
*/
|
|
3310
3322
|
/**
|
|
3311
3323
|
* 中括弧の形で読める日本語と、その英語名 (#1301)。
|
|
3312
3324
|
*
|
|
@@ -3340,6 +3352,13 @@ function 中括弧の別名を寄せる(opts: Record<string, string>): Record<st
|
|
|
3340
3352
|
return 触った ? out : opts;
|
|
3341
3353
|
}
|
|
3342
3354
|
|
|
3355
|
+
/**
|
|
3356
|
+
* 中括弧の形で読める項目名。
|
|
3357
|
+
*
|
|
3358
|
+
* ここに無い名前は、 パーツでない箱ではどこにも入らずに消える。 `ACTOR_ITEM_KEYS` (縦に
|
|
3359
|
+
* 並べた形) とは別に持つ = 中括弧の形は位置や大きさ (`pos` / `size` / `scale`) を未対応にしてあり、
|
|
3360
|
+
* 同じ集合にすると「知らせない」 側がずれる。
|
|
3361
|
+
*/
|
|
3343
3362
|
export const INLINE_ACTOR_KEYS: ReadonlySet<string> = new Set([
|
|
3344
3363
|
"kind",
|
|
3345
3364
|
"subtitle",
|