@cardenelabs/cdl 0.5.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 (108) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +343 -0
  3. package/SPEC.md +374 -0
  4. package/dist/index.cjs +28085 -0
  5. package/dist/index.cjs.map +1 -0
  6. package/dist/index.d.cts +3415 -0
  7. package/dist/index.d.ts +3415 -0
  8. package/dist/index.js +27962 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/react.cjs +23043 -0
  11. package/dist/react.cjs.map +1 -0
  12. package/dist/react.d.cts +2 -0
  13. package/dist/react.d.ts +2 -0
  14. package/dist/react.js +23041 -0
  15. package/dist/react.js.map +1 -0
  16. package/dist/render-C78lIXeC.d.cts +2449 -0
  17. package/dist/render-C78lIXeC.d.ts +2449 -0
  18. package/examples/quick-start.md +88 -0
  19. package/package.json +79 -0
  20. package/src/anim/core/easing.ts +77 -0
  21. package/src/anim/core/timeline.ts +335 -0
  22. package/src/anim/core/types.ts +42 -0
  23. package/src/anim/index.ts +20 -0
  24. package/src/anim/react/index.ts +3 -0
  25. package/src/anim/react/useReducedMotion.ts +27 -0
  26. package/src/anim/react/useTimeline.ts +48 -0
  27. package/src/assert-never.ts +16 -0
  28. package/src/author-intent-verify.ts +587 -0
  29. package/src/builder.ts +3740 -0
  30. package/src/compile.ts +12 -0
  31. package/src/dom-verify-core.ts +121 -0
  32. package/src/dom-verify-types.ts +21 -0
  33. package/src/dom-verify.ts +948 -0
  34. package/src/event-handler/index.ts +184 -0
  35. package/src/formula/ast.ts +46 -0
  36. package/src/formula/evaluator.ts +163 -0
  37. package/src/formula/formula-computeds.ts +83 -0
  38. package/src/formula/index.ts +5 -0
  39. package/src/formula/parser.ts +399 -0
  40. package/src/index.ts +284 -0
  41. package/src/input-signals.ts +74 -0
  42. package/src/kinds/actor.tsx +79 -0
  43. package/src/kinds/card.tsx +61 -0
  44. package/src/kinds/chart-bar.tsx +121 -0
  45. package/src/kinds/chart-line.tsx +163 -0
  46. package/src/kinds/chart-pie.tsx +107 -0
  47. package/src/kinds/compact-title.ts +164 -0
  48. package/src/kinds/dyn-shape.tsx +394 -0
  49. package/src/kinds/event.tsx +70 -0
  50. package/src/kinds/function.tsx +53 -0
  51. package/src/kinds/funnel.tsx +122 -0
  52. package/src/kinds/gantt.tsx +193 -0
  53. package/src/kinds/generic.tsx +368 -0
  54. package/src/kinds/mind-map.tsx +367 -0
  55. package/src/kinds/mind-radial.tsx +209 -0
  56. package/src/kinds/node-tone.ts +18 -0
  57. package/src/kinds/quadrant.tsx +164 -0
  58. package/src/kinds/row-align.ts +179 -0
  59. package/src/kinds/shape-basement.tsx +683 -0
  60. package/src/kinds/shape-blockchain.tsx +750 -0
  61. package/src/kinds/shape-commerce.tsx +553 -0
  62. package/src/kinds/shape-finance.tsx +706 -0
  63. package/src/kinds/shape-hardware.tsx +862 -0
  64. package/src/kinds/shape-people.tsx +439 -0
  65. package/src/kinds/shape-region.tsx +383 -0
  66. package/src/kinds/shape-software.tsx +859 -0
  67. package/src/kinds/storage.tsx +180 -0
  68. package/src/kinds/text-width.ts +73 -0
  69. package/src/kinds/tree.tsx +275 -0
  70. package/src/kinds/user-journey.tsx +240 -0
  71. package/src/label-text.ts +71 -0
  72. package/src/layout/clearance-constants.ts +47 -0
  73. package/src/layout/collisions.ts +2078 -0
  74. package/src/layout/edges.ts +2498 -0
  75. package/src/layout/footer-shape.ts +97 -0
  76. package/src/layout/geometry.ts +135 -0
  77. package/src/layout/label-shift.ts +28 -0
  78. package/src/layout/lanes.ts +328 -0
  79. package/src/layout/nodes.ts +190 -0
  80. package/src/layout/predict-bbox.ts +76 -0
  81. package/src/layout/px-projection.ts +176 -0
  82. package/src/layout/spec.ts +872 -0
  83. package/src/layout/text-width.ts +133 -0
  84. package/src/layout/tokens.ts +181 -0
  85. package/src/layout/viewbox.ts +47 -0
  86. package/src/layout-with-validation.ts +175 -0
  87. package/src/layout.ts +381 -0
  88. package/src/presets.ts +2108 -0
  89. package/src/reactive/batch.ts +48 -0
  90. package/src/reactive/computed.ts +85 -0
  91. package/src/reactive/effect.ts +145 -0
  92. package/src/reactive/index.ts +6 -0
  93. package/src/reactive/internal.ts +109 -0
  94. package/src/reactive/signal.ts +113 -0
  95. package/src/render/edges.tsx +318 -0
  96. package/src/render/header.tsx +146 -0
  97. package/src/render/interactive-panel.tsx +9620 -0
  98. package/src/render/nodes.tsx +319 -0
  99. package/src/render/stage.tsx +377 -0
  100. package/src/render/tone.ts +64 -0
  101. package/src/render/utils.ts +238 -0
  102. package/src/render.tsx +221 -0
  103. package/src/scroll-trigger/index.ts +109 -0
  104. package/src/scroll-trigger/progress.ts +49 -0
  105. package/src/thumbnail.tsx +114 -0
  106. package/src/types.ts +2371 -0
  107. package/src/validate.ts +268 -0
  108. package/src/visual-validate.ts +4381 -0
