@diagrammo/dgmo 0.47.0 → 0.48.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 (319) hide show
  1. package/dist/auto.js +1 -1
  2. package/dist/auto.mjs +1 -1
  3. package/dist/element.js +1 -1
  4. package/dist/element.mjs +1 -1
  5. package/package.json +19 -70
  6. package/dist/advanced.cjs +0 -71648
  7. package/dist/advanced.d.cts +0 -5719
  8. package/dist/auto.cjs +0 -70648
  9. package/dist/auto.d.cts +0 -54
  10. package/dist/block.cjs +0 -70208
  11. package/dist/block.d.cts +0 -115
  12. package/dist/editor.cjs +0 -526
  13. package/dist/editor.d.cts +0 -26
  14. package/dist/element.cjs +0 -70530
  15. package/dist/element.d.cts +0 -62
  16. package/dist/highlight.cjs +0 -852
  17. package/dist/highlight.d.cts +0 -49
  18. package/dist/index.cjs +0 -70491
  19. package/dist/index.d.cts +0 -160
  20. package/dist/pert.cjs +0 -325
  21. package/dist/pert.d.cts +0 -559
  22. package/dist/themes-1-CuKpeH.d.cts +0 -908
  23. package/src/advanced.ts +0 -873
  24. package/src/arc/parser.ts +0 -15
  25. package/src/arc/renderer.ts +0 -633
  26. package/src/auto/index.ts +0 -621
  27. package/src/auto/shared.ts +0 -370
  28. package/src/auto/styles.ts +0 -73
  29. package/src/block/layout.ts +0 -198
  30. package/src/block/parser.ts +0 -427
  31. package/src/block/renderer.ts +0 -484
  32. package/src/block/types.ts +0 -67
  33. package/src/boxes-and-lines/collapse.ts +0 -96
  34. package/src/boxes-and-lines/focus.ts +0 -257
  35. package/src/boxes-and-lines/label-placement.ts +0 -339
  36. package/src/boxes-and-lines/layout-grouped.ts +0 -912
  37. package/src/boxes-and-lines/layout-layered.ts +0 -722
  38. package/src/boxes-and-lines/layout-search.ts +0 -1678
  39. package/src/boxes-and-lines/layout.ts +0 -398
  40. package/src/boxes-and-lines/parser.ts +0 -1181
  41. package/src/boxes-and-lines/renderer.ts +0 -1465
  42. package/src/boxes-and-lines/types.ts +0 -66
  43. package/src/c4/layout.ts +0 -2441
  44. package/src/c4/parser.ts +0 -1158
  45. package/src/c4/renderer.ts +0 -2008
  46. package/src/c4/types.ts +0 -89
  47. package/src/chart-type-registry.ts +0 -529
  48. package/src/chart-types.ts +0 -225
  49. package/src/chart.ts +0 -685
  50. package/src/charts-d3/bar.ts +0 -338
  51. package/src/charts-d3/chord.ts +0 -140
  52. package/src/charts-d3/function.ts +0 -147
  53. package/src/charts-d3/funnel.ts +0 -104
  54. package/src/charts-d3/heatmap.ts +0 -145
  55. package/src/charts-d3/index.ts +0 -337
  56. package/src/charts-d3/interactions.ts +0 -477
  57. package/src/charts-d3/line.ts +0 -344
  58. package/src/charts-d3/mount.ts +0 -151
  59. package/src/charts-d3/pie.ts +0 -117
  60. package/src/charts-d3/polar.ts +0 -89
  61. package/src/charts-d3/radar.ts +0 -0
  62. package/src/charts-d3/sankey.ts +0 -0
  63. package/src/charts-d3/scatter.ts +0 -185
  64. package/src/charts-d3/shared.ts +0 -208
  65. package/src/class/layout.ts +0 -326
  66. package/src/class/parser.ts +0 -635
  67. package/src/class/renderer.ts +0 -793
  68. package/src/class/types.ts +0 -58
  69. package/src/cli-banner.ts +0 -106
  70. package/src/cli.ts +0 -1080
  71. package/src/colors-diagnostics.ts +0 -35
  72. package/src/colors.ts +0 -379
  73. package/src/completion-types.ts +0 -27
  74. package/src/completion.ts +0 -2668
  75. package/src/cycle/layout.ts +0 -1026
  76. package/src/cycle/parser.ts +0 -387
  77. package/src/cycle/renderer.ts +0 -653
  78. package/src/cycle/types.ts +0 -101
  79. package/src/d3.ts +0 -1434
  80. package/src/data-chart-parser.ts +0 -1007
  81. package/src/dgmo-router.ts +0 -296
  82. package/src/diagnostics-registry.ts +0 -108
  83. package/src/diagnostics.ts +0 -0
  84. package/src/dimensions.ts +0 -20
  85. package/src/directives-registry.ts +0 -396
  86. package/src/editor/dgmo.grammar +0 -76
  87. package/src/editor/dgmo.grammar.d.ts +0 -2
  88. package/src/editor/dgmo.grammar.js +0 -18
  89. package/src/editor/dgmo.grammar.terms.d.ts +0 -5
  90. package/src/editor/dgmo.grammar.terms.js +0 -35
  91. package/src/editor/highlight-api.ts +0 -596
  92. package/src/editor/highlight.ts +0 -36
  93. package/src/editor/index.ts +0 -27
  94. package/src/editor/keywords.ts +0 -128
  95. package/src/editor/tokens.ts +0 -30
  96. package/src/element/index.ts +0 -432
  97. package/src/embed/css.ts +0 -312
  98. package/src/embed/escape.ts +0 -15
  99. package/src/embed/index.ts +0 -280
  100. package/src/er/classify.ts +0 -228
  101. package/src/er/layout.ts +0 -530
  102. package/src/er/parser.ts +0 -750
  103. package/src/er/renderer.ts +0 -725
  104. package/src/er/types.ts +0 -52
  105. package/src/error-card.ts +0 -269
  106. package/src/event-line/diagnostics.ts +0 -117
  107. package/src/event-line/parser.ts +0 -643
  108. package/src/event-line/renderer.ts +0 -1742
  109. package/src/event-line/types.ts +0 -74
  110. package/src/fonts.ts +0 -3
  111. package/src/gantt/calculator.ts +0 -979
  112. package/src/gantt/parser.ts +0 -1641
  113. package/src/gantt/renderer.ts +0 -3787
  114. package/src/gantt/resolver.ts +0 -172
  115. package/src/gantt/types.ts +0 -206
  116. package/src/graph/diagnostics.ts +0 -64
  117. package/src/graph/edge-spline.ts +0 -29
  118. package/src/graph/flowchart-parser.ts +0 -759
  119. package/src/graph/flowchart-renderer.ts +0 -817
  120. package/src/graph/layout.ts +0 -472
  121. package/src/graph/notes.ts +0 -21
  122. package/src/graph/state-collapse.ts +0 -78
  123. package/src/graph/state-parser.ts +0 -574
  124. package/src/graph/state-renderer.ts +0 -653
  125. package/src/graph/types.ts +0 -65
  126. package/src/index.ts +0 -261
  127. package/src/infra/compute.ts +0 -1385
  128. package/src/infra/layout.ts +0 -888
  129. package/src/infra/parser.ts +0 -1234
  130. package/src/infra/renderer.ts +0 -2534
  131. package/src/infra/roles.ts +0 -75
  132. package/src/infra/types.ts +0 -216
  133. package/src/infra/validation.ts +0 -201
  134. package/src/journey-map/layout.ts +0 -490
  135. package/src/journey-map/parser.ts +0 -557
  136. package/src/journey-map/renderer.ts +0 -1580
  137. package/src/journey-map/types.ts +0 -47
  138. package/src/kanban/mutations.ts +0 -175
  139. package/src/kanban/parser.ts +0 -509
  140. package/src/kanban/renderer.ts +0 -1449
  141. package/src/kanban/types.ts +0 -39
  142. package/src/label-layout.ts +0 -368
  143. package/src/map/colorize.ts +0 -54
  144. package/src/map/completion.ts +0 -316
  145. package/src/map/context-labels.ts +0 -609
  146. package/src/map/data/PROVENANCE.json +0 -1
  147. package/src/map/data/README.md +0 -40
  148. package/src/map/data/airport-collisions.json +0 -1
  149. package/src/map/data/airports.json +0 -1
  150. package/src/map/data/gazetteer.json +0 -1
  151. package/src/map/data/lakes.json +0 -1
  152. package/src/map/data/mountain-ranges.json +0 -1
  153. package/src/map/data/na-lakes.json +0 -1
  154. package/src/map/data/na-land.json +0 -1
  155. package/src/map/data/region-names.json +0 -1
  156. package/src/map/data/rivers.json +0 -1
  157. package/src/map/data/types.ts +0 -121
  158. package/src/map/data/us-states.json +0 -1
  159. package/src/map/data/water-bodies.json +0 -1
  160. package/src/map/data/world-coarse.json +0 -1
  161. package/src/map/data/world-detail.json +0 -1
  162. package/src/map/diagnostics.ts +0 -134
  163. package/src/map/dimensions.ts +0 -150
  164. package/src/map/geo-query.ts +0 -295
  165. package/src/map/geo.ts +0 -438
  166. package/src/map/invert.ts +0 -111
  167. package/src/map/layout.ts +0 -5069
  168. package/src/map/legend-band.ts +0 -127
  169. package/src/map/load-data.ts +0 -186
  170. package/src/map/parser.ts +0 -783
  171. package/src/map/renderer.ts +0 -1445
  172. package/src/map/resolved-types.ts +0 -160
  173. package/src/map/resolver.ts +0 -1146
  174. package/src/map/types.ts +0 -194
  175. package/src/mindmap/collapse.ts +0 -67
  176. package/src/mindmap/layout.ts +0 -649
  177. package/src/mindmap/parser.ts +0 -430
  178. package/src/mindmap/renderer.ts +0 -637
  179. package/src/mindmap/text-wrap.ts +0 -218
  180. package/src/mindmap/types.ts +0 -56
  181. package/src/org/collapse.ts +0 -151
  182. package/src/org/layout.ts +0 -1262
  183. package/src/org/parser.ts +0 -519
  184. package/src/org/renderer.ts +0 -866
  185. package/src/org/resolver.ts +0 -636
  186. package/src/palettes/atlas.ts +0 -77
  187. package/src/palettes/blueprint.ts +0 -73
  188. package/src/palettes/catppuccin.ts +0 -84
  189. package/src/palettes/color-utils.ts +0 -443
  190. package/src/palettes/index.ts +0 -67
  191. package/src/palettes/nord.ts +0 -67
  192. package/src/palettes/registry.ts +0 -114
  193. package/src/palettes/slate.ts +0 -73
  194. package/src/palettes/tidewater.ts +0 -73
  195. package/src/palettes/tokyo-night.ts +0 -86
  196. package/src/palettes/types.ts +0 -88
  197. package/src/pert/analyzer.ts +0 -1183
  198. package/src/pert/diagnostics.ts +0 -110
  199. package/src/pert/index.ts +0 -43
  200. package/src/pert/internal.ts +0 -252
  201. package/src/pert/layout.ts +0 -971
  202. package/src/pert/monte-carlo.ts +0 -446
  203. package/src/pert/parser.ts +0 -1606
  204. package/src/pert/renderer.ts +0 -4009
  205. package/src/pert/share-normalize.ts +0 -62
  206. package/src/pert/types.ts +0 -604
  207. package/src/pyramid/parser.ts +0 -185
  208. package/src/pyramid/renderer.ts +0 -768
  209. package/src/pyramid/types.ts +0 -28
  210. package/src/quadrant/parser.ts +0 -18
  211. package/src/quadrant/renderer.ts +0 -638
  212. package/src/raci/diagnostics.ts +0 -213
  213. package/src/raci/index.ts +0 -23
  214. package/src/raci/mutations.ts +0 -321
  215. package/src/raci/parser.ts +0 -699
  216. package/src/raci/renderer.ts +0 -1627
  217. package/src/raci/types.ts +0 -82
  218. package/src/raci/variants.ts +0 -320
  219. package/src/render.ts +0 -268
  220. package/src/ring/parser.ts +0 -212
  221. package/src/ring/renderer.ts +0 -490
  222. package/src/ring/types.ts +0 -27
  223. package/src/sequence/collapse.ts +0 -174
  224. package/src/sequence/parser.ts +0 -1596
  225. package/src/sequence/participant-inference.ts +0 -152
  226. package/src/sequence/renderer.ts +0 -3168
  227. package/src/sequence/tag-resolution.ts +0 -164
  228. package/src/sharing.ts +0 -211
  229. package/src/sitemap/collapse.ts +0 -100
  230. package/src/sitemap/layout.ts +0 -925
  231. package/src/sitemap/parser.ts +0 -755
  232. package/src/sitemap/renderer.ts +0 -828
  233. package/src/sitemap/types.ts +0 -42
  234. package/src/slope/parser.ts +0 -18
  235. package/src/slope/renderer.ts +0 -378
  236. package/src/swimlane/diagnostics.ts +0 -94
  237. package/src/swimlane/layout.ts +0 -597
  238. package/src/swimlane/parser.ts +0 -613
  239. package/src/swimlane/renderer.ts +0 -681
  240. package/src/swimlane/types.ts +0 -151
  241. package/src/tech-radar/interactive.ts +0 -1129
  242. package/src/tech-radar/layout.ts +0 -197
  243. package/src/tech-radar/parser.ts +0 -436
  244. package/src/tech-radar/renderer.ts +0 -1239
  245. package/src/tech-radar/shared.ts +0 -151
  246. package/src/tech-radar/types.ts +0 -86
  247. package/src/themes.ts +0 -22
  248. package/src/timeline/parser.ts +0 -472
  249. package/src/timeline/renderer.ts +0 -3024
  250. package/src/timeline/types.ts +0 -33
  251. package/src/timeline/viz-parser.ts +0 -21
  252. package/src/treemap/diagnostics.ts +0 -62
  253. package/src/treemap/layout-radial.ts +0 -233
  254. package/src/treemap/layout.ts +0 -189
  255. package/src/treemap/parser.ts +0 -463
  256. package/src/treemap/renderer-radial.ts +0 -438
  257. package/src/treemap/renderer.ts +0 -572
  258. package/src/treemap/treemap-shared.ts +0 -309
  259. package/src/treemap/types.ts +0 -60
  260. package/src/utils/arrow-markers.ts +0 -51
  261. package/src/utils/arrows-diagnostics.ts +0 -62
  262. package/src/utils/arrows.ts +0 -227
  263. package/src/utils/brand.ts +0 -61
  264. package/src/utils/card.ts +0 -183
  265. package/src/utils/collapse-engine/tree-edges.ts +0 -115
  266. package/src/utils/collapse-engine/tree.ts +0 -117
  267. package/src/utils/d3-helpers.ts +0 -219
  268. package/src/utils/d3-types.ts +0 -9
  269. package/src/utils/description-helpers.ts +0 -38
  270. package/src/utils/duration.ts +0 -312
  271. package/src/utils/export-container.ts +0 -41
  272. package/src/utils/expr-eval.ts +0 -27
  273. package/src/utils/fit-canvas.ts +0 -64
  274. package/src/utils/hover-styles.ts +0 -673
  275. package/src/utils/inline-markdown.ts +0 -96
  276. package/src/utils/legend-constants.ts +0 -48
  277. package/src/utils/legend-d3.ts +0 -618
  278. package/src/utils/legend-integration.ts +0 -107
  279. package/src/utils/legend-layout.ts +0 -776
  280. package/src/utils/legend-svg.ts +0 -214
  281. package/src/utils/legend-types.ts +0 -272
  282. package/src/utils/name-normalize.ts +0 -163
  283. package/src/utils/note-box/constants.ts +0 -25
  284. package/src/utils/note-box/index.ts +0 -11
  285. package/src/utils/note-box/metrics.ts +0 -90
  286. package/src/utils/note-box/svg.ts +0 -331
  287. package/src/utils/notes/bounds.ts +0 -30
  288. package/src/utils/notes/build.ts +0 -131
  289. package/src/utils/notes/index.ts +0 -18
  290. package/src/utils/notes/model.ts +0 -19
  291. package/src/utils/notes/parse.ts +0 -131
  292. package/src/utils/notes/place.ts +0 -177
  293. package/src/utils/notes/resolve.ts +0 -88
  294. package/src/utils/number-format.ts +0 -36
  295. package/src/utils/parsing.ts +0 -953
  296. package/src/utils/reserved-key-registry.ts +0 -239
  297. package/src/utils/safe-href.ts +0 -38
  298. package/src/utils/scaling.ts +0 -97
  299. package/src/utils/svg-embed.ts +0 -251
  300. package/src/utils/tag-groups.ts +0 -607
  301. package/src/utils/text-measure.ts +0 -122
  302. package/src/utils/time-ticks.ts +0 -253
  303. package/src/utils/title-constants.ts +0 -24
  304. package/src/utils/visual-conventions.ts +0 -61
  305. package/src/utils/wrapped-desc.ts +0 -87
  306. package/src/venn/parser.ts +0 -18
  307. package/src/venn/renderer.ts +0 -1047
  308. package/src/version-control/diagnostics.ts +0 -67
  309. package/src/version-control/parser.ts +0 -544
  310. package/src/version-control/renderer.ts +0 -724
  311. package/src/version-control/types.ts +0 -100
  312. package/src/visualizations/parse.ts +0 -1560
  313. package/src/visualizations/types.ts +0 -262
  314. package/src/wireframe/layout.ts +0 -479
  315. package/src/wireframe/parser.ts +0 -1114
  316. package/src/wireframe/renderer.ts +0 -1318
  317. package/src/wireframe/types.ts +0 -110
  318. package/src/wordcloud/parser.ts +0 -18
  319. package/src/wordcloud/renderer.ts +0 -357
