@misoto22/design 0.7.0 → 0.9.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 (398) hide show
  1. package/AGENTS.md +87 -0
  2. package/CLAUDE.md +1 -0
  3. package/README.md +50 -5
  4. package/bin/misoto22-design.mjs +59 -13
  5. package/dist/agent/Accordion.md +24 -1
  6. package/dist/agent/Alert.md +23 -1
  7. package/dist/agent/AppShell.md +30 -2
  8. package/dist/agent/ArchitectureFigure.md +25 -2
  9. package/dist/agent/AreaChart.md +27 -2
  10. package/dist/agent/Article.md +26 -3
  11. package/dist/agent/AspectRatio.md +57 -0
  12. package/dist/agent/Avatar.md +25 -5
  13. package/dist/agent/Badge.md +22 -1
  14. package/dist/agent/BarChart.md +29 -2
  15. package/dist/agent/BarList.md +27 -1
  16. package/dist/agent/BigNumber.md +26 -2
  17. package/dist/agent/BoxPlot.md +24 -1
  18. package/dist/agent/Breadcrumb.md +24 -2
  19. package/dist/agent/BulletChart.md +23 -1
  20. package/dist/agent/Button.md +25 -1
  21. package/dist/agent/Calendar.md +37 -3
  22. package/dist/agent/Card.md +24 -2
  23. package/dist/agent/Checkbox.md +23 -2
  24. package/dist/agent/Code.md +50 -0
  25. package/dist/agent/CodeBlock.md +74 -0
  26. package/dist/agent/Collapsible.md +22 -1
  27. package/dist/agent/ColorPicker.md +75 -0
  28. package/dist/agent/Combobox.md +29 -3
  29. package/dist/agent/Command.md +25 -2
  30. package/dist/agent/ComposedChart.md +26 -2
  31. package/dist/agent/ContextMenu.md +37 -4
  32. package/dist/agent/DataflowFigure.md +22 -1
  33. package/dist/agent/DatePicker.md +34 -6
  34. package/dist/agent/DescriptionList.md +68 -0
  35. package/dist/agent/Diagram.md +27 -3
  36. package/dist/agent/DiagramCanvas.md +24 -1
  37. package/dist/agent/DiagramExportMenu.md +29 -3
  38. package/dist/agent/DiagramInspector.md +23 -1
  39. package/dist/agent/DiagramLegend.md +22 -1
  40. package/dist/agent/DiagramMinimap.md +24 -4
  41. package/dist/agent/DiagramToolbar.md +23 -1
  42. package/dist/agent/Dialog.md +26 -3
  43. package/dist/agent/DropdownMenu.md +45 -6
  44. package/dist/agent/EmptyState.md +26 -1
  45. package/dist/agent/ErrorState.md +27 -2
  46. package/dist/agent/Facet.md +25 -1
  47. package/dist/agent/Field.md +45 -4
  48. package/dist/agent/FigureBand.md +24 -1
  49. package/dist/agent/FloatingIconButton.md +23 -1
  50. package/dist/agent/FunnelChart.md +23 -1
  51. package/dist/agent/Heading.md +64 -0
  52. package/dist/agent/Heatmap.md +25 -1
  53. package/dist/agent/Histogram.md +26 -2
  54. package/dist/agent/Input.md +22 -1
  55. package/dist/agent/Kbd.md +19 -1
  56. package/dist/agent/LifecycleFigure.md +22 -1
  57. package/dist/agent/LineChart.md +26 -2
  58. package/dist/agent/LinkArrow.md +20 -1
  59. package/dist/agent/Markdown.md +68 -0
  60. package/dist/agent/NativeSelect.md +23 -2
  61. package/dist/agent/NavItem.md +21 -1
  62. package/dist/agent/NumberField.md +72 -0
  63. package/dist/agent/Pagination.md +28 -2
  64. package/dist/agent/PieChart.md +26 -2
  65. package/dist/agent/Popover.md +23 -1
  66. package/dist/agent/Progress.md +27 -3
  67. package/dist/agent/RadarChart.md +26 -2
  68. package/dist/agent/RadialChart.md +26 -3
  69. package/dist/agent/RadioGroup.md +23 -2
  70. package/dist/agent/SankeyChart.md +25 -2
  71. package/dist/agent/ScatterChart.md +27 -2
  72. package/dist/agent/ScrollArea.md +24 -3
  73. package/dist/agent/SearchableMenu.md +23 -1
  74. package/dist/agent/Select.md +29 -3
  75. package/dist/agent/Separator.md +28 -4
  76. package/dist/agent/SequenceFigure.md +23 -1
  77. package/dist/agent/Sheet.md +25 -2
  78. package/dist/agent/Sidebar.md +193 -0
  79. package/dist/agent/Skeleton.md +24 -2
  80. package/dist/agent/Slider.md +31 -4
  81. package/dist/agent/Sparkline.md +23 -1
  82. package/dist/agent/Spinner.md +24 -2
  83. package/dist/agent/StatusDot.md +20 -1
  84. package/dist/agent/StatusPill.md +28 -2
  85. package/dist/agent/Steps.md +24 -1
  86. package/dist/agent/Switch.md +21 -1
  87. package/dist/agent/Table.md +28 -3
  88. package/dist/agent/Tabs.md +24 -2
  89. package/dist/agent/Tag.md +40 -9
  90. package/dist/agent/Text.md +65 -0
  91. package/dist/agent/Textarea.md +20 -1
  92. package/dist/agent/Timestamp.md +64 -0
  93. package/dist/agent/Toast.md +26 -2
  94. package/dist/agent/ToggleGroup.md +24 -2
  95. package/dist/agent/Toolbar.md +71 -0
  96. package/dist/agent/Tooltip.md +23 -1
  97. package/dist/agent/TreemapChart.md +24 -1
  98. package/dist/agent/WaterfallChart.md +24 -1
  99. package/dist/agent/WorkflowFigure.md +24 -2
  100. package/dist/agent/catalog.json +5915 -699
  101. package/dist/agent/index.md +20 -8
  102. package/dist/article.css +16 -4
  103. package/dist/charts/AreaChart/AreaChart.d.ts +9 -2
  104. package/dist/charts/AreaChart/AreaChart.js +12 -13
  105. package/dist/charts/AreaChart/AreaChart.js.map +1 -1
  106. package/dist/charts/BarChart/BarChart.d.ts +14 -3
  107. package/dist/charts/BarChart/BarChart.js +20 -12
  108. package/dist/charts/BarChart/BarChart.js.map +1 -1
  109. package/dist/charts/BarList/BarList.d.ts +9 -1
  110. package/dist/charts/BarList/BarList.js +9 -1
  111. package/dist/charts/BarList/BarList.js.map +1 -1
  112. package/dist/charts/BigNumber/BigNumber.d.ts +19 -3
  113. package/dist/charts/BigNumber/BigNumber.js +25 -5
  114. package/dist/charts/BigNumber/BigNumber.js.map +1 -1
  115. package/dist/charts/BoxPlot/BoxPlot.js +4 -2
  116. package/dist/charts/BoxPlot/BoxPlot.js.map +1 -1
  117. package/dist/charts/BulletChart/BulletChart.js +44 -9
  118. package/dist/charts/BulletChart/BulletChart.js.map +1 -1
  119. package/dist/charts/ComposedChart/ComposedChart.d.ts +9 -2
  120. package/dist/charts/ComposedChart/ComposedChart.js +10 -10
  121. package/dist/charts/ComposedChart/ComposedChart.js.map +1 -1
  122. package/dist/charts/FunnelChart/FunnelChart.d.ts +7 -2
  123. package/dist/charts/FunnelChart/FunnelChart.js +6 -16
  124. package/dist/charts/FunnelChart/FunnelChart.js.map +1 -1
  125. package/dist/charts/Heatmap/Heatmap.d.ts +17 -3
  126. package/dist/charts/Heatmap/Heatmap.js +20 -11
  127. package/dist/charts/Heatmap/Heatmap.js.map +1 -1
  128. package/dist/charts/Histogram/Histogram.d.ts +24 -1
  129. package/dist/charts/Histogram/Histogram.js +43 -11
  130. package/dist/charts/Histogram/Histogram.js.map +1 -1
  131. package/dist/charts/LineChart/LineChart.d.ts +9 -2
  132. package/dist/charts/LineChart/LineChart.js +11 -12
  133. package/dist/charts/LineChart/LineChart.js.map +1 -1
  134. package/dist/charts/PieChart/PieChart.d.ts +15 -3
  135. package/dist/charts/PieChart/PieChart.js +14 -6
  136. package/dist/charts/PieChart/PieChart.js.map +1 -1
  137. package/dist/charts/RadarChart/RadarChart.d.ts +15 -3
  138. package/dist/charts/RadarChart/RadarChart.js +15 -11
  139. package/dist/charts/RadarChart/RadarChart.js.map +1 -1
  140. package/dist/charts/RadialChart/RadialChart.d.ts +22 -4
  141. package/dist/charts/RadialChart/RadialChart.js +28 -6
  142. package/dist/charts/RadialChart/RadialChart.js.map +1 -1
  143. package/dist/charts/SankeyChart/SankeyChart.d.ts +16 -4
  144. package/dist/charts/SankeyChart/SankeyChart.js +14 -6
  145. package/dist/charts/SankeyChart/SankeyChart.js.map +1 -1
  146. package/dist/charts/ScatterChart/ScatterChart.d.ts +24 -3
  147. package/dist/charts/ScatterChart/ScatterChart.js +21 -15
  148. package/dist/charts/ScatterChart/ScatterChart.js.map +1 -1
  149. package/dist/charts/Sparkline/Sparkline.js +5 -3
  150. package/dist/charts/Sparkline/Sparkline.js.map +1 -1
  151. package/dist/charts/TreemapChart/TreemapChart.d.ts +7 -2
  152. package/dist/charts/TreemapChart/TreemapChart.js +20 -11
  153. package/dist/charts/TreemapChart/TreemapChart.js.map +1 -1
  154. package/dist/charts/WaterfallChart/WaterfallChart.js +4 -2
  155. package/dist/charts/WaterfallChart/WaterfallChart.js.map +1 -1
  156. package/dist/charts/lib/export.d.ts +1 -0
  157. package/dist/charts/lib/figure.d.ts +21 -1
  158. package/dist/charts/lib/figure.js +5 -2
  159. package/dist/charts/lib/figure.js.map +1 -1
  160. package/dist/charts/lib/scale.d.ts +23 -0
  161. package/dist/charts/lib/scale.js +13 -0
  162. package/dist/charts/lib/scale.js.map +1 -0
  163. package/dist/charts/lib/selection.d.ts +20 -0
  164. package/dist/charts/lib/selection.js +18 -0
  165. package/dist/charts/lib/selection.js.map +1 -0
  166. package/dist/charts/lib/toolbar.d.ts +1 -0
  167. package/dist/components/Accordion/Accordion.js +8 -1
  168. package/dist/components/Accordion/Accordion.js.map +1 -1
  169. package/dist/components/AppShell/AppShell.d.ts +6 -0
  170. package/dist/components/AppShell/AppShell.js +34 -4
  171. package/dist/components/AppShell/AppShell.js.map +1 -1
  172. package/dist/components/Article/Article.d.ts +11 -4
  173. package/dist/components/Article/Article.js +13 -0
  174. package/dist/components/Article/Article.js.map +1 -1
  175. package/dist/components/AspectRatio/AspectRatio.d.ts +46 -0
  176. package/dist/components/AspectRatio/AspectRatio.js +33 -0
  177. package/dist/components/AspectRatio/AspectRatio.js.map +1 -0
  178. package/dist/components/Avatar/Avatar.d.ts +12 -3
  179. package/dist/components/Avatar/Avatar.js +6 -1
  180. package/dist/components/Avatar/Avatar.js.map +1 -1
  181. package/dist/components/Breadcrumb/Breadcrumb.d.ts +6 -0
  182. package/dist/components/Breadcrumb/Breadcrumb.js +15 -0
  183. package/dist/components/Breadcrumb/Breadcrumb.js.map +1 -1
  184. package/dist/components/Button/Button.js +14 -0
  185. package/dist/components/Button/Button.js.map +1 -1
  186. package/dist/components/Calendar/Calendar.d.ts +29 -3
  187. package/dist/components/Calendar/Calendar.js +60 -12
  188. package/dist/components/Calendar/Calendar.js.map +1 -1
  189. package/dist/components/Card/Card.d.ts +6 -0
  190. package/dist/components/Card/Card.js +1 -1
  191. package/dist/components/Card/Card.js.map +1 -1
  192. package/dist/components/Checkbox/Checkbox.d.ts +5 -2
  193. package/dist/components/Checkbox/Checkbox.js +12 -2
  194. package/dist/components/Checkbox/Checkbox.js.map +1 -1
  195. package/dist/components/Code/Code.d.ts +30 -0
  196. package/dist/components/Code/Code.js +21 -0
  197. package/dist/components/Code/Code.js.map +1 -0
  198. package/dist/components/CodeBlock/CodeBlock.d.ts +141 -0
  199. package/dist/components/CodeBlock/CodeBlock.js +123 -0
  200. package/dist/components/CodeBlock/CodeBlock.js.map +1 -0
  201. package/dist/components/Collapsible/Collapsible.js +10 -2
  202. package/dist/components/Collapsible/Collapsible.js.map +1 -1
  203. package/dist/components/ColorPicker/ColorPicker.d.ts +67 -0
  204. package/dist/components/ColorPicker/ColorPicker.js +334 -0
  205. package/dist/components/ColorPicker/ColorPicker.js.map +1 -0
  206. package/dist/components/Combobox/Combobox.d.ts +15 -1
  207. package/dist/components/Combobox/Combobox.js +25 -5
  208. package/dist/components/Combobox/Combobox.js.map +1 -1
  209. package/dist/components/Command/Command.d.ts +4 -2
  210. package/dist/components/Command/Command.js +14 -2
  211. package/dist/components/Command/Command.js.map +1 -1
  212. package/dist/components/ContextMenu/ContextMenu.d.ts +30 -5
  213. package/dist/components/ContextMenu/ContextMenu.js +33 -3
  214. package/dist/components/ContextMenu/ContextMenu.js.map +1 -1
  215. package/dist/components/DatePicker/DatePicker.d.ts +36 -4
  216. package/dist/components/DatePicker/DatePicker.js +55 -12
  217. package/dist/components/DatePicker/DatePicker.js.map +1 -1
  218. package/dist/components/DescriptionList/DescriptionList.d.ts +73 -0
  219. package/dist/components/DescriptionList/DescriptionList.js +35 -0
  220. package/dist/components/DescriptionList/DescriptionList.js.map +1 -0
  221. package/dist/components/Diagram/Diagram.d.ts +8 -3
  222. package/dist/components/Diagram/Diagram.js +82 -10
  223. package/dist/components/Diagram/Diagram.js.map +1 -1
  224. package/dist/components/Dialog/Dialog.d.ts +18 -3
  225. package/dist/components/Dialog/Dialog.js +21 -3
  226. package/dist/components/Dialog/Dialog.js.map +1 -1
  227. package/dist/components/DropdownMenu/DropdownMenu.d.ts +49 -11
  228. package/dist/components/DropdownMenu/DropdownMenu.js +33 -3
  229. package/dist/components/DropdownMenu/DropdownMenu.js.map +1 -1
  230. package/dist/components/EmptyState/EmptyState.d.ts +25 -1
  231. package/dist/components/EmptyState/EmptyState.js +3 -1
  232. package/dist/components/EmptyState/EmptyState.js.map +1 -1
  233. package/dist/components/ErrorState/ErrorState.d.ts +22 -1
  234. package/dist/components/ErrorState/ErrorState.js +12 -3
  235. package/dist/components/ErrorState/ErrorState.js.map +1 -1
  236. package/dist/components/Field/Field.d.ts +70 -3
  237. package/dist/components/Field/Field.js +53 -12
  238. package/dist/components/Field/Field.js.map +1 -1
  239. package/dist/components/Field/field-control.d.ts +31 -0
  240. package/dist/components/Field/field-control.js +12 -0
  241. package/dist/components/Field/field-control.js.map +1 -0
  242. package/dist/components/FloatingIconButton/FloatingIconButton.js +2 -0
  243. package/dist/components/FloatingIconButton/FloatingIconButton.js.map +1 -1
  244. package/dist/components/Heading/Heading.d.ts +62 -0
  245. package/dist/components/Heading/Heading.js +44 -0
  246. package/dist/components/Heading/Heading.js.map +1 -0
  247. package/dist/components/Markdown/Markdown.d.ts +139 -0
  248. package/dist/components/Markdown/Markdown.js +135 -0
  249. package/dist/components/Markdown/Markdown.js.map +1 -0
  250. package/dist/components/Markdown/inline.d.ts +29 -0
  251. package/dist/components/Markdown/inline.js +74 -0
  252. package/dist/components/Markdown/inline.js.map +1 -0
  253. package/dist/components/Markdown/nodes.d.ts +65 -0
  254. package/dist/components/Markdown/nodes.js +1 -0
  255. package/dist/components/Markdown/nodes.js.map +1 -0
  256. package/dist/components/Markdown/parse.d.ts +24 -0
  257. package/dist/components/Markdown/parse.js +110 -0
  258. package/dist/components/Markdown/parse.js.map +1 -0
  259. package/dist/components/Markdown/slug.d.ts +15 -0
  260. package/dist/components/Markdown/slug.js +7 -0
  261. package/dist/components/Markdown/slug.js.map +1 -0
  262. package/dist/components/NativeSelect/NativeSelect.d.ts +6 -0
  263. package/dist/components/NativeSelect/NativeSelect.js +2 -3
  264. package/dist/components/NativeSelect/NativeSelect.js.map +1 -1
  265. package/dist/components/NavItem/NavItem.js +1 -1
  266. package/dist/components/NavItem/NavItem.js.map +1 -1
  267. package/dist/components/NumberField/NumberField.d.ts +69 -0
  268. package/dist/components/NumberField/NumberField.js +162 -0
  269. package/dist/components/NumberField/NumberField.js.map +1 -0
  270. package/dist/components/Pagination/Pagination.d.ts +24 -1
  271. package/dist/components/Pagination/Pagination.js +6 -3
  272. package/dist/components/Pagination/Pagination.js.map +1 -1
  273. package/dist/components/Popover/Popover.js +1 -1
  274. package/dist/components/Popover/Popover.js.map +1 -1
  275. package/dist/components/Progress/Progress.d.ts +17 -5
  276. package/dist/components/Progress/Progress.js +18 -6
  277. package/dist/components/Progress/Progress.js.map +1 -1
  278. package/dist/components/RadioGroup/RadioGroup.d.ts +6 -0
  279. package/dist/components/RadioGroup/RadioGroup.js +11 -1
  280. package/dist/components/RadioGroup/RadioGroup.js.map +1 -1
  281. package/dist/components/ScrollArea/ScrollArea.d.ts +15 -0
  282. package/dist/components/ScrollArea/ScrollArea.js +2 -2
  283. package/dist/components/ScrollArea/ScrollArea.js.map +1 -1
  284. package/dist/components/SearchableMenu/SearchableMenu.d.ts +11 -2
  285. package/dist/components/SearchableMenu/SearchableMenu.js +24 -2
  286. package/dist/components/SearchableMenu/SearchableMenu.js.map +1 -1
  287. package/dist/components/Select/Select.d.ts +18 -2
  288. package/dist/components/Select/Select.js +21 -4
  289. package/dist/components/Select/Select.js.map +1 -1
  290. package/dist/components/Separator/Separator.d.ts +21 -3
  291. package/dist/components/Separator/Separator.js +9 -1
  292. package/dist/components/Separator/Separator.js.map +1 -1
  293. package/dist/components/Sheet/Sheet.d.ts +3 -0
  294. package/dist/components/Sheet/Sheet.js +12 -3
  295. package/dist/components/Sheet/Sheet.js.map +1 -1
  296. package/dist/components/Sidebar/Sidebar.d.ts +349 -0
  297. package/dist/components/Sidebar/Sidebar.js +455 -0
  298. package/dist/components/Sidebar/Sidebar.js.map +1 -0
  299. package/dist/components/Skeleton/Skeleton.d.ts +8 -1
  300. package/dist/components/Skeleton/Skeleton.js +1 -1
  301. package/dist/components/Skeleton/Skeleton.js.map +1 -1
  302. package/dist/components/Slider/Slider.d.ts +42 -3
  303. package/dist/components/Slider/Slider.js +121 -30
  304. package/dist/components/Slider/Slider.js.map +1 -1
  305. package/dist/components/Spinner/Spinner.d.ts +8 -0
  306. package/dist/components/Spinner/Spinner.js +3 -2
  307. package/dist/components/Spinner/Spinner.js.map +1 -1
  308. package/dist/components/StatusPill/StatusPill.d.ts +5 -0
  309. package/dist/components/StatusPill/StatusPill.js +6 -0
  310. package/dist/components/StatusPill/StatusPill.js.map +1 -1
  311. package/dist/components/Table/Table.d.ts +20 -0
  312. package/dist/components/Table/Table.js +3 -1
  313. package/dist/components/Table/Table.js.map +1 -1
  314. package/dist/components/Tabs/Tabs.js +7 -1
  315. package/dist/components/Tabs/Tabs.js.map +1 -1
  316. package/dist/components/Tag/Tag.d.ts +68 -9
  317. package/dist/components/Tag/Tag.js +50 -10
  318. package/dist/components/Tag/Tag.js.map +1 -1
  319. package/dist/components/Text/Text.d.ts +62 -0
  320. package/dist/components/Text/Text.js +30 -0
  321. package/dist/components/Text/Text.js.map +1 -0
  322. package/dist/components/Timestamp/Timestamp.d.ts +59 -0
  323. package/dist/components/Timestamp/Timestamp.js +56 -0
  324. package/dist/components/Timestamp/Timestamp.js.map +1 -0
  325. package/dist/components/Toast/Toast.d.ts +9 -1
  326. package/dist/components/Toast/Toast.js +27 -4
  327. package/dist/components/Toast/Toast.js.map +1 -1
  328. package/dist/components/ToggleGroup/ToggleGroup.d.ts +6 -1
  329. package/dist/components/ToggleGroup/ToggleGroup.js +11 -1
  330. package/dist/components/ToggleGroup/ToggleGroup.js.map +1 -1
  331. package/dist/components/Toolbar/Toolbar.d.ts +55 -0
  332. package/dist/components/Toolbar/Toolbar.js +43 -0
  333. package/dist/components/Toolbar/Toolbar.js.map +1 -0
  334. package/dist/diagrams/ArchitectureFigure/ArchitectureFigure.d.ts +6 -0
  335. package/dist/diagrams/ArchitectureFigure/ArchitectureFigure.js +48 -8
  336. package/dist/diagrams/ArchitectureFigure/ArchitectureFigure.js.map +1 -1
  337. package/dist/diagrams/DataflowFigure/DataflowFigure.js +21 -2
  338. package/dist/diagrams/DataflowFigure/DataflowFigure.js.map +1 -1
  339. package/dist/diagrams/DiagramCanvas/DiagramCanvas.d.ts +14 -0
  340. package/dist/diagrams/DiagramCanvas/DiagramCanvas.js +14 -7
  341. package/dist/diagrams/DiagramCanvas/DiagramCanvas.js.map +1 -1
  342. package/dist/diagrams/DiagramExportMenu/DiagramExportMenu.d.ts +38 -4
  343. package/dist/diagrams/DiagramExportMenu/DiagramExportMenu.js +14 -16
  344. package/dist/diagrams/DiagramExportMenu/DiagramExportMenu.js.map +1 -1
  345. package/dist/diagrams/DiagramInspector/DiagramInspector.js +2 -2
  346. package/dist/diagrams/DiagramInspector/DiagramInspector.js.map +1 -1
  347. package/dist/diagrams/DiagramMinimap/DiagramMinimap.d.ts +23 -7
  348. package/dist/diagrams/DiagramMinimap/DiagramMinimap.js +45 -18
  349. package/dist/diagrams/DiagramMinimap/DiagramMinimap.js.map +1 -1
  350. package/dist/diagrams/LifecycleFigure/LifecycleFigure.js +44 -12
  351. package/dist/diagrams/LifecycleFigure/LifecycleFigure.js.map +1 -1
  352. package/dist/diagrams/SequenceFigure/SequenceFigure.js +35 -5
  353. package/dist/diagrams/SequenceFigure/SequenceFigure.js.map +1 -1
  354. package/dist/diagrams/WorkflowFigure/WorkflowFigure.d.ts +3 -1
  355. package/dist/diagrams/WorkflowFigure/WorkflowFigure.js +76 -28
  356. package/dist/diagrams/WorkflowFigure/WorkflowFigure.js.map +1 -1
  357. package/dist/diagrams/index.d.ts +1 -0
  358. package/dist/diagrams/index.js +10 -0
  359. package/dist/diagrams/index.js.map +1 -1
  360. package/dist/diagrams/lib/dev.d.ts +61 -0
  361. package/dist/diagrams/lib/dev.js +96 -0
  362. package/dist/diagrams/lib/dev.js.map +1 -0
  363. package/dist/diagrams/lib/frame.d.ts +48 -7
  364. package/dist/diagrams/lib/frame.js +17 -2
  365. package/dist/diagrams/lib/frame.js.map +1 -1
  366. package/dist/diagrams/lib/route.d.ts +22 -1
  367. package/dist/diagrams/lib/route.js +24 -1
  368. package/dist/diagrams/lib/route.js.map +1 -1
  369. package/dist/diagrams/lib/wires.js +9 -5
  370. package/dist/diagrams/lib/wires.js.map +1 -1
  371. package/dist/diagrams/spec.d.ts +64 -12
  372. package/dist/index.d.ts +19 -4
  373. package/dist/index.js +12 -0
  374. package/dist/index.js.map +1 -1
  375. package/dist/keyframes.css +57 -3
  376. package/dist/lib/color.d.ts +80 -0
  377. package/dist/lib/color.js +189 -0
  378. package/dist/lib/color.js.map +1 -0
  379. package/dist/lib/numeric.d.ts +35 -0
  380. package/dist/lib/numeric.js +29 -0
  381. package/dist/lib/numeric.js.map +1 -0
  382. package/dist/lib/overlay-container.d.ts +14 -4
  383. package/dist/lib/overlay-container.js.map +1 -1
  384. package/dist/lib/warn.d.ts +73 -0
  385. package/dist/lib/warn.js +38 -0
  386. package/dist/lib/warn.js.map +1 -0
  387. package/dist/semantic.css +17 -6
  388. package/dist/styles.css +1 -1
  389. package/dist/themes.css +8 -0
  390. package/dist/tokens.css +86 -5
  391. package/dist/tokens.d.ts +4 -1
  392. package/dist/tokens.js +56 -36
  393. package/dist/tokens.json +56 -36
  394. package/llms.txt +35 -0
  395. package/package.json +5 -2
  396. package/skills/misoto22-design/evals/evals.json +84 -0
  397. package/skills/misoto22-design/rules/a11y.md +55 -3
  398. package/skills/misoto22-design/rules/forms.md +8 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/diagrams/LifecycleFigure/LifecycleFigure.tsx"],"sourcesContent":["'use client'\n\nimport { useId, useMemo } from 'react'\nimport { DiagramFrame, type FigureChrome, type FigureModel } from '../lib/frame'\nimport { inflate, round, TYPE, textWidth, union, wrapText, type Box } from '../lib/geometry'\nimport { resolveLegend, stateLegend } from '../lib/legend'\nimport { Chip, PLATE, plateHeight, StatePlate } from '../lib/marks'\nimport { renderWires, wiresFor } from '../lib/wires'\nimport type { LifecycleSpec, LifecycleStateKind } from '../spec'\n\nconst KINDS: LifecycleStateKind[] = [\n 'start',\n 'active',\n 'waiting',\n 'decision',\n 'success',\n 'failure',\n 'neutral',\n 'external',\n]\n\nconst GRID = { originX: 132, originY: 56, colW: 158, gapX: 62, laneGap: 52, stateH: 68 }\n\n/**\n * How far a secondary lane's columns are shifted along the main rail.\n *\n * archify's lifecycle contract: the first lane holds the ordered phases in\n * columns 0–4, and a secondary lane's column N sits exactly beneath main column\n * N + 2. It is a convention rather than a derivation — the alternative would be\n * making every author restate a column index they already implied — and\n * honouring it is what keeps `executing → approval` a straight vertical drop\n * instead of a dogleg across two unrelated states.\n */\nconst SECONDARY_LANE_OFFSET = 2\n\n/** How much horizontal room a state's own shape leaves for its name. */\nfunction textRoom(width: number, kind: LifecycleStateKind): number {\n return kind === 'decision' ? width * 0.56 : width - PLATE.padX * 2\n}\n\nexport interface LifecycleFigureProps extends FigureChrome {\n spec: LifecycleSpec\n}\n\n/**\n * A state machine: what something can BE, and what moves it.\n *\n * The one figure here that spends colour, and it spends exactly the two tokens\n * this system reserves for state: `--success` on a terminal success,\n * `--danger` on a terminal failure. Nothing else in the package is allowed\n * those two, and a lifecycle diagram is the case they were reserved for — the\n * whole point of the picture is which end a run came out of.\n *\n * The other six kinds are carried by shape, so a greyscale print loses the two\n * outcomes' hue and keeps every other distinction: a start is a filled cap, a\n * decision is a diamond, a wait is a dashed frame, an external is a plate with\n * a cut corner.\n *\n * A RECOVERABLE FAILURE IS A FAILURE WITH A WAY BACK. `type: \"failure\"` says\n * how a state is drawn; a transition OUT of it is what says whether the run is\n * over. A retryable error and a terminal one look identical until you follow\n * the arrows, which is correct — the difference between them is a real edge in\n * the machine, not an adjective on a box.\n */\nexport function LifecycleFigure({\n spec,\n className,\n legend = 'auto',\n cards = true,\n heading = true,\n activeIds,\n onSelectNode,\n}: LifecycleFigureProps) {\n const uid = useId().replace(/:/g, '')\n const model = useMemo(\n () => buildModel(spec, uid, activeIds, onSelectNode),\n [spec, uid, activeIds, onSelectNode],\n )\n\n return (\n <DiagramFrame\n meta={spec.meta}\n model={model}\n cards={spec.cards}\n showCards={cards}\n heading={heading}\n legend={legend}\n className={className}\n activeIds={activeIds}\n onSelectNode={onSelectNode}\n />\n )\n}\n\nfunction buildModel(\n spec: LifecycleSpec,\n uid: string,\n activeIds: string[] | undefined,\n onSelectNode: ((id: string) => void) | undefined,\n): FigureModel {\n const lanes = spec.lanes ?? [{ id: '', label: '' }]\n const laneIndex = new Map(lanes.map((lane, index) => [lane.id, index]))\n\n const boxes = new Map<string, Box>()\n const placed = spec.states.map((state) => {\n // A diamond is only half as wide as its box where the label crosses it, so\n // it gets a wider box for the same words. Sizing it like a rectangle is\n // what pushes a decision's name out past its own two sloping edges.\n const w = state.width ?? (state.type === 'decision' ? GRID.colW + 34 : GRID.colW)\n const lines = wrapText(state.label, TYPE.label, textRoom(w, state.type), 2)\n const h = plateHeight(lines.length, Boolean(state.sublabel), true, state.height ?? GRID.stateH)\n\n const lane = laneIndex.get(state.lane ?? '') ?? 0\n const col = state.col + (lane === 0 ? 0 : SECONDARY_LANE_OFFSET)\n\n // `yOffset` is deliberately NOT applied. It is an absolute nudge measured in\n // archify's own lane heights, and this renderer's lanes are a different\n // depth — so a 78-unit push that cleared a neighbour there drops a state\n // squarely on top of the lane below here. Two overlapping plates are worse\n // than a state sitting on its lane's centre line, which is where the\n // specification put it before the nudge.\n const x = GRID.originX + col * (GRID.colW + GRID.gapX)\n const y = GRID.originY + lane * (GRID.stateH + GRID.laneGap)\n\n const box: Box = { x, y, w, h }\n boxes.set(state.id, box)\n return { state, box, lane }\n })\n\n // The main rail is IMPLICIT, and has to be. archify's lifecycle contract\n // gives the first lane's columns 0..4 to the ordered phases and does not ask\n // an author to restate `queued → planning → executing` as transitions —\n // those are the diagram's spine, not its exceptions. Without them drawn, the\n // top row reads as five unconnected boxes.\n const rail = placed\n .filter((entry) => entry.lane === 0)\n .sort((a, b) => a.state.col - b.state.col)\n const declared = new Set((spec.transitions ?? []).map((t) => `${t.from}→${t.to}`))\n const implied = rail\n .slice(0, -1)\n .map((entry, index) => ({ from: entry.state.id, to: rail[index + 1]!.state.id }))\n .filter((edge) => !declared.has(`${edge.from}→${edge.to}`))\n\n const wires = [\n ...wiresFor(implied, boxes, () => ({ weight: 2.1 })),\n ...wiresFor(spec.transitions, boxes),\n ]\n const { paths, labels } = renderWires(wires, uid)\n\n const extent = inflate(union([...boxes.values()]) ?? { x: 0, y: 0, w: 500, h: 240 }, 34)\n const active = activeIds && activeIds.length > 0 ? new Set(activeIds) : null\n\n // A lane's rule spans what it actually holds. Drawing it across the figure's\n // full width would claim a band reaches columns nothing in it occupies.\n const laneRules = lanes.map((lane, index) => {\n const inside = placed.filter((entry) => entry.lane === index).map((entry) => entry.box)\n const span = union(inside)\n return span ? { lane, y: span.y - 16, x1: extent.x + 18, x2: span.x + span.w } : null\n })\n\n const artwork = (\n <>\n {laneRules.map((rule, index) =>\n rule && rule.lane.label ? (\n <g key={`lane-${index}`} aria-hidden=\"true\">\n <path\n d={`M ${round(rule.x1)} ${round(rule.y)} H ${round(rule.x2)}`}\n className=\"stroke-(--diagram-rule) [stroke-width:1]\"\n />\n <text\n x={round(rule.x1)}\n y={round(rule.y - 7)}\n className=\"fill-(--diagram-ink-3) font-mono [font-size:10.5px] [letter-spacing:0.08em] uppercase\"\n >\n {rule.lane.label}\n </text>\n </g>\n ) : null,\n )}\n\n {paths}\n\n {placed.map(({ state, box }) => {\n const lines = wrapText(state.label, TYPE.label, textRoom(box.w, state.type), 2)\n const bottomReserve = state.tag ? 20 : 0\n const blockHeight = lines.length * (TYPE.label * 1.25) + (state.sublabel ? TYPE.sub * 1.5 : 0)\n const top = box.y + (box.h - bottomReserve - blockHeight) / 2\n const reversed = state.type === 'start'\n const centreX = box.x + box.w / 2\n\n return (\n <g\n key={state.id}\n data-node={state.id}\n data-active={active?.has(state.id) ? '' : undefined}\n className={active && !active.has(state.id) ? 'opacity-25' : undefined}\n onClick={onSelectNode ? () => onSelectNode(state.id) : undefined}\n >\n <StatePlate box={box} kind={state.type}>\n {active?.has(state.id) && (\n <rect\n x={round(box.x) - 3}\n y={round(box.y) - 3}\n width={round(box.w) + 6}\n height={round(box.h) + 6}\n rx={9}\n fill=\"none\"\n className=\"stroke-(--accent) [stroke-width:1.4]\"\n />\n )}\n {state.step && (\n <text\n x={round(box.x + 10)}\n y={round(box.y + 15)}\n className={\n reversed\n ? 'fill-(--diagram-plate-ink) font-mono [font-size:9.5px] opacity-70'\n : 'fill-(--diagram-ink-3) font-mono [font-size:9.5px]'\n }\n >\n {state.step}\n </text>\n )}\n {lines.map((line, index) => (\n <text\n key={index}\n x={round(centreX)}\n y={round(top + (index + 1) * TYPE.label * 1.25 - 3.5)}\n textAnchor=\"middle\"\n className={\n reversed\n ? 'fill-(--diagram-plate-ink) font-sans [font-size:13px]'\n : 'fill-(--diagram-ink) font-sans [font-size:13px]'\n }\n >\n {line}\n </text>\n ))}\n {state.sublabel && (\n <text\n x={round(centreX)}\n y={round(top + lines.length * TYPE.label * 1.25 + TYPE.sub)}\n textAnchor=\"middle\"\n className={\n reversed\n ? 'fill-(--diagram-plate-ink) font-mono [font-size:10.5px] opacity-70'\n : 'fill-(--diagram-ink-3) font-mono [font-size:10.5px]'\n }\n >\n {state.sublabel}\n </text>\n )}\n {state.tag && <Chip x={centreX} y={box.y + box.h - 9} text={state.tag} align=\"middle\" />}\n </StatePlate>\n </g>\n )\n })}\n\n {labels}\n\n </>\n )\n\n const used = [...new Set(spec.states.map((state) => state.type))]\n const widest = Math.max(\n 0,\n ...lanes.map((lane) => textWidth(lane.label, TYPE.band)),\n )\n\n // Widening the frame to the LEFT for the lane names has to widen it, not\n // slide it: moving `x` without adding the same amount to `w` trims exactly\n // that much off the right-hand edge, which cost the last column of states.\n const railLeft = Math.min(extent.x, GRID.originX - widest - 30)\n\n return {\n extent: { ...extent, x: railLeft, w: extent.w + (extent.x - railLeft) },\n artwork,\n nodes: spec.states.map((state) => ({\n id: state.id,\n label: state.label,\n sublabel: state.sublabel,\n kind: state.type,\n })),\n edges: (spec.transitions ?? []).map((transition) => ({\n from: transition.from,\n to: transition.to,\n label: [transition.label, transition.note].filter(Boolean).join(' — ') || undefined,\n })),\n legend: stateLegend(\n resolveLegend(used, spec.meta.legend?.mode, KINDS, spec.meta.legend?.entries),\n spec.meta.legend?.entries,\n ),\n }\n}\n\nexport default LifecycleFigure\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/diagrams/LifecycleFigure/LifecycleFigure.tsx"],"sourcesContent":["'use client'\n\nimport { useId, useMemo } from 'react'\nimport { cn } from '../../lib/cn'\nimport { liveEdges, useSpecIdentity, warnUnknownLane } from '../lib/dev'\nimport { DiagramFrame, type FigureBand, type FigureChrome, type FigureModel } from '../lib/frame'\nimport { inflate, round, TYPE, textWidth, union, wrapText, type Box } from '../lib/geometry'\nimport { resolveLegend, stateLegend } from '../lib/legend'\nimport { Chip, PLATE, plateHeight, StatePlate } from '../lib/marks'\nimport { renderWires, wiresFor } from '../lib/wires'\nimport type { LifecycleSpec, LifecycleStateKind } from '../spec'\n\nconst KINDS: LifecycleStateKind[] = [\n 'start',\n 'active',\n 'waiting',\n 'decision',\n 'success',\n 'failure',\n 'neutral',\n 'external',\n]\n\nconst GRID = { originX: 132, originY: 56, colW: 158, gapX: 62, laneGap: 52, stateH: 68 }\n\n/**\n * How far a secondary lane's columns are shifted along the main rail.\n *\n * archify's lifecycle contract: the first lane holds the ordered phases in\n * columns 0–4, and a secondary lane's column N sits exactly beneath main column\n * N + 2. It is a convention rather than a derivation — the alternative would be\n * making every author restate a column index they already implied — and\n * honouring it is what keeps `executing → approval` a straight vertical drop\n * instead of a dogleg across two unrelated states.\n */\nconst SECONDARY_LANE_OFFSET = 2\n\n/** How much horizontal room a state's own shape leaves for its name. */\nfunction textRoom(width: number, kind: LifecycleStateKind): number {\n return kind === 'decision' ? width * 0.56 : width - PLATE.padX * 2\n}\n\nexport interface LifecycleFigureProps extends FigureChrome {\n spec: LifecycleSpec\n}\n\n/**\n * A state machine: what something can BE, and what moves it.\n *\n * The one figure here that spends colour, and it spends exactly the two tokens\n * this system reserves for state: `--success` on a terminal success,\n * `--danger` on a terminal failure. Nothing else in the package is allowed\n * those two, and a lifecycle diagram is the case they were reserved for — the\n * whole point of the picture is which end a run came out of.\n *\n * The other six kinds are carried by shape, so a greyscale print loses the two\n * outcomes' hue and keeps every other distinction: a start is a filled cap, a\n * decision is a diamond, a wait is a dashed frame, an external is a plate with\n * a cut corner.\n *\n * A RECOVERABLE FAILURE IS A FAILURE WITH A WAY BACK. `type: \"failure\"` says\n * how a state is drawn; a transition OUT of it is what says whether the run is\n * over. A retryable error and a terminal one look identical until you follow\n * the arrows, which is correct — the difference between them is a real edge in\n * the machine, not an adjective on a box.\n */\nexport function LifecycleFigure({\n spec,\n className,\n legend = 'auto',\n cards = true,\n heading = true,\n activeIds,\n onSelectNode,\n}: LifecycleFigureProps) {\n const uid = useId().replace(/:/g, '')\n useSpecIdentity(spec, 'LifecycleFigure')\n const model = useMemo(\n () => buildModel(spec, uid, activeIds, onSelectNode),\n [spec, uid, activeIds, onSelectNode],\n )\n\n return (\n <DiagramFrame\n meta={spec.meta}\n model={model}\n cards={spec.cards}\n showCards={cards}\n heading={heading}\n legend={legend}\n className={className}\n activeIds={activeIds}\n onSelectNode={onSelectNode}\n />\n )\n}\n\nfunction buildModel(\n spec: LifecycleSpec,\n uid: string,\n activeIds: string[] | undefined,\n onSelectNode: ((id: string) => void) | undefined,\n): FigureModel {\n const lanes = spec.lanes ?? [{ id: '', label: '' }]\n const laneIndex = new Map(lanes.map((lane, index) => [lane.id, index]))\n\n // A state that names no lane belongs to the first one — that is the documented\n // default. A state that names a lane NOTHING DECLARES is a different thing:\n // it also lands in the first lane, and the first lane is the main rail, so a\n // typo does not merely misplace a box, it enrols the state in the spine and\n // invents a transition the machine does not have. Hence the flag, and hence\n // being told.\n const laneOf = (state: { id: string; lane?: string }) => {\n if (!state.lane) return { index: 0, known: true }\n const index = laneIndex.get(state.lane)\n if (index !== undefined) return { index, known: true }\n warnUnknownLane('LifecycleFigure', state.id, state.lane)\n return { index: 0, known: false }\n }\n\n const boxes = new Map<string, Box>()\n const placed = spec.states.map((state) => {\n // A diamond is only half as wide as its box where the label crosses it, so\n // it gets a wider box for the same words. Sizing it like a rectangle is\n // what pushes a decision's name out past its own two sloping edges.\n const w = state.width ?? (state.type === 'decision' ? GRID.colW + 34 : GRID.colW)\n const lines = wrapText(state.label, TYPE.label, textRoom(w, state.type), 2)\n const h = plateHeight(lines.length, Boolean(state.sublabel), true, state.height ?? GRID.stateH)\n\n const { index: lane, known } = laneOf(state)\n const col = state.col + (lane === 0 ? 0 : SECONDARY_LANE_OFFSET)\n\n // `yOffset` is applied, the way `WorkflowFigure` and `DataflowFigure` apply\n // theirs. One field name meaning \"nudge this off its row\" in two figures\n // and \"nothing\" in a third is not a safety measure — it is a specification\n // that typechecks and draws something else, with nothing in the type to say\n // which of the two it will be. A nudge that puts two plates on top of each\n // other is visible; a nudge silently dropped is not.\n const x = GRID.originX + col * (GRID.colW + GRID.gapX)\n const y = GRID.originY + lane * (GRID.stateH + GRID.laneGap) + (state.yOffset ?? 0)\n\n const box: Box = { x, y, w, h }\n boxes.set(state.id, box)\n return { state, box, lane, known }\n })\n\n // The main rail is IMPLICIT, and has to be. archify's lifecycle contract\n // gives the first lane's columns 0..4 to the ordered phases and does not ask\n // an author to restate `queued → planning → executing` as transitions —\n // those are the diagram's spine, not its exceptions. Without them drawn, the\n // top row reads as five unconnected boxes.\n const rail = placed\n .filter((entry) => entry.lane === 0 && entry.known)\n .sort((a, b) => a.state.col - b.state.col)\n const declared = new Set((spec.transitions ?? []).map((t) => `${t.from}→${t.to}`))\n const implied = rail\n .slice(0, -1)\n .map((entry, index) => ({ from: entry.state.id, to: rail[index + 1]!.state.id }))\n .filter((edge) => !declared.has(`${edge.from}→${edge.to}`))\n\n const transitions = liveEdges('LifecycleFigure', spec.transitions, (id) => boxes.has(id))\n const wires = [\n ...wiresFor(implied, boxes, () => ({ weight: 2.1 })),\n ...wiresFor(transitions, boxes),\n ]\n const { paths, labels } = renderWires(wires, uid)\n\n const extent = inflate(union([...boxes.values()]) ?? { x: 0, y: 0, w: 500, h: 240 }, 34)\n const active = activeIds && activeIds.length > 0 ? new Set(activeIds) : null\n\n // A lane's rule spans what it actually holds. Drawing it across the figure's\n // full width would claim a band reaches columns nothing in it occupies.\n const laneRules = lanes.map((lane, index) => {\n const inside = placed.filter((entry) => entry.lane === index).map((entry) => entry.box)\n const span = union(inside)\n return span ? { lane, y: span.y - 16, x1: extent.x + 18, x2: span.x + span.w } : null\n })\n\n const artwork = (\n <>\n {laneRules.map((rule, index) =>\n rule && rule.lane.label ? (\n <g key={`lane-${index}`} aria-hidden=\"true\">\n <path\n d={`M ${round(rule.x1)} ${round(rule.y)} H ${round(rule.x2)}`}\n className=\"stroke-(--diagram-rule) [stroke-width:1]\"\n />\n <text\n x={round(rule.x1)}\n y={round(rule.y - 7)}\n className=\"fill-(--diagram-ink-3) font-mono [font-size:10.5px] [letter-spacing:0.08em] uppercase\"\n >\n {rule.lane.label}\n </text>\n </g>\n ) : null,\n )}\n\n {paths}\n\n {placed.map(({ state, box }) => {\n const lines = wrapText(state.label, TYPE.label, textRoom(box.w, state.type), 2)\n const bottomReserve = state.tag ? 20 : 0\n const blockHeight = lines.length * (TYPE.label * 1.25) + (state.sublabel ? TYPE.sub * 1.5 : 0)\n const top = box.y + (box.h - bottomReserve - blockHeight) / 2\n const reversed = state.type === 'start'\n const centreX = box.x + box.w / 2\n\n return (\n <g\n key={state.id}\n data-node={state.id}\n data-active={active?.has(state.id) ? '' : undefined}\n className={cn(\n active && !active.has(state.id) && 'opacity-25',\n // The one figure that builds its own group instead of using\n // NodePlate, and so the one figure whose selectable plates showed\n // a text caret over a control.\n onSelectNode && 'cursor-pointer',\n )}\n onClick={onSelectNode ? () => onSelectNode(state.id) : undefined}\n >\n <StatePlate box={box} kind={state.type}>\n {active?.has(state.id) && (\n <rect\n x={round(box.x) - 3}\n y={round(box.y) - 3}\n width={round(box.w) + 6}\n height={round(box.h) + 6}\n rx={9}\n fill=\"none\"\n className=\"stroke-(--accent) [stroke-width:1.4]\"\n />\n )}\n {state.step && (\n <text\n x={round(box.x + 10)}\n y={round(box.y + 15)}\n className={\n reversed\n ? 'fill-(--diagram-plate-ink) font-mono [font-size:9.5px] opacity-70'\n : 'fill-(--diagram-ink-3) font-mono [font-size:9.5px]'\n }\n >\n {state.step}\n </text>\n )}\n {lines.map((line, index) => (\n <text\n key={index}\n x={round(centreX)}\n y={round(top + (index + 1) * TYPE.label * 1.25 - 3.5)}\n textAnchor=\"middle\"\n className={\n reversed\n ? 'fill-(--diagram-plate-ink) font-sans [font-size:13px]'\n : 'fill-(--diagram-ink) font-sans [font-size:13px]'\n }\n >\n {line}\n </text>\n ))}\n {state.sublabel && (\n <text\n x={round(centreX)}\n y={round(top + lines.length * TYPE.label * 1.25 + TYPE.sub)}\n textAnchor=\"middle\"\n className={\n reversed\n ? 'fill-(--diagram-plate-ink) font-mono [font-size:10.5px] opacity-70'\n : 'fill-(--diagram-ink-3) font-mono [font-size:10.5px]'\n }\n >\n {state.sublabel}\n </text>\n )}\n {state.tag && <Chip x={centreX} y={box.y + box.h - 9} text={state.tag} align=\"middle\" />}\n </StatePlate>\n </g>\n )\n })}\n\n {labels}\n\n </>\n )\n\n const used = [...new Set(spec.states.map((state) => state.type))]\n\n // Lanes group the list only when the specification actually has them: the\n // single unnamed lane a laneless spec falls back to would publish a band\n // called nothing, around every state in the figure.\n const bands: FigureBand[] | undefined =\n spec.lanes && spec.lanes.length > 0\n ? lanes.map((lane, index) => ({\n kind: 'Lane',\n label: lane.label,\n ids: placed\n .filter((entry) => entry.lane === index)\n .sort((a, b) => a.state.col - b.state.col)\n .map((entry) => entry.state.id),\n }))\n : undefined\n const widest = Math.max(\n 0,\n ...lanes.map((lane) => textWidth(lane.label, TYPE.band)),\n )\n\n // Widening the frame to the LEFT for the lane names has to widen it, not\n // slide it: moving `x` without adding the same amount to `w` trims exactly\n // that much off the right-hand edge, which cost the last column of states.\n const railLeft = Math.min(extent.x, GRID.originX - widest - 30)\n\n return {\n extent: { ...extent, x: railLeft, w: extent.w + (extent.x - railLeft) },\n artwork,\n nodes: spec.states.map((state) => ({\n id: state.id,\n label: state.label,\n sublabel: state.sublabel,\n kind: state.type,\n // Printed in the plate's corner, inside artwork a screen reader is told\n // to skip — and a step number is the reading order of the machine.\n detail: state.step ? `step ${state.step}` : undefined,\n })),\n // The implied rail is DRAWN, so it is published. Leaving it out gave the\n // text equivalent the exceptions and none of the spine: a five-state\n // machine reading as four disconnected states and one error path.\n edges: [\n ...implied.map((edge) => ({ from: edge.from, to: edge.to })),\n ...transitions.map((transition) => ({\n from: transition.from,\n to: transition.to,\n label: [transition.label, transition.note].filter(Boolean).join(' — ') || undefined,\n })),\n ],\n bands,\n legend: stateLegend(\n resolveLegend(used, spec.meta.legend?.mode, KINDS, spec.meta.legend?.entries),\n spec.meta.legend?.entries,\n ),\n }\n}\n\nexport default LifecycleFigure\n"],"mappings":"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","names":[]}
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useId, useMemo } from "react";
4
+ import { liveEdges, useSpecIdentity, warnUnknownRef } from "../lib/dev.js";
4
5
  import { DiagramFrame } from "../lib/frame.js";
5
6
  import { round, textWidth, TYPE } from "../lib/geometry.js";
6
7
  import { resolveLegend, variantLegend } from "../lib/legend.js";
@@ -17,6 +18,7 @@ function SequenceFigure({
17
18
  onSelectNode
18
19
  }) {
19
20
  const uid = useId().replace(/:/g, "");
21
+ useSpecIdentity(spec, "SequenceFigure");
20
22
  const model = useMemo(
21
23
  () => buildModel(spec, uid, activeIds, onSelectNode),
22
24
  [spec, uid, activeIds, onSelectNode]
@@ -54,9 +56,10 @@ function buildModel(spec, _uid, activeIds, onSelectNode) {
54
56
  centre.set(participant.id, x + colW / 2);
55
57
  return { participant, box: { x, y: HEAD.y, w: colW, h: HEAD.h } };
56
58
  });
59
+ const messages = liveEdges("SequenceFigure", spec.messages, (id) => centre.has(id));
57
60
  const lastY = Math.max(
58
61
  HEAD.y + HEAD.h + 80,
59
- ...spec.messages.map((message) => message.y),
62
+ ...messages.map((message) => message.y),
60
63
  ...(spec.activations ?? []).map((activation) => activation.to),
61
64
  ...(spec.segments ?? []).map((segment) => segment.to)
62
65
  );
@@ -119,7 +122,7 @@ function buildModel(spec, _uid, activeIds, onSelectNode) {
119
122
  `act-${index}`
120
123
  );
121
124
  }),
122
- spec.messages.map((message, index) => {
125
+ messages.map((message, index) => {
123
126
  const from = centre.get(message.from);
124
127
  const to = centre.get(message.to);
125
128
  if (from === void 0 || to === void 0) return null;
@@ -154,7 +157,7 @@ function buildModel(spec, _uid, activeIds, onSelectNode) {
154
157
  },
155
158
  participant.id
156
159
  )),
157
- spec.messages.map((message, index) => {
160
+ messages.map((message, index) => {
158
161
  const from = centre.get(message.from);
159
162
  const to = centre.get(message.to);
160
163
  if (from === void 0 || to === void 0 || !message.label) return null;
@@ -171,8 +174,10 @@ function buildModel(spec, _uid, activeIds, onSelectNode) {
171
174
  })
172
175
  ] });
173
176
  const used = [
174
- ...new Set(spec.messages.map((message) => message.variant ?? "default"))
177
+ ...new Set(messages.map((message) => message.variant ?? "default"))
175
178
  ];
179
+ const across = (from, to) => messages.filter((message) => message.y >= from && message.y <= to && message.label).map((message) => message.label);
180
+ const named = new Map(spec.participants.map((p) => [p.id, p.label]));
176
181
  return {
177
182
  extent: { x: 0, y: 0, w: width, h: lifelineBottom + 24 },
178
183
  artwork,
@@ -182,11 +187,36 @@ function buildModel(spec, _uid, activeIds, onSelectNode) {
182
187
  sublabel: participant.sublabel,
183
188
  kind: participant.type
184
189
  })),
185
- edges: spec.messages.map((message) => ({
190
+ edges: messages.map((message) => ({
186
191
  from: message.from,
187
192
  to: message.to,
188
193
  label: message.label
189
194
  })),
195
+ // Segments band the TIME axis and activations sit on a lifeline, so neither
196
+ // groups the participants; both are statements the picture makes and the
197
+ // list did not carry at all.
198
+ notes: [
199
+ ...(spec.segments ?? []).map((segment) => {
200
+ const held = across(segment.from, segment.to);
201
+ return held.length > 0 ? `Phase ${segment.label} covers ${held.join(", ")}.` : `Phase ${segment.label} covers no message this figure labels.`;
202
+ }),
203
+ ...(spec.activations ?? []).flatMap((activation) => {
204
+ const who = named.get(activation.participant);
205
+ if (who === void 0) {
206
+ warnUnknownRef(
207
+ "SequenceFigure",
208
+ "activations[].participant",
209
+ activation.participant,
210
+ "the bar is drawn on no lifeline at all"
211
+ );
212
+ return [];
213
+ }
214
+ const held = across(activation.from, activation.to);
215
+ if (held.length > 1) return [`${who} is busy from ${held[0]} to ${held.at(-1)}.`];
216
+ if (held.length === 1) return [`${who} is busy during ${held[0]}.`];
217
+ return [`${who} is busy for a span none of the messages name.`];
218
+ })
219
+ ],
190
220
  legend: variantLegend(
191
221
  resolveLegend(used, spec.meta.legend?.mode, VARIANTS, spec.meta.legend?.entries),
192
222
  spec.meta.legend?.entries
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/diagrams/SequenceFigure/SequenceFigure.tsx"],"sourcesContent":["'use client'\n\nimport { useId, useMemo } from 'react'\nimport { DiagramFrame, type FigureChrome, type FigureModel } from '../lib/frame'\nimport { round, textWidth, TYPE, type Box } from '../lib/geometry'\nimport { resolveLegend, variantLegend } from '../lib/legend'\nimport { EdgeLabel, NodePlate } from '../lib/marks'\nimport type { SequenceSpec, Variant } from '../spec'\n\nconst HEAD = { y: 44, h: 62, w: 148, gap: 40 }\nconst VARIANTS: (Variant | 'return')[] = ['default', 'emphasis', 'security', 'dashed', 'return']\n\nexport interface SequenceFigureProps extends FigureChrome {\n spec: SequenceSpec\n}\n\n/**\n * A call chain over time: who asks whom, in what order, and what comes back.\n *\n * The one diagram type here whose vertical axis MEANS something. Every other\n * figure's y is layout; a sequence's y is time, which is why a message carries\n * an explicit `y` rather than an index — two calls 8 units apart happened\n * together, and two 200 apart did not, and an evenly spaced list of messages\n * would erase that distinction while looking tidier.\n *\n * **Lifelines** are hairlines, not solid rules, because they are the axis\n * rather than the content. A lifeline drawn at the weight of a message is a\n * diagram where seven vertical lines compete with twelve horizontal ones.\n *\n * **Activation bars** say who is BUSY, which is the fact a sequence diagram\n * carries that a list of calls does not — the third participant's bar\n * overlapping the second's is the reason to draw them at all.\n *\n * **Segments** band the axis into phases — request, fallback, response —\n * printed as a rule with a mono caption rather than as a tinted panel, for the\n * same reason architecture boundaries are: a second ground inside the figure\n * would sit under every message label's mask.\n *\n * A `return` message is dashed AND takes an open arrowhead. Two signals rather\n * than one, because the reply is the thing a reader most often needs to pick\n * out of a dense trace, and a dash alone is doing the same work as `dashed`\n * already does for an asynchronous call.\n */\nexport function SequenceFigure({\n spec,\n className,\n legend = 'auto',\n cards = true,\n heading = true,\n activeIds,\n onSelectNode,\n}: SequenceFigureProps) {\n const uid = useId().replace(/:/g, '')\n const model = useMemo(\n () => buildModel(spec, uid, activeIds, onSelectNode),\n [spec, uid, activeIds, onSelectNode],\n )\n\n return (\n <DiagramFrame\n meta={spec.meta}\n model={model}\n cards={spec.cards}\n showCards={cards}\n heading={heading}\n legend={legend}\n className={className}\n activeIds={activeIds}\n onSelectNode={onSelectNode}\n />\n )\n}\n\nfunction buildModel(\n spec: SequenceSpec,\n _uid: string,\n activeIds: string[] | undefined,\n onSelectNode: ((id: string) => void) | undefined,\n): FigureModel {\n // A participant's box has to hold its own name. `spread` lets the widest\n // label set the pitch for every column, which is the layout archify reaches\n // for when a fixed 86-unit column would clip a meaningful name — and\n // shortening the name instead is not a repair, it is a different diagram.\n const natural = Math.max(\n HEAD.w,\n ...spec.participants.map(\n (participant) =>\n Math.max(\n textWidth(participant.label, TYPE.label),\n textWidth(participant.sublabel ?? '', TYPE.sub),\n ) + 44,\n ),\n )\n const colW = spec.meta.column_fit === 'spread' ? natural : HEAD.w\n const pitch = colW + HEAD.gap\n\n const centre = new Map<string, number>()\n const heads = spec.participants.map((participant, index) => {\n const x = 40 + index * pitch\n centre.set(participant.id, x + colW / 2)\n return { participant, box: { x, y: HEAD.y, w: colW, h: HEAD.h } as Box }\n })\n\n const lastY = Math.max(\n HEAD.y + HEAD.h + 80,\n ...spec.messages.map((message) => message.y),\n ...(spec.activations ?? []).map((activation) => activation.to),\n ...(spec.segments ?? []).map((segment) => segment.to),\n )\n const lifelineBottom = lastY + 42\n\n const active = activeIds && activeIds.length > 0 ? new Set(activeIds) : null\n const width = 40 + spec.participants.length * pitch - HEAD.gap + 40\n\n const artwork = (\n <>\n {/* Segments first: they are the ground the whole exchange happens on. */}\n {(spec.segments ?? []).map((segment, index) => (\n <g key={`segment-${index}`} aria-hidden=\"true\">\n <path\n d={`M 20 ${round(segment.from)} H ${round(width - 20)}`}\n className=\"stroke-(--diagram-rule) [stroke-width:1] [stroke-dasharray:2_4]\"\n />\n <rect\n x={20}\n y={round(segment.from - 7)}\n width={round(textWidth(segment.label, TYPE.band) + 14)}\n height={14}\n className=\"fill-(--diagram-surface)\"\n />\n <text\n x={27}\n y={round(segment.from + 3.5)}\n className=\"fill-(--diagram-ink-3) font-mono [font-size:10.5px] [letter-spacing:0.08em] uppercase\"\n >\n {segment.label}\n </text>\n </g>\n ))}\n\n {/* Lifelines. */}\n {heads.map(({ participant, box }) => (\n <path\n key={`life-${participant.id}`}\n d={`M ${round(box.x + colW / 2)} ${round(box.y + box.h)} V ${round(lifelineBottom)}`}\n className=\"stroke-(--diagram-rule) [stroke-width:1]\"\n aria-hidden=\"true\"\n />\n ))}\n\n {/* Activation bars. */}\n {(spec.activations ?? []).map((activation, index) => {\n const x = centre.get(activation.participant)\n if (x === undefined) return null\n return (\n <rect\n key={`act-${index}`}\n x={round(x - 5)}\n y={round(activation.from)}\n width={10}\n height={round(Math.max(6, activation.to - activation.from))}\n rx={2}\n className=\"fill-(--diagram-node-2) stroke-(--diagram-rule)\"\n strokeWidth={1}\n aria-hidden=\"true\"\n />\n )\n })}\n\n {/* Messages. */}\n {spec.messages.map((message, index) => {\n const from = centre.get(message.from)\n const to = centre.get(message.to)\n if (from === undefined || to === undefined) return null\n const forward = to >= from\n const inset = 6\n const x1 = forward ? from + inset : from - inset\n const x2 = forward ? to - inset : to + inset\n return (\n <path\n key={message.id ?? `msg-${index}`}\n d={`M ${round(x1)} ${round(message.y)} H ${round(x2)}`}\n fill=\"none\"\n data-edge={message.id}\n markerEnd={`url(#${_uid}-${messageMarker(message.variant)})`}\n className={messageClasses(message.variant)}\n {...messageStroke(message.variant)}\n />\n )\n })}\n\n {/* Participants, over the lines that end at them. */}\n {heads.map(({ participant, box }) => (\n <NodePlate\n key={participant.id}\n nodeId={participant.id}\n box={box}\n label={participant.label}\n sublabel={participant.sublabel}\n kind={participant.type}\n active={active?.has(participant.id)}\n dimmed={active ? !active.has(participant.id) : false}\n onSelect={onSelectNode ? () => onSelectNode(participant.id) : undefined}\n />\n ))}\n\n {/* Message wording last, so its mask covers every line. */}\n {spec.messages.map((message, index) => {\n const from = centre.get(message.from)\n const to = centre.get(message.to)\n if (from === undefined || to === undefined || !message.label) return null\n return (\n <EdgeLabel\n key={`label-${message.id ?? index}`}\n x={(from + to) / 2}\n y={message.y}\n axis=\"x\"\n text={message.label}\n />\n )\n })}\n </>\n )\n\n const used = [\n ...new Set(spec.messages.map((message) => message.variant ?? 'default')),\n ] as (Variant | 'return')[]\n\n return {\n extent: { x: 0, y: 0, w: width, h: lifelineBottom + 24 },\n artwork,\n nodes: spec.participants.map((participant) => ({\n id: participant.id,\n label: participant.label,\n sublabel: participant.sublabel,\n kind: participant.type,\n })),\n edges: spec.messages.map((message) => ({\n from: message.from,\n to: message.to,\n label: message.label,\n })),\n legend: variantLegend(\n resolveLegend(used, spec.meta.legend?.mode, VARIANTS, spec.meta.legend?.entries),\n spec.meta.legend?.entries,\n ),\n }\n}\n\n/** Which arrowhead a message ends in. */\nfunction messageMarker(\n variant: SequenceSpec['messages'][number]['variant'],\n): 'arrow' | 'arrow-strong' | 'arrow-open' {\n if (variant === 'emphasis') return 'arrow-strong'\n if (variant === 'return') return 'arrow-open'\n return 'arrow'\n}\n\n/** A message's colour. Geometry goes through attributes — see `edgeClasses`. */\nfunction messageClasses(variant: SequenceSpec['messages'][number]['variant']): string {\n switch (variant) {\n case 'emphasis':\n return 'stroke-(--diagram-line-strong)'\n case 'return':\n case 'dashed':\n return 'stroke-(--diagram-line-soft)'\n default:\n return 'stroke-(--diagram-line)'\n }\n}\n\n/** A message's width and dash pattern. Heavier than a map's line: it IS the content. */\nfunction messageStroke(variant: SequenceSpec['messages'][number]['variant']): {\n strokeWidth: number\n strokeDasharray?: string\n} {\n switch (variant) {\n case 'emphasis':\n return { strokeWidth: 2 }\n case 'security':\n return { strokeWidth: 1.4, strokeDasharray: '6 3' }\n case 'return':\n return { strokeWidth: 1.2, strokeDasharray: '4 3.5' }\n case 'dashed':\n return { strokeWidth: 1.2, strokeDasharray: '3 3' }\n default:\n return { strokeWidth: 1.4 }\n }\n}\n\nexport default SequenceFigure\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/diagrams/SequenceFigure/SequenceFigure.tsx"],"sourcesContent":["'use client'\n\nimport { useId, useMemo } from 'react'\nimport { liveEdges, useSpecIdentity, warnUnknownRef } from '../lib/dev'\nimport { DiagramFrame, type FigureChrome, type FigureModel } from '../lib/frame'\nimport { round, textWidth, TYPE, type Box } from '../lib/geometry'\nimport { resolveLegend, variantLegend } from '../lib/legend'\nimport { EdgeLabel, NodePlate } from '../lib/marks'\nimport type { SequenceSpec, Variant } from '../spec'\n\nconst HEAD = { y: 44, h: 62, w: 148, gap: 40 }\nconst VARIANTS: (Variant | 'return')[] = ['default', 'emphasis', 'security', 'dashed', 'return']\n\nexport interface SequenceFigureProps extends FigureChrome {\n spec: SequenceSpec\n}\n\n/**\n * A call chain over time: who asks whom, in what order, and what comes back.\n *\n * The one diagram type here whose vertical axis MEANS something. Every other\n * figure's y is layout; a sequence's y is time, which is why a message carries\n * an explicit `y` rather than an index — two calls 8 units apart happened\n * together, and two 200 apart did not, and an evenly spaced list of messages\n * would erase that distinction while looking tidier.\n *\n * **Lifelines** are hairlines, not solid rules, because they are the axis\n * rather than the content. A lifeline drawn at the weight of a message is a\n * diagram where seven vertical lines compete with twelve horizontal ones.\n *\n * **Activation bars** say who is BUSY, which is the fact a sequence diagram\n * carries that a list of calls does not — the third participant's bar\n * overlapping the second's is the reason to draw them at all.\n *\n * **Segments** band the axis into phases — request, fallback, response —\n * printed as a rule with a mono caption rather than as a tinted panel, for the\n * same reason architecture boundaries are: a second ground inside the figure\n * would sit under every message label's mask.\n *\n * A `return` message is dashed AND takes an open arrowhead. Two signals rather\n * than one, because the reply is the thing a reader most often needs to pick\n * out of a dense trace, and a dash alone is doing the same work as `dashed`\n * already does for an asynchronous call.\n */\nexport function SequenceFigure({\n spec,\n className,\n legend = 'auto',\n cards = true,\n heading = true,\n activeIds,\n onSelectNode,\n}: SequenceFigureProps) {\n const uid = useId().replace(/:/g, '')\n useSpecIdentity(spec, 'SequenceFigure')\n const model = useMemo(\n () => buildModel(spec, uid, activeIds, onSelectNode),\n [spec, uid, activeIds, onSelectNode],\n )\n\n return (\n <DiagramFrame\n meta={spec.meta}\n model={model}\n cards={spec.cards}\n showCards={cards}\n heading={heading}\n legend={legend}\n className={className}\n activeIds={activeIds}\n onSelectNode={onSelectNode}\n />\n )\n}\n\nfunction buildModel(\n spec: SequenceSpec,\n _uid: string,\n activeIds: string[] | undefined,\n onSelectNode: ((id: string) => void) | undefined,\n): FigureModel {\n // A participant's box has to hold its own name. `spread` lets the widest\n // label set the pitch for every column, which is the layout archify reaches\n // for when a fixed 86-unit column would clip a meaningful name — and\n // shortening the name instead is not a repair, it is a different diagram.\n const natural = Math.max(\n HEAD.w,\n ...spec.participants.map(\n (participant) =>\n Math.max(\n textWidth(participant.label, TYPE.label),\n textWidth(participant.sublabel ?? '', TYPE.sub),\n ) + 44,\n ),\n )\n const colW = spec.meta.column_fit === 'spread' ? natural : HEAD.w\n const pitch = colW + HEAD.gap\n\n const centre = new Map<string, number>()\n const heads = spec.participants.map((participant, index) => {\n const x = 40 + index * pitch\n centre.set(participant.id, x + colW / 2)\n return { participant, box: { x, y: HEAD.y, w: colW, h: HEAD.h } as Box }\n })\n\n // A message to a participant nothing declares has no column to be drawn\n // between, so the artwork returned null for it and the summary list went on\n // reporting a call to a raw id — the picture and its text equivalent\n // disagreeing about what the exchange contains. Both halves read this.\n const messages = liveEdges('SequenceFigure', spec.messages, (id) => centre.has(id))\n\n const lastY = Math.max(\n HEAD.y + HEAD.h + 80,\n ...messages.map((message) => message.y),\n ...(spec.activations ?? []).map((activation) => activation.to),\n ...(spec.segments ?? []).map((segment) => segment.to),\n )\n const lifelineBottom = lastY + 42\n\n const active = activeIds && activeIds.length > 0 ? new Set(activeIds) : null\n const width = 40 + spec.participants.length * pitch - HEAD.gap + 40\n\n const artwork = (\n <>\n {/* Segments first: they are the ground the whole exchange happens on. */}\n {(spec.segments ?? []).map((segment, index) => (\n <g key={`segment-${index}`} aria-hidden=\"true\">\n <path\n d={`M 20 ${round(segment.from)} H ${round(width - 20)}`}\n className=\"stroke-(--diagram-rule) [stroke-width:1] [stroke-dasharray:2_4]\"\n />\n <rect\n x={20}\n y={round(segment.from - 7)}\n width={round(textWidth(segment.label, TYPE.band) + 14)}\n height={14}\n className=\"fill-(--diagram-surface)\"\n />\n <text\n x={27}\n y={round(segment.from + 3.5)}\n className=\"fill-(--diagram-ink-3) font-mono [font-size:10.5px] [letter-spacing:0.08em] uppercase\"\n >\n {segment.label}\n </text>\n </g>\n ))}\n\n {/* Lifelines. */}\n {heads.map(({ participant, box }) => (\n <path\n key={`life-${participant.id}`}\n d={`M ${round(box.x + colW / 2)} ${round(box.y + box.h)} V ${round(lifelineBottom)}`}\n className=\"stroke-(--diagram-rule) [stroke-width:1]\"\n aria-hidden=\"true\"\n />\n ))}\n\n {/* Activation bars. */}\n {(spec.activations ?? []).map((activation, index) => {\n const x = centre.get(activation.participant)\n if (x === undefined) return null\n\n return (\n <rect\n key={`act-${index}`}\n x={round(x - 5)}\n y={round(activation.from)}\n width={10}\n height={round(Math.max(6, activation.to - activation.from))}\n rx={2}\n className=\"fill-(--diagram-node-2) stroke-(--diagram-rule)\"\n strokeWidth={1}\n aria-hidden=\"true\"\n />\n )\n })}\n\n {/* Messages. */}\n {messages.map((message, index) => {\n const from = centre.get(message.from)\n const to = centre.get(message.to)\n if (from === undefined || to === undefined) return null\n const forward = to >= from\n const inset = 6\n const x1 = forward ? from + inset : from - inset\n const x2 = forward ? to - inset : to + inset\n return (\n <path\n key={message.id ?? `msg-${index}`}\n d={`M ${round(x1)} ${round(message.y)} H ${round(x2)}`}\n fill=\"none\"\n data-edge={message.id}\n markerEnd={`url(#${_uid}-${messageMarker(message.variant)})`}\n className={messageClasses(message.variant)}\n {...messageStroke(message.variant)}\n />\n )\n })}\n\n {/* Participants, over the lines that end at them. */}\n {heads.map(({ participant, box }) => (\n <NodePlate\n key={participant.id}\n nodeId={participant.id}\n box={box}\n label={participant.label}\n sublabel={participant.sublabel}\n kind={participant.type}\n active={active?.has(participant.id)}\n dimmed={active ? !active.has(participant.id) : false}\n onSelect={onSelectNode ? () => onSelectNode(participant.id) : undefined}\n />\n ))}\n\n {/* Message wording last, so its mask covers every line. */}\n {messages.map((message, index) => {\n const from = centre.get(message.from)\n const to = centre.get(message.to)\n if (from === undefined || to === undefined || !message.label) return null\n return (\n <EdgeLabel\n key={`label-${message.id ?? index}`}\n x={(from + to) / 2}\n y={message.y}\n axis=\"x\"\n text={message.label}\n />\n )\n })}\n </>\n )\n\n const used = [\n ...new Set(messages.map((message) => message.variant ?? 'default')),\n ] as (Variant | 'return')[]\n\n // The messages a span of the axis holds, which is what both a segment caption\n // and an activation bar are actually saying. Naming them by the calls inside\n // them rather than by their y bounds is the only version of that fact a\n // reader who cannot see the axis can use.\n const across = (from: number, to: number) =>\n messages\n .filter((message) => message.y >= from && message.y <= to && message.label)\n .map((message) => message.label as string)\n\n const named = new Map(spec.participants.map((p) => [p.id, p.label]))\n\n return {\n extent: { x: 0, y: 0, w: width, h: lifelineBottom + 24 },\n artwork,\n nodes: spec.participants.map((participant) => ({\n id: participant.id,\n label: participant.label,\n sublabel: participant.sublabel,\n kind: participant.type,\n })),\n edges: messages.map((message) => ({\n from: message.from,\n to: message.to,\n label: message.label,\n })),\n // Segments band the TIME axis and activations sit on a lifeline, so neither\n // groups the participants; both are statements the picture makes and the\n // list did not carry at all.\n notes: [\n ...(spec.segments ?? []).map((segment) => {\n const held = across(segment.from, segment.to)\n return held.length > 0\n ? `Phase ${segment.label} covers ${held.join(', ')}.`\n : `Phase ${segment.label} covers no message this figure labels.`\n }),\n ...(spec.activations ?? []).flatMap((activation) => {\n const who = named.get(activation.participant)\n if (who === undefined) {\n warnUnknownRef(\n 'SequenceFigure',\n 'activations[].participant',\n activation.participant,\n 'the bar is drawn on no lifeline at all',\n )\n return []\n }\n const held = across(activation.from, activation.to)\n if (held.length > 1) return [`${who} is busy from ${held[0]} to ${held.at(-1)}.`]\n if (held.length === 1) return [`${who} is busy during ${held[0]}.`]\n return [`${who} is busy for a span none of the messages name.`]\n }),\n ],\n legend: variantLegend(\n resolveLegend(used, spec.meta.legend?.mode, VARIANTS, spec.meta.legend?.entries),\n spec.meta.legend?.entries,\n ),\n }\n}\n\n/** Which arrowhead a message ends in. */\nfunction messageMarker(\n variant: SequenceSpec['messages'][number]['variant'],\n): 'arrow' | 'arrow-strong' | 'arrow-open' {\n if (variant === 'emphasis') return 'arrow-strong'\n if (variant === 'return') return 'arrow-open'\n return 'arrow'\n}\n\n/** A message's colour. Geometry goes through attributes — see `edgeClasses`. */\nfunction messageClasses(variant: SequenceSpec['messages'][number]['variant']): string {\n switch (variant) {\n case 'emphasis':\n return 'stroke-(--diagram-line-strong)'\n case 'return':\n case 'dashed':\n return 'stroke-(--diagram-line-soft)'\n default:\n return 'stroke-(--diagram-line)'\n }\n}\n\n/** A message's width and dash pattern. Heavier than a map's line: it IS the content. */\nfunction messageStroke(variant: SequenceSpec['messages'][number]['variant']): {\n strokeWidth: number\n strokeDasharray?: string\n} {\n switch (variant) {\n case 'emphasis':\n return { strokeWidth: 2 }\n case 'security':\n return { strokeWidth: 1.4, strokeDasharray: '6 3' }\n case 'return':\n return { strokeWidth: 1.2, strokeDasharray: '4 3.5' }\n case 'dashed':\n return { strokeWidth: 1.2, strokeDasharray: '3 3' }\n default:\n return { strokeWidth: 1.4 }\n }\n}\n\nexport default SequenceFigure\n"],"mappings":"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","names":[]}
@@ -27,7 +27,9 @@ interface WorkflowFigureProps extends FigureChrome {
27
27
  * between two consecutive ids on it is drawn at the emphasis weight whatever
28
28
  * its own variant says, which is the one thing that turns fourteen boxes and
29
29
  * nineteen arrows into a diagram with a subject. An `error` role goes the other
30
- * way — dashed and soft — so the exception path recedes without disappearing.
30
+ * way — dashed and soft — so the exception path recedes without disappearing,
31
+ * and so does `async`. `branch` adds no stroke of its own: a branch is an edge
32
+ * the main path does not list, and that is already the difference on the page.
31
33
  */
32
34
  declare function WorkflowFigure({ spec, className, legend, cards, heading, activeIds, onSelectNode, }: WorkflowFigureProps): react.JSX.Element;
33
35
 
@@ -1,12 +1,18 @@
1
1
  "use client";
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useId, useMemo } from "react";
4
+ import { liveEdges, useSpecIdentity, warnUnknownLane } from "../lib/dev.js";
4
5
  import { DiagramFrame } from "../lib/frame.js";
5
6
  import { inflate, round, textWidth, TYPE, union, wrapText } from "../lib/geometry.js";
6
7
  import { resolveLegend, variantLegend } from "../lib/legend.js";
7
8
  import { BandFrame, NodePlate, PLATE, plateHeight } from "../lib/marks.js";
8
9
  import { renderWires, wiresFor } from "../lib/wires.js";
9
10
  const VARIANTS = ["default", "emphasis", "security", "dashed"];
11
+ const ROLE_VARIANT = {
12
+ async: "dashed",
13
+ error: "dashed"
14
+ };
15
+ const variantOf = (edge) => edge.variant ?? (edge.role ? ROLE_VARIANT[edge.role] : void 0);
10
16
  const GRID = {
11
17
  /** The gutter the lane names live in. */
12
18
  railW: 118,
@@ -26,6 +32,7 @@ function WorkflowFigure({
26
32
  onSelectNode
27
33
  }) {
28
34
  const uid = useId().replace(/:/g, "");
35
+ useSpecIdentity(spec, "WorkflowFigure");
29
36
  const model = useMemo(
30
37
  () => buildModel(spec, uid, activeIds, onSelectNode),
31
38
  [spec, uid, activeIds, onSelectNode]
@@ -55,12 +62,18 @@ const spanBox = (fromCol, toCol, y, h) => ({
55
62
  function buildModel(spec, uid, activeIds, onSelectNode) {
56
63
  const laneIndex = new Map(spec.lanes.map((lane, index) => [lane.id, index]));
57
64
  const laneY = (index) => GRID.originY + index * (GRID.laneH + GRID.laneGap);
65
+ const laneOf = (id, owner) => {
66
+ const index = laneIndex.get(id);
67
+ if (index !== void 0) return index;
68
+ warnUnknownLane("WorkflowFigure", owner, id);
69
+ return 0;
70
+ };
58
71
  const boxes = /* @__PURE__ */ new Map();
59
72
  const placed = spec.nodes.map((node) => {
60
73
  const w = node.width ?? GRID.colW;
61
74
  const lines = wrapText(node.label, TYPE.label, w - PLATE.padX * 2, 2);
62
75
  const h = plateHeight(lines.length, Boolean(node.sublabel), true, node.height ?? 0);
63
- const index = laneIndex.get(node.lane) ?? 0;
76
+ const index = laneOf(node.lane, node.id);
64
77
  const box = {
65
78
  x: colX(node.col),
66
79
  y: laneY(index) + (GRID.laneH - h) / 2 + (node.yOffset ?? 0),
@@ -74,9 +87,12 @@ function buildModel(spec, uid, activeIds, onSelectNode) {
74
87
  for (let index = 0; index < (spec.mainPath?.length ?? 0) - 1; index += 1) {
75
88
  mainPairs.add(`${spec.mainPath[index]}\u2192${spec.mainPath[index + 1]}`);
76
89
  }
77
- const wires = wiresFor(spec.edges, boxes, (edge) => {
90
+ const edges = liveEdges("WorkflowFigure", spec.edges, (id) => boxes.has(id));
91
+ const wires = wiresFor(edges, boxes, (edge) => {
78
92
  const onMain = mainPairs.has(`${edge.from}\u2192${edge.to}`);
93
+ const variant = variantOf(edge);
79
94
  return {
95
+ edge: variant === edge.variant ? edge : { ...edge, variant },
80
96
  weight: onMain ? 2.1 : void 0,
81
97
  open: edge.role === "return"
82
98
  };
@@ -84,15 +100,15 @@ function buildModel(spec, uid, activeIds, onSelectNode) {
84
100
  const { paths, labels } = renderWires(wires, uid);
85
101
  const laneBoxes = spec.lanes.map((lane, index) => {
86
102
  const inside = placed.filter((entry) => entry.lane === index).map((entry) => entry.box);
87
- const span = union(inside);
88
- const left2 = span ? span.x - 14 : GRID.railW - 14;
103
+ const span2 = union(inside);
104
+ const left2 = span2 ? span2.x - 14 : GRID.railW - 14;
89
105
  return {
90
106
  lane,
91
107
  index,
92
108
  box: {
93
109
  x: left2,
94
110
  y: laneY(index) - 8,
95
- w: (span ? span.x + span.w + 14 : colX(0) + GRID.colW) - left2,
111
+ w: (span2 ? span2.x + span2.w + 14 : colX(0) + GRID.colW) - left2,
96
112
  h: GRID.laneH + 16
97
113
  }
98
114
  };
@@ -109,26 +125,30 @@ function buildModel(spec, uid, activeIds, onSelectNode) {
109
125
  );
110
126
  const active = activeIds && activeIds.length > 0 ? new Set(activeIds) : null;
111
127
  const artwork = /* @__PURE__ */ jsxs(Fragment, { children: [
112
- (spec.phases ?? []).length > 0 && /* @__PURE__ */ jsx(
113
- "path",
114
- {
115
- d: `M ${round(GRID.railW)} ${round(headerY + 8)} H ${round(extent.x + extent.w - 20)}`,
116
- className: "stroke-(--diagram-rule)",
117
- strokeWidth: 1,
118
- "aria-hidden": "true"
119
- }
120
- ),
121
- (spec.phases ?? []).map((phase) => /* @__PURE__ */ jsx(
122
- "text",
123
- {
124
- x: round(colX(phase.fromCol)),
125
- y: round(headerY),
126
- className: "fill-(--diagram-ink-3) font-mono [font-size:9px] [letter-spacing:0.14em] uppercase",
127
- "aria-hidden": "true",
128
- children: phase.label
129
- },
130
- phase.id
131
- )),
128
+ (spec.phases ?? []).map((phase) => {
129
+ const span2 = spanBox(phase.fromCol, Math.max(phase.fromCol, phase.toCol), headerY + 8, 0);
130
+ const dashed = phase.variant === "security" || phase.variant === "dashed";
131
+ return /* @__PURE__ */ jsxs("g", { "data-phase": phase.id, "aria-hidden": "true", children: [
132
+ /* @__PURE__ */ jsx(
133
+ "path",
134
+ {
135
+ d: `M ${round(span2.x)} ${round(span2.y)} H ${round(span2.x + span2.w)}`,
136
+ className: phase.variant === "emphasis" ? "stroke-(--diagram-rule-hard)" : "stroke-(--diagram-rule)",
137
+ strokeWidth: phase.variant === "emphasis" ? 1.6 : 1,
138
+ strokeDasharray: dashed ? "7 4" : void 0
139
+ }
140
+ ),
141
+ /* @__PURE__ */ jsx(
142
+ "text",
143
+ {
144
+ x: round(span2.x),
145
+ y: round(headerY),
146
+ className: "fill-(--diagram-ink-3) font-mono [font-size:9px] [letter-spacing:0.14em] uppercase",
147
+ children: phase.label
148
+ }
149
+ )
150
+ ] }, phase.id);
151
+ }),
132
152
  laneBoxes.map(({ lane, box }) => /* @__PURE__ */ jsxs("g", { "aria-hidden": "true", children: [
133
153
  lane.variant === "exception" && /* @__PURE__ */ jsx(
134
154
  "rect",
@@ -153,7 +173,7 @@ function buildModel(spec, uid, activeIds, onSelectNode) {
153
173
  )
154
174
  ] }, lane.id)),
155
175
  (spec.groups ?? []).map((group) => {
156
- const index = laneIndex.get(group.lane) ?? 0;
176
+ const index = laneOf(group.lane, group.id);
157
177
  return /* @__PURE__ */ jsx(
158
178
  BandFrame,
159
179
  {
@@ -182,7 +202,12 @@ function buildModel(spec, uid, activeIds, onSelectNode) {
182
202
  )),
183
203
  labels
184
204
  ] });
185
- const used = [...new Set((spec.edges ?? []).map((edge) => edge.variant ?? "default"))];
205
+ const used = [...new Set(edges.map((edge) => variantOf(edge) ?? "default"))];
206
+ const bands = spec.lanes.map((lane, index) => ({
207
+ kind: lane.variant === "exception" ? "Exception lane" : "Lane",
208
+ label: lane.label,
209
+ ids: placed.filter((entry) => entry.lane === index).sort((a, b) => a.node.col - b.node.col).map((entry) => entry.node.id)
210
+ }));
186
211
  return {
187
212
  extent,
188
213
  artwork,
@@ -192,17 +217,40 @@ function buildModel(spec, uid, activeIds, onSelectNode) {
192
217
  sublabel: node.sublabel,
193
218
  kind: node.type
194
219
  })),
195
- edges: (spec.edges ?? []).map((edge) => ({
220
+ edges: edges.map((edge) => ({
196
221
  from: edge.from,
197
222
  to: edge.to,
198
223
  label: edge.label
199
224
  })),
225
+ bands,
226
+ // A phase and a group both cut ACROSS the lanes, so neither can be the
227
+ // list's structure without publishing a node twice. They are sentences.
228
+ notes: [
229
+ ...(spec.phases ?? []).map(
230
+ (phase) => span("Phase", phase.label, phase.fromCol, phase.toCol, spec.nodes, "")
231
+ ),
232
+ ...(spec.groups ?? []).map(
233
+ (group) => span(
234
+ "Group",
235
+ group.label,
236
+ group.fromCol,
237
+ group.toCol,
238
+ spec.nodes.filter((node) => node.lane === group.lane),
239
+ ` in lane ${spec.lanes[laneIndex.get(group.lane) ?? 0]?.label ?? group.lane}`
240
+ )
241
+ )
242
+ ],
200
243
  legend: variantLegend(
201
244
  resolveLegend(used, spec.meta.legend?.mode, VARIANTS, spec.meta.legend?.entries),
202
245
  spec.meta.legend?.entries
203
246
  )
204
247
  };
205
248
  }
249
+ function span(kind, label, fromCol, toCol, nodes, where) {
250
+ const inside = nodes.filter((node) => node.col >= fromCol && node.col <= toCol);
251
+ const held = inside.length > 0 ? `: ${inside.map((node) => node.label).join(", ")}` : "";
252
+ return `${kind} ${label} covers columns ${fromCol} to ${toCol}${where}${held}.`;
253
+ }
206
254
  var WorkflowFigure_default = WorkflowFigure;
207
255
  export {
208
256
  WorkflowFigure,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/diagrams/WorkflowFigure/WorkflowFigure.tsx"],"sourcesContent":["'use client'\n\nimport { useId, useMemo } from 'react'\nimport { DiagramFrame, type FigureChrome, type FigureModel } from '../lib/frame'\nimport { inflate, round, textWidth, TYPE, union, wrapText, type Box } from '../lib/geometry'\nimport { resolveLegend, variantLegend } from '../lib/legend'\nimport { BandFrame, NodePlate, PLATE, plateHeight } from '../lib/marks'\nimport { renderWires, wiresFor, type Wire } from '../lib/wires'\nimport type { Variant, WorkflowEdge, WorkflowSpec } from '../spec'\n\nconst VARIANTS: (Variant | 'return')[] = ['default', 'emphasis', 'security', 'dashed']\n\nconst GRID = {\n /** The gutter the lane names live in. */\n railW: 118,\n originY: 74,\n colW: 168,\n gapX: 58,\n laneH: 92,\n laneGap: 40,\n}\n\nexport interface WorkflowFigureProps extends FigureChrome {\n spec: WorkflowSpec\n}\n\n/**\n * A process: who does what, in what order, and where it can go wrong.\n *\n * Three structures stacked on one grid, and they are three because they answer\n * three different questions a reader brings to a runbook.\n *\n * **Lanes** are rows and answer WHO. A lane marked `exception` is the one band\n * that gets a wash rather than a rule, and it earns the exception to the\n * frames-are-rules law because nothing routes across it — it IS the ground for\n * the failure path, not a frame drawn over the happy one.\n *\n * **Phases** are columns across every lane and answer WHEN. Printed as captions\n * on one header rule, because a phase is an axis label. **Groups** are frames\n * inside a single lane and answer WHAT BELONGS TOGETHER — a planning loop, an\n * evidence path.\n *\n * THE MAIN PATH IS HEAVIER, and that is the whole hierarchy. `mainPath` lists\n * the node ids a reader should be able to follow without thinking. Every edge\n * between two consecutive ids on it is drawn at the emphasis weight whatever\n * its own variant says, which is the one thing that turns fourteen boxes and\n * nineteen arrows into a diagram with a subject. An `error` role goes the other\n * way — dashed and soft — so the exception path recedes without disappearing.\n */\nexport function WorkflowFigure({\n spec,\n className,\n legend = 'auto',\n cards = true,\n heading = true,\n activeIds,\n onSelectNode,\n}: WorkflowFigureProps) {\n const uid = useId().replace(/:/g, '')\n const model = useMemo(\n () => buildModel(spec, uid, activeIds, onSelectNode),\n [spec, uid, activeIds, onSelectNode],\n )\n\n return (\n <DiagramFrame\n meta={spec.meta}\n model={model}\n cards={spec.cards}\n showCards={cards}\n heading={heading}\n legend={legend}\n className={className}\n activeIds={activeIds}\n onSelectNode={onSelectNode}\n />\n )\n}\n\n/** The x of a column's left edge, and of the whole span from `from` to `to`. */\nconst colX = (col: number) => GRID.railW + col * (GRID.colW + GRID.gapX)\nconst spanBox = (fromCol: number, toCol: number, y: number, h: number): Box => ({\n x: colX(fromCol),\n y,\n w: colX(toCol) + GRID.colW - colX(fromCol),\n h,\n})\n\nfunction buildModel(\n spec: WorkflowSpec,\n uid: string,\n activeIds: string[] | undefined,\n onSelectNode: ((id: string) => void) | undefined,\n): FigureModel {\n const laneIndex = new Map(spec.lanes.map((lane, index) => [lane.id, index]))\n const laneY = (index: number) => GRID.originY + index * (GRID.laneH + GRID.laneGap)\n\n const boxes = new Map<string, Box>()\n const placed = spec.nodes.map((node) => {\n const w = node.width ?? GRID.colW\n const lines = wrapText(node.label, TYPE.label, w - PLATE.padX * 2, 2)\n const h = plateHeight(lines.length, Boolean(node.sublabel), true, node.height ?? 0)\n const index = laneIndex.get(node.lane) ?? 0\n\n const box: Box = {\n x: colX(node.col),\n y: laneY(index) + (GRID.laneH - h) / 2 + (node.yOffset ?? 0),\n w,\n h,\n }\n boxes.set(node.id, box)\n return { node, box, lane: index }\n })\n\n // Consecutive pairs on the main path, so the edge between them can be drawn\n // at the weight that says \"follow this one\".\n const mainPairs = new Set<string>()\n for (let index = 0; index < (spec.mainPath?.length ?? 0) - 1; index += 1) {\n mainPairs.add(`${spec.mainPath![index]}→${spec.mainPath![index + 1]}`)\n }\n\n const wires = wiresFor(spec.edges, boxes, (edge: WorkflowEdge): Partial<Wire> => {\n const onMain = mainPairs.has(`${edge.from}→${edge.to}`)\n return {\n weight: onMain ? 2.1 : undefined,\n open: edge.role === 'return',\n }\n })\n const { paths, labels } = renderWires(wires, uid)\n\n // A lane's band spans what the lane actually holds. Starting it at the rail\n // instead ran the exception wash under the label gutter, which made the\n // gutter look like part of the band rather than like the axis it is.\n const laneBoxes = spec.lanes.map((lane, index) => {\n const inside = placed.filter((entry) => entry.lane === index).map((entry) => entry.box)\n const span = union(inside)\n const left = span ? span.x - 14 : GRID.railW - 14\n return {\n lane,\n index,\n box: {\n x: left,\n y: laneY(index) - 8,\n w: (span ? span.x + span.w + 14 : colX(0) + GRID.colW) - left,\n h: GRID.laneH + 16,\n } as Box,\n }\n })\n\n const drawn = [...boxes.values(), ...laneBoxes.map((entry) => entry.box)]\n const headerY = GRID.originY - 30\n\n // The frame has to hold what is printed OUTSIDE the boxes too: the lane names\n // in the gutter and the phase captions above the header rule. Sizing the\n // extent from the boxes alone clipped both — and a clipped axis label is the\n // one kind of overflow a reader reads as a broken renderer rather than as a\n // long word.\n const railTextW = Math.max(0, ...spec.lanes.map((lane) => textWidth(lane.label, 9)))\n const raw = union(drawn) ?? { x: 0, y: 0, w: 600, h: 300 }\n const left = Math.min(raw.x, GRID.railW - 26 - railTextW)\n const top = Math.min(raw.y, headerY - 14)\n const extent = inflate(\n { x: left, y: top, w: raw.x + raw.w - left, h: raw.y + raw.h - top },\n 24,\n )\n const active = activeIds && activeIds.length > 0 ? new Set(activeIds) : null\n\n const artwork = (\n <>\n {/* Phases: one header rule across the figure, with a caption per span. */}\n {(spec.phases ?? []).length > 0 && (\n <path\n d={`M ${round(GRID.railW)} ${round(headerY + 8)} H ${round(extent.x + extent.w - 20)}`}\n className=\"stroke-(--diagram-rule)\"\n strokeWidth={1}\n aria-hidden=\"true\"\n />\n )}\n {(spec.phases ?? []).map((phase) => (\n <text\n key={phase.id}\n x={round(colX(phase.fromCol))}\n y={round(headerY)}\n className=\"fill-(--diagram-ink-3) font-mono [font-size:9px] [letter-spacing:0.14em] uppercase\"\n aria-hidden=\"true\"\n >\n {phase.label}\n </text>\n ))}\n\n {/* Lanes. The exception band is the one wash in the system's diagrams. */}\n {laneBoxes.map(({ lane, box }) => (\n <g key={lane.id} aria-hidden=\"true\">\n {lane.variant === 'exception' && (\n <rect\n x={round(box.x)}\n y={round(box.y)}\n width={round(box.w)}\n height={round(box.h)}\n rx={10}\n className=\"fill-(--diagram-band) [rx:var(--radius-lg)] [ry:var(--radius-lg)]\"\n />\n )}\n <text\n x={round(GRID.railW - 26)}\n y={round(box.y + box.h / 2 + 3)}\n textAnchor=\"end\"\n className=\"fill-(--diagram-ink-3) font-mono [font-size:9px] [letter-spacing:0.14em] uppercase\"\n >\n {lane.label}\n </text>\n </g>\n ))}\n\n {/* Groups: a frame inside one lane. */}\n {(spec.groups ?? []).map((group) => {\n const index = laneIndex.get(group.lane) ?? 0\n return (\n <BandFrame\n key={group.id}\n box={spanBox(group.fromCol, group.toCol, laneY(index) - 2, GRID.laneH + 4)}\n label={group.label}\n dashed={group.variant === 'security'}\n />\n )\n })}\n\n {paths}\n\n {placed.map(({ node, box }) => (\n <NodePlate\n key={node.id}\n nodeId={node.id}\n box={box}\n label={node.label}\n sublabel={node.sublabel}\n tag={node.tag}\n kind={node.type}\n active={active?.has(node.id)}\n dimmed={active ? !active.has(node.id) : false}\n onSelect={onSelectNode ? () => onSelectNode(node.id) : undefined}\n />\n ))}\n\n {labels}\n </>\n )\n\n const used = [...new Set((spec.edges ?? []).map((edge) => edge.variant ?? 'default'))]\n\n return {\n extent,\n artwork,\n nodes: spec.nodes.map((node) => ({\n id: node.id,\n label: node.label,\n sublabel: node.sublabel,\n kind: node.type,\n })),\n edges: (spec.edges ?? []).map((edge) => ({\n from: edge.from,\n to: edge.to,\n label: edge.label,\n })),\n legend: variantLegend(\n resolveLegend(used, spec.meta.legend?.mode, VARIANTS, spec.meta.legend?.entries),\n spec.meta.legend?.entries,\n ),\n }\n}\n\nexport default WorkflowFigure\n"],"mappings":"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","names":["left"]}
1
+ {"version":3,"sources":["../../../src/diagrams/WorkflowFigure/WorkflowFigure.tsx"],"sourcesContent":["'use client'\n\nimport { useId, useMemo } from 'react'\nimport { liveEdges, useSpecIdentity, warnUnknownLane } from '../lib/dev'\nimport { DiagramFrame, type FigureBand, type FigureChrome, type FigureModel } from '../lib/frame'\nimport { inflate, round, textWidth, TYPE, union, wrapText, type Box } from '../lib/geometry'\nimport { resolveLegend, variantLegend } from '../lib/legend'\nimport { BandFrame, NodePlate, PLATE, plateHeight } from '../lib/marks'\nimport { renderWires, wiresFor, type Wire } from '../lib/wires'\nimport type { Variant, WorkflowEdge, WorkflowEdgeRole, WorkflowSpec } from '../spec'\n\nconst VARIANTS: (Variant | 'return')[] = ['default', 'emphasis', 'security', 'dashed']\n\n/**\n * What a ROLE does to the line, where the author did not say it with a variant.\n *\n * `role` is about meaning and `variant` is about drawing, and for three of the\n * five roles the two answers coincide: an asynchronous hop and a path to the\n * exception lane are both the quiet line this system already has. `main` and\n * `branch` are deliberately absent — a main edge is heavier because `mainPath`\n * lists it, and a branch is simply an edge that is not on it, so both are\n * already carried by weight rather than by a second dash pattern.\n *\n * An explicit `variant` always wins: the author drew it on purpose.\n */\nconst ROLE_VARIANT: Partial<Record<WorkflowEdgeRole, Variant>> = {\n async: 'dashed',\n error: 'dashed',\n}\n\n/** How a line is drawn, once its role has had its say. */\nconst variantOf = (edge: WorkflowEdge): Variant | undefined =>\n edge.variant ?? (edge.role ? ROLE_VARIANT[edge.role] : undefined)\n\nconst GRID = {\n /** The gutter the lane names live in. */\n railW: 118,\n originY: 74,\n colW: 168,\n gapX: 58,\n laneH: 92,\n laneGap: 40,\n}\n\nexport interface WorkflowFigureProps extends FigureChrome {\n spec: WorkflowSpec\n}\n\n/**\n * A process: who does what, in what order, and where it can go wrong.\n *\n * Three structures stacked on one grid, and they are three because they answer\n * three different questions a reader brings to a runbook.\n *\n * **Lanes** are rows and answer WHO. A lane marked `exception` is the one band\n * that gets a wash rather than a rule, and it earns the exception to the\n * frames-are-rules law because nothing routes across it — it IS the ground for\n * the failure path, not a frame drawn over the happy one.\n *\n * **Phases** are columns across every lane and answer WHEN. Printed as captions\n * on one header rule, because a phase is an axis label. **Groups** are frames\n * inside a single lane and answer WHAT BELONGS TOGETHER — a planning loop, an\n * evidence path.\n *\n * THE MAIN PATH IS HEAVIER, and that is the whole hierarchy. `mainPath` lists\n * the node ids a reader should be able to follow without thinking. Every edge\n * between two consecutive ids on it is drawn at the emphasis weight whatever\n * its own variant says, which is the one thing that turns fourteen boxes and\n * nineteen arrows into a diagram with a subject. An `error` role goes the other\n * way — dashed and soft — so the exception path recedes without disappearing,\n * and so does `async`. `branch` adds no stroke of its own: a branch is an edge\n * the main path does not list, and that is already the difference on the page.\n */\nexport function WorkflowFigure({\n spec,\n className,\n legend = 'auto',\n cards = true,\n heading = true,\n activeIds,\n onSelectNode,\n}: WorkflowFigureProps) {\n const uid = useId().replace(/:/g, '')\n useSpecIdentity(spec, 'WorkflowFigure')\n const model = useMemo(\n () => buildModel(spec, uid, activeIds, onSelectNode),\n [spec, uid, activeIds, onSelectNode],\n )\n\n return (\n <DiagramFrame\n meta={spec.meta}\n model={model}\n cards={spec.cards}\n showCards={cards}\n heading={heading}\n legend={legend}\n className={className}\n activeIds={activeIds}\n onSelectNode={onSelectNode}\n />\n )\n}\n\n/** The x of a column's left edge, and of the whole span from `from` to `to`. */\nconst colX = (col: number) => GRID.railW + col * (GRID.colW + GRID.gapX)\nconst spanBox = (fromCol: number, toCol: number, y: number, h: number): Box => ({\n x: colX(fromCol),\n y,\n w: colX(toCol) + GRID.colW - colX(fromCol),\n h,\n})\n\nfunction buildModel(\n spec: WorkflowSpec,\n uid: string,\n activeIds: string[] | undefined,\n onSelectNode: ((id: string) => void) | undefined,\n): FigureModel {\n const laneIndex = new Map(spec.lanes.map((lane, index) => [lane.id, index]))\n const laneY = (index: number) => GRID.originY + index * (GRID.laneH + GRID.laneGap)\n\n // A lane id nothing declares still resolves to a row — there is nowhere else\n // to put the box — so the only thing that separates \"I meant lane 0\" from\n // \"the renderer gave up\" is being told.\n const laneOf = (id: string, owner: string) => {\n const index = laneIndex.get(id)\n if (index !== undefined) return index\n warnUnknownLane('WorkflowFigure', owner, id)\n return 0\n }\n\n const boxes = new Map<string, Box>()\n const placed = spec.nodes.map((node) => {\n const w = node.width ?? GRID.colW\n const lines = wrapText(node.label, TYPE.label, w - PLATE.padX * 2, 2)\n const h = plateHeight(lines.length, Boolean(node.sublabel), true, node.height ?? 0)\n const index = laneOf(node.lane, node.id)\n\n const box: Box = {\n x: colX(node.col),\n y: laneY(index) + (GRID.laneH - h) / 2 + (node.yOffset ?? 0),\n w,\n h,\n }\n boxes.set(node.id, box)\n return { node, box, lane: index }\n })\n\n // Consecutive pairs on the main path, so the edge between them can be drawn\n // at the weight that says \"follow this one\".\n const mainPairs = new Set<string>()\n for (let index = 0; index < (spec.mainPath?.length ?? 0) - 1; index += 1) {\n mainPairs.add(`${spec.mainPath![index]}→${spec.mainPath![index + 1]}`)\n }\n\n const edges = liveEdges('WorkflowFigure', spec.edges, (id) => boxes.has(id))\n const wires = wiresFor(edges, boxes, (edge: WorkflowEdge): Partial<Wire> => {\n const onMain = mainPairs.has(`${edge.from}→${edge.to}`)\n const variant = variantOf(edge)\n return {\n edge: variant === edge.variant ? edge : { ...edge, variant },\n weight: onMain ? 2.1 : undefined,\n open: edge.role === 'return',\n }\n })\n const { paths, labels } = renderWires(wires, uid)\n\n // A lane's band spans what the lane actually holds. Starting it at the rail\n // instead ran the exception wash under the label gutter, which made the\n // gutter look like part of the band rather than like the axis it is.\n const laneBoxes = spec.lanes.map((lane, index) => {\n const inside = placed.filter((entry) => entry.lane === index).map((entry) => entry.box)\n const span = union(inside)\n const left = span ? span.x - 14 : GRID.railW - 14\n return {\n lane,\n index,\n box: {\n x: left,\n y: laneY(index) - 8,\n w: (span ? span.x + span.w + 14 : colX(0) + GRID.colW) - left,\n h: GRID.laneH + 16,\n } as Box,\n }\n })\n\n const drawn = [...boxes.values(), ...laneBoxes.map((entry) => entry.box)]\n const headerY = GRID.originY - 30\n\n // The frame has to hold what is printed OUTSIDE the boxes too: the lane names\n // in the gutter and the phase captions above the header rule. Sizing the\n // extent from the boxes alone clipped both — and a clipped axis label is the\n // one kind of overflow a reader reads as a broken renderer rather than as a\n // long word.\n const railTextW = Math.max(0, ...spec.lanes.map((lane) => textWidth(lane.label, 9)))\n const raw = union(drawn) ?? { x: 0, y: 0, w: 600, h: 300 }\n const left = Math.min(raw.x, GRID.railW - 26 - railTextW)\n const top = Math.min(raw.y, headerY - 14)\n const extent = inflate(\n { x: left, y: top, w: raw.x + raw.w - left, h: raw.y + raw.h - top },\n 24,\n )\n const active = activeIds && activeIds.length > 0 ? new Set(activeIds) : null\n\n const artwork = (\n <>\n {/* Phases: one rule per phase, drawn across the columns that phase\n claims. A single rule spanning the figure made `toCol` a field the\n type accepted and the picture could not show — two phases starting in\n the same column printed their captions on top of each other, and a\n phase's extent was not a thing a reader could see. */}\n {(spec.phases ?? []).map((phase) => {\n const span = spanBox(phase.fromCol, Math.max(phase.fromCol, phase.toCol), headerY + 8, 0)\n const dashed = phase.variant === 'security' || phase.variant === 'dashed'\n return (\n <g key={phase.id} data-phase={phase.id} aria-hidden=\"true\">\n <path\n d={`M ${round(span.x)} ${round(span.y)} H ${round(span.x + span.w)}`}\n className={\n phase.variant === 'emphasis'\n ? 'stroke-(--diagram-rule-hard)'\n : 'stroke-(--diagram-rule)'\n }\n strokeWidth={phase.variant === 'emphasis' ? 1.6 : 1}\n strokeDasharray={dashed ? '7 4' : undefined}\n />\n <text\n x={round(span.x)}\n y={round(headerY)}\n className=\"fill-(--diagram-ink-3) font-mono [font-size:9px] [letter-spacing:0.14em] uppercase\"\n >\n {phase.label}\n </text>\n </g>\n )\n })}\n\n {/* Lanes. The exception band is the one wash in the system's diagrams. */}\n {laneBoxes.map(({ lane, box }) => (\n <g key={lane.id} aria-hidden=\"true\">\n {lane.variant === 'exception' && (\n <rect\n x={round(box.x)}\n y={round(box.y)}\n width={round(box.w)}\n height={round(box.h)}\n rx={10}\n className=\"fill-(--diagram-band) [rx:var(--radius-lg)] [ry:var(--radius-lg)]\"\n />\n )}\n <text\n x={round(GRID.railW - 26)}\n y={round(box.y + box.h / 2 + 3)}\n textAnchor=\"end\"\n className=\"fill-(--diagram-ink-3) font-mono [font-size:9px] [letter-spacing:0.14em] uppercase\"\n >\n {lane.label}\n </text>\n </g>\n ))}\n\n {/* Groups: a frame inside one lane. */}\n {(spec.groups ?? []).map((group) => {\n const index = laneOf(group.lane, group.id)\n return (\n <BandFrame\n key={group.id}\n box={spanBox(group.fromCol, group.toCol, laneY(index) - 2, GRID.laneH + 4)}\n label={group.label}\n dashed={group.variant === 'security'}\n />\n )\n })}\n\n {paths}\n\n {placed.map(({ node, box }) => (\n <NodePlate\n key={node.id}\n nodeId={node.id}\n box={box}\n label={node.label}\n sublabel={node.sublabel}\n tag={node.tag}\n kind={node.type}\n active={active?.has(node.id)}\n dimmed={active ? !active.has(node.id) : false}\n onSelect={onSelectNode ? () => onSelectNode(node.id) : undefined}\n />\n ))}\n\n {labels}\n </>\n )\n\n const used = [...new Set(edges.map((edge) => variantOf(edge) ?? 'default'))]\n\n // The lanes ARE the list's structure, not a phrase repeated on every row.\n // WHO does a step is half of where the step sits, and it was drawn only in a\n // gutter inside artwork a screen reader is told to skip.\n const bands: FigureBand[] = spec.lanes.map((lane, index) => ({\n kind: lane.variant === 'exception' ? 'Exception lane' : 'Lane',\n label: lane.label,\n ids: placed\n .filter((entry) => entry.lane === index)\n .sort((a, b) => a.node.col - b.node.col)\n .map((entry) => entry.node.id),\n }))\n\n return {\n extent,\n artwork,\n nodes: spec.nodes.map((node) => ({\n id: node.id,\n label: node.label,\n sublabel: node.sublabel,\n kind: node.type,\n })),\n edges: edges.map((edge) => ({\n from: edge.from,\n to: edge.to,\n label: edge.label,\n })),\n bands,\n // A phase and a group both cut ACROSS the lanes, so neither can be the\n // list's structure without publishing a node twice. They are sentences.\n notes: [\n ...(spec.phases ?? []).map((phase) =>\n span('Phase', phase.label, phase.fromCol, phase.toCol, spec.nodes, ''),\n ),\n ...(spec.groups ?? []).map((group) =>\n span(\n 'Group',\n group.label,\n group.fromCol,\n group.toCol,\n spec.nodes.filter((node) => node.lane === group.lane),\n ` in lane ${spec.lanes[laneIndex.get(group.lane) ?? 0]?.label ?? group.lane}`,\n ),\n ),\n ],\n legend: variantLegend(\n resolveLegend(used, spec.meta.legend?.mode, VARIANTS, spec.meta.legend?.entries),\n spec.meta.legend?.entries,\n ),\n }\n}\n\n/** One phase or group, written out: what it covers, and what is inside it. */\nfunction span(\n kind: string,\n label: string,\n fromCol: number,\n toCol: number,\n nodes: { col: number; label: string }[],\n where: string,\n): string {\n const inside = nodes.filter((node) => node.col >= fromCol && node.col <= toCol)\n const held = inside.length > 0 ? `: ${inside.map((node) => node.label).join(', ')}` : ''\n return `${kind} ${label} covers columns ${fromCol} to ${toCol}${where}${held}.`\n}\n\nexport default WorkflowFigure\n"],"mappings":"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","names":["span","left"]}
@@ -10,6 +10,7 @@ export { DiagramExportMenu, DiagramExportMenuProps, ExportFormat, ExportResult,
10
10
  export { DiagramInspector, DiagramInspectorProps, InspectorFact, InspectorLink } from './DiagramInspector/DiagramInspector.js';
11
11
  export { DiagramMinimap, DiagramMinimapProps } from './DiagramMinimap/DiagramMinimap.js';
12
12
  export { DiagramLegend, DiagramLegendEntry, DiagramLegendProps } from './DiagramLegend/DiagramLegend.js';
13
+ export { RasterFormat, RasterOptions, SerializeOptions, downloadBlob, exportFilename, rasterize, serializeSvg } from '../lib/svg-export.js';
13
14
  export { LegendEntry, kindLegend, resolveLegend, stateLegend, variantLegend } from './lib/legend.js';
14
15
  export { FigureChrome } from './lib/frame.js';
15
16
  import 'react';
@@ -9,10 +9,20 @@ export * from "./DiagramExportMenu/DiagramExportMenu.js";
9
9
  export * from "./DiagramInspector/DiagramInspector.js";
10
10
  export * from "./DiagramMinimap/DiagramMinimap.js";
11
11
  export * from "./DiagramLegend/DiagramLegend.js";
12
+ import {
13
+ downloadBlob,
14
+ exportFilename,
15
+ rasterize,
16
+ serializeSvg
17
+ } from "../lib/svg-export.js";
12
18
  import { kindLegend, resolveLegend, stateLegend, variantLegend } from "./lib/legend.js";
13
19
  export {
20
+ downloadBlob,
21
+ exportFilename,
14
22
  kindLegend,
23
+ rasterize,
15
24
  resolveLegend,
25
+ serializeSvg,
16
26
  stateLegend,
17
27
  variantLegend
18
28
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/diagrams/index.ts"],"sourcesContent":["/**\n * @misoto22/design/diagrams — the public entry.\n *\n * Five renderers for the five diagram shapes software actually needs, plus the\n * chrome a reader explores one with. They ship from their own entry point\n * rather than from the package root for one reason: a page that renders a Badge\n * should not be paying for a routing engine. Nothing under `src/components`\n * imports anything here, so the main barrel stays the size it was.\n *\n * The specification types mirror\n * [archify](https://github.com/tt-a1i/archify)'s published JSON schemas, so a\n * document authored for that tool renders here with no translation step — in\n * this system's own monochrome terms rather than in archify's palette.\n *\n * The look ships with the rest of the package:\n *\n * import '@misoto22/design/styles.css'\n *\n * Everything below is a consumer contract. Adding an export is cheap; changing\n * or removing one is a breaking change (DESIGN-API-001).\n */\n\n// ─── Specifications ───\nexport type {\n ArchitectureBoundary,\n ArchitectureComponent,\n ArchitectureSpec,\n DataflowEdge,\n DataflowNode,\n DataflowSpec,\n DataflowStage,\n DiagramCard,\n DiagramMeta,\n DiagramSpecAny,\n DiagramView,\n EdgeBase,\n LegendMode,\n LifecycleLane,\n LifecycleSpec,\n LifecycleState,\n LifecycleStateKind,\n LifecycleTransition,\n NodeBase,\n NodeKind,\n Point,\n RouteMode,\n SequenceActivation,\n SequenceMessage,\n SequenceParticipant,\n SequenceSegment,\n SequenceSpec,\n Side,\n SourceRef,\n Variant,\n WorkflowEdge,\n WorkflowEdgeRole,\n WorkflowGroup,\n WorkflowLane,\n WorkflowNode,\n WorkflowPhase,\n WorkflowSpec,\n} from './spec'\n\n// ─── Figures ───\nexport * from './ArchitectureFigure/ArchitectureFigure'\nexport * from './WorkflowFigure/WorkflowFigure'\nexport * from './SequenceFigure/SequenceFigure'\nexport * from './DataflowFigure/DataflowFigure'\nexport * from './LifecycleFigure/LifecycleFigure'\n\n// ─── Chrome ───\nexport * from './DiagramCanvas/DiagramCanvas'\nexport * from './DiagramToolbar/DiagramToolbar'\nexport * from './DiagramExportMenu/DiagramExportMenu'\nexport * from './DiagramInspector/DiagramInspector'\nexport * from './DiagramMinimap/DiagramMinimap'\nexport * from './DiagramLegend/DiagramLegend'\n\n// ─── Building a key ───\nexport { kindLegend, resolveLegend, stateLegend, variantLegend } from './lib/legend'\nexport type { LegendEntry } from './lib/legend'\n\n// ─── Shared chrome types ───\nexport type { FigureChrome } from './lib/frame'\n"],"mappings":"AAgEA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAGd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAGd,SAAS,YAAY,eAAe,aAAa,qBAAqB;","names":[]}
1
+ {"version":3,"sources":["../../src/diagrams/index.ts"],"sourcesContent":["/**\n * @misoto22/design/diagrams — the public entry.\n *\n * Five renderers for the five diagram shapes software actually needs, plus the\n * chrome a reader explores one with. They ship from their own entry point\n * rather than from the package root for one reason: a page that renders a Badge\n * should not be paying for a routing engine. Nothing under `src/components`\n * imports anything here, so the main barrel stays the size it was.\n *\n * The specification types mirror\n * [archify](https://github.com/tt-a1i/archify)'s published JSON schemas, so a\n * document authored for that tool renders here with no translation step — in\n * this system's own monochrome terms rather than in archify's palette.\n *\n * The look ships with the rest of the package:\n *\n * import '@misoto22/design/styles.css'\n *\n * Everything below is a consumer contract. Adding an export is cheap; changing\n * or removing one is a breaking change (DESIGN-API-001).\n */\n\n// ─── Specifications ───\nexport type {\n ArchitectureBoundary,\n ArchitectureComponent,\n ArchitectureSpec,\n DataflowEdge,\n DataflowNode,\n DataflowSpec,\n DataflowStage,\n DiagramCard,\n DiagramMeta,\n DiagramSpecAny,\n DiagramView,\n EdgeBase,\n LegendMode,\n LifecycleLane,\n LifecycleSpec,\n LifecycleState,\n LifecycleStateKind,\n LifecycleTransition,\n NodeBase,\n NodeKind,\n Point,\n RouteMode,\n SequenceActivation,\n SequenceMessage,\n SequenceParticipant,\n SequenceSegment,\n SequenceSpec,\n Side,\n SourceRef,\n Variant,\n WorkflowEdge,\n WorkflowEdgeRole,\n WorkflowGroup,\n WorkflowLane,\n WorkflowNode,\n WorkflowPhase,\n WorkflowSpec,\n} from './spec'\n\n// ─── Figures ───\nexport * from './ArchitectureFigure/ArchitectureFigure'\nexport * from './WorkflowFigure/WorkflowFigure'\nexport * from './SequenceFigure/SequenceFigure'\nexport * from './DataflowFigure/DataflowFigure'\nexport * from './LifecycleFigure/LifecycleFigure'\n\n// ─── Chrome ───\nexport * from './DiagramCanvas/DiagramCanvas'\nexport * from './DiagramToolbar/DiagramToolbar'\nexport * from './DiagramExportMenu/DiagramExportMenu'\nexport * from './DiagramInspector/DiagramInspector'\nexport * from './DiagramMinimap/DiagramMinimap'\nexport * from './DiagramLegend/DiagramLegend'\n\n// ─── Taking a figure off the page ───\n/**\n * The export pipeline the menu runs, for a caller who needs a different one.\n *\n * `DiagramExportMenu` is five decisions already made — a plate behind the\n * artwork, 2× raster, a 1200 × 630 share card — and a caller who wants any of\n * them differently had nothing to build on: the serialiser is where the hard\n * half lives (resolving `var(--ink)` against the live element, so the file is\n * not an invisible rectangle), and it was private.\n */\nexport {\n downloadBlob,\n exportFilename,\n rasterize,\n serializeSvg,\n} from '../lib/svg-export'\nexport type { RasterFormat, RasterOptions, SerializeOptions } from '../lib/svg-export'\n\n// ─── Building a key ───\nexport { kindLegend, resolveLegend, stateLegend, variantLegend } from './lib/legend'\nexport type { LegendEntry } from './lib/legend'\n\n// ─── Shared chrome types ───\nexport type { FigureChrome } from './lib/frame'\n"],"mappings":"AAgEA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAGd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAYd;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAIP,SAAS,YAAY,eAAe,aAAa,qBAAqB;","names":[]}