@@ -0,0 +1,3415 @@
1
+ import { C as CdlDiagram, S as Signal, a as CdlInput, b as CdlLane, c as CdlNode, T as Tone, d as Side, E as EdgeStyle, e as CdlState, f as CdlEventTarget, g as Computed, h as CdlScrollTrigger, L as LaidDiagram, i as CdlDiagramViewProps, j as LaidEdge, k as LaidNode, l as CdlEdge, B as BBox, N as NodeKind } from './render-C78lIXeC.cjs';
2
+ export { m as CdlDiagramView, n as CdlEventBinding, o as CdlEventKind, p as CdlFormula, q as CdlPhase, I as InteractiveHandlerMap, r as InteractiveSignalsAccessor, s as LaidLane, t as NODE_KINDS, u as ScrollProgressHandle, v as TONES, w as computeEventBindingKey, x as computeFormulaKey, y as computeInputSignalKey, z as computeScrollTriggerKey, A as computed, D as createInteractiveSignalsAccessor, F as createScrollProgressHandle, G as createScrollProgressSignals, H as hasInteractivePrimitives, J as signal } from './render-C78lIXeC.cjs';
3
+ import { JSX } from 'react';
4
+
5
+ declare function effect(fn: () => void | (() => void)): () => void;
6
+
7
+ /**
8
+ * 複数の signal set を 1 tick に集約する。 batch 内では effect の実行が deferred queue に蓄積、
9
+ * batch 終了時にまとめて flush される (同じ effect が dep 変化で複数回 trigger されても 1 回に集約)。
10
+ *
11
+ * nested batch は最外側の batch 終了時にのみ flush、 途中の内側 batch では実行しない。
12
+ */
13
+ declare function batch<T>(fn: () => T): T;
14
+ /**
15
+ * fn 内の signal / computed read を dependency 追跡から除外する。 effect 内で
16
+ * 「値は見たいが再実行 trigger にしたくない」 用途 (initial state だけ読みたい 等)。
17
+ */
18
+ declare function untrack<T>(fn: () => T): T;
19
+
20
+ /**
21
+ * diagram の `inputs` から signal を生成、 id をキーにした map を返す helper (CAR #243)。
22
+ *
23
+ * consumer app 側 = 「HTML widget → onChange で signal.value 更新 → SVG 内 bind が signal.value を read」
24
+ * の bind 経路の起点。 static SSR では signal 生成せず defaultValue を SVG に埋込むだけで良い、
25
+ * client hydration の時点で本 helper を呼んで signal を生成、 widget と bind する。
26
+ *
27
+ * diagram に inputs がない場合は空 object を返す。 id が重複する diagram は builder 側で error になるため
28
+ * 本 helper 内では重複チェックしない (fail-fast は builder 責務)。
29
+ */
30
+ declare function createInputSignals(diagram: CdlDiagram): Record<string, Signal<unknown>>;
31
+ /**
32
+ * CdlInput から default value を kind-safe に取り出す。 discriminated union なので type narrowing が効く。
33
+ * default 節 assertNever で新 kind 追加時 compile error 検知する (CAR #262)。
34
+ */
35
+ declare function inputDefaultValue(input: CdlInput): number | string | boolean;
36
+
37
+ /**
38
+ * `.animation.scroll(id, opts)` を提供する chain-facing helper。 将来 `.animation.time(...)` 等の
39
+ * time-based primitive を追加する余地を残すため animation namespace として分離。
40
+ */
41
+ type AnimationChain = {
42
+ scroll: (id: string, opts?: {
43
+ start?: number;
44
+ end?: number;
45
+ scrub?: number;
46
+ label?: string;
47
+ }) => DiagramBuilder;
48
+ };
49
+ /**
50
+ * `.on.click(target, handlerId)` / `.on.hover(target, handlerId)` を提供する chain helper。
51
+ * target は node / edge / lane / diagram の 4 種、 handlerId は string で consumer が attach 時に
52
+ * 対応関数 map を渡す (JSON DSL 互換のため関数は diagram data model に含めない)。
53
+ */
54
+ type EventChain = {
55
+ click: (target: CdlEventTarget, handlerId: string) => DiagramBuilder;
56
+ hover: (target: CdlEventTarget, handlerId: string) => DiagramBuilder;
57
+ doubleClick: (target: CdlEventTarget, handlerId: string) => DiagramBuilder;
58
+ longPress: (target: CdlEventTarget, handlerId: string) => DiagramBuilder;
59
+ drag: (target: CdlEventTarget, handlerId: string) => DiagramBuilder;
60
+ drop: (target: CdlEventTarget, handlerId: string) => DiagramBuilder;
61
+ keydown: (target: CdlEventTarget, handlerId: string) => DiagramBuilder;
62
+ focus: (target: CdlEventTarget, handlerId: string) => DiagramBuilder;
63
+ blur: (target: CdlEventTarget, handlerId: string) => DiagramBuilder;
64
+ };
65
+ /**
66
+ * `.input.slider()` / `.number()` / `.dropdown()` / `.toggle()` を提供する chain-facing helper。
67
+ * 各 method は widget spec を `CdlInput` として累積し、 DiagramBuilder を返して chain 継続を許す。
68
+ */
69
+ /**
70
+ * ReadoutChain = visual readout widget を diagram に追加する chain (CAR: widget 拡充)。
71
+ * 各 method は CdlReadout として累積する。
72
+ */
73
+ type ReadoutChain = {
74
+ bar: (id: string, opts: {
75
+ source: string;
76
+ min: number;
77
+ max: number;
78
+ color?: string;
79
+ label?: string;
80
+ }) => DiagramBuilder;
81
+ gauge: (id: string, opts: {
82
+ source: string;
83
+ min: number;
84
+ max: number;
85
+ color?: string;
86
+ label?: string;
87
+ }) => DiagramBuilder;
88
+ stat: (id: string, opts: {
89
+ source: string;
90
+ caption?: string;
91
+ unit?: string;
92
+ label?: string;
93
+ }) => DiagramBuilder;
94
+ sparkline: (id: string, opts: {
95
+ source: string;
96
+ history?: number;
97
+ color?: string;
98
+ label?: string;
99
+ }) => DiagramBuilder;
100
+ countup: (id: string, opts: {
101
+ source: string;
102
+ durationMs?: number;
103
+ decimals?: number;
104
+ unit?: string;
105
+ label?: string;
106
+ }) => DiagramBuilder;
107
+ typewriter: (id: string, opts: {
108
+ source: string;
109
+ charMs?: number;
110
+ label?: string;
111
+ }) => DiagramBuilder;
112
+ delta: (id: string, opts: {
113
+ source: string;
114
+ decimals?: number;
115
+ unit?: string;
116
+ label?: string;
117
+ }) => DiagramBuilder;
118
+ percentRing: (id: string, opts: {
119
+ source: string;
120
+ max: number;
121
+ color?: string;
122
+ label?: string;
123
+ }) => DiagramBuilder;
124
+ heatCell: (id: string, opts: {
125
+ source: string;
126
+ min: number;
127
+ max: number;
128
+ colors?: readonly string[];
129
+ label?: string;
130
+ }) => DiagramBuilder;
131
+ badge: (id: string, opts: {
132
+ source: string;
133
+ colorSource?: string;
134
+ map?: readonly {
135
+ value: string;
136
+ color: string;
137
+ }[];
138
+ label?: string;
139
+ }) => DiagramBuilder;
140
+ statusDot: (id: string, opts: {
141
+ source: string;
142
+ map: readonly {
143
+ value: string;
144
+ color: string;
145
+ label?: string;
146
+ }[];
147
+ label?: string;
148
+ }) => DiagramBuilder;
149
+ /** SVG path を signal の 0-100 進行率で stroke-dashoffset animation */
150
+ pathProgress: (id: string, opts: {
151
+ source: string;
152
+ pathD: string;
153
+ viewW?: number;
154
+ viewH?: number;
155
+ strokeWidth?: number;
156
+ color?: string;
157
+ max?: number;
158
+ label?: string;
159
+ }) => DiagramBuilder;
160
+ /** array signal を bullet list 表示 (各 element を row 化) */
161
+ arrayList: (id: string, opts: {
162
+ source: string;
163
+ itemTemplate?: string;
164
+ max?: number;
165
+ label?: string;
166
+ }) => DiagramBuilder;
167
+ /** array signal の各 element を bar 高さで並列表示 (histogram 的) */
168
+ arrayBar: (id: string, opts: {
169
+ source: string;
170
+ min: number;
171
+ max: number;
172
+ color?: string;
173
+ label?: string;
174
+ }) => DiagramBuilder;
175
+ /** array signal を line chart (折れ線 + optional area fill) */
176
+ lineChart: (id: string, opts: {
177
+ source: string;
178
+ min: number;
179
+ max: number;
180
+ viewW?: number;
181
+ viewH?: number;
182
+ color?: string;
183
+ fill?: boolean;
184
+ label?: string;
185
+ }) => DiagramBuilder;
186
+ /** 2 array signal を並列 bar で比較 (A/B histogram) */
187
+ stackedBar: (id: string, opts: {
188
+ sourceA: string;
189
+ sourceB: string;
190
+ min: number;
191
+ max: number;
192
+ colorA?: string;
193
+ colorB?: string;
194
+ label?: string;
195
+ }) => DiagramBuilder;
196
+ /** array signal を左から累積、 element 差分を bar 表示 (財務 waterfall chart 用) */
197
+ waterfall: (id: string, opts: {
198
+ source: string;
199
+ min: number;
200
+ max: number;
201
+ viewW?: number;
202
+ viewH?: number;
203
+ colorPos?: string;
204
+ colorNeg?: string;
205
+ label?: string;
206
+ }) => DiagramBuilder;
207
+ /** 2D array signal を色 gradient cell で表示 (confusion matrix / heatmap) */
208
+ matrix: (id: string, opts: {
209
+ source: string;
210
+ min: number;
211
+ max: number;
212
+ cellSize?: number;
213
+ colors?: readonly string[];
214
+ showValue?: boolean;
215
+ label?: string;
216
+ }) => DiagramBuilder;
217
+ /** array signal を progress bar list として表示 (task list progress) */
218
+ progressGroup: (id: string, opts: {
219
+ source: string;
220
+ max: number;
221
+ labelSource?: string;
222
+ color?: string;
223
+ label?: string;
224
+ }) => DiagramBuilder;
225
+ /** array signal を横 timeline event marker で表示 (login → auth → response 系 flow) */
226
+ sequenceTimeline: (id: string, opts: {
227
+ source: string;
228
+ min: number;
229
+ max: number;
230
+ viewW?: number;
231
+ viewH?: number;
232
+ color?: string;
233
+ label?: string;
234
+ }) => DiagramBuilder;
235
+ /** N 次元 array を polygon spider chart で表示 */
236
+ radar: (id: string, opts: {
237
+ source: string;
238
+ max: number;
239
+ viewW?: number;
240
+ viewH?: number;
241
+ color?: string;
242
+ labelSource?: string;
243
+ label?: string;
244
+ }) => DiagramBuilder;
245
+ /** 3D data (x, y, r) を bubbles で表示 */
246
+ bubbleChart: (id: string, opts: {
247
+ source: string;
248
+ xMin: number;
249
+ xMax: number;
250
+ yMin: number;
251
+ yMax: number;
252
+ rMin: number;
253
+ rMax: number;
254
+ viewW?: number;
255
+ viewH?: number;
256
+ color?: string;
257
+ label?: string;
258
+ }) => DiagramBuilder;
259
+ /** array を multi-segment donut chart で表示 (percent-ring の N 分割版) */
260
+ donut: (id: string, opts: {
261
+ source: string;
262
+ viewW?: number;
263
+ viewH?: number;
264
+ colors?: readonly string[];
265
+ innerRatio?: number;
266
+ label?: string;
267
+ }) => DiagramBuilder;
268
+ /** 1 年分の日次 count を GitHub-style contribution heatmap で表示 (53 週 × 7 日) */
269
+ calendarHeatmap: (id: string, opts: {
270
+ source: string;
271
+ max: number;
272
+ cellSize?: number;
273
+ cellGap?: number;
274
+ colors?: readonly string[];
275
+ label?: string;
276
+ }) => DiagramBuilder;
277
+ /** 大 canvas 上の viewport rect を縮小表示 (overview 用) */
278
+ miniMap: (id: string, opts: {
279
+ source: string;
280
+ canvasW: number;
281
+ canvasH: number;
282
+ viewW?: number;
283
+ viewH?: number;
284
+ color?: string;
285
+ label?: string;
286
+ }) => DiagramBuilder;
287
+ /** 現在値 + 直前値との delta + mini sparkline を 1 tile に composite (dashboard KPI 単体) */
288
+ kpiCard: (id: string, opts: {
289
+ source: string;
290
+ historySource: string;
291
+ comparisonSource: string;
292
+ unit?: string;
293
+ colorPos?: string;
294
+ colorNeg?: string;
295
+ label?: string;
296
+ }) => DiagramBuilder;
297
+ /** OHLC array を蝋燭足 (candlestick chart) で表示 (finance 用) */
298
+ candlestick: (id: string, opts: {
299
+ source: string;
300
+ min: number;
301
+ max: number;
302
+ viewW?: number;
303
+ viewH?: number;
304
+ colorUp?: string;
305
+ colorDown?: string;
306
+ label?: string;
307
+ }) => DiagramBuilder;
308
+ /** 2-set Venn diagram (`[|A|, |B|, |A∩B|]`) */
309
+ venn: (id: string, opts: {
310
+ source: string;
311
+ viewW?: number;
312
+ viewH?: number;
313
+ colorA?: string;
314
+ colorB?: string;
315
+ labelA?: string;
316
+ labelB?: string;
317
+ label?: string;
318
+ }) => DiagramBuilder;
319
+ /** before/after slope chart (`[[before, after, name], ...]`) */
320
+ slope: (id: string, opts: {
321
+ source: string;
322
+ min: number;
323
+ max: number;
324
+ viewW?: number;
325
+ viewH?: number;
326
+ colorUp?: string;
327
+ colorDown?: string;
328
+ label?: string;
329
+ }) => DiagramBuilder;
330
+ /** conversion funnel chart (`[[stage, count], ...]`) */
331
+ funnel: (id: string, opts: {
332
+ source: string;
333
+ viewW?: number;
334
+ viewH?: number;
335
+ colorTop?: string;
336
+ colorBottom?: string;
337
+ label?: string;
338
+ }) => DiagramBuilder;
339
+ /** gantt task timeline (`[[name, start, duration], ...]`) */
340
+ gantt: (id: string, opts: {
341
+ source: string;
342
+ min: number;
343
+ max: number;
344
+ viewW?: number;
345
+ viewH?: number;
346
+ color?: string;
347
+ label?: string;
348
+ }) => DiagramBuilder;
349
+ /** treemap hierarchical rectangles (`[[name, size], ...]`) */
350
+ treemap: (id: string, opts: {
351
+ source: string;
352
+ viewW?: number;
353
+ viewH?: number;
354
+ colors?: readonly string[];
355
+ label?: string;
356
+ }) => DiagramBuilder;
357
+ /** 2 column sankey flow diagram (`[[fromName, toName, flow], ...]`) */
358
+ sankey: (id: string, opts: {
359
+ source: string;
360
+ viewW?: number;
361
+ viewH?: number;
362
+ colors?: readonly string[];
363
+ label?: string;
364
+ }) => DiagramBuilder;
365
+ /** nightingale rose polar area chart */
366
+ polarArea: (id: string, opts: {
367
+ source: string;
368
+ max: number;
369
+ viewW?: number;
370
+ viewH?: number;
371
+ colors?: readonly string[];
372
+ labelSource?: string;
373
+ label?: string;
374
+ }) => DiagramBuilder;
375
+ /** wizard step indicator (current index + step names) */
376
+ stepIndicator: (id: string, opts: {
377
+ source: string;
378
+ stepsSource: string;
379
+ viewW?: number;
380
+ viewH?: number;
381
+ colorActive?: string;
382
+ colorPending?: string;
383
+ label?: string;
384
+ }) => DiagramBuilder;
385
+ /** bullet-chart (KPI actual vs target + 3 range) */
386
+ bulletChart: (id: string, opts: {
387
+ source: string;
388
+ targetSource: string;
389
+ max: number;
390
+ rangeBad?: number;
391
+ rangeAvg?: number;
392
+ viewW?: number;
393
+ viewH?: number;
394
+ colorActual?: string;
395
+ label?: string;
396
+ }) => DiagramBuilder;
397
+ /** 大 numeric display (scoreboard style) */
398
+ numberBoard: (id: string, opts: {
399
+ source: string;
400
+ prefix?: string;
401
+ suffix?: string;
402
+ size?: number;
403
+ color?: string;
404
+ caption?: string;
405
+ label?: string;
406
+ }) => DiagramBuilder;
407
+ /** ranked list (top-N with medal decoration) */
408
+ leaderboard: (id: string, opts: {
409
+ source: string;
410
+ max?: number;
411
+ color?: string;
412
+ label?: string;
413
+ }) => DiagramBuilder;
414
+ /** 3-color status indicator (traffic light) */
415
+ trafficLight: (id: string, opts: {
416
+ source: string;
417
+ viewW?: number;
418
+ viewH?: number;
419
+ label?: string;
420
+ }) => DiagramBuilder;
421
+ /** weighted tag cloud (`[[tag, weight], ...]`) */
422
+ tagCloud: (id: string, opts: {
423
+ source: string;
424
+ minSize?: number;
425
+ maxSize?: number;
426
+ colors?: readonly string[];
427
+ label?: string;
428
+ }) => DiagramBuilder;
429
+ /** activity feed (`[[actor, action, time], ...]`) */
430
+ activityFeed: (id: string, opts: {
431
+ source: string;
432
+ max?: number;
433
+ color?: string;
434
+ label?: string;
435
+ }) => DiagramBuilder;
436
+ /** N star rating display (half-star 対応) */
437
+ rating: (id: string, opts: {
438
+ source: string;
439
+ count?: number;
440
+ color?: string;
441
+ label?: string;
442
+ }) => DiagramBuilder;
443
+ /** alert card (info/warn/error/success 4 kind + title + body) */
444
+ notification: (id: string, opts: {
445
+ kindSource: string;
446
+ titleSource: string;
447
+ bodySource?: string;
448
+ label?: string;
449
+ }) => DiagramBuilder;
450
+ /** git commit style +N / -N counter */
451
+ diffCounter: (id: string, opts: {
452
+ additionsSource: string;
453
+ deletionsSource: string;
454
+ colorAdd?: string;
455
+ colorDel?: string;
456
+ label?: string;
457
+ }) => DiagramBuilder;
458
+ /** conversation thread (`[[author, text, isSelf], ...]`) */
459
+ chatBubble: (id: string, opts: {
460
+ source: string;
461
+ max?: number;
462
+ colorSelf?: string;
463
+ colorOther?: string;
464
+ label?: string;
465
+ }) => DiagramBuilder;
466
+ /** user avatar circle (initials + color) */
467
+ avatar: (id: string, opts: {
468
+ source: string;
469
+ size?: number;
470
+ color?: string;
471
+ label?: string;
472
+ }) => DiagramBuilder;
473
+ /** checklist (`[[label, checked], ...]`) with progress % */
474
+ checklist: (id: string, opts: {
475
+ source: string;
476
+ color?: string;
477
+ label?: string;
478
+ }) => DiagramBuilder;
479
+ /** 270° dial gauge (speedometer / tachometer) */
480
+ circularGauge: (id: string, opts: {
481
+ source: string;
482
+ min: number;
483
+ max: number;
484
+ viewW?: number;
485
+ viewH?: number;
486
+ color?: string;
487
+ unit?: string;
488
+ label?: string;
489
+ }) => DiagramBuilder;
490
+ /** e-commerce price tag (old/new/discount%) */
491
+ priceTag: (id: string, opts: {
492
+ oldSource: string;
493
+ newSource: string;
494
+ currency?: string;
495
+ colorNew?: string;
496
+ colorOld?: string;
497
+ colorDiscount?: string;
498
+ label?: string;
499
+ }) => DiagramBuilder;
500
+ /** loading spinner (running/done/error) + text */
501
+ spinner: (id: string, opts: {
502
+ source: string;
503
+ textSource: string;
504
+ color?: string;
505
+ label?: string;
506
+ }) => DiagramBuilder;
507
+ /** letter grade (A/B/C/D/F) with color band */
508
+ grade: (id: string, opts: {
509
+ source: string;
510
+ max?: number;
511
+ label?: string;
512
+ }) => DiagramBuilder;
513
+ /** MM:SS.ms timer display */
514
+ stopwatch: (id: string, opts: {
515
+ source: string;
516
+ runningSource?: string;
517
+ size?: number;
518
+ color?: string;
519
+ label?: string;
520
+ }) => DiagramBuilder;
521
+ /** confidence meter (0-100% + 3 color band) */
522
+ confidenceMeter: (id: string, opts: {
523
+ source: string;
524
+ lowThreshold?: number;
525
+ highThreshold?: number;
526
+ viewW?: number;
527
+ viewH?: number;
528
+ label?: string;
529
+ }) => DiagramBuilder;
530
+ /** emoji reaction bar (`[[emoji, count], ...]`) */
531
+ reactionBar: (id: string, opts: {
532
+ source: string;
533
+ color?: string;
534
+ label?: string;
535
+ }) => DiagramBuilder;
536
+ /** small pill badges list (string array or `[[label, color], ...]`) */
537
+ pillGroup: (id: string, opts: {
538
+ source: string;
539
+ color?: string;
540
+ label?: string;
541
+ }) => DiagramBuilder;
542
+ /** segmented battery/fuel bar (0-100% + 3 color band) */
543
+ fuelBar: (id: string, opts: {
544
+ source: string;
545
+ segments?: number;
546
+ lowThreshold?: number;
547
+ highThreshold?: number;
548
+ viewW?: number;
549
+ viewH?: number;
550
+ label?: string;
551
+ }) => DiagramBuilder;
552
+ /** 2x2 stat grid (`[[name, value, unit?], ...]`) */
553
+ metricsGrid: (id: string, opts: {
554
+ source: string;
555
+ color?: string;
556
+ label?: string;
557
+ }) => DiagramBuilder;
558
+ /** vertical thermometer (min-max normalized) */
559
+ thermometer: (id: string, opts: {
560
+ source: string;
561
+ min: number;
562
+ max: number;
563
+ viewW?: number;
564
+ viewH?: number;
565
+ color?: string;
566
+ unit?: string;
567
+ label?: string;
568
+ }) => DiagramBuilder;
569
+ /** icon + label + value tiles (`[[icon, label, value], ...]`) */
570
+ iconTile: (id: string, opts: {
571
+ source: string;
572
+ color?: string;
573
+ label?: string;
574
+ }) => DiagramBuilder;
575
+ /** crypto/asset token list (`[[icon, name, amount, deltaPct], ...]`) */
576
+ tokenList: (id: string, opts: {
577
+ source: string;
578
+ colorUp?: string;
579
+ colorDown?: string;
580
+ label?: string;
581
+ }) => DiagramBuilder;
582
+ /** 2D map pins (`[[name, x, y], ...]`) */
583
+ mapPin: (id: string, opts: {
584
+ source: string;
585
+ xMin: number;
586
+ xMax: number;
587
+ yMin: number;
588
+ yMax: number;
589
+ viewW?: number;
590
+ viewH?: number;
591
+ color?: string;
592
+ label?: string;
593
+ }) => DiagramBuilder;
594
+ /** priority badge (high/med/low + optional label text) */
595
+ priorityBadge: (id: string, opts: {
596
+ source: string;
597
+ textSource?: string;
598
+ label?: string;
599
+ }) => DiagramBuilder;
600
+ /** 1st/2nd/3rd podium (competition winners) */
601
+ podium: (id: string, opts: {
602
+ source: string;
603
+ viewW?: number;
604
+ viewH?: number;
605
+ label?: string;
606
+ }) => DiagramBuilder;
607
+ /** poll option bar (`[[option, count], ...]` + winner highlight) */
608
+ pollBar: (id: string, opts: {
609
+ source: string;
610
+ color?: string;
611
+ colorWinner?: string;
612
+ label?: string;
613
+ }) => DiagramBuilder;
614
+ /** stacked circular avatars (name array) */
615
+ userStack: (id: string, opts: {
616
+ source: string;
617
+ max?: number;
618
+ size?: number;
619
+ label?: string;
620
+ }) => DiagramBuilder;
621
+ /** git commit history (`[[sha, msg, author], ...]`) */
622
+ commitList: (id: string, opts: {
623
+ source: string;
624
+ max?: number;
625
+ color?: string;
626
+ label?: string;
627
+ }) => DiagramBuilder;
628
+ /** audio/video mini player (play/pause + progress + time) */
629
+ mediaPlayer: (id: string, opts: {
630
+ source: string;
631
+ durationSource: string;
632
+ playingSource: string;
633
+ color?: string;
634
+ viewW?: number;
635
+ label?: string;
636
+ }) => DiagramBuilder;
637
+ /** event stream log (`[[timestamp, severity, msg], ...]`) */
638
+ eventLog: (id: string, opts: {
639
+ source: string;
640
+ max?: number;
641
+ label?: string;
642
+ }) => DiagramBuilder;
643
+ /** search result hits (`[[title, snippet, url], ...]`) */
644
+ searchResult: (id: string, opts: {
645
+ source: string;
646
+ max?: number;
647
+ color?: string;
648
+ label?: string;
649
+ }) => DiagramBuilder;
650
+ /** quarterly roadmap Q1-Q4 (`[[quarter, [items]], ...]`) */
651
+ roadmap: (id: string, opts: {
652
+ source: string;
653
+ viewW?: number;
654
+ viewH?: number;
655
+ label?: string;
656
+ }) => DiagramBuilder;
657
+ /** 5-day weather forecast (`[[day, icon, high, low], ...]`) */
658
+ weatherForecast: (id: string, opts: {
659
+ source: string;
660
+ label?: string;
661
+ }) => DiagramBuilder;
662
+ /** video card list (`[[emoji, title, duration, viewCount], ...]`) */
663
+ videoCard: (id: string, opts: {
664
+ source: string;
665
+ max?: number;
666
+ color?: string;
667
+ label?: string;
668
+ }) => DiagramBuilder;
669
+ /** order tracking steps (current step index + step names) */
670
+ orderStatus: (id: string, opts: {
671
+ source: string;
672
+ stepsSource: string;
673
+ color?: string;
674
+ label?: string;
675
+ }) => DiagramBuilder;
676
+ /** attendance grid (rows=days, cols=members) */
677
+ attendanceGrid: (id: string, opts: {
678
+ source: string;
679
+ membersSource: string;
680
+ color?: string;
681
+ label?: string;
682
+ }) => DiagramBuilder;
683
+ /** multi-timezone clock (`[[city, offsetHours, HH:MM], ...]`) */
684
+ timezoneClock: (id: string, opts: {
685
+ source: string;
686
+ color?: string;
687
+ label?: string;
688
+ }) => DiagramBuilder;
689
+ /** form field summary (`[[fieldName, value], ...]`) */
690
+ formSummary: (id: string, opts: {
691
+ source: string;
692
+ color?: string;
693
+ label?: string;
694
+ }) => DiagramBuilder;
695
+ /** song playlist queue with current index highlight */
696
+ songQueue: (id: string, opts: {
697
+ source: string;
698
+ currentSource?: string;
699
+ max?: number;
700
+ color?: string;
701
+ label?: string;
702
+ }) => DiagramBuilder;
703
+ /** 1 month calendar view (day cells + event dots + today border) */
704
+ calendarMonth: (id: string, opts: {
705
+ source: string;
706
+ monthName?: string;
707
+ color?: string;
708
+ label?: string;
709
+ }) => DiagramBuilder;
710
+ /** CLI terminal output (`[[prompt, cmd, output], ...]`) */
711
+ terminal: (id: string, opts: {
712
+ source: string;
713
+ max?: number;
714
+ color?: string;
715
+ label?: string;
716
+ }) => DiagramBuilder;
717
+ /** 8×8 chess board with piece placement */
718
+ chessBoard: (id: string, opts: {
719
+ source: string;
720
+ cellSize?: number;
721
+ label?: string;
722
+ }) => DiagramBuilder;
723
+ /** 3 column kanban board (Todo / In Progress / Done) */
724
+ kanbanBoard: (id: string, opts: {
725
+ source: string;
726
+ columnWidth?: number;
727
+ max?: number;
728
+ label?: string;
729
+ }) => DiagramBuilder;
730
+ /** navigation breadcrumb (Home > Docs > API pattern) */
731
+ breadcrumb: (id: string, opts: {
732
+ source: string;
733
+ currentSource?: string;
734
+ color?: string;
735
+ label?: string;
736
+ }) => DiagramBuilder;
737
+ /** 縦 timeline (dot + line + event text) */
738
+ timelineVertical: (id: string, opts: {
739
+ source: string;
740
+ color?: string;
741
+ max?: number;
742
+ label?: string;
743
+ }) => DiagramBuilder;
744
+ /** 時系列 status 遷移 strip (Active/Idle/Error 等) */
745
+ statusTimeline: (id: string, opts: {
746
+ source: string;
747
+ colorMap?: Array<{
748
+ status: string;
749
+ color: string;
750
+ }>;
751
+ max?: number;
752
+ label?: string;
753
+ }) => DiagramBuilder;
754
+ /** 7-day mini calendar (week view) */
755
+ calendarWeek: (id: string, opts: {
756
+ source: string;
757
+ cellSize?: number;
758
+ color?: string;
759
+ label?: string;
760
+ }) => DiagramBuilder;
761
+ /** 2 KPI horizontal comparison bar (A vs B) */
762
+ kpiComparison: (id: string, opts: {
763
+ source: string;
764
+ max?: number;
765
+ colorA?: string;
766
+ colorB?: string;
767
+ label?: string;
768
+ }) => DiagramBuilder;
769
+ /** numbered step + progress line (wizard progress) */
770
+ stepProgress: (id: string, opts: {
771
+ source: string;
772
+ stepsSource: string;
773
+ color?: string;
774
+ label?: string;
775
+ }) => DiagramBuilder;
776
+ /** user list + online/away/offline presence dot */
777
+ userPresence: (id: string, opts: {
778
+ source: string;
779
+ max?: number;
780
+ label?: string;
781
+ }) => DiagramBuilder;
782
+ /** thumbs up/down rating (2 count + up %) */
783
+ ratingThumb: (id: string, opts: {
784
+ source: string;
785
+ colorUp?: string;
786
+ colorDown?: string;
787
+ label?: string;
788
+ }) => DiagramBuilder;
789
+ /** organization hierarchy 3 level (root/mid/leaf) */
790
+ orgChartMini: (id: string, opts: {
791
+ source: string;
792
+ color?: string;
793
+ label?: string;
794
+ }) => DiagramBuilder;
795
+ /** KPI current + delta + sparkline を 1 tile */
796
+ kpiTrendTile: (id: string, opts: {
797
+ source: string;
798
+ prevSource: string;
799
+ historySource: string;
800
+ unit?: string;
801
+ colorPos?: string;
802
+ colorNeg?: string;
803
+ label?: string;
804
+ }) => DiagramBuilder;
805
+ /** 3-4 emoji poll (👍/❤️/🎉 等) */
806
+ quickPollEmoji: (id: string, opts: {
807
+ source: string;
808
+ colorWinner?: string;
809
+ label?: string;
810
+ }) => DiagramBuilder;
811
+ /** voice message playback UI (waveform bars + play + duration) */
812
+ voiceMessage: (id: string, opts: {
813
+ source: string;
814
+ progressSource?: string;
815
+ duration?: number;
816
+ colorPlay?: string;
817
+ colorBar?: string;
818
+ label?: string;
819
+ }) => DiagramBuilder;
820
+ /** thread summary card (unread + participants + last author + time ago) */
821
+ threadSummary: (id: string, opts: {
822
+ source: string;
823
+ colorUnread?: string;
824
+ label?: string;
825
+ }) => DiagramBuilder;
826
+ /** message read receipt (single / double check) */
827
+ readReceipt: (id: string, opts: {
828
+ source: string;
829
+ colorRead?: string;
830
+ colorPending?: string;
831
+ label?: string;
832
+ }) => DiagramBuilder;
833
+ /** password strength 4-level meter */
834
+ passwordStrength: (id: string, opts: {
835
+ source: string;
836
+ colorStrong?: string;
837
+ colorWeak?: string;
838
+ label?: string;
839
+ }) => DiagramBuilder;
840
+ /** 6-digit OTP input box grid */
841
+ otpInput: (id: string, opts: {
842
+ source: string;
843
+ colorFocus?: string;
844
+ label?: string;
845
+ }) => DiagramBuilder;
846
+ /** file drag-and-drop upload zone */
847
+ fileDropzone: (id: string, opts: {
848
+ source: string;
849
+ colorActive?: string;
850
+ label?: string;
851
+ }) => DiagramBuilder;
852
+ /** real-time log tail (5 rows, level color) */
853
+ logStream: (id: string, opts: {
854
+ source: string;
855
+ label?: string;
856
+ }) => DiagramBuilder;
857
+ /** severity alert banner (info / success / warn / error) */
858
+ alertBanner: (id: string, opts: {
859
+ source: string;
860
+ label?: string;
861
+ }) => DiagramBuilder;
862
+ /** service health matrix (down / degraded / up) */
863
+ serviceHealth: (id: string, opts: {
864
+ source: string;
865
+ label?: string;
866
+ }) => DiagramBuilder;
867
+ /** shopping cart summary (items / subtotal / shipping / total) */
868
+ cartSummary: (id: string, opts: {
869
+ source: string;
870
+ currency?: string;
871
+ colorTotal?: string;
872
+ label?: string;
873
+ }) => DiagramBuilder;
874
+ /** pricing tier card (name + price + 3 features + CTA) */
875
+ pricingTier: (id: string, opts: {
876
+ source: string;
877
+ colorAccent?: string;
878
+ currency?: string;
879
+ label?: string;
880
+ }) => DiagramBuilder;
881
+ /** coupon code entry + apply badge */
882
+ couponCode: (id: string, opts: {
883
+ source: string;
884
+ colorApplied?: string;
885
+ label?: string;
886
+ }) => DiagramBuilder;
887
+ /** blog article preview card (title / excerpt / author / timeAgo) */
888
+ articlePreview: (id: string, opts: {
889
+ source: string;
890
+ colorAccent?: string;
891
+ label?: string;
892
+ }) => DiagramBuilder;
893
+ /** table of contents nav with active section highlight */
894
+ tocNav: (id: string, opts: {
895
+ source: string;
896
+ colorActive?: string;
897
+ label?: string;
898
+ }) => DiagramBuilder;
899
+ /** social share button row */
900
+ shareButtons: (id: string, opts: {
901
+ source: string;
902
+ label?: string;
903
+ }) => DiagramBuilder;
904
+ };
905
+ type InputChain = {
906
+ slider: (id: string, opts: {
907
+ min: number;
908
+ max: number;
909
+ step?: number;
910
+ defaultValue: number;
911
+ label?: string;
912
+ }) => DiagramBuilder;
913
+ number: (id: string, opts: {
914
+ min?: number;
915
+ max?: number;
916
+ defaultValue: number;
917
+ label?: string;
918
+ }) => DiagramBuilder;
919
+ dropdown: (id: string, opts: {
920
+ options: readonly string[];
921
+ defaultValue: string;
922
+ label?: string;
923
+ }) => DiagramBuilder;
924
+ toggle: (id: string, opts: {
925
+ defaultValue: boolean;
926
+ label?: string;
927
+ }) => DiagramBuilder;
928
+ /** 2 軸 slider = XY pad、 単一 id で pointer position を保持 (x/y は同 signal に格納) */
929
+ xypad: (id: string, opts: {
930
+ xMin: number;
931
+ xMax: number;
932
+ yMin: number;
933
+ yMax: number;
934
+ defaultX: number;
935
+ defaultY: number;
936
+ label?: string;
937
+ }) => DiagramBuilder;
938
+ /** +/- ボタンで integer 増減、 slider より狭い変化範囲 (数値の細かい調整) */
939
+ stepper: (id: string, opts: {
940
+ min?: number;
941
+ max?: number;
942
+ step?: number;
943
+ defaultValue: number;
944
+ label?: string;
945
+ }) => DiagramBuilder;
946
+ /** 排他選択、 dropdown と同じ semantics だが視覚は横並び button */
947
+ radio: (id: string, opts: {
948
+ options: readonly string[];
949
+ defaultValue: string;
950
+ label?: string;
951
+ }) => DiagramBuilder;
952
+ /** hex color (#RRGGBB) picker、 SVG stroke / fill に signal 経由で bind */
953
+ color: (id: string, opts: {
954
+ defaultValue: string;
955
+ label?: string;
956
+ }) => DiagramBuilder;
957
+ /**
958
+ * timeline = 時間軸を signal 化、 0..1 progress + play/pause/seek/speed 制御 UI。
959
+ * SVG animation を signal に紐付けて手動制御可能に。
960
+ */
961
+ timeline: (id: string, opts: {
962
+ duration: number;
963
+ autoplay?: boolean;
964
+ loop?: boolean;
965
+ speeds?: readonly number[];
966
+ defaultSpeedIdx?: number;
967
+ label?: string;
968
+ }) => DiagramBuilder;
969
+ range: (id: string, opts: {
970
+ min: number;
971
+ max: number;
972
+ step?: number;
973
+ defaultLo: number;
974
+ defaultHi: number;
975
+ label?: string;
976
+ }) => DiagramBuilder;
977
+ multiSelect: (id: string, opts: {
978
+ options: readonly string[];
979
+ defaultValues: readonly string[];
980
+ label?: string;
981
+ }) => DiagramBuilder;
982
+ tabs: (id: string, opts: {
983
+ options: readonly string[];
984
+ defaultValue: string;
985
+ label?: string;
986
+ }) => DiagramBuilder;
987
+ datetime: (id: string, opts: {
988
+ defaultValue: string;
989
+ label?: string;
990
+ }) => DiagramBuilder;
991
+ text: (id: string, opts: {
992
+ defaultValue: string;
993
+ placeholder?: string;
994
+ maxLength?: number;
995
+ label?: string;
996
+ }) => DiagramBuilder;
997
+ };
998
+ type PhaseBuilder = {
999
+ activate: (...ids: string[]) => PhaseBuilder;
1000
+ /** 数値 state の線形補間 (write phase で 100→90 等) */
1001
+ tween: (stateId: string, from: number, to: number) => PhaseBuilder;
1002
+ /** 任意型 state を即時切替 (この phase 到達で value 上書き、 lerp なし) */
1003
+ set: (stateId: string, value: string | number) => PhaseBuilder;
1004
+ badge: (text: string) => PhaseBuilder;
1005
+ };
1006
+ type DiagramBuilder = {
1007
+ lane: (id: string, opts: Omit<CdlLane, "id">) => DiagramBuilder;
1008
+ node: (id: string, opts: Omit<CdlNode, "id">) => DiagramBuilder;
1009
+ edge: (from: string, to: string, opts: {
1010
+ /** edge id (省略時は `${from}-${to}` ベースで auto 生成、 重複時は連番付与) */
1011
+ id?: string;
1012
+ label: string;
1013
+ sub?: string;
1014
+ /** tone (default "accent") */
1015
+ tone?: Tone;
1016
+ side?: Side;
1017
+ style?: EdgeStyle;
1018
+ /** FSM guard 条件、 text-dsl v0.5 inline option (`guard: "..."`) を CdlEdge.guard へ透過 */
1019
+ guard?: string;
1020
+ /** ER 関係 cardinality、 text-dsl v0.5 inline option (`cardinality: "1:N"`) を CdlEdge.cardinality へ透過 */
1021
+ cardinality?: string;
1022
+ labelOffsetX?: number;
1023
+ labelOffsetY?: number;
1024
+ routing?: "default" | "back-detour";
1025
+ /** CAR-549 SSOT ... true で label を edge path 上に重ねる (flowchart condition label 用) */
1026
+ overlay?: boolean;
1027
+ /** signal binding: stroke-width template (CAR edge-signal-binding) */
1028
+ widthBind?: string;
1029
+ /** signal binding: stroke color template */
1030
+ strokeBind?: string;
1031
+ /** signal binding: stroke-dashoffset template */
1032
+ dashOffsetBind?: string;
1033
+ }) => DiagramBuilder;
1034
+ /**
1035
+ * 複数 node を 1 行で宣言する batch helper。
1036
+ * @example .nodes([{ id: "a", lane: "l", stack: 0, kind: "actor", title: "Client" }, ...])
1037
+ */
1038
+ nodes: (defs: Array<{
1039
+ id: string;
1040
+ } & Omit<CdlNode, "id">>) => DiagramBuilder;
1041
+ /**
1042
+ * repeat = N 個の同形 node を宣言的に生成 (CAR: repeat primitive)。
1043
+ * templateFn は index (0..count-1) を受け取って node spec を返す関数、 or template で
1044
+ * `{i}` を書けば index に置換される。 array に対する map より typed で、 index-based signal
1045
+ * 参照 (source: 'gas_{i}') が組める。
1046
+ *
1047
+ * 例: EIP1559 の gas rect 3 個並び (base の 1.0x / 1.2x / 1.5x)
1048
+ * .repeatNodes(3, (i) => ({
1049
+ * id: `rect-${i}`,
1050
+ * lane: `l${i+1}`,
1051
+ * stack: 0,
1052
+ * kind: 'dyn-rect',
1053
+ * title: `Block ${i+1}`,
1054
+ * shape: { kind: 'rect', source: `{gas${i+1}}`, fillMax: 100, orient: 'up' }
1055
+ * }))
1056
+ */
1057
+ repeatNodes: (count: number, templateFn: (i: number) => {
1058
+ id: string;
1059
+ } & Omit<CdlNode, "id">) => DiagramBuilder;
1060
+ /**
1061
+ * gridNodes = 2D grid で node を配置、 rows × cols で iteration index (r, c) を渡す。
1062
+ * template で `{r}` / `{c}` / `{i}` (r*cols + c) を置換可能。 grid の見出し diagram (chess board / matrix / mesh) を宣言的に。
1063
+ */
1064
+ gridNodes: (rows: number, cols: number, templateFn: (r: number, c: number, i: number) => {
1065
+ id: string;
1066
+ } & Omit<CdlNode, "id">) => DiagramBuilder;
1067
+ /**
1068
+ * radialNodes = 中心点周りの円周上に count 個の node を宣言的に配置 (hub-and-spoke)。
1069
+ * templateFn は (i, angle_deg, x_offset, y_offset) を受け取り、 spec に stack 値と
1070
+ * `{i}` / `{deg}` template 経由で id / title を組める。 SVG 座標系 (y 下向き) 前提で
1071
+ * angle=0 が右、 90 が下、 180 が左、 270 が上。 layout 上は 1 lane に stack=i として
1072
+ * 並べつつ、 render 側 relative offset で円形配置する想定 (consumer 側で offset を適用可)。
1073
+ */
1074
+ radialNodes: (count: number, radius: number, templateFn: (i: number, angleDeg: number, offsetX: number, offsetY: number) => {
1075
+ id: string;
1076
+ } & Omit<CdlNode, "id">) => DiagramBuilder;
1077
+ /**
1078
+ * treeNodes = 深さ (depth) × 分岐数 (branching) の完全 tree layout。
1079
+ * 各 node の (level, posInLevel, i, offsetX, offsetY) を templateFn に渡す。
1080
+ * offsetX/Y は renderOffsetX/Y として直接使える相対 pixel。 root = level 0、 leaves = level depth-1。
1081
+ */
1082
+ treeNodes: (depth: number, branching: number,
1083
+ /** horizontal spacing (px) per leaf */
1084
+ hSpacing: number,
1085
+ /** vertical spacing (px) per level */
1086
+ vSpacing: number, templateFn: (level: number, posInLevel: number, i: number, offsetX: number, offsetY: number) => {
1087
+ id: string;
1088
+ } & Omit<CdlNode, "id">) => DiagramBuilder;
1089
+ /**
1090
+ * 複数 edge を 1 行で宣言する batch helper。
1091
+ * @example .edges([{ from: "a", to: "b", label: "call" }, ...])
1092
+ */
1093
+ edges: (defs: Array<{
1094
+ from: string;
1095
+ to: string;
1096
+ id?: string;
1097
+ label: string;
1098
+ sub?: string;
1099
+ tone?: Tone;
1100
+ side?: Side;
1101
+ style?: EdgeStyle;
1102
+ guard?: string;
1103
+ cardinality?: string;
1104
+ labelOffsetX?: number;
1105
+ labelOffsetY?: number;
1106
+ routing?: "default" | "back-detour";
1107
+ /** CAR-549 SSOT ... true で label を edge path 上に重ねる (flowchart condition label 用) */
1108
+ overlay?: boolean;
1109
+ }>) => DiagramBuilder;
1110
+ state: (id: string, opts: Omit<CdlState, "id">) => DiagramBuilder;
1111
+ /**
1112
+ * arraySignal = 配列を保持する signal の shortcut (SSOT)。
1113
+ * 内部は state と同じ格納 (initial は JSON stringify)、 template では
1114
+ * `{sig[0]}` / `{sig.length}` / `{sig.sum}` / `{sig.max}` / `{sig.min}` / `{sig.avg}`
1115
+ * で access する (`render/utils.ts:interpolate` の拡張規則)。
1116
+ * event handler で update する場合は `JSON.stringify(newArray)` を setSignal に渡す。
1117
+ */
1118
+ arraySignal: (id: string, initial: (string | number)[]) => DiagramBuilder;
1119
+ /**
1120
+ * interactive input widget entry (CAR #243)。
1121
+ * `.input.slider(id, { min, max, ... })` の様に chain して widget spec を追加する。
1122
+ * consumer app 側の React / plain HTML が対応 signal に bind して SVG と反応させる。
1123
+ */
1124
+ input: InputChain;
1125
+ /**
1126
+ * formula primitive (CAR #244)。 safe subset の algebraic expression を diagram に追加、
1127
+ * consumer が createFormulaComputeds() で computed 化して signal と reactive bind する。
1128
+ * expression syntax error は本 method 内で throw (fail-fast)。
1129
+ * 例 `.formula("gasFee", "gasBase * (1 + delta/8)")`
1130
+ */
1131
+ formula: (id: string, expression: string) => DiagramBuilder;
1132
+ /**
1133
+ * derive chain = 「連鎖形 formula」 を N 個宣言的に生成 (CAR: derive chain)。
1134
+ * 各 iteration で `{i}` 置換と、 直前 iteration の id (`prev`) を参照可能。
1135
+ *
1136
+ * 例: gas1 = base、 gas2 = gas1 * 1.2、 gas3 = gas2 * 1.2 のような chain
1137
+ * .deriveChain('gas', 3, (i) => i === 0 ? 'base' : 'prev * 1.2')
1138
+ *
1139
+ * baseId は「gas1 / gas2 / gas3」 のように `gas${i+1}` として使われる、
1140
+ * expr fn は index (0..count-1) と prev id (前 iteration の完全 id) を受け取る。
1141
+ */
1142
+ deriveChain: (baseId: string, count: number, exprFn: (i: number, prev: string | null) => string) => DiagramBuilder;
1143
+ /**
1144
+ * animation primitive (CAR #245)。 `.animation.scroll(id, opts)` で scroll-driven trigger を追加。
1145
+ * 将来的に `.animation.time(...)` 等の time-based primitive を追加する余地を残す namespace。
1146
+ */
1147
+ animation: AnimationChain;
1148
+ /**
1149
+ * event handler primitive (CAR #246)。 `.on.click(target, handlerId)` / `.on.hover(...)` で
1150
+ * DOM event handler を bind、 consumer app 側で attachEventHandlers(root, diagram, handlers) 経由で
1151
+ * signal update 等を実行する。
1152
+ */
1153
+ on: EventChain;
1154
+ /**
1155
+ * readout primitive (visual widget for displaying signal / state values)。
1156
+ * `.readout.bar / .gauge / .stat / .sparkline` で spec 追加、 interactive panel に描画される。
1157
+ */
1158
+ readout: ReadoutChain;
1159
+ phase: (id: string, opts: {
1160
+ duration?: number;
1161
+ title: string;
1162
+ body: string;
1163
+ }, build: (p: PhaseBuilder) => PhaseBuilder) => DiagramBuilder;
1164
+ build: () => CdlDiagram;
1165
+ };
1166
+ declare function diagram(id: string, options: {
1167
+ topic: string;
1168
+ /**
1169
+ * structured-data (schema.org) 抽出の対象とみなすか (既定 = `"extract"`)。
1170
+ * figure 1 つを見せる見本など、 文章として取り出す中身を持たせるつもりが無い図に
1171
+ * `"exclude"` を付ける。 詳細は `CdlDiagram.structuredData` の doc 参照。
1172
+ */
1173
+ structuredData?: "extract" | "exclude";
1174
+ }): DiagramBuilder;
1175
+
1176
+ /**
1177
+ * formula expression の AST 型 (CAR #244)。
1178
+ *
1179
+ * safe subset のみを表現する。 演算子 (+ - * / %) / 比較 (< <= > >= == !=) / 三項 (?:) / 括弧 /
1180
+ * 数値リテラル / identifier (signal 名) / Math.* 関数呼出の 7 pattern。
1181
+ *
1182
+ * discriminated union で kind ごとに fields を厳密化、 evaluator / extractor が type narrowing で走査する。
1183
+ */
1184
+ type FormulaAst = {
1185
+ type: "number";
1186
+ value: number;
1187
+ } | {
1188
+ type: "identifier";
1189
+ name: string;
1190
+ } | {
1191
+ type: "binaryOp";
1192
+ op: BinaryOp;
1193
+ left: FormulaAst;
1194
+ right: FormulaAst;
1195
+ } | {
1196
+ type: "unaryOp";
1197
+ op: "-";
1198
+ operand: FormulaAst;
1199
+ } | {
1200
+ type: "ternary";
1201
+ condition: FormulaAst;
1202
+ whenTrue: FormulaAst;
1203
+ whenFalse: FormulaAst;
1204
+ } | {
1205
+ type: "call";
1206
+ fn: MathFnName;
1207
+ args: FormulaAst[];
1208
+ };
1209
+ type BinaryOp = "+" | "-" | "*" | "/" | "%" | "<" | "<=" | ">" | ">=" | "==" | "!=";
1210
+ type MathFnName = "Math.min" | "Math.max" | "Math.abs" | "Math.floor" | "Math.ceil" | "Math.round";
1211
+
1212
+ /**
1213
+ * expression 文字列を parse して AST に変換する。 syntax error は Error を throw する。
1214
+ * 空文字列は Error。
1215
+ */
1216
+ declare function parseFormula(expression: string): FormulaAst;
1217
+ /**
1218
+ * AST 内で参照される identifier (signal 名) を全て収集する。 evaluator が signal 経由で
1219
+ * runtime value を lookup する時の識別子リスト作成に使う。
1220
+ */
1221
+ declare function extractIdentifiers(ast: FormulaAst, out?: Set<string>): Set<string>;
1222
+
1223
+ /**
1224
+ * formula 評価 context = identifier 名 → runtime value の map。 caller が signal.value 等から構築して渡す。
1225
+ * `evaluate` に直接渡す eager 経路の型 (簡易 use case)。
1226
+ */
1227
+ type FormulaContext = Record<string, number | boolean>;
1228
+ /**
1229
+ * formula 評価 resolver = identifier 名 を受け取って値を返す関数。
1230
+ * lazy evaluation を可能にする (ternary の unselected branch で undefined identifier / division by zero を回避)。
1231
+ */
1232
+ type FormulaResolver = (name: string) => number | boolean;
1233
+ /**
1234
+ * AST を pure に評価して結果を返す (CAR #244)。
1235
+ * safe subset のみサポート (parser 側で syntax error は既に排除済)。
1236
+ *
1237
+ * 第 2 引数は `FormulaContext` (record) or `FormulaResolver` (function) のどちらも受け付ける。
1238
+ * ternary の unselected branch を skip したい caller は resolver 経路を使う (evaluator が identifier
1239
+ * node に到達した時のみ resolver を呼ぶので、 unselected branch の identifier は lookup されない)。
1240
+ *
1241
+ * runtime error として:
1242
+ * - undefined identifier (resolver / ctx が throw)
1243
+ * - number 期待箇所に boolean が来た (or 逆)
1244
+ * - division by zero (`/ 0` and `% 0`)
1245
+ * - ternary condition が boolean でない
1246
+ */
1247
+ declare function evaluate(ast: FormulaAst, ctxOrResolver: FormulaContext | FormulaResolver): number | boolean;
1248
+
1249
+ /**
1250
+ * diagram の `formulas` から computed を生成、 formula.id をキーにした map を返す helper (CAR #244)。
1251
+ *
1252
+ * 各 formula は expression を parse し、 参照する identifier (signal 名) を signals map から lookup して
1253
+ * computed を生成する。 computed.value を read すると:
1254
+ * 1. formula 内 identifier に対応する signal.value を read (自動 dep 登録)
1255
+ * 2. AST を evaluate して結果を返す
1256
+ * signal 変化で computed が自動再計算される (Step 2 の reactive graph に統合)。
1257
+ *
1258
+ * `Object.create(null)` で prototype pollution safe (input-signals.ts と同 pattern)。
1259
+ */
1260
+ declare function createFormulaComputeds(diagram: CdlDiagram, signals: Record<string, Signal<unknown>>): Record<string, Computed<number | boolean>>;
1261
+
1262
+ /**
1263
+ * scroll progress 計算 (pure function、 CAR #245)。
1264
+ *
1265
+ * spec.start / spec.end の semantics:
1266
+ * - progress 0 = element top が viewport 相対位置 start に到達した瞬間
1267
+ * (default start = 1.0 = element top が viewport bottom に到達 = 進捗開始)
1268
+ * - progress 1 = element bottom が viewport 相対位置 end に到達した瞬間
1269
+ * (default end = 0.0 = element bottom が viewport top に到達 = 進捗完了)
1270
+ *
1271
+ * 中心基準ではなく edge 基準にすることで、 element の height に依存しない一貫した scroll 距離での
1272
+ * 進捗計算になる (codex Round 1 finding fix、 中心基準だと element height の半分だけ range が縮まる)。
1273
+ *
1274
+ * @param elementRect getBoundingClientRect 相当 (top / bottom は viewport 相対座標)
1275
+ * @param viewportHeight window.innerHeight 相当
1276
+ * @param spec CdlScrollTrigger の start / end
1277
+ * @returns 0..1 の progress (clamp 済)
1278
+ */
1279
+ declare function computeScrollProgress(elementRect: {
1280
+ top: number;
1281
+ bottom: number;
1282
+ }, viewportHeight: number, spec: Pick<CdlScrollTrigger, "start" | "end">): number;
1283
+
1284
+ /**
1285
+ * event handler function type (CAR #246)。
1286
+ * consumer が signal update / computed 参照 / batch update 等を実装する。
1287
+ * event 引数は DOM Event 本体 (target / clientX / preventDefault 等が使える)。
1288
+ */
1289
+ type CdlEventHandler = (event: Event) => void;
1290
+ /**
1291
+ * attach 結果 handle (CAR #246)。 dispose 呼出で全 listener を cleanup。
1292
+ */
1293
+ type EventAttachHandle = {
1294
+ /** 全 event listener を DOM から解除、 hydration unmount 経路。 */
1295
+ dispose: () => void;
1296
+ };
1297
+ /**
1298
+ * diagram の eventBindings を DOM element に attach する (CAR #246)。
1299
+ *
1300
+ * root element 内で target 記述に応じて element を lookup:
1301
+ * - node: `[data-cdl-node="{id}"]` selector
1302
+ * - edge: `[data-cdl-edge="{id}"]` selector
1303
+ * - lane: `[data-cdl-lane="{id}"]` selector
1304
+ * - diagram: root 自身
1305
+ *
1306
+ * event 種別:
1307
+ * - click: `click` event
1308
+ * - hover: `mouseenter` + `mouseleave` の 2 event を同 handler に bind、 consumer は
1309
+ * `event.type` で enter / leave を分岐する (tooltip / highlight の enter+clear を 1 binding で表現)
1310
+ *
1311
+ * listener は passive: true で登録 (scroll blocking 回避)。 target element が見つからない場合は
1312
+ * silent skip + console warn (initial render 未完了の可能性、 fatal ではない)。
1313
+ * handler map に該当 handlerId がない場合も silent skip + warn。
1314
+ *
1315
+ * 戻り値の dispose() で全 listener を解除。 hydration lifecycle (React useEffect の cleanup / unmount)
1316
+ * と紐付けて use する。
1317
+ */
1318
+ declare function attachEventHandlers(root: Element, diagram: CdlDiagram, handlers: Record<string, CdlEventHandler>): EventAttachHandle;
1319
+ /**
1320
+ * event target 記述から実 DOM element を 1 つ lookup (最初の一致)。 test / debug 用途。
1321
+ * edge target で label sibling も対象にする場合は resolveTargetAll を使う。
1322
+ */
1323
+ declare function resolveTarget(root: Element, target: CdlEventTarget): Element | null;
1324
+ /**
1325
+ * event target 記述から実 DOM element を全て lookup (attach 経路の SSOT)。
1326
+ * edge target は本体 `[data-cdl-edge="{id}"]` + label sibling `[data-cdl-edge-label-for="{id}"]` の 2 系統
1327
+ * を含める (label 上での click / hover を miss しないため、 codex Round 2 P2 finding)。
1328
+ * 該当 element がなければ空配列を返す (silent skip 判定は attach 側)。
1329
+ */
1330
+ declare function resolveTargetAll(root: Element, target: CdlEventTarget): Element[];
1331
+
1332
+ /**
1333
+ * discriminated union の exhaustive check helper (CAR #262)。
1334
+ *
1335
+ * switch case で全 union member を処理した後 `default: return assertNever(x)` と書くと、
1336
+ * union に新 member が追加された時に compile error で検知できる (x: never が narrow できずに
1337
+ * TypeScript 側で type error になる)。 runtime に到達した場合は Error を throw する
1338
+ * (typing 通り不到達だが、 想定外 kind が type check bypass 経路で流れ込んだ時の fail-fast)。
1339
+ *
1340
+ * 適用対象は builder / factory / render 内で input.kind / edge.style / node.kind 等の
1341
+ * discriminated union を分岐する経路。 新 kind 追加時に silent fallthrough を防ぐ。
1342
+ */
1343
+ declare function assertNever(value: never, context?: string): never;
1344
+
1345
+ /**
1346
+ * compile = validate → layout で LaidDiagram を返す。
1347
+ * render は別 file (render.tsx) が LaidDiagram を消費。
1348
+ */
1349
+ declare function compile(diag: CdlDiagram): LaidDiagram;
1350
+
1351
+ /**
1352
+ * Layout engine
1353
+ *
1354
+ * 規約 (SPEC.md §Layout engine 規約 と一致)。
1355
+ * - lane が x 軸 column を決める
1356
+ * - node は lane 内 stack 順で縦並び、 各 stack 間 gap 80px、 上端 80px
1357
+ * - lane label は lane 上部 30px 高
1358
+ * - contain: true の lane は内部 node 全部を 24px padding で囲む boundary
1359
+ * - edge routing は orthogonal L 字 (起点 side → 中継 → 終点 side)、 左右 lane 跨ぎは S 字 bezier
1360
+ * - edge label は path 中点から path 進行方向の垂直方向に 50-80px、 node bbox を避ける
1361
+ * - viewBox は cdl が auto 計算 (右端 +80 / 下端 +80)
1362
+ */
1363
+ declare function layout(diag: CdlDiagram): LaidDiagram;
1364
+
1365
+ /**
1366
+ * cdl が compile される前に通る validator。
1367
+ * 構造的破綻 (重複 id / 不存在 id 参照 / 必須要素 0 件 等) は throw、
1368
+ * 文体警告 (japanese-only 違反) は console.warn で build を止めない。
1369
+ *
1370
+ * error 統一 format ... `[cdl] {category}: {detail}` で grep / 著者の locate が楽に。
1371
+ */
1372
+ declare function validate(diag: CdlDiagram): void;
1373
+
1374
+ /**
1375
+ * edge label の 2 行の文字仕様 SSOT。
1376
+ *
1377
+ * 描画 (`render/edges.tsx`) と検査 (`visual-validate.ts` の軸 27 `contrast-basics`) が同じ
1378
+ * 値を読む。 描画側に literal を残して検査側が「描画はこうなっているはず」 と書き写す形は
1379
+ * 使わない。 書き写しは片方だけが変わった時に検知できず、 検査が実在しない描画を測る
1380
+ * (軸 27 が sub 行を通常文字として測っていた間、 描画は 19px のままで検査だけが正しかった
1381
+ * ように見えていた)。
1382
+ */
1383
+ /** edge label の行。 `main` = `label`、 `sub` = `sub`。 */
1384
+ type EdgeLabelLine = "main" | "sub";
1385
+ interface EdgeLabelTextSpec {
1386
+ fontSize: number;
1387
+ fontWeight: number;
1388
+ /**
1389
+ * 文字を背景に重ねる不透明度。 1 = 合成なし。
1390
+ *
1391
+ * 1 未満だと実際に目に入る色は背景との合成結果になり、 対比が必ず下がる。
1392
+ */
1393
+ opacity: number;
1394
+ /**
1395
+ * 描画で指定する font-family の先頭。 指定せず継承する行は null。
1396
+ *
1397
+ * 下流が「その太さの face を読み込んでいるか」 を確かめる時、 どの family を見ればよいかが
1398
+ * ここで決まる。 主題が family を宣言しない時の落とし先でもある。
1399
+ */
1400
+ fontFamily: string | null;
1401
+ }
1402
+ declare const EDGE_LABEL_TEXT: Record<EdgeLabelLine, EdgeLabelTextSpec>;
1403
+ /** WCAG AA の large text (24px 以上 or 太字 18.66px 以上) の閾値。 */
1404
+ declare const WCAG_AA_LARGE = 3;
1405
+ /** WCAG AA の通常文字の閾値。 */
1406
+ declare const WCAG_AA_NORMAL = 4.5;
1407
+ /**
1408
+ * WCAG の large text (14pt 太字 = 18.66px、 または 18pt = 24px) に当たるか。
1409
+ *
1410
+ * 太字の下限を 700 に取るのは WCAG が「bold」 の具体値を定めておらず、 CSS の `bold` が
1411
+ * 700 に対応するため。 600 を太字と見なすと、 実際には満たさない対比を通す。
1412
+ */
1413
+ declare function isLargeText(spec: EdgeLabelTextSpec): boolean;
1414
+ /** 文字仕様から WCAG AA が要求する対比を返す。 */
1415
+ declare function requiredContrastRatio(spec: EdgeLabelTextSpec): number;
1416
+
1417
+ /**
1418
+ * Visual validator ... layout 出力 (LaidDiagram) を 67 軸で検証する。
1419
+ *
1420
+ * cdl 著者が「component が render される」 までで完成と思いがちな問題に対し、
1421
+ * 「視覚的に正しい」 を engine 層で機械判定する。
1422
+ *
1423
+ * 軸の一覧は `VisualAxis` 型が SSOT。 Axis 48 (`axis-documentation-completeness`) が見るのは
1424
+ * **手で並べた `allAxes` の要素数が `EXPECTED_AXIS_COUNT` と一致するか** だけで、 `VisualAxis` /
1425
+ * `emptyCounts` / `allAxes` の 3 つが揃っているかまでは見ない。 型と `emptyCounts` のずれは
1426
+ * 型検査が捕まえるが、 `allAxes` への追記漏れは捕まらない (#381)。
1427
+ *
1428
+ * 戻り値 ... ViolationReport (各軸の違反一覧 + sum + ok flag)。
1429
+ * 既存 validate (構造的破綻) と別 layer。 throw せず report のみ返す純粋関数。
1430
+ *
1431
+ * ───────────────────────────────────────────────────────────
1432
+ * ## 新しい軸を足す時の判断基準 (#381)
1433
+ *
1434
+ * **軸は、 品質と結びつく量を、 検証済の前提の下で測る**。 これを外すと、 件数が 0 でも品質を
1435
+ * 表さない軸ができる。 0 件は「問題がない」 とも「測れていない」 とも読めるが、 軸の側からは
1436
+ * 区別できない。
1437
+ *
1438
+ * 2026-07-31 の 1 セッションで、 同じ性質の欠陥を 9 例続けて直した。 件数は dragon の見本
1439
+ * 512 図での実測。
1440
+ *
1441
+ * | 軸 | 何がずれていたか | 件数 |
1442
+ * |---|---|---|
1443
+ * | `lane-lane-gap` | `contain: false` の帯は境界を描かないのに、 境界どうしの間隔を測っていた | 99 |
1444
+ * | `text-readability` / `accessibility-basics` | 題名を一度も描かない種別と、 常に非表示の節を対象にしていた | 50 |
1445
+ * | `responsive-viewport` | 幅の下限の判定が **反転** していた (十分広い時に警告) | 17 |
1446
+ * | `mermaid-parity` | 外部ライブラリの対応表が古く、 対応物がある 3 種を「なし」 としていた | 3 |
1447
+ * | `edge-label-proximity` | label の **中心** から **弦** までを測っていた (弦は描かれず、 中心は label の長さで動く) | 3 |
1448
+ * | `grid-alignment` | 箱の **中心** が 16 の格子に載るかを見ていた (中心を格子に載せる実装は無い) | 2 |
1449
+ * | 衝突判定 (label × 線) | 弦に落とした線と label 矩形の交差を見て、 自分の弦と一致する segment を除いていた。 端点が同じ弧は弦も一致するため、 兄弟の弧が丸ごと消えていた | **-3** |
1450
+ * | `accessibility-basics` (再) | `role="img"` の子孫は支援技術に公開されないのに、 節ごとの題名を見ていた | 0 |
1451
+ * | `mermaid-parity` (再) | 図の id から組み立て器を推定していた (id は著者が付ける値で無関係) | 0 |
1452
+ *
1453
+ * `-3` は「見えていなかった破綻が 3 件あった」 の意味。 直すと件数が **増える** 側の例。
1454
+ *
1455
+ * ずれ方は 4 つに分かれる。
1456
+ *
1457
+ * - **描かれない図形を測る** ... 見えない帯の境界 / 弦 / 箱の中心。 幾何の代表点を実際の
1458
+ * 描画物と取り違える形
1459
+ * - **描かれない情報を測る** ... 題名を描かない種別 / 支援技術に公開されない節。 データには
1460
+ * あるが出力に出ない値を測る形
1461
+ * - **意図を形から推測する** ... id からの組み立て器推定。 著者が持つ情報を図の形から当てよう
1462
+ * とする形。 推測は必ず両方向に外れる (偽陽性と偽陰性が同時に出る)
1463
+ * - **外部の情報が古びる** ... mermaid の対応表。 外部が更新されても軸は気付かない
1464
+ *
1465
+ * `responsive-viewport` だけは別で、 測る量 (`viewBox` の幅) は実在した。 **判定の向きが逆**
1466
+ * だっただけで、 これは片方向の例しか試さないと通ってしまう。
1467
+ *
1468
+ * 足す前に 6 点を確かめる。
1469
+ *
1470
+ * 1. **測る量が出力から取れるか**。 DOM / SVG の `d` / 描画結果のいずれかから取れないなら、
1471
+ * その軸は書けない。 データ構造の値で代用しない
1472
+ * 2. **その量が品質と結びつく根拠を、 実装か規約で示せるか**。 「箱の中心が 16 の格子に載る」
1473
+ * のように、 **誰も実装していない不変条件** を軸が勝手に決めていないか確かめる。 出力から
1474
+ * 取れる量でも、 品質と結びついていなければ測る意味が無い
1475
+ * 3. **発火する例と発火しない例を両方書いたか**。 判定の向きの誤りは片方だけでは通る
1476
+ * 4. **0 件になった時、 それが「実在しない」 ことを示せるか**。 示せないなら、 発火する反例を
1477
+ * 1 つ作って test に置く (軸が dead になっていないことの証明)
1478
+ * 5. **著者の意図を要する判定は、 形から推測せず宣言させる**。 宣言の例 =
1479
+ * `structuredData: "exclude"`
1480
+ * 6. **外部の情報に依存するなら、 基準にした版を書き、 いつ確かめ直すかを決める**。 例 =
1481
+ * `MERMAID_PARITY_BASIS_VERSION`。 版だけでは外部の更新に気付けないので、 版を書いた上で
1482
+ * 「次にこの表を見直す条件」 も併せて残す
1483
+ *
1484
+ * 同じことを静的 test が拾えるなら、 図ごとの軸は要らない。 静的 test は実際の export を
1485
+ * 列挙でき、 推測が要らない (例 = `test/mermaid-parity.test.ts` が `presets.ts` の export を
1486
+ * 全件列挙する)。
1487
+ *
1488
+ * 直した後は、 手で持つ一覧が実装とずれない仕組みを併せて置く。 全 90 種別の描画結果と述語の
1489
+ * 一致 / 全 export と対応表の照合 / 生成物と種別集合の一致 のように、 **実装を列挙して照合する**
1490
+ * test が無いと、 次に種別を足した時に同じ状態へ戻る。
1491
+ * ───────────────────────────────────────────────────────────
1492
+ */
1493
+
1494
+ interface Violation {
1495
+ axis: VisualAxis;
1496
+ diagramId: string;
1497
+ detail: string;
1498
+ severity: "error" | "warn";
1499
+ }
1500
+ type VisualAxis = "node-visibility" | "edge-label-overlap" | "edge-label-proximity" | "text-readability" | "row-format" | "alignment" | "clearance" | "arrow-endpoint-anchoring" | "label-char-range" | "node-overlap" | "edge-crossing" | "edge-node-cross" | "edge-segment-orthogonality" | "label-inside-viewbox" | "lane-cx-consistency" | "row-vertical-spacing" | "group-boundary-clearance" | "node-vertical-clearance" | "lane-lane-gap" | "arrow-marker-clearance" | "grid-alignment" | "phase-layout-stability" | "responsive-viewport" | "accessibility-basics" | "animation-frame-integrity" | "i18n-cjk-detection" | "contrast-basics" | "print-media-compat" | "color-blind-safety" | "marker-gradient-def-integrity" | "subpixel-precision" | "dom-complexity-budget" | "reduced-motion-compat" | "touch-target-size" | "row-content-typing" | "terminal-safe-text" | "gpu-layer-efficiency" | "memory-budget" | "svg-injection-safety" | "seo-metadata-quality" | "bidi-hyphenation" | "structured-data-extraction" | "diagram-version-semver" | "migration-path-consistency" | "axis-coverage-meta" | "axis-documentation-completeness" | "fixture-drift-detection" | "locale-parity" | "validate-performance-budget" | "node-inside-viewbox" | "node-inside-lane" | "edge-inside-viewbox" | "lane-label-inside-viewbox" | "lane-label-overlap" | "row-alignment" | "column-alignment" | "edge-stubout-min" | "fan-origin-single-point" | "detour-slot-distinct" | "arrow-endpoint-center" | "lane-border-clearance" | "row-gap-uniform" | "column-gap-uniform" | "rows-not-rendered" | "malformed-input" | "validation-interrupted";
1501
+ /**
1502
+ * 検査の用途。 用途によって見るべき軸が違う。
1503
+ *
1504
+ * - `production` = 公開する web ページ。 全ての軸を見る (既定)
1505
+ * - `catalog` = 記法の見本。 SEO の軸を見ない
1506
+ *
1507
+ * 見本は「記法をどう書くか」 を示すもので、 検索結果に出す対象ではない。 SEO の軸
1508
+ * (`seo-metadata-quality` / `structured-data-extraction`) は題名の長さや有名な節の数を見るため、
1509
+ * 短い題名の見本を足すたびに警告が出る。
1510
+ *
1511
+ * 経緯 = cardene777/dragon#887 の起票時は見本 412 図で 179 件出ていたが、 その後 見本側の題名が
1512
+ * 整備されて現在は 0 件 (実測 = 316 図で 0 件)。 本区分は「今ある noise を消す」 のではなく
1513
+ * 「見本に SEO の軸を課さない」 契約を先に置くもの。
1514
+ */
1515
+ type ValidationProfile = "production" | "catalog";
1516
+ /** 検査の設定。 */
1517
+ interface ValidateOptions {
1518
+ /** 検査の用途。 既定は `production` (全ての軸を見る)。 */
1519
+ profile?: ValidationProfile;
1520
+ }
1521
+ interface VisualValidationReport {
1522
+ diagramId: string;
1523
+ ok: boolean;
1524
+ violations: Violation[];
1525
+ counts: Record<VisualAxis, number>;
1526
+ /** 検査した用途。 report を保存して後から読む時に、 何を見たかが分かる。 */
1527
+ profile: ValidationProfile;
1528
+ /**
1529
+ * 用途によって見なかった軸。
1530
+ *
1531
+ * `counts` はこれらも 0 を返す。 載せないと「検査して 0 件」 と「検査していない」 を
1532
+ * 区別できず、 集計した時に見た範囲を過大に読む。
1533
+ */
1534
+ skippedAxes: VisualAxis[];
1535
+ }
1536
+ /**
1537
+ * 辺どうしの交差を **実際に描かれる曲線** で数える (軸 11、 #385)。
1538
+ *
1539
+ * 弦のまま数えていた頃は、 弦どうしが平行で弧だけが交わる形を落としていた (#383 の反例 =
1540
+ * 弦 0 件 / 実曲線 2 件)。
1541
+ *
1542
+ * 折れ線に開くだけでは直らない。 `segmentsIntersect` は端点の接触を交差に数えないため、
1543
+ * 交差が分割点にちょうど乗ると隣り合う 2 本の分割片が両方とも端点接触になり、 交差が
1544
+ * **消える** (#384 実測 = `M 0 0 Q 80 80, 160 160` と y=40/80/120 の 3 本で 弦 3 件 → 折れ線
1545
+ * 0 件)。 端点を数える判定に替えると、 今度は同じ節から出る辺どうしの共有端点を数える。
1546
+ *
1547
+ * そこで交点の **座標** を返す判定を使い、 辺の元の端点に一致する交点だけを除く。 分割で
1548
+ * 生まれた節は除かない。 残りを座標で重複排除すると、 分割点に乗った交差が 2 度数えられる
1549
+ * ことも無くなる。
1550
+ *
1551
+ * 関数に切り出してあるのは、 計算量を test から直接測れるようにするため。 検証全体の時間で
1552
+ * 測ると、 曲線では他の軸も分割の分だけ重くなって本軸の寄与が読めない (実測 = 全体の差 138ms に
1553
+ * 対し本軸の寄与は 41ms)。
1554
+ *
1555
+ * @param warnAt 数えるのをやめる件数。 判定は「この件数以上か」 だけなので、 達したら打ち切る。
1556
+ * @returns `complete` は最後まで数え切れたか。 比較回数の上限で止めた時は `false` になり、
1557
+ * 「交差が無い」 と「数え切れなかった」 を呼出側が区別できる。
1558
+ */
1559
+ declare function countEdgeCrossings(edges: ReadonlyArray<{
1560
+ id: string;
1561
+ d: string;
1562
+ from?: string;
1563
+ to?: string;
1564
+ }>, warnAt?: number): {
1565
+ count: number;
1566
+ pairs: string[];
1567
+ complete: boolean;
1568
+ };
1569
+ declare function visualValidateLaid(laid: LaidDiagram, diag: CdlDiagram, opts?: ValidateOptions): VisualValidationReport;
1570
+ /**
1571
+ * 1 つの cdl diagram を 6 軸で視覚検証する。
1572
+ *
1573
+ * layout 計算込みで実行する。 `diag` は CdlDiagram (compile 前の builder 出力)。
1574
+ * layout 中の console.warn ([cdl layout] overlap / near) は本 validator が再判定するため抑止する。
1575
+ */
1576
+ declare function visualValidate(diag: CdlDiagram, opts?: ValidateOptions): VisualValidationReport;
1577
+ /**
1578
+ * 複数 diagram を sweep して合計 report を返す。
1579
+ */
1580
+ interface SweepReport {
1581
+ total: number;
1582
+ pass: number;
1583
+ fail: number;
1584
+ reports: VisualValidationReport[];
1585
+ totalCounts: Record<VisualAxis, number>;
1586
+ /** Axis 47 / 48 meta 判定結果 (per-diagram でなく engine 全体) */
1587
+ metaViolations: Violation[];
1588
+ /** 検査した用途。 */
1589
+ profile: ValidationProfile;
1590
+ /** 用途によって見なかった軸。 `totalCounts` はこれらも 0 を返す。 */
1591
+ skippedAxes: VisualAxis[];
1592
+ }
1593
+ declare function visualValidateAll(diagrams: CdlDiagram[], opts?: ValidateOptions): SweepReport;
1594
+
1595
+ /**
1596
+ * layout + visualValidate を 1 回 loop 化して自動修復する SSOT 経路。
1597
+ *
1598
+ * `layout()` は 1 pass の layout 計算、 17 軸 visualValidate の warn / error は fixWithRetry で
1599
+ * 3 loop まで自動修復を試みる。 修復不能な error は throw 前に戻り値の `report.violations` に集約。
1600
+ *
1601
+ * 呼出しモデル。
1602
+ * - CI / test = `layoutWithValidation(diag, { fix: false })` で diagnose のみ
1603
+ * - dragon build = `layoutWithValidation(diag)` (fix default true) で修復 pass
1604
+ * - 開発中 = `layoutWithValidation(diag, { emitConsole: true })` で warn を console に流す
1605
+ *
1606
+ * 修復 heuristics (順次適用、 1 loop で全 heuristic を試行 → 再 validate)。
1607
+ * - h1 label-offset-reset ... label-char-range / label-inside-viewbox / edge-label-overlap を
1608
+ * 持つ edge の labelOffsetX/Y を 0 にリセット (作者の手動 offset で bbox 範囲外に押し出された
1609
+ * label を default 位置に戻す)
1610
+ * - h2 node-shift-down ... node-overlap error node pair の下 node の stack を +1
1611
+ * - h3 edge-back-detour ... edge-node-cross error edge の routing を "back-detour" に切替
1612
+ */
1613
+
1614
+ interface LayoutWithValidationOptions {
1615
+ /** 修復 loop の最大回数 (default 3)。 0 で修復 skip。 */
1616
+ maxFixLoops?: number;
1617
+ /** true なら warn / error を console.warn / console.error に流す (dev のみ推奨)。 */
1618
+ emitConsole?: boolean;
1619
+ /** true なら修復 heuristics 適用、 false なら diagnose のみ (default true)。 */
1620
+ fix?: boolean;
1621
+ }
1622
+ interface LayoutWithValidationResult {
1623
+ laid: LaidDiagram;
1624
+ report: VisualValidationReport;
1625
+ /** 実際に走った修復 loop 数 (0 = 修復不要 or fix:false or maxFixLoops:0) */
1626
+ fixLoops: number;
1627
+ /** loop ごとに適用された heuristic 名の履歴 */
1628
+ appliedHeuristics: string[];
1629
+ }
1630
+ declare function layoutWithValidation(diag: CdlDiagram, opts?: LayoutWithValidationOptions): LayoutWithValidationResult;
1631
+
1632
+ /**
1633
+ * 幾何演算 helper SSOT。
1634
+ *
1635
+ * visual-validate / collisions / dragon test で共通利用する
1636
+ * 2D geometry primitive を 1 箇所に集約。 前 visual-validate.ts 内 local helper と
1637
+ * Playwright test 内 local helper で 2 度書きだったのを engine から export で統一。
1638
+ */
1639
+ interface Segment {
1640
+ x1: number;
1641
+ y1: number;
1642
+ x2: number;
1643
+ y2: number;
1644
+ }
1645
+ interface Rect {
1646
+ x: number;
1647
+ y: number;
1648
+ w: number;
1649
+ h: number;
1650
+ }
1651
+ /** 点 (px, py) と矩形の縁 (外周) までの最短距離。 内側なら 0、 外側なら実距離。 */
1652
+ declare function pointRectEdgeDistance(px: number, py: number, rect: Rect): number;
1653
+ /** 矩形同士の重なり面積 (world 単位)。 重ならなければ 0。 */
1654
+ declare function rectRectOverlapArea(a: Rect, b: Rect): number;
1655
+ /** 矩形同士の clearance (最短距離)。 重なっていれば 0、 離れていれば実距離。 */
1656
+ declare function rectRectClearance(a: Rect, b: Rect): number;
1657
+ /** 2 線分の交差判定 (端点共有は交差扱いしない)。 */
1658
+ declare function segmentsIntersect(a: Segment, b: Segment): boolean;
1659
+
1660
+ /**
1661
+ * dom-verify-core / dom-verify (cdl adapter) 両方で共有する types。
1662
+ *
1663
+ * PageLike を独立 file に切り出すことで、 core / adapter の循環 import を避ける。
1664
+ */
1665
+ /**
1666
+ * Playwright Page-like 抽象。 packages/cdl は Playwright を直接 import せず、
1667
+ * 利用側で Page を渡す。 これで cdl が browser dependency 持たない。
1668
+ */
1669
+ interface PageLike {
1670
+ /** querySelector の thin wrapper */
1671
+ $$eval<T>(selector: string, fn: (els: Element[]) => T): Promise<T>;
1672
+ $eval<T>(selector: string, fn: (el: Element) => T): Promise<T>;
1673
+ /** 任意 JS を browser context で evaluate */
1674
+ evaluate<T, A>(fn: (arg: A) => T, arg: A): Promise<T>;
1675
+ /** sleep (ms) */
1676
+ waitForTimeout(ms: number): Promise<void>;
1677
+ /** click selector */
1678
+ click(selector: string): Promise<void>;
1679
+ }
1680
+
1681
+ /**
1682
+ * dom-verify core (library 化前段 = adapter pattern の足場)。
1683
+ *
1684
+ * cdl-specific 型 (CdlDiagram / LaidDiagram) に依存しない、 任意の SVG diagram tool に
1685
+ * attach 可能な generic 検証 core。 将来 @cardenelabs/diagram-verifier として独立化する際の
1686
+ * 「枝葉に依存しない API 設計」 を確立する。
1687
+ *
1688
+ * 利用側 (cdl adapter / Mermaid adapter / D2 adapter 等) は GenericDiagramSpec を満たす
1689
+ * object を構築し、 verifyGenericDom(page, spec) で検証を回す。
1690
+ *
1691
+ * 既存 dom-verify.ts は本 file の上に cdl adapter として乗る (cdl の CdlDiagram → GenericDiagramSpec に変換)。
1692
+ */
1693
+
1694
+ /**
1695
+ * 任意 diagram tool が「目」 で検証されるために提供すべき構造的 spec。
1696
+ * cdl-specific な型を排し、 generic な primitive (id / cx / cy / w / h / path d / activate set 等) のみ。
1697
+ */
1698
+ interface GenericDiagramSpec {
1699
+ /** diagram の一意 id ([data-cdl-diagram="..."] でも別 attr でも良い、 adapter で selector を構築) */
1700
+ id: string;
1701
+ /** root selector の DOM attribute (default "data-cdl-diagram") */
1702
+ rootAttribute?: string;
1703
+ /** node selector の DOM attribute (default "data-cdl-node") */
1704
+ nodeAttribute?: string;
1705
+ /** edge selector の DOM attribute (default "data-cdl-edge") */
1706
+ edgeAttribute?: string;
1707
+ /** particle selector の DOM attribute (default "data-cdl-particle") */
1708
+ particleAttribute?: string;
1709
+ /** node 一覧 (id / cx / cy / w / h) */
1710
+ nodes: Array<{
1711
+ id: string;
1712
+ cx: number;
1713
+ cy: number;
1714
+ w: number;
1715
+ h: number;
1716
+ }>;
1717
+ /** edge 一覧 (id / from / to / d / labelX / labelY / style) */
1718
+ edges: Array<{
1719
+ id: string;
1720
+ from: string;
1721
+ to: string;
1722
+ d: string;
1723
+ labelX: number;
1724
+ labelY: number;
1725
+ /** "dotted-flow" 相当のスタイル (particle 検証対象判定用) */
1726
+ hasParticle: boolean;
1727
+ }>;
1728
+ /** phase 一覧 (id / activate set) */
1729
+ phases: Array<{
1730
+ id: string;
1731
+ activate: string[];
1732
+ }>;
1733
+ }
1734
+ /**
1735
+ * 検証種別。
1736
+ */
1737
+ type GenericDiscrepancyKind = "missing-node-element" | "missing-edge-element" | "node-position-mismatch" | "node-size-mismatch" | "edge-path-endpoint-mismatch" | "edge-label-position-mismatch" | "particle-out-of-path" | "activation-mismatch" | "phase-transition-failure";
1738
+ interface GenericDiscrepancy {
1739
+ kind: GenericDiscrepancyKind;
1740
+ diagramId: string;
1741
+ elementId: string;
1742
+ expected: unknown;
1743
+ actual: unknown;
1744
+ detail: string;
1745
+ }
1746
+ interface GenericVerifyOptions {
1747
+ positionTolerance?: number;
1748
+ edgeEndpointTolerance?: number;
1749
+ particleTolerance?: number;
1750
+ particleFrameCount?: number;
1751
+ particleConsecutiveFailThreshold?: number;
1752
+ bboxTolerance?: number;
1753
+ skipBbox?: boolean;
1754
+ skipParticle?: boolean;
1755
+ skipPhase?: boolean;
1756
+ }
1757
+ /**
1758
+ * 任意 diagram tool の adapter が満たすべき contract。
1759
+ *
1760
+ * cdl の場合 ... CdlDiagram → GenericDiagramSpec を返す純関数。
1761
+ * Mermaid の場合 ... mermaid.parse() で AST 取得 → GenericDiagramSpec に変換。
1762
+ * D2 の場合 ... d2 CLI で JSON dump → GenericDiagramSpec に変換。
1763
+ */
1764
+ interface DiagramAdapter<DiagramType> {
1765
+ /** diagram type 名 (例 "cdl" / "mermaid" / "d2") */
1766
+ readonly name: string;
1767
+ /** diagram 型から GenericDiagramSpec への変換 */
1768
+ toSpec(diagram: DiagramType): GenericDiagramSpec;
1769
+ }
1770
+
1771
+ /**
1772
+ * cdl 定義 ↔ 実画面整合性 verifier (「目」 v3 の core)。
1773
+ *
1774
+ * cdl で宣言した node / edge / phase / particle が、 実際の SVG render と一致しているかを
1775
+ * DOM scrape + 数値判定で自動検証する。 LLM 不要、 課金 0。
1776
+ *
1777
+ * 検証 5 種類。
1778
+ * 1. node 位置 ... data-cdl-node="{id}" 要素の boundingBox cx/cy が layout() 計算値と一致
1779
+ * 2. edge path 起点終点 ... <path id="cdl-edge-{id}"> の d 属性の起点 / 終点が from / to node の側面と一致
1780
+ * 3. particle 位置 ... animation N frame 撮影で particle SVG の中心が path 上の点と < tolerance
1781
+ * 4. activation ... phase 切替時に data-cdl-active="true" が phase.activate() で宣言した element のみ
1782
+ * 5. phase 切替 ... phase index 進行で active class set が正確に切替わる
1783
+ */
1784
+
1785
+ /**
1786
+ * 図を描く `<svg>` を取る selector (cdl#336)。
1787
+ *
1788
+ * `[data-cdl-diagram]` の中には `<svg>` が複数ある。 interactive panel が widget ごとに 1 つ
1789
+ * ずつ持ち (readout の ring / gauge / sparkline 等)、 **DOM 順では panel が stage より先**
1790
+ * (`render.tsx` = panel → `CdlStage`)。 `querySelector("svg")` は document 順の最初を返すので、
1791
+ * interactive な図では widget の `<svg>` を掴む。 その `viewBox` (`0 0 64 64` 等) で node 座標を
1792
+ * 換算するため、 照合結果は意味を持たない数字になる。
1793
+ *
1794
+ * 属性で名指しする。 interactive でない図では `<svg>` が 1 つしかないため、 従来と同じものを
1795
+ * 取る (挙動は変わらない)。
1796
+ *
1797
+ * `data-cdl-role` を使わないのは、 あれが consumer の CSS 契約 (`CdlRole`、 `types.ts`) だから。
1798
+ * stage は装飾の対象になる部品ではなく図全体の入れ物で、 role に混ぜると
1799
+ * `[data-cdl-role]` を広く拾う selector が外側の `<svg>` にも当たる。
1800
+ */
1801
+ declare const STAGE_SVG_SELECTOR = "svg[data-cdl-stage]";
1802
+ /**
1803
+ * node の実寸の枠を取る selector (#390)。
1804
+ *
1805
+ * `data-cdl-w/h` は layout の静的値で、 `wBind` / `hBind` の縮小や `renderOffsetX/Y` の移動を
1806
+ * 含まない。 行が枠に収まっているかを実 DOM で測る側はこの要素を見る。
1807
+ *
1808
+ * `STAGE_SVG_SELECTOR` と同じ理由で `data-cdl-role` に置かない。 この矩形は
1809
+ * `fill="none" stroke="none" pointer-events="none"` で、 描画されない計測用の目印。
1810
+ *
1811
+ * **`$$eval` / `evaluate` の callback からこの定数を参照してはいけない**。 callback は文字列
1812
+ * として browser に渡るので module scope が消え、 実行時に `ReferenceError` になる。 callback
1813
+ * 内では同じ値を literal で書くか、 `STAGE_SVG_SELECTOR` のように引数として渡す。
1814
+ */
1815
+ declare const NODE_FRAME_SELECTOR = "[data-cdl-frame]";
1816
+ type DiscrepancyKind = "missing-node-element" | "missing-edge-element" | "node-position-mismatch" | "node-size-mismatch" | "edge-path-endpoint-mismatch" | "edge-label-position-mismatch" | "node-row-overflow" | "node-row-unverifiable" | "particle-out-of-path" | "activation-mismatch" | "phase-transition-failure";
1817
+ interface Discrepancy {
1818
+ kind: DiscrepancyKind;
1819
+ diagramId: string;
1820
+ elementId: string;
1821
+ expected: unknown;
1822
+ actual: unknown;
1823
+ detail: string;
1824
+ }
1825
+ interface VerifyOptions {
1826
+ /** node 位置の許容誤差 (px、 default 2) */
1827
+ positionTolerance?: number;
1828
+ /** edge path 起点終点と node 側面の許容誤差 (px、 default 4) */
1829
+ edgeEndpointTolerance?: number;
1830
+ /**
1831
+ * particle 位置 vs path 上の点との許容誤差 (px、 default 250)。
1832
+ * Chrome / Firefox の animateMotion + getBoundingClientRect が animation 中の particle 位置を
1833
+ * 必ずしも正確に返さない (frame 1 で static 0,0 fallback / mpath transform が screen CTM に反映されない 等)
1834
+ * ため、 user 視覚的に同 diagram 内で許容できる 250px (= 1 lane 程度) を default に。
1835
+ * 実エンジン bug (particle が画面外 / 別 diagram 領域へ飛ぶ) は依然検知できる。
1836
+ */
1837
+ particleTolerance?: number;
1838
+ /** particle 検証時の animation frame 撮影数 (default 8、 0.25s 間隔) */
1839
+ particleFrameCount?: number;
1840
+ /** particle が「path から外れている」 と判定する連続 frame 数の閾値 (default 3、 偶発的 1 frame ずれを許容) */
1841
+ particleConsecutiveFailThreshold?: number;
1842
+ /** boundingBox 照合の許容誤差 (px、 viewBox transform scale を考慮するため大きめ default 8) */
1843
+ bboxTolerance?: number;
1844
+ /** boundingBox 照合を skip (高速 partial run 用) */
1845
+ skipBbox?: boolean;
1846
+ /** particle 検証を skip (animation がない static diagram 用) */
1847
+ skipParticle?: boolean;
1848
+ /** phase 切替検証を skip (single phase diagram 用) */
1849
+ skipPhase?: boolean;
1850
+ /**
1851
+ * 行の文字が node の枠に収まっているかの検査を skip する。
1852
+ *
1853
+ * 実 font に依存するので、 font が読み込まれていない環境では意味のある判定にならない。
1854
+ */
1855
+ skipRowBounds?: boolean;
1856
+ /**
1857
+ * 行の文字が枠から出ていると判定する許容量 (screen px、 default 2)。
1858
+ *
1859
+ * 0 にしない理由は 2 つ。 `getBoundingClientRect()` は字形の外接矩形を返すので、 letter-spacing や
1860
+ * antialias の分だけ数値が揺れる。 加えて枠の角が丸い (`rx`) ため、 角の近くでは枠の内側でも
1861
+ * 矩形としては外に出る。 「明らかに出ている」 だけを拾う。
1862
+ */
1863
+ rowBoundsTolerance?: number;
1864
+ }
1865
+
1866
+ /**
1867
+ * 1 つの diagram について 5 種類の verifier を全部走らせる。
1868
+ *
1869
+ * options.skipBbox / skipParticle / skipPhase で各検証を skip 可能 (動作確認 / partial run 用)。
1870
+ */
1871
+ declare function verifyDiagramDom(page: PageLike, diagram: CdlDiagram, options?: VerifyOptions): Promise<Discrepancy[]>;
1872
+ /**
1873
+ * 複数 diagram を一括 verify。
1874
+ */
1875
+ interface DomVerifyReport {
1876
+ total: number;
1877
+ pass: number;
1878
+ fail: number;
1879
+ discrepancies: Discrepancy[];
1880
+ }
1881
+ declare function verifyAllDiagramsDom(page: PageLike, diagrams: CdlDiagram[], options?: VerifyOptions): Promise<DomVerifyReport>;
1882
+ /**
1883
+ * cdl-adapter: CdlDiagram → GenericDiagramSpec への変換 (目 v4 library 化前段)。
1884
+ *
1885
+ * 将来 @cardenelabs/diagram-verifier を独立 npm publish する際、
1886
+ * dom-verify-core.ts (generic) + 本 adapter (cdl 用) の 2 層構成にする。
1887
+ * 同様の adapter を Mermaid / D2 / 任意 SVG tool 用に増やせる構造。
1888
+ */
1889
+ declare const cdlAdapter: DiagramAdapter<CdlDiagram>;
1890
+
1891
+ /**
1892
+ * 「目」 v5 = author intent verifier。
1893
+ *
1894
+ * cdl で人が宣言した意味的事実 (= 作者意図) が、 実画面 (SVG render) に
1895
+ * 期待通り反映されているかを 6 軸で照合する。 LLM 不要、 課金 0、 規則ベース。
1896
+ *
1897
+ * これまでの dom-verify は engine の自己整合 (cdl 計算値 ↔ DOM attribute 同期) しか見ていなかった。
1898
+ * v5 では「人が `.node("user", { title: "User" })` と書いた時、 画面に `User` という文字が
1899
+ * 出ているか」 のような mermaid 的に当然期待される事実を確認する。
1900
+ *
1901
+ * 6 軸。
1902
+ * 1. node-in-lane ... node が宣言した lane の領域内に居る
1903
+ * 2. node-text-visible ... node.title / subtitle / eyebrow が文字として SVG に存在
1904
+ * 3. edge-label-visible ... edge.label / sub が文字として SVG に存在
1905
+ * 4. edge-connected ... edge path 起点 / 終点が from / to node の bbox の近く
1906
+ * 5. activation-visible ... phase.activate() で宣言した要素が画面で「強調」 されている
1907
+ * 6. tone-color ... edge.tone の指定色が SVG stroke / fill に反映
1908
+ */
1909
+
1910
+ type IntentDiscrepancyKind = "node-outside-lane" | "node-title-missing" | "node-subtitle-missing" | "node-eyebrow-missing" | "edge-label-missing" | "edge-sub-missing" | "edge-disconnected-from-source" | "edge-disconnected-from-target" | "activation-not-visible" | "tone-color-mismatch";
1911
+ interface IntentDiscrepancy {
1912
+ kind: IntentDiscrepancyKind;
1913
+ diagramId: string;
1914
+ elementId: string;
1915
+ expected: unknown;
1916
+ actual: unknown;
1917
+ detail: string;
1918
+ }
1919
+ interface AuthorIntentOptions {
1920
+ /** edge 起点 / 終点と from / to node bbox の許容距離 (px、 default 80) */
1921
+ edgeConnectionTolerance?: number;
1922
+ /**
1923
+ * lane bbox からの node はみ出し許容 (px、 default 500)。
1924
+ * lane height 計算が node h 全部を含まないケースが engine layer に存在し、
1925
+ * 視覚的には許容範囲 (同じ diagram 内に収まる) なので default は緩く設定。
1926
+ * engine bug hunting は --strict (= 24px) で。
1927
+ */
1928
+ laneContainmentTolerance?: number;
1929
+ /** tone 色判定の RGB 距離許容 (default 30) */
1930
+ toneColorTolerance?: number;
1931
+ /** activation 視覚効果判定の opacity / scale 差最小値 (default 0.1) */
1932
+ activationVisualDelta?: number;
1933
+ /** 軸を skip する flag (個別 disable 用) */
1934
+ skipNodeInLane?: boolean;
1935
+ skipNodeText?: boolean;
1936
+ skipEdgeLabel?: boolean;
1937
+ skipEdgeConnected?: boolean;
1938
+ skipActivation?: boolean;
1939
+ skipToneColor?: boolean;
1940
+ }
1941
+ /**
1942
+ * 1 つの diagram について 6 軸検証を全部走らせる。
1943
+ */
1944
+ declare function verifyAuthorIntent(page: PageLike, diagram: CdlDiagram, options?: AuthorIntentOptions): Promise<IntentDiscrepancy[]>;
1945
+ /**
1946
+ * 複数 diagram を一括 verify。
1947
+ */
1948
+ interface AuthorIntentReport {
1949
+ total: number;
1950
+ pass: number;
1951
+ fail: number;
1952
+ discrepancies: IntentDiscrepancy[];
1953
+ }
1954
+ declare function verifyAuthorIntentAll(page: PageLike, diagrams: CdlDiagram[], options?: AuthorIntentOptions): Promise<AuthorIntentReport>;
1955
+
1956
+ /**
1957
+ * CdlDiagramThumbnail ... 通常は CdlDiagramView を縮小表示、
1958
+ * クリックでモーダル開いて viewport いっぱい (94vw x 90vh) に拡大表示する。
1959
+ * Escape / 背景クリック / × ボタンで close。
1960
+ */
1961
+ declare function CdlDiagramThumbnail(props: CdlDiagramViewProps): JSX.Element;
1962
+
1963
+ /**
1964
+ * World 座標系 (engine viewBox 内) と DOM 実測 px の相互変換を提供する pixel-perfect projection layer。
1965
+ *
1966
+ * 背景 ...
1967
+ * engine は viewBox world 単位で全 layout (node.cx / cy / w / h、 edge.d、 label.x / y) を確定する。
1968
+ * 実 DOM 描画は SVG viewBox → CSS width の scale で 縮小 / 拡大され、 world 単位と DOM px は
1969
+ * 一致しない (dragon 実測 scale = displayWidth 490 / viewBox 幅 1820 ≒ 0.269)。
1970
+ *
1971
+ * 従来 routing v8 / v10.1 では WORLD_SCALE = 4.0 の経験係数で world 座標の
1972
+ * label 幅 / clearance を 「DOM px 相当」 に補正していたが、 実は viewBox 幅 / displayWidth の
1973
+ * 比率で数式的に算出可能。 本 module で計算を SSOT 化し、 全 downstream (edges routing / gate 判定 /
1974
+ * visual regression) が正確な変換を使う。
1975
+ *
1976
+ * 提供する変換 ...
1977
+ * 1. worldToPx(worldValue, scale) ... 単一値 world → px
1978
+ * 2. pxToWorld(pxValue, scale) ... 単一値 px → world (WORLD_SCALE 逆算)
1979
+ * 3. computeViewportScale(viewBoxWidth, displayWidth) ... scale factor = viewBoxWidth / displayWidth
1980
+ * 4. projectNode / projectEdgeLabel / projectPathSegment ... 各要素の px 予測 bbox / point
1981
+ *
1982
+ * SSOT ...
1983
+ * 本 module の変換式は SVG viewBox の projection と完全に一致する (SVG spec `preserveAspectRatio`
1984
+ * default = xMidYMid meet で uniform scale)。 fontSize は viewBox 単位で描画時に scale される
1985
+ * ため、 world font 幅 × scale = DOM 実測 幅。
1986
+ */
1987
+
1988
+ /**
1989
+ * viewBox width と CSS display width から scale factor を計算する。
1990
+ *
1991
+ * scale factor = viewBoxWidth / displayWidth
1992
+ * 例 = dragon CdlDiagramThumbnail は viewBox width 1820 world unit、 display width 490 CSS px
1993
+ * → scale = 1820 / 490 ≒ 3.714
1994
+ * world 座標を DOM px に落とす場合は `world / scale`、
1995
+ * DOM px を world に戻す場合は `px * scale`。
1996
+ *
1997
+ * @param viewBoxWidth SVG viewBox の width 属性値 (world 単位)
1998
+ * @param displayWidth CSS 描画時の width (px)
1999
+ * @returns world unit per DOM px (常に > 0)
2000
+ */
2001
+ declare function computeViewportScale(viewBoxWidth: number, displayWidth: number): number;
2002
+ /**
2003
+ * world 座標値を DOM px に変換する。 scale = viewBoxWidth / displayWidth。
2004
+ */
2005
+ declare function worldToPx(worldValue: number, scale: number): number;
2006
+ /**
2007
+ * DOM px を world 座標に変換する。 scale = viewBoxWidth / displayWidth。
2008
+ */
2009
+ declare function pxToWorld(pxValue: number, scale: number): number;
2010
+ /**
2011
+ * Node の px 予測 bbox。 world 座標 (cx, cy, w, h) を DOM px 実測相当に変換する。
2012
+ *
2013
+ * SVG viewBox は uniform scale (preserveAspectRatio xMidYMid meet)、 x / y 両軸同一 scale。
2014
+ * node cx / cy / w / h は viewBox 内 world 座標そのまま、 SVG viewBox → CSS 描画時に scale で
2015
+ * 縮小される。 実 DOM getBoundingClientRect().width == worldW / scale。
2016
+ *
2017
+ * @param n LaidNode (engine 計算後の world 座標)
2018
+ * @param scale viewBox width / display width (world unit per DOM px)
2019
+ * @param svgOffsetPx SVG 要素の CSS 位置 offset (通常 svgClientRect.left / top、 省略時 0)
2020
+ * @returns 予測 DOM px bbox { x, y, w, h }
2021
+ */
2022
+ declare function projectNode(n: LaidNode, scale: number, svgOffsetPx?: {
2023
+ x: number;
2024
+ y: number;
2025
+ }, viewBoxOrigin?: {
2026
+ x: number;
2027
+ y: number;
2028
+ }): {
2029
+ x: number;
2030
+ y: number;
2031
+ w: number;
2032
+ h: number;
2033
+ };
2034
+ /**
2035
+ * Edge label の px 予測 bbox。 label.x / y は world 座標での中心 (anchor middle 時) or
2036
+ * 起点 / 終点 (anchor start / end 時)、 labelBoxW を world 単位で受け取り px 換算する。
2037
+ *
2038
+ * @param edge LaidEdge (labelX, labelY, labelAnchor 既確定)
2039
+ * @param labelBoxWorldW world 単位での label bbox 幅 (measureTextWidth + padding)
2040
+ * @param labelBoxWorldH world 単位での label bbox 高 (36 default、 sub あり時 64)
2041
+ * @param scale viewBox width / display width
2042
+ * @param svgOffsetPx SVG 要素の CSS 位置 offset
2043
+ * @param viewBoxOrigin viewBox の (x_min, y_min)、 auto viewBox で 0 でない
2044
+ */
2045
+ declare function projectEdgeLabel(edge: LaidEdge, labelBoxWorldW: number, labelBoxWorldH: number, scale: number, svgOffsetPx?: {
2046
+ x: number;
2047
+ y: number;
2048
+ }, viewBoxOrigin?: {
2049
+ x: number;
2050
+ y: number;
2051
+ }): {
2052
+ x: number;
2053
+ y: number;
2054
+ w: number;
2055
+ h: number;
2056
+ };
2057
+ /**
2058
+ * Path segment (start point → end point) を DOM px に投影する。
2059
+ *
2060
+ * @param seg { x1, y1, x2, y2 } の world 座標 segment
2061
+ * @param scale viewBox width / display width
2062
+ * @param svgOffsetPx SVG 要素の CSS 位置 offset
2063
+ * @param viewBoxOrigin viewBox の (x_min, y_min)
2064
+ */
2065
+ declare function projectPathSegment(seg: {
2066
+ x1: number;
2067
+ y1: number;
2068
+ x2: number;
2069
+ y2: number;
2070
+ }, scale: number, svgOffsetPx?: {
2071
+ x: number;
2072
+ y: number;
2073
+ }, viewBoxOrigin?: {
2074
+ x: number;
2075
+ y: number;
2076
+ }): {
2077
+ x1: number;
2078
+ y1: number;
2079
+ x2: number;
2080
+ y2: number;
2081
+ };
2082
+ /**
2083
+ * 2 bbox の距離 (rectangular gap、 交差時 0)。 px 単位で受け取り px 単位で返す。
2084
+ * label と node の clearance 判定に使う。
2085
+ */
2086
+ declare function bboxClearance(a: {
2087
+ x: number;
2088
+ y: number;
2089
+ w: number;
2090
+ h: number;
2091
+ }, b: {
2092
+ x: number;
2093
+ y: number;
2094
+ w: number;
2095
+ h: number;
2096
+ }): number;
2097
+ /**
2098
+ * 点と segment の最短距離 (px 単位)。 label center → path segment 判定に使う。
2099
+ */
2100
+ declare function pointToSegmentDistance(px: number, py: number, seg: {
2101
+ x1: number;
2102
+ y1: number;
2103
+ x2: number;
2104
+ y2: number;
2105
+ }): number;
2106
+
2107
+ /**
2108
+ * Clearance / distance SSOT 定数 (world unit)。
2109
+ *
2110
+ * label × node / label × label / label × path の clearance と label × path 距離の
2111
+ * 目標範囲を engine 内で SSOT 定義。 downstream (edges / collisions / visual-diagnostics / dragon
2112
+ * gate) が本 module を import して同一値を使うことで、 threshold の食い違いを防ぐ。
2113
+ *
2114
+ * world unit = SVG viewBox 内の座標系単位。 実 DOM px は viewBox scale で 縮小 / 拡大される
2115
+ * (px-projection 参照)。 gate 判定は px-projection でこれらの world 値を実測 scale に変換して
2116
+ * 使う (pixel-perfect assertion)。
2117
+ */
2118
+ /** node × edge-label ... 誤読を起こす重大事案、 32 world unit (edges.ts / collisions.ts SSOT) */
2119
+ declare const CLEARANCE_NODE_LABEL = 32;
2120
+ /** lane-label × edge-label ... lane 見出しと edge label の近接判定、 24 world unit */
2121
+ declare const CLEARANCE_LANE_LABEL = 24;
2122
+ /** edge-label × edge-label ... 2 label が近接して読みづらい、 36 world unit */
2123
+ declare const CLEARANCE_LABEL_LABEL = 36;
2124
+ /** edge-path × edge-label ... path 線が label 文字を貫通する誤読、 14 world unit */
2125
+ declare const CLEARANCE_PATH_LABEL = 14;
2126
+ /** label 箱 → path 最短距離の下限 (label が path に貼り付き) */
2127
+ declare const DIST_LABEL_PATH_MIN = 14;
2128
+ /**
2129
+ * label 箱 → path 最短距離の上限 (label が path から浮遊)。
2130
+ *
2131
+ * 「見た目の隙間」 の上限で、 軸 7 (`edge-label-proximity`) の warn 閾値がこの値を使う。
2132
+ * engine 自身が置く label は 16 world 以内に収まり、 これを超えるのは著者が `labelOffset`
2133
+ * で動かした場合に限る。
2134
+ */
2135
+ declare const DIST_LABEL_PATH_MAX = 80;
2136
+ /** label 位置 score の目標 path 距離 (routing v10 SSOT、 NORMAL 100 world = DOM ~25 px 相当) */
2137
+ declare const TARGET_LABEL_PATH_DIST = 100;
2138
+ /** arrow tail / head の折れ角許容 (degree) */
2139
+ declare const ARROW_ANGLE_MAX_DEG = 100;
2140
+ /** Fan-in / fan-out path 分散の slot 幅 (world unit) */
2141
+ declare const FAN_GAP = 120;
2142
+ /** Detour path の gap = obstacle bottom / top からの余白 (routing v10.1 SSOT、 44 → 94 拡張) */
2143
+ declare const DETOUR_GAP = 94;
2144
+
2145
+ /**
2146
+ * Layout spec SSOT (world unit)。
2147
+ *
2148
+ * CAR-421 (PR 1 / CAR-418 chain 伝搬 shift + spec 固定化) で新設した固定値 SSOT。
2149
+ * 既存 `clearance-constants.ts` は clearance (near collision 距離下限) の SSOT だが、
2150
+ * 本 file は「positive spec」 = engine が満たすべき正しい配置目標値 の SSOT。
2151
+ *
2152
+ * clearance-constants.ts と役割分担。
2153
+ * - clearance-constants.ts ... 「これ以下は誤読」 の下限 (near collision detection)
2154
+ * - spec.ts (本 file) ... 「これを満たすと正しい」 の目標値 (positive-check axis)
2155
+ *
2156
+ * downstream (layout / edges / collisions / visual-validate) は本 module を import して同一値を
2157
+ * 使うことで、 magic number の散在を防ぐ。 dragon 側 pixel-perfect gate も同 SSOT を参照する。
2158
+ *
2159
+ * world unit = SVG viewBox 内の座標系単位 (clearance-constants.ts と同定義)。
2160
+ */
2161
+
2162
+ /**
2163
+ * その node が `rows` を文字として描くか (SSOT)。
2164
+ *
2165
+ * 描かない種別に対して「行が `key: value` 形式か」 (`row-format`) や「行が枠内に収まるか」
2166
+ * (`row-vertical-spacing`) を判定しても、 前提が成り立たない。 逆に、 描かない種別に `rows` が
2167
+ * 書かれていること自体は「書いた内容が消える」 という別の破綻で、 `rows-not-rendered` が見る。
2168
+ */
2169
+ declare function rendersRows(kind: string | undefined): boolean;
2170
+ /**
2171
+ * `rows` を持つ node に必要な高さ (SSOT)。
2172
+ *
2173
+ * 最終行の baseline から 1 行送りぶんとレイアウト余白を確保する。 行を描かない種別は `null`。
2174
+ *
2175
+ * `layout/nodes.ts` の `autoStorageHeight` と、 下流 (dragon の sequence header 等) が
2176
+ * 同じ値を使う。 各所で式を写すと、 描画を変えた時に一部だけ古くなる。
2177
+ *
2178
+ * これは「そう置くとよい高さ」 で、 「これを下回ると破綻する高さ」 ではない。 後者は
2179
+ * `rowBaselineY` + `rowGlyphDepth` で、 軸 16 はそちらを見る。
2180
+ */
2181
+ declare function requiredRowsHeight(kind: string | undefined, rowCount: number): number | null;
2182
+ /**
2183
+ * label pill 端 と path segment 距離 (world unit)。
2184
+ *
2185
+ * **engine が label を離して置く時の目標値**。 検証器の軸 59 `edge-label-clearance` が
2186
+ * 「この値未満なら貼り付いている」 と判定していたが、 #386 で畳んだ = 中置き label (線の上に
2187
+ * 載せて pill で線を隠す配置) が正常なので、 距離の下限を一律に課せない。
2188
+ *
2189
+ * 下限の契約は layout の test が持つ (`test/car202-edge-label-clearance.test.ts` が、 離して
2190
+ * 置いた辺について実際に描かれる線との隙間を測る)。 検証器はこの値を見ない。
2191
+ *
2192
+ * clearance-constants.ts の `CLEARANCE_PATH_LABEL` (14) より大きい ... 本値は
2193
+ * 「positive spec」 = 目標値、 CLEARANCE_PATH_LABEL は「これ以下は誤読」 の下限。
2194
+ * 16 world unit = 大 diagram で ~4 px。 経緯 = CAR-483 で 8 → 16 拡大 → CAR-570 で
2195
+ * 16 → 8 縮小 (「16 は離れすぎ」 判定) → CAR-630 で 8 → 16 に戻す (実描画で 8 world
2196
+ * ~2 px は近すぎ、 user 目視判定「4px が最適」 で 16 world 復帰)。
2197
+ */
2198
+ declare const LABEL_TO_PATH_CLEARANCE = 16;
2199
+ /**
2200
+ * edge label center と path center の初期距離 (world unit)。
2201
+ *
2202
+ * routePath() が返す init labelY は path 中央から本値だけ離れた位置に配置する。
2203
+ * pill 端 と path 実距離 = 本値 - LABEL_PILL_H_MAIN_ONLY / 2 = LABEL_TO_PATH_CLEARANCE。
2204
+ *
2205
+ * 計算式 = LABEL_TO_PATH_CLEARANCE (16) + LABEL_PILL_H_MAIN_ONLY (36) / 2 = 34 world。
2206
+ * CAR-468 で 5 箇所 (旧 30 / 30 / 32 / 50 / 30 magic number) を本 SSOT に集約、 CAR-482 で
2207
+ * LABEL_TO_PATH_CLEARANCE 4 → 8 拡大に伴い derived value 22 → 26、 CAR-483 で
2208
+ * 8 → 16 拡大に伴い derived value 26 → 34、 CAR-570 で 16 → 8 縮小に伴い derived
2209
+ * value 34 → 26 に戻す、 CAR-630 で 8 → 16 に戻し derived value 26 → 34 に復帰
2210
+ * (user 目視判定 「4px が最適」)。
2211
+ * spec.ts の 2 定数を組み合わせた derived value のため、 downstream から本定数を import する。
2212
+ */
2213
+ declare const LABEL_INIT_CLEARANCE: number;
2214
+ /**
2215
+ * dotted-flow style edge の marker glow 最外周 radius (world unit)。
2216
+ *
2217
+ * `render/edges.tsx` の EdgeGlowCircles で描画する 3 重 glow 円 (r=28/18/11 world) のうち最外周値。
2218
+ * 動く marker が path 両側に広がる範囲 = 本値、 dotted-flow の label 距離拡張基準として使う。
2219
+ * render 側 hard-code 値と一致必須、 render 側 radius 変更時は本値も同期。
2220
+ */
2221
+ declare const MARKER_GLOW_RADIUS = 28;
2222
+ /**
2223
+ * dotted-flow style edge の label center と path center の初期距離 (world unit)。
2224
+ *
2225
+ * CAR-518 で追加。 dotted-flow edge (Fan-out / Fan-in / Rollback / flow preset 等) は
2226
+ * 動く marker glow (r=28 world) が path 両側に広がるため、 通常 LABEL_INIT_CLEARANCE (34) では
2227
+ * pill 端 と marker 外周の実距離 = 16 - 28 = -12 world (marker が pill 内部に食い込む)。
2228
+ *
2229
+ * 計算式 = LABEL_INIT_CLEARANCE (34) + MARKER_GLOW_RADIUS (28) = 62 world。
2230
+ * pill 端 と marker 外周実距離 = 62 - 18 - 28 = LABEL_TO_PATH_CLEARANCE (16) と等しくなり、
2231
+ * solid edge と同じ視覚的 clearance を marker 通過帯を挟んで確保する。 CAR-570 で
2232
+ * LABEL_TO_PATH_CLEARANCE 16 → 8 縮小に伴い derived value 62 → 54、 CAR-630 で
2233
+ * 8 → 16 に戻し derived value 54 → 62 に復帰。
2234
+ * routePath 内は `labelInitClearance(edge)` helper 経由でのみ本値を参照する (SSOT 単一経路)。
2235
+ */
2236
+ declare const LABEL_INIT_CLEARANCE_DOTTED_FLOW: number;
2237
+ /**
2238
+ * edge style に応じた label init clearance (world unit) を返す。
2239
+ *
2240
+ * routePath 内 labelY 計算 8 箇所の SSOT helper。 solid = 34 (LABEL_INIT_CLEARANCE)、
2241
+ * dotted-flow = 62 (LABEL_INIT_CLEARANCE_DOTTED_FLOW)。 edge 未指定 (routePath 単体 test 経路) は
2242
+ * solid 扱い。
2243
+ *
2244
+ * 分岐を 8 箇所に散らさず helper 化することで、 (a) 将来の新 style 追加時に 1 箇所修正で済む
2245
+ * (b) test で helper を直叩きして分岐を全 style pattern で cover 可能、 という 2 利点を得る。
2246
+ */
2247
+ declare function labelInitClearance(edge?: Pick<CdlEdge, "style" | "sub"> | null): number;
2248
+ /**
2249
+ * edge 起点から水平方向に直進する最小距離 (world unit)。
2250
+ *
2251
+ * L 字 edge が起点即折れすると (起点直後で垂直方向 に turn すると) 「起点がどの
2252
+ * node 由来か視覚判別困難」 になる。 EDGE_STUB_OUT world 以上直進すると起点が
2253
+ * node bbox 縁から明確に離れる → 起点認識容易。
2254
+ *
2255
+ * 40 world = 通常 node w (200-400) の 10-20%、 detour 発火閾値と分離、 label pill
2256
+ * 標準 h (36 world) より大きく起点 stub が label 位置と干渉しない値。
2257
+ */
2258
+ declare const EDGE_STUB_OUT = 40;
2259
+ /**
2260
+ * 同一 obstacle を迂回する複数 detour path の Y 分離距離 (world unit)。
2261
+ *
2262
+ * 例 = A → C と B → C の 2 edge が同 obstacle X を上方 detour するとき、 detour
2263
+ * top Y が近すぎると 2 path が視覚的に重なる。 DETOUR_SLOT_GAP world 以上離すと
2264
+ * どちらがどの edge か視認可能。
2265
+ *
2266
+ * 30 world = 通常 edge label h (36) より少し小さく、 detour 群が縦に密集しても label
2267
+ * 表示スペースを圧迫しない値。 単一 obstacle の detour 3-4 本まで実用的に分離可能。
2268
+ */
2269
+ declare const DETOUR_SLOT_GAP = 30;
2270
+ /**
2271
+ * 同 row 内 node の cy 差分許容 (world unit)。
2272
+ *
2273
+ * 同 row 概念 = 同 stack index を持つ横並び node 群。 各 node の cy が完全一致
2274
+ * (差 0) が理想だが、 subpixel 丸め誤差で 1 world 未満の差が発生し得る。 この値
2275
+ * 未満は許容、 超過は positive-check axis `row-alignment` で fail 判定。
2276
+ *
2277
+ * 1 world = 大 diagram で subpixel 相当、 実測差 = 0 の場合のみ pass する厳格判定。
2278
+ */
2279
+ declare const MIN_ROW_ALIGNMENT_TOLERANCE = 1;
2280
+ /**
2281
+ * 同 column 内 node の cx 差分許容 (world unit)。
2282
+ *
2283
+ * 同 column 概念 = 同 lane 内で縦積みされた node 群。 cx は lane center に engine が
2284
+ * 揃える設計、 差 0 が理想だが `MIN_ROW_ALIGNMENT_TOLERANCE` と同 tolerance を採用。
2285
+ */
2286
+ declare const MIN_COLUMN_ALIGNMENT_TOLERANCE = 1;
2287
+ /**
2288
+ * kind pair 別 clearance policy (world unit)。
2289
+ *
2290
+ * `collisions.ts` の CLEARANCE_POLICY (near collision 下限) と分離、 本 policy は
2291
+ * 目標値を満たすかを見る軸 (`row-alignment` / `column-alignment` 等) が使う。
2292
+ * `edge-label-clearance` は #386 で畳んだ。
2293
+ *
2294
+ * pair key format = `${kindA}|${kindB}` (アルファベット順)、 `requiredSpecClearance()` で
2295
+ * 双方向 lookup。 未登録 pair は 0 = 判定対象外。
2296
+ */
2297
+ declare const SPEC_CLEARANCE_POLICY: Record<string, number>;
2298
+ /**
2299
+ * kind pair 別 spec clearance を返す。 未登録 pair は 0 (判定対象外)。
2300
+ */
2301
+ declare function requiredSpecClearance(kindA: string, kindB: string): number;
2302
+ /**
2303
+ * chain 伝搬 shift resolver の最大 iter 数。
2304
+ *
2305
+ * resolveOverlapsWithChain() で shift → 再検出 → 再 shift を loop するが、
2306
+ * 発散防止のため N iter で強制収束。 通常 diagram は 3-5 iter で収束、 20 iter は
2307
+ * 極端に密な layout でも十分な余裕。
2308
+ */
2309
+ declare const CHAIN_SHIFT_MAX_ITER = 20;
2310
+ /**
2311
+ * chain 伝搬 shift の最小 clearance (world unit)。
2312
+ *
2313
+ * overlap 検出時に shift する距離 = overlap 実測 + `CHAIN_MIN_CLEARANCE`。
2314
+ * shift 後の gap が最低この値になる保証。 16 world = 通常 node gap の 20% 相当、
2315
+ * shift 後に「くっつきすぎ」 を視覚防止する余裕距離。
2316
+ */
2317
+ declare const CHAIN_MIN_CLEARANCE = 16;
2318
+ /**
2319
+ * chain 伝搬 shift の 1 要素あたり累積 shift 上限 (world unit)。
2320
+ *
2321
+ * CAR-508 SSOT。 chain 伝搬 loop 内で label / edge-path が各軸方向にこの値以上
2322
+ * 移動することを禁止し、 発散を止める。 Fan-out / Fan-in topology で label が互いに
2323
+ * shift 連鎖して viewBox 外に飛ぶ regression の防止 guard。
2324
+ *
2325
+ * 100 world = label pill 高 (36) + label-label clearance (36) + 予備 (28) を目安、
2326
+ * 通常の shift ケース (fail label ↔ retry path で 30-50 world 程度) には影響しない。
2327
+ */
2328
+ declare const CHAIN_SHIFT_MAX_ACCUMULATED = 100;
2329
+ /**
2330
+ * arrow 終点と終点 node の辺中央との距離許容 (world unit)。
2331
+ *
2332
+ * 終点 node の toSide (top/right/bottom/left) の辺中央 と、 arrow 実際の
2333
+ * 終点座標との距離が本値以下なら「中央着地」 と判定。 超過は 4 隅当て症状。
2334
+ *
2335
+ * 8 world = 通常 node 短辺 (h=100 前後) の 8% 相当、 実描画上「辺の真ん中」
2336
+ * と視覚判別できる余裕距離。 axis 8 (arrow-endpoint-anchoring) が「側 or 内側」
2337
+ * を判定するのに対し、 本値は「側の中央 or 端」 を判定する棲み分け。
2338
+ */
2339
+ declare const ARROW_ENDPOINT_CENTER_TOL = 8;
2340
+ /**
2341
+ * 同 row 内 node 間の gap variance 許容 (world unit)。
2342
+ *
2343
+ * 同 stack index を持つ node 群を cx 昇順で並べ、 隣接 node cx 差 = gap を求める。
2344
+ * gap の max - min が本値以下なら「均一」 判定。 超過は「片寄っている」 症状で
2345
+ * axis `row-gap-uniform` で fail 判定。
2346
+ *
2347
+ * 40 world = 通常 node 間 gap (100-300) の 15-40% 相当、 layout engine が
2348
+ * 均等配置に努めた結果として許容される subpixel 誤差 + 意図的 offset 余裕。
2349
+ */
2350
+ declare const ROW_GAP_VARIANCE_TOL = 40;
2351
+ /**
2352
+ * 同 column 内 node 間の端間 gap variance 許容 (world unit)。
2353
+ *
2354
+ * 同 lane 内 node 群を cy 昇順で並べ、 隣接 node の端間 gap
2355
+ * (下の node の上端 - 上の node の下端) を求める。
2356
+ * gap の max - min が本値以下なら「均一」 判定。 縦積み layout の gap uniform 判定。
2357
+ * 40 world = row 側と同 tolerance で対称、 vertical stacking の意図的 offset 余裕を含む。
2358
+ *
2359
+ * 中心間距離 (cy 差) ではなく端間 gap を測る (Issue #202)。 node 高さは kind ごとに
2360
+ * 異なるため、 中心間距離は端間 gap が完全に均一でも node 高さ差の分だけばらつく。
2361
+ * engine が制御しているのは端間 gap であり、 検査もそれに合わせる。
2362
+ */
2363
+ declare const COLUMN_GAP_VARIANCE_TOL = 40;
2364
+ /**
2365
+ * edge-label bbox と lane border 貫通の判定 gap (world unit)。
2366
+ *
2367
+ * edge-label pill が lane 縁 (lane.x / lane.x+width) を貫通すると、 label の一部が
2368
+ * 「別 lane に侵入」 したように見える。 label bbox 端が lane border から本値以下の
2369
+ * 距離にあれば「貫通疑い」 として警告。
2370
+ *
2371
+ * 2 world = lane border の描画線幅 (通常 1-2 px) と bbox pill 端の許容誤差。
2372
+ * 0 = 完全接触は許容、 内側に食い込むと fail。
2373
+ */
2374
+ declare const LANE_BORDER_CLEARANCE_TOL = 2;
2375
+ /**
2376
+ * near-collision 検出用 kind pair 別 clearance policy (world unit)。
2377
+ *
2378
+ * `SPEC_CLEARANCE_POLICY` (positive spec、 目標値) と分離、 本 policy は
2379
+ * `detectNearCollisions()` (`collisions.ts`) が「これ以下は誤読」 の下限判定で使う。
2380
+ *
2381
+ * SSOT 集約 (CAR-424) — 旧実装は `collisions.ts` 内 module-local const `CLEARANCE_POLICY`
2382
+ * として定義されていたが、 CAR-421 の `spec.ts` 追加後に「positive spec は spec.ts、
2383
+ * near-collision は collisions.ts」 の分散所有になっていた。 本 file に集約する事で
2384
+ * layout SSOT が 1 module に集中する (`clearance-constants.ts` の primitive 定数と
2385
+ * 2 SSOT policy dict は組み合わせて downstream が使う)。
2386
+ *
2387
+ * default 未登録 pair は `NEAR_COLLISION_DEFAULT_MIN` (16 world unit)。
2388
+ */
2389
+ declare const NEAR_COLLISION_POLICY: Record<string, number>;
2390
+ /**
2391
+ * `NEAR_COLLISION_POLICY` に登録なし pair の default 下限 (world unit)。
2392
+ */
2393
+ declare const NEAR_COLLISION_DEFAULT_MIN = 16;
2394
+ /**
2395
+ * near-collision 用 kind pair 別 clearance を返す。 未登録 pair は
2396
+ * `NEAR_COLLISION_DEFAULT_MIN` を返す (旧 collisions.ts `requiredClearance` 挙動保持)。
2397
+ */
2398
+ declare function requiredNearClearance(kindA: string, kindB: string): number;
2399
+
2400
+ /**
2401
+ * rows[] から最低限必要な node width を算出。
2402
+ * 左 padding + 左列 max 幅 + col gap + 右列 max 幅 + 右 padding。
2403
+ *
2404
+ * rows 空 / undefined なら 0 を返す (caller が default w と max() で安全 fallback)。
2405
+ *
2406
+ * `kind` で書体と字の大きさが変わる。 行を等幅の 26 で描くのは `storage` だけで、 列の間に
2407
+ * 縦線を引くため間隔も広い (32)。 それ以外は `generic` の書式 (左が比例の 18 / 右が等幅の 21)。
2408
+ *
2409
+ * **`kind` を渡さない呼出には広い方を返す**。 どちらの書式で描かれるか決められないので、
2410
+ * 狭い方を返すと行が箱からはみ出す。 広い方なら余白が増えるだけで済む。 呼び出し側が種別を
2411
+ * 知っているなら渡した方が箱は締まる (`cardene777/dragon#1147`)。
2412
+ *
2413
+ * 上に丸めるのは、 送り幅が小数のため幅が小数になり、 図の座標が読みにくくなるから。 下に
2414
+ * 丸めると 1 未満だけ足りない箱ができる。
2415
+ */
2416
+ declare function requiredRowsWidth(rows: string[] | undefined, kind?: string): number;
2417
+
2418
+ /**
2419
+ * chain 伝搬 shift で node-node overlap を解消する (CAR-421 導入)。
2420
+ *
2421
+ * `resolveOverlaps` (後勝ち一発 shift) との違い ...
2422
+ * 1. 検出 → shift → 再検出 → 影響先 shift の loop、 CHAIN_SHIFT_MAX_ITER で強制収束
2423
+ * 2. shift 方向 = 後 declare node を「overlap 軸の逆方向」 に押し出す
2424
+ * 3. shift 後に 他 node と新規 overlap した場合、 その他 node も 同方向同量 shift 連鎖
2425
+ * 4. 収束条件 = 1 iter で shift が 1 件も発生しない、 or MAX_ITER 到達
2426
+ *
2427
+ * 保守性 = `resolveOverlaps` と同じく node-node のみ対象。 edge-path / edge-label
2428
+ * との衝突は下流の resolveEdgeLabelOverlapsWithChain / detour routing に任せる。
2429
+ *
2430
+ * shift 量 = overlap 距離 (軸方向) + minClearance。 shift 後の gap は
2431
+ * 少なくとも minClearance 保証、 shift 累積で「押し切った」 状態を作る。
2432
+ *
2433
+ * 適用範囲 = `layout()` の pipeline で `resolveOverlaps` の後に opt-in で呼ぶ経路を
2434
+ * 想定。 sequence preset (lifeline stacking) を壊さないため、 現状 default OFF、
2435
+ * 呼び出し側で明示的に有効化する。
2436
+ *
2437
+ * @param nodes 元 LaidNode 列 (順序保存で返す)
2438
+ * @param minClearance shift 後の最小 gap (world unit、 default 16)
2439
+ * @param maxIter 収束上限 iteration 数 (default 20)
2440
+ * @returns shift 済 LaidNode 列 (input の順序を保持)
2441
+ */
2442
+ declare function resolveOverlapsWithChain(nodes: LaidNode[], minClearance?: number, maxIter?: number): LaidNode[];
2443
+ /**
2444
+ * chain 伝搬 shift で edge-label overlap を解消する (CAR-426 導入、 CAR-482 で node / lane-label
2445
+ * 伝搬拡張)。
2446
+ *
2447
+ * `resolveOverlapsWithChain` の edge-label 拡張版。 node-node overlap は
2448
+ * `resolveOverlapsWithChain` が担当、 本 function は edge-label ↔ edge-label /
2449
+ * edge-label ↔ node / edge-label ↔ lane-label の overlap を chain 伝搬解消する。
2450
+ *
2451
+ * 設計指針。
2452
+ * 1. edge-label が obstacles (node / lane-label) と overlap する場合、
2453
+ * label を path 側 normal 方向に押し出す (path から遠ざける方向)。
2454
+ * 2. edge-label 同士が overlap する場合、 後 declare の label を normal 方向 shift。
2455
+ * 3. shift 後に新規 overlap が生じた場合、 同方向で連鎖 shift (chain propagation)。
2456
+ * 4. CAR-482 拡張 ... label が shift 方向に node / lane-label と衝突する場合、 その node /
2457
+ * lane-label も同方向同量 shift 連鎖 (旧 fixedObstacles → 可変 obstacles)。
2458
+ * 5. 収束条件 = 1 iter で shift が 1 件も発生しない、 or MAX_ITER 到達。
2459
+ *
2460
+ * label 位置権限フロー (CAR-430 SSOT)。
2461
+ * 1. `edges.ts routePath()` = 初期 label 位置を SSOT で決定
2462
+ * 2. 本 function = overlap 検出時のみ chain 伝搬 shift で連鎖解消
2463
+ *
2464
+ * pipeline 配線 (`layout.ts` post-pass、 escape hatch = env `CDL_DISABLE_CHAIN_EDGE_LABEL=1`
2465
+ * で OFF にすると edges.ts init 位置がそのまま最終 label 位置)。 CAR-429 で edges.ts の
2466
+ * fan-in / fan-out offset 分散削除、 CAR-430 で label-shift.ts の shift 探索削除に伴い、
2467
+ * label overlap 解消の単一 SSOT になった。
2468
+ *
2469
+ * @param edges 元 LaidEdge 列 (順序保存で返す)
2470
+ * @param fixedObstacles 障害物 (node / lane-label の BBox 列)。 CAR-482 以前は不動、
2471
+ * 現在は shift 対象 (CAR-482 chain 伝搬拡張)。 後方互換のため名前は fixedObstacles のまま。
2472
+ * @param minClearance shift 後の最小 gap (world unit、 default 16)
2473
+ * @param maxIter 収束上限 iter 数 (default 20、 spec.CHAIN_SHIFT_MAX_ITER と同値)
2474
+ * @returns shift 済 LaidEdge 列 (input 順序を保持、 label 位置のみ変更、 path d は不変)
2475
+ */
2476
+ declare function resolveEdgeLabelOverlapsWithChain(edges: LaidEdge[], fixedObstacles: BBox[], minClearance?: number, maxIter?: number): LaidEdge[];
2477
+ /**
2478
+ * chain 伝搬 shift の拡張版 (CAR-482 導入、 CAR-485 で edge-path 拡張)。 label だけでなく node /
2479
+ * lane-label / edge-path (別 edge) も同方向同量 shift 連鎖。
2480
+ *
2481
+ * `resolveEdgeLabelOverlapsWithChain` は edge-label のみ shift、 obstacles (node / lane-label) は不動。
2482
+ * 本 function は「label が shift 方向にある obstacle / edge-path に衝突するなら、 その要素も同方向同量 shift 連鎖」
2483
+ * を実装、 「押した先にある要素も押し出す」 user 期待に応える。
2484
+ *
2485
+ * shift 対象。
2486
+ * 1. edge-label ... `resolveEdgeLabelOverlapsWithChain` と同経路
2487
+ * 2. node ... label shift 方向にある node は同方向同量 shift、 chain 伝搬で再検出 → 再 shift
2488
+ * 3. lane-label ... 同上
2489
+ * 4. edge-path (別 edge、 CAR-485) ... label 近接 (LABEL_TO_PATH_CLEARANCE 未達) or 直接 overlap で
2490
+ * label を path 逆方向に shift + shift 方向にある edge-path も同方向連鎖 shift (path.d 平行移動)
2491
+ *
2492
+ * 保守性 = 収束条件は maxIter で強制、 発散防止。 shift 累積は各要素ごと Map で管理。
2493
+ * 既存 preset で label overlap 発生しないなら shift 0 で edges / nodes / laneLabels / edgePathShifts 素通し。
2494
+ *
2495
+ * 自 edge の label × path は legit として shift 対象外 (routePath init 位置 = LABEL_INIT_CLEARANCE 34 world (CAR-630 で 26 → 34 復帰)
2496
+ * で自 path から離した設計、 自 pair の近接判定はここでは無視する)。
2497
+ *
2498
+ * @param edges 元 LaidEdge 列 (順序保存で返す)
2499
+ * @param obstacles 障害物 (node / lane-label の BBox 列)。 shift 対象。
2500
+ * @param minClearance shift 後の最小 gap (default 16)
2501
+ * @param maxIter 収束上限 iter 数 (default 20)
2502
+ * @returns { edges, nodeShifts, laneLabelShifts, edgePathShifts } shift 済 edges + kind 別 shift map。
2503
+ * edgePathShifts は edge id → (dx, dy) の shift 量。 caller は `translatePathD(edge.d, dx, dy)` で
2504
+ * path.d を平行移動する。
2505
+ */
2506
+ declare function resolveEdgeLabelOverlapsWithChainAndPropagate(edges: LaidEdge[], obstacles: BBox[], minClearance?: number, maxIter?: number,
2507
+ /**
2508
+ * CAR-485 = label × edge-path (別 edge) near-collision 判定に使う clearance (world unit)。
2509
+ * `minClearance` は label × node / lane-label / label 用 (大きめ 32-36)、 本値は path 用
2510
+ * (SSOT `LABEL_TO_PATH_CLEARANCE` = 16)、 pair 別 clearance を分離する事で fan-in / fan-out
2511
+ * などで label が sibling path 近傍 (LABEL_INIT_CLEARANCE 26) にある正常状態を誤検知しない。
2512
+ */
2513
+ pathLabelClearance?: number): {
2514
+ edges: LaidEdge[];
2515
+ nodeShifts: Map<string, {
2516
+ dx: number;
2517
+ dy: number;
2518
+ }>;
2519
+ laneLabelShifts: Map<string, {
2520
+ dx: number;
2521
+ dy: number;
2522
+ }>;
2523
+ edgePathShifts: Map<string, {
2524
+ dx: number;
2525
+ dy: number;
2526
+ }>;
2527
+ };
2528
+
2529
+ /**
2530
+ * Engine が予測する label / node bbox の world 単位 public API。
2531
+ *
2532
+ * CAR-430 で label-shift.ts の shift 探索は削除、 label 位置 SSOT は edges.ts routePath() +
2533
+ * collisions.ts resolveEdgeLabelOverlapsWithChain に移管。 CAR-431 で pill h/w/padding SSOT は
2534
+ * spec.ts に集約 (render / layout / predict-bbox 3 箇所の drift 解消)、 本 file は spec.ts の
2535
+ * `computeLabelBBoxWorld` を re-export する public API 層として engine 予測値と DOM 実測値を
2536
+ * diff で assert する用途に特化した stable interface を提供する。
2537
+ */
2538
+
2539
+ /**
2540
+ * Edge label の world 単位 bbox 予測 (spec.ts SSOT の re-export)。
2541
+ *
2542
+ * SSOT は `spec.ts` の `computeLabelBBoxWorld` に集約 (CAR-431)、 本 function は名前互換のため
2543
+ * delegate wrapper。 render (render/edges.tsx) / layout (layout/collisions.ts) / predict-bbox (本 file)
2544
+ * の 3 箇所で個別実装されていた bbox 計算の drift (boxH 64 vs 68、 labelY 位置の centered vs shifted)
2545
+ * を解消。
2546
+ *
2547
+ * @param edge LaidEdge (labelX, labelY, labelAnchor, label, sub 既確定)
2548
+ * @returns { x, y, w, h } の world 座標 bbox (render 実 rect 位置と一致)
2549
+ */
2550
+ declare function predictLabelBBoxWorld(edge: LaidEdge): {
2551
+ x: number;
2552
+ y: number;
2553
+ w: number;
2554
+ h: number;
2555
+ };
2556
+ /**
2557
+ * Node の world 単位 bbox 予測。
2558
+ */
2559
+ declare function predictNodeBBoxWorld(n: LaidNode): {
2560
+ x: number;
2561
+ y: number;
2562
+ w: number;
2563
+ h: number;
2564
+ };
2565
+ /**
2566
+ * SVG font rendering の実測誤差 tolerance (world unit)。
2567
+ *
2568
+ * 用途別 2 種類。
2569
+ * - `FONT_RENDER_TOLERANCE_WORLD` = 一般 bbox 予測との tolerance (label position / node size)、
2570
+ * 80 world (実 DOM 約 22 px @ scale 3.7)
2571
+ * - `LABEL_SIZE_TOLERANCE_WORLD` = **label bbox size 専用**、 150 world (約 40 px @ scale 3.7)
2572
+ *
2573
+ * label bbox tolerance を大きく取る理由 = getBoundingClientRect() は SVG `<g>` の外接矩形を
2574
+ * 返し、 内部 `<text>` の glyph render (kerning / overhang / letter-spacing) が `<rect>` の
2575
+ * pad から数-十 world 単位はみ出す。 measureTextWidth (advance width 集計) と DOM 実測
2576
+ * (外接矩形) は本質的に別測定なので、 label size は 150 world で妥当な近似。
2577
+ *
2578
+ * engine measureTextWidth と SVG 実 render の乖離 breakdown:
2579
+ * - Inter font の hinting 差 ≈ ±0.5 px / char
2580
+ * - subpixel rendering の rounding ≈ ±1 px / bbox
2581
+ * - Playwright DOM 実測 と engine world 座標系の scale 差 ≈ ±10-15 world
2582
+ * - text glyph の rect padding 外 overhang ≈ ±20-40 world (label のみ)
2583
+ */
2584
+ declare const FONT_RENDER_TOLERANCE_WORLD = 80;
2585
+ declare const LABEL_SIZE_TOLERANCE_WORLD = 150;
2586
+ /**
2587
+ * bbox diff 計算 (world 単位)。 各辺の差の Math.abs の Math.max を返す。
2588
+ * tolerance 内なら「engine 予測と実測が一致」 と判定。
2589
+ */
2590
+ declare function bboxDiffWorld(predicted: {
2591
+ x: number;
2592
+ y: number;
2593
+ w: number;
2594
+ h: number;
2595
+ }, actual: {
2596
+ x: number;
2597
+ y: number;
2598
+ w: number;
2599
+ h: number;
2600
+ }): {
2601
+ dx: number;
2602
+ dy: number;
2603
+ dw: number;
2604
+ dh: number;
2605
+ max: number;
2606
+ };
2607
+
2608
+ /**
2609
+ * 高位 API ... 著者が「lane / node / edge を 1 件ずつ宣言」 する低位 API を簡略化。
2610
+ *
2611
+ * - swimlane({ id, topic, lanes: ["送信元", "コントラクト", "出力"] }) で 3 lane 自動配置
2612
+ * - flow().step("A", "func").step("B", "storage").build() で sequence diagram 風
2613
+ */
2614
+ type SwimlanePreset = {
2615
+ id: string;
2616
+ topic: string;
2617
+ /** lane label の配列。 各 lane width は固定 (default 400)、 x は auto-layout */
2618
+ lanes: string[];
2619
+ /** lane width (default 400) */
2620
+ laneWidth?: number;
2621
+ /** 各 lane が枠囲み contain か (default false、 true で全 lane contain) */
2622
+ contain?: boolean;
2623
+ };
2624
+ /**
2625
+ * 横並び 複数 lane を 1 行で生成する preset。
2626
+ * 著者は lane label のみ指定、 x / width は engine が自動配置。
2627
+ *
2628
+ * @example
2629
+ * swimlane({ id: "tx", topic: "Tx Flow", lanes: ["送信元", "Contract", "出力"] })
2630
+ * .node("client", { lane: "send-mototo", stack: 0, kind: "actor", title: "Client" }) // lane id は label を slug 化
2631
+ * ...
2632
+ */
2633
+ /** swimlane の戻り値 ... DiagramBuilder + 各 lane の slug id を取得する getter */
2634
+ type SwimlaneResult = DiagramBuilder & {
2635
+ /** lane label から自動生成された slug id を返す (label 順 0-indexed) */
2636
+ laneId: (indexOrLabel: number | string) => string;
2637
+ };
2638
+ declare function swimlane(preset: SwimlanePreset): SwimlaneResult;
2639
+ type FlowStepInput = {
2640
+ id: string;
2641
+ kind: NodeKind;
2642
+ title: string;
2643
+ eyebrow?: string;
2644
+ subtitle?: string;
2645
+ };
2646
+ type FlowPreset = {
2647
+ id: string;
2648
+ topic: string;
2649
+ /** lane label (1 本のみ、 全 step が縦 stack で並ぶ) */
2650
+ laneLabel?: string;
2651
+ laneWidth?: number;
2652
+ /** edge tone / style の default (各 step → next step の edge) */
2653
+ defaultTone?: Tone;
2654
+ defaultStyle?: EdgeStyle;
2655
+ };
2656
+ type FlowBuilder = {
2657
+ /** step を順番に追加、 自動で前 step から edge 接続 */
2658
+ step: (node: FlowStepInput, edgeLabel?: string) => FlowBuilder;
2659
+ build: () => ReturnType<DiagramBuilder["build"]>;
2660
+ };
2661
+ /**
2662
+ * flow preset ... 1 lane に縦 stack で node を並べ、 前 step → 次 step の edge を auto 接続。
2663
+ * sequence diagram 風 (mermaid `sequenceDiagram` の cdl 版)。
2664
+ *
2665
+ * @example
2666
+ * flow({ id: "auth", topic: "Auth Flow" })
2667
+ * .step({ id: "user", kind: "person", title: "User" })
2668
+ * .step({ id: "api", kind: "api", title: "POST /login" }, "ログイン要求")
2669
+ * .step({ id: "db", kind: "database", title: "users 表" }, "credential 検証")
2670
+ * .build();
2671
+ */
2672
+ declare function flow(preset: FlowPreset): FlowBuilder;
2673
+ type SequenceStep = {
2674
+ /** from actor id (or label slug) */
2675
+ from: string;
2676
+ /** to actor id (or label slug) */
2677
+ to: string;
2678
+ /** message label */
2679
+ label: string;
2680
+ /** 補足 (2 行目、 例 ... POST /login の body 仕様等) */
2681
+ sub?: string;
2682
+ tone?: Tone;
2683
+ style?: EdgeStyle;
2684
+ };
2685
+ type SequencePreset = {
2686
+ id: string;
2687
+ topic: string;
2688
+ /** actor label の配列 (lifeline、 column 単位) */
2689
+ actors: string[];
2690
+ /** actor の lane width (default 220) */
2691
+ laneWidth?: number;
2692
+ /** lane gap (default 60) */
2693
+ laneGap?: number;
2694
+ /** default edge tone (default accent) */
2695
+ defaultTone?: Tone;
2696
+ /** default edge style (default solid) */
2697
+ defaultStyle?: EdgeStyle;
2698
+ };
2699
+ type SequenceBuilder = {
2700
+ /** message step を時系列順に追加、 from → to の水平 edge を引く */
2701
+ step: (s: SequenceStep) => SequenceBuilder;
2702
+ build: () => ReturnType<DiagramBuilder["build"]>;
2703
+ };
2704
+ /**
2705
+ * sequence preset ... UML sequence diagram 風。 actors を column 化 (lifeline)、
2706
+ * step({ from, to, label }) で時系列順に message edge を生成。
2707
+ *
2708
+ * @example
2709
+ * sequence({ id: "auth-seq", topic: "Auth Sequence", actors: ["User", "API", "DB"] })
2710
+ * .step({ from: "User", to: "API", label: "POST /login" })
2711
+ * .step({ from: "API", to: "DB", label: "SELECT credentials" })
2712
+ * .step({ from: "DB", to: "API", label: "rows", tone: "success", style: "dotted-flow" })
2713
+ * .step({ from: "API", to: "User", label: "200 OK", tone: "success" })
2714
+ * .build();
2715
+ */
2716
+ declare function sequence(preset: SequencePreset): SequenceBuilder;
2717
+ type TopologyContainer = {
2718
+ id: string;
2719
+ kind: NodeKind;
2720
+ title: string;
2721
+ eyebrow?: string;
2722
+ };
2723
+ type TopologyConnection = {
2724
+ from: string;
2725
+ to: string;
2726
+ label: string;
2727
+ sub?: string;
2728
+ tone?: Tone;
2729
+ style?: EdgeStyle;
2730
+ labelOffsetX?: number;
2731
+ labelOffsetY?: number;
2732
+ };
2733
+ type TopologyPreset = {
2734
+ id: string;
2735
+ topic: string;
2736
+ /** group の lane width (default 360) */
2737
+ groupWidth?: number;
2738
+ defaultTone?: Tone;
2739
+ defaultStyle?: EdgeStyle;
2740
+ };
2741
+ type TopologyGroupBuilder = {
2742
+ add: (container: TopologyContainer) => TopologyGroupBuilder;
2743
+ };
2744
+ type TopologyBuilder = {
2745
+ group: (id: string, opts: {
2746
+ label: string;
2747
+ }) => TopologyGroupBuilder;
2748
+ connect: (from: string, to: string, opts: {
2749
+ label: string;
2750
+ sub?: string;
2751
+ tone?: Tone;
2752
+ style?: EdgeStyle;
2753
+ labelOffsetX?: number;
2754
+ labelOffsetY?: number;
2755
+ }) => TopologyBuilder;
2756
+ build: () => ReturnType<DiagramBuilder["build"]>;
2757
+ };
2758
+ /**
2759
+ * topology preset ... 構成図 / deployment diagram 風。
2760
+ * group で container を囲む、 add で内部 element を配置、 connect で element 間 connection。
2761
+ *
2762
+ * @example
2763
+ * topology({ id: "aws", topic: "AWS Deployment" })
2764
+ * .group("aws", { label: "AWS" })
2765
+ * .add({ id: "alb", kind: "service", title: "ALB" })
2766
+ * .add({ id: "ecs", kind: "service", title: "ECS Task" })
2767
+ * .add({ id: "rds", kind: "database", title: "RDS" })
2768
+ * .group("client", { label: "Client" })
2769
+ * .add({ id: "browser", kind: "frontend", title: "Browser" })
2770
+ * .connect("browser", "alb", { label: "HTTPS" })
2771
+ * .connect("alb", "ecs", { label: "round-robin" })
2772
+ * .connect("ecs", "rds", { label: "TCP 5432" })
2773
+ * .build();
2774
+ */
2775
+ declare function topology(preset: TopologyPreset): TopologyBuilder;
2776
+ type ErEntity = {
2777
+ id: string;
2778
+ /** entity 名 (例 "User") */
2779
+ title: string;
2780
+ /** 列定義 (例 "id: PK", "email: string", "userId: FK") */
2781
+ rows: string[];
2782
+ };
2783
+ type ErRelationCardinality = "1:1" | "1:N" | "N:1" | "N:M" | "0..1" | "1..*";
2784
+ type ErRelation = {
2785
+ from: string;
2786
+ to: string;
2787
+ cardinality: ErRelationCardinality;
2788
+ /** 関係名 (例 "places", "belongs_to") */
2789
+ label?: string;
2790
+ tone?: Tone;
2791
+ };
2792
+ type ErPreset = {
2793
+ id: string;
2794
+ topic: string;
2795
+ defaultTone?: Tone;
2796
+ /**
2797
+ * 実体 1 個の箱の幅 (world unit、 既定 = `storage` の既定幅)。
2798
+ *
2799
+ * 帯ではなく **箱** の幅。 帯は `箱の幅 + 余白 × 2` で宣言する (engine が帯を同じ式まで
2800
+ * 広げるため、 狭く宣言すると宣言座標と実座標がずれる)。
2801
+ */
2802
+ entityWidth?: number;
2803
+ };
2804
+ type ErBuilder = {
2805
+ entity: (e: ErEntity) => ErBuilder;
2806
+ relation: (r: ErRelation) => ErBuilder;
2807
+ build: () => ReturnType<DiagramBuilder["build"]>;
2808
+ };
2809
+ /**
2810
+ * er preset ... ER 図 (Entity Relationship)。 entity を table 風 node (rows 構造) で配置、
2811
+ * relation を edge で接続、 cardinality は edge label に記法 (1:1 / 1:N / N:M / 0..1 / 1..*) で表記。
2812
+ * mermaid ER 図の cdl 版。
2813
+ *
2814
+ * @example
2815
+ * er({ id: "user-order", topic: "User-Order ER" })
2816
+ * .entity({ id: "user", title: "User", rows: ["id: PK", "email: string", "createdAt: timestamp"] })
2817
+ * .entity({ id: "order", title: "Order", rows: ["id: PK", "userId: FK", "total: number"] })
2818
+ * .relation({ from: "user", to: "order", cardinality: "1:N", label: "places" })
2819
+ * .build();
2820
+ */
2821
+ declare function er(preset: ErPreset): ErBuilder;
2822
+ type FsmState = {
2823
+ id: string;
2824
+ title: string;
2825
+ /** initial state (1 つだけ) */
2826
+ initial?: boolean;
2827
+ /** final state (複数可) */
2828
+ final?: boolean;
2829
+ };
2830
+ type FsmTransition = {
2831
+ from: string;
2832
+ to: string;
2833
+ /** 遷移 trigger (例 "submit" / "success" / "fail") */
2834
+ trigger: string;
2835
+ /** guard 条件 (例 "if validated") */
2836
+ guard?: string;
2837
+ tone?: Tone;
2838
+ };
2839
+ type StateMachinePreset = {
2840
+ id: string;
2841
+ topic: string;
2842
+ defaultTone?: Tone;
2843
+ /**
2844
+ * 状態 1 個の箱の幅 (world unit、 既定 320 = card の既定幅)。
2845
+ *
2846
+ * 帯の幅ではなく **箱の幅** を指す。 帯は engine が「箱の幅 + 余白 × 2」 まで広げるので、
2847
+ * 帯だけを狭く指定しても図は縮まない (#357 以前はここが帯にしか渡っておらず、
2848
+ * 370 未満の指定が全て無視されていた)。
2849
+ *
2850
+ * 80 未満と有限でない値は例外を投げる。 箱の幅として使う以上、 描画が壊れる値を
2851
+ * 黙って受け取らない。
2852
+ */
2853
+ stateWidth?: number;
2854
+ };
2855
+ type StateMachineBuilder = {
2856
+ state: (s: FsmState) => StateMachineBuilder;
2857
+ transition: (t: FsmTransition) => StateMachineBuilder;
2858
+ build: () => ReturnType<DiagramBuilder["build"]>;
2859
+ };
2860
+ /**
2861
+ * stateMachine preset ... FSM (Finite State Machine) / workflow。
2862
+ * state を card kind で配置、 transition を edge で接続、 trigger は edge label、 guard は sub。
2863
+ * initial / final state は eyebrow + tone で識別表現。
2864
+ *
2865
+ * @example
2866
+ * stateMachine({ id: "auth-fsm", topic: "Auth FSM" })
2867
+ * .state({ id: "idle", title: "Idle", initial: true })
2868
+ * .state({ id: "loading", title: "Loading" })
2869
+ * .state({ id: "done", title: "Done", final: true })
2870
+ * .state({ id: "error", title: "Error" })
2871
+ * .transition({ from: "idle", to: "loading", trigger: "submit" })
2872
+ * .transition({ from: "loading", to: "done", trigger: "success", tone: "success" })
2873
+ * .transition({ from: "loading", to: "error", trigger: "fail", tone: "error" })
2874
+ * .transition({ from: "error", to: "idle", trigger: "retry", guard: "if attempts < 3" })
2875
+ * .build();
2876
+ */
2877
+ declare function stateMachine(preset: StateMachinePreset): StateMachineBuilder;
2878
+ type InfraNode = {
2879
+ id: string;
2880
+ kind: NodeKind;
2881
+ title: string;
2882
+ /** 表示位置 (col, row)、 0-indexed。 grid 上の配置を author が制御 */
2883
+ col: number;
2884
+ row: number;
2885
+ subtitle?: string;
2886
+ eyebrow?: string;
2887
+ };
2888
+ type InfraConnection = {
2889
+ from: string;
2890
+ to: string;
2891
+ label: string;
2892
+ sub?: string;
2893
+ tone?: Tone;
2894
+ style?: EdgeStyle;
2895
+ /** label の水平方向 fine offset (default 0)、 preset 交互 ±60 を override */
2896
+ labelOffsetX?: number;
2897
+ /** label の垂直方向 fine offset (default 0、 preset 交互 ±60 を override) */
2898
+ labelOffsetY?: number;
2899
+ };
2900
+ type InfrastructurePreset = {
2901
+ id: string;
2902
+ topic: string;
2903
+ /** col あたりの lane width (default 380) */
2904
+ laneWidth?: number;
2905
+ defaultTone?: Tone;
2906
+ defaultStyle?: EdgeStyle;
2907
+ };
2908
+ type InfrastructureBuilder = {
2909
+ node: (n: InfraNode) => InfrastructureBuilder;
2910
+ connect: (c: InfraConnection) => InfrastructureBuilder;
2911
+ build: () => ReturnType<DiagramBuilder["build"]>;
2912
+ };
2913
+ /**
2914
+ * infrastructure preset ... AWS / GCP / Azure 等の cloud / system 構成図。
2915
+ * node を col + row の grid で配置、 connect で接続線 + edge label。
2916
+ * topology preset (group + container 包含) と違って flat grid 配置 + 各 node が
2917
+ * 独立 lane を持つので、 BFF / Lambda / CDN 等を散在配置するシステム概要図に適する。
2918
+ *
2919
+ * @example
2920
+ * infrastructure({ id: "saas-infra", topic: "SaaS Architecture" })
2921
+ * .node({ id: "user", kind: "person", title: "User", col: 0, row: 0 })
2922
+ * .node({ id: "cdn", kind: "cdn", title: "CloudFront", col: 1, row: 0 })
2923
+ * .node({ id: "alb", kind: "service", title: "ALB", col: 2, row: 0 })
2924
+ * .node({ id: "app", kind: "service", title: "App", col: 2, row: 1 })
2925
+ * .node({ id: "db", kind: "database", title: "RDS", col: 3, row: 1 })
2926
+ * .connect({ from: "user", to: "cdn", label: "HTTPS" })
2927
+ * .connect({ from: "cdn", to: "alb", label: "origin" })
2928
+ * .connect({ from: "alb", to: "app", label: "route" })
2929
+ * .connect({ from: "app", to: "db", label: "TCP 5432" })
2930
+ * .build();
2931
+ */
2932
+ declare function infrastructure(preset: InfrastructurePreset): InfrastructureBuilder;
2933
+ type ClassRelationType = "extends" | "implements" | "uses" | "aggregates" | "composes";
2934
+ type UmlClass = {
2935
+ id: string;
2936
+ title: string;
2937
+ /** 属性 (例 "+name: string" / "-id: number") */
2938
+ attributes?: string[];
2939
+ /** メソッド (例 "+login(): void") */
2940
+ methods?: string[];
2941
+ /** stereotype (例 "abstract" / "interface" / "trait") */
2942
+ stereotype?: string;
2943
+ };
2944
+ type UmlRelation = {
2945
+ from: string;
2946
+ to: string;
2947
+ type: ClassRelationType;
2948
+ /** 関係 label (default は type 名) */
2949
+ label?: string;
2950
+ /** 多重度 (例 "1..*" / "0..1") */
2951
+ cardinality?: string;
2952
+ tone?: Tone;
2953
+ };
2954
+ type ClassDiagramPreset = {
2955
+ id: string;
2956
+ topic: string;
2957
+ defaultTone?: Tone;
2958
+ /**
2959
+ * クラス 1 個の箱の幅 (world unit、 既定 = `storage` の既定幅)。
2960
+ *
2961
+ * 帯ではなく **箱** の幅。 帯は `箱の幅 + 余白 × 2` で宣言する。
2962
+ */
2963
+ classWidth?: number;
2964
+ };
2965
+ type ClassDiagramBuilder = {
2966
+ class: (c: UmlClass) => ClassDiagramBuilder;
2967
+ relation: (r: UmlRelation) => ClassDiagramBuilder;
2968
+ build: () => ReturnType<DiagramBuilder["build"]>;
2969
+ };
2970
+ /**
2971
+ * classDiagram preset ... UML クラス図。 attributes + methods を rows で表現、
2972
+ * relation を edge で接続。 extends / implements / uses / aggregates / composes 5 種対応。
2973
+ *
2974
+ * @example
2975
+ * classDiagram({ id: "user-class", topic: "User domain model" })
2976
+ * .class({ id: "User", title: "User", attributes: ["+name: string", "+email: string"], methods: ["+login(): void"] })
2977
+ * .class({ id: "Admin", title: "Admin", attributes: ["+permissions: string[]"], methods: ["+banUser(): void"] })
2978
+ * .relation({ from: "Admin", to: "User", type: "extends" })
2979
+ * .build();
2980
+ */
2981
+ declare function classDiagram(preset: ClassDiagramPreset): ClassDiagramBuilder;
2982
+ type TreeNode = {
2983
+ id: string;
2984
+ title: string;
2985
+ parent?: string;
2986
+ /** 表示 kind (default "card") */
2987
+ kind?: NodeKind;
2988
+ subtitle?: string;
2989
+ eyebrow?: string;
2990
+ };
2991
+ type TreePreset = {
2992
+ id: string;
2993
+ topic: string;
2994
+ defaultTone?: Tone;
2995
+ /** depth ごとの lane width (default 320) */
2996
+ nodeWidth?: number;
2997
+ };
2998
+ type TreeBuilder = {
2999
+ node: (n: TreeNode) => TreeBuilder;
3000
+ build: () => ReturnType<DiagramBuilder["build"]>;
3001
+ };
3002
+ /**
3003
+ * tree preset ... parent-child の階層構造を縦列の lane stack で表現。
3004
+ * 各 node は parent を指定、 親が同 lane stack の上に配置される lane (depth 別) を auto 計算。
3005
+ * 組織図 / file tree / class 階層 / 製品カテゴリ等の階層図に使う。
3006
+ *
3007
+ * @example
3008
+ * tree({ id: "org", topic: "組織図" })
3009
+ * .node({ id: "ceo", title: "CEO" })
3010
+ * .node({ id: "cto", title: "CTO", parent: "ceo" })
3011
+ * .node({ id: "cfo", title: "CFO", parent: "ceo" })
3012
+ * .node({ id: "eng-mgr", title: "Eng Manager", parent: "cto" })
3013
+ * .build();
3014
+ */
3015
+ declare function tree(preset: TreePreset): TreeBuilder;
3016
+ type JourneyEmotion = "delighted" | "happy" | "neutral" | "frustrated" | "angry";
3017
+ type JourneyStep = {
3018
+ id: string;
3019
+ title: string;
3020
+ emotion: JourneyEmotion;
3021
+ /** どの touchpoint (例 "Website", "Email", "Support") */
3022
+ touchpoint?: string;
3023
+ /** opportunity (改善余地のメモ) */
3024
+ opportunity?: string;
3025
+ };
3026
+ type UserJourneyPreset = {
3027
+ id: string;
3028
+ topic: string;
3029
+ defaultTone?: Tone;
3030
+ stepWidth?: number;
3031
+ };
3032
+ type UserJourneyBuilder = {
3033
+ step: (s: JourneyStep) => UserJourneyBuilder;
3034
+ build: () => ReturnType<DiagramBuilder["build"]>;
3035
+ };
3036
+ /**
3037
+ * userJourney preset ... User research / UX research の journey map。
3038
+ * step を時系列に横並びで並べ、 emotion を tone で色分け、 touchpoint / opportunity を subtitle で示す。
3039
+ *
3040
+ * @example
3041
+ * userJourney({ id: "signup", topic: "Signup journey" })
3042
+ * .step({ id: "land", title: "Land on /", emotion: "neutral", touchpoint: "Website" })
3043
+ * .step({ id: "form", title: "Fill form", emotion: "frustrated", touchpoint: "Form" })
3044
+ * .step({ id: "verify", title: "Email verify", emotion: "happy", touchpoint: "Email" })
3045
+ * .step({ id: "done", title: "Done", emotion: "delighted", touchpoint: "Dashboard" })
3046
+ * .build();
3047
+ */
3048
+ declare function userJourney(preset: UserJourneyPreset): UserJourneyBuilder;
3049
+ type MindBranch = {
3050
+ id: string;
3051
+ title: string;
3052
+ /** 親 branch (root の子は parent=root id) */
3053
+ parent: string;
3054
+ /** 色 tone (default は parent の tone を継承) */
3055
+ tone?: Tone;
3056
+ subtitle?: string;
3057
+ };
3058
+ type MindMapPreset = {
3059
+ id: string;
3060
+ topic: string;
3061
+ /** 中心 topic ID + label */
3062
+ rootId: string;
3063
+ rootTitle: string;
3064
+ branchWidth?: number;
3065
+ defaultTone?: Tone;
3066
+ };
3067
+ type MindMapBuilder = {
3068
+ branch: (b: MindBranch) => MindMapBuilder;
3069
+ build: () => ReturnType<DiagramBuilder["build"]>;
3070
+ };
3071
+ /**
3072
+ * mindMap preset ... 中心 root topic から放射状に branch を伸ばす mind map。
3073
+ * tree との違いは「root は 1 つ固定 + 第 1 階層の branch ごとに tone を auto rotate」。
3074
+ *
3075
+ * @example
3076
+ * mindMap({ id: "ideas", topic: "Project ideas", rootId: "root", rootTitle: "Project" })
3077
+ * .branch({ id: "feat", title: "Features", parent: "root" })
3078
+ * .branch({ id: "ui", title: "UI design", parent: "root" })
3079
+ * .branch({ id: "auth", title: "Auth", parent: "feat" })
3080
+ * .build();
3081
+ */
3082
+ declare function mindMap(preset: MindMapPreset): MindMapBuilder;
3083
+ type FunnelStage = {
3084
+ id: string;
3085
+ title: string;
3086
+ /** この stage の人数 / 件数 */
3087
+ count: number;
3088
+ /** stage 詳細 */
3089
+ subtitle?: string;
3090
+ };
3091
+ type FunnelPreset = {
3092
+ id: string;
3093
+ topic: string;
3094
+ defaultTone?: Tone;
3095
+ stageWidth?: number;
3096
+ };
3097
+ type FunnelBuilder = {
3098
+ stage: (s: FunnelStage) => FunnelBuilder;
3099
+ build: () => ReturnType<DiagramBuilder["build"]>;
3100
+ };
3101
+ /**
3102
+ * funnel preset ... Sales / marketing funnel。 Awareness → Conversion の階層を縦に並べ、
3103
+ * 各 stage の count と drop rate (前 stage 比) を可視化。
3104
+ *
3105
+ * @example
3106
+ * funnel({ id: "sales", topic: "Sales funnel" })
3107
+ * .stage({ id: "visit", title: "Visit", count: 10000 })
3108
+ * .stage({ id: "signup", title: "Sign up", count: 1500 })
3109
+ * .stage({ id: "trial", title: "Trial", count: 800 })
3110
+ * .stage({ id: "paid", title: "Paid", count: 200 })
3111
+ * .build();
3112
+ */
3113
+ declare function funnel(preset: FunnelPreset): FunnelBuilder;
3114
+ type QuadrantQuadrantLabel = "topLeft" | "topRight" | "bottomLeft" | "bottomRight";
3115
+ type QuadrantItem = {
3116
+ id: string;
3117
+ title: string;
3118
+ /** どの象限 (4 象限) */
3119
+ quadrant: QuadrantQuadrantLabel;
3120
+ subtitle?: string;
3121
+ };
3122
+ type QuadrantPreset = {
3123
+ id: string;
3124
+ topic: string;
3125
+ /** x 軸 label (left / right) */
3126
+ xAxis: {
3127
+ left: string;
3128
+ right: string;
3129
+ };
3130
+ /** y 軸 label (bottom / top) */
3131
+ yAxis: {
3132
+ bottom: string;
3133
+ top: string;
3134
+ };
3135
+ /** 各象限の label */
3136
+ quadrantLabels?: Record<QuadrantQuadrantLabel, string>;
3137
+ defaultTone?: Tone;
3138
+ };
3139
+ type QuadrantBuilder = {
3140
+ item: (i: QuadrantItem) => QuadrantBuilder;
3141
+ build: () => ReturnType<DiagramBuilder["build"]>;
3142
+ };
3143
+ /**
3144
+ * quadrant preset ... 2 軸 マトリクス (Priority matrix / SWOT / Eisenhower 等)。
3145
+ * 4 象限 (topLeft / topRight / bottomLeft / bottomRight) に item を配置。
3146
+ * lane = 2 col (left / right)、 stack = 2 row (top / bottom)。
3147
+ *
3148
+ * @example
3149
+ * quadrant({ id: "priority", topic: "Priority matrix",
3150
+ * xAxis: { left: "Low effort", right: "High effort" },
3151
+ * yAxis: { bottom: "Low value", top: "High value" } })
3152
+ * .item({ id: "a", title: "Quick win", quadrant: "topLeft" })
3153
+ * .item({ id: "b", title: "Major project", quadrant: "topRight" })
3154
+ * .item({ id: "c", title: "Fill in", quadrant: "bottomLeft" })
3155
+ * .item({ id: "d", title: "Thankless", quadrant: "bottomRight" })
3156
+ * .build();
3157
+ */
3158
+ declare function quadrant(preset: QuadrantPreset): QuadrantBuilder;
3159
+ type ChartType = "pie" | "bar" | "line";
3160
+ type ChartDatum = {
3161
+ id: string;
3162
+ label: string;
3163
+ value: number;
3164
+ /** 個別 tone (default は preset の defaultTone) */
3165
+ tone?: Tone;
3166
+ };
3167
+ type ChartPreset = {
3168
+ id: string;
3169
+ topic: string;
3170
+ type: ChartType;
3171
+ defaultTone?: Tone;
3172
+ itemWidth?: number;
3173
+ };
3174
+ type ChartBuilder = {
3175
+ datum: (d: ChartDatum) => ChartBuilder;
3176
+ build: () => ReturnType<DiagramBuilder["build"]>;
3177
+ };
3178
+ /**
3179
+ * chart preset ... 統計チャート (Pie / Bar / Line)、 CAR-994 Phase A で SVG geometry render 導入。
3180
+ *
3181
+ * datum 配列を 1 大 node (kind=chart-line / chart-pie / chart-bar) の chartData payload に
3182
+ * 格納し、 kinds/chart-*.tsx が自己完結で折れ線 / 円 / 棒グラフを SVG 描画する。
3183
+ * layout は 1 lane + 1 node (chart canvas サイズ) で完結、 lane / edge を datum 数だけ増やさない。
3184
+ *
3185
+ * @example
3186
+ * chart({ id: "share", topic: "Market share", type: "pie" })
3187
+ * .datum({ id: "a", label: "A", value: 45 })
3188
+ * .datum({ id: "b", label: "B", value: 30 })
3189
+ * .datum({ id: "c", label: "C", value: 25 })
3190
+ * .build();
3191
+ */
3192
+ declare function chart(preset: ChartPreset): ChartBuilder;
3193
+ type GanttTask = {
3194
+ id: string;
3195
+ title: string;
3196
+ /** 開始 (例 "2026-Q1" / "Day 1") */
3197
+ start: string;
3198
+ /** 終了 (例 "2026-Q2") */
3199
+ end: string;
3200
+ /** owner / assignee */
3201
+ owner?: string;
3202
+ /** 依存 task id */
3203
+ dependsOn?: string;
3204
+ };
3205
+ type GanttPreset = {
3206
+ id: string;
3207
+ topic: string;
3208
+ defaultTone?: Tone;
3209
+ laneWidth?: number;
3210
+ };
3211
+ type GanttBuilder = {
3212
+ task: (t: GanttTask) => GanttBuilder;
3213
+ build: () => ReturnType<DiagramBuilder["build"]>;
3214
+ };
3215
+ /**
3216
+ * gantt preset ... Gantt chart / sprint planning / release timeline。
3217
+ * task ごとに 1 lane を作り、 start - end + owner を subtitle、 dependsOn で前 task → 次 task の edge を auto 生成。
3218
+ *
3219
+ * @example
3220
+ * gantt({ id: "release", topic: "Release timeline" })
3221
+ * .task({ id: "design", title: "Design", start: "Q1", end: "Q1" })
3222
+ * .task({ id: "build", title: "Build", start: "Q2", end: "Q2", dependsOn: "design" })
3223
+ * .task({ id: "test", title: "Test", start: "Q3", end: "Q3", dependsOn: "build" })
3224
+ * .build();
3225
+ */
3226
+ declare function gantt(preset: GanttPreset): GanttBuilder;
3227
+ type FlowchartNodeShape = "process" | "decision" | "start" | "end" | "loop";
3228
+ type FlowchartNode = {
3229
+ id: string;
3230
+ title: string;
3231
+ shape: FlowchartNodeShape;
3232
+ /** swimlane (role) */
3233
+ lane: string;
3234
+ };
3235
+ type FlowchartEdge = {
3236
+ from: string;
3237
+ to: string;
3238
+ /** decision shape の場合 "true" / "false" / null */
3239
+ label?: string;
3240
+ tone?: Tone;
3241
+ };
3242
+ type FlowchartPreset = {
3243
+ id: string;
3244
+ topic: string;
3245
+ /** swimlane の lane (role) ラベル */
3246
+ lanes: string[];
3247
+ defaultTone?: Tone;
3248
+ laneWidth?: number;
3249
+ };
3250
+ type FlowchartBuilder = {
3251
+ node: (n: FlowchartNode) => FlowchartBuilder;
3252
+ edge: (e: FlowchartEdge) => FlowchartBuilder;
3253
+ build: () => ReturnType<DiagramBuilder["build"]>;
3254
+ };
3255
+ /**
3256
+ * flowchart preset ... swimlane + decision diamond + loop 枠を扱うビジネス process diagram。
3257
+ * mermaid flowchart より「lane 別の役割分担」 と「decision / loop」 を明示できる。
3258
+ *
3259
+ * @example
3260
+ * flowchart({ id: "approve", topic: "Approval flow", lanes: ["User", "Manager"] })
3261
+ * .node({ id: "submit", title: "Submit", shape: "start", lane: "User" })
3262
+ * .node({ id: "review", title: "Review", shape: "decision", lane: "Manager" })
3263
+ * .node({ id: "approve", title: "Approve", shape: "end", lane: "Manager" })
3264
+ * .node({ id: "revise", title: "Revise", shape: "process", lane: "User" })
3265
+ * .edge({ from: "submit", to: "review" })
3266
+ * .edge({ from: "review", to: "approve", label: "true" })
3267
+ * .edge({ from: "review", to: "revise", label: "false" })
3268
+ * .build();
3269
+ */
3270
+ declare function flowchart(preset: FlowchartPreset): FlowchartBuilder;
3271
+ type NetworkDeviceKind = "router" | "switch" | "firewall" | "server" | "client";
3272
+ type NetworkDevice = {
3273
+ id: string;
3274
+ title: string;
3275
+ kind: NetworkDeviceKind;
3276
+ segment?: string;
3277
+ /** 表示 col + row */
3278
+ col: number;
3279
+ row: number;
3280
+ };
3281
+ type NetworkLink = {
3282
+ from: string;
3283
+ to: string;
3284
+ /** プロトコル / VLAN 等 */
3285
+ protocol?: string;
3286
+ tone?: Tone;
3287
+ };
3288
+ type NetworkPreset = {
3289
+ id: string;
3290
+ topic: string;
3291
+ defaultTone?: Tone;
3292
+ laneWidth?: number;
3293
+ };
3294
+ type NetworkBuilder = {
3295
+ device: (d: NetworkDevice) => NetworkBuilder;
3296
+ link: (l: NetworkLink) => NetworkBuilder;
3297
+ build: () => ReturnType<DiagramBuilder["build"]>;
3298
+ };
3299
+ /**
3300
+ * network preset ... NW topology (router / switch / firewall / server / client) を col × row 配置、
3301
+ * link で接続 (protocol / VLAN を label)。 On-prem / hybrid NW 構成図向け。
3302
+ *
3303
+ * @example
3304
+ * network({ id: "office-nw", topic: "Office NW" })
3305
+ * .device({ id: "fw", title: "Firewall", kind: "firewall", col: 0, row: 0 })
3306
+ * .device({ id: "sw1", title: "Switch A", kind: "switch", col: 1, row: 0 })
3307
+ * .device({ id: "srv", title: "Server", kind: "server", col: 2, row: 0 })
3308
+ * .link({ from: "fw", to: "sw1", protocol: "VLAN 10" })
3309
+ * .link({ from: "sw1", to: "srv", protocol: "TCP 22" })
3310
+ * .build();
3311
+ */
3312
+ declare function network(preset: NetworkPreset): NetworkBuilder;
3313
+ type FsmStateNested = {
3314
+ id: string;
3315
+ title: string;
3316
+ initial?: boolean;
3317
+ final?: boolean;
3318
+ /** 親 state (nested state、 例 "loading" の親 "active") */
3319
+ parent?: string;
3320
+ /** entry action (例 "onEnter: startTimer") */
3321
+ entry?: string;
3322
+ /** exit action */
3323
+ exit?: string;
3324
+ };
3325
+ type FsmTransitionExt = {
3326
+ from: string;
3327
+ to: string;
3328
+ trigger: string;
3329
+ guard?: string;
3330
+ /** action (例 "onTransition: incrementCounter") */
3331
+ action?: string;
3332
+ tone?: Tone;
3333
+ };
3334
+ type StateMachine2Preset = {
3335
+ id: string;
3336
+ topic: string;
3337
+ defaultTone?: Tone;
3338
+ /**
3339
+ * 状態 1 個の箱の幅 (world unit、 既定 320 = card の既定幅)。
3340
+ *
3341
+ * 帯の幅ではなく **箱の幅** を指す。 帯は engine が「箱の幅 + 余白 × 2」 まで広げるので、
3342
+ * 帯だけを狭く指定しても図は縮まない (#357 以前はここが帯にしか渡っておらず、
3343
+ * 370 未満の指定が全て無視されていた)。
3344
+ *
3345
+ * 80 未満と有限でない値は例外を投げる。 箱の幅として使う以上、 描画が壊れる値を
3346
+ * 黙って受け取らない。
3347
+ */
3348
+ stateWidth?: number;
3349
+ };
3350
+ type StateMachine2Builder = {
3351
+ state: (s: FsmStateNested) => StateMachine2Builder;
3352
+ transition: (t: FsmTransitionExt) => StateMachine2Builder;
3353
+ build: () => ReturnType<DiagramBuilder["build"]>;
3354
+ };
3355
+ /**
3356
+ * stateMachine2 preset ... 拡張 FSM (UML statechart)。
3357
+ * stateMachine と違って (a) nested state (parent 指定) (b) entry / exit action (c) transition action サポート。
3358
+ * subtitle / sub で action / guard を表示。
3359
+ *
3360
+ * @example
3361
+ * stateMachine2({ id: "auth", topic: "Auth FSM" })
3362
+ * .state({ id: "idle", title: "Idle", initial: true, entry: "clearForm" })
3363
+ * .state({ id: "active", title: "Active" })
3364
+ * .state({ id: "loading", title: "Loading", parent: "active" })
3365
+ * .state({ id: "done", title: "Done", final: true })
3366
+ * .transition({ from: "idle", to: "loading", trigger: "submit", action: "startSpinner" })
3367
+ * .transition({ from: "loading", to: "done", trigger: "success" })
3368
+ * .build();
3369
+ */
3370
+ declare function stateMachine2(preset: StateMachine2Preset): StateMachine2Builder;
3371
+ type MindMapRadialBranch = {
3372
+ id: string;
3373
+ title: string;
3374
+ /** 色 tone (default は defaultTone)。 8 方向で auto rotate も可 */
3375
+ tone?: Tone;
3376
+ subtitle?: string;
3377
+ };
3378
+ type MindMapRadialPreset = {
3379
+ id: string;
3380
+ topic: string;
3381
+ /** 中心 node の title (id は builder が "center" で内部生成) */
3382
+ centerTitle: string;
3383
+ /** 中心 node の id override (default "center") */
3384
+ centerId?: string;
3385
+ /** 中心から branch までの距離 (world coord、 default 350) */
3386
+ radius?: number;
3387
+ /** branch node の width (default 260) */
3388
+ branchWidth?: number;
3389
+ /** 中心 node の width (default 260) */
3390
+ centerWidth?: number;
3391
+ defaultTone?: Tone;
3392
+ };
3393
+ type MindMapRadialBuilder = {
3394
+ branch: (b: MindMapRadialBranch) => MindMapRadialBuilder;
3395
+ build: () => ReturnType<DiagramBuilder["build"]>;
3396
+ };
3397
+ /**
3398
+ * mindMapRadial preset ... 中心 node を軸に 8 方向 (0°/45°/90°/135°/180°/225°/270°/315°)
3399
+ * へ branch を放射状に配置する。 現行 mindMap (左から右へ水平 tree) と使い分ける用途。
3400
+ *
3401
+ * 実装戦略 ... engine の lane × stack grid を活用し、 3 lane (left / center / right) を
3402
+ * radius に基づき explicit x で配置、 3 stack row (top / middle / bottom) を stack index 0/1/2 で
3403
+ * 割当てる。 branch 順は position 順 (E/SE/S/SW/W/NW/N/NE) に固定し、 index → grid cell mapping。
3404
+ *
3405
+ * @example
3406
+ * mindMapRadial({ id: "mr", topic: "Product Radial", centerTitle: "Product" })
3407
+ * .branch({ id: "users", title: "Users" }) // 0° (right)
3408
+ * .branch({ id: "road", title: "Roadmap" }) // 45° (bottom-right)
3409
+ * .branch({ id: "metrics", title: "Metrics" }) // 90° (bottom)
3410
+ * ...
3411
+ * .build();
3412
+ */
3413
+ declare function mindMapRadial(preset: MindMapRadialPreset): MindMapRadialBuilder;
3414
+
3415
+ export { ARROW_ANGLE_MAX_DEG, ARROW_ENDPOINT_CENTER_TOL, type AnimationChain, type AuthorIntentOptions, type AuthorIntentReport, type BinaryOp, CHAIN_MIN_CLEARANCE, CHAIN_SHIFT_MAX_ACCUMULATED, CHAIN_SHIFT_MAX_ITER, CLEARANCE_LABEL_LABEL, CLEARANCE_LANE_LABEL, CLEARANCE_NODE_LABEL, CLEARANCE_PATH_LABEL, COLUMN_GAP_VARIANCE_TOL, CdlDiagram, CdlDiagramThumbnail, CdlEdge, type CdlEventHandler, CdlEventTarget, CdlInput, CdlLane, CdlNode, CdlScrollTrigger, CdlState, type ChartBuilder, type ChartDatum, type ChartPreset, type ChartType, type ClassDiagramBuilder, type ClassDiagramPreset, type ClassRelationType, Computed, DETOUR_GAP, DETOUR_SLOT_GAP, DIST_LABEL_PATH_MAX, DIST_LABEL_PATH_MIN, type DiagramAdapter, type DiagramBuilder, type Discrepancy, type DiscrepancyKind, type DomVerifyReport, EDGE_LABEL_TEXT, EDGE_STUB_OUT, type EdgeLabelLine, type EdgeLabelTextSpec, EdgeStyle, type ErBuilder, type ErEntity, type ErPreset, type ErRelation, type ErRelationCardinality, type EventAttachHandle, type EventChain, FAN_GAP, FONT_RENDER_TOLERANCE_WORLD, type FlowBuilder, type FlowPreset, type FlowStepInput, type FlowchartBuilder, type FlowchartEdge, type FlowchartNode, type FlowchartNodeShape, type FlowchartPreset, type FormulaAst, type FormulaContext, type FsmState, type FsmStateNested, type FsmTransition, type FsmTransitionExt, type FunnelBuilder, type FunnelPreset, type FunnelStage, type GanttBuilder, type GanttPreset, type GanttTask, type GenericDiagramSpec, type GenericDiscrepancy, type GenericDiscrepancyKind, type GenericVerifyOptions, type Rect as GeometryRect, type Segment as GeometrySegment, type InfraConnection, type InfraNode, type InfrastructureBuilder, type InfrastructurePreset, type InputChain, type IntentDiscrepancy, type IntentDiscrepancyKind, type JourneyEmotion, type JourneyStep, LABEL_INIT_CLEARANCE, LABEL_INIT_CLEARANCE_DOTTED_FLOW, LABEL_SIZE_TOLERANCE_WORLD, LABEL_TO_PATH_CLEARANCE, LANE_BORDER_CLEARANCE_TOL, LaidDiagram, LaidEdge, LaidNode, type LayoutWithValidationOptions, type LayoutWithValidationResult, MARKER_GLOW_RADIUS, MIN_COLUMN_ALIGNMENT_TOLERANCE, MIN_ROW_ALIGNMENT_TOLERANCE, type MathFnName, type MindBranch, type MindMapBuilder, type MindMapPreset, type MindMapRadialBranch, type MindMapRadialBuilder, type MindMapRadialPreset, NEAR_COLLISION_DEFAULT_MIN, NEAR_COLLISION_POLICY, NODE_FRAME_SELECTOR, type NetworkBuilder, type NetworkDevice, type NetworkDeviceKind, type NetworkLink, type NetworkPreset, NodeKind, type PageLike, type PhaseBuilder, type QuadrantBuilder, type QuadrantItem, type QuadrantPreset, type QuadrantQuadrantLabel, ROW_GAP_VARIANCE_TOL, SPEC_CLEARANCE_POLICY, STAGE_SVG_SELECTOR, type SequenceBuilder, type SequencePreset, type SequenceStep, Side, Signal, type StateMachine2Builder, type StateMachine2Preset, type StateMachineBuilder, type StateMachinePreset, type SweepReport, type SwimlanePreset, TARGET_LABEL_PATH_DIST, Tone, type TopologyBuilder, type TopologyConnection, type TopologyContainer, type TopologyGroupBuilder, type TopologyPreset, type TreeBuilder, type TreeNode, type TreePreset, type UmlClass, type UmlRelation, type UserJourneyBuilder, type UserJourneyPreset, type ValidateOptions, type ValidationProfile, type VerifyOptions, type Violation, type VisualAxis, type VisualValidationReport, WCAG_AA_LARGE, WCAG_AA_NORMAL, assertNever, attachEventHandlers, batch, bboxClearance, bboxDiffWorld, cdlAdapter, chart, classDiagram, compile, computeScrollProgress, computeViewportScale, countEdgeCrossings, createFormulaComputeds, createInputSignals, diagram, effect, er, evaluate as evaluateFormula, extractIdentifiers, flow, flowchart, funnel, gantt, infrastructure, inputDefaultValue, isLargeText, labelInitClearance, layout, layoutWithValidation, mindMap, mindMapRadial, network, parseFormula, pointRectEdgeDistance, pointToSegmentDistance, predictLabelBBoxWorld, predictNodeBBoxWorld, projectEdgeLabel, projectNode, projectPathSegment, pxToWorld, quadrant, rectRectClearance, rectRectOverlapArea, rendersRows, requiredContrastRatio, requiredNearClearance, requiredRowsHeight, requiredRowsWidth, requiredSpecClearance, resolveEdgeLabelOverlapsWithChain, resolveEdgeLabelOverlapsWithChainAndPropagate, resolveOverlapsWithChain, resolveTarget, resolveTargetAll, segmentsIntersect, sequence, stateMachine, stateMachine2, swimlane, topology, tree, untrack, userJourney, validate, verifyAllDiagramsDom, verifyAuthorIntent, verifyAuthorIntentAll, verifyDiagramDom, visualValidate, visualValidateAll, visualValidateLaid, worldToPx };