@@ -1,122 +0,0 @@
1
- // ============================================================
2
- // Shared text measurement
3
- // ============================================================
4
- // One source of truth for "how wide is this string?" Before this
5
- // module, ~30 renderers/layouts each carried their own char-width
6
- // estimate (7.5px, 7px, 0.6·fontSize, 0.58, 0.55 …). The drift meant
7
- // the same label measured differently per chart type — a label could
8
- // fit inside an org node but overflow an infra node. All sizing,
9
- // wrapping, and truncation now route through the per-glyph table here.
10
-
11
- // Helvetica character width ratios (fraction of fontSize). Replaces the
12
- // naive `chars * 0.6 * fontSize` estimate with per-character widths.
13
- // prettier-ignore
14
- const CHAR_W: Record<string, number> = {
15
- ' ':.28,'!': .28,'"': .36,'#': .56,'$': .56,'%': .89,'&': .67,"'":.19,
16
- '(':.33,')':.33,'*': .39,'+':.58,',':.28,'-':.33,'.':.28,'/':.28,
17
- '0':.56,'1':.56,'2':.56,'3':.56,'4':.56,'5':.56,'6':.56,'7':.56,'8':.56,'9':.56,
18
- ':':.28,';':.28,'<':.58,'=':.58,'>':.58,'?':.56,'@':1.02,
19
- A:.67,B:.67,C:.72,D:.72,E:.67,F:.61,G:.78,H:.72,I:.28,J:.50,K:.67,L:.56,M:.83,
20
- N:.72,O:.78,P:.67,Q:.78,R:.72,S:.67,T:.61,U:.72,V:.67,W:.94,X:.67,Y:.67,Z:.61,
21
- a:.56,b:.56,c:.50,d:.56,e:.56,f:.28,g:.56,h:.56,i:.22,j:.22,k:.50,l:.22,m:.83,
22
- n:.56,o:.56,p:.56,q:.56,r:.33,s:.50,t:.28,u:.56,v:.50,w:.72,x:.50,y:.50,z:.50,
23
- };
24
- const DEFAULT_W = 0.56;
25
-
26
- /**
27
- * Average glyph-width ratio (fraction of fontSize). Only for the rare
28
- * call site that needs a single scalar (e.g. estimating a max char count
29
- * up front). Prefer {@link measureText} / {@link wrapTextToWidth} —
30
- * they account for actual glyph widths.
31
- */
32
- export const CHAR_WIDTH_RATIO = DEFAULT_W;
33
-
34
- /** Estimate rendered text width using Helvetica proportional character widths. */
35
- export function measureText(text: string, fontSize: number): number {
36
- let w = 0;
37
- for (let i = 0; i < text.length; i++) {
38
- // charAt returns '' for out-of-bounds, never undefined.
39
- w += (CHAR_W[text.charAt(i)] ?? DEFAULT_W) * fontSize;
40
- }
41
- return w;
42
- }
43
-
44
- /**
45
- * Truncate text with a trailing ellipsis to fit within maxWidth.
46
- * Returns the original text if it already fits, or '' if even the
47
- * ellipsis alone won't fit.
48
- */
49
- export function truncateText(
50
- text: string,
51
- fontSize: number,
52
- maxWidth: number
53
- ): string {
54
- if (measureText(text, fontSize) <= maxWidth) return text;
55
- const ellipsis = '…';
56
- const ellipsisW = measureText(ellipsis, fontSize);
57
- if (ellipsisW > maxWidth) return '';
58
- let lo = 0;
59
- let hi = text.length;
60
- while (lo < hi) {
61
- const mid = Math.ceil((lo + hi) / 2);
62
- if (measureText(text.slice(0, mid), fontSize) + ellipsisW <= maxWidth) {
63
- lo = mid;
64
- } else {
65
- hi = mid - 1;
66
- }
67
- }
68
- return lo === 0 ? ellipsis : text.slice(0, lo) + ellipsis;
69
- }
70
-
71
- /**
72
- * Greedy word-wrap to a pixel width using accurate glyph measurement.
73
- *
74
- * - Splits on whitespace; a word that alone exceeds `maxWidth` is kept
75
- * whole unless `hardBreak` is set, in which case it is broken at the
76
- * character boundary that fits.
77
- * - Returns at least one line (`['']` for empty input).
78
- */
79
- export function wrapTextToWidth(
80
- text: string,
81
- fontSize: number,
82
- maxWidth: number,
83
- opts?: { hardBreak?: boolean }
84
- ): string[] {
85
- const words = text.split(/\s+/).filter((w) => w.length > 0);
86
- if (words.length === 0) return [''];
87
- const hardBreak = opts?.hardBreak ?? false;
88
- const lines: string[] = [];
89
- let current = '';
90
- const pushWord = (word: string) => {
91
- const test = current ? `${current} ${word}` : word;
92
- if (measureText(test, fontSize) <= maxWidth || !current) {
93
- current = test;
94
- } else {
95
- lines.push(current);
96
- current = word;
97
- }
98
- };
99
- for (const word of words) {
100
- if (hardBreak && measureText(word, fontSize) > maxWidth) {
101
- // Break the over-long word into width-fitting chunks.
102
- if (current) {
103
- lines.push(current);
104
- current = '';
105
- }
106
- let chunk = '';
107
- for (const ch of word) {
108
- if (chunk && measureText(chunk + ch, fontSize) > maxWidth) {
109
- lines.push(chunk);
110
- chunk = ch;
111
- } else {
112
- chunk += ch;
113
- }
114
- }
115
- current = chunk;
116
- continue;
117
- }
118
- pushWord(word);
119
- }
120
- if (current) lines.push(current);
121
- return lines.length > 0 ? lines : [''];
122
- }
@@ -1,253 +0,0 @@
1
- // ============================================================
2
- // Time axis tick computation — shared by d3.ts and gantt/renderer.ts
3
- // ============================================================
4
-
5
- import type * as d3Scale from 'd3-scale';
6
-
7
- export const MONTH_ABBR = [
8
- 'Jan',
9
- 'Feb',
10
- 'Mar',
11
- 'Apr',
12
- 'May',
13
- 'Jun',
14
- 'Jul',
15
- 'Aug',
16
- 'Sep',
17
- 'Oct',
18
- 'Nov',
19
- 'Dec',
20
- ];
21
-
22
- function fractionalYearToDate(frac: number): Date {
23
- const year = Math.floor(frac);
24
- const remainder = frac - year;
25
- // Inverse of: (month-1)/12 + (day-1)/365 + hour/8760 + minute/525600 + second/31536000
26
- const monthFrac = remainder * 12;
27
- const month = Math.floor(monthFrac); // 0-based
28
- const monthRemainder = remainder - month / 12;
29
- const dayFrac = monthRemainder * 365; // fractional day-of-year offset
30
- const day = Math.floor(dayFrac) + 1;
31
- const dayRemainder = dayFrac - Math.floor(dayFrac);
32
- const hourFrac = dayRemainder * 24;
33
- const hour = Math.floor(hourFrac);
34
- const minuteFrac = (hourFrac - hour) * 60;
35
- const minute = Math.floor(minuteFrac);
36
- const second = Math.round((minuteFrac - minute) * 60);
37
- return new Date(year, month, day, hour, minute, second);
38
- }
39
-
40
- /** Convert a Date to a fractional year number. */
41
- function dateToFractionalYear(d: Date): number {
42
- return (
43
- d.getFullYear() +
44
- d.getMonth() / 12 +
45
- (d.getDate() - 1) / 365 +
46
- d.getHours() / 8760 +
47
- d.getMinutes() / 525600 +
48
- d.getSeconds() / 31536000
49
- );
50
- }
51
-
52
- /** Year axis label: negative years render as `N BCE`. */
53
- function formatYearTick(y: number): string {
54
- return y < 0 ? `${-y} BCE` : String(y);
55
- }
56
-
57
- /**
58
- * Generates adaptive tick marks along a time axis.
59
- * Picks the right granularity (years, months, weeks, days, hours, minutes)
60
- * based on the domain span.
61
- *
62
- * Optional boundary parameters add ticks at exact data start/end:
63
- * - boundaryStart/boundaryEnd: numeric date values
64
- * - boundaryStartLabel/boundaryEndLabel: formatted labels for those dates
65
- */
66
- export function computeTimeTicks(
67
- domainMin: number,
68
- domainMax: number,
69
- scale: d3Scale.ScaleLinear<number, number>,
70
- boundaryStart?: number,
71
- boundaryEnd?: number,
72
- boundaryStartLabel?: string,
73
- boundaryEndLabel?: string
74
- ): { pos: number; label: string }[] {
75
- const minYear = Math.floor(domainMin);
76
- const maxYear = Math.floor(domainMax);
77
- const span = domainMax - domainMin;
78
-
79
- let ticks: { pos: number; label: string }[] = [];
80
-
81
- // Year ticks for multi-year spans (need at least 2 boundaries)
82
- const firstYear = Math.ceil(domainMin);
83
- const lastYear = Math.floor(domainMax);
84
- if (lastYear >= firstYear + 1) {
85
- // Decimate ticks for long spans so labels don't overlap. The ladder runs
86
- // up to century/bicentury steps so millennium-scale spans (and BCE labels,
87
- // which are ~2× wider) stay legible.
88
- const yearSpan = lastYear - firstYear;
89
- let step = 1;
90
- if (yearSpan > 800) step = 200;
91
- else if (yearSpan > 400) step = 100;
92
- else if (yearSpan > 160) step = 50;
93
- else if (yearSpan > 80) step = 20;
94
- else if (yearSpan > 40) step = 10;
95
- else if (yearSpan > 20) step = 5;
96
- else if (yearSpan > 10) step = 2;
97
-
98
- // Align to step boundary so ticks land on round years (1700, 1710, …)
99
- const alignedFirst = Math.ceil(firstYear / step) * step;
100
- for (let y = alignedFirst; y <= lastYear; y += step) {
101
- ticks.push({ pos: scale(y), label: formatYearTick(y) });
102
- }
103
- } else if (span > 0.25) {
104
- // Month ticks for spans > ~3 months
105
- const crossesYear = maxYear > minYear;
106
- for (let y = minYear; y <= maxYear + 1; y++) {
107
- for (let m = 1; m <= 12; m++) {
108
- const val = y + (m - 1) / 12;
109
- if (val > domainMax) break;
110
- if (val >= domainMin) {
111
- // m is 1..12, so MONTH_ABBR[m-1] is always defined.
112
- const abbr = MONTH_ABBR[m - 1]!;
113
- ticks.push({
114
- pos: scale(val),
115
- label: crossesYear ? `${abbr} '${String(y).slice(-2)}` : abbr,
116
- });
117
- }
118
- }
119
- }
120
- } else if (span <= 0.0000114) {
121
- // Second ticks for spans ≤ ~6 minutes
122
- // Adaptive step: >3min → 30s, >1min → 15s, >30s → 10s, else 5s
123
- let stepSec = 5;
124
- const spanSec = span * 31536000;
125
- if (spanSec > 180) stepSec = 30;
126
- else if (spanSec > 60) stepSec = 15;
127
- else if (spanSec > 30) stepSec = 10;
128
-
129
- const startDate = fractionalYearToDate(domainMin);
130
- // Round down to nearest step boundary
131
- startDate.setSeconds(
132
- Math.floor(startDate.getSeconds() / stepSec) * stepSec,
133
- 0
134
- );
135
-
136
- while (true) {
137
- const val = dateToFractionalYear(startDate);
138
- if (val > domainMax) break;
139
- if (val >= domainMin) {
140
- const hh = String(startDate.getHours()).padStart(2, '0');
141
- const mm = String(startDate.getMinutes()).padStart(2, '0');
142
- const ss = String(startDate.getSeconds()).padStart(2, '0');
143
- ticks.push({ pos: scale(val), label: `${hh}:${mm}:${ss}` });
144
- }
145
- startDate.setSeconds(startDate.getSeconds() + stepSec);
146
- }
147
- } else if (span <= 0.000685) {
148
- // Minute ticks for spans ≤ ~6 hours
149
- // Adaptive step: >3h → 30min, >1h → 15min, >30min → 10min, else 5min
150
- let stepMin = 5;
151
- const spanHours = span * 8760;
152
- if (spanHours > 3) stepMin = 30;
153
- else if (spanHours > 1) stepMin = 15;
154
- else if (spanHours > 0.5) stepMin = 10;
155
-
156
- // Iterate from the start hour boundary
157
- const startDate = fractionalYearToDate(domainMin);
158
- // Round down to nearest step boundary
159
- startDate.setMinutes(
160
- Math.floor(startDate.getMinutes() / stepMin) * stepMin,
161
- 0,
162
- 0
163
- );
164
-
165
- while (true) {
166
- const val = dateToFractionalYear(startDate);
167
- if (val > domainMax) break;
168
- if (val >= domainMin) {
169
- const hh = String(startDate.getHours()).padStart(2, '0');
170
- const mm = String(startDate.getMinutes()).padStart(2, '0');
171
- ticks.push({ pos: scale(val), label: `${hh}:${mm}` });
172
- }
173
- startDate.setMinutes(startDate.getMinutes() + stepMin);
174
- }
175
- } else if (span <= 0.00822) {
176
- // Hour ticks for spans ≤ ~3 days
177
- // Adaptive step: >2d → 6h, >1d → 3h, >12h → 2h, else 1h
178
- let stepHour = 1;
179
- const spanHours = span * 8760;
180
- if (spanHours > 48) stepHour = 6;
181
- else if (spanHours > 24) stepHour = 3;
182
- else if (spanHours > 12) stepHour = 2;
183
-
184
- // For single-day spans, just show HH:MM without the date prefix
185
- const singleDay = spanHours <= 24;
186
-
187
- const startDate = fractionalYearToDate(domainMin);
188
- // Round down to nearest step boundary
189
- startDate.setHours(
190
- Math.floor(startDate.getHours() / stepHour) * stepHour,
191
- 0,
192
- 0,
193
- 0
194
- );
195
-
196
- while (true) {
197
- const val = dateToFractionalYear(startDate);
198
- if (val > domainMax) break;
199
- if (val >= domainMin) {
200
- const hh = String(startDate.getHours()).padStart(2, '0');
201
- const mm = String(startDate.getMinutes()).padStart(2, '0');
202
- if (singleDay) {
203
- ticks.push({ pos: scale(val), label: `${hh}:${mm}` });
204
- } else {
205
- const mon = MONTH_ABBR[startDate.getMonth()];
206
- const d = startDate.getDate();
207
- ticks.push({ pos: scale(val), label: `${mon} ${d} ${hh}:${mm}` });
208
- }
209
- }
210
- startDate.setHours(startDate.getHours() + stepHour);
211
- }
212
- } else {
213
- // Week ticks for spans ≤ ~3 months (1st, 8th, 15th, 22nd of each month)
214
- for (let y = minYear; y <= maxYear + 1; y++) {
215
- for (let m = 1; m <= 12; m++) {
216
- for (const d of [1, 8, 15, 22]) {
217
- const val = y + (m - 1) / 12 + (d - 1) / 365;
218
- if (val > domainMax) break;
219
- if (val >= domainMin) {
220
- ticks.push({
221
- pos: scale(val),
222
- label: `${MONTH_ABBR[m - 1]} ${d}`,
223
- });
224
- }
225
- }
226
- }
227
- }
228
- }
229
-
230
- // Add boundary ticks at exact data start/end if provided
231
- // When a boundary tick collides with a standard tick, replace the standard tick
232
- const collisionThreshold = 40; // pixels
233
-
234
- if (boundaryStart !== undefined && boundaryStartLabel) {
235
- const boundaryPos = scale(boundaryStart);
236
- // Remove any standard ticks that would collide with the start boundary
237
- ticks = ticks.filter(
238
- (t) => Math.abs(t.pos - boundaryPos) >= collisionThreshold
239
- );
240
- ticks.unshift({ pos: boundaryPos, label: boundaryStartLabel });
241
- }
242
-
243
- if (boundaryEnd !== undefined && boundaryEndLabel) {
244
- const boundaryPos = scale(boundaryEnd);
245
- // Remove any standard ticks that would collide with the end boundary
246
- ticks = ticks.filter(
247
- (t) => Math.abs(t.pos - boundaryPos) >= collisionThreshold
248
- );
249
- ticks.push({ pos: boundaryPos, label: boundaryEndLabel });
250
- }
251
-
252
- return ticks;
253
- }
@@ -1,24 +0,0 @@
1
- // ============================================================
2
- // Shared chart title constants
3
- // All renderers import from here to stay in sync.
4
- // ============================================================
5
-
6
- export const TITLE_FONT_SIZE = 20;
7
- export const TITLE_FONT_WEIGHT = '700';
8
- export const TITLE_Y = 30;
9
- export const TITLE_OFFSET = 40;
10
-
11
- // Caption — supplementary banner text. The pert renderer wraps caption
12
- // content in a node-styled rectangle below the diagram body; backward-
13
- // anchor annotations still render as a free-floating italic line below
14
- // the title. Pixel `dy` (not `'1.2em'`) so layout math and rendered text
15
- // never drift between JSDOM and resvg.
16
- export const CAPTION_FONT_SIZE = 13;
17
- export const CAPTION_FONT_WEIGHT = '400';
18
- export const CAPTION_LINE_HEIGHT = 18;
19
- export const CAPTION_TOP_GAP = 12;
20
- // Inner padding for the caption rectangle. X = horizontal inset from
21
- // rect edge to bullet glyph; Y = vertical inset from rect top to first
22
- // baseline.
23
- export const CAPTION_BOX_PADDING_X = 16;
24
- export const CAPTION_BOX_PADDING_Y = 12;
@@ -1,61 +0,0 @@
1
- // ============================================================
2
- // Visual conventions — shared card/group/collapse constants (Story 111.1)
3
- // ============================================================
4
- //
5
- // Single source of truth for the structured-diagram visual constants described
6
- // in docs/architecture/diagram-visual-conventions.md. Before this module these
7
- // values were re-declared in every renderer (NODE_STROKE_WIDTH in 13 files,
8
- // HEADER_HEIGHT in 12, ...), so a convention change meant editing N files and
9
- // hoping the snapshots agreed.
10
- //
11
- // Two tiers, because the renderers do NOT all agree:
12
- //
13
- // 1. UNIVERSAL — identical at every call site. Import these everywhere; never
14
- // re-declare. Changing one of these is a deliberate cross-chart change.
15
- //
16
- // 2. CONVENTION DEFAULTS — the org/sitemap baseline from the convention. Most
17
- // card-shaped renderers match these and should import them. A few have a
18
- // DOCUMENTED intentional deviation and keep a local override (with a
19
- // comment saying why):
20
- // - infra: META_FONT_SIZE 10 / META_LINE_HEIGHT 14 (denser meta rows)
21
- // - boxes-and-lines: COLLAPSE_BAR_HEIGHT 4 / SEPARATOR_GAP 4
22
- // - cycle, raci: HEADER_HEIGHT 36 (not label+meta cards)
23
- // Those overrides are the exception that proves the rule — they stay local
24
- // and visible, not hidden inside a re-declared full constant set.
25
-
26
- // ── Universal (same value at every site) ──
27
-
28
- /** Node card + group border stroke width. */
29
- export const NODE_STROKE_WIDTH = 1.5;
30
- /** Edge / connector stroke width. */
31
- export const EDGE_STROKE_WIDTH = 1.5;
32
- /** Node card corner radius. */
33
- export const CARD_RADIUS = 6;
34
- /** Group / container corner radius. */
35
- export const CONTAINER_RADIUS = 8;
36
- /** Collapse accent bar horizontal inset from the card edges. */
37
- export const COLLAPSE_BAR_INSET = 0;
38
-
39
- // ── Convention defaults (org/sitemap baseline; see deviations above) ──
40
-
41
- /** Node card header band height. */
42
- export const HEADER_HEIGHT = 28;
43
- /** Node card label font size. */
44
- export const LABEL_FONT_SIZE = 13;
45
- /** Node card metadata-row font size. */
46
- export const META_FONT_SIZE = 11;
47
- /** Node card metadata-row line height. */
48
- export const META_LINE_HEIGHT = 16;
49
- /** Gap between the header separator and the first metadata row. */
50
- export const SEPARATOR_GAP = 6;
51
- /** Collapse accent bar height. */
52
- export const COLLAPSE_BAR_HEIGHT = 6;
53
-
54
- /** Group / container reserved header band height. */
55
- export const CONTAINER_HEADER_HEIGHT = 28;
56
- /** Group / container label font size. */
57
- export const CONTAINER_LABEL_FONT_SIZE = 13;
58
- /** Group / container metadata-row font size. */
59
- export const CONTAINER_META_FONT_SIZE = 11;
60
- /** Group / container metadata-row line height. */
61
- export const CONTAINER_META_LINE_HEIGHT = 16;
@@ -1,87 +0,0 @@
1
- // ============================================================
2
- // Wrapped Description — shared bullet-aware text wrapping
3
- // ============================================================
4
-
5
- /**
6
- * One rendered description line. `kind` controls horizontal placement and
7
- * whether the renderer draws a bullet glyph:
8
- * - `plain` — flush left at the description's left edge
9
- * - `bullet-first` — "•" drawn at the left edge, body text at the bullet column
10
- * - `bullet-cont` — body continuation at the bullet column (no glyph)
11
- *
12
- * Splitting first-line bullet rendering into separate text elements lets
13
- * continuation lines align exactly under the first word past the bullet,
14
- * regardless of font-width estimation drift.
15
- */
16
- export interface WrappedDescLine {
17
- text: string;
18
- kind: 'plain' | 'bullet-first' | 'bullet-cont';
19
- }
20
-
21
- /** Bullet prefix that triggers split-rendering with a hanging body column. */
22
- export const BULLET_PREFIX = '• ';
23
- /** Equivalent body-column width in characters (subtracted from wrap width). */
24
- export const BULLET_INDENT_CHARS = 2;
25
-
26
- /**
27
- * Optional measurer — returns the *display* length of a string in characters.
28
- * Defaults to `s.length`. Pass a markdown-aware function (stripping link
29
- * targets, emphasis markers, etc.) when wrapping rich text so the wrap
30
- * boundary matches what the user will actually see.
31
- */
32
- export type LengthFn = (s: string) => number;
33
-
34
- /**
35
- * Word-wrap description lines. Bullet lines (starting with "• ") are split
36
- * so the renderer can place the "•" glyph at the description's left edge and
37
- * the body text at a fixed bullet-body column — continuation lines align
38
- * exactly under the first word past the bullet without depending on
39
- * font-width estimation.
40
- */
41
- export function wrapDescriptionLines(
42
- lines: string[],
43
- charsPerLine: number,
44
- lengthFn: LengthFn = (s) => s.length
45
- ): WrappedDescLine[] {
46
- const result: WrappedDescLine[] = [];
47
- for (const line of lines) {
48
- if (!line.startsWith(BULLET_PREFIX)) {
49
- const wrapped = wrapPlainLine(line, charsPerLine, lengthFn);
50
- for (const w of wrapped) result.push({ text: w, kind: 'plain' });
51
- continue;
52
- }
53
- // Strip "• " — the renderer draws the bullet glyph; we only wrap the body.
54
- const body = line.slice(BULLET_PREFIX.length);
55
- const bodyLimit = Math.max(8, charsPerLine - BULLET_INDENT_CHARS);
56
- const wrapped = wrapPlainLine(body, bodyLimit, lengthFn);
57
- wrapped.forEach((w, i) => {
58
- result.push({ text: w, kind: i === 0 ? 'bullet-first' : 'bullet-cont' });
59
- });
60
- }
61
- return result;
62
- }
63
-
64
- /**
65
- * Greedy word-wrap of a single string at the given character limit.
66
- * Long words are kept whole even if they exceed the limit (no mid-word break).
67
- */
68
- function wrapPlainLine(
69
- line: string,
70
- charsPerLine: number,
71
- lengthFn: LengthFn
72
- ): string[] {
73
- const words = line.split(/\s+/);
74
- const result: string[] = [];
75
- let current = '';
76
- for (const word of words) {
77
- const test = current ? `${current} ${word}` : word;
78
- if (lengthFn(test) > charsPerLine && current) {
79
- result.push(current);
80
- current = word;
81
- } else {
82
- current = test;
83
- }
84
- }
85
- if (current) result.push(current);
86
- return result;
87
- }
@@ -1,18 +0,0 @@
1
- // ============================================================
2
- // venn parser door — Story 109.2 (arch-review).
3
- //
4
- // The six visualizations share one parse state machine (visualizations/parse.ts);
5
- // this is venn's typed entry point into it, returning the narrowed
6
- // ParsedVenn shape. The registry binds the 'venn' chart type to this door.
7
- // ============================================================
8
-
9
- import type { PaletteColors } from '../palettes';
10
- import { parseVisualization } from '../visualizations/parse';
11
- import type { ParsedVenn } from '../visualizations/types';
12
-
13
- export function parseVenn(
14
- content: string,
15
- palette?: PaletteColors
16
- ): ParsedVenn {
17
- return parseVisualization(content, palette) as ParsedVenn;
18
- }