@cardenelabs/cdl 0.121.0 → 0.123.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.
Files changed (69) hide show
  1. package/CHANGELOG.md +51 -1
  2. package/README.md +137 -0
  3. package/SPEC.md +1 -1
  4. package/dist/index.cjs +1513 -667
  5. package/dist/index.cjs.map +1 -1
  6. package/dist/index.d.cts +51 -33
  7. package/dist/index.d.ts +51 -33
  8. package/dist/index.js +1513 -668
  9. package/dist/index.js.map +1 -1
  10. package/dist/react.cjs +1271 -444
  11. package/dist/react.cjs.map +1 -1
  12. package/dist/react.d.cts +1 -1
  13. package/dist/react.d.ts +1 -1
  14. package/dist/react.js +1271 -444
  15. package/dist/react.js.map +1 -1
  16. package/dist/{render-A5AKjNuy.d.cts → render-CVczwfRb.d.cts} +40 -7
  17. package/dist/{render-A5AKjNuy.d.ts → render-CVczwfRb.d.ts} +40 -7
  18. package/package.json +9 -12
  19. package/src/builder.ts +8 -2
  20. package/src/index.ts +3 -0
  21. package/src/kinds/box-lines.ts +17 -4
  22. package/src/kinds/chart-bar.tsx +4 -0
  23. package/src/kinds/chart-gauge.tsx +20 -9
  24. package/src/kinds/chart-pie.tsx +67 -17
  25. package/src/kinds/chart-radial.tsx +7 -2
  26. package/src/kinds/chart-slope.tsx +26 -7
  27. package/src/kinds/chart-stacked-bar.tsx +8 -4
  28. package/src/kinds/chart-stat.tsx +19 -4
  29. package/src/kinds/chart-waffle.tsx +7 -2
  30. package/src/kinds/dyn-shape.tsx +22 -9
  31. package/src/kinds/funnel.tsx +9 -2
  32. package/src/kinds/generic.tsx +5 -2
  33. package/src/kinds/mind-map.tsx +26 -10
  34. package/src/kinds/quadrant.tsx +14 -4
  35. package/src/kinds/row-align.ts +19 -7
  36. package/src/kinds/sequence-board-metrics.ts +13 -3
  37. package/src/kinds/sequence-board.tsx +4 -2
  38. package/src/kinds/shape-basement.tsx +57 -32
  39. package/src/kinds/shape-blockchain.tsx +83 -34
  40. package/src/kinds/shape-commerce.tsx +65 -24
  41. package/src/kinds/shape-finance.tsx +90 -26
  42. package/src/kinds/shape-hardware.tsx +48 -17
  43. package/src/kinds/shape-region.tsx +16 -6
  44. package/src/kinds/shape-software.tsx +30 -8
  45. package/src/kinds/station.tsx +29 -0
  46. package/src/kinds/text-width.ts +17 -3
  47. package/src/kinds/tree.tsx +25 -7
  48. package/src/layout/collisions.ts +50 -28
  49. package/src/layout/curve-path.ts +126 -0
  50. package/src/layout/edges.ts +271 -58
  51. package/src/layout/head-label.ts +11 -5
  52. package/src/layout/lanes.ts +3 -2
  53. package/src/layout/metro-path.ts +185 -0
  54. package/src/layout/nodes.ts +14 -6
  55. package/src/layout/predict-bbox.ts +6 -3
  56. package/src/layout/self-loop.ts +10 -4
  57. package/src/layout/spec.ts +36 -7
  58. package/src/layout/text-width.ts +25 -8
  59. package/src/layout/tokens.ts +2 -0
  60. package/src/layout-with-validation.ts +10 -5
  61. package/src/layout.ts +55 -21
  62. package/src/presets.ts +69 -43
  63. package/src/render/edges.tsx +19 -3
  64. package/src/render/interactive-panel.tsx +34 -7
  65. package/src/render/nodes.tsx +100 -48
  66. package/src/render/stage.tsx +3 -0
  67. package/src/text-metrics.ts +9 -0
  68. package/src/types.ts +41 -4
  69. package/src/visual-validate.ts +91 -21
