@cardenelabs/cdl 0.23.0 → 0.24.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
  *
@@ -572,6 +574,21 @@ type CdlEdge = {
572
574
  side?: Side;
573
575
  /** visual style、 default "solid" */
574
576
  style?: EdgeStyle;
577
+ /**
578
+ * 辺の役目 (#618)。 書かなければ従来どおり tone の色で引く。
579
+ *
580
+ * `main` を書いた辺だけ「いま」 の色 (`--cdl-now`) で引く。 図の中で 1 本の道を
581
+ * 目立たせ、 どこから読むかを決めるための口。 すべての辺に書くと差が消えるので、
582
+ * 主となる 1 本 (または 1 続き) にだけ書く。
583
+ */
584
+ role?: EdgeRole;
585
+ /**
586
+ * 名前の下地を敷くか (#618)。 書かなければ敷く (従来どおり)。
587
+ *
588
+ * 丸い下地は箱と同じ形なので、 名前が小さな箱に見える。 罫の細い図では外したほうが
589
+ * 線と名前の区別が付く。
590
+ */
591
+ labelPlate?: boolean;
575
592
  /** 端の形、 default "triangle" (#560)。 線の種類とは別の軸 */
576
593
  head?: EdgeHead;
577
594
  /**
@@ -2810,4 +2827,4 @@ type CdlDiagramViewProps = {
2810
2827
  };
2811
2828
  declare function CdlDiagramView({ diagram, laid: laidProp, hideHeader, hideMiniPhaseIndicator, focusPhaseId, debug, emitGeometryWarn, headingLevel, interactiveHandlers }: CdlDiagramViewProps): JSX.Element;
2812
2829
 
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 };
2830
+ 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
  *
@@ -572,6 +574,21 @@ type CdlEdge = {
572
574
  side?: Side;
573
575
  /** visual style、 default "solid" */
574
576
  style?: EdgeStyle;
577
+ /**
578
+ * 辺の役目 (#618)。 書かなければ従来どおり tone の色で引く。
579
+ *
580
+ * `main` を書いた辺だけ「いま」 の色 (`--cdl-now`) で引く。 図の中で 1 本の道を
581
+ * 目立たせ、 どこから読むかを決めるための口。 すべての辺に書くと差が消えるので、
582
+ * 主となる 1 本 (または 1 続き) にだけ書く。
583
+ */
584
+ role?: EdgeRole;
585
+ /**
586
+ * 名前の下地を敷くか (#618)。 書かなければ敷く (従来どおり)。
587
+ *
588
+ * 丸い下地は箱と同じ形なので、 名前が小さな箱に見える。 罫の細い図では外したほうが
589
+ * 線と名前の区別が付く。
590
+ */
591
+ labelPlate?: boolean;
575
592
  /** 端の形、 default "triangle" (#560)。 線の種類とは別の軸 */
576
593
  head?: EdgeHead;
577
594
  /**
@@ -2810,4 +2827,4 @@ type CdlDiagramViewProps = {
2810
2827
  };
2811
2828
  declare function CdlDiagramView({ diagram, laid: laidProp, hideHeader, hideMiniPhaseIndicator, focusPhaseId, debug, emitGeometryWarn, headingLevel, interactiveHandlers }: CdlDiagramViewProps): JSX.Element;
2812
2829
 
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 };
2830
+ 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.24.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)。 書かなければ形ごとの既定 */
@@ -1755,6 +1760,8 @@ export function diagram(
1755
1760
  tone: opts.tone ?? "accent",
1756
1761
  side: opts.side,
1757
1762
  style: opts.style,
1763
+ role: opts.role,
1764
+ labelPlate: opts.labelPlate,
1758
1765
  head: opts.head,
1759
1766
  headFill: opts.headFill,
1760
1767
  tailHead: opts.tailHead,
@@ -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
  )}
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
  * 状態を読む書き方 (`{名前}`)。
