@cardenelabs/cdl 0.5.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -33,7 +33,7 @@ declare function computed<T>(fn: () => T): Computed<T>;
33
33
  declare const TONES: readonly ["accent", "teal", "success", "error", "warning", "info"];
34
34
  type Tone = (typeof TONES)[number];
35
35
  /** 描画できる箱の種類。 検査・記法一覧・型はこの 1 つの列を出所にする。 */
36
- declare const NODE_KINDS: readonly ["dyn-rect", "dyn-circle", "dyn-arc", "dyn-wave", "dyn-polygon", "actor", "function", "storage", "event", "card", "person", "user-group", "admin", "developer", "external-user", "database", "cache", "queue", "message-bus", "cloud", "cdn", "service", "api", "frontend", "backend", "webhook", "microservice", "signer", "oracle", "merkle-tree", "decision", "chart-pie", "chart-line", "chart-bar", "gantt-timeline", "mind-map", "mind-radial", "funnel-stages", "quadrant-matrix", "tree-hierarchy", "journey-map", "shape-file", "shape-folder", "shape-cloud", "shape-cylinder", "shape-hexagon", "shape-diamond", "shape-stack", "shape-person", "shape-window", "shape-terminal", "shape-code-block", "shape-kanban-card", "shape-message-bubble", "shape-gear", "shape-server-rack", "shape-network-node", "shape-mobile-device", "shape-iot-sensor", "shape-robot-arm", "shape-satellite", "shape-smart-contract", "shape-blockchain-block", "shape-rpc-node", "shape-wallet", "shape-nft", "shape-token", "shape-blockchain", "shape-bitcoin-chain", "shape-ethereum-chain", "shape-blockchain-node", "shape-bank", "shape-trust-bank", "shape-payment-provider", "shape-credit-card", "shape-brokerage", "shape-exchange", "shape-atm", "shape-website", "shape-storefront", "shape-warehouse", "shape-online-shop", "shape-cdn-edge", "shape-api-gateway", "shape-auditor", "shape-regulator", "shape-notary", "shape-lawyer", "shape-trader", "shape-customer-service"];
36
+ declare const NODE_KINDS: readonly ["dyn-rect", "dyn-circle", "dyn-arc", "dyn-wave", "dyn-polygon", "actor", "function", "storage", "event", "card", "person", "user-group", "admin", "developer", "external-user", "database", "cache", "queue", "message-bus", "cloud", "cdn", "service", "api", "frontend", "backend", "webhook", "microservice", "signer", "oracle", "merkle-tree", "decision", "chart-pie", "chart-line", "chart-bar", "gantt-timeline", "mind-map", "funnel-stages", "quadrant-matrix", "tree-hierarchy", "journey-map", "shape-file", "shape-folder", "shape-cloud", "shape-cylinder", "shape-hexagon", "shape-diamond", "shape-stack", "shape-person", "shape-window", "shape-terminal", "shape-code-block", "shape-kanban-card", "shape-message-bubble", "shape-gear", "shape-server-rack", "shape-network-node", "shape-mobile-device", "shape-iot-sensor", "shape-robot-arm", "shape-satellite", "shape-smart-contract", "shape-blockchain-block", "shape-rpc-node", "shape-wallet", "shape-nft", "shape-token", "shape-blockchain", "shape-bitcoin-chain", "shape-ethereum-chain", "shape-blockchain-node", "shape-bank", "shape-trust-bank", "shape-payment-provider", "shape-credit-card", "shape-brokerage", "shape-exchange", "shape-atm", "shape-website", "shape-storefront", "shape-warehouse", "shape-online-shop", "shape-cdn-edge", "shape-api-gateway", "shape-auditor", "shape-regulator", "shape-notary", "shape-lawyer", "shape-trader", "shape-customer-service"];
37
37
  type NodeKind = (typeof NODE_KINDS)[number];
38
38
  type Side = "top" | "right" | "bottom" | "left";
39
39
  /**
@@ -209,8 +209,8 @@ type CdlNode = {
209
209
  */
210
210
  ganttData?: GanttTaskPayload[];
211
211
  /**
212
- * mind-map / mind-radial 専用の branch payload。 tree 構造を 1 node に格納し、
213
- * kinds/mind-map.tsx / mind-radial.tsx が中心 + 放射で SVG 描画する。
212
+ * mind-map 専用の branch payload。 tree 構造を 1 node に格納し、
213
+ * kinds/mind-map.tsx が中心 + 放射で SVG 描画する。
214
214
  */
215
215
  mindData?: MindBranchPayload;
216
216
  /** funnel-stages 専用の stage 配列 payload、 逆三角形 stage 描画に使う */
@@ -222,20 +222,41 @@ type CdlNode = {
222
222
  /** journey-map 専用の step 配列 payload、 感情曲線 + touchpoint 描画に使う */
223
223
  journeyData?: JourneyStepPayload[];
224
224
  };
225
+ /**
226
+ * 図表 payload の数値欄。 数を直接書くか、 `{signal}` を書いて状態から読む。
227
+ *
228
+ * 文字列を許すのは、 図表が状態を読む唯一の経路だから (`render/payload-binding.ts` が
229
+ * 描画直前に stateValues で解決する)。 **解決できない欄でも項目は落とさず**、 既定値で
230
+ * 描いて `data-cdl-unresolved="true"` を付ける (落とすと配列の長さが変わって図の形が
231
+ * 別物になる)。
232
+ *
233
+ * 既定値は欄ごとに違う。 数の欄は 0 だが、 `gantt-timeline` の終端は自分の始端
234
+ * (0 にすると帯が軸の左端まで伸びて別の図に見える)、 `quadrant-matrix` は左下、
235
+ * `journey-map` は「普通」。 いずれも `render/payload-binding.ts` が持つ。
236
+ */
237
+ type BoundNumber = number | string;
238
+ /**
239
+ * 図表 payload の語の欄。 決まった語を直接書くか、 `{signal}` を書いて状態から読む。
240
+ *
241
+ * `(string & {})` は「決まった語の候補を出しつつ任意の文字列も許す」 ための書き方。
242
+ * 素の `string` と union すると候補が消えるため、 補完を残す目的で挟んでいる。
243
+ */
244
+ type BoundEnum<T extends string> = T | (string & {});
225
245
  /** Chart 系 kind の 1 datum payload。 label + value + optional tone (color) */