@@ -88,7 +88,7 @@ type SequenceBoardMetrics = {
88
88
  * 幅は **語の桁 + 列 × 面の数**。 語を線の脇ではなく左の桁に置くので、線の長さが語の長さに
89
89
  * 引きずられない = 面の数だけで横幅が決まる。
90
90
  */
91
- declare function sequenceBoardMetrics(data: SequenceBoardData): SequenceBoardMetrics;
91
+ declare function sequenceBoardMetrics(data: SequenceBoardData, textMetrics?: CdlTextMetrics): SequenceBoardMetrics;
92
92
 
93
93
  /**
94
94
  * 描画側が書く固定の字の言語 (#847)。
@@ -117,8 +117,16 @@ declare function resolveLang(value: unknown): CdlLang;
117
117
  /** 意味付きの色の名前。 検査・描画・test はこの 1 つの列を出所にする。 */
118
118
  declare const TONES: readonly ["accent", "teal", "success", "error", "warning", "info"];
119
119
  type Tone = (typeof TONES)[number];
120
+ /**
121
+ * 図の字幅をどの規則で測るか。`"proportional"` は現在の Inter 比例幅表、`"monospace"` は
122
+ * 等幅書体向けの幅を使う。既定は `"proportional"`。
123
+ *
124
+ * 消費側が図を等幅書体で描く場合、比例幅で配置すると英数字の描画幅と箱・札の計算幅が
125
+ * 食い違うため、図単位で測り方を揃えられるようにする。
126
+ */
127
+ type CdlTextMetrics = "proportional" | "monospace";
120
128
  /** 描画できる箱の種類。 検査・記法一覧・型はこの 1 つの列を出所にする。 */
121
- 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", "sequence-board"];
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"];
122
130
  type NodeKind = (typeof NODE_KINDS)[number];
123
131
  type Side = "top" | "right" | "bottom" | "left";
124
132
  /**
@@ -133,6 +141,17 @@ type Side = "top" | "right" | "bottom" | "left";
133
141
  * ER の「識別しない」 のように **刻みが意味を持つ関係** はこちらを使う。
134
142
  */
135
143
  type EdgeStyle = "solid" | "dotted-flow" | "dashed";
144
+ /**
145
+ * 線の道筋。
146
+ *
147
+ * - `"default"` ... 自動。 箱を避け、角を丸めた直角の線を引く。 書かない時も同じ
148
+ * - `"back-detour"` ... 箱の上を回って前の箱へ戻る
149
+ * - `"curve"` ... 始点から水平に出て終点へ水平に入る 3 次曲線。 途中の箱は避けず、
150
+ * 2 点が同じ高さなら直線になる。 終点の矢印は水平を向く
151
+ * - `"metro"` ... 横線と傾き ±1 の斜線だけで結び、角を半径 r の円弧で丸める。
152
+ * 途中の箱は避けず、2 点が同じ高さなら横 1 本になる
153
+ */
154
+ type EdgeRouting = "default" | "back-detour" | "curve" | "metro";
136
155
  /** 辺の役目 (#618)。 `main` = 図の主となる道 */
137
156
  type EdgeRole = "main";
138
157
  /**
@@ -393,8 +412,8 @@ type CdlNode = {
393
412
  * 変わるのは kind の既定色を使って描かれる部分で、 枠線 / eyebrow の文字色 / icon /
394
413
  * 強調表示中の行の値。 本文の文字色と背景は変わらない。
395
414
  *
396
- * 効くのは `actor` / `function` / `storage` / `event` / `card` と `person` 以降の汎用 kind の
397
- * 計 26 種。 それ以外は自前の配色を持つため指定しても変わらず、 検査も通す。 効く kind の
415
+ * 効くのは `actor` / `function` / `storage` / `event` / `card`、`person` 以降の汎用 kind、
416
+ * 流れの印、路線図の駅。 それ以外は自前の配色を持つため指定しても変わらず、検査も通す。
398
417
  * 一覧は `test/node-styling.test.tsx` が実装から計算した集合と突き合わせている。
399
418
  *
400
419
  * 既定色と同じ名前を明示指定しても色が変わる kind がある (既定色の hex が `render/tone.ts`
@@ -746,8 +765,12 @@ type CdlEdge = {
746
765
  labelOffsetX?: number;
747
766
  /** label の y 方向 fine offset (default 0、 + で下、 - で上) */
748
767
  labelOffsetY?: number;
749
- /** routing mode (default "default" = 自動 routing、 "back-detour" = back transition 用の上方 detour 強制) */
750
- routing?: "default" | "back-detour";
768
+ /**
769
+ * 線の道筋。 `default` は箱を避ける自動経路、 `back-detour` は箱の上を回る戻り線、
770
+ * `curve` は途中の箱を避けず、両端で水平になる 3 次曲線。 `metro` は途中の箱を避けず、
771
+ * 横線と傾き ±1 の斜線を半径 r の円弧で結ぶ。2 点が同じ高さなら横 1 本。書かなければ `default`。
772
+ */
773
+ routing?: EdgeRouting;
751
774
  /**
752
775
  * CAR-549 SSOT ... label を edge path 上に「被せる」 か「離す」 かの制御 flag。
753
776
  * default (false or undefined) = LABEL_INIT_CLEARANCE (34 world、 CAR-630 で 26 → 34 復帰) 離して配置。
@@ -2608,6 +2631,14 @@ type CdlDiagram = {
2608
2631
  * 型の外の値は `layout` が既定に落とす (記法から文字列で届く場合の書き損じ)。
2609
2632
  */
2610
2633
  lang?: CdlLang;
2634
+ /**
2635
+ * 図の字幅を測る規則。`"proportional"` または `"monospace"` を取り、既定は
2636
+ * `"proportional"`。
2637
+ *
2638
+ * 消費側が等幅書体で描く図では、比例幅の配置計算と実際の描画幅が食い違うため指定する。
2639
+ * 型の外の値は `layout` が既定へ落とす。
2640
+ */
2641
+ textMetrics?: CdlTextMetrics;
2611
2642
  /**
2612
2643
  * この図がどの種類か (#645)。 書かない図は種類を持たない。
2613
2644
  *
@@ -2833,6 +2864,8 @@ type LaidDiagram = {
2833
2864
  * 無い時は `"en"` で描く。
2834
2865
  */
2835
2866
  lang?: CdlLang;
2867
+ /** 等幅で測る図だけが持つ。無い時は `"proportional"` */
2868
+ textMetrics?: "monospace";
2836
2869
  /** どの種類の図か (#645)。 `CdlDiagram.type` をそのまま持ち越す */
2837
2870
  type?: string;
2838
2871
  viewBox: {
@@ -3035,4 +3068,4 @@ type CdlDiagramViewProps = {
3035
3068
  };
3036
3069
  declare function CdlDiagramView({ diagram, laid: laidProp, hideHeader, hideMiniPhaseIndicator, focusPhaseId, debug, emitGeometryWarn, headingLevel, interactiveHandlers }: CdlDiagramViewProps): JSX.Element;
3037
3070
 
3038
- export { computeEventBindingKey as $, EDGE_HEADS as A, type BBox as B, type CdlDiagram as C, EDGE_HEAD_DEFAULT as D, type EdgeStyle as E, EDGE_HEAD_FILLS as F, EDGE_HEAD_FILL_DEFAULT as G, EDGE_REVEALS as H, EDGE_REVEAL_DEFAULT as I, type JourneyEmotion as J, EDGE_STYLES as K, type LaidDiagram as L, type EdgeReveal as M, type NodeKind as N, type InteractiveHandlerMap as O, type InteractiveSignalsAccessor as P, type QuadrantKey as Q, type RowMark as R, type Signal as S, type Tone as T, type LaidLane as U, NODE_KINDS as V, RELATION_FOCUSES as W, RELATION_FOCUS_DEFAULT as X, type RelationFocus as Y, type ScrollProgressHandle as Z, TONES as _, type CdlInput as a, computeFormulaKey as a0, computeInputSignalKey as a1, computeScrollTriggerKey as a2, computed as a3, createInteractiveSignalsAccessor as a4, createScrollProgressHandle as a5, createScrollProgressSignals as a6, hasInteractivePrimitives as a7, resolveLang as a8, sequenceBoardMetrics as a9, sequenceStepId as aa, signal as ab, 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 CdlState as h, type CdlInputOption as i, type CdlEventTarget as j, type CdlLang as k, type Computed as l, type CdlDerivedValue as m, type CdlScrollTrigger as n, type CdlDiagramViewProps as o, type LaidEdge as p, type LaidNode as q, type CdlEdge as r, type TreeNodePayload as s, CdlDiagramView as t, type CdlEventBinding as u, type CdlEventKind as v, type CdlFormula as w, type CdlPhase as x, type ChartPieForm as y, type ChartSlopeForm as z };
3071
+ 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 };
@@ -88,7 +88,7 @@ type SequenceBoardMetrics = {
88
88
  * 幅は **語の桁 + 列 × 面の数**。 語を線の脇ではなく左の桁に置くので、線の長さが語の長さに
89
89
  * 引きずられない = 面の数だけで横幅が決まる。
90
90
  */
91
- declare function sequenceBoardMetrics(data: SequenceBoardData): SequenceBoardMetrics;
91
+ declare function sequenceBoardMetrics(data: SequenceBoardData, textMetrics?: CdlTextMetrics): SequenceBoardMetrics;
92
92
 
93
93
  /**
94
94
  * 描画側が書く固定の字の言語 (#847)。
@@ -117,8 +117,16 @@ declare function resolveLang(value: unknown): CdlLang;
117
117
  /** 意味付きの色の名前。 検査・描画・test はこの 1 つの列を出所にする。 */
118
118
  declare const TONES: readonly ["accent", "teal", "success", "error", "warning", "info"];
119
119
  type Tone = (typeof TONES)[number];
120
+ /**
121
+ * 図の字幅をどの規則で測るか。`"proportional"` は現在の Inter 比例幅表、`"monospace"` は
122
+ * 等幅書体向けの幅を使う。既定は `"proportional"`。
123
+ *
124
+ * 消費側が図を等幅書体で描く場合、比例幅で配置すると英数字の描画幅と箱・札の計算幅が
125
+ * 食い違うため、図単位で測り方を揃えられるようにする。
126
+ */
127
+ type CdlTextMetrics = "proportional" | "monospace";
120
128
  /** 描画できる箱の種類。 検査・記法一覧・型はこの 1 つの列を出所にする。 */
121
- 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", "sequence-board"];
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"];
122
130
  type NodeKind = (typeof NODE_KINDS)[number];
123
131
  type Side = "top" | "right" | "bottom" | "left";
124
132
  /**
@@ -133,6 +141,17 @@ type Side = "top" | "right" | "bottom" | "left";
133
141
  * ER の「識別しない」 のように **刻みが意味を持つ関係** はこちらを使う。
134
142
  */
135
143
  type EdgeStyle = "solid" | "dotted-flow" | "dashed";
144
+ /**
145
+ * 線の道筋。
146
+ *
147
+ * - `"default"` ... 自動。 箱を避け、角を丸めた直角の線を引く。 書かない時も同じ
148
+ * - `"back-detour"` ... 箱の上を回って前の箱へ戻る
149
+ * - `"curve"` ... 始点から水平に出て終点へ水平に入る 3 次曲線。 途中の箱は避けず、
150
+ * 2 点が同じ高さなら直線になる。 終点の矢印は水平を向く
151
+ * - `"metro"` ... 横線と傾き ±1 の斜線だけで結び、角を半径 r の円弧で丸める。
152
+ * 途中の箱は避けず、2 点が同じ高さなら横 1 本になる
153
+ */
154
+ type EdgeRouting = "default" | "back-detour" | "curve" | "metro";
136
155
  /** 辺の役目 (#618)。 `main` = 図の主となる道 */
137
156
  type EdgeRole = "main";
138
157
  /**
@@ -393,8 +412,8 @@ type CdlNode = {
393
412
  * 変わるのは kind の既定色を使って描かれる部分で、 枠線 / eyebrow の文字色 / icon /
394
413
  * 強調表示中の行の値。 本文の文字色と背景は変わらない。
395
414
  *
396
- * 効くのは `actor` / `function` / `storage` / `event` / `card` と `person` 以降の汎用 kind の
397
- * 計 26 種。 それ以外は自前の配色を持つため指定しても変わらず、 検査も通す。 効く kind の
415
+ * 効くのは `actor` / `function` / `storage` / `event` / `card`、`person` 以降の汎用 kind、
416
+ * 流れの印、路線図の駅。 それ以外は自前の配色を持つため指定しても変わらず、検査も通す。
398
417
  * 一覧は `test/node-styling.test.tsx` が実装から計算した集合と突き合わせている。
399
418
  *
400
419
  * 既定色と同じ名前を明示指定しても色が変わる kind がある (既定色の hex が `render/tone.ts`
@@ -746,8 +765,12 @@ type CdlEdge = {
746
765
  labelOffsetX?: number;
747
766
  /** label の y 方向 fine offset (default 0、 + で下、 - で上) */
748
767
  labelOffsetY?: number;
749
- /** routing mode (default "default" = 自動 routing、 "back-detour" = back transition 用の上方 detour 強制) */
750
- routing?: "default" | "back-detour";
768
+ /**
769
+ * 線の道筋。 `default` は箱を避ける自動経路、 `back-detour` は箱の上を回る戻り線、
770
+ * `curve` は途中の箱を避けず、両端で水平になる 3 次曲線。 `metro` は途中の箱を避けず、
771
+ * 横線と傾き ±1 の斜線を半径 r の円弧で結ぶ。2 点が同じ高さなら横 1 本。書かなければ `default`。
772
+ */
773
+ routing?: EdgeRouting;
751
774
  /**
752
775
  * CAR-549 SSOT ... label を edge path 上に「被せる」 か「離す」 かの制御 flag。
753
776
  * default (false or undefined) = LABEL_INIT_CLEARANCE (34 world、 CAR-630 で 26 → 34 復帰) 離して配置。
@@ -2608,6 +2631,14 @@ type CdlDiagram = {
2608
2631
  * 型の外の値は `layout` が既定に落とす (記法から文字列で届く場合の書き損じ)。
2609
2632
  */
2610
2633
  lang?: CdlLang;
2634
+ /**
2635
+ * 図の字幅を測る規則。`"proportional"` または `"monospace"` を取り、既定は
2636
+ * `"proportional"`。
2637
+ *
2638
+ * 消費側が等幅書体で描く図では、比例幅の配置計算と実際の描画幅が食い違うため指定する。
2639
+ * 型の外の値は `layout` が既定へ落とす。
2640
+ */
2641
+ textMetrics?: CdlTextMetrics;
2611
2642
  /**
2612
2643
  * この図がどの種類か (#645)。 書かない図は種類を持たない。
2613
2644
  *
@@ -2833,6 +2864,8 @@ type LaidDiagram = {
2833
2864
  * 無い時は `"en"` で描く。
2834
2865
  */
2835
2866
  lang?: CdlLang;
2867
+ /** 等幅で測る図だけが持つ。無い時は `"proportional"` */
2868
+ textMetrics?: "monospace";
2836
2869
  /** どの種類の図か (#645)。 `CdlDiagram.type` をそのまま持ち越す */
2837
2870
  type?: string;
2838
2871
  viewBox: {
@@ -3035,4 +3068,4 @@ type CdlDiagramViewProps = {
3035
3068
  };
3036
3069
  declare function CdlDiagramView({ diagram, laid: laidProp, hideHeader, hideMiniPhaseIndicator, focusPhaseId, debug, emitGeometryWarn, headingLevel, interactiveHandlers }: CdlDiagramViewProps): JSX.Element;
3037
3070
 
3038
- export { computeEventBindingKey as $, EDGE_HEADS as A, type BBox as B, type CdlDiagram as C, EDGE_HEAD_DEFAULT as D, type EdgeStyle as E, EDGE_HEAD_FILLS as F, EDGE_HEAD_FILL_DEFAULT as G, EDGE_REVEALS as H, EDGE_REVEAL_DEFAULT as I, type JourneyEmotion as J, EDGE_STYLES as K, type LaidDiagram as L, type EdgeReveal as M, type NodeKind as N, type InteractiveHandlerMap as O, type InteractiveSignalsAccessor as P, type QuadrantKey as Q, type RowMark as R, type Signal as S, type Tone as T, type LaidLane as U, NODE_KINDS as V, RELATION_FOCUSES as W, RELATION_FOCUS_DEFAULT as X, type RelationFocus as Y, type ScrollProgressHandle as Z, TONES as _, type CdlInput as a, computeFormulaKey as a0, computeInputSignalKey as a1, computeScrollTriggerKey as a2, computed as a3, createInteractiveSignalsAccessor as a4, createScrollProgressHandle as a5, createScrollProgressSignals as a6, hasInteractivePrimitives as a7, resolveLang as a8, sequenceBoardMetrics as a9, sequenceStepId as aa, signal as ab, 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 CdlState as h, type CdlInputOption as i, type CdlEventTarget as j, type CdlLang as k, type Computed as l, type CdlDerivedValue as m, type CdlScrollTrigger as n, type CdlDiagramViewProps as o, type LaidEdge as p, type LaidNode as q, type CdlEdge as r, type TreeNodePayload as s, CdlDiagramView as t, type CdlEventBinding as u, type CdlEventKind as v, type CdlFormula as w, type CdlPhase as x, type ChartPieForm as y, type ChartSlopeForm as z };
3071
+ 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 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cardenelabs/cdl",
3
- "version": "0.121.0",
3
+ "version": "0.123.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,16 +57,6 @@
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
- },
70
60
  "peerDependencies": {
71
61
  "react": "^19.0.0",
72
62
  "react-dom": "^19.0.0"
@@ -78,5 +68,12 @@
78
68
  "typedoc": "^0.28.19",
79
69
  "typedoc-plugin-markdown": "^4.12.0",
80
70
  "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"
81
78
  }
82
- }
79
+ }
package/src/builder.ts CHANGED
@@ -15,8 +15,10 @@ import type {
15
15
  CdlReadout,
16
16
  CdlScrollTrigger,
17
17
  CdlState,
18
+ CdlTextMetrics,
18
19
  EdgeHead,
19
20
  EdgeHeadFill,
21
+ EdgeRouting,
20
22
  EdgeStyle,
21
23
  EdgeRole,
22
24
  NodeKind,
@@ -1461,7 +1463,7 @@ export type DiagramBuilder = {
1461
1463
  tailLabel?: string;
1462
1464
  labelOffsetX?: number;
1463
1465
  labelOffsetY?: number;
1464
- routing?: "default" | "back-detour";
1466
+ routing?: EdgeRouting;
1465
1467
  /** CAR-549 SSOT ... true で label を edge path 上に重ねる (flowchart condition label 用) */
1466
1468
  overlay?: boolean;
1467
1469
  /** signal binding: stroke-width template (CAR edge-signal-binding) */
@@ -1573,7 +1575,7 @@ export type DiagramBuilder = {
1573
1575
  tailLabel?: string;
1574
1576
  labelOffsetX?: number;
1575
1577
  labelOffsetY?: number;
1576
- routing?: "default" | "back-detour";
1578
+ routing?: EdgeRouting;
1577
1579
  /** CAR-549 SSOT ... true で label を edge path 上に重ねる (flowchart condition label 用) */
1578
1580
  overlay?: boolean;
1579
1581
  }>,
@@ -1662,6 +1664,8 @@ export function diagram(
1662
1664
  * 描画側が書く固定の字の言語 (#847)。 詳しくは `CdlDiagram.lang` の説明を参照。
1663
1665
  */
1664
1666
  lang?: CdlLang;
1667
+ /** 図の字幅を測る規則。詳しくは `CdlDiagram.textMetrics` の説明を参照。 */
1668
+ textMetrics?: CdlTextMetrics;
1665
1669
  /**
1666
1670
  * この図がどの種類か (#645)。 詳しくは `CdlDiagram.type` の説明を参照。
1667
1671
  */
@@ -3817,6 +3821,8 @@ export function diagram(
3817
3821
  ...(options.palette !== undefined ? { palette: options.palette } : {}),
3818
3822
  // 言語も素通しする。 型の外の値を既定に落とすのは `layout` の 1 箇所に置く
3819
3823
  ...(options.lang !== undefined ? { lang: options.lang } : {}),
3824
+ // 字幅の測り方も素通しする。型の外の値を既定に落とすのは `layout` の 1 箇所に置く
3825
+ ...(options.textMetrics !== undefined ? { textMetrics: options.textMetrics } : {}),
3820
3826
  // 種類の名前も同じ経路で渡し、空文字の扱いを `layout` の 1 箇所に揃える
3821
3827
  ...(options.type !== undefined ? { type: options.type } : {}),
3822
3828
  // inputs / formulas / scrollTriggers / eventBindings は空なら省略 (static diagram の JSON 形状維持)
package/src/index.ts CHANGED
@@ -15,6 +15,7 @@ export type { CdlInput, CdlInputOption } from "./types";
15
15
  // 字を直すたびに利用側が壊れる
16
16
  export { resolveLang } from "./lang";
17
17
  export type { CdlLang } from "./lang";
18
+ export { resolveTextMetrics } from "./text-metrics";
18
19
 
19
20
  // formula primitive (CAR #244) — builder chain `.formula(id, "gasBase * 2")` で safe subset の
20
21
  // algebraic expression を追加、 createFormulaComputeds(diagram, signals) で computed を得て
@@ -309,9 +310,11 @@ export type {
309
310
  CdlNode,
310
311
  CdlPhase,
311
312
  CdlState,
313
+ CdlTextMetrics,
312
314
  EdgeHead,
313
315
  EdgeHeadFill,
314
316
  EdgeReveal,
317
+ EdgeRouting,
315
318
  RelationFocus,
316
319
  RowMark,
317
320
  EdgeStyle,
@@ -1,3 +1,4 @@
1
+ import type { CdlTextMetrics } from "../types";
1
2
  import { textWidth } from "./text-width";
2
3
  import { MIN_TEXT_SIZE } from "./compact-title";
3
4
 
@@ -50,6 +51,13 @@ export type 積んだ行 = 積む行 & {
50
51
  dy: number;
51
52
  };
52
53
 
54
+ /** tree の箱を描画と検査で同じ字幅規則にそろえる。 */
55
+ export function 箱の字を測る(
56
+ textMetrics: CdlTextMetrics = "proportional",
57
+ ): (text: string, fontSize: number) => number {
58
+ return (text, fontSize) => textWidth(text, fontSize, 0, textMetrics);
59
+ }
60
+
53
61
  /**
54
62
  * 幅に収まるところまでで切り、 切った時は末尾に `…` を付ける。
55
63
  *
@@ -63,7 +71,7 @@ export function 幅で切る(
63
71
  text: string,
64
72
  fontSize: number,
65
73
  使える幅: number,
66
- 測る: (s: string, size: number) => number = textWidth,
74
+ 測る: (s: string, size: number) => number,
67
75
  ): string {
68
76
  if (使える幅 <= 0) return "";
69
77
  if (測る(text, fontSize) <= 使える幅) return text;
@@ -112,7 +120,7 @@ export function 幅に収める(
112
120
  text: string,
113
121
  fontSize: number,
114
122
  使える幅: number,
115
- 測る: (s: string, size: number) => number = textWidth,
123
+ 測る: (s: string, size: number) => number,
116
124
  ): { text: string; fontSize: number } {
117
125
  let size = fontSize;
118
126
  while (size > 縮めの下限 && 測る(text, size) > 使える幅) size -= 1;
@@ -143,7 +151,12 @@ function 書記素に分ける(text: string): string[] {
143
151
  *
144
152
  * 返す `dy` は箱の中心から見たずれで、 行の塊を上下の中央に置いた時の baseline になる。
145
153
  */
146
- export function 箱に積む(行: 積む行[], 箱の幅: number, 箱の高さ: number): 積んだ行[] {
154
+ export function 箱に積む(
155
+ 行: 積む行[],
156
+ 箱の幅: number,
157
+ 箱の高さ: number,
158
+ 測る: (s: string, size: number) => number,
159
+ ): 積んだ行[] {
147
160
  const 候補 = 行.filter((r, i) => i === 0 || r.text.trim() !== "");
148
161
  if (候補.length === 0) return [];
149
162
 
@@ -159,7 +172,7 @@ export function 箱に積む(行: 積む行[], 箱の幅: number, 箱の高さ:
159
172
  const 幅 = 使える幅(箱の幅);
160
173
  // **幅に収めるのを先に済ませる** (#534)。 縮めると行の高さも変わるため、後から縮めると
161
174
  // `dy` が縮める前の大きさで出て、行の塊が箱の上下の中央からずれる
162
- const 収めた = 残す.map((r) => ({ ...r, ...幅に収める(r.text, r.fontSize, 幅) }));
175
+ const 収めた = 残す.map((r) => ({ ...r, ...幅に収める(r.text, r.fontSize, 幅, 測る) }));
163
176
 
164
177
  const 総高 = 高さ(収めた);
165
178
  let 積み = -総高 / 2;
@@ -120,6 +120,9 @@ export function ChartBarNode({
120
120
  * 4 本に 4 色を配ると「色に意味がある」 と誤読する余地が残る。
121
121
  */
122
122
  const 濃さ = (v: number): number => (vMax > 0 ? (v >= vMax ? 1 : 0.45) : 0.9);
123
+ /** `data-cdl-emphasis` はいちばん大きい棒すべてに付ける。 `濃さ` が 1 の棒と同じ条件で、
124
+ * `chart-bar-look.test.tsx` の `(#1008)` が両方を同じ並びで見張る。 */
125
+ const 主役の棒か = (v: number): boolean => vMax > 0 && v >= vMax;
123
126
  /** 角丸は棒の太さに合わせる。 太い棒に固定値を当てると直角に見える */
124
127
  const 角丸 = Math.min(barW * 0.06, 8);
125
128
 
@@ -190,6 +193,7 @@ export function ChartBarNode({
190
193
  <rect
191
194
  data-cdl-role="chart-bar"
192
195
  data-cdl-unresolved={d.unresolved ? "true" : undefined}
196
+ data-cdl-emphasis={主役の棒か(d.value) ? "primary" : undefined}
193
197
  x={bx}
194
198
  y={by}
195
199
  width={barW}
@@ -1,5 +1,5 @@
1
1
  import { useId, type JSX } from "react";
2
- import type { LaidNode, Tone } from "../types";
2
+ import type { CdlTextMetrics, LaidNode, Tone } from "../types";
3
3
  import { TONE, chartSeriesColor } from "../render/tone";
4
4
  import { resolveChartData } from "../render/payload-binding";
5
5
  import { textWidth } from "./text-width";
@@ -44,6 +44,7 @@ export function ChartGaugeNode({
44
44
  stateValues = {},
45
45
  drawing = false,
46
46
  progress = 1,
47
+ textMetrics = "proportional",
47
48
  }: {
48
49
  node: LaidNode;
49
50
  active: boolean;
@@ -52,6 +53,7 @@ export function ChartGaugeNode({
52
53
  drawing?: boolean;
53
54
  /** phase の進み (0→1)。 `drawing` が true の時だけ読む */
54
55
  progress?: number;
56
+ textMetrics?: CdlTextMetrics;
55
57
  }): JSX.Element {
56
58
  const x0 = node.cx - node.w / 2;
57
59
  const y0 = node.cy - node.h / 2;
@@ -99,7 +101,11 @@ export function ChartGaugeNode({
99
101
  * 防ぐ下限として置いている。 万一この関係が崩れても印は区画に合わせて縮む (下の `Math.min`)。
100
102
  */
101
103
  const 割り付ける = (件たち: typeof data): { 行の件数: number; 段数: number } => {
102
- const 幅 = Math.max(印の幅, ...件たち.map((d) => textWidth(数値の行(d), 数値の級))) + 区画の間;
104
+ const 幅 =
105
+ Math.max(
106
+ 印の幅,
107
+ ...件たち.map((d) => textWidth(数値の行(d), 数値の級, 0, textMetrics)),
108
+ ) + 区画の間;
103
109
  const 行の件数 = Math.min(
104
110
  Math.max(1, Math.floor(使える幅 / Math.max(幅, 1))),
105
111
  Math.max(件たち.length, 1),
@@ -256,7 +262,7 @@ export function ChartGaugeNode({
256
262
  */
257
263
  const 合計の文字 = 実値表記(Math.round(total));
258
264
  /** 級 1 あたりの字幅 */
259
- const 単位幅 = Math.max(textWidth(合計の文字, 100), 1) / 100;
265
+ const 単位幅 = Math.max(textWidth(合計の文字, 100, 0, textMetrics), 1) / 100;
260
266
  const 級の候補 = Math.min(
261
267
  // 横 = 字の上の角が内側の円に入る大きさ
262
268
  内半径 / Math.sqrt((単位幅 / 2) ** 2 + 1.25 ** 2),
@@ -395,7 +401,7 @@ export function ChartGaugeNode({
395
401
  * 切れ目が値の途中に来た行だけ色が付かない形になる。
396
402
  */
397
403
  const 数の行 = 内訳の数値[idx] ?? "";
398
- const 切った数の行 = 切り詰める(数の行, 収まる幅, 数値の級);
404
+ const 切った数の行 = 切り詰める(数の行, 収まる幅, 数値の級, textMetrics);
399
405
  return (
400
406
  <g key={`legend-${idx}`} data-cdl-role="chart-gauge-item">
401
407
  <rect
@@ -413,7 +419,7 @@ export function ChartGaugeNode({
413
419
  fontWeight={700}
414
420
  fill="var(--cdl-text, #1a1f2a)"
415
421
  >
416
- {切り詰める(d.label ?? "", 収まる幅, 名前の級)}
422
+ {切り詰める(d.label ?? "", 収まる幅, 名前の級, textMetrics)}
417
423
  </text>
418
424
  {/* **数値も切り詰める** = 切らないと隣の区画の字とくっつく (cdl#731) */}
419
425
  <text x={x} y={y + 46} fontSize={数値の級} fill="var(--cdl-text-dim, #5a6270)">
@@ -445,12 +451,17 @@ function 極(cx: number, cy: number, r: number, a: number): { x: number; y: numb
445
451
  * 切らないと隣の区画の字に重なる = どちらの件の名前かが読めなくなる。 切ったことが分かるよう
446
452
  * `…` を付ける。
447
453
  */
448
- function 切り詰める(s: string, 上限: number, 級: number): string {
449
- if (上限 <= 0 || textWidth(s, 級) <= 上限) return s;
450
- const 記号幅 = textWidth("…", 級);
454
+ function 切り詰める(
455
+ s: string,
456
+ 上限: number,
457
+ 級: number,
458
+ textMetrics: CdlTextMetrics = "proportional",
459
+ ): string {
460
+ if (上限 <= 0 || textWidth(s, 級, 0, textMetrics) <= 上限) return s;
461
+ const 記号幅 = textWidth("…", 級, 0, textMetrics);
451
462
  let out = "";
452
463
  for (const c of s) {
453
- if (textWidth(out + c, 級) + 記号幅 > 上限) break;
464
+ if (textWidth(out + c, 級, 0, textMetrics) + 記号幅 > 上限) break;
454
465
  out += c;
455
466
  }
456
467
  return `${out}…`;