@@ -1167,6 +1167,10 @@ export type InfraConnection = {
1167
1167
  sub?: string;
1168
1168
  tone?: Tone;
1169
1169
  style?: EdgeStyle;
1170
+ /** 辺の役目 (#618)。 `main` を書いた辺だけ「いま」 の色で引く */
1171
+ role?: EdgeRole;
1172
+ /** 名前の下地を敷くか (#618)。 書かなければ敷く */
1173
+ labelPlate?: boolean;
1170
1174
  /** label の水平方向 fine offset (default 0)、 preset 交互 ±60 を override */
1171
1175
  labelOffsetX?: number;
1172
1176
  /** label の垂直方向 fine offset (default 0、 preset 交互 ±60 を override) */
@@ -1255,6 +1259,8 @@ export function infrastructure(preset: InfrastructurePreset): InfrastructureBuil
1255
1259
  ...(c.sub ? { sub: c.sub } : {}),
1256
1260
  tone: c.tone ?? tone,
1257
1261
  style: c.style ?? style,
1262
+ ...(c.role ? { role: c.role } : {}),
1263
+ ...(c.labelPlate === undefined ? {} : { labelPlate: c.labelPlate }),
1258
1264
  labelOffsetX: c.labelOffsetX ?? 0,
1259
1265
  labelOffsetY: c.labelOffsetY ?? 0,
1260
1266
  });
@@ -3,6 +3,28 @@ import { EDGE_HEAD_DEFAULT, EDGE_HEAD_FILL_DEFAULT, type LaidEdge } from "../typ
3
3
  import { edgeHeadMarkerId } from "./edge-head";
4
4
  import { computeLabelBBoxWorld, hasRenderedLabel, LABEL_PILL_PAD_X } from "../layout/spec";
5
5
  import { hasTone, toneColor } from "./tone";
6
+ import type { EdgeRole, Tone } from "../types";
7
+
8
+ /**
9
+ * 辺の色 (#618)。 役目を書いた辺だけ「いま」 の色で引く。
10
+ *
11
+ * 図の中に道が 2 種 (主となる 1 本と、 そこから枝分かれする預け先) ある時、 どちらも
12
+ * 同じ色だとどこから読むかが決まらない。 主となる道にだけ別の色を当てて順番を作る。
13
+ */
14
+ /**
15
+ * 辺の太さの既定 (#618)。
16
+ *
17
+ * 元は光って 5 / 光らず 2.5 で、 消費側 (dragon) が `!important` で 2.25 に上書きしていた。
18
+ * 実際に画面へ出る 2.25 は箱の枠 (1.75) より太く、 線が箱より前に出ていた。
19
+ * 箱の枠と釣り合う細さへ下げる。
20
+ */
21
+ export function defaultEdgeWidth(active: boolean): number {
22
+ return active ? 2.5 : 1.25;
23
+ }
24
+
25
+ export function edgeColor(edge: { tone: Tone; role?: EdgeRole }): string {
26
+ return edge.role === "main" ? "var(--cdl-now, #c0421f)" : toneColor(edge.tone);
27
+ }
6
28
  import { EDGE_LABEL_TEXT, fontFamilyAttr, type EdgeLabelLine } from "../label-text";
7
29
  import { interpolate, pathSubpath, resolveNumericAttr, shrinkPathEnd } from "./utils";
8
30
 