226
246
  type ChartDatumPayload = {
227
247
  label: string;
228
- value: number;
248
+ /** 棒 / 折れ線 / 扇の大きさ。 `{signal}` で状態に追随する */
249
+ value: BoundNumber;
229
250
  tone?: Tone;
230
251
  };
231
252
  /** gantt-timeline kind の 1 task payload。 label + start/end + owner + dependsOn */
232
253
  type GanttTaskPayload = {
233
254
  id: string;
234
255
  title: string;
235
- /** 開始 index (0-based 相対位置) */
236
- startIdx: number;
237
- /** 終了 index (0-based 相対位置、 startIdx と同じなら 1 cell 幅) */
238
- endIdx: number;
256
+ /** 開始 index (0-based 相対位置) `{signal}` で状態に追随する */
257
+ startIdx: BoundNumber;
258
+ /** 終了 index (0-based 相対位置、 startIdx と同じなら 1 cell 幅) `{signal}` 可 */
259
+ endIdx: BoundNumber;
239
260
  /** 開始 label (例 "Q1" / "2026-01") */
240
261
  startLabel: string;
241
262
  /** 終了 label (例 "Q2" / "2026-03") */
@@ -244,7 +265,7 @@ type GanttTaskPayload = {
244
265
  dependsOn?: string;
245
266
  tone?: Tone;
246
267
  };
247
- /** mind-map / mind-radial kind の branch tree payload。 rootId + branch 配列 */
268
+ /** mind-map kind の branch tree payload。 rootId + branch 配列 */
248
269
  type MindBranchPayload = {
249
270
  rootId: string;
250
271
  rootTitle: string;
@@ -262,7 +283,8 @@ type MindBranchNode = {
262
283
  type FunnelStagePayload = {
263
284
  id: string;
264
285
  title: string;
265
- count: number;
286
+ /** 段の人数 / 件数。 `{signal}` で状態に追随する */
287
+ count: BoundNumber;
266
288
  subtitle?: string;
267
289
  };
268
290
  /** quadrant-matrix kind の 2 軸 + item 配列 payload */
@@ -283,11 +305,14 @@ type QuadrantPayload = {
283
305
  };
284
306
  items: QuadrantItemPayload[];
285
307
  };
308
+ /** quadrant-matrix の象限 4 種 */
309
+ type QuadrantKey = "topLeft" | "topRight" | "bottomLeft" | "bottomRight";
286
310
  /** quadrant-matrix の 1 item payload */
287
311
  type QuadrantItemPayload = {
288
312
  id: string;
289
313
  title: string;
290
- quadrant: "topLeft" | "topRight" | "bottomLeft" | "bottomRight";
314
+ /** どの象限に居るか。 `{signal}` を書くと項目が象限を移る */
315
+ quadrant: BoundEnum<QuadrantKey>;
291
316
  subtitle?: string;
292
317
  };
293
318
  /** tree-hierarchy kind の 1 node payload */
@@ -298,12 +323,14 @@ type TreeNodePayload = {
298
323
  subtitle?: string;
299
324
  eyebrow?: string;
300
325
  };
326
+ /** journey-map の気持ち 5 段階 */
327
+ type JourneyEmotion = "delighted" | "happy" | "neutral" | "frustrated" | "angry";
301
328
  /** journey-map kind の 1 step payload */
302
329
  type JourneyStepPayload = {
303
330
  id: string;
304
331
  title: string;
305
- /** emotion 5 段階 (delighted / happy / neutral / frustrated / angry) */
306
- emotion: "delighted" | "happy" | "neutral" | "frustrated" | "angry";
332
+ /** emotion 5 段階 (delighted / happy / neutral / frustrated / angry) `{signal}` で動く */
333
+ emotion: BoundEnum<JourneyEmotion>;
307
334
  touchpoint?: string;
308
335
  opportunity?: string;
309
336
  };
@@ -2128,6 +2155,13 @@ type CdlDiagram = {
2128
2155
  * 省略時は空 array 相当で下位互換。
2129
2156
  */
2130
2157
  readouts?: CdlReadout[];
2158
+ /**
2159
+ * 他の値から自動で決まる値。 段の補間中も毎 frame 決まり直す (`computeStateValues`)。
2160
+ *
2161
+ * 入力欄の式 (`formulas`) とは経路が別。 あちらは入力欄の signal を読む reactive graph に
2162
+ * 載り、 こちらは段が動かす状態を読む。 省略時は空 array 相当で下位互換。
2163
+ */
2164
+ derived?: CdlDerivedValue[];
2131
2165
  lanes: CdlLane[];
2132
2166
  nodes: CdlNode[];
2133
2167
  edges: CdlEdge[];
@@ -2136,6 +2170,21 @@ type CdlDiagram = {
2136
2170
  /** v0.5+ ... canvas 全体仕様 (viewport.width / height で全体 size を author が宣言) */
2137
2171
  viewport?: CdlViewport;
2138
2172
  };
2173
+ /**
2174
+ * 他の値から自動で決まる値の宣言。
2175
+ *
2176
+ * 式は `{名前}` で他の値 (`states` / 他の `derived`) を読み、 四則 / 括弧 / 比較 /
2177
+ * `min` / `max` が書ける。 比較の結果は真 = 1 / 偽 = 0 の数として載る。
2178
+ *
2179
+ * 書いた順は結果に影響しない (参照から順序を解く)。 解けない値 (輪 / 存在しない名前 /
2180
+ * 0 除算 / 数でない値) はその値だけ止まり、 残りは動く。
2181
+ */
2182
+ type CdlDerivedValue = {
2183
+ /** 値の名前。 図からは `{名前}` で読む */
2184
+ id: string;
2185
+ /** 式。 例 `"{inflow} - {done}"` */
2186
+ expression: string;
2187
+ };
2139
2188
  /** Canvas 全体仕様 (v0.5+ syntax `viewport: { width, height, scale, gap, laneGap, nodeGap, labelMargin }`)。
2140
2189
  * 未指定時は layout が node 配置から自動算出する従来挙動。
2141
2190
  *
@@ -2279,6 +2328,8 @@ type LaidDiagram = {
2279
2328
  edges: LaidEdge[];
2280
2329
  states: CdlState[];
2281
2330
  phases: CdlPhase[];
2331
+ /** 他の値から自動で決まる値 (`CdlDiagram.derived` をそのまま引き継ぐ) */
2332
+ derived?: CdlDerivedValue[];
2282
2333
  /**
2283
2334
  * キャンバス全要素の bounding box list。
2284
2335
  * engine が「ここに何があるか」 を全部把握、 衝突検証 / debug 表示 / validation で使う。
@@ -2446,4 +2497,4 @@ type CdlDiagramViewProps = {
2446
2497
  };
2447
2498
  declare function CdlDiagramView({ diagram, laid: laidProp, hideHeader, hideMiniPhaseIndicator, focusPhaseId, debug, emitGeometryWarn, headingLevel, interactiveHandlers }: CdlDiagramViewProps): JSX.Element;
2448
2499
 
2449
- export { computed as A, type BBox as B, type CdlDiagram as C, createInteractiveSignalsAccessor as D, type EdgeStyle as E, createScrollProgressHandle as F, createScrollProgressSignals as G, hasInteractivePrimitives as H, type InteractiveHandlerMap as I, signal as J, type LaidDiagram as L, type NodeKind as N, type Signal as S, type Tone as T, type CdlInput as a, type CdlLane as b, type CdlNode as c, type Side as d, type CdlState as e, type CdlEventTarget as f, type Computed as g, type CdlScrollTrigger as h, type CdlDiagramViewProps as i, type LaidEdge as j, type LaidNode as k, type CdlEdge as l, CdlDiagramView as m, type CdlEventBinding as n, type CdlEventKind as o, type CdlFormula as p, type CdlPhase as q, type InteractiveSignalsAccessor as r, type LaidLane as s, NODE_KINDS as t, type ScrollProgressHandle as u, TONES as v, computeEventBindingKey as w, computeFormulaKey as x, computeInputSignalKey as y, computeScrollTriggerKey as z };
2500
+ export { computeScrollTriggerKey as A, type BBox as B, type CdlDiagram as C, computed as D, type EdgeStyle as E, createInteractiveSignalsAccessor as F, createScrollProgressHandle as G, createScrollProgressSignals as H, type InteractiveHandlerMap as I, type JourneyEmotion as J, hasInteractivePrimitives as K, type LaidDiagram as L, signal as M, type NodeKind as N, type QuadrantKey as Q, type Signal as S, type Tone as T, type CdlInput as a, type CdlLane as b, type CdlNode as c, type Side as d, type CdlState as e, type CdlEventTarget as f, type Computed as g, type CdlDerivedValue as h, type CdlScrollTrigger as i, type CdlDiagramViewProps as j, type LaidEdge as k, type LaidNode as l, type CdlEdge as m, CdlDiagramView as n, type CdlEventBinding as o, type CdlEventKind as p, type CdlFormula as q, type CdlPhase as r, type InteractiveSignalsAccessor as s, type LaidLane as t, NODE_KINDS as u, type ScrollProgressHandle as v, TONES as w, computeEventBindingKey as x, computeFormulaKey as y, computeInputSignalKey as z };
@@ -33,7 +33,7 @@ declare function computed<T>(fn: () => T): Computed<T>;
33
33
  declare const TONES: readonly ["accent", "teal", "success", "error", "warning", "info"];
34
34
  type Tone = (typeof TONES)[number];
35
35
  /** 描画できる箱の種類。 検査・記法一覧・型はこの 1 つの列を出所にする。 */
36
- declare const NODE_KINDS: readonly ["dyn-rect", "dyn-circle", "dyn-arc", "dyn-wave", "dyn-polygon", "actor", "function", "storage", "event", "card", "person", "user-group", "admin", "developer", "external-user", "database", "cache", "queue", "message-bus", "cloud", "cdn", "service", "api", "frontend", "backend", "webhook", "microservice", "signer", "oracle", "merkle-tree", "decision", "chart-pie", "chart-line", "chart-bar", "gantt-timeline", "mind-map", "mind-radial", "funnel-stages", "quadrant-matrix", "tree-hierarchy", "journey-map", "shape-file", "shape-folder", "shape-cloud", "shape-cylinder", "shape-hexagon", "shape-diamond", "shape-stack", "shape-person", "shape-window", "shape-terminal", "shape-code-block", "shape-kanban-card", "shape-message-bubble", "shape-gear", "shape-server-rack", "shape-network-node", "shape-mobile-device", "shape-iot-sensor", "shape-robot-arm", "shape-satellite", "shape-smart-contract", "shape-blockchain-block", "shape-rpc-node", "shape-wallet", "shape-nft", "shape-token", "shape-blockchain", "shape-bitcoin-chain", "shape-ethereum-chain", "shape-blockchain-node", "shape-bank", "shape-trust-bank", "shape-payment-provider", "shape-credit-card", "shape-brokerage", "shape-exchange", "shape-atm", "shape-website", "shape-storefront", "shape-warehouse", "shape-online-shop", "shape-cdn-edge", "shape-api-gateway", "shape-auditor", "shape-regulator", "shape-notary", "shape-lawyer", "shape-trader", "shape-customer-service"];
36
+ declare const NODE_KINDS: readonly ["dyn-rect", "dyn-circle", "dyn-arc", "dyn-wave", "dyn-polygon", "actor", "function", "storage", "event", "card", "person", "user-group", "admin", "developer", "external-user", "database", "cache", "queue", "message-bus", "cloud", "cdn", "service", "api", "frontend", "backend", "webhook", "microservice", "signer", "oracle", "merkle-tree", "decision", "chart-pie", "chart-line", "chart-bar", "gantt-timeline", "mind-map", "funnel-stages", "quadrant-matrix", "tree-hierarchy", "journey-map", "shape-file", "shape-folder", "shape-cloud", "shape-cylinder", "shape-hexagon", "shape-diamond", "shape-stack", "shape-person", "shape-window", "shape-terminal", "shape-code-block", "shape-kanban-card", "shape-message-bubble", "shape-gear", "shape-server-rack", "shape-network-node", "shape-mobile-device", "shape-iot-sensor", "shape-robot-arm", "shape-satellite", "shape-smart-contract", "shape-blockchain-block", "shape-rpc-node", "shape-wallet", "shape-nft", "shape-token", "shape-blockchain", "shape-bitcoin-chain", "shape-ethereum-chain", "shape-blockchain-node", "shape-bank", "shape-trust-bank", "shape-payment-provider", "shape-credit-card", "shape-brokerage", "shape-exchange", "shape-atm", "shape-website", "shape-storefront", "shape-warehouse", "shape-online-shop", "shape-cdn-edge", "shape-api-gateway", "shape-auditor", "shape-regulator", "shape-notary", "shape-lawyer", "shape-trader", "shape-customer-service"];
37
37
  type NodeKind = (typeof NODE_KINDS)[number];
38
38
  type Side = "top" | "right" | "bottom" | "left";
39
39
  /**
@@ -209,8 +209,8 @@ type CdlNode = {
209
209
  */
210
210
  ganttData?: GanttTaskPayload[];
211
211
  /**
212
- * mind-map / mind-radial 専用の branch payload。 tree 構造を 1 node に格納し、
213
- * kinds/mind-map.tsx / mind-radial.tsx が中心 + 放射で SVG 描画する。
212
+ * mind-map 専用の branch payload。 tree 構造を 1 node に格納し、
213
+ * kinds/mind-map.tsx が中心 + 放射で SVG 描画する。
214
214
  */
215
215
  mindData?: MindBranchPayload;
216
216
  /** funnel-stages 専用の stage 配列 payload、 逆三角形 stage 描画に使う */
@@ -222,20 +222,41 @@ type CdlNode = {
222
222
  /** journey-map 専用の step 配列 payload、 感情曲線 + touchpoint 描画に使う */
223
223
  journeyData?: JourneyStepPayload[];
224
224
  };
225
+ /**
226
+ * 図表 payload の数値欄。 数を直接書くか、 `{signal}` を書いて状態から読む。
227
+ *
228
+ * 文字列を許すのは、 図表が状態を読む唯一の経路だから (`render/payload-binding.ts` が
229
+ * 描画直前に stateValues で解決する)。 **解決できない欄でも項目は落とさず**、 既定値で
230
+ * 描いて `data-cdl-unresolved="true"` を付ける (落とすと配列の長さが変わって図の形が
231
+ * 別物になる)。
232
+ *
233
+ * 既定値は欄ごとに違う。 数の欄は 0 だが、 `gantt-timeline` の終端は自分の始端
234
+ * (0 にすると帯が軸の左端まで伸びて別の図に見える)、 `quadrant-matrix` は左下、
235
+ * `journey-map` は「普通」。 いずれも `render/payload-binding.ts` が持つ。
236
+ */
237
+ type BoundNumber = number | string;
238
+ /**
239
+ * 図表 payload の語の欄。 決まった語を直接書くか、 `{signal}` を書いて状態から読む。
240
+ *
241
+ * `(string & {})` は「決まった語の候補を出しつつ任意の文字列も許す」 ための書き方。
242
+ * 素の `string` と union すると候補が消えるため、 補完を残す目的で挟んでいる。
243
+ */
244
+ type BoundEnum<T extends string> = T | (string & {});
225
245
  /** Chart 系 kind の 1 datum payload。 label + value + optional tone (color) */
226
246
  type ChartDatumPayload = {
227
247
  label: string;
228
- value: number;
248
+ /** 棒 / 折れ線 / 扇の大きさ。 `{signal}` で状態に追随する */
249
+ value: BoundNumber;
229
250
  tone?: Tone;
230
251
  };
231
252
  /** gantt-timeline kind の 1 task payload。 label + start/end + owner + dependsOn */
232
253
  type GanttTaskPayload = {
233
254
  id: string;
234
255
  title: string;
235
- /** 開始 index (0-based 相対位置) */
236
- startIdx: number;
237
- /** 終了 index (0-based 相対位置、 startIdx と同じなら 1 cell 幅) */
238
- endIdx: number;
256
+ /** 開始 index (0-based 相対位置) `{signal}` で状態に追随する */
257
+ startIdx: BoundNumber;
258
+ /** 終了 index (0-based 相対位置、 startIdx と同じなら 1 cell 幅) `{signal}` 可 */
259
+ endIdx: BoundNumber;
239
260
  /** 開始 label (例 "Q1" / "2026-01") */
240
261
  startLabel: string;
241
262
  /** 終了 label (例 "Q2" / "2026-03") */
@@ -244,7 +265,7 @@ type GanttTaskPayload = {
244
265
  dependsOn?: string;
245
266
  tone?: Tone;
246
267
  };
247
- /** mind-map / mind-radial kind の branch tree payload。 rootId + branch 配列 */
268
+ /** mind-map kind の branch tree payload。 rootId + branch 配列 */
248
269
  type MindBranchPayload = {
249
270
  rootId: string;
250
271
  rootTitle: string;
@@ -262,7 +283,8 @@ type MindBranchNode = {
262
283
  type FunnelStagePayload = {
263
284
  id: string;
264
285
  title: string;
265
- count: number;
286
+ /** 段の人数 / 件数。 `{signal}` で状態に追随する */
287
+ count: BoundNumber;
266
288
  subtitle?: string;
267
289
  };
268
290
  /** quadrant-matrix kind の 2 軸 + item 配列 payload */
@@ -283,11 +305,14 @@ type QuadrantPayload = {
283
305
  };
284
306
  items: QuadrantItemPayload[];
285
307
  };
308
+ /** quadrant-matrix の象限 4 種 */
309
+ type QuadrantKey = "topLeft" | "topRight" | "bottomLeft" | "bottomRight";
286
310
  /** quadrant-matrix の 1 item payload */
287
311
  type QuadrantItemPayload = {
288
312
  id: string;
289
313
  title: string;
290
- quadrant: "topLeft" | "topRight" | "bottomLeft" | "bottomRight";
314
+ /** どの象限に居るか。 `{signal}` を書くと項目が象限を移る */
315
+ quadrant: BoundEnum<QuadrantKey>;
291
316
  subtitle?: string;
292
317
  };
293
318
  /** tree-hierarchy kind の 1 node payload */
@@ -298,12 +323,14 @@ type TreeNodePayload = {
298
323
  subtitle?: string;
299
324
  eyebrow?: string;
300
325
  };
326
+ /** journey-map の気持ち 5 段階 */
327
+ type JourneyEmotion = "delighted" | "happy" | "neutral" | "frustrated" | "angry";
301
328
  /** journey-map kind の 1 step payload */
302
329
  type JourneyStepPayload = {
303
330
  id: string;
304
331
  title: string;
305
- /** emotion 5 段階 (delighted / happy / neutral / frustrated / angry) */
306
- emotion: "delighted" | "happy" | "neutral" | "frustrated" | "angry";
332
+ /** emotion 5 段階 (delighted / happy / neutral / frustrated / angry) `{signal}` で動く */
333
+ emotion: BoundEnum<JourneyEmotion>;
307
334
  touchpoint?: string;
308
335
  opportunity?: string;
309
336
  };
@@ -2128,6 +2155,13 @@ type CdlDiagram = {
2128
2155
  * 省略時は空 array 相当で下位互換。
2129
2156
  */
2130
2157
  readouts?: CdlReadout[];
2158
+ /**
2159
+ * 他の値から自動で決まる値。 段の補間中も毎 frame 決まり直す (`computeStateValues`)。
2160
+ *
2161
+ * 入力欄の式 (`formulas`) とは経路が別。 あちらは入力欄の signal を読む reactive graph に
2162
+ * 載り、 こちらは段が動かす状態を読む。 省略時は空 array 相当で下位互換。
2163
+ */
2164
+ derived?: CdlDerivedValue[];
2131
2165
  lanes: CdlLane[];
2132
2166
  nodes: CdlNode[];
2133
2167
  edges: CdlEdge[];
@@ -2136,6 +2170,21 @@ type CdlDiagram = {
2136
2170
  /** v0.5+ ... canvas 全体仕様 (viewport.width / height で全体 size を author が宣言) */
2137
2171
  viewport?: CdlViewport;
2138
2172
  };
2173
+ /**
2174
+ * 他の値から自動で決まる値の宣言。
2175
+ *
2176
+ * 式は `{名前}` で他の値 (`states` / 他の `derived`) を読み、 四則 / 括弧 / 比較 /
2177
+ * `min` / `max` が書ける。 比較の結果は真 = 1 / 偽 = 0 の数として載る。
2178
+ *
2179
+ * 書いた順は結果に影響しない (参照から順序を解く)。 解けない値 (輪 / 存在しない名前 /
2180
+ * 0 除算 / 数でない値) はその値だけ止まり、 残りは動く。
2181
+ */
2182
+ type CdlDerivedValue = {
2183
+ /** 値の名前。 図からは `{名前}` で読む */
2184
+ id: string;
2185
+ /** 式。 例 `"{inflow} - {done}"` */
2186
+ expression: string;
2187
+ };
2139
2188
  /** Canvas 全体仕様 (v0.5+ syntax `viewport: { width, height, scale, gap, laneGap, nodeGap, labelMargin }`)。
2140
2189
  * 未指定時は layout が node 配置から自動算出する従来挙動。
2141
2190
  *
@@ -2279,6 +2328,8 @@ type LaidDiagram = {
2279
2328
  edges: LaidEdge[];
2280
2329
  states: CdlState[];
2281
2330
  phases: CdlPhase[];
2331
+ /** 他の値から自動で決まる値 (`CdlDiagram.derived` をそのまま引き継ぐ) */
2332
+ derived?: CdlDerivedValue[];
2282
2333
  /**
2283
2334
  * キャンバス全要素の bounding box list。
2284
2335
  * engine が「ここに何があるか」 を全部把握、 衝突検証 / debug 表示 / validation で使う。
@@ -2446,4 +2497,4 @@ type CdlDiagramViewProps = {
2446
2497
  };
2447
2498
  declare function CdlDiagramView({ diagram, laid: laidProp, hideHeader, hideMiniPhaseIndicator, focusPhaseId, debug, emitGeometryWarn, headingLevel, interactiveHandlers }: CdlDiagramViewProps): JSX.Element;
2448
2499
 
2449
- export { computed as A, type BBox as B, type CdlDiagram as C, createInteractiveSignalsAccessor as D, type EdgeStyle as E, createScrollProgressHandle as F, createScrollProgressSignals as G, hasInteractivePrimitives as H, type InteractiveHandlerMap as I, signal as J, type LaidDiagram as L, type NodeKind as N, type Signal as S, type Tone as T, type CdlInput as a, type CdlLane as b, type CdlNode as c, type Side as d, type CdlState as e, type CdlEventTarget as f, type Computed as g, type CdlScrollTrigger as h, type CdlDiagramViewProps as i, type LaidEdge as j, type LaidNode as k, type CdlEdge as l, CdlDiagramView as m, type CdlEventBinding as n, type CdlEventKind as o, type CdlFormula as p, type CdlPhase as q, type InteractiveSignalsAccessor as r, type LaidLane as s, NODE_KINDS as t, type ScrollProgressHandle as u, TONES as v, computeEventBindingKey as w, computeFormulaKey as x, computeInputSignalKey as y, computeScrollTriggerKey as z };
2500
+ export { computeScrollTriggerKey as A, type BBox as B, type CdlDiagram as C, computed as D, type EdgeStyle as E, createInteractiveSignalsAccessor as F, createScrollProgressHandle as G, createScrollProgressSignals as H, type InteractiveHandlerMap as I, type JourneyEmotion as J, hasInteractivePrimitives as K, type LaidDiagram as L, signal as M, type NodeKind as N, type QuadrantKey as Q, type Signal as S, type Tone as T, type CdlInput as a, type CdlLane as b, type CdlNode as c, type Side as d, type CdlState as e, type CdlEventTarget as f, type Computed as g, type CdlDerivedValue as h, type CdlScrollTrigger as i, type CdlDiagramViewProps as j, type LaidEdge as k, type LaidNode as l, type CdlEdge as m, CdlDiagramView as n, type CdlEventBinding as o, type CdlEventKind as p, type CdlFormula as q, type CdlPhase as r, type InteractiveSignalsAccessor as s, type LaidLane as t, NODE_KINDS as u, type ScrollProgressHandle as v, TONES as w, computeEventBindingKey as x, computeFormulaKey as y, computeInputSignalKey as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cardenelabs/cdl",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "description": "CDL (Chainome Diagram Language). Mermaid-like declarative DSL that compiles to animated SVG diagrams. Built for blockchain / Solidity flows, generic enough for sequence / flow / state / ER / topology diagrams.",
5
5
  "license": "MIT",
6
6
  "author": "cardene777",
@@ -1,4 +1,5 @@
1
1
  import { ALLOWED_MATH_FNS, type BinaryOp, type FormulaAst, type MathFnName } from "./ast";
2
+ import { isValidTemplateName } from "../template-name";
2
3
 
3
4
  /**
4
5
  * formula expression parser (CAR #244)。
@@ -71,6 +72,30 @@ function tokenize(src: string): Token[] {
71
72
  tokens.push({ kind: "num", value: num, pos: start });
72
73
  continue;
73
74
  }
75
+ // 波括弧で囲んだ名前 (`{waiting}`)。 図の他の場所と同じ書き方で値を参照する形。
76
+ // 中身は前後の空白を落としてから名前として扱う。
77
+ if (c === "{") {
78
+ const start = i;
79
+ const close = src.indexOf("}", i + 1);
80
+ if (close === -1) {
81
+ throw new Error(`unclosed "{" at position ${start} (write a name like {waiting})`);
82
+ }
83
+ const name = src.slice(i + 1, close).trim();
84
+ if (name === "") {
85
+ throw new Error(`empty name "{}" at position ${start}`);
86
+ }
87
+ // 名前として使える文字は engine 全体で 1 つの定義に従う (`isValidTemplateName`)。
88
+ // ここだけ緩めると、 式では読めるのに図のどこからも読めない名前ができる
89
+ // (`{v.done}` は `.` が accessor の始まりなので読み手が拾えない)。
90
+ if (!isValidTemplateName(name)) {
91
+ throw new Error(
92
+ `invalid name "{${name}}" at position ${start} (空白 / { } / . / [ ] は名前に使えない)`,
93
+ );
94
+ }
95
+ tokens.push({ kind: "id", name, pos: start });
96
+ i = close + 1;
97
+ continue;
98
+ }
74
99
  // identifier / Math.<fn>
75
100
  if ((c >= "a" && c <= "z") || (c >= "A" && c <= "Z") || c === "_" || c === "$") {
76
101
  const start = i;
@@ -115,6 +140,16 @@ function tokenize(src: string): Token[] {
115
140
  tokens.push({ kind: "math", fn: `Math.${fnName}` as MathFnName, pos: start });
116
141
  continue;
117
142
  }
143
+ // 許可済 Math 関数は prefix 無しでも書ける (`min(a, b)`)。 直後が `(` の時だけ関数として
144
+ // 読むので、 同名の値を参照する形 (`min * 2`) は今までどおり identifier のまま。
145
+ //
146
+ // 6 つ全てを対象にするのは、 2 つだけ許すと「どれが prefix 無しで書けるか」 を
147
+ // 覚える必要が出るため。 呼び出し側が式に書ける範囲を狭めたい場合は、 呼び出し側で
148
+ // 自分の記法として絞る。
149
+ if (ALLOWED_MATH_FNS.has(name) && src[i] === "(") {
150
+ tokens.push({ kind: "math", fn: `Math.${name}` as MathFnName, pos: start });
151
+ continue;
152
+ }
118
153
  // property access on other identifiers is禁止
119
154
  if (src[i] === ".") {
120
155
  throw new Error(
package/src/index.ts CHANGED
@@ -22,6 +22,13 @@ export {
22
22
  export type { FormulaAst, FormulaContext, BinaryOp, MathFnName } from "./formula";
23
23
  export type { CdlFormula } from "./types";
24
24
 
25
+ // 他の値から自動で決まる値 (cdl#453)。 入力欄の式 (formulas) と違い、 段が動かす状態を読み、
26
+ // `computeStateValues` の中で毎 frame 解かれる。 呼び出し側が止まった値を自分で伝えたい時だけ
27
+ // `applyDerivedValues` を直接使う。
28
+ export { applyDerivedValues, withDerivedValues } from "./render/derived-values";
29
+ export type { DerivedResult, DerivedNotice, DerivedNoticeKind } from "./render/derived-values";
30
+ export type { CdlDerivedValue } from "./types";
31
+
25
32
  // scroll-driven trigger (CAR #245) — builder chain `.animation.scroll(id, opts)` で scroll trigger を
26
33
  // 追加、 createScrollProgressSignals(diagram) で { progress signal, attach(element), detach } を得て
27
34
  // DOM element に IntersectionObserver 経由で bind、 signal.value で 0..1 progress を read。
@@ -186,7 +193,7 @@ export {
186
193
  LABEL_SIZE_TOLERANCE_WORLD,
187
194
  bboxDiffWorld,
188
195
  } from "./layout/predict-bbox";
189
- export { swimlane, flow, sequence, topology, er, stateMachine, infrastructure, classDiagram, tree, userJourney, mindMap, mindMapRadial, funnel, quadrant, chart, gantt, flowchart, network, stateMachine2 } from "./presets";
196
+ export { swimlane, flow, sequence, topology, er, stateMachine, infrastructure, classDiagram, tree, userJourney, mindMap, funnel, quadrant, chart, gantt, flowchart, network, stateMachine2 } from "./presets";
190
197
  // Text DSL は @cardenelabs/dragon に切出済 (cdl は engine only)
191
198
  export type {
192
199
  SwimlanePreset,
@@ -229,9 +236,6 @@ export type {
229
236
  MindMapPreset,
230
237
  MindMapBuilder,
231
238
  MindBranch,
232
- MindMapRadialPreset,
233
- MindMapRadialBuilder,
234
- MindMapRadialBranch,
235
239
  FunnelPreset,
236
240
  FunnelBuilder,
237
241
  FunnelStage,
@@ -282,3 +286,19 @@ export type {
282
286
  // 値としての tone / kind 一覧。 downstream (dragon の記法一覧 / parser 等) が
283
287
  // 受理できる値の集合を手書きせずに済ませるため公開する。
284
288
  export { TONES, NODE_KINDS } from "./types";
289
+
290
+ /**
291
+ * 段ごとの実効値を出す 2 つ。 downstream が「この段で箱に何の字が出るか」 を
292
+ * engine と同じ経路で確かめるために公開する。
293
+ *
294
+ * 公開しない間、 consumer は `src/render/utils` を相対 path で直接読むしかなかった
295
+ * (`exports` は `.` と `./react` しか開いていないため package 経由では届かない)。
296
+ * dragon の見本の検査が実際にそうしており、 **隣に cdl を checkout していないと
297
+ * 型検査も test も通らない**状態になっていた (dragon#1166)。
298
+ *
299
+ * `computeStateValues` は段と進みから状態の値を組み立てる。 `set` を順に当ててから
300
+ * `tween` が上書きする順序を持つので、 呼ぶ側が組み直すと描画とずれる。
301
+ * `interpolate` は `{名前}` を値で置き換える。 名前に使える字は `template-name.ts` が
302
+ * 決めており、 ここを自前で書くと「読めるが書けない」 名前ができる。
303
+ */
304
+ export { computeStateValues, interpolate } from "./render/utils";
@@ -1,14 +1,24 @@
1
1
  import type { JSX } from "react";
2
2
  import type { LaidNode } from "../types";
3
+ import { resolveChartData } from "../render/payload-binding";
3
4
 
4
5
  /**
5
6
  * chart-bar kind ... 1 node に datum 配列 (chartData) を保持し、 SVG rect で棒グラフ描画する。
6
7
  * layout は node bbox 内側の canvas に縦棒を等間隔配置、 縦軸 = value 0-max、 横軸 = datum label。
8
+ * 各 value は数でも `{signal}` でもよく、 後者は状態が動くたび棒の高さが追随する。
7
9
  */
8
- export function ChartBarNode({ node, active }: { node: LaidNode; active: boolean }): JSX.Element {
10
+ export function ChartBarNode({
11
+ node,
12
+ active,
13
+ stateValues = {},
14
+ }: {
15
+ node: LaidNode;
16
+ active: boolean;
17
+ stateValues?: Record<string, string>;
18
+ }): JSX.Element {
9
19
  const x0 = node.cx - node.w / 2;
10
20
  const y0 = node.cy - node.h / 2;
11
- const data = node.chartData ?? [];
21
+ const data = resolveChartData(node.chartData ?? [], stateValues);
12
22
 
13
23
  const PAD_TOP = 20;
14
24
  const PAD_RIGHT = 24;
@@ -87,6 +97,7 @@ export function ChartBarNode({ node, active }: { node: LaidNode; active: boolean
87
97
  <g key={`bar-${idx}`}>
88
98
  <rect
89
99
  data-cdl-role="chart-bar"
100
+ data-cdl-unresolved={d.unresolved ? "true" : undefined}
90
101
  x={bx}
91
102
  y={by}
92
103
  width={barW}
@@ -1,15 +1,25 @@
1
1
  import type { JSX } from "react";
2
2
  import type { LaidNode } from "../types";
3
+ import { resolveChartData } from "../render/payload-binding";
3
4
 
4
5
  /**
5
6
  * chart-line kind ... 1 node に datum 配列 (chartData) を保持し、 SVG polyline で折れ線描画する。
6
7
  * layout は node bbox (node.w × node.h) 内側に padding (top/right/bottom/left) を確保し、
7
8
  * その内側 rect を chart canvas とみなす。 縦軸 = value min-max、 横軸 = datum index。
9
+ * 各 value は数でも `{signal}` でもよく、 後者は状態が動くたび折れ線が追随する。
8
10
  */
9
- export function ChartLineNode({ node, active }: { node: LaidNode; active: boolean }): JSX.Element {
11
+ export function ChartLineNode({
12
+ node,
13
+ active,
14
+ stateValues = {},
15
+ }: {
16
+ node: LaidNode;
17
+ active: boolean;
18
+ stateValues?: Record<string, string>;
19
+ }): JSX.Element {
10
20
  const x0 = node.cx - node.w / 2;
11
21
  const y0 = node.cy - node.h / 2;
12
- const data = node.chartData ?? [];
22
+ const data = resolveChartData(node.chartData ?? [], stateValues);
13
23
 
14
24
  const PAD_TOP = 36;
15
25
  const PAD_RIGHT = 40;
@@ -120,7 +130,7 @@ export function ChartLineNode({ node, active }: { node: LaidNode; active: boolea
120
130
  const labelAbove = !isValley;
121
131
  const labelY = labelAbove ? cy - 12 : cy + 18;
122
132
  return (
123
- <g key={`pt-${idx}`}>
133
+ <g key={`pt-${idx}`} data-cdl-unresolved={d.unresolved ? "true" : undefined}>
124
134
  <circle
125
135
  cx={cx}
126
136
  cy={cy}
@@ -1,15 +1,25 @@
1
1
  import type { JSX } from "react";
2
2
  import type { LaidNode, Tone } from "../types";
3
3
  import { TONE } from "../render/tone";
4
+ import { resolveChartData } from "../render/payload-binding";
4
5
 
5
6
  /**
6
7
  * chart-pie kind ... 1 node に datum 配列 (chartData) を保持し、 SVG path arc で円グラフ描画する。
7
8
  * layout は node bbox 内側の左半分に円を配置、 右半分に凡例 (label + %) を stack 表示する。
9
+ * 各 value は数でも `{signal}` でもよく、 後者は状態が動くたび扇の角度が追随する。
8
10
  */
9
- export function ChartPieNode({ node, active }: { node: LaidNode; active: boolean }): JSX.Element {
11
+ export function ChartPieNode({
12
+ node,
13
+ active,
14
+ stateValues = {},
15
+ }: {
16
+ node: LaidNode;
17
+ active: boolean;
18
+ stateValues?: Record<string, string>;
19
+ }): JSX.Element {
10
20
  const x0 = node.cx - node.w / 2;
11
21
  const y0 = node.cy - node.h / 2;
12
- const data = node.chartData ?? [];
22
+ const data = resolveChartData(node.chartData ?? [], stateValues);
13
23
  const total = data.reduce((acc, d) => acc + d.value, 0) || 1;
14
24
 
15
25
  const PAD_TOP = 20;
@@ -58,6 +68,7 @@ export function ChartPieNode({ node, active }: { node: LaidNode; active: boolean
58
68
  <path
59
69
  key={`slice-${idx}`}
60
70
  data-cdl-role="chart-pie-slice"
71
+ data-cdl-unresolved={s.d.unresolved ? "true" : undefined}
61
72
  d={s.path}
62
73
  fill={sliceColor(s.d.tone, idx)}
63
74
  fillOpacity={active ? 0.95 : 0.85}
@@ -1,6 +1,6 @@
1
1
  import { useEffect, useState, type JSX } from "react";
2
2
  import type { CdlDynShape, LaidNode } from "../types";
3
- import { resolveNumericAttr } from "../render/utils";
3
+ import { interpolate, resolveNumericAttr } from "../render/utils";
4
4
 
5
5
  /**
6
6
  * time-driven tick hook = ~30fps throttle で performance.now() を state に反映。
@@ -390,5 +390,6 @@ function interpolateColor(
390
390
  fallback: string,
391
391
  ): string {
392
392
  if (!template) return fallback;
393
- return template.replace(/\{(\w+)\}/g, (_, id: string) => stateValues[id] ?? `{${id}}`);
393
+ // engine 共通の経路で解決する。 独自に書くと名前の字種がここだけずれる
394
+ return interpolate(template, stateValues);
394
395
  }
@@ -1,17 +1,27 @@
1
1
  import type { JSX } from "react";
2
2
  import type { LaidNode, Tone } from "../types";
3
3
  import { TONE } from "../render/tone";
4
+ import { resolveFunnelData } from "../render/payload-binding";
4
5
 
5
6
  /**
6
7
  * funnel-stages kind ... 1 node に stage 配列 (funnelData) を保持し、 逆三角形 funnel を SVG 描画する。
7
8
  * layout = 全体で自然な逆三角形になるように上端 100% → 下端 40% で線形補間、
8
9
  * 各 stage は同じ縦幅 + 上下辺の幅を段階的に減らして「じょうご」 形状で表現。
9
10
  * count / drop rate は polygon の右横 (side legend) に配置して図形と数値を分離。
11
+ * count は数でも `{signal}` でもよく、 後者は状態が動くたび件数と離脱率が追随する。
10
12
  */
11
- export function FunnelNode({ node, active }: { node: LaidNode; active: boolean }): JSX.Element {
13
+ export function FunnelNode({
14
+ node,
15
+ active,
16
+ stateValues = {},
17
+ }: {
18
+ node: LaidNode;
19
+ active: boolean;
20
+ stateValues?: Record<string, string>;
21
+ }): JSX.Element {
12
22
  const x0 = node.cx - node.w / 2;
13
23
  const y0 = node.cy - node.h / 2;
14
- const stages = node.funnelData ?? [];
24
+ const stages = resolveFunnelData(node.funnelData ?? [], stateValues);
15
25
 
16
26
  const PAD_TOP = 32;
17
27
  const PAD_RIGHT = 24;
@@ -48,7 +58,9 @@ export function FunnelNode({ node, active }: { node: LaidNode; active: boolean }
48
58
 
49
59
  {stages.map((s, idx) => {
50
60
  const prev = idx > 0 ? stages[idx - 1]! : null;
51
- const dropRate = prev ? ((prev.count - s.count) / prev.count) * 100 : 0;
61
+ // 前段が 0 件なら離脱率は決まらない (0 で割ることになる) 静止画では起きなかったが、
62
+ // count が状態を読むようになると動き始めの 0 で必ず通る。
63
+ const dropRate = prev && prev.count > 0 ? ((prev.count - s.count) / prev.count) * 100 : 0;
52
64
  const y = PAD_TOP + idx * (stageH + gap);
53
65
  const topW = widthAtStep(idx);
54
66
  const bottomW = widthAtStep(idx + 1);
@@ -62,6 +74,7 @@ export function FunnelNode({ node, active }: { node: LaidNode; active: boolean }
62
74
  <g key={`stage-${s.id}`}>
63
75
  <polygon
64
76
  data-cdl-role="funnel-stage"
77
+ data-cdl-unresolved={s.unresolved ? "true" : undefined}
65
78
  points={points}
66
79
  fill={tone}
67
80
  fillOpacity={0.9}