@cardenelabs/cdl 0.124.0 → 0.125.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.
@@ -125,8 +125,16 @@ type Tone = (typeof TONES)[number];
125
125
  * 食い違うため、図単位で測り方を揃えられるようにする。
126
126
  */
127
127
  type CdlTextMetrics = "proportional" | "monospace";
128
+ /** 凡例に描ける印。 配置・描画・検査はこの 1 つの列を出所にする。 */
129
+ declare const LEGEND_MARKS: readonly ["diamond", "filled-circle", "double-circle", "dotted-line", "solid-line", "curved-line", "rounded-label", "numbered-circle", "station", "arrow"];
130
+ type LegendMark = (typeof LEGEND_MARKS)[number];
131
+ /** 図の下へ置く凡例の 1 項目。 */
132
+ type CdlLegendItem = {
133
+ mark: LegendMark;
134
+ text: string;
135
+ };
128
136
  /** 描画できる箱の種類。 検査・記法一覧・型はこの 1 つの列を出所にする。 */
129
- 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", "chart-gauge", "chart-radial", "chart-stat", "chart-waffle", "chart-stacked-bar", "chart-slope", "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", "mark-start", "mark-end", "station", "sequence-board"];
137
+ 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", "chart-gauge", "chart-radial", "chart-stat", "chart-waffle", "chart-stacked-bar", "chart-slope", "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", "mark-start", "mark-end", "station", "timeline-number", "sequence-board"];
130
138
  type NodeKind = (typeof NODE_KINDS)[number];
131
139
  type Side = "top" | "right" | "bottom" | "left";
132
140
  /**
@@ -148,7 +156,9 @@ type EdgeStyle = "solid" | "dotted-flow" | "dashed";
148
156
  * - `"back-detour"` ... 箱の上を回って前の箱へ戻り、途中の箱と交わる時は外側を回る
149
157
  * - `"curve"` ... 始点から水平に出て終点へ水平に入る 3 次曲線。 途中の箱は避けず、
150
158
  * 2 点が同じ高さなら直線になる。 終点の矢印は水平を向く
151
- * - `"metro"` ... 横線と傾き ±1 の斜線だけで結び、角を半径 r の円弧で丸める。
159
+ * - `"metro"` ... 通常は横線と傾き ±1 の斜線で結び、角を円弧で丸める。
160
+ * `decision` からの枝は、行き先が同じ横位置なら上下の角から真っ直ぐ、右にあれば右の角から出て曲がる。
161
+ * 左へ戻る `dashed` は行き先側へ横に出して駅の上下へ入る。
152
162
  * 途中の箱は避けず、2 点が同じ高さなら横 1 本になる
153
163
  */
154
164
  type EdgeRouting = "default" | "back-detour" | "curve" | "metro";
