@cardenelabs/cdl 0.23.0 → 0.25.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.
@@ -109,6 +109,8 @@ type Side = "top" | "right" | "bottom" | "left";
109
109
  * ER の「識別しない」 のように **刻みが意味を持つ関係** はこちらを使う。
110
110
  */
111
111
  type EdgeStyle = "solid" | "dotted-flow" | "dashed";
112
+ /** 辺の役目 (#618)。 `main` = 図の主となる道 */
113
+ type EdgeRole = "main";
112
114
  /**
113
115
  * 線の種類の全種 (#578)。 **型だけでなく実体を持つ**。
114
116
  *
@@ -291,6 +293,16 @@ type CdlNode = {
291
293
  * 空文字の行と `null` の印を組にすると **群の間** になる (持ち物と振る舞いの間など)。
292
294
  */
293
295
  rowMarks?: readonly (RowMark | null)[];
296
+ /**
297
+ * 行に 1 行おきの縞を敷くか (#1553)。
298
+ *
299
+ * 表の箱は小さい字が密に並ぶので、行を横に追う時の目印が無い。
300
+ * 色を増やさずに、面を 1 行おきに変えて追えるようにする。
301
+ *
302
+ * **既定では敷かない**。 `er` が設計の形で立てた実体だけが持つ。
303
+ * クラス図も同じ `storage` を使うが、持ち物と振る舞いは行頭の印で既に分かれている。
304
+ */
305
+ rowStripe?: boolean;
294
306
  /**
295
307
  * 時系列のやり取りの中身 (#578)。 `sequence-board` 種別が読む。
296
308
  *
@@ -572,6 +584,21 @@ type CdlEdge = {
572
584
  side?: Side;
573
585
  /** visual style、 default "solid" */
574
586
  style?: EdgeStyle;
587
+ /**
588
+ * 辺の役目 (#618)。 書かなければ従来どおり tone の色で引く。
589
+ *
590
+ * `main` を書いた辺だけ「いま」 の色 (`--cdl-now`) で引く。 図の中で 1 本の道を
591
+ * 目立たせ、 どこから読むかを決めるための口。 すべての辺に書くと差が消えるので、
592
+ * 主となる 1 本 (または 1 続き) にだけ書く。
593
+ */
594
+ role?: EdgeRole;
595
+ /**
596
+ * 名前の下地を敷くか (#618)。 書かなければ敷く (従来どおり)。
597
+ *
598
+ * 丸い下地は箱と同じ形なので、 名前が小さな箱に見える。 罫の細い図では外したほうが
599
+ * 線と名前の区別が付く。
600
+ */
601
+ labelPlate?: boolean;
575
602
  /** 端の形、 default "triangle" (#560)。 線の種類とは別の軸 */
576
603
  head?: EdgeHead;
577
604
  /**
@@ -2415,6 +2442,17 @@ type CdlDiagram = {
2415
2442
  * 既定は `"extract"` = 付け忘れても検知が緩まない向き。
2416
2443
  */
2417
2444
  structuredData?: "extract" | "exclude";