@@ -39,7 +61,7 @@ export function CdlEdgeView({
39
61
  progress: number;
40
62
  stateValues?: Record<string, string>;
41
63
  }): JSX.Element {
42
- const color = toneColor(edge.tone);
64
+ const color = edgeColor(edge);
43
65
  // 光っていない edge も tone の色で描く。 以前は一律の灰色に落としていたため、 何も光っていない
44
66
  // 静的な図では全 edge が同じ色になり、 因果の向き (警戒 = 赤 / 良好 = 緑) が線から読めなかった。
45
67
  // 光っている / いないの区別は、 実線は太さ (5 対 2.5) と線種 (実線対破線)、 点線は太さ (5 対 4)
@@ -171,7 +193,7 @@ function SolidEdgePath({
171
193
  // 伸ばすのは初めて出る段だけ (#584)。 光っているかで引くと、光り続ける図で毎段引き直される
172
194
  const visibleD = drawing ? pathSubpath(edge.d, progress) : edge.d;
173
195
  // signal binding: widthBind / strokeBind / dashOffsetBind (CAR edge-signal-binding)
174
- const defaultWidth = active ? 5 : 2.5;
196
+ const defaultWidth = defaultEdgeWidth(active);
175
197
  const boundWidth = edge.widthBind && stateValues
176
198
  ? resolveNumericAttr(edge.widthBind, stateValues, defaultWidth)
177
199
  : defaultWidth;
@@ -269,7 +291,7 @@ export function CdlEdgeGlowView({
269
291
  active: boolean;
270
292
  }): JSX.Element | null {
271
293
  if (!active) return null;
272
- const color = toneColor(edge.tone);
294
+ const color = edgeColor(edge);
273
295
  return <EdgeGlowCircles color={color} edgeId={edge.id} />;
274
296
  }
275
297
 
@@ -286,7 +308,7 @@ export function CdlEdgeLabelView({
286
308
  // 空 label でも rect + text で 12.5x12.5 の小 marker が残り、 dragon sweep で
287
309
  // node bbox と数十 px² の実測 overlap を生む (mind-demo 61px² 事象)。
288
310
  if (!hasRenderedLabel(edge)) return null;
289
- const color = toneColor(edge.tone);
311
+ const color = edgeColor(edge);
290
312
  // SSOT = spec.ts の computeLabelBBoxWorld (CAR-431 で render / layout / predict-bbox 3 箇所の
291
313
  // bbox 計算 drift を解消)。 pill h / w / padding / boxLeftX / boxTopY は本 helper 経由で取得、
292
314
  // <g transform="translate(labelX, labelY)"> 内 rect は相対座標なので world x/y を helper から
@@ -312,19 +334,22 @@ export function CdlEdgeLabelView({
312
334
  data-cdl-edge-label-text={edge.label}
313
335
  data-cdl-edge-sub-text={edge.sub ?? ""}
314
336
  >
315
- <rect
316
- data-cdl-role="edge-label-bg"
317
- x={boxLeftX}
318
- y={boxTopY}
319
- width={boxW}
320
- height={boxH}
321
- rx={10}
322
- fill="var(--cdl-label-bg, #ffffff)"
323
- stroke={active ? color : "var(--cdl-divider, #d4d4d8)"}
324
- strokeOpacity={active ? 0.9 : 0.55}
325
- strokeWidth={1.5}
326
- opacity={active ? 1 : 0.92}
327
- />
337
+ {/* 下地は既定で敷く。 丸い下地は箱と同じ形なので、 罫の細い図では外せるようにした (#618) */}
338
+ {edge.labelPlate !== false && (
339
+ <rect
340
+ data-cdl-role="edge-label-bg"
341
+ x={boxLeftX}
342
+ y={boxTopY}
343
+ width={boxW}
344
+ height={boxH}
345
+ rx={10}
346
+ fill="var(--cdl-label-bg, #ffffff)"
347
+ stroke={active ? color : "var(--cdl-divider, #d4d4d8)"}
348
+ strokeOpacity={active ? 0.9 : 0.55}
349
+ strokeWidth={1.5}
350
+ opacity={active ? 1 : 0.92}
351
+ />
352
+ )}
328
353
  <text
329
354
  data-cdl-role="edge-label"
330
355
  x={textX}
package/src/types.ts CHANGED
@@ -134,6 +134,9 @@ export type Side = "top" | "right" | "bottom" | "left";
134
134
  */
135
135
  export type EdgeStyle = "solid" | "dotted-flow" | "dashed";
136
136
 
137
+ /** 辺の役目 (#618)。 `main` = 図の主となる道 */
138
+ export type EdgeRole = "main";
139
+
137
140
  /**
138
141
  * 線の種類の全種 (#578)。 **型だけでなく実体を持つ**。
139
142
  *
@@ -637,6 +640,21 @@ export type CdlEdge = {
637
640
  side?: Side;
638
641
  /** visual style、 default "solid" */
639
642
  style?: EdgeStyle;
643
+ /**
644
+ * 辺の役目 (#618)。 書かなければ従来どおり tone の色で引く。
645
+ *
646
+ * `main` を書いた辺だけ「いま」 の色 (`--cdl-now`) で引く。 図の中で 1 本の道を
647
+ * 目立たせ、 どこから読むかを決めるための口。 すべての辺に書くと差が消えるので、
648
+ * 主となる 1 本 (または 1 続き) にだけ書く。
649
+ */
650
+ role?: EdgeRole;
651
+ /**
652
+ * 名前の下地を敷くか (#618)。 書かなければ敷く (従来どおり)。
653
+ *
654
+ * 丸い下地は箱と同じ形なので、 名前が小さな箱に見える。 罫の細い図では外したほうが
655
+ * 線と名前の区別が付く。
656
+ */
657
+ labelPlate?: boolean;
640
658
  /** 端の形、 default "triangle" (#560)。 線の種類とは別の軸 */
641
659
  head?: EdgeHead;
642
660
  /**