@@ -291,7 +301,29 @@ type CdlLane = {
291
301
  */
292
302
  width: number;
293
303
  label?: string;
304
+ /**
305
+ * 路線図の担当として描く lane。 `label` を名札の名前、`subtitle` をその補足に使う。
306
+ *
307
+ * 書いた lane だけが名札と案内線を出す。書かない lane の出力は変わらない。
308
+ */
309
+ metro?: {
310
+ subtitle?: string;
311
+ };
294
312
  contain?: boolean;
313
+ /**
314
+ * lane を同じ上端・高さの段階列として描く時の見出し。
315
+ *
316
+ * `number` と `name` は別の SVG text に出るため、利用側は `stage-number` と `stage-name` を
317
+ * 個別に選べる。この lane にある `card` node の `subtitle` は、札の右側へ `stage-note` として
318
+ * 描き、通常の card の補足位置には出さない。
319
+ * `leaderTo` を持つ `card` も、段階列の内外にかかわらず同じ `stage-note` を使う。
320
+ *
321
+ * 書かない lane は既存の縦列のままで、`lane-container` / `lane-label` の入力と出力は変わらない。
322
+ */
323
+ stage?: {
324
+ number: string;
325
+ name: string;
326
+ };
295
327
  /**
296
328
  * この lane を何のために置いているか (既定 = `"column"`)。
297
329
  *
@@ -311,6 +343,12 @@ type CdlLane = {
311
343
  role?: "column" | "overlay";
312
344
  /** UML sequence diagram の lifeline (= lane の中央 x に縦の点線を描画) */
313
345
  lifeline?: boolean;
346
+ /**
347
+ * lane の中央 x に時間軸の実線を描く。線はこの lane にある描画対象 node の中心間を結ぶ。
348
+ *
349
+ * `lifeline` の点線とは独立しており、両方を書いた lane では両方を描く。
350
+ */
351
+ timelineAxis?: boolean;
314
352
  /**
315
353
  * 明示指定した座標。 `posX` と `posY` の **両方**が揃った lane は「置いた位置に置く」
316
354
  * 扱いになる。 片方だけでは何も変わらない。
@@ -336,6 +374,11 @@ type CdlNode = {
336
374
  title: string;
337
375
  eyebrow?: string;
338
376
  subtitle?: string;
377
+ /**
378
+ * この箱から指定した node へ矢印なしの補助線を引く。edge ではないため `laid.edges` には入らない。
379
+ * `card` の `subtitle` は札の右側へ `stage-note` として描き、通常の補足位置には出さない。
380
+ */
381
+ leaderTo?: string;
339
382
  /** mono 数値表示用、 `{stateId}` を含めれば state 値で動的置換 */
340
383
  value?: string;
341
384
  /** kind=storage 時の行 (各 mono、 state 参照可) */
@@ -2651,6 +2694,8 @@ type CdlDiagram = {
2651
2694
  * 種類を書いていない図にも当たる。
2652
2695
  */
2653
2696
  type?: string;
2697
+ /** SVG 内の図の下へ、指定順の横一列で置く凡例。 */
2698
+ legend?: readonly CdlLegendItem[];
2654
2699
  /**
2655
2700
  * 矢印をいつ出すか (既定 = `"phase"`、 #582)。
2656
2701
  *
@@ -2836,6 +2881,13 @@ type LaidEdge = CdlEdge & {
2836
2881
  labelX: number;
2837
2882
  labelY: number;
2838
2883
  labelAnchor: "start" | "middle" | "end";
2884
+ /** 前の station へ戻る metro 線だけが持つ、専用の点線と矢印を描く目印。 */
2885
+ metroReturn?: true;
2886
+ };
2887
+ type LaidLegendItem = CdlLegendItem & {
2888
+ x: number;
2889
+ /** 文字の baseline。 印はこの値を基準に同じ行へ揃える。 */
2890
+ y: number;
2839
2891
  };
2840
2892
  /**
2841
2893
  * AABB (axis-aligned bounding box)。
@@ -2869,6 +2921,8 @@ type LaidDiagram = {
2869
2921
  textMetrics?: "monospace";
2870
2922
  /** どの種類の図か (#645)。 `CdlDiagram.type` をそのまま持ち越す */
2871
2923
  type?: string;
2924
+ /** 指定がある図だけが持つ、配置済みの凡例。 */
2925
+ legend?: readonly LaidLegendItem[];
2872
2926
  viewBox: {
2873
2927
  x: number;
2874
2928
  y: number;
@@ -3069,4 +3123,4 @@ type CdlDiagramViewProps = {
3069
3123
  };
3070
3124
  declare function CdlDiagramView({ diagram, laid: laidProp, hideHeader, hideMiniPhaseIndicator, focusPhaseId, debug, emitGeometryWarn, headingLevel, interactiveHandlers }: CdlDiagramViewProps): JSX.Element;
3071
3125
 
3072
- export { type ScrollProgressHandle as $, type ChartPieForm as A, type BBox as B, type CdlDiagram as C, type ChartSlopeForm as D, type EdgeStyle as E, EDGE_HEADS as F, EDGE_HEAD_DEFAULT as G, EDGE_HEAD_FILLS as H, EDGE_HEAD_FILL_DEFAULT as I, type JourneyEmotion as J, EDGE_REVEALS as K, type LaidDiagram as L, EDGE_REVEAL_DEFAULT as M, type NodeKind as N, EDGE_STYLES as O, type EdgeReveal as P, type QuadrantKey as Q, type RowMark as R, type Signal as S, type Tone as T, type InteractiveHandlerMap as U, type InteractiveSignalsAccessor as V, type LaidLane as W, NODE_KINDS as X, RELATION_FOCUSES as Y, RELATION_FOCUS_DEFAULT as Z, type RelationFocus as _, type CdlInput as a, TONES as a0, computeEventBindingKey as a1, computeFormulaKey as a2, computeInputSignalKey as a3, computeScrollTriggerKey as a4, computed as a5, createInteractiveSignalsAccessor as a6, createScrollProgressHandle as a7, createScrollProgressSignals as a8, hasInteractivePrimitives as a9, resolveLang as aa, sequenceBoardMetrics as ab, sequenceStepId as ac, signal as ad, type CdlLane as b, type CdlNode as c, type Side as d, type EdgeRole as e, type EdgeHead as f, type EdgeHeadFill as g, type EdgeRouting as h, type CdlState as i, type CdlInputOption as j, type CdlEventTarget as k, type CdlLang as l, type CdlTextMetrics as m, type Computed as n, type CdlDerivedValue as o, type CdlScrollTrigger as p, type CdlDiagramViewProps as q, type LaidEdge as r, type LaidNode as s, type CdlEdge as t, type TreeNodePayload as u, CdlDiagramView as v, type CdlEventBinding as w, type CdlEventKind as x, type CdlFormula as y, type CdlPhase as z };
3126
+ export { NODE_KINDS as $, type CdlPhase as A, type BBox as B, type CdlDiagram as C, type ChartPieForm as D, type EdgeStyle as E, type ChartSlopeForm as F, EDGE_HEADS as G, EDGE_HEAD_DEFAULT as H, EDGE_HEAD_FILLS as I, type JourneyEmotion as J, EDGE_HEAD_FILL_DEFAULT as K, type LaidDiagram as L, EDGE_REVEALS as M, type NodeKind as N, EDGE_REVEAL_DEFAULT as O, EDGE_STYLES as P, type QuadrantKey as Q, type RowMark as R, type Signal as S, type Tone as T, type EdgeReveal as U, type InteractiveHandlerMap as V, type InteractiveSignalsAccessor as W, LEGEND_MARKS as X, type LaidLane as Y, type LaidLegendItem as Z, type LegendMark as _, type CdlInput as a, RELATION_FOCUSES as a0, RELATION_FOCUS_DEFAULT as a1, type RelationFocus as a2, type ScrollProgressHandle as a3, TONES as a4, computeEventBindingKey as a5, computeFormulaKey as a6, computeInputSignalKey as a7, computeScrollTriggerKey as a8, computed as a9, createInteractiveSignalsAccessor as aa, createScrollProgressHandle as ab, createScrollProgressSignals as ac, hasInteractivePrimitives as ad, resolveLang as ae, sequenceBoardMetrics as af, sequenceStepId as ag, signal as ah, type CdlLane as b, type CdlNode as c, type Side as d, type EdgeRole as e, type EdgeHead as f, type EdgeHeadFill as g, type EdgeRouting as h, type CdlState as i, type CdlInputOption as j, type CdlEventTarget as k, type CdlLang as l, type CdlTextMetrics as m, type Computed as n, type CdlDerivedValue as o, type CdlScrollTrigger as p, type CdlDiagramViewProps as q, type LaidEdge as r, type LaidNode as s, type CdlEdge as t, type TreeNodePayload as u, CdlDiagramView as v, type CdlEventBinding as w, type CdlEventKind as x, type CdlFormula as y, type CdlLegendItem as z };
@@ -125,8 +125,16 @@ type Tone = (typeof TONES)[number];
125
125
  * 食い違うため、図単位で測り方を揃えられるようにする。
126
126
  */
127
127
  type CdlTextMetrics = "proportional" | "monospace";
128
+ /** 凡例に描ける印。 配置・描画・検査はこの 1 つの列を出所にする。 */
129
+ declare const LEGEND_MARKS: readonly ["diamond", "filled-circle", "double-circle", "dotted-line", "solid-line", "curved-line", "rounded-label", "numbered-circle", "station", "arrow"];
130
+ type LegendMark = (typeof LEGEND_MARKS)[number];
131
+ /** 図の下へ置く凡例の 1 項目。 */
132
+ type CdlLegendItem = {
133
+ mark: LegendMark;
134
+ text: string;
135
+ };
128
136
  /** 描画できる箱の種類。 検査・記法一覧・型はこの 1 つの列を出所にする。 */
129
- 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", "chart-gauge", "chart-radial", "chart-stat", "chart-waffle", "chart-stacked-bar", "chart-slope", "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", "mark-start", "mark-end", "station", "sequence-board"];
137
+ 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", "chart-gauge", "chart-radial", "chart-stat", "chart-waffle", "chart-stacked-bar", "chart-slope", "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", "mark-start", "mark-end", "station", "timeline-number", "sequence-board"];
130
138
  type NodeKind = (typeof NODE_KINDS)[number];
131
139
  type Side = "top" | "right" | "bottom" | "left";
132
140
  /**
@@ -148,7 +156,9 @@ type EdgeStyle = "solid" | "dotted-flow" | "dashed";
148
156
  * - `"back-detour"` ... 箱の上を回って前の箱へ戻り、途中の箱と交わる時は外側を回る
149
157
  * - `"curve"` ... 始点から水平に出て終点へ水平に入る 3 次曲線。 途中の箱は避けず、
150
158
  * 2 点が同じ高さなら直線になる。 終点の矢印は水平を向く
151
- * - `"metro"` ... 横線と傾き ±1 の斜線だけで結び、角を半径 r の円弧で丸める。
159
+ * - `"metro"` ... 通常は横線と傾き ±1 の斜線で結び、角を円弧で丸める。
160
+ * `decision` からの枝は、行き先が同じ横位置なら上下の角から真っ直ぐ、右にあれば右の角から出て曲がる。
161
+ * 左へ戻る `dashed` は行き先側へ横に出して駅の上下へ入る。
152
162
  * 途中の箱は避けず、2 点が同じ高さなら横 1 本になる
153
163
  */
154
164
  type EdgeRouting = "default" | "back-detour" | "curve" | "metro";
@@ -291,7 +301,29 @@ type CdlLane = {
291
301
  */
292
302
  width: number;
293
303
  label?: string;
304
+ /**
305
+ * 路線図の担当として描く lane。 `label` を名札の名前、`subtitle` をその補足に使う。
306
+ *
307
+ * 書いた lane だけが名札と案内線を出す。書かない lane の出力は変わらない。
308
+ */
309
+ metro?: {
310
+ subtitle?: string;
311
+ };
294
312
  contain?: boolean;
313
+ /**
314
+ * lane を同じ上端・高さの段階列として描く時の見出し。
315
+ *
316
+ * `number` と `name` は別の SVG text に出るため、利用側は `stage-number` と `stage-name` を
317
+ * 個別に選べる。この lane にある `card` node の `subtitle` は、札の右側へ `stage-note` として
318
+ * 描き、通常の card の補足位置には出さない。
319
+ * `leaderTo` を持つ `card` も、段階列の内外にかかわらず同じ `stage-note` を使う。
320
+ *
321
+ * 書かない lane は既存の縦列のままで、`lane-container` / `lane-label` の入力と出力は変わらない。
322
+ */
323
+ stage?: {
324
+ number: string;
325
+ name: string;
326
+ };
295
327
  /**
296
328
  * この lane を何のために置いているか (既定 = `"column"`)。
297
329
  *
@@ -311,6 +343,12 @@ type CdlLane = {
311
343
  role?: "column" | "overlay";
312
344
  /** UML sequence diagram の lifeline (= lane の中央 x に縦の点線を描画) */
313
345
  lifeline?: boolean;
346
+ /**
347
+ * lane の中央 x に時間軸の実線を描く。線はこの lane にある描画対象 node の中心間を結ぶ。
348
+ *
349
+ * `lifeline` の点線とは独立しており、両方を書いた lane では両方を描く。
350
+ */
351
+ timelineAxis?: boolean;
314
352
  /**
315
353
  * 明示指定した座標。 `posX` と `posY` の **両方**が揃った lane は「置いた位置に置く」
316
354
  * 扱いになる。 片方だけでは何も変わらない。
@@ -336,6 +374,11 @@ type CdlNode = {
336
374
  title: string;
337
375
  eyebrow?: string;
338
376
  subtitle?: string;
377
+ /**
378
+ * この箱から指定した node へ矢印なしの補助線を引く。edge ではないため `laid.edges` には入らない。
379
+ * `card` の `subtitle` は札の右側へ `stage-note` として描き、通常の補足位置には出さない。
380
+ */
381
+ leaderTo?: string;
339
382
  /** mono 数値表示用、 `{stateId}` を含めれば state 値で動的置換 */
340
383
  value?: string;
341
384
  /** kind=storage 時の行 (各 mono、 state 参照可) */
@@ -2651,6 +2694,8 @@ type CdlDiagram = {
2651
2694
  * 種類を書いていない図にも当たる。
2652
2695
  */
2653
2696
  type?: string;
2697
+ /** SVG 内の図の下へ、指定順の横一列で置く凡例。 */
2698
+ legend?: readonly CdlLegendItem[];
2654
2699
  /**
2655
2700
  * 矢印をいつ出すか (既定 = `"phase"`、 #582)。
2656
2701
  *
@@ -2836,6 +2881,13 @@ type LaidEdge = CdlEdge & {
2836
2881
  labelX: number;
2837
2882
  labelY: number;
2838
2883
  labelAnchor: "start" | "middle" | "end";
2884
+ /** 前の station へ戻る metro 線だけが持つ、専用の点線と矢印を描く目印。 */
2885
+ metroReturn?: true;
2886
+ };
2887
+ type LaidLegendItem = CdlLegendItem & {
2888
+ x: number;
2889
+ /** 文字の baseline。 印はこの値を基準に同じ行へ揃える。 */
2890
+ y: number;
2839
2891
  };
2840
2892
  /**
2841
2893
  * AABB (axis-aligned bounding box)。
@@ -2869,6 +2921,8 @@ type LaidDiagram = {
2869
2921
  textMetrics?: "monospace";
2870
2922
  /** どの種類の図か (#645)。 `CdlDiagram.type` をそのまま持ち越す */
2871
2923
  type?: string;
2924
+ /** 指定がある図だけが持つ、配置済みの凡例。 */
2925
+ legend?: readonly LaidLegendItem[];
2872
2926
  viewBox: {
2873
2927
  x: number;
2874
2928
  y: number;
@@ -3069,4 +3123,4 @@ type CdlDiagramViewProps = {
3069
3123
  };
3070
3124
  declare function CdlDiagramView({ diagram, laid: laidProp, hideHeader, hideMiniPhaseIndicator, focusPhaseId, debug, emitGeometryWarn, headingLevel, interactiveHandlers }: CdlDiagramViewProps): JSX.Element;
3071
3125
 
3072
- export { type ScrollProgressHandle as $, type ChartPieForm as A, type BBox as B, type CdlDiagram as C, type ChartSlopeForm as D, type EdgeStyle as E, EDGE_HEADS as F, EDGE_HEAD_DEFAULT as G, EDGE_HEAD_FILLS as H, EDGE_HEAD_FILL_DEFAULT as I, type JourneyEmotion as J, EDGE_REVEALS as K, type LaidDiagram as L, EDGE_REVEAL_DEFAULT as M, type NodeKind as N, EDGE_STYLES as O, type EdgeReveal as P, type QuadrantKey as Q, type RowMark as R, type Signal as S, type Tone as T, type InteractiveHandlerMap as U, type InteractiveSignalsAccessor as V, type LaidLane as W, NODE_KINDS as X, RELATION_FOCUSES as Y, RELATION_FOCUS_DEFAULT as Z, type RelationFocus as _, type CdlInput as a, TONES as a0, computeEventBindingKey as a1, computeFormulaKey as a2, computeInputSignalKey as a3, computeScrollTriggerKey as a4, computed as a5, createInteractiveSignalsAccessor as a6, createScrollProgressHandle as a7, createScrollProgressSignals as a8, hasInteractivePrimitives as a9, resolveLang as aa, sequenceBoardMetrics as ab, sequenceStepId as ac, signal as ad, type CdlLane as b, type CdlNode as c, type Side as d, type EdgeRole as e, type EdgeHead as f, type EdgeHeadFill as g, type EdgeRouting as h, type CdlState as i, type CdlInputOption as j, type CdlEventTarget as k, type CdlLang as l, type CdlTextMetrics as m, type Computed as n, type CdlDerivedValue as o, type CdlScrollTrigger as p, type CdlDiagramViewProps as q, type LaidEdge as r, type LaidNode as s, type CdlEdge as t, type TreeNodePayload as u, CdlDiagramView as v, type CdlEventBinding as w, type CdlEventKind as x, type CdlFormula as y, type CdlPhase as z };
3126
+ export { NODE_KINDS as $, type CdlPhase as A, type BBox as B, type CdlDiagram as C, type ChartPieForm as D, type EdgeStyle as E, type ChartSlopeForm as F, EDGE_HEADS as G, EDGE_HEAD_DEFAULT as H, EDGE_HEAD_FILLS as I, type JourneyEmotion as J, EDGE_HEAD_FILL_DEFAULT as K, type LaidDiagram as L, EDGE_REVEALS as M, type NodeKind as N, EDGE_REVEAL_DEFAULT as O, EDGE_STYLES as P, type QuadrantKey as Q, type RowMark as R, type Signal as S, type Tone as T, type EdgeReveal as U, type InteractiveHandlerMap as V, type InteractiveSignalsAccessor as W, LEGEND_MARKS as X, type LaidLane as Y, type LaidLegendItem as Z, type LegendMark as _, type CdlInput as a, RELATION_FOCUSES as a0, RELATION_FOCUS_DEFAULT as a1, type RelationFocus as a2, type ScrollProgressHandle as a3, TONES as a4, computeEventBindingKey as a5, computeFormulaKey as a6, computeInputSignalKey as a7, computeScrollTriggerKey as a8, computed as a9, createInteractiveSignalsAccessor as aa, createScrollProgressHandle as ab, createScrollProgressSignals as ac, hasInteractivePrimitives as ad, resolveLang as ae, sequenceBoardMetrics as af, sequenceStepId as ag, signal as ah, type CdlLane as b, type CdlNode as c, type Side as d, type EdgeRole as e, type EdgeHead as f, type EdgeHeadFill as g, type EdgeRouting as h, type CdlState as i, type CdlInputOption as j, type CdlEventTarget as k, type CdlLang as l, type CdlTextMetrics as m, type Computed as n, type CdlDerivedValue as o, type CdlScrollTrigger as p, type CdlDiagramViewProps as q, type LaidEdge as r, type LaidNode as s, type CdlEdge as t, type TreeNodePayload as u, CdlDiagramView as v, type CdlEventBinding as w, type CdlEventKind as x, type CdlFormula as y, type CdlLegendItem as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cardenelabs/cdl",
3
- "version": "0.124.0",
3
+ "version": "0.125.0",
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",
@@ -57,6 +57,16 @@
57
57
  "node": ">=18"
58
58
  },
59
59
  "sideEffects": false,
60
+ "scripts": {
61
+ "build": "tsup",
62
+ "dev": "tsup --watch",
63
+ "typecheck": "tsc --noEmit",
64
+ "test": "cd ../.. && vitest run packages/cdl",
65
+ "lint": "tsc --noEmit",
66
+ "prepublishOnly": "pnpm run typecheck && pnpm run build",
67
+ "prepack": "node scripts/prepack-check.mjs",
68
+ "postpack": "node scripts/prepack-check.mjs"
69
+ },
60
70
  "peerDependencies": {
61
71
  "react": "^19.0.0",
62
72
  "react-dom": "^19.0.0"
@@ -68,12 +78,5 @@
68
78
  "typedoc": "^0.28.19",
69
79
  "typedoc-plugin-markdown": "^4.12.0",
70
80
  "typescript": "^5.8.0"
71
- },
72
- "scripts": {
73
- "build": "tsup",
74
- "dev": "tsup --watch",
75
- "typecheck": "tsc --noEmit",
76
- "test": "cd ../.. && vitest run packages/cdl",
77
- "lint": "tsc --noEmit"
78
81
  }
79
- }
82
+ }
package/src/builder.ts CHANGED
@@ -1670,6 +1670,8 @@ export function diagram(
1670
1670
  * この図がどの種類か (#645)。 詳しくは `CdlDiagram.type` の説明を参照。
1671
1671
  */
1672
1672
  type?: string;
1673
+ /** SVG 内の図の下へ置く共通凡例。 */
1674
+ legend?: CdlDiagram["legend"];
1673
1675
  },
1674
1676
  ): DiagramBuilder {
1675
1677
  const lanes: CdlLane[] = [];
@@ -3825,6 +3827,10 @@ export function diagram(
3825
3827
  ...(options.textMetrics !== undefined ? { textMetrics: options.textMetrics } : {}),
3826
3828
  // 種類の名前も同じ経路で渡し、空文字の扱いを `layout` の 1 箇所に揃える
3827
3829
  ...(options.type !== undefined ? { type: options.type } : {}),
3830
+ // 空の並びは書かなかった図と同じ形にする。 凡例なしの JSON と配置を変えない
3831
+ ...(options.legend && options.legend.length > 0
3832
+ ? { legend: options.legend.map((item) => ({ ...item })) }
3833
+ : {}),
3828
3834
  // inputs / formulas / scrollTriggers / eventBindings は空なら省略 (static diagram の JSON 形状維持)
3829
3835
  ...(inputs.length > 0 ? { inputs: [...inputs] } : {}),
3830
3836
  ...(formulas.length > 0 ? { formulas: [...formulas] } : {}),
package/src/index.ts CHANGED
@@ -303,6 +303,7 @@ export type {
303
303
  } from "./presets";
304
304
  export type {
305
305
  CdlDiagram,
306
+ CdlLegendItem,
306
307
  CdlEdge,
307
308
  ChartPieForm,
308
309
  ChartSlopeForm,
@@ -321,10 +322,12 @@ export type {
321
322
  LaidDiagram,
322
323
  LaidEdge,
323
324
  LaidLane,
325
+ LaidLegendItem,
324
326
  LaidNode,
325
327
  NodeKind,
326
328
  Side,
327
329
  Tone,
330
+ LegendMark,
328
331
  } from "./types";
329
332
  // 値としての tone 一覧。 downstream (dragon の parser 等) が受理できる色名の集合を
330
333
  // 手書きせずに済ませるため公開する。
@@ -342,6 +345,7 @@ export {
342
345
  EDGE_HEAD_FILL_DEFAULT,
343
346
  EDGE_REVEALS,
344
347
  EDGE_REVEAL_DEFAULT,
348
+ LEGEND_MARKS,
345
349
  EDGE_STYLES,
346
350
  RELATION_FOCUSES,
347
351
  RELATION_FOCUS_DEFAULT,
@@ -6,7 +6,15 @@ import { SUBTITLE_SIZE, 行送りの倍率, 説明文を割り付ける } from "
6
6
  import { 余りを分ける, 目印なしの繰り上げ } from "../layout/spec";
7
7
  import { NODE_SIZE } from "../layout/tokens";
8
8
 
9
- export function CardNode({ node, active }: { node: LaidNode; active: boolean }): JSX.Element {
9
+ export function CardNode({
10
+ node,
11
+ active,
12
+ compactTitleInset,
13
+ }: {
14
+ node: LaidNode;
15
+ active: boolean;
16
+ compactTitleInset?: number;
17
+ }): JSX.Element {
10
18
  const accent = nodeAccent(node, "var(--cdl-tone-accent, #2d6a8f)");
11
19
  const x = node.cx - node.w / 2;
12
20
  const y = node.cy - node.h / 2;
@@ -32,12 +40,12 @@ export function CardNode({ node, active }: { node: LaidNode; active: boolean }):
32
40
  });
33
41
  const ずらし = 繰り上げ - 余り;
34
42
  const titleY = compact ? (小型に呼び名 ? node.h / 2 - 2 : node.h / 2 + 8) : 86 - ずらし;
35
- const titleX = compact ? node.w / 2 : 26;
36
- const paddingX = compact ? 16 : 52;
43
+ const titleX = compact ? compactTitleInset ?? node.w / 2 : 26;
44
+ const paddingX = compact ? (compactTitleInset ?? 8) * 2 : 52;
37
45
  const availableW = node.w - paddingX;
38
46
  const initialTitleSize = compact ? 20 : 24;
39
47
  const titleSize = fitTextSize(node.title, availableW, initialTitleSize, 11);
40
- const textAnchor = compact ? ("middle" as const) : ("start" as const);
48
+ const textAnchor = compact && compactTitleInset === undefined ? ("middle" as const) : ("start" as const);
41
49
  const subtitleY = 124 - ずらし;
42
50
  // 2 行を置けるのは、題名の下端に触れず箱の下端も越えない時だけ (#843)。
43
51
  // 判定は最大の大きさ (21) で行う = 実際はこれ以下に収まるので、入ると判定した箱には必ず入る
@@ -238,6 +238,8 @@ export function GenericNode({
238
238
  stateValues,
239
239
  値の名前,
240
240
  textMetrics = "proportional",
241
+ showKindIcon = true,
242
+ metroDecision = false,
241
243
  }: {
242
244
  node: LaidNode;
243
245
  active: boolean;
@@ -246,6 +248,10 @@ export function GenericNode({
246
248
  /** 入力欄の値ごとの名前 (#855)。 値と行の差し込みに使う */
247
249
  値の名前?: 値の名前の表;
248
250
  textMetrics?: CdlTextMetrics;
251
+ /** 路線図の分かれ道のように、箱の形自体が種類を示す時は種類の印を重ねない。 */
252
+ showKindIcon?: boolean;
253
+ /** `routing: "metro"` の線が出入りする分かれ道だけに見本の寸法を使う。 */
254
+ metroDecision?: boolean;
249
255
  }): JSX.Element {
250
256
  const base = STYLES[node.kind] ?? FALLBACK;
251
257
  // `tone` 指定があれば kind の既定色を置き換える。 色を決める式は `node-tone.ts` に集約。
@@ -281,13 +287,13 @@ export function GenericNode({
281
287
  const cy = h / 2;
282
288
  // diamond の上下頂点を内側に 8px padding して stroke clip を回避。
283
289
  // 旧実装は M cx 0 で上頂点が container 上端と完全一致、 strokeWidth=3 で半分が out of bounds に。
284
- const pad = 8;
290
+ const pad = metroDecision ? 0 : 8;
285
291
  return (
286
292
  <path
287
293
  d={`M ${cx} ${pad} L ${w - pad} ${cy} L ${cx} ${h - pad} L ${pad} ${cy} Z`}
288
294
  fill="var(--cdl-node-fill, #ffffff)"
289
295
  stroke={active ? style.accent : "var(--cdl-divider, #d4d4d8)"}
290
- strokeWidth={active ? 3 : 1.25}
296
+ strokeWidth={metroDecision ? 1.5 : active ? 3 : 1.25}
291
297
  />
292
298
  );
293
299
  }
@@ -401,13 +407,15 @@ export function GenericNode({
401
407
  */
402
408
  const 絵の芯 = centered && STYLES[node.kind]?.shape === "cloud" ? h * CLOUD_ART_CENTER_RISE : 0;
403
409
  const eyebrowY = centered ? h / 2 - 26 - 絵の芯 : 32 + cylinderShift + 余り;
404
- const titleY = genericTitleBaselineY(node.kind, h, hasRows) - ずらし - 絵の芯;
410
+ const titleY = metroDecision
411
+ ? h / 2 + 9
412
+ : genericTitleBaselineY(node.kind, h, hasRows) - ずらし - 絵の芯;
405
413
  const subtitleY = centered ? h / 2 + 28 - 絵の芯 : 96 + cylinderShift - ずらし;
406
414
  // icon 位置 (diamond / cloud は title の真右の少し上、 中心配置に合わせる)
407
415
  const iconX = centered ? w / 2 + 40 : w - 48;
408
416
  // 目印も字と同じ芯に合わせる (#588)。 字だけ動かすと目印との間隔が変わる
409
417
  const iconY = centered ? h / 2 - 44 - 絵の芯 : 18 + cylinderShift;
410
- const dominantBaseline = centered ? ("middle" as const) : undefined;
418
+ const dominantBaseline = centered && !metroDecision ? ("middle" as const) : undefined;
411
419
 
412
420
  return (
413
421
  <g
@@ -420,16 +428,18 @@ export function GenericNode({
420
428
  >
421
429
  {shapeEl}
422
430
  {/* icon */}
423
- <g transform={`translate(${iconX} ${iconY}) scale(1)`} data-cdl-role="node-kind-icon">
424
- <rect width={24} height={24} fill="transparent" />
425
- <path d={style.icon} fill={style.eyebrowColor} opacity={0.7} transform="scale(1)" />
426
- </g>
431
+ {showKindIcon && (
432
+ <g transform={`translate(${iconX} ${iconY}) scale(1)`} data-cdl-role="node-kind-icon">
433
+ <rect width={24} height={24} fill="transparent" />
434
+ <path d={style.icon} fill={style.eyebrowColor} opacity={0.7} transform="scale(1)" />
435
+ </g>
436
+ )}
427
437
  {node.eyebrow && (
428
438
  <text x={textX} y={eyebrowY} fontSize={17} fontWeight={700} letterSpacing={1.5} fill={paint.onSolid ? "var(--cdl-node-fill, #ffffff)" : style.eyebrowColor} textAnchor={textAnchor} dominantBaseline={dominantBaseline}>
429
439
  {node.eyebrow}
430
440
  </text>
431
441
  )}
432
- <text data-cdl-role="node-label" x={textX} y={titleY} fontSize={20} fontWeight={700} fill={paint.onSolid ? "var(--cdl-node-fill, #ffffff)" : "var(--cdl-text, #1a1f2a)"} textAnchor={textAnchor} dominantBaseline={dominantBaseline} style={node.kind === "function" || node.kind === "api" ? { fontFamily: "'JetBrains Mono', monospace" } : undefined}>
442
+ <text data-cdl-role="node-label" x={textX} y={titleY} fontSize={metroDecision ? 26 : 20} fontWeight={700} fill={paint.onSolid ? "var(--cdl-node-fill, #ffffff)" : "var(--cdl-text, #1a1f2a)"} textAnchor={textAnchor} dominantBaseline={dominantBaseline} style={node.kind === "function" || node.kind === "api" ? { fontFamily: "'JetBrains Mono', monospace" } : undefined}>
433
443
  {node.kind === "function" || node.kind === "api" ? 等幅の字(node.title, 20) : node.title}
434
444
  </text>
435
445
  {node.subtitle && (
@@ -1,29 +1,115 @@
1
1
  import type { JSX } from "react";
2
2
 
3
+ import { TIMELINE_NUMBER_STROKE, titleUsableWidth } from "../layout/spec";
3
4
  import type { LaidNode } from "../types";
4
- import { renderShapeWithOutsideLabels } from "./dyn-shape";
5
+ import { fitTextSize, MIN_TEXT_SIZE } from "./compact-title";
5
6
  import { nodeAccent } from "./node-tone";
6
7
 
7
- const STATION_STROKE_ACTIVE = 4;
8
- const STATION_STROKE_IDLE = 3;
8
+ export const METRO_STATION_RADIUS = 16;
9
+ export const METRO_STATION_STROKE_WIDTH = 6;
10
+ const METRO_STATION_RADIUS_CSS = `var(--cdl-metro-station-radius, ${METRO_STATION_RADIUS}px)`;
11
+ const METRO_STATION_STROKE_WIDTH_CSS = `var(--cdl-metro-station-stroke-width, ${METRO_STATION_STROKE_WIDTH}px)`;
9
12
  const STATION_DEFAULT = "var(--cdl-tone-accent, #2d6a8f)";
13
+ const STATION_NAME_FONT_SIZE = 25;
14
+ const STATION_NAME_GAP = 18;
15
+ const STATION_NAME_RIGHT_GAP = 32;
16
+ const STATION_NAME_RIGHT_BASELINE = 9;
17
+ const STATION_SUBTITLE_FONT_SIZE = 18;
18
+ const STATION_SUBTITLE_GAP = 22;
10
19
 
11
20
  /** 路線図の線路上に置く駅。名前と説明は丸を隠さないよう外側に置く。 */
12
- export function StationNode({ node, active }: { node: LaidNode; active: boolean }): JSX.Element {
13
- const strokeWidth = active ? STATION_STROKE_ACTIVE : STATION_STROKE_IDLE;
14
- // 線は箱の辺で止まるため、枠線の外縁を箱の辺へ合わせると線路と丸の間が空かない。
15
- const radius = Math.max(0, Math.min(node.w, node.h) / 2 - strokeWidth / 2);
16
- const 形 = (
17
- <circle
18
- data-cdl-role="node-body"
19
- data-cdl-mark="station"
20
- cx={node.cx}
21
- cy={node.cy}
22
- r={radius}
23
- fill="var(--cdl-node-fill, #ffffff)"
24
- stroke={nodeAccent(node, STATION_DEFAULT)}
25
- strokeWidth={strokeWidth}
26
- />
21
+ export function StationNode({
22
+ node,
23
+ nameRight = false,
24
+ }: {
25
+ node: LaidNode;
26
+ active: boolean;
27
+ /** 線路が駅へ真上から入る時は、枝の札と重ならないよう名前を右へ置く。 */
28
+ nameRight?: boolean;
29
+ }): JSX.Element {
30
+ const name = node.title.trim();
31
+ const subtitle = node.subtitle?.trim() ?? "";
32
+ return (
33
+ <g>
34
+ <circle
35
+ data-cdl-role="node-body"
36
+ data-cdl-mark="station"
37
+ cx={node.cx}
38
+ cy={node.cy}
39
+ r={METRO_STATION_RADIUS}
40
+ fill="var(--cdl-node-fill, #ffffff)"
41
+ stroke={nodeAccent(node, STATION_DEFAULT)}
42
+ strokeWidth={METRO_STATION_STROKE_WIDTH}
43
+ style={{ r: METRO_STATION_RADIUS_CSS, strokeWidth: METRO_STATION_STROKE_WIDTH_CSS }}
44
+ />
45
+ {name !== "" && (
46
+ <text
47
+ data-cdl-role="shape-name"
48
+ x={nameRight ? node.cx + STATION_NAME_RIGHT_GAP : node.cx}
49
+ y={nameRight
50
+ ? node.cy + STATION_NAME_RIGHT_BASELINE
51
+ : node.cy - METRO_STATION_RADIUS - STATION_NAME_GAP}
52
+ textAnchor={nameRight ? "start" : "middle"}
53
+ fontSize={STATION_NAME_FONT_SIZE}
54
+ fontWeight={700}
55
+ fill="var(--cdl-text, #23201b)"
56
+ stroke="var(--cdl-node-fill, #ffffff)"
57
+ strokeWidth={8}
58
+ strokeLinejoin="round"
59
+ paintOrder="stroke"
60
+ >
61
+ {name}
62
+ </text>
63
+ )}
64
+ {subtitle !== "" && (
65
+ <text
66
+ data-cdl-role="shape-value"
67
+ x={node.cx}
68
+ y={node.cy + METRO_STATION_RADIUS + STATION_SUBTITLE_GAP}
69
+ textAnchor="middle"
70
+ fontSize={STATION_SUBTITLE_FONT_SIZE}
71
+ fill="var(--cdl-text-dim, #5a6270)"
72
+ >
73
+ {subtitle}
74
+ </text>
75
+ )}
76
+ </g>
77
+ );
78
+ }
79
+
80
+ /** 時間軸の実線上に置き、番号を中央に収める丸。路線図の駅の意味は兼ねない。 */
81
+ export function TimelineNumberNode({ node }: { node: LaidNode }): JSX.Element {
82
+ const radius = Math.max(0, Math.min(node.w, node.h) / 2 - TIMELINE_NUMBER_STROKE / 2);
83
+ const fontSize = fitTextSize(
84
+ node.title,
85
+ titleUsableWidth(node.kind, node.w, node.h),
86
+ 18,
87
+ MIN_TEXT_SIZE,
88
+ );
89
+ return (
90
+ <>
91
+ <circle
92
+ data-cdl-role="node-body"
93
+ data-cdl-mark="timeline-number"
94
+ cx={node.cx}
95
+ cy={node.cy}
96
+ r={radius}
97
+ fill="var(--cdl-node-fill, #ffffff)"
98
+ stroke={nodeAccent(node, STATION_DEFAULT)}
99
+ strokeWidth={TIMELINE_NUMBER_STROKE}
100
+ />
101
+ <text
102
+ data-cdl-role="node-label"
103
+ x={node.cx}
104
+ y={node.cy}
105
+ textAnchor="middle"
106
+ dominantBaseline="central"
107
+ fontSize={fontSize}
108
+ fontWeight={700}
109
+ fill="var(--cdl-text, #1a1f2a)"
110
+ >
111
+ {node.title}
112
+ </text>
113
+ </>
27
114
  );
28
- return renderShapeWithOutsideLabels(形, node, "middle");
29
115
  }