2445
+ /**
2446
+ * この図をどの配色で描くか (#1553)。 書かない図は消費側の既定で描かれる。
2447
+ *
2448
+ * **cdl は色の値を持たない**。 名前を `data-cdl-palette` として markup に出すだけで、
2449
+ * 実際の値は消費側 (dragon の theme 等) が名前を見て決める。 値を engine に持たせると、
2450
+ * 消費側が配色を変えるたびに engine の release が要る。
2451
+ *
2452
+ * 空文字は書かなかった扱いにする = 空を出すと消費側の `[data-cdl-palette]` が
2453
+ * 既定の図にも当たる。
2454
+ */
2455
+ palette?: string;
2418
2456
  /**
2419
2457
  * 矢印をいつ出すか (既定 = `"phase"`、 #582)。
2420
2458
  *
@@ -2612,6 +2650,8 @@ type BBox = {
2612
2650
  type LaidDiagram = {
2613
2651
  id: string;
2614
2652
  topic: string;
2653
+ /** どの配色で描くか (#1553)。 `CdlDiagram.palette` をそのまま持ち越す */
2654
+ palette?: string;
2615
2655
  viewBox: {
2616
2656
  x: number;
2617
2657
  y: number;
@@ -2810,4 +2850,4 @@ type CdlDiagramViewProps = {
2810
2850
  };
2811
2851
  declare function CdlDiagramView({ diagram, laid: laidProp, hideHeader, hideMiniPhaseIndicator, focusPhaseId, debug, emitGeometryWarn, headingLevel, interactiveHandlers }: CdlDiagramViewProps): JSX.Element;
2812
2852
 
2813
- export { sequenceBoardMetrics as $, EDGE_STYLES as A, type BBox as B, type CdlDiagram as C, type EdgeReveal as D, type EdgeStyle as E, type InteractiveSignalsAccessor as F, type LaidLane as G, NODE_KINDS as H, type InteractiveHandlerMap as I, type JourneyEmotion as J, type ScrollProgressHandle as K, type LaidDiagram as L, TONES as M, type NodeKind as N, computeEventBindingKey as O, computeFormulaKey as P, type QuadrantKey as Q, type RowMark as R, type Signal as S, type Tone as T, computeInputSignalKey as U, computeScrollTriggerKey as V, computed as W, createInteractiveSignalsAccessor as X, createScrollProgressHandle as Y, createScrollProgressSignals as Z, hasInteractivePrimitives as _, type CdlInput as a, sequenceStepId as a0, signal as a1, type CdlLane as b, type CdlNode as c, type Side as d, type EdgeHead as e, type EdgeHeadFill as f, type CdlState as g, type CdlEventTarget as h, type Computed as i, type CdlDerivedValue as j, type CdlScrollTrigger as k, type CdlDiagramViewProps as l, type LaidEdge as m, type LaidNode as n, type CdlEdge as o, CdlDiagramView as p, type CdlEventBinding as q, type CdlEventKind as r, type CdlFormula as s, type CdlPhase as t, EDGE_HEADS as u, EDGE_HEAD_DEFAULT as v, EDGE_HEAD_FILLS as w, EDGE_HEAD_FILL_DEFAULT as x, EDGE_REVEALS as y, EDGE_REVEAL_DEFAULT as z };
2853
+ export { hasInteractivePrimitives as $, EDGE_REVEAL_DEFAULT as A, type BBox as B, type CdlDiagram as C, EDGE_STYLES as D, type EdgeStyle as E, type EdgeReveal as F, type InteractiveSignalsAccessor as G, type LaidLane as H, type InteractiveHandlerMap as I, type JourneyEmotion as J, NODE_KINDS as K, type LaidDiagram as L, type ScrollProgressHandle as M, type NodeKind as N, TONES as O, computeEventBindingKey as P, type QuadrantKey as Q, type RowMark as R, type Signal as S, type Tone as T, computeFormulaKey as U, computeInputSignalKey as V, computeScrollTriggerKey as W, computed as X, createInteractiveSignalsAccessor as Y, createScrollProgressHandle as Z, createScrollProgressSignals as _, type CdlInput as a, sequenceBoardMetrics as a0, sequenceStepId as a1, signal as a2, 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 CdlEventTarget as i, type Computed as j, type CdlDerivedValue as k, type CdlScrollTrigger as l, type CdlDiagramViewProps as m, type LaidEdge as n, type LaidNode as o, type CdlEdge as p, CdlDiagramView as q, type CdlEventBinding as r, type CdlEventKind as s, type CdlFormula as t, type CdlPhase as u, EDGE_HEADS as v, EDGE_HEAD_DEFAULT as w, EDGE_HEAD_FILLS as x, EDGE_HEAD_FILL_DEFAULT as y, EDGE_REVEALS as z };
@@ -109,6 +109,8 @@ type Side = "top" | "right" | "bottom" | "left";
109
109
  * ER の「識別しない」 のように **刻みが意味を持つ関係** はこちらを使う。
110
110
  */
111
111
  type EdgeStyle = "solid" | "dotted-flow" | "dashed";
112
+ /** 辺の役目 (#618)。 `main` = 図の主となる道 */
113
+ type EdgeRole = "main";
112
114
  /**
113
115
  * 線の種類の全種 (#578)。 **型だけでなく実体を持つ**。
114
116
  *
@@ -291,6 +293,16 @@ type CdlNode = {
291
293
  * 空文字の行と `null` の印を組にすると **群の間** になる (持ち物と振る舞いの間など)。
292
294
  */
293
295
  rowMarks?: readonly (RowMark | null)[];
296
+ /**
297
+ * 行に 1 行おきの縞を敷くか (#1553)。
298
+ *
299
+ * 表の箱は小さい字が密に並ぶので、行を横に追う時の目印が無い。
300
+ * 色を増やさずに、面を 1 行おきに変えて追えるようにする。
301
+ *
302
+ * **既定では敷かない**。 `er` が設計の形で立てた実体だけが持つ。
303
+ * クラス図も同じ `storage` を使うが、持ち物と振る舞いは行頭の印で既に分かれている。
304
+ */
305
+ rowStripe?: boolean;
294
306
  /**
295
307
  * 時系列のやり取りの中身 (#578)。 `sequence-board` 種別が読む。
296
308
  *
@@ -572,6 +584,21 @@ type CdlEdge = {
572
584
  side?: Side;
573
585
  /** visual style、 default "solid" */
574
586
  style?: EdgeStyle;
587
+ /**
588
+ * 辺の役目 (#618)。 書かなければ従来どおり tone の色で引く。
589
+ *
590
+ * `main` を書いた辺だけ「いま」 の色 (`--cdl-now`) で引く。 図の中で 1 本の道を
591
+ * 目立たせ、 どこから読むかを決めるための口。 すべての辺に書くと差が消えるので、
592
+ * 主となる 1 本 (または 1 続き) にだけ書く。
593
+ */
594
+ role?: EdgeRole;
595
+ /**
596
+ * 名前の下地を敷くか (#618)。 書かなければ敷く (従来どおり)。
597
+ *
598
+ * 丸い下地は箱と同じ形なので、 名前が小さな箱に見える。 罫の細い図では外したほうが
599
+ * 線と名前の区別が付く。
600
+ */
601
+ labelPlate?: boolean;
575
602
  /** 端の形、 default "triangle" (#560)。 線の種類とは別の軸 */
576
603
  head?: EdgeHead;
577
604
  /**
@@ -2415,6 +2442,17 @@ type CdlDiagram = {
2415
2442
  * 既定は `"extract"` = 付け忘れても検知が緩まない向き。
2416
2443
  */
2417
2444
  structuredData?: "extract" | "exclude";
2445
+ /**
2446
+ * この図をどの配色で描くか (#1553)。 書かない図は消費側の既定で描かれる。
2447
+ *
2448
+ * **cdl は色の値を持たない**。 名前を `data-cdl-palette` として markup に出すだけで、
2449
+ * 実際の値は消費側 (dragon の theme 等) が名前を見て決める。 値を engine に持たせると、
2450
+ * 消費側が配色を変えるたびに engine の release が要る。
2451
+ *
2452
+ * 空文字は書かなかった扱いにする = 空を出すと消費側の `[data-cdl-palette]` が
2453
+ * 既定の図にも当たる。
2454
+ */
2455
+ palette?: string;
2418
2456
  /**
2419
2457
  * 矢印をいつ出すか (既定 = `"phase"`、 #582)。
2420
2458
  *
@@ -2612,6 +2650,8 @@ type BBox = {
2612
2650
  type LaidDiagram = {
2613
2651
  id: string;
2614
2652
  topic: string;
2653
+ /** どの配色で描くか (#1553)。 `CdlDiagram.palette` をそのまま持ち越す */
2654
+ palette?: string;
2615
2655
  viewBox: {
2616
2656
  x: number;
2617
2657
  y: number;
@@ -2810,4 +2850,4 @@ type CdlDiagramViewProps = {
2810
2850
  };
2811
2851
  declare function CdlDiagramView({ diagram, laid: laidProp, hideHeader, hideMiniPhaseIndicator, focusPhaseId, debug, emitGeometryWarn, headingLevel, interactiveHandlers }: CdlDiagramViewProps): JSX.Element;
2812
2852
 
2813
- export { sequenceBoardMetrics as $, EDGE_STYLES as A, type BBox as B, type CdlDiagram as C, type EdgeReveal as D, type EdgeStyle as E, type InteractiveSignalsAccessor as F, type LaidLane as G, NODE_KINDS as H, type InteractiveHandlerMap as I, type JourneyEmotion as J, type ScrollProgressHandle as K, type LaidDiagram as L, TONES as M, type NodeKind as N, computeEventBindingKey as O, computeFormulaKey as P, type QuadrantKey as Q, type RowMark as R, type Signal as S, type Tone as T, computeInputSignalKey as U, computeScrollTriggerKey as V, computed as W, createInteractiveSignalsAccessor as X, createScrollProgressHandle as Y, createScrollProgressSignals as Z, hasInteractivePrimitives as _, type CdlInput as a, sequenceStepId as a0, signal as a1, type CdlLane as b, type CdlNode as c, type Side as d, type EdgeHead as e, type EdgeHeadFill as f, type CdlState as g, type CdlEventTarget as h, type Computed as i, type CdlDerivedValue as j, type CdlScrollTrigger as k, type CdlDiagramViewProps as l, type LaidEdge as m, type LaidNode as n, type CdlEdge as o, CdlDiagramView as p, type CdlEventBinding as q, type CdlEventKind as r, type CdlFormula as s, type CdlPhase as t, EDGE_HEADS as u, EDGE_HEAD_DEFAULT as v, EDGE_HEAD_FILLS as w, EDGE_HEAD_FILL_DEFAULT as x, EDGE_REVEALS as y, EDGE_REVEAL_DEFAULT as z };
2853
+ export { hasInteractivePrimitives as $, EDGE_REVEAL_DEFAULT as A, type BBox as B, type CdlDiagram as C, EDGE_STYLES as D, type EdgeStyle as E, type EdgeReveal as F, type InteractiveSignalsAccessor as G, type LaidLane as H, type InteractiveHandlerMap as I, type JourneyEmotion as J, NODE_KINDS as K, type LaidDiagram as L, type ScrollProgressHandle as M, type NodeKind as N, TONES as O, computeEventBindingKey as P, type QuadrantKey as Q, type RowMark as R, type Signal as S, type Tone as T, computeFormulaKey as U, computeInputSignalKey as V, computeScrollTriggerKey as W, computed as X, createInteractiveSignalsAccessor as Y, createScrollProgressHandle as Z, createScrollProgressSignals as _, type CdlInput as a, sequenceBoardMetrics as a0, sequenceStepId as a1, signal as a2, 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 CdlEventTarget as i, type Computed as j, type CdlDerivedValue as k, type CdlScrollTrigger as l, type CdlDiagramViewProps as m, type LaidEdge as n, type LaidNode as o, type CdlEdge as p, CdlDiagramView as q, type CdlEventBinding as r, type CdlEventKind as s, type CdlFormula as t, type CdlPhase as u, EDGE_HEADS as v, EDGE_HEAD_DEFAULT as w, EDGE_HEAD_FILLS as x, EDGE_HEAD_FILL_DEFAULT as y, EDGE_REVEALS as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cardenelabs/cdl",
3
- "version": "0.23.0",
3
+ "version": "0.25.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",
package/src/builder.ts CHANGED
@@ -15,6 +15,7 @@ import type {
15
15
  EdgeHead,
16
16
  EdgeHeadFill,
17
17
  EdgeStyle,
18
+ EdgeRole,
18
19
  NodeKind,
19
20
  Side,
20
21
  Tone,
@@ -1435,6 +1436,10 @@ export type DiagramBuilder = {
1435
1436
  tone?: Tone;
1436
1437
  side?: Side;
1437
1438
  style?: EdgeStyle;
1439
+ /** 辺の役目 (#618)。 `main` を書いた辺だけ「いま」 の色で引く */
1440
+ role?: EdgeRole;
1441
+ /** 名前の下地を敷くか (#618)。 書かなければ敷く */
1442
+ labelPlate?: boolean;
1438
1443
  /** 端の形 (#560)、 既定は塗った三角。 線の種類とは別の軸 */
1439
1444
  head?: EdgeHead;
1440
1445
  /** 端の印の塗り (#578)。 書かなければ形ごとの既定 */
@@ -1633,6 +1638,10 @@ export function diagram(
1633
1638
  * `"exclude"` を付ける。 詳細は `CdlDiagram.structuredData` の doc 参照。
1634
1639
  */
1635
1640
  structuredData?: "extract" | "exclude";
1641
+ /**
1642
+ * この図をどの配色で描くか (#1553)。 詳細は `CdlDiagram.palette` の doc 参照。
1643
+ */
1644
+ palette?: string;
1636
1645
  },
1637
1646
  ): DiagramBuilder {
1638
1647
  const lanes: CdlLane[] = [];
@@ -1755,6 +1764,8 @@ export function diagram(
1755
1764
  tone: opts.tone ?? "accent",
1756
1765
  side: opts.side,
1757
1766
  style: opts.style,
1767
+ role: opts.role,
1768
+ labelPlate: opts.labelPlate,
1758
1769
  head: opts.head,
1759
1770
  headFill: opts.headFill,
1760
1771
  tailHead: opts.tailHead,
@@ -3767,6 +3778,8 @@ export function diagram(
3767
3778
  topic: options.topic,
3768
3779
  // 既定 (extract) は省略して JSON 形状を変えない。 exclude を明示した図だけ持たせる。
3769
3780
  ...(options.structuredData === "exclude" ? { structuredData: "exclude" as const } : {}),
3781
+ // 配色の名前は素通しする。 空文字の遮断は `layout` が持つ (後段が必ず通るため)
3782
+ ...(options.palette !== undefined ? { palette: options.palette } : {}),
3770
3783
  // inputs / formulas / scrollTriggers / eventBindings は空なら省略 (static diagram の JSON 形状維持)
3771
3784
  ...(inputs.length > 0 ? { inputs: [...inputs] } : {}),
3772
3785
  ...(formulas.length > 0 ? { formulas: [...formulas] } : {}),
@@ -39,12 +39,71 @@ type KindStyle = {
39
39
  icon: string;
40
40
  /** shape: rect (default) / cylinder / cloud / diamond / hexagon */
41
41
  shape?: "rect" | "cylinder" | "cloud" | "diamond" | "hexagon";
42
+ /**
43
+ * 面と枠の描き方 (#616)。 未指定は従来どおり。
44
+ *
45
+ * 色で kind を分ける形は、 消費側の配色によっては読めない。 dragon の茶墨は
46
+ * kind の色が 4 つとも暗く彩度が低く、 枠線や細い帯の面積では差が出ない
47
+ * (隔たりは 18.6 以上あるが、 面積が小さいと効かない)。 形で分ける口を用意する。
48
+ */
49
+ look?: NodeLook;
42
50
  };
43
51
 
52
+ /** 箱の見せ方。 何を示すかは値の名前が持つ */
53
+ export type NodeLook =
54
+ /** 図の外にある = 面を塗らず枠だけ */
55
+ | "outline"
56
+ /** 通り道 = 薄い面。 止まらず抜ける */
57
+ | "tint"
58
+ /** ここが働く = ベタ塗り。 図で最も重い */
59
+ | "solid"
60
+ /** 消えては困る = 底を二重の罫で締める */
61
+ | "ledger"
62
+ /** 失っても作り直せる = 破線の枠 */
63
+ | "dashed";
64
+
65
+ /** 薄い面の濃さ。 `color-mix` を読めない環境では第 2 引数の色に落ちる */
66
+ const TINT_FILL = "color-mix(in srgb, var(--cdl-text, #1a1f2a) 6%, transparent)";
67
+
68
+ /**
69
+ * `look` から面と枠を決める。
70
+ *
71
+ * 未指定は従来どおり (面 = node-fill、 枠 = 光れば kind の色 / 光らなければ divider)。
72
+ * ここを通らない kind の出力は 1 文字も変わらない。
73
+ */
74
+ export function lookPaint(look: NodeLook | undefined, accent: string, active: boolean): {
75
+ fill: string;
76
+ stroke: string;
77
+ strokeWidth: number;
78
+ dash?: string;
79
+ /** ベタ塗りの上では字を紙の色で抜く */
80
+ onSolid: boolean;
81
+ } {
82
+ const 既定枠 = active ? accent : "var(--cdl-divider, #d4d4d8)";
83
+ const 既定太さ = active ? 3 : 1.25;
84
+ switch (look) {
85
+ case "outline":
86
+ return { fill: "none", stroke: 既定枠, strokeWidth: 既定太さ, onSolid: false };
87
+ case "tint":
88
+ return { fill: TINT_FILL, stroke: 既定枠, strokeWidth: 既定太さ, onSolid: false };
89
+ case "solid":
90
+ // 枠は従来どおり (光れば kind の色)。 面だけを墨で塗る = 「光っている節の枠は kind の色」
91
+ // は既存の契約で、 `node-styling` が守っている
92
+ return { fill: "var(--cdl-text, #1a1f2a)", stroke: 既定枠, strokeWidth: 既定太さ, onSolid: true };
93
+ case "ledger":
94
+ return { fill: "var(--cdl-node-fill, #ffffff)", stroke: 既定枠, strokeWidth: 既定太さ, onSolid: false };
95
+ case "dashed":
96
+ return { fill: "none", stroke: 既定枠, strokeWidth: 既定太さ, dash: "6 4", onSolid: false };
97
+ default:
98
+ return { fill: "var(--cdl-node-fill, #ffffff)", stroke: 既定枠, strokeWidth: 既定太さ, onSolid: false };
99
+ }
100
+ }
101
+
44
102
  // 各 kind の visual style (icon は heroicons / lucide 風の minimal SVG path)
45
103
  const STYLES: Partial<Record<NodeKind, KindStyle>> = {
46
104
  // 人系
47
105
  person: {
106
+ look: "outline",
48
107
  accent: "var(--cdl-tone-accent, #2d6a8f)",
49
108
  eyebrowColor: "var(--cdl-tone-accent, #2d6a8f)",
50
109
  icon: "M12 12c2.2 0 4-1.8 4-4s-1.8-4-4-4-4 1.8-4 4 1.8 4 4 4zm0 2c-2.7 0-8 1.3-8 4v2h16v-2c0-2.7-5.3-4-8-4z",
@@ -78,11 +137,13 @@ const STYLES: Partial<Record<NodeKind, KindStyle>> = {
78
137
  shape: "cylinder",
79
138
  },
80
139
  cache: {
140
+ look: "dashed",
81
141
  accent: "var(--cdl-tone-warning, #a67a2e)",
82
142
  eyebrowColor: "var(--cdl-tone-warning, #a67a2e)",
83
143
  icon: "M13 3c-4.97 0-9 4.03-9 9H1l3.89 3.89.07.14L9 12H6c0-3.87 3.13-7 7-7s7 3.13 7 7-3.13 7-7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42C8.27 19.99 10.51 21 13 21c4.97 0 9-4.03 9-9s-4.03-9-9-9zm-1 5v5l4.28 2.54.72-1.21-3.5-2.08V8H12z",
84
144
  },
85
145
  queue: {
146
+ look: "ledger",
86
147
  accent: "var(--cdl-tone-warning, #a67a2e)",
87
148
  eyebrowColor: "var(--cdl-tone-warning, #a67a2e)",
88
149
  icon: "M4 6h16v2H4zm0 5h16v2H4zm0 5h16v2H4z",
@@ -99,6 +160,7 @@ const STYLES: Partial<Record<NodeKind, KindStyle>> = {
99
160
  shape: "cloud",
100
161
  },
101
162
  cdn: {
163
+ look: "tint",
102
164
  accent: "var(--cdl-tone-info, #5a8ec1)",
103
165
  eyebrowColor: "var(--cdl-tone-info, #5a8ec1)",
104
166
  icon: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z",
@@ -106,6 +168,7 @@ const STYLES: Partial<Record<NodeKind, KindStyle>> = {
106
168
 
107
169
  // アプリ系
108
170
  service: {
171
+ look: "solid",
109
172
  accent: "var(--cdl-tone-teal, #2f8770)",
110
173
  eyebrowColor: "var(--cdl-tone-teal, #2f8770)",
111
174
  icon: "M19.43 12.98c.04-.32.07-.64.07-.98 0-.34-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.39-.3-.61-.22l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65C14.46 2.18 14.25 2 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.23-.09-.49 0-.61.22l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98 0 .33.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46c.12.22.39.3.61.22l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.23.09.49 0 .61-.22l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zM12 15.5c-1.93 0-3.5-1.57-3.5-3.5s1.57-3.5 3.5-3.5 3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z",
@@ -188,6 +251,7 @@ export function GenericNode({
188
251
 
189
252
  // shape 別 path
190
253
  const shape = style.shape ?? "rect";
254
+ const paint = lookPaint(style.look, style.accent, active);
191
255
  const shapeEl = (() => {
192
256
  if (shape === "cylinder") {
193
257
  // 円柱 (top ellipse + side + bottom ellipse)
@@ -267,16 +331,23 @@ export function GenericNode({
267
331
  }
268
332
  // default rect
269
333
  return (
270
- <rect
271
- x={0}
272
- y={0}
273
- width={w}
274
- height={h}
275
- rx={18}
276
- fill="var(--cdl-node-fill, #ffffff)"
277
- stroke={active ? style.accent : "var(--cdl-divider, #d4d4d8)"}
278
- strokeWidth={active ? 3 : 1.25}
279
- />
334
+ <>
335
+ <rect
336
+ x={0}
337
+ y={0}
338
+ width={w}
339
+ height={h}
340
+ rx={18}
341
+ fill={paint.fill}
342
+ stroke={paint.stroke}
343
+ strokeWidth={paint.strokeWidth}
344
+ strokeDasharray={paint.dash}
345
+ />
346
+ {/* 台帳 = 底を二重の罫で締める。 消えては困る物という約束を形で示す (#616) */}
347
+ {style.look === "ledger" && (
348
+ <line x1={1} y1={h - 7} x2={w - 1} y2={h - 7} stroke={paint.stroke} strokeWidth={1} />
349
+ )}
350
+ </>
280
351
  );
281
352
  })();
282
353
 
@@ -332,7 +403,14 @@ export function GenericNode({
332
403
  const dominantBaseline = centered ? ("middle" as const) : undefined;
333
404
 
334
405
  return (
335
- <g transform={`translate(${x} ${y})`} data-cdl-role="node-body">
406
+ <g
407
+ transform={`translate(${x} ${y})`}
408
+ data-cdl-role="node-body"
409
+ /* 見せ方を持つ箱だけに出す目印 (#620)。 消費側が `:not([data-cdl-look])` で
410
+ 従来どおりの箱だけを上書きの対象にできる。 持たない kind には出さない
411
+ = 空文字を出すと選択子が効かなくなる */
412
+ data-cdl-look={style.look}
413
+ >
336
414
  {shapeEl}
337
415
  {/* icon */}
338
416
  <g transform={`translate(${iconX} ${iconY}) scale(1)`} data-cdl-role="node-kind-icon">
@@ -340,15 +418,15 @@ export function GenericNode({
340
418
  <path d={style.icon} fill={style.eyebrowColor} opacity={0.7} transform="scale(1)" />
341
419
  </g>
342
420
  {node.eyebrow && (
343
- <text x={textX} y={eyebrowY} fontSize={17} fontWeight={700} letterSpacing={1.5} fill={style.eyebrowColor} textAnchor={textAnchor} dominantBaseline={dominantBaseline}>
421
+ <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}>
344
422
  {node.eyebrow}
345
423
  </text>
346
424
  )}
347
- <text data-cdl-role="node-label" x={textX} y={titleY} fontSize={20} fontWeight={700} fill="var(--cdl-text, #1a1f2a)" textAnchor={textAnchor} dominantBaseline={dominantBaseline} style={node.kind === "function" || node.kind === "api" ? { fontFamily: "'JetBrains Mono', monospace" } : undefined}>
425
+ <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}>
348
426
  {node.title}
349
427
  </text>
350
428
  {node.subtitle && (
351
- <text x={textX} y={subtitleY} fontSize={17} fill="var(--cdl-text-dim, #5a6270)" textAnchor={textAnchor} dominantBaseline={dominantBaseline} style={{ fontFamily: "'JetBrains Mono', monospace" }}>
429
+ <text x={textX} y={subtitleY} fontSize={17} fill={paint.onSolid ? "var(--cdl-node-fill, #ffffff)" : "var(--cdl-text-dim, #5a6270)"} textAnchor={textAnchor} dominantBaseline={dominantBaseline} style={{ fontFamily: "'JetBrains Mono', monospace" }}>
352
430
  {node.subtitle}
353
431
  </text>
354
432
  )}
@@ -12,6 +12,7 @@ import {
12
12
  SUBTITLE_ROW_SHIFT,
13
13
  rowBaselineY,
14
14
  rowGlyphDepth,
15
+ rowStripeBand,
15
16
  } from "../layout/spec";
16
17
  import {
17
18
  ROW_PAD_X_PX,
@@ -159,6 +160,37 @@ export function StorageNode({
159
160
  stroke={active ? accent : "var(--cdl-divider, #d4d4d8)"}
160
161
  strokeWidth={active ? 3 : 1.25}
161
162
  />
163
+ {/*
164
+ 1 行おきの縞 (#1553)。
165
+
166
+ **面の直後に置く**。 面より前だと塗り潰され、行の字より後だと字を隠す。
167
+ 位置は `rowStripeBand` が baseline から導く = 縞だけが定数を持つと、行送りを
168
+ 変えた時に字だけ動いて縞が取り残される。
169
+
170
+ 帯の左右は枠の内側に 1 だけ寄せる。 枠と同じ x に置くと、角の丸みから帯の角が
171
+ はみ出して四隅に色が出る。
172
+ */}
173
+ {node.rowStripe &&
174
+ rows.map((_, idx) => {
175
+ if (idx % 2 !== 0) return null;
176
+ const 帯 = rowStripeBand("storage", idx, 行の下げ);
177
+ if (!帯) return null;
178
+ // 箱の下端をまたぐ縞は描かない。 行数と箱の高さは独立に決まるので、行が多い図では
179
+ // 最後の行が箱の外に来る
180
+ if (帯.y + 帯.h > node.h) return null;
181
+ return (
182
+ <rect
183
+ key={`stripe-${idx}`}
184
+ data-cdl-role="node-row-stripe"
185
+ data-cdl-row-index={idx}
186
+ x={1}
187
+ y={帯.y}
188
+ width={node.w - 2}
189
+ height={帯.h}
190
+ fill="var(--cdl-row-stripe, var(--cdl-label-bg, #f3f4f6))"
191
+ />
192
+ );
193
+ })}
162
194
  {node.eyebrow && (
163
195
  <text x={26} y={38} fontSize={18} fontWeight={700} letterSpacing={2} fill={accent}>
164
196
  {node.eyebrow}
@@ -204,6 +204,29 @@ export function rowBaselineY(kind: string | undefined, index: number): number |
204
204
  : ROW_TOP_GENERIC + genericTextShift(kind) + index * ROW_PITCH_GENERIC;
205
205
  }
206
206
 
207
+ /**
208
+ * 1 行おきに敷く縞の帯 (#1553)。 node 上端から見た上端 `y` と高さ `h` を返す。
209
+ *
210
+ * **baseline から導く**。 縞だけが自前の定数を持つと、行送りを変えた時に字だけ動いて
211
+ * 縞が取り残される。 行を描かない種別では `null` を返す (`rowBaselineY` と同じ判定)。
212
+ *
213
+ * 帯は baseline を挟む。 上端を baseline に置くと字が帯の上へはみ出すため、
214
+ * 行送りの半分だけ上へ出して行の高さぶんを覆う。
215
+ *
216
+ * `shift` は呼び名で行の塊が下がる分 (`SUBTITLE_ROW_SHIFT`)。 描画側が行に足すのと
217
+ * 同じ値を帯にも足す = 別々に持つと呼び名のある箱でだけ縞と字がずれる。
218
+ */
219
+ export function rowStripeBand(
220
+ kind: string | undefined,
221
+ index: number,
222
+ shift: number,
223
+ ): { y: number; h: number } | null {
224
+ const baseline = rowBaselineY(kind, index);
225
+ if (baseline === null) return null;
226
+ const pitch = kind === "storage" ? ROW_PITCH_STORAGE : ROW_PITCH_GENERIC;
227
+ return { y: baseline + shift - pitch / 2, h: pitch };
228
+ }
229
+
207
230
  /**
208
231
  * `kinds/generic.tsx` が字の塊を下へずらす量 (SSOT)。
209
232
  *
package/src/layout.ts CHANGED
@@ -397,6 +397,12 @@ export function layout(diag: CdlDiagram): LaidDiagram {
397
397
  ...(diag.derived ? { derived: diag.derived } : {}),
398
398
  // 矢印をいつ出すか (#582)。 描き手が読む欄なので、そのまま持ち越す
399
399
  ...(diag.edgeReveal ? { edgeReveal: diag.edgeReveal } : {}),
400
+ // どの配色で描くか (#1553)。 空文字は書かなかった扱いにする = 空の目印を出すと、
401
+ // 消費側の `[data-cdl-palette]` が配色を書いていない図にも当たる。
402
+ //
403
+ // **遮断はここだけに置く**。 描画は必ず組み立てを通り、組み立て API も `er` も
404
+ // 素通しにしてある。 上流にも置くと、どの入力でも通らない分岐が残る
405
+ ...(diag.palette ? { palette: diag.palette } : {}),
400
406
  bboxes,
401
407
  collisions,
402
408
  nearCollisions,
package/src/presets.ts CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  type SequenceBoardMessage,
12
12
  type SequenceBoardMessageKind,
13
13
  } from "./kinds/sequence-board-metrics";
14
- import type { NodeKind, Tone, EdgeStyle, EdgeHead, EdgeHeadFill, RowMark, JourneyEmotion, QuadrantKey, BoundNumber } from "./types";
14
+ import type { NodeKind, Tone, EdgeStyle, EdgeHead, EdgeHeadFill, RowMark, JourneyEmotion, QuadrantKey, BoundNumber, EdgeRole } from "./types";
15
15
 
16
16
  /**
17
17
  * 状態を読む書き方 (`{名前}`)。
@@ -762,6 +762,22 @@ export type ErPreset = {
762
762
  * 広げるため、 狭く宣言すると宣言座標と実座標がずれる)。
763
763
  */
764
764
  entityWidth?: number;
765
+ /**
766
+ * 行に 1 行おきの縞を敷くか (#1553、 既定 = 敷く)。
767
+ *
768
+ * ER 図は小さい字が密に並ぶので、行を横に追う時の目印が無い。
769
+ * 色を増やさずに面を 1 行おきに変えて追えるようにする。
770
+ *
771
+ * 設計の形 (`columns` を書いた実体) だけが対象。 `rows` を直接書いた古い形は
772
+ * 行頭の印を持たないので敷かない。
773
+ */
774
+ rowStripe?: boolean;
775
+ /**
776
+ * この図をどの配色で描くか (#1553)。 書かない図は消費側の既定で描かれる。
777
+ *
778
+ * cdl は色の値を持たない。 名前を markup に出すだけで、実際の値は消費側が決める。
779
+ */
780
+ palette?: string;
765
781
  };
766
782
 
767
783
  export type ErBuilder = {
@@ -785,7 +801,9 @@ export type ErBuilder = {
785
801
  export function er(preset: ErPreset): ErBuilder {
786
802
  const entityW = resolveStorageBoxW(preset.entityWidth, "er", "entityWidth");
787
803
  const tone: Tone = preset.defaultTone ?? "info";
788
- const b = diagram(preset.id, { topic: preset.topic });
804
+ const 縞を敷く = preset.rowStripe ?? true;
805
+ // 配色の名前は素通しする。 空文字の遮断は `diagram` が持つ = ここで重ねると到達しない分岐になる
806
+ const b = diagram(preset.id, { topic: preset.topic, palette: preset.palette });
789
807
 
790
808
  const entities: string[] = [];
791
809
  const phaseActivate: string[] = [];
@@ -829,6 +847,9 @@ export function er(preset: ErPreset): ErBuilder {
829
847
  ...(設計の形 && e.subtitle ? { subtitle: e.subtitle } : {}),
830
848
  rows,
831
849
  ...(設計の形 ? { rowMarks: 印 } : {}),
850
+ // 縞は設計の形だけに敷く (#1553)。 `rows` を直接書いた古い形は行頭の印を持たず、
851
+ // 群の区切りも線で表しているので、縞を足すと手掛かりが 3 つになる
852
+ ...(設計の形 && 縞を敷く ? { rowStripe: true } : {}),
832
853
  });
833
854
  entities.push(e.id);
834
855
  phaseActivate.push(e.id);
@@ -1167,6 +1188,10 @@ export type InfraConnection = {
1167
1188
  sub?: string;
1168
1189
  tone?: Tone;
1169
1190
  style?: EdgeStyle;
1191
+ /** 辺の役目 (#618)。 `main` を書いた辺だけ「いま」 の色で引く */
1192
+ role?: EdgeRole;
1193
+ /** 名前の下地を敷くか (#618)。 書かなければ敷く */
1194
+ labelPlate?: boolean;
1170
1195
  /** label の水平方向 fine offset (default 0)、 preset 交互 ±60 を override */
1171
1196
  labelOffsetX?: number;
1172
1197
  /** label の垂直方向 fine offset (default 0、 preset 交互 ±60 を override) */
@@ -1255,6 +1280,8 @@ export function infrastructure(preset: InfrastructurePreset): InfrastructureBuil
1255
1280
  ...(c.sub ? { sub: c.sub } : {}),
1256
1281
  tone: c.tone ?? tone,
1257
1282
  style: c.style ?? style,
1283
+ ...(c.role ? { role: c.role } : {}),
1284
+ ...(c.labelPlate === undefined ? {} : { labelPlate: c.labelPlate }),
1258
1285
  labelOffsetX: c.labelOffsetX ?? 0,
1259
1286
  labelOffsetY: c.labelOffsetY ?? 0,
1260
1287
  });