@cardenelabs/dragon 0.14.0 → 0.16.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cardenelabs/dragon",
3
- "version": "0.14.0",
3
+ "version": "0.16.0",
4
4
  "description": "Dragon — Mermaid 感覚で animated SVG を生成する Text DSL。 cdl engine を内部利用。",
5
5
  "license": "MIT",
6
6
  "author": "cardene777",
@@ -50,13 +50,14 @@
50
50
  "build": "tsup",
51
51
  "dev": "tsup --watch",
52
52
  "typecheck": "tsc --noEmit",
53
+ "typecheck:test": "tsc --noEmit -p tsconfig.test.json",
53
54
  "test": "cd ../.. && vitest run packages/dragon",
54
55
  "test:watch": "cd ../.. && vitest packages/dragon",
55
56
  "lint": "tsc --noEmit",
56
57
  "prepublishOnly": "pnpm run typecheck && pnpm run test && pnpm run build"
57
58
  },
58
59
  "dependencies": {
59
- "@cardenelabs/cdl": "^0.14.0"
60
+ "@cardenelabs/cdl": "^0.15.0"
60
61
  },
61
62
  "peerDependencies": {
62
63
  "react": "^19.0.0",
package/src/compile.ts CHANGED
@@ -25,6 +25,7 @@ import type {
25
25
  ErRelationCardinality,
26
26
  FormulaAst,
27
27
  LaidDiagram,
28
+ NodeKind,
28
29
  } from "@cardenelabs/cdl";
29
30
  import {
30
31
  sequence,
@@ -45,7 +46,7 @@ import {
45
46
  inputDefaultValue,
46
47
  } from "@cardenelabs/cdl";
47
48
  import { parseFocusEntry } from "./focus";
48
- import { DRAW_TARGETS } from "./v05/parser";
49
+ import { DRAW_TARGETS, DSL_ONLY_KINDS } from "./v05/parser";
49
50
  import { isColorValue, pointsOutside, stripExternalPaint } from "./color";
50
51
  import {
51
52
  MAX_INPUT_ELEMENTS,
@@ -60,6 +61,56 @@ import {
60
61
  type RelativeDirection,
61
62
  } from "./relative-pos";
62
63
 
64
+ /**
65
+ * 記法だけが持つ種類を、描画できる種類へ読み替える (#1420)。
66
+ *
67
+ * 記法は `contract` / `eoa` のような **描画側に無い種類** を受け付ける
68
+ * (`v05/parser.ts` の `DSL_ONLY_KINDS`)。 図種ごとの役割分け (`solidity` の縦列の並べ替え
69
+ * など) に使うためで、記法としては正しい。
70
+ *
71
+ * **そのまま描画側へ渡すと図の組み立てが落ちる**。 描画側は知らない種類の大きさを引けず、
72
+ * `Cannot read properties of undefined (reading 'h')` で止まる (実測)。
73
+ *
74
+ * `solidity` と `er` は組み立ての中で別の種類に置き換えていたが、`flow` / `swimlane` /
75
+ * `state` / `topology` は `a.kind` をそのまま渡していた。 記法が受ける値で図が出ない状態
76
+ * だったので、渡す手前で必ず通す。
77
+ *
78
+ * ## 読み替え先
79
+ *
80
+ * | 種類 | 読み替え先 | なぜ |
81
+ * |---|---|---|
82
+ * | `entity` | `storage` | 表を持つ = ER 図の実体 |
83
+ * | `state` | `card` | 状態は札で表す |
84
+ * | `contract` / `proxy` / `library` / `interface` | `card` | 契約は札で表す (`solidity` の置き換え先に合わせた) |
85
+ * | `eoa` | `person` | 人が持つ財布 |
86
+ * | `multisig` | `signer` | 複数人で署名する (`solidity` の置き換え先に合わせた) |
87
+ *
88
+ * **表は `DSL_ONLY_KINDS` を鍵にして書く**。 種類を足した時に読み替え先が無いと
89
+ * 型検査が落ちるので、足し忘れが残らない。
90
+ */
91
+ const 記法だけの種類の読み替え: Readonly<
92
+ Record<(typeof DSL_ONLY_KINDS)[number], NodeKind>
93
+ > = {
94
+ entity: "storage",
95
+ state: "card",
96
+ contract: "card",
97
+ proxy: "card",
98
+ library: "card",
99
+ interface: "card",
100
+ eoa: "person",
101
+ multisig: "signer",
102
+ };
103
+
104
+ /** 描画側へ渡せる種類にする。 記法だけの種類はここで読み替わる (#1420) */
105
+ function 描ける種別(kind: string | undefined): NodeKind {
106
+ if (kind === undefined) return "actor";
107
+ const 読み替え先 = (
108
+ 記法だけの種類の読み替え as Record<string, NodeKind | undefined>
109
+ )[kind];
110
+ return 読み替え先 ?? (kind as NodeKind);
111
+ }
112
+
113
+
63
114
  export interface CompileToCdlOpts {
64
115
  /**
65
116
  * CAR-1657 = parts identifier lookup catalog、 caller (CdlEditor / test) が inject。
@@ -204,6 +255,21 @@ export function compileToCdl(doc: DslDocument, opts?: CompileToCdlOpts): CdlDiag
204
255
  case "line":
205
256
  diagram = compileValueChart(doc, "line", "chart-line", opts?.onNotice);
206
257
  break;
258
+ case "gauge":
259
+ diagram = compileValueChart(doc, "gauge", "chart-gauge", opts?.onNotice);
260
+ break;
261
+ case "radial":
262
+ diagram = compileValueChart(doc, "radial", "chart-radial", opts?.onNotice);
263
+ break;
264
+ case "stat":
265
+ diagram = compileValueChart(doc, "stat", "chart-stat", opts?.onNotice);
266
+ break;
267
+ case "waffle":
268
+ diagram = compileValueChart(doc, "waffle", "chart-waffle", opts?.onNotice);
269
+ break;
270
+ case "stacked":
271
+ diagram = compileValueChart(doc, "stacked", "chart-stacked-bar", opts?.onNotice);
272
+ break;
207
273
  case "funnel":
208
274
  diagram = compileFunnel(doc, opts?.onNotice);
209
275
  break;
@@ -649,6 +715,11 @@ const 図種の作り: Record<PresetType, "登場人物ごとに箱" | "図全
649
715
  pie: "図全体を 1 箱",
650
716
  bar: "図全体を 1 箱",
651
717
  line: "図全体を 1 箱",
718
+ gauge: "図全体を 1 箱",
719
+ radial: "図全体を 1 箱",
720
+ stat: "図全体を 1 箱",
721
+ waffle: "図全体を 1 箱",
722
+ stacked: "図全体を 1 箱",
652
723
  funnel: "図全体を 1 箱",
653
724
  tree: "図全体を 1 箱",
654
725
  journey: "図全体を 1 箱",
@@ -1011,6 +1082,11 @@ const 空の形の逃げ先: Record<PresetType, string[]> = {
1011
1082
  pie: [],
1012
1083
  bar: [],
1013
1084
  line: [],
1085
+ gauge: [],
1086
+ radial: [],
1087
+ stat: [],
1088
+ waffle: [],
1089
+ stacked: [],
1014
1090
  funnel: [],
1015
1091
  tree: [],
1016
1092
  journey: [],
@@ -4540,8 +4616,16 @@ function 参照する名前(value: number | string | null): string | null {
4540
4616
  */
4541
4617
  function compileValueChart(
4542
4618
  doc: DslDocument,
4543
- 型: "pie" | "bar" | "line",
4544
- kind: "chart-pie" | "chart-bar" | "chart-line",
4619
+ 型: "pie" | "bar" | "line" | "gauge" | "radial" | "stat" | "waffle" | "stacked",
4620
+ kind:
4621
+ | "chart-pie"
4622
+ | "chart-bar"
4623
+ | "chart-line"
4624
+ | "chart-gauge"
4625
+ | "chart-radial"
4626
+ | "chart-stat"
4627
+ | "chart-waffle"
4628
+ | "chart-stacked-bar",
4545
4629
  onNotice?: (notice: CompileNotice) => void,
4546
4630
  ): CdlDiagram {
4547
4631
  const b = diagram(slugify(doc.title), { topic: doc.title });
@@ -4549,11 +4633,19 @@ function compileValueChart(
4549
4633
  // **高さは型で違い、 格子に載せる**。 描画側 (`cdl` の `chart()` preset) は `pie` を 320、
4550
4634
  // 棒と折れ線を 360 とした上で **16 の倍数へ切り上げる** (360 は 16 で割り切れないので 368)。
4551
4635
  // 切り上げないと下端が格子から外れ、 全図で位置の警告が出る (review 指摘)
4552
- const CHART_H = === "pie" ? 320 : 368;
4636
+ // 半円と弧は縦を使わないので円と同じ 320。 描画側 (`cdl` `chart()` preset)
4637
+ // `pie` / `gauge` / `radial` を 320、棒と折れ線を 360 とし、16 の倍数へ切り上げる
4638
+ // 縦に余白が要らない型。 描画側 (`cdl` の `chart()` preset) と揃える。
4639
+ // 半円 / 弧 / 割合の印 は縦を使わず、値 1 つを大きく示す図も縦に伸びない
4640
+ const 低い型 =
4641
+ 型 === "pie" || 型 === "gauge" || 型 === "radial" || 型 === "waffle" || 型 === "stat";
4642
+ const CHART_H = 低い型 ? 320 : 368;
4553
4643
  b.lane("chart", { width: CHART_W + 64 });
4554
4644
 
4555
4645
  const data: NonNullable<CdlDiagram["nodes"][number]["chartData"]> = [];
4556
4646
  const 読めない: string[] = [];
4647
+ const 前が読めない: string[] = [];
4648
+ let 前が読めない行 = 0;
4557
4649
  const 未宣言: string[] = [];
4558
4650
  let 未宣言行 = 0;
4559
4651
  const 参照できる = 数の欄から参照できる名前(doc);
@@ -4580,16 +4672,37 @@ function compileValueChart(
4580
4672
  continue;
4581
4673
  }
4582
4674
  // 色はそのまま渡す。 箱が 1 つになっても、 書いた色が消えないようにする
4583
- data.push({ label: 箱の題(a), value, ...(a.tone !== undefined ? { tone: a.tone } : {}) });
4675
+ // 前の時点の値 (#1450) 読めない形は黙って捨てず知らせる = 書いたのに 2 本目の帯が
4676
+ // 出ない状態になり、手掛かりが残らない
4677
+ const 前 = a.previous === undefined ? null : parseChartValue(a.previous);
4678
+ if (a.previous !== undefined && 前 === null) {
4679
+ if (前が読めない.length === 0) 前が読めない行 = a.pos?.line ?? 0;
4680
+ 前が読めない.push(a.name);
4681
+ }
4682
+ data.push({
4683
+ label: 箱の題(a),
4684
+ value,
4685
+ ...(前 === null ? {} : { previous: 前 }),
4686
+ ...(a.tone !== undefined ? { tone: a.tone } : {}),
4687
+ });
4584
4688
  }
4585
4689
  // 案内の言葉は型ごとに変える。 共通化した時に `pie` の「割合 / 円 / 45%」 が「値 / 図 / 45」 に
4586
4690
  // 薄まり、 既存の案内が後退した (review 指摘)。 何を書けばよいかは型ごとに違う
4587
- const =
4588
- 型 === "pie"
4589
- ? { 量: "割合", 図: "円", 例: '"45%"' }
4590
- : === "bar"
4591
- ? { 量: "値", 図: "棒", 例: '"420"' }
4592
- : { 量: "", 図: "折れ線", 例: '"180"' };
4691
+ // 何を書けばよいかは型ごとに違う。 まとめると「割合 / 円 / 45%」 が「値 / 図 / 45」 に薄まる
4692
+ //
4693
+ // **表で持つ**。 三項の連鎖にすると、型が増えるたびに深さが増え、最後の枝が
4694
+ // 「それ以外」 になるため型検査が新しい型の漏れを教えてくれない
4695
+ const 語の表: Record<typeof 型, { 量: string; 図: string; 例: string }> = {
4696
+ pie: { 量: "割合", 図: "", 例: '"45%"' },
4697
+ bar: { 量: "値", 図: "棒", 例: '"420"' },
4698
+ line: { 量: "値", 図: "折れ線", 例: '"180"' },
4699
+ gauge: { 量: "値", 図: "半円", 例: '"680"' },
4700
+ radial: { 量: "値", 図: "弧", 例: '"72"' },
4701
+ stat: { 量: "値", 図: "大きな数字", 例: '"1200"' },
4702
+ waffle: { 量: "割合", 図: "100 個の印", 例: '"45%"' },
4703
+ stacked: { 量: "内訳の値", 図: "帯", 例: '"320"' },
4704
+ };
4705
+ const 語 = 語の表[型];
4593
4706
 
4594
4707
  /**
4595
4708
  * 利用者に伝える。 **`console.warn` だけにしない**。 エディタは受け取った notice を画面に
@@ -4618,6 +4731,15 @@ function compileValueChart(
4618
4731
  未宣言行,
4619
4732
  );
4620
4733
  }
4734
+ if (前が読めない.length > 0) {
4735
+ 伝える(
4736
+ "chart-value-unreadable",
4737
+ 前が読めない[0]!,
4738
+ `type: ${型} で前の時点の値を読めない項目があります (前の値を使いません): ${前が読めない.join(", ")}。` +
4739
+ " `- 名前: { value: " + 語.例 + ", previous: " + 語.例 + " }` の形で書いてください",
4740
+ 前が読めない行,
4741
+ );
4742
+ }
4621
4743
  if (doc.flow.length > 0) {
4622
4744
  伝える(
4623
4745
  "chart-edge-dropped",
@@ -5330,7 +5452,7 @@ function compileC4(doc: DslDocument): CdlDiagram {
5330
5452
  b.node(x.id, {
5331
5453
  lane: lid,
5332
5454
  stack,
5333
- kind: x.actor.kind,
5455
+ kind: 描ける種別(x.actor.kind),
5334
5456
  title: x.actor.name,
5335
5457
  });
5336
5458
  }
@@ -5442,6 +5564,8 @@ type 放射で描けない欄 =
5442
5564
  | "stack"
5443
5565
  | "initial"
5444
5566
  | "final"
5567
+ // 前の時点の値 (#1450)。 放射の枝は 1 時点しか描かない = 2 本目の帯に当たるものが無い
5568
+ | "previous"
5445
5569
  | "colorHex"
5446
5570
  | "stateOverride"
5447
5571
  | "posX"
@@ -5505,6 +5629,7 @@ const 放射で描けない欄の名前: Record<放射で描けない欄, string
5505
5629
  stack: "積む順",
5506
5630
  initial: "始まり / 終わり の印",
5507
5631
  final: "始まり / 終わり の印",
5632
+ previous: "前の時点の値",
5508
5633
  colorHex: "色番号",
5509
5634
  stateOverride: "状態の上書き",
5510
5635
  // 位置は「登場人物ごとの箱をどこに置くか」 の指定で、 箱が 1 つの図では置く先が無い
@@ -6123,6 +6248,9 @@ function injectPhasesFallback(diagram: CdlDiagram, doc: DslDocument): void {
6123
6248
  body: p.body ?? "",
6124
6249
  activate: activateIds,
6125
6250
  ...(drawIds.length > 0 ? { draw: drawIds } : {}),
6251
+ // 描く速さ (#1441)。 **描く相手が決まった段にだけ載せる** = 語が効かない図種
6252
+ // (`drawIds` が空) で割合だけ渡すと、描画側が「draw が空なのに割合がある」 と知らせる
6253
+ ...(drawIds.length > 0 && p.drawRatio !== undefined ? { drawRatio: p.drawRatio } : {}),
6126
6254
  tweens: (p.tweens ?? []).map((t) => ({ stateId: t.state, from: t.from, to: t.to })),
6127
6255
  sets: (p.sets ?? []).map((s) => ({ stateId: s.state, value: s.value })),
6128
6256
  ...(p.badge ? { badge: p.badge } : {}),
@@ -6384,7 +6512,7 @@ function compileFlow(doc: DslDocument): CdlDiagram {
6384
6512
  flowBuilder.step(
6385
6513
  {
6386
6514
  id: slugify(a.name) || `n${i}`,
6387
- kind: a.kind,
6515
+ kind: 描ける種別(a.kind),
6388
6516
  title: 箱の題(a),
6389
6517
  },
6390
6518
  edgeLabel,
@@ -6423,7 +6551,7 @@ function compileSwimlane(doc: DslDocument): CdlDiagram {
6423
6551
  swim.node(nodeId, {
6424
6552
  lane: laneId,
6425
6553
  stack,
6426
- kind: actor?.kind ?? "actor",
6554
+ kind: 描ける種別(actor?.kind),
6427
6555
  title: actorName,
6428
6556
  });
6429
6557
  laneStackCount.set(laneId, stack + 1);
@@ -6454,7 +6582,7 @@ function compileSwimlane(doc: DslDocument): CdlDiagram {
6454
6582
  swim.node(slugify(a.name) || `n${i}`, {
6455
6583
  lane: swim.laneId(a.name),
6456
6584
  stack: 0,
6457
- kind: a.kind ?? "actor",
6585
+ kind: 描ける種別(a.kind),
6458
6586
  title: 箱の題(a),
6459
6587
  });
6460
6588
  });
@@ -6519,9 +6647,35 @@ function 始まりと終わりの決め方(doc: DslDocument): {
6519
6647
  };
6520
6648
  }
6521
6649
 
6650
+ /** 状態の図で `stateMachine` preset が全ての箱に使う種類 */
6651
+ const 状態の図の既定の種類: NodeKind = "card";
6652
+
6653
+ /**
6654
+ * 状態の図で、既定と違う種類を書いた登場人物がいるか (#1450)。
6655
+ *
6656
+ * `stateMachine` preset は全ての状態を `card` で描くため、書いた `kind:` が黙って消える。
6657
+ * 始点終点の印 (`mark-start` / `mark-end`) を書けるようにするには、書いた形を効かせる必要がある。
6658
+ *
6659
+ * **「書いたか」 だけでは広すぎる**。 記法だけの種類 (`kind: state` 等) は `card` に読み替わり、
6660
+ * preset の出す形と同じになる = 書いても何も変わらないので経路を切り替える理由が無い。
6661
+ * 切り替えると既存の図の id と枠の作りが変わる (実測で golden 8 件が落ちた)。
6662
+ *
6663
+ * 読み替えた後の種類が既定と違う時だけ切り替える。 こうすると「書いたのに効かない」 は消え、
6664
+ * 「書いたが結果が同じ」 は従来の経路に留まる。
6665
+ */
6666
+ function 既定と違う種類を書いた(doc: DslDocument): boolean {
6667
+ return doc.actors.some(
6668
+ (a) => a.kindWritten === true && 描ける種別(a.kind) !== 状態の図の既定の種類,
6669
+ );
6670
+ }
6671
+
6522
6672
  function compileState(doc: DslDocument): CdlDiagram {
6523
6673
  // v0.4 ... animation あり時 builder 直接経路 (各 state を lane で配置、 transition を edge)
6524
- if (doc.animate && doc.animate.phases.length > 0) {
6674
+ //
6675
+ // **種類を書いた形は動きの有無に関わらず generic 経路へ** (#1450、 #1263 と同じ理由)。
6676
+ // 動く図だけで効かせると、同じ記法でも静止図では指定が黙って消える
6677
+ // (実測 = `kind: mark-start` を書いた箱が `card` になり知らせも出ない)
6678
+ if ((doc.animate && doc.animate.phases.length > 0) || 既定と違う種類を書いた(doc)) {
6525
6679
  return compileGenericWithAnimate(doc, { kind: "state", laneWidth: 360 });
6526
6680
  }
6527
6681
  // stateMachine preset ... actors を state に、 流れ を transition に
@@ -6577,7 +6731,7 @@ function compileTopology(doc: DslDocument): CdlDiagram {
6577
6731
  for (const a of doc.actors) {
6578
6732
  groupBuilder.add({
6579
6733
  id: slugify(a.name) || a.name,
6580
- kind: a.kind,
6734
+ kind: 描ける種別(a.kind),
6581
6735
  title: 箱の題(a),
6582
6736
  });
6583
6737
  }
@@ -6717,7 +6871,12 @@ function compileGenericWithAnimate(doc: DslDocument, opts: GenericOpts): CdlDiag
6717
6871
  while (集合?.has(stack)) stack += 1;
6718
6872
  埋める(lid, stack);
6719
6873
  }
6720
- b.node(id, { lane: lid, stack, kind: a.kind, title: 箱の題(a) });
6874
+ b.node(id, {
6875
+ lane: lid,
6876
+ stack,
6877
+ kind: 描ける種別(a.kind),
6878
+ title: 箱の題(a),
6879
+ });
6721
6880
  });
6722
6881
  } else if (kind === "flow" || kind === "topology") {
6723
6882
  // 1 lane に全 actor を縦 stack
@@ -6730,7 +6889,12 @@ function compileGenericWithAnimate(doc: DslDocument, opts: GenericOpts): CdlDiag
6730
6889
  doc.actors.forEach((a, idx) => {
6731
6890
  const id = slugify(a.name) || `n${idx}`;
6732
6891
  actorToNodeId.set(a.name, id);
6733
- b.node(id, { lane: lid, stack: idx, kind: a.kind, title: 箱の題(a) });
6892
+ b.node(id, {
6893
+ lane: lid,
6894
+ stack: idx,
6895
+ kind: 描ける種別(a.kind),
6896
+ title: 箱の題(a),
6897
+ });
6734
6898
  });
6735
6899
  } else {
6736
6900
  // swimlane / er / state ... actor ごとに 1 lane (横並び)
@@ -6755,7 +6919,7 @@ function compileGenericWithAnimate(doc: DslDocument, opts: GenericOpts): CdlDiag
6755
6919
  b.node(id, {
6756
6920
  lane: lid,
6757
6921
  stack: 0,
6758
- kind: a.kind,
6922
+ kind: 描ける種別(a.kind),
6759
6923
  title: 箱の題(a),
6760
6924
  ...(isInitial ? { eyebrow: "初期" } : {}),
6761
6925
  ...(isFinal ? { eyebrow: "最終" } : {}),
package/src/index.ts CHANGED
@@ -11,6 +11,17 @@
11
11
  export { parseTextDsl } from "./parser";
12
12
  export { compileToCdl } from "./compile";
13
13
  export type { CompileNotice } from "./compile";
14
+ /**
15
+ * 組み立てた図の型 (#1415)。
16
+ *
17
+ * `textDslToDiagram` が返す型そのもので、実体は `@cardenelabs/cdl` が持つ。 使う側が
18
+ * 戻り値に型を付けるには必ず要るので、ここから引けるようにする。
19
+ *
20
+ * 再 export が無い間、使う側は `@cardenelabs/cdl` を直接の依存に足す必要があった。
21
+ * 足さずに書くと **型が暗黙の `any` に落ち、その先の `.map((n) => ...)` まで検査が効かなく
22
+ * なる** (実測 = この 1 件が 39 件の暗黙 `any` を生んでいた)。
23
+ */
24
+ export type { CdlDiagram } from "@cardenelabs/cdl";
14
25
  export { parseTextDslV05 } from "./v05";
15
26
  // 記法一覧が「実際に受け付ける値」 を実装から引くための公開。 手書きすると説明と実装がずれる。
16
27
  export { DRAW_TARGETS, DRAW_WORDS, PHASE_ITEM_WORDS, PRESET_TYPES, TOP_LEVEL_KEYS } from "./v05/parser";
@@ -212,6 +212,13 @@ export interface JsonActor {
212
212
  subtitle?: string;
213
213
  eyebrow?: string;
214
214
  value?: string;
215
+ /**
216
+ * 前の時点の値 (#1450)。 記法の `{ value: "320", previous: "280" }` と同じ。
217
+ *
218
+ * 内訳の変化を帯で示す図 (`type: stacked`) が 2 本目の帯として描き、
219
+ * 値 1 つを大きく示す図 (`type: stat`) が差として出す。 書かない図は 1 本のまま。
220
+ */
221
+ previous?: string;
215
222
  rows?: string[];
216
223
  lane?: string;
217
224
  stack?: number;
@@ -354,6 +361,13 @@ export interface JsonPhase {
354
361
  * 受ける 8 語は `DRAW_WORDS` が持つ。 語と同じ図種で、その段の間に図が起点から現れる。
355
362
  */
356
363
  draw?: string;
364
+ /**
365
+ * 描き終えるまでに段の何割を使うか (#1441)。 記法の `draw: line 0.4` の後半と同じ。
366
+ *
367
+ * 0 より大きく 1 以下。 書かなければ段の終わりに描き終わる。 `draw` が無い段に書いても
368
+ * 何も起きない (描く相手が決まらないため、写す時に外す)。
369
+ */
370
+ drawRatio?: number;
357
371
  /**
358
372
  * 段の中で値を動かす (#1186)。 記法の `tween: name 100 -> 90` と同じ。
359
373
  *
@@ -444,6 +458,8 @@ export const ACCEPTED_KEYS = {
444
458
  "subtitle",
445
459
  "eyebrow",
446
460
  "value",
461
+ // 前の時点の値 (#1450)。 内訳の変化を帯で示す図と、値 1 つを大きく示す図が読む
462
+ "previous",
447
463
  "rows",
448
464
  "lane",
449
465
  "stack",
@@ -496,7 +512,7 @@ export const ACCEPTED_KEYS = {
496
512
  "overlay",
497
513
  "pos",
498
514
  ],
499
- phase: ["step", "duration", "focus", "body", "badge", "tween", "set", "draw"],
515
+ phase: ["step", "duration", "focus", "body", "badge", "tween", "set", "draw", "drawRatio"],
500
516
  viewport: ["width", "height", "scale", "laneWidth", "gap", "laneGap", "nodeGap", "labelMargin"],
501
517
  lane: ["x", "width", "label", "contain", "lifeline", "pos"],
502
518
  group: ["label", "lanes"],
@@ -583,6 +599,7 @@ export const 欄の型表 = {
583
599
  subtitle: "文字列",
584
600
  eyebrow: "文字列",
585
601
  value: "文字列",
602
+ previous: "文字列",
586
603
  rows: "文字列の並び",
587
604
  lane: "文字列",
588
605
  stack: "数",
@@ -644,6 +661,7 @@ export const 欄の型表 = {
644
661
  tween: "object",
645
662
  set: "object",
646
663
  draw: "描くもの",
664
+ drawRatio: "数",
647
665
  },
648
666
  viewport: {
649
667
  width: "数",
@@ -2161,6 +2179,7 @@ export function jsonToDoc(json: DragonJson): DslDocument {
2161
2179
  subtitle: a.subtitle,
2162
2180
  eyebrow: a.eyebrow,
2163
2181
  value: a.value,
2182
+ previous: a.previous,
2164
2183
  rows: a.rows,
2165
2184
  lane: a.lane,
2166
2185
  stack: a.stack,
@@ -2286,6 +2305,9 @@ export function jsonToDoc(json: DragonJson): DslDocument {
2286
2305
  badge: p.badge,
2287
2306
  // 書いた段だけが欄を持つ。 空文字を置くと「書いた」 と「書いていない」 が同じ形になる
2288
2307
  ...(p.draw !== undefined ? { draw: p.draw, drawPos: p0 } : {}),
2308
+ // 割合は `draw` がある段にだけ写す。 単独で書いても描く相手が決まらず何も起きないので、
2309
+ // 記法側 (同じ行に書かせる形) と同じ状態に揃える
2310
+ ...(p.draw !== undefined && p.drawRatio !== undefined ? { drawRatio: p.drawRatio } : {}),
2289
2311
  // 段の中で動かす分 (#1186)。 記法側の `tweens` / `sets` と同じ形に写す。
2290
2312
  // 空の配列を置かないのは、記法側が「無ければ field ごと持たない」 形だから
2291
2313
  ...(p.tween && Object.keys(p.tween).length > 0
@@ -27,6 +27,11 @@
27
27
  "pie",
28
28
  "bar",
29
29
  "line",
30
+ "gauge",
31
+ "radial",
32
+ "stat",
33
+ "waffle",
34
+ "stacked",
30
35
  "funnel",
31
36
  "tree",
32
37
  "journey",
@@ -34,11 +39,11 @@
34
39
  "c4",
35
40
  "mind"
36
41
  ],
37
- "description": "図の種類。 型ごとに actors の書き方が違う。 [関係を描く] sequence (時系列の呼び出し) / flow (処理の流れ) / swimlane (責務ごとの流れ) / er (DB の schema) / state (状態の遷移) / topology (network) / solidity (contract) / class (UML の class) / c4 (architecture) / mind (発想の枝分かれ) は actors に名前を並べ flow に矢印を書く。 [値を描く] pie (割合) / bar (棒の高さ) / line (線の高さ、 書いた順に並ぶ) は actors に `- 名前: \"45\"` の形で数を書く。 funnel (段ごとに減る数) も同じ形。 [語を描く] journey (体験の起伏) は `- 登録: \"不満\"` の形で 最高 / 満足 / 普通 / 不満 / 怒り のどれかを書く。 quadrant (2 軸の仕分け) は `- 重複削除: \"左上\"` の形で 左上 / 右上 / 左下 / 右下 のどれかを書く。 [時期を描く] gantt (工程の並び) は `- 設計: \"1月\"` の形で始まりの時期を書き、 flow の矢印で前後関係を書く。 [親子を描く] tree (親子の入れ子) は actors に名前を並べ flow の矢印で親子を書く (矢印の先が子)"
42
+ "description": "図の種類。 型ごとに actors の書き方が違う。 [関係を描く] sequence (時系列の呼び出し) / flow (処理の流れ) / swimlane (責務ごとの流れ) / er (DB の schema) / state (状態の遷移) / topology (network) / solidity (contract) / class (UML の class) / c4 (architecture) / mind (発想の枝分かれ) は actors に名前を並べ flow に矢印を書く。 [値を描く] pie (割合) / bar (棒の高さ) / line (線の高さ、 書いた順に並ぶ) / gauge (合計を半円で示す) / radial (弧の長さで比べる) / stat (値 1 つを大きく示す) / waffle (割合を 100 個の印で示す) / stacked (内訳を帯で示す) は actors に `- 名前: \"45\"` の形で数を書く。 stat と stacked は `- 名前: { value: \"320\", previous: \"280\" }` の形で前の時点の値も書ける。 funnel (段ごとに減る数) も同じ形。 [語を描く] journey (体験の起伏) は `- 登録: \"不満\"` の形で 最高 / 満足 / 普通 / 不満 / 怒り のどれかを書く。 quadrant (2 軸の仕分け) は `- 重複削除: \"左上\"` の形で 左上 / 右上 / 左下 / 右下 のどれかを書く。 [時期を描く] gantt (工程の並び) は `- 設計: \"1月\"` の形で始まりの時期を書き、 flow の矢印で前後関係を書く。 [親子を描く] tree (親子の入れ子) は actors に名前を並べ flow の矢印で親子を書く (矢印の先が子)"
38
43
  },
39
44
  "eyebrow": {
40
45
  "type": "string",
41
- "description": "図表の箱の上に出す小見出し (省略可)。 効くのは図全体を 1 箱にする型 (pie / bar / line / funnel / tree / journey / quadrant / mind / gantt) だけ。 箱ごとに分かれる型では相手が決まらないため、 actors[].eyebrow に書く"
46
+ "description": "図表の箱の上に出す小見出し (省略可)。 効くのは図全体を 1 箱にする型 (pie / bar / line / gauge / radial / stat / waffle / stacked / funnel / tree / journey / quadrant / mind / gantt) だけ。 箱ごとに分かれる型では相手が決まらないため、 actors[].eyebrow に書く"
42
47
  },
43
48
  "axes": {
44
49
  "type": "object",
@@ -107,6 +112,10 @@
107
112
  "type": "string",
108
113
  "description": "actor 数値表示 (kind: actor 用)"
109
114
  },
115
+ "previous": {
116
+ "type": "string",
117
+ "description": "前の時点の値。 type: stacked が 2 本目の帯として描き、type: stat が差として出す。 書かない図は 1 本のまま"
118
+ },
110
119
  "rows": {
111
120
  "type": "array",
112
121
  "items": {
@@ -584,6 +593,12 @@
584
593
  "enum": ["line", "bar", "pie", "journey", "mind", "tree", "gantt", "funnel"],
585
594
  "description": "この phase で起点から描くもの。 語は図種と揃える。 line / journey = 線が左端から右へ伸びる / bar = 棒が横軸から上へ伸びる / pie = 扇が 12 時から時計回りに開く / mind = 枝が中心から外へ伸びる / tree = 枝が根から下へ伸びる / gantt = 帯が各始端から右へ伸びる / funnel = 段が上端から順に積まれる"
586
595
  },
596
+ "drawRatio": {
597
+ "type": "number",
598
+ "exclusiveMinimum": 0,
599
+ "maximum": 1,
600
+ "description": "描き終えるまでに phase の何割を使うか。 0.4 なら進みが 0.4 に達した時点で描き終わり、残りは描き終わった姿のまま値だけが動く。 書かなければ phase の終わりに描き終わる。 draw と一緒に書く (draw が無いと何も起きない)"
601
+ },
587
602
  "tween": {
588
603
  "type": "object",
589
604
  "description": "この phase で補間する状態。 状態名 -> [開始値, 終了値]。 名前は英字か _ で始め、 英数字と _ だけを使う",
package/src/types.ts CHANGED
@@ -4,6 +4,18 @@
4
4
  */
5
5
 
6
6
  import type { CdlDiagram, NodeKind, Tone, EdgeStyle } from "@cardenelabs/cdl";
7
+ import type { DslOnlyKind } from "./v05/parser";
8
+
9
+ /**
10
+ * 記法が書ける箱の種類 (#1420)。
11
+ *
12
+ * 描画できる種類 (`NodeKind`) に、**記法だけが持つ種類** (`contract` / `eoa` など) を足す。
13
+ * 後者は図種ごとの役割分け (`solidity` の縦列の並べ替えなど) に使い、描画側へ渡す手前で
14
+ * `compile.ts` の `描ける種別` が読み替える。
15
+ *
16
+ * `NodeKind` だけに縛っていた間、記法が受ける値を型が表せていなかった。
17
+ */
18
+ export type DslNodeKind = NodeKind | DslOnlyKind;
7
19
  import type { RelativePos } from "./relative-pos";
8
20
 
9
21
  /**
@@ -59,6 +71,11 @@ export type PresetType =
59
71
  | "pie"
60
72
  | "bar"
61
73
  | "line"
74
+ | "gauge"
75
+ | "radial"
76
+ | "stat"
77
+ | "waffle"
78
+ | "stacked"
62
79
  | "funnel"
63
80
  | "tree"
64
81
  | "journey"
@@ -188,7 +205,7 @@ export type DslAxes = {
188
205
  /** 登場人物 (v0.5+ ... inline option 拡張) */
189
206
  export type DslActor = {
190
207
  name: string;
191
- kind: NodeKind;
208
+ kind: DslNodeKind;
192
209
  /**
193
210
  * 著者が種類を書いたか。 書かなかった時 `kind` には既定の `actor` が入るため、
194
211
  * `kind` の値だけでは「書いた `actor`」 と「書かなかった」 を区別できない。
@@ -202,6 +219,16 @@ export type DslActor = {
202
219
  subtitle?: string;
203
220
  eyebrow?: string;
204
221
  value?: string;
222
+ /**
223
+ * 前の時点の値 (#1450)。
224
+ *
225
+ * 内訳の変化を帯で示す図 (`type: stacked`) が 2 本目の帯として描き、値 1 つを大きく示す図
226
+ * (`type: stat`) が差として出す。 書かない図は 1 本のまま (従来と同じ)。
227
+ *
228
+ * **3 時点以上は持たない**。 描画側が 2 時点で設計されており (`cdl#551`)、足すなら欄の形ごと
229
+ * 決め直すことになる。
230
+ */
231
+ previous?: string;
205
232
  rows?: string[];
206
233
  /**
207
234
  * 箱の中に描く図形 (`shape:`、 #1374)。 水位や角度を状態で動かせる。
@@ -534,6 +561,20 @@ export type DslPhase = {
534
561
  * 相手の名前は書かせない = 対象の 8 図種はいずれも図全体を 1 箱で描くため相手が決まる。
535
562
  */
536
563
  draw?: string;
564
+ /**
565
+ * 描き終えるまでに段の何割を使うか (#1441)。
566
+ *
567
+ * `draw: line 0.4` のように語の後ろに書く。 0 より大きく 1 以下。
568
+ * 書かなければ段の終わりに描き終わる (従来どおり)。
569
+ *
570
+ * **段の長さと描く速さを切り離すための欄**。 伸び具合は段の進みそのものなので、
571
+ * これが無いと「線はゆっくり引きたいが値の移りは短くしたい」 が書けず、描く速さのために
572
+ * 段の長さを動かすことになる。 段の長さを動かすと同じ段の `tween` まで遅くなる。
573
+ *
574
+ * **`draw` と同じ行に書かせる**。 別の項目にすると「割合だけ書いて `draw` が無い段」 が
575
+ * 書けてしまい、何も起きない指定になる。 同じ行なら書けない。
576
+ */
577
+ drawRatio?: number;
537
578
  /**
538
579
  * `draw` を書いた行 (#1312)。 知らせの行番号に使う。
539
580
  *