@misoto22/design 0.6.1 → 0.8.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 (402) hide show
  1. package/AGENTS.md +87 -0
  2. package/CLAUDE.md +1 -0
  3. package/README.md +62 -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 +71 -0
  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/Combobox.md +29 -3
  28. package/dist/agent/Command.md +25 -2
  29. package/dist/agent/ComposedChart.md +26 -2
  30. package/dist/agent/ContextMenu.md +37 -4
  31. package/dist/agent/DataflowFigure.md +48 -0
  32. package/dist/agent/DatePicker.md +34 -6
  33. package/dist/agent/DescriptionList.md +68 -0
  34. package/dist/agent/Diagram.md +27 -3
  35. package/dist/agent/DiagramCanvas.md +70 -0
  36. package/dist/agent/DiagramExportMenu.md +64 -0
  37. package/dist/agent/DiagramInspector.md +72 -0
  38. package/dist/agent/DiagramLegend.md +55 -0
  39. package/dist/agent/DiagramMinimap.md +68 -0
  40. package/dist/agent/DiagramToolbar.md +69 -0
  41. package/dist/agent/Dialog.md +26 -3
  42. package/dist/agent/DropdownMenu.md +45 -6
  43. package/dist/agent/EmptyState.md +26 -1
  44. package/dist/agent/ErrorState.md +27 -2
  45. package/dist/agent/Facet.md +25 -1
  46. package/dist/agent/Field.md +45 -4
  47. package/dist/agent/FigureBand.md +24 -1
  48. package/dist/agent/FloatingIconButton.md +23 -1
  49. package/dist/agent/FunnelChart.md +23 -1
  50. package/dist/agent/Heading.md +64 -0
  51. package/dist/agent/Heatmap.md +25 -1
  52. package/dist/agent/Histogram.md +26 -2
  53. package/dist/agent/Input.md +22 -1
  54. package/dist/agent/Kbd.md +19 -1
  55. package/dist/agent/LifecycleFigure.md +49 -0
  56. package/dist/agent/LineChart.md +26 -2
  57. package/dist/agent/LinkArrow.md +20 -1
  58. package/dist/agent/Markdown.md +68 -0
  59. package/dist/agent/NativeSelect.md +23 -2
  60. package/dist/agent/NavItem.md +21 -1
  61. package/dist/agent/Pagination.md +28 -2
  62. package/dist/agent/PieChart.md +26 -2
  63. package/dist/agent/Popover.md +23 -1
  64. package/dist/agent/Progress.md +27 -3
  65. package/dist/agent/RadarChart.md +26 -2
  66. package/dist/agent/RadialChart.md +26 -3
  67. package/dist/agent/RadioGroup.md +23 -2
  68. package/dist/agent/SankeyChart.md +25 -2
  69. package/dist/agent/ScatterChart.md +27 -2
  70. package/dist/agent/ScrollArea.md +24 -3
  71. package/dist/agent/SearchableMenu.md +23 -1
  72. package/dist/agent/Select.md +29 -3
  73. package/dist/agent/Separator.md +28 -4
  74. package/dist/agent/SequenceFigure.md +50 -0
  75. package/dist/agent/Sheet.md +25 -2
  76. package/dist/agent/Skeleton.md +24 -2
  77. package/dist/agent/Slider.md +27 -4
  78. package/dist/agent/Sparkline.md +23 -1
  79. package/dist/agent/Spinner.md +24 -2
  80. package/dist/agent/StatusDot.md +20 -1
  81. package/dist/agent/StatusPill.md +28 -2
  82. package/dist/agent/Steps.md +24 -1
  83. package/dist/agent/Switch.md +21 -1
  84. package/dist/agent/Table.md +26 -3
  85. package/dist/agent/Tabs.md +24 -2
  86. package/dist/agent/Tag.md +40 -9
  87. package/dist/agent/Text.md +65 -0
  88. package/dist/agent/Textarea.md +20 -1
  89. package/dist/agent/Timestamp.md +64 -0
  90. package/dist/agent/Toast.md +26 -2
  91. package/dist/agent/ToggleGroup.md +24 -2
  92. package/dist/agent/Toolbar.md +71 -0
  93. package/dist/agent/Tooltip.md +23 -1
  94. package/dist/agent/TreemapChart.md +24 -1
  95. package/dist/agent/WaterfallChart.md +24 -1
  96. package/dist/agent/WorkflowFigure.md +50 -0
  97. package/dist/agent/catalog.json +6086 -1009
  98. package/dist/agent/index.md +27 -4
  99. package/dist/article.css +16 -4
  100. package/dist/charts/AreaChart/AreaChart.d.ts +9 -2
  101. package/dist/charts/AreaChart/AreaChart.js +12 -13
  102. package/dist/charts/AreaChart/AreaChart.js.map +1 -1
  103. package/dist/charts/BarChart/BarChart.d.ts +14 -3
  104. package/dist/charts/BarChart/BarChart.js +20 -12
  105. package/dist/charts/BarChart/BarChart.js.map +1 -1
  106. package/dist/charts/BarList/BarList.d.ts +9 -1
  107. package/dist/charts/BarList/BarList.js +9 -1
  108. package/dist/charts/BarList/BarList.js.map +1 -1
  109. package/dist/charts/BigNumber/BigNumber.d.ts +19 -3
  110. package/dist/charts/BigNumber/BigNumber.js +25 -5
  111. package/dist/charts/BigNumber/BigNumber.js.map +1 -1
  112. package/dist/charts/BoxPlot/BoxPlot.js +4 -2
  113. package/dist/charts/BoxPlot/BoxPlot.js.map +1 -1
  114. package/dist/charts/BulletChart/BulletChart.js +28 -7
  115. package/dist/charts/BulletChart/BulletChart.js.map +1 -1
  116. package/dist/charts/ComposedChart/ComposedChart.d.ts +9 -2
  117. package/dist/charts/ComposedChart/ComposedChart.js +10 -10
  118. package/dist/charts/ComposedChart/ComposedChart.js.map +1 -1
  119. package/dist/charts/FunnelChart/FunnelChart.d.ts +7 -2
  120. package/dist/charts/FunnelChart/FunnelChart.js +6 -16
  121. package/dist/charts/FunnelChart/FunnelChart.js.map +1 -1
  122. package/dist/charts/Heatmap/Heatmap.d.ts +17 -3
  123. package/dist/charts/Heatmap/Heatmap.js +20 -11
  124. package/dist/charts/Heatmap/Heatmap.js.map +1 -1
  125. package/dist/charts/Histogram/Histogram.d.ts +24 -1
  126. package/dist/charts/Histogram/Histogram.js +43 -11
  127. package/dist/charts/Histogram/Histogram.js.map +1 -1
  128. package/dist/charts/LineChart/LineChart.d.ts +9 -2
  129. package/dist/charts/LineChart/LineChart.js +11 -12
  130. package/dist/charts/LineChart/LineChart.js.map +1 -1
  131. package/dist/charts/PieChart/PieChart.d.ts +15 -3
  132. package/dist/charts/PieChart/PieChart.js +14 -6
  133. package/dist/charts/PieChart/PieChart.js.map +1 -1
  134. package/dist/charts/RadarChart/RadarChart.d.ts +15 -3
  135. package/dist/charts/RadarChart/RadarChart.js +11 -9
  136. package/dist/charts/RadarChart/RadarChart.js.map +1 -1
  137. package/dist/charts/RadialChart/RadialChart.d.ts +22 -4
  138. package/dist/charts/RadialChart/RadialChart.js +28 -6
  139. package/dist/charts/RadialChart/RadialChart.js.map +1 -1
  140. package/dist/charts/SankeyChart/SankeyChart.d.ts +16 -4
  141. package/dist/charts/SankeyChart/SankeyChart.js +14 -6
  142. package/dist/charts/SankeyChart/SankeyChart.js.map +1 -1
  143. package/dist/charts/ScatterChart/ScatterChart.d.ts +24 -3
  144. package/dist/charts/ScatterChart/ScatterChart.js +21 -15
  145. package/dist/charts/ScatterChart/ScatterChart.js.map +1 -1
  146. package/dist/charts/Sparkline/Sparkline.js +5 -3
  147. package/dist/charts/Sparkline/Sparkline.js.map +1 -1
  148. package/dist/charts/TreemapChart/TreemapChart.d.ts +7 -2
  149. package/dist/charts/TreemapChart/TreemapChart.js +20 -11
  150. package/dist/charts/TreemapChart/TreemapChart.js.map +1 -1
  151. package/dist/charts/WaterfallChart/WaterfallChart.js +4 -2
  152. package/dist/charts/WaterfallChart/WaterfallChart.js.map +1 -1
  153. package/dist/charts/index.d.ts +2 -1
  154. package/dist/charts/lib/export.d.ts +34 -43
  155. package/dist/charts/lib/export.js +16 -158
  156. package/dist/charts/lib/export.js.map +1 -1
  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/Combobox/Combobox.d.ts +15 -1
  204. package/dist/components/Combobox/Combobox.js +25 -5
  205. package/dist/components/Combobox/Combobox.js.map +1 -1
  206. package/dist/components/Command/Command.d.ts +4 -2
  207. package/dist/components/Command/Command.js +13 -1
  208. package/dist/components/Command/Command.js.map +1 -1
  209. package/dist/components/ContextMenu/ContextMenu.d.ts +30 -5
  210. package/dist/components/ContextMenu/ContextMenu.js +32 -2
  211. package/dist/components/ContextMenu/ContextMenu.js.map +1 -1
  212. package/dist/components/DatePicker/DatePicker.d.ts +36 -4
  213. package/dist/components/DatePicker/DatePicker.js +55 -12
  214. package/dist/components/DatePicker/DatePicker.js.map +1 -1
  215. package/dist/components/DescriptionList/DescriptionList.d.ts +73 -0
  216. package/dist/components/DescriptionList/DescriptionList.js +35 -0
  217. package/dist/components/DescriptionList/DescriptionList.js.map +1 -0
  218. package/dist/components/Diagram/Diagram.d.ts +8 -3
  219. package/dist/components/Diagram/Diagram.js +82 -10
  220. package/dist/components/Diagram/Diagram.js.map +1 -1
  221. package/dist/components/Dialog/Dialog.d.ts +18 -3
  222. package/dist/components/Dialog/Dialog.js +20 -2
  223. package/dist/components/Dialog/Dialog.js.map +1 -1
  224. package/dist/components/DropdownMenu/DropdownMenu.d.ts +49 -11
  225. package/dist/components/DropdownMenu/DropdownMenu.js +32 -2
  226. package/dist/components/DropdownMenu/DropdownMenu.js.map +1 -1
  227. package/dist/components/EmptyState/EmptyState.d.ts +25 -1
  228. package/dist/components/EmptyState/EmptyState.js +3 -1
  229. package/dist/components/EmptyState/EmptyState.js.map +1 -1
  230. package/dist/components/ErrorState/ErrorState.d.ts +22 -1
  231. package/dist/components/ErrorState/ErrorState.js +11 -2
  232. package/dist/components/ErrorState/ErrorState.js.map +1 -1
  233. package/dist/components/Field/Field.d.ts +70 -3
  234. package/dist/components/Field/Field.js +53 -12
  235. package/dist/components/Field/Field.js.map +1 -1
  236. package/dist/components/Field/field-control.d.ts +31 -0
  237. package/dist/components/Field/field-control.js +12 -0
  238. package/dist/components/Field/field-control.js.map +1 -0
  239. package/dist/components/FloatingIconButton/FloatingIconButton.js +2 -0
  240. package/dist/components/FloatingIconButton/FloatingIconButton.js.map +1 -1
  241. package/dist/components/Heading/Heading.d.ts +62 -0
  242. package/dist/components/Heading/Heading.js +44 -0
  243. package/dist/components/Heading/Heading.js.map +1 -0
  244. package/dist/components/Markdown/Markdown.d.ts +139 -0
  245. package/dist/components/Markdown/Markdown.js +135 -0
  246. package/dist/components/Markdown/Markdown.js.map +1 -0
  247. package/dist/components/Markdown/inline.d.ts +29 -0
  248. package/dist/components/Markdown/inline.js +74 -0
  249. package/dist/components/Markdown/inline.js.map +1 -0
  250. package/dist/components/Markdown/nodes.d.ts +65 -0
  251. package/dist/components/Markdown/nodes.js +1 -0
  252. package/dist/components/Markdown/nodes.js.map +1 -0
  253. package/dist/components/Markdown/parse.d.ts +24 -0
  254. package/dist/components/Markdown/parse.js +110 -0
  255. package/dist/components/Markdown/parse.js.map +1 -0
  256. package/dist/components/Markdown/slug.d.ts +15 -0
  257. package/dist/components/Markdown/slug.js +7 -0
  258. package/dist/components/Markdown/slug.js.map +1 -0
  259. package/dist/components/NativeSelect/NativeSelect.d.ts +6 -0
  260. package/dist/components/NativeSelect/NativeSelect.js +2 -3
  261. package/dist/components/NativeSelect/NativeSelect.js.map +1 -1
  262. package/dist/components/Pagination/Pagination.d.ts +24 -1
  263. package/dist/components/Pagination/Pagination.js +6 -3
  264. package/dist/components/Pagination/Pagination.js.map +1 -1
  265. package/dist/components/Progress/Progress.d.ts +17 -5
  266. package/dist/components/Progress/Progress.js +18 -6
  267. package/dist/components/Progress/Progress.js.map +1 -1
  268. package/dist/components/RadioGroup/RadioGroup.d.ts +6 -0
  269. package/dist/components/RadioGroup/RadioGroup.js +11 -1
  270. package/dist/components/RadioGroup/RadioGroup.js.map +1 -1
  271. package/dist/components/ScrollArea/ScrollArea.d.ts +15 -0
  272. package/dist/components/ScrollArea/ScrollArea.js +2 -2
  273. package/dist/components/ScrollArea/ScrollArea.js.map +1 -1
  274. package/dist/components/SearchableMenu/SearchableMenu.d.ts +11 -2
  275. package/dist/components/SearchableMenu/SearchableMenu.js +24 -2
  276. package/dist/components/SearchableMenu/SearchableMenu.js.map +1 -1
  277. package/dist/components/Select/Select.d.ts +18 -2
  278. package/dist/components/Select/Select.js +20 -3
  279. package/dist/components/Select/Select.js.map +1 -1
  280. package/dist/components/Separator/Separator.d.ts +21 -3
  281. package/dist/components/Separator/Separator.js +9 -1
  282. package/dist/components/Separator/Separator.js.map +1 -1
  283. package/dist/components/Sheet/Sheet.d.ts +3 -0
  284. package/dist/components/Sheet/Sheet.js +11 -2
  285. package/dist/components/Sheet/Sheet.js.map +1 -1
  286. package/dist/components/Skeleton/Skeleton.d.ts +8 -1
  287. package/dist/components/Skeleton/Skeleton.js +1 -1
  288. package/dist/components/Skeleton/Skeleton.js.map +1 -1
  289. package/dist/components/Slider/Slider.d.ts +21 -3
  290. package/dist/components/Slider/Slider.js +54 -27
  291. package/dist/components/Slider/Slider.js.map +1 -1
  292. package/dist/components/Spinner/Spinner.d.ts +8 -0
  293. package/dist/components/Spinner/Spinner.js +3 -2
  294. package/dist/components/Spinner/Spinner.js.map +1 -1
  295. package/dist/components/StatusPill/StatusPill.d.ts +5 -0
  296. package/dist/components/StatusPill/StatusPill.js +6 -0
  297. package/dist/components/StatusPill/StatusPill.js.map +1 -1
  298. package/dist/components/Table/Table.d.ts +6 -0
  299. package/dist/components/Table/Table.js +3 -1
  300. package/dist/components/Table/Table.js.map +1 -1
  301. package/dist/components/Tag/Tag.d.ts +68 -9
  302. package/dist/components/Tag/Tag.js +50 -10
  303. package/dist/components/Tag/Tag.js.map +1 -1
  304. package/dist/components/Text/Text.d.ts +62 -0
  305. package/dist/components/Text/Text.js +30 -0
  306. package/dist/components/Text/Text.js.map +1 -0
  307. package/dist/components/Timestamp/Timestamp.d.ts +59 -0
  308. package/dist/components/Timestamp/Timestamp.js +56 -0
  309. package/dist/components/Timestamp/Timestamp.js.map +1 -0
  310. package/dist/components/Toast/Toast.d.ts +9 -1
  311. package/dist/components/Toast/Toast.js +27 -4
  312. package/dist/components/Toast/Toast.js.map +1 -1
  313. package/dist/components/ToggleGroup/ToggleGroup.d.ts +6 -1
  314. package/dist/components/ToggleGroup/ToggleGroup.js +11 -1
  315. package/dist/components/ToggleGroup/ToggleGroup.js.map +1 -1
  316. package/dist/components/Toolbar/Toolbar.d.ts +55 -0
  317. package/dist/components/Toolbar/Toolbar.js +43 -0
  318. package/dist/components/Toolbar/Toolbar.js.map +1 -0
  319. package/dist/diagrams/ArchitectureFigure/ArchitectureFigure.d.ts +55 -0
  320. package/dist/diagrams/ArchitectureFigure/ArchitectureFigure.js +175 -0
  321. package/dist/diagrams/ArchitectureFigure/ArchitectureFigure.js.map +1 -0
  322. package/dist/diagrams/DataflowFigure/DataflowFigure.d.ts +28 -0
  323. package/dist/diagrams/DataflowFigure/DataflowFigure.js +148 -0
  324. package/dist/diagrams/DataflowFigure/DataflowFigure.js.map +1 -0
  325. package/dist/diagrams/DiagramCanvas/DiagramCanvas.d.ts +78 -0
  326. package/dist/diagrams/DiagramCanvas/DiagramCanvas.js +229 -0
  327. package/dist/diagrams/DiagramCanvas/DiagramCanvas.js.map +1 -0
  328. package/dist/diagrams/DiagramExportMenu/DiagramExportMenu.d.ts +106 -0
  329. package/dist/diagrams/DiagramExportMenu/DiagramExportMenu.js +123 -0
  330. package/dist/diagrams/DiagramExportMenu/DiagramExportMenu.js.map +1 -0
  331. package/dist/diagrams/DiagramInspector/DiagramInspector.d.ts +70 -0
  332. package/dist/diagrams/DiagramInspector/DiagramInspector.js +94 -0
  333. package/dist/diagrams/DiagramInspector/DiagramInspector.js.map +1 -0
  334. package/dist/diagrams/DiagramLegend/DiagramLegend.d.ts +42 -0
  335. package/dist/diagrams/DiagramLegend/DiagramLegend.js +35 -0
  336. package/dist/diagrams/DiagramLegend/DiagramLegend.js.map +1 -0
  337. package/dist/diagrams/DiagramMinimap/DiagramMinimap.d.ts +77 -0
  338. package/dist/diagrams/DiagramMinimap/DiagramMinimap.js +106 -0
  339. package/dist/diagrams/DiagramMinimap/DiagramMinimap.js.map +1 -0
  340. package/dist/diagrams/DiagramToolbar/DiagramToolbar.d.ts +48 -0
  341. package/dist/diagrams/DiagramToolbar/DiagramToolbar.js +53 -0
  342. package/dist/diagrams/DiagramToolbar/DiagramToolbar.js.map +1 -0
  343. package/dist/diagrams/LifecycleFigure/LifecycleFigure.d.ts +31 -0
  344. package/dist/diagrams/LifecycleFigure/LifecycleFigure.js +234 -0
  345. package/dist/diagrams/LifecycleFigure/LifecycleFigure.js.map +1 -0
  346. package/dist/diagrams/SequenceFigure/SequenceFigure.d.ts +38 -0
  347. package/dist/diagrams/SequenceFigure/SequenceFigure.js +261 -0
  348. package/dist/diagrams/SequenceFigure/SequenceFigure.js.map +1 -0
  349. package/dist/diagrams/WorkflowFigure/WorkflowFigure.d.ts +36 -0
  350. package/dist/diagrams/WorkflowFigure/WorkflowFigure.js +259 -0
  351. package/dist/diagrams/WorkflowFigure/WorkflowFigure.js.map +1 -0
  352. package/dist/diagrams/index.d.ts +17 -0
  353. package/dist/diagrams/index.js +29 -0
  354. package/dist/diagrams/index.js.map +1 -0
  355. package/dist/diagrams/lib/dev.d.ts +61 -0
  356. package/dist/diagrams/lib/dev.js +96 -0
  357. package/dist/diagrams/lib/dev.js.map +1 -0
  358. package/dist/diagrams/lib/frame.d.ts +138 -0
  359. package/dist/diagrams/lib/frame.js +131 -0
  360. package/dist/diagrams/lib/frame.js.map +1 -0
  361. package/dist/diagrams/lib/geometry.d.ts +114 -0
  362. package/dist/diagrams/lib/geometry.js +113 -0
  363. package/dist/diagrams/lib/geometry.js.map +1 -0
  364. package/dist/diagrams/lib/legend.d.ts +32 -0
  365. package/dist/diagrams/lib/legend.js +84 -0
  366. package/dist/diagrams/lib/legend.js.map +1 -0
  367. package/dist/diagrams/lib/marks.d.ts +207 -0
  368. package/dist/diagrams/lib/marks.js +480 -0
  369. package/dist/diagrams/lib/marks.js.map +1 -0
  370. package/dist/diagrams/lib/route.d.ts +85 -0
  371. package/dist/diagrams/lib/route.js +178 -0
  372. package/dist/diagrams/lib/route.js.map +1 -0
  373. package/dist/diagrams/lib/wires.d.ts +59 -0
  374. package/dist/diagrams/lib/wires.js +90 -0
  375. package/dist/diagrams/lib/wires.js.map +1 -0
  376. package/dist/diagrams/spec.d.ts +405 -0
  377. package/dist/diagrams/spec.js +1 -0
  378. package/dist/diagrams/spec.js.map +1 -0
  379. package/dist/index.d.ts +16 -4
  380. package/dist/index.js +9 -0
  381. package/dist/index.js.map +1 -1
  382. package/dist/keyframes.css +57 -3
  383. package/dist/lib/overlay-container.d.ts +14 -4
  384. package/dist/lib/overlay-container.js.map +1 -1
  385. package/dist/lib/svg-export.d.ts +138 -0
  386. package/dist/lib/svg-export.js +198 -0
  387. package/dist/lib/svg-export.js.map +1 -0
  388. package/dist/lib/warn.d.ts +73 -0
  389. package/dist/lib/warn.js +38 -0
  390. package/dist/lib/warn.js.map +1 -0
  391. package/dist/semantic.css +51 -6
  392. package/dist/styles.css +1 -1
  393. package/dist/tokens.css +20 -2
  394. package/dist/tokens.d.ts +17 -1
  395. package/dist/tokens.js +121 -36
  396. package/dist/tokens.json +121 -36
  397. package/llms.txt +35 -0
  398. package/package.json +9 -2
  399. package/skills/misoto22-design/SKILL.md +21 -7
  400. package/skills/misoto22-design/evals/evals.json +84 -0
  401. package/skills/misoto22-design/rules/a11y.md +55 -3
  402. package/skills/misoto22-design/rules/forms.md +5 -0
@@ -0,0 +1,229 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import {
4
+ useCallback,
5
+ useEffect,
6
+ useId,
7
+ useImperativeHandle,
8
+ useRef,
9
+ useState
10
+ } from "react";
11
+ import { cn } from "../../lib/cn.js";
12
+ const MIN_SCALE = 0.35;
13
+ const MAX_SCALE = 4;
14
+ const STEP = 1.25;
15
+ function DiagramCanvas({
16
+ children,
17
+ className,
18
+ height = "24rem",
19
+ onViewChange,
20
+ controls = true,
21
+ label = "Diagram canvas",
22
+ ref
23
+ }) {
24
+ const uid = useId().replace(/:/g, "");
25
+ const frame = useRef(null);
26
+ const [view, setView] = useState({ scale: 1, x: 0, y: 0 });
27
+ const drag = useRef(null);
28
+ const measure = useCallback(() => {
29
+ const box = frame.current?.getBoundingClientRect();
30
+ return box && box.width > 0 ? { width: box.width, height: box.height } : void 0;
31
+ }, []);
32
+ const apply = useCallback(
33
+ (next) => {
34
+ const measured = { ...next, frame: measure() };
35
+ setView(measured);
36
+ onViewChange?.(measured);
37
+ },
38
+ [measure, onViewChange]
39
+ );
40
+ const zoomAbout = useCallback(
41
+ (factor, originX, originY) => {
42
+ const box = frame.current?.getBoundingClientRect();
43
+ const cx = originX ?? (box ? box.width / 2 : 0);
44
+ const cy = originY ?? (box ? box.height / 2 : 0);
45
+ setView((current) => {
46
+ const scale = Math.min(MAX_SCALE, Math.max(MIN_SCALE, current.scale * factor));
47
+ const ratio = scale / current.scale;
48
+ const next = {
49
+ scale,
50
+ x: cx - (cx - current.x) * ratio,
51
+ y: cy - (cy - current.y) * ratio,
52
+ frame: measure()
53
+ };
54
+ onViewChange?.(next);
55
+ return next;
56
+ });
57
+ },
58
+ [measure, onViewChange]
59
+ );
60
+ useImperativeHandle(
61
+ ref,
62
+ () => ({
63
+ zoomIn: () => zoomAbout(STEP),
64
+ zoomOut: () => zoomAbout(1 / STEP),
65
+ reset: () => apply({ scale: 1, x: 0, y: 0 }),
66
+ centerOn: (x, y) => {
67
+ const box = frame.current?.getBoundingClientRect();
68
+ if (!box) return;
69
+ setView((current) => {
70
+ const next = {
71
+ ...current,
72
+ x: box.width / 2 - x * current.scale,
73
+ y: box.height / 2 - y * current.scale,
74
+ frame: measure()
75
+ };
76
+ onViewChange?.(next);
77
+ return next;
78
+ });
79
+ },
80
+ view
81
+ }),
82
+ [apply, measure, onViewChange, view, zoomAbout]
83
+ );
84
+ useEffect(() => {
85
+ const node = frame.current;
86
+ if (!node) return;
87
+ const onWheel = (event) => {
88
+ if (!event.ctrlKey && !event.metaKey) return;
89
+ event.preventDefault();
90
+ const box = node.getBoundingClientRect();
91
+ zoomAbout(
92
+ event.deltaY < 0 ? STEP : 1 / STEP,
93
+ event.clientX - box.left,
94
+ event.clientY - box.top
95
+ );
96
+ };
97
+ node.addEventListener("wheel", onWheel, { passive: false });
98
+ return () => node.removeEventListener("wheel", onWheel);
99
+ }, [zoomAbout]);
100
+ const onPointerDown = (event) => {
101
+ if (event.button !== 0) return;
102
+ drag.current = { pointer: event.pointerId, x: event.clientX - view.x, y: event.clientY - view.y };
103
+ event.currentTarget.setPointerCapture(event.pointerId);
104
+ };
105
+ const onPointerMove = (event) => {
106
+ const state = drag.current;
107
+ if (!state || state.pointer !== event.pointerId) return;
108
+ apply({ ...view, x: event.clientX - state.x, y: event.clientY - state.y });
109
+ };
110
+ const endDrag = (event) => {
111
+ if (drag.current?.pointer !== event.pointerId) return;
112
+ drag.current = null;
113
+ if (event.currentTarget.hasPointerCapture(event.pointerId)) {
114
+ event.currentTarget.releasePointerCapture(event.pointerId);
115
+ }
116
+ };
117
+ return /* @__PURE__ */ jsxs(
118
+ "div",
119
+ {
120
+ className: cn(
121
+ "relative overflow-hidden rounded-(--radius-lg) border border-(--rule) bg-(--diagram-surface)",
122
+ className
123
+ ),
124
+ style: { height },
125
+ children: [
126
+ /* @__PURE__ */ jsx(
127
+ "div",
128
+ {
129
+ ref: frame,
130
+ role: "group",
131
+ "aria-label": label,
132
+ "aria-describedby": `${uid}-hint`,
133
+ tabIndex: 0,
134
+ className: "size-full cursor-grab touch-none select-none outline-none focus-visible:ring-2 focus-visible:ring-(--ring) focus-visible:ring-inset active:cursor-grabbing",
135
+ onPointerDown,
136
+ onPointerMove,
137
+ onPointerUp: endDrag,
138
+ onPointerCancel: endDrag,
139
+ onKeyDown: (event) => {
140
+ const nudge = event.shiftKey ? 80 : 24;
141
+ switch (event.key) {
142
+ case "+":
143
+ case "=":
144
+ event.preventDefault();
145
+ zoomAbout(STEP);
146
+ break;
147
+ case "-":
148
+ event.preventDefault();
149
+ zoomAbout(1 / STEP);
150
+ break;
151
+ case "0":
152
+ event.preventDefault();
153
+ apply({ scale: 1, x: 0, y: 0 });
154
+ break;
155
+ case "ArrowLeft":
156
+ event.preventDefault();
157
+ apply({ ...view, x: view.x + nudge });
158
+ break;
159
+ case "ArrowRight":
160
+ event.preventDefault();
161
+ apply({ ...view, x: view.x - nudge });
162
+ break;
163
+ case "ArrowUp":
164
+ event.preventDefault();
165
+ apply({ ...view, y: view.y + nudge });
166
+ break;
167
+ case "ArrowDown":
168
+ event.preventDefault();
169
+ apply({ ...view, y: view.y - nudge });
170
+ break;
171
+ default:
172
+ break;
173
+ }
174
+ },
175
+ children: /* @__PURE__ */ jsx(
176
+ "div",
177
+ {
178
+ className: "origin-top-left will-change-transform",
179
+ style: { transform: `translate(${view.x}px, ${view.y}px) scale(${view.scale})` },
180
+ "data-diagram-stage": "",
181
+ children
182
+ }
183
+ )
184
+ }
185
+ ),
186
+ /* @__PURE__ */ jsx("p", { id: `${uid}-hint`, className: "sr-only", children: "Drag to pan. Plus and minus zoom, zero resets, the arrow keys pan." }),
187
+ controls && /* @__PURE__ */ jsxs("div", { className: "absolute bottom-3 end-3 flex items-center gap-px rounded-(--radius) border border-(--rule-2) bg-(--panel-bg) p-0.5", children: [
188
+ /* @__PURE__ */ jsx(CanvasButton, { label: "Zoom out", onClick: () => zoomAbout(1 / STEP), children: /* @__PURE__ */ jsx("span", { "aria-hidden": true, children: "\u2212" }) }),
189
+ /* @__PURE__ */ jsxs(
190
+ "button",
191
+ {
192
+ type: "button",
193
+ onClick: () => apply({ scale: 1, x: 0, y: 0 }),
194
+ "aria-label": "Reset the view",
195
+ className: "min-w-12 rounded-(--radius-xs) px-1.5 py-1 mono-meta text-(--ink-2) transition-colors duration-(--duration-fast) hover:bg-(--stone) hover:text-(--ink) focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-(--ring)",
196
+ children: [
197
+ Math.round(view.scale * 100),
198
+ "%"
199
+ ]
200
+ }
201
+ ),
202
+ /* @__PURE__ */ jsx(CanvasButton, { label: "Zoom in", onClick: () => zoomAbout(STEP), children: /* @__PURE__ */ jsx("span", { "aria-hidden": true, children: "+" }) })
203
+ ] })
204
+ ]
205
+ }
206
+ );
207
+ }
208
+ function CanvasButton({
209
+ label,
210
+ onClick,
211
+ children
212
+ }) {
213
+ return /* @__PURE__ */ jsx(
214
+ "button",
215
+ {
216
+ type: "button",
217
+ onClick,
218
+ "aria-label": label,
219
+ className: "flex size-7 items-center justify-center rounded-(--radius-xs) font-mono text-[13px] leading-none text-(--ink-2) transition-colors duration-(--duration-fast) hover:bg-(--stone) hover:text-(--ink) focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-(--ring)",
220
+ children
221
+ }
222
+ );
223
+ }
224
+ var DiagramCanvas_default = DiagramCanvas;
225
+ export {
226
+ DiagramCanvas,
227
+ DiagramCanvas_default as default
228
+ };
229
+ //# sourceMappingURL=DiagramCanvas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/diagrams/DiagramCanvas/DiagramCanvas.tsx"],"sourcesContent":["'use client'\n\nimport {\n useCallback,\n useEffect,\n useId,\n useImperativeHandle,\n useRef,\n useState,\n type PointerEvent as ReactPointerEvent,\n type ReactNode,\n type Ref,\n} from 'react'\nimport { cn } from '../../lib/cn'\n\nconst MIN_SCALE = 0.35\nconst MAX_SCALE = 4\nconst STEP = 1.25\n\nexport interface CanvasView {\n scale: number\n x: number\n y: number\n /**\n * How big the frame was, in CSS pixels, when this view was reported.\n *\n * Carried on the view because everything that consumes a view needs it and\n * nothing outside this component can measure it: a `DiagramMinimap`'s\n * viewport rectangle is the frame's size over the zoom, and a caller who had\n * to hand-declare that number was hand-declaring the one number that makes\n * the map lie. Absent until the frame has been measured — the first render\n * has no box yet — which is exactly when a map should draw nothing.\n */\n frame?: { width: number; height: number }\n}\n\n/** What a caller can do to the canvas from outside it. */\nexport interface DiagramCanvasHandle {\n zoomIn: () => void\n zoomOut: () => void\n reset: () => void\n /** Pans so a point in CONTENT coordinates sits in the middle of the frame. */\n centerOn: (x: number, y: number) => void\n view: CanvasView\n}\n\nexport interface DiagramCanvasProps {\n children: ReactNode\n className?: string\n /** How tall the frame is. Anything CSS accepts. */\n height?: string\n /** Told about every view change, for a minimap or a percentage readout. */\n onViewChange?: (view: CanvasView) => void\n /** Hides the built-in zoom controls, for a caller supplying its own. */\n controls?: boolean\n /** Names the region for assistive tech. */\n label?: string\n ref?: Ref<DiagramCanvasHandle>\n}\n\n/**\n * A frame that a picture larger than it can be moved around inside.\n *\n * Pan with a drag, zoom with the controls or with ⌘/Ctrl and the wheel, and\n * reset with a key. That is the whole of it — this is deliberately a VIEWPORT\n * and not a diagram editor: nothing here knows what a node is, so it works for\n * any oversized figure, an SVG, an image, a table that will not fold.\n *\n * Three decisions here are not obvious.\n *\n * **A plain wheel scrolls the page, not the diagram.** A canvas that swallows\n * the wheel is a scroll trap: a reader flicking down an article hits the figure\n * and the page stops moving for no reason they can see. Zooming needs the\n * modifier — which is also the platform gesture for zoom everywhere else, and\n * is what a trackpad pinch already sends.\n *\n * **The transform is on a wrapper, not on the content.** The child keeps its\n * own coordinate space, so a figure inside can still be measured, exported and\n * read by anything that walks it. Scaling the child directly would make every\n * `getBoundingClientRect` inside it a lie.\n *\n * **Keyboard first, and the frame is a real tab stop.** `+` / `-` / `0` and the\n * arrow keys move the view; the frame takes focus so they can be pressed at\n * all. A canvas that only answers a drag is a canvas half the readers cannot\n * operate.\n *\n * @example\n * <DiagramCanvas height=\"26rem\" label=\"Request path\">\n * <ArchitectureFigure spec={spec} heading={false} legend=\"hidden\" cards={false} />\n * </DiagramCanvas>\n */\nexport function DiagramCanvas({\n children,\n className,\n height = '24rem',\n onViewChange,\n controls = true,\n label = 'Diagram canvas',\n ref,\n}: DiagramCanvasProps) {\n const uid = useId().replace(/:/g, '')\n const frame = useRef<HTMLDivElement>(null)\n const [view, setView] = useState<CanvasView>({ scale: 1, x: 0, y: 0 })\n const drag = useRef<{ pointer: number; x: number; y: number } | null>(null)\n\n /** The frame's own size, or nothing when it has not been laid out yet. */\n const measure = useCallback(() => {\n const box = frame.current?.getBoundingClientRect()\n return box && box.width > 0 ? { width: box.width, height: box.height } : undefined\n }, [])\n\n const apply = useCallback(\n (next: CanvasView) => {\n const measured = { ...next, frame: measure() }\n setView(measured)\n onViewChange?.(measured)\n },\n [measure, onViewChange],\n )\n\n /**\n * Zooms about a fixed point in FRAME coordinates.\n *\n * The arithmetic is what makes the pointer stay on the thing it is over:\n * the content point under the cursor must land back under the cursor, so the\n * offset moves by the difference the scale change opened up. Without it, a\n * zoom drifts toward the origin and a reader chases the node they were\n * looking at.\n */\n const zoomAbout = useCallback(\n (factor: number, originX?: number, originY?: number) => {\n const box = frame.current?.getBoundingClientRect()\n const cx = originX ?? (box ? box.width / 2 : 0)\n const cy = originY ?? (box ? box.height / 2 : 0)\n\n setView((current) => {\n const scale = Math.min(MAX_SCALE, Math.max(MIN_SCALE, current.scale * factor))\n const ratio = scale / current.scale\n const next = {\n scale,\n x: cx - (cx - current.x) * ratio,\n y: cy - (cy - current.y) * ratio,\n frame: measure(),\n }\n onViewChange?.(next)\n return next\n })\n },\n [measure, onViewChange],\n )\n\n useImperativeHandle(\n ref,\n () => ({\n zoomIn: () => zoomAbout(STEP),\n zoomOut: () => zoomAbout(1 / STEP),\n reset: () => apply({ scale: 1, x: 0, y: 0 }),\n centerOn: (x: number, y: number) => {\n const box = frame.current?.getBoundingClientRect()\n if (!box) return\n setView((current) => {\n const next = {\n ...current,\n x: box.width / 2 - x * current.scale,\n y: box.height / 2 - y * current.scale,\n frame: measure(),\n }\n onViewChange?.(next)\n return next\n })\n },\n view,\n }),\n [apply, measure, onViewChange, view, zoomAbout],\n )\n\n // Registered imperatively because React marks `wheel` passive, and a passive\n // listener cannot call preventDefault — so the modifier-wheel zoom would also\n // scroll the page underneath it.\n useEffect(() => {\n const node = frame.current\n if (!node) return\n\n const onWheel = (event: WheelEvent) => {\n if (!event.ctrlKey && !event.metaKey) return\n event.preventDefault()\n const box = node.getBoundingClientRect()\n zoomAbout(\n event.deltaY < 0 ? STEP : 1 / STEP,\n event.clientX - box.left,\n event.clientY - box.top,\n )\n }\n\n node.addEventListener('wheel', onWheel, { passive: false })\n return () => node.removeEventListener('wheel', onWheel)\n }, [zoomAbout])\n\n const onPointerDown = (event: ReactPointerEvent<HTMLDivElement>) => {\n if (event.button !== 0) return\n drag.current = { pointer: event.pointerId, x: event.clientX - view.x, y: event.clientY - view.y }\n event.currentTarget.setPointerCapture(event.pointerId)\n }\n\n const onPointerMove = (event: ReactPointerEvent<HTMLDivElement>) => {\n const state = drag.current\n if (!state || state.pointer !== event.pointerId) return\n apply({ ...view, x: event.clientX - state.x, y: event.clientY - state.y })\n }\n\n const endDrag = (event: ReactPointerEvent<HTMLDivElement>) => {\n if (drag.current?.pointer !== event.pointerId) return\n drag.current = null\n if (event.currentTarget.hasPointerCapture(event.pointerId)) {\n event.currentTarget.releasePointerCapture(event.pointerId)\n }\n }\n\n return (\n <div\n className={cn(\n 'relative overflow-hidden rounded-(--radius-lg) border border-(--rule) bg-(--diagram-surface)',\n className,\n )}\n style={{ height }}\n >\n <div\n ref={frame}\n role=\"group\"\n aria-label={label}\n aria-describedby={`${uid}-hint`}\n tabIndex={0}\n className=\"size-full cursor-grab touch-none select-none outline-none focus-visible:ring-2 focus-visible:ring-(--ring) focus-visible:ring-inset active:cursor-grabbing\"\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={endDrag}\n onPointerCancel={endDrag}\n onKeyDown={(event) => {\n const nudge = event.shiftKey ? 80 : 24\n switch (event.key) {\n case '+':\n case '=':\n event.preventDefault()\n zoomAbout(STEP)\n break\n case '-':\n event.preventDefault()\n zoomAbout(1 / STEP)\n break\n case '0':\n event.preventDefault()\n apply({ scale: 1, x: 0, y: 0 })\n break\n case 'ArrowLeft':\n event.preventDefault()\n apply({ ...view, x: view.x + nudge })\n break\n case 'ArrowRight':\n event.preventDefault()\n apply({ ...view, x: view.x - nudge })\n break\n case 'ArrowUp':\n event.preventDefault()\n apply({ ...view, y: view.y + nudge })\n break\n case 'ArrowDown':\n event.preventDefault()\n apply({ ...view, y: view.y - nudge })\n break\n default:\n break\n }\n }}\n >\n <div\n className=\"origin-top-left will-change-transform\"\n style={{ transform: `translate(${view.x}px, ${view.y}px) scale(${view.scale})` }}\n data-diagram-stage=\"\"\n >\n {children}\n </div>\n </div>\n\n <p id={`${uid}-hint`} className=\"sr-only\">\n Drag to pan. Plus and minus zoom, zero resets, the arrow keys pan.\n </p>\n\n {controls && (\n <div className=\"absolute bottom-3 end-3 flex items-center gap-px rounded-(--radius) border border-(--rule-2) bg-(--panel-bg) p-0.5\">\n <CanvasButton label=\"Zoom out\" onClick={() => zoomAbout(1 / STEP)}>\n <span aria-hidden>−</span>\n </CanvasButton>\n <button\n type=\"button\"\n onClick={() => apply({ scale: 1, x: 0, y: 0 })}\n aria-label=\"Reset the view\"\n className=\"min-w-12 rounded-(--radius-xs) px-1.5 py-1 mono-meta text-(--ink-2) transition-colors duration-(--duration-fast) hover:bg-(--stone) hover:text-(--ink) focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-(--ring)\"\n >\n {Math.round(view.scale * 100)}%\n </button>\n <CanvasButton label=\"Zoom in\" onClick={() => zoomAbout(STEP)}>\n <span aria-hidden>+</span>\n </CanvasButton>\n </div>\n )}\n </div>\n )\n}\n\nfunction CanvasButton({\n label,\n onClick,\n children,\n}: {\n label: string\n onClick: () => void\n children: ReactNode\n}) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n aria-label={label}\n className=\"flex size-7 items-center justify-center rounded-(--radius-xs) font-mono text-[13px] leading-none text-(--ink-2) transition-colors duration-(--duration-fast) hover:bg-(--stone) hover:text-(--ink) focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-(--ring)\"\n >\n {children}\n </button>\n )\n}\n\nexport default DiagramCanvas\n"],"mappings":";AAkRQ,cAkBE,YAlBF;AAhRR;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AACP,SAAS,UAAU;AAEnB,MAAM,YAAY;AAClB,MAAM,YAAY;AAClB,MAAM,OAAO;AA0EN,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA,SAAS;AAAA,EACT;AAAA,EACA,WAAW;AAAA,EACX,QAAQ;AAAA,EACR;AACF,GAAuB;AACrB,QAAM,MAAM,MAAM,EAAE,QAAQ,MAAM,EAAE;AACpC,QAAM,QAAQ,OAAuB,IAAI;AACzC,QAAM,CAAC,MAAM,OAAO,IAAI,SAAqB,EAAE,OAAO,GAAG,GAAG,GAAG,GAAG,EAAE,CAAC;AACrE,QAAM,OAAO,OAAyD,IAAI;AAG1E,QAAM,UAAU,YAAY,MAAM;AAChC,UAAM,MAAM,MAAM,SAAS,sBAAsB;AACjD,WAAO,OAAO,IAAI,QAAQ,IAAI,EAAE,OAAO,IAAI,OAAO,QAAQ,IAAI,OAAO,IAAI;AAAA,EAC3E,GAAG,CAAC,CAAC;AAEL,QAAM,QAAQ;AAAA,IACZ,CAAC,SAAqB;AACpB,YAAM,WAAW,EAAE,GAAG,MAAM,OAAO,QAAQ,EAAE;AAC7C,cAAQ,QAAQ;AAChB,qBAAe,QAAQ;AAAA,IACzB;AAAA,IACA,CAAC,SAAS,YAAY;AAAA,EACxB;AAWA,QAAM,YAAY;AAAA,IAChB,CAAC,QAAgB,SAAkB,YAAqB;AACtD,YAAM,MAAM,MAAM,SAAS,sBAAsB;AACjD,YAAM,KAAK,YAAY,MAAM,IAAI,QAAQ,IAAI;AAC7C,YAAM,KAAK,YAAY,MAAM,IAAI,SAAS,IAAI;AAE9C,cAAQ,CAAC,YAAY;AACnB,cAAM,QAAQ,KAAK,IAAI,WAAW,KAAK,IAAI,WAAW,QAAQ,QAAQ,MAAM,CAAC;AAC7E,cAAM,QAAQ,QAAQ,QAAQ;AAC9B,cAAM,OAAO;AAAA,UACX;AAAA,UACA,GAAG,MAAM,KAAK,QAAQ,KAAK;AAAA,UAC3B,GAAG,MAAM,KAAK,QAAQ,KAAK;AAAA,UAC3B,OAAO,QAAQ;AAAA,QACjB;AACA,uBAAe,IAAI;AACnB,eAAO;AAAA,MACT,CAAC;AAAA,IACH;AAAA,IACA,CAAC,SAAS,YAAY;AAAA,EACxB;AAEA;AAAA,IACE;AAAA,IACA,OAAO;AAAA,MACL,QAAQ,MAAM,UAAU,IAAI;AAAA,MAC5B,SAAS,MAAM,UAAU,IAAI,IAAI;AAAA,MACjC,OAAO,MAAM,MAAM,EAAE,OAAO,GAAG,GAAG,GAAG,GAAG,EAAE,CAAC;AAAA,MAC3C,UAAU,CAAC,GAAW,MAAc;AAClC,cAAM,MAAM,MAAM,SAAS,sBAAsB;AACjD,YAAI,CAAC,IAAK;AACV,gBAAQ,CAAC,YAAY;AACnB,gBAAM,OAAO;AAAA,YACX,GAAG;AAAA,YACH,GAAG,IAAI,QAAQ,IAAI,IAAI,QAAQ;AAAA,YAC/B,GAAG,IAAI,SAAS,IAAI,IAAI,QAAQ;AAAA,YAChC,OAAO,QAAQ;AAAA,UACjB;AACA,yBAAe,IAAI;AACnB,iBAAO;AAAA,QACT,CAAC;AAAA,MACH;AAAA,MACA;AAAA,IACF;AAAA,IACA,CAAC,OAAO,SAAS,cAAc,MAAM,SAAS;AAAA,EAChD;AAKA,YAAU,MAAM;AACd,UAAM,OAAO,MAAM;AACnB,QAAI,CAAC,KAAM;AAEX,UAAM,UAAU,CAAC,UAAsB;AACrC,UAAI,CAAC,MAAM,WAAW,CAAC,MAAM,QAAS;AACtC,YAAM,eAAe;AACrB,YAAM,MAAM,KAAK,sBAAsB;AACvC;AAAA,QACE,MAAM,SAAS,IAAI,OAAO,IAAI;AAAA,QAC9B,MAAM,UAAU,IAAI;AAAA,QACpB,MAAM,UAAU,IAAI;AAAA,MACtB;AAAA,IACF;AAEA,SAAK,iBAAiB,SAAS,SAAS,EAAE,SAAS,MAAM,CAAC;AAC1D,WAAO,MAAM,KAAK,oBAAoB,SAAS,OAAO;AAAA,EACxD,GAAG,CAAC,SAAS,CAAC;AAEd,QAAM,gBAAgB,CAAC,UAA6C;AAClE,QAAI,MAAM,WAAW,EAAG;AACxB,SAAK,UAAU,EAAE,SAAS,MAAM,WAAW,GAAG,MAAM,UAAU,KAAK,GAAG,GAAG,MAAM,UAAU,KAAK,EAAE;AAChG,UAAM,cAAc,kBAAkB,MAAM,SAAS;AAAA,EACvD;AAEA,QAAM,gBAAgB,CAAC,UAA6C;AAClE,UAAM,QAAQ,KAAK;AACnB,QAAI,CAAC,SAAS,MAAM,YAAY,MAAM,UAAW;AACjD,UAAM,EAAE,GAAG,MAAM,GAAG,MAAM,UAAU,MAAM,GAAG,GAAG,MAAM,UAAU,MAAM,EAAE,CAAC;AAAA,EAC3E;AAEA,QAAM,UAAU,CAAC,UAA6C;AAC5D,QAAI,KAAK,SAAS,YAAY,MAAM,UAAW;AAC/C,SAAK,UAAU;AACf,QAAI,MAAM,cAAc,kBAAkB,MAAM,SAAS,GAAG;AAC1D,YAAM,cAAc,sBAAsB,MAAM,SAAS;AAAA,IAC3D;AAAA,EACF;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACA,OAAO,EAAE,OAAO;AAAA,MAEhB;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,KAAK;AAAA,YACL,MAAK;AAAA,YACL,cAAY;AAAA,YACZ,oBAAkB,GAAG,GAAG;AAAA,YACxB,UAAU;AAAA,YACV,WAAU;AAAA,YACV;AAAA,YACA;AAAA,YACA,aAAa;AAAA,YACb,iBAAiB;AAAA,YACjB,WAAW,CAAC,UAAU;AACpB,oBAAM,QAAQ,MAAM,WAAW,KAAK;AACpC,sBAAQ,MAAM,KAAK;AAAA,gBACjB,KAAK;AAAA,gBACL,KAAK;AACH,wBAAM,eAAe;AACrB,4BAAU,IAAI;AACd;AAAA,gBACF,KAAK;AACH,wBAAM,eAAe;AACrB,4BAAU,IAAI,IAAI;AAClB;AAAA,gBACF,KAAK;AACH,wBAAM,eAAe;AACrB,wBAAM,EAAE,OAAO,GAAG,GAAG,GAAG,GAAG,EAAE,CAAC;AAC9B;AAAA,gBACF,KAAK;AACH,wBAAM,eAAe;AACrB,wBAAM,EAAE,GAAG,MAAM,GAAG,KAAK,IAAI,MAAM,CAAC;AACpC;AAAA,gBACF,KAAK;AACH,wBAAM,eAAe;AACrB,wBAAM,EAAE,GAAG,MAAM,GAAG,KAAK,IAAI,MAAM,CAAC;AACpC;AAAA,gBACF,KAAK;AACH,wBAAM,eAAe;AACrB,wBAAM,EAAE,GAAG,MAAM,GAAG,KAAK,IAAI,MAAM,CAAC;AACpC;AAAA,gBACF,KAAK;AACH,wBAAM,eAAe;AACrB,wBAAM,EAAE,GAAG,MAAM,GAAG,KAAK,IAAI,MAAM,CAAC;AACpC;AAAA,gBACF;AACE;AAAA,cACJ;AAAA,YACF;AAAA,YAEA;AAAA,cAAC;AAAA;AAAA,gBACC,WAAU;AAAA,gBACV,OAAO,EAAE,WAAW,aAAa,KAAK,CAAC,OAAO,KAAK,CAAC,aAAa,KAAK,KAAK,IAAI;AAAA,gBAC/E,sBAAmB;AAAA,gBAElB;AAAA;AAAA,YACH;AAAA;AAAA,QACF;AAAA,QAEA,oBAAC,OAAE,IAAI,GAAG,GAAG,SAAS,WAAU,WAAU,gFAE1C;AAAA,QAEC,YACC,qBAAC,SAAI,WAAU,sHACb;AAAA,8BAAC,gBAAa,OAAM,YAAW,SAAS,MAAM,UAAU,IAAI,IAAI,GAC9D,8BAAC,UAAK,eAAW,MAAC,oBAAC,GACrB;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAS,MAAM,MAAM,EAAE,OAAO,GAAG,GAAG,GAAG,GAAG,EAAE,CAAC;AAAA,cAC7C,cAAW;AAAA,cACX,WAAU;AAAA,cAET;AAAA,qBAAK,MAAM,KAAK,QAAQ,GAAG;AAAA,gBAAE;AAAA;AAAA;AAAA,UAChC;AAAA,UACA,oBAAC,gBAAa,OAAM,WAAU,SAAS,MAAM,UAAU,IAAI,GACzD,8BAAC,UAAK,eAAW,MAAC,eAAC,GACrB;AAAA,WACF;AAAA;AAAA;AAAA,EAEJ;AAEJ;AAEA,SAAS,aAAa;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL;AAAA,MACA,cAAY;AAAA,MACZ,WAAU;AAAA,MAET;AAAA;AAAA,EACH;AAEJ;AAEA,IAAO,wBAAQ;","names":[]}
@@ -0,0 +1,106 @@
1
+ import * as react from 'react';
2
+ import { RefObject, ReactNode } from 'react';
3
+
4
+ /** The files a figure can be taken away as. */
5
+ type ExportFormat = 'png' | 'jpeg' | 'webp' | 'svg' | 'share-card';
6
+ /** What came of an export, for a page that wants to say so. */
7
+ interface ExportResult {
8
+ format: ExportFormat;
9
+ /**
10
+ * Whether the pipeline named by `source` finished without throwing.
11
+ *
12
+ * Read it with `source`, because the two sources can promise different
13
+ * amounts. `built-in` means a file was handed to the browser. `caller` means
14
+ * an `onExport` handler resolved — and a handler that did nothing resolves
15
+ * exactly like one that wrote a file, which is a thing this component cannot
16
+ * see and therefore does not claim.
17
+ */
18
+ ok: boolean;
19
+ error?: Error;
20
+ /** Which pipeline ran: this component's, or the caller's `onExport`. */
21
+ source: 'built-in' | 'caller';
22
+ }
23
+ interface DiagramExportMenuProps {
24
+ /**
25
+ * The `<svg>` to export, or an element containing exactly one.
26
+ *
27
+ * A ref rather than a selector, because a page can hold several figures and a
28
+ * selector would export whichever the document happened to reach first.
29
+ */
30
+ targetRef: RefObject<HTMLElement | SVGSVGElement | null>;
31
+ /** Names the file, and is printed on the share card. */
32
+ title: string;
33
+ /** Replaces the trigger. */
34
+ trigger?: ReactNode;
35
+ className?: string;
36
+ /**
37
+ * Runs instead of the built-in export — for a caller with its own pipeline.
38
+ *
39
+ * Taking it turns everything below into a no-op, INCLUDING the part that
40
+ * knows whether a file was produced: a result from this path reports
41
+ * `source: 'caller'` and an `ok` that means only "your handler resolved".
42
+ */
43
+ onExport?: (format: ExportFormat) => void | Promise<void>;
44
+ /** Told what happened, so a page can raise a toast. */
45
+ onResult?: (result: ExportResult) => void;
46
+ /**
47
+ * Painted behind the artwork. Defaults to the surface the reader is looking
48
+ * at; `null` exports on a transparent ground.
49
+ *
50
+ * JPEG has no alpha channel, so it is flattened onto the reader's own paper
51
+ * whatever this says — a transparent JPEG is a black one.
52
+ */
53
+ background?: string | null;
54
+ }
55
+ /**
56
+ * Taking the figure off the page: five files, one menu.
57
+ *
58
+ * WHY THE MENU DOES THE WORK rather than handing back a format: every one of
59
+ * these exports is the same six steps — find the `<svg>`, walk it with
60
+ * `getComputedStyle` to bake the custom properties into real colours,
61
+ * serialise, rasterise, name the file, hand it to the browser — and only the
62
+ * last two differ between them. A menu that emitted `'png'` and left the caller
63
+ * to do the rest would be a menu that every consumer reimplements, badly, and
64
+ * the interesting half (a serialised SVG resolves `var(--ink)` to nothing and
65
+ * comes out invisible) is exactly the half a caller would not know to write.
66
+ *
67
+ * `onExport` is still there for a page with its own pipeline — a server-side
68
+ * renderer, a different frame size — and taking it turns everything below into
69
+ * a no-op, which is why a result from that path says `source: 'caller'`: this
70
+ * component cannot see whether the handler produced a file, so it does not
71
+ * report that it did.
72
+ *
73
+ * What each format actually is, stated rather than implied.
74
+ *
75
+ * **SVG** is the artwork with resolved colours. It is editable and it is the
76
+ * only lossless one, but it carries no web fonts: a machine without the family
77
+ * renders it in a fallback, so type metrics will differ.
78
+ *
79
+ * **The ground** is the reader's own surface unless `background` says
80
+ * otherwise. `background={null}` exports on transparency — for a figure being
81
+ * dropped onto a coloured page — except in JPEG, which has no alpha and is
82
+ * flattened onto paper whatever is asked for.
83
+ *
84
+ * **PNG, JPEG and WebP** are the browser's own rasteriser re-drawing that SVG
85
+ * at 2×. Not a screenshot — antialiasing and any effect a page stylesheet
86
+ * applied from OUTSIDE the `<svg>` are not in it. **JPEG has no alpha**, so it
87
+ * is flattened onto the resolved paper colour rather than onto black, which is
88
+ * what a transparent PNG becomes when a format with no transparency is asked
89
+ * to hold it.
90
+ *
91
+ * **The share card** is a 1200 × 630 frame with the title on it and the whole
92
+ * diagram letterboxed inside — never cropped. A card that cropped to fill the
93
+ * frame would be a picture of a different diagram.
94
+ */
95
+ declare function DiagramExportMenu({ targetRef, title, trigger, className, onExport, onResult, background, }: DiagramExportMenuProps): react.JSX.Element;
96
+ /**
97
+ * One figure, one format, one file.
98
+ *
99
+ * Exported so a page can wire a keyboard shortcut or its own button to exactly
100
+ * what the menu does, without reimplementing the six steps.
101
+ */
102
+ declare function exportFigure(target: HTMLElement | SVGSVGElement | null, title: string, format: ExportFormat, options?: {
103
+ background?: string | null;
104
+ }): Promise<void>;
105
+
106
+ export { DiagramExportMenu, type DiagramExportMenuProps, type ExportFormat, type ExportResult, DiagramExportMenu as default, exportFigure };
@@ -0,0 +1,123 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useState } from "react";
4
+ import { cn } from "../../lib/cn.js";
5
+ import {
6
+ downloadBlob,
7
+ exportFilename,
8
+ rasterize,
9
+ readToken,
10
+ serializeSvg
11
+ } from "../../lib/svg-export.js";
12
+ import {
13
+ DropdownMenu,
14
+ DropdownMenuContent,
15
+ DropdownMenuItem,
16
+ DropdownMenuLabel,
17
+ DropdownMenuSeparator,
18
+ DropdownMenuTrigger
19
+ } from "../../components/DropdownMenu/DropdownMenu.js";
20
+ import { Button } from "../../components/Button/Button.js";
21
+ const SHARE_CARD = { width: 1200, height: 630 };
22
+ const ITEMS = [
23
+ { group: "Image", format: "png", title: "PNG", hint: "Lossless, 2\xD7 for retina" },
24
+ { group: "Image", format: "jpeg", title: "JPEG", hint: "Compact, flattened onto paper" },
25
+ { group: "Image", format: "webp", title: "WebP", hint: "Smaller, modern browsers" },
26
+ { group: "Vector", format: "svg", title: "SVG", hint: "Editable, colours resolved" },
27
+ { group: "Share", format: "share-card", title: "Share card", hint: "1200 \xD7 630 PNG" }
28
+ ];
29
+ function DiagramExportMenu({
30
+ targetRef,
31
+ title,
32
+ trigger,
33
+ className,
34
+ onExport,
35
+ onResult,
36
+ background
37
+ }) {
38
+ const [busy, setBusy] = useState(null);
39
+ async function run(format) {
40
+ const source = onExport ? "caller" : "built-in";
41
+ setBusy(format);
42
+ try {
43
+ if (onExport) {
44
+ await onExport(format);
45
+ } else {
46
+ await exportFigure(targetRef.current, title, format, { background });
47
+ }
48
+ onResult?.({ format, ok: true, source });
49
+ } catch (cause) {
50
+ onResult?.({ format, ok: false, error: cause, source });
51
+ } finally {
52
+ setBusy(null);
53
+ }
54
+ }
55
+ const groups = [...new Set(ITEMS.map((item) => item.group))];
56
+ return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
57
+ /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: trigger ?? /* @__PURE__ */ jsx(Button, { size: "sm", variant: "secondary", className, children: "Export" }) }),
58
+ /* @__PURE__ */ jsx(DropdownMenuContent, { align: "end", className: "w-64", children: groups.map((group, index) => /* @__PURE__ */ jsxs("div", { children: [
59
+ index > 0 && /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
60
+ /* @__PURE__ */ jsx(DropdownMenuLabel, { children: group }),
61
+ ITEMS.filter((item) => item.group === group).map((item) => /* @__PURE__ */ jsxs(
62
+ DropdownMenuItem,
63
+ {
64
+ disabled: busy !== null,
65
+ onSelect: () => void run(item.format),
66
+ className: cn("flex-col items-start gap-0.5"),
67
+ children: [
68
+ /* @__PURE__ */ jsxs("span", { className: "text-[13px] leading-tight text-(--ink)", children: [
69
+ item.title,
70
+ busy === item.format && " \u2026"
71
+ ] }),
72
+ /* @__PURE__ */ jsx("span", { className: "mono-meta text-(--ink-3-aa)", children: item.hint })
73
+ ]
74
+ },
75
+ item.format
76
+ ))
77
+ ] }, group)) })
78
+ ] });
79
+ }
80
+ async function exportFigure(target, title, format, options = {}) {
81
+ const svg = findArtwork(target);
82
+ if (!svg) throw new Error("exportFigure: found no <svg> to export inside the given element");
83
+ const paper = readToken(svg, "--diagram-surface", "#ffffff");
84
+ const ink = readToken(svg, "--ink", "#101010");
85
+ const font = getComputedStyle(svg).fontFamily || "sans-serif";
86
+ const plate = options.background === null ? void 0 : options.background ?? paper;
87
+ if (format === "share-card") {
88
+ const markup2 = serializeSvg(svg, {
89
+ padding: 56,
90
+ background: plate,
91
+ title,
92
+ titleColor: ink,
93
+ titleFont: font,
94
+ frame: SHARE_CARD
95
+ });
96
+ const blob2 = await rasterize(markup2, "png", { scale: 1 });
97
+ downloadBlob(blob2, exportFilename(title, "png", "share-card"));
98
+ return;
99
+ }
100
+ const markup = serializeSvg(svg, { padding: 20, background: plate });
101
+ if (format === "svg") {
102
+ downloadBlob(new Blob([markup], { type: "image/svg+xml;charset=utf-8" }), exportFilename(title, "svg"));
103
+ return;
104
+ }
105
+ const blob = await rasterize(markup, format, {
106
+ scale: 2,
107
+ // JPEG has no alpha channel, so an unflattened export lands on black.
108
+ background: format === "jpeg" ? paper : void 0
109
+ });
110
+ downloadBlob(blob, exportFilename(title, format));
111
+ }
112
+ function findArtwork(element) {
113
+ if (!element) return null;
114
+ if (element.tagName.toLowerCase() === "svg") return element;
115
+ return element.querySelector("svg[data-diagram-artwork]") ?? element.querySelector("svg");
116
+ }
117
+ var DiagramExportMenu_default = DiagramExportMenu;
118
+ export {
119
+ DiagramExportMenu,
120
+ DiagramExportMenu_default as default,
121
+ exportFigure
122
+ };
123
+ //# sourceMappingURL=DiagramExportMenu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/diagrams/DiagramExportMenu/DiagramExportMenu.tsx"],"sourcesContent":["'use client'\n\nimport { useState, type ReactNode, type RefObject } from 'react'\nimport { cn } from '../../lib/cn'\nimport {\n downloadBlob,\n exportFilename,\n rasterize,\n readToken,\n serializeSvg,\n} from '../../lib/svg-export'\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from '../../components/DropdownMenu/DropdownMenu'\nimport { Button } from '../../components/Button/Button'\n\n/** The files a figure can be taken away as. */\nexport type ExportFormat = 'png' | 'jpeg' | 'webp' | 'svg' | 'share-card'\n\n/** What came of an export, for a page that wants to say so. */\nexport interface ExportResult {\n format: ExportFormat\n /**\n * Whether the pipeline named by `source` finished without throwing.\n *\n * Read it with `source`, because the two sources can promise different\n * amounts. `built-in` means a file was handed to the browser. `caller` means\n * an `onExport` handler resolved — and a handler that did nothing resolves\n * exactly like one that wrote a file, which is a thing this component cannot\n * see and therefore does not claim.\n */\n ok: boolean\n error?: Error\n /** Which pipeline ran: this component's, or the caller's `onExport`. */\n source: 'built-in' | 'caller'\n}\n\n/** A share card is a fixed frame, because the surfaces that consume one are. */\nconst SHARE_CARD = { width: 1200, height: 630 } as const\n\nconst ITEMS: { format: ExportFormat; title: string; hint: string; group: string }[] = [\n { group: 'Image', format: 'png', title: 'PNG', hint: 'Lossless, 2× for retina' },\n { group: 'Image', format: 'jpeg', title: 'JPEG', hint: 'Compact, flattened onto paper' },\n { group: 'Image', format: 'webp', title: 'WebP', hint: 'Smaller, modern browsers' },\n { group: 'Vector', format: 'svg', title: 'SVG', hint: 'Editable, colours resolved' },\n { group: 'Share', format: 'share-card', title: 'Share card', hint: '1200 × 630 PNG' },\n]\n\nexport interface DiagramExportMenuProps {\n /**\n * The `<svg>` to export, or an element containing exactly one.\n *\n * A ref rather than a selector, because a page can hold several figures and a\n * selector would export whichever the document happened to reach first.\n */\n targetRef: RefObject<HTMLElement | SVGSVGElement | null>\n /** Names the file, and is printed on the share card. */\n title: string\n /** Replaces the trigger. */\n trigger?: ReactNode\n className?: string\n /**\n * Runs instead of the built-in export — for a caller with its own pipeline.\n *\n * Taking it turns everything below into a no-op, INCLUDING the part that\n * knows whether a file was produced: a result from this path reports\n * `source: 'caller'` and an `ok` that means only \"your handler resolved\".\n */\n onExport?: (format: ExportFormat) => void | Promise<void>\n /** Told what happened, so a page can raise a toast. */\n onResult?: (result: ExportResult) => void\n /**\n * Painted behind the artwork. Defaults to the surface the reader is looking\n * at; `null` exports on a transparent ground.\n *\n * JPEG has no alpha channel, so it is flattened onto the reader's own paper\n * whatever this says — a transparent JPEG is a black one.\n */\n background?: string | null\n}\n\n/**\n * Taking the figure off the page: five files, one menu.\n *\n * WHY THE MENU DOES THE WORK rather than handing back a format: every one of\n * these exports is the same six steps — find the `<svg>`, walk it with\n * `getComputedStyle` to bake the custom properties into real colours,\n * serialise, rasterise, name the file, hand it to the browser — and only the\n * last two differ between them. A menu that emitted `'png'` and left the caller\n * to do the rest would be a menu that every consumer reimplements, badly, and\n * the interesting half (a serialised SVG resolves `var(--ink)` to nothing and\n * comes out invisible) is exactly the half a caller would not know to write.\n *\n * `onExport` is still there for a page with its own pipeline — a server-side\n * renderer, a different frame size — and taking it turns everything below into\n * a no-op, which is why a result from that path says `source: 'caller'`: this\n * component cannot see whether the handler produced a file, so it does not\n * report that it did.\n *\n * What each format actually is, stated rather than implied.\n *\n * **SVG** is the artwork with resolved colours. It is editable and it is the\n * only lossless one, but it carries no web fonts: a machine without the family\n * renders it in a fallback, so type metrics will differ.\n *\n * **The ground** is the reader's own surface unless `background` says\n * otherwise. `background={null}` exports on transparency — for a figure being\n * dropped onto a coloured page — except in JPEG, which has no alpha and is\n * flattened onto paper whatever is asked for.\n *\n * **PNG, JPEG and WebP** are the browser's own rasteriser re-drawing that SVG\n * at 2×. Not a screenshot — antialiasing and any effect a page stylesheet\n * applied from OUTSIDE the `<svg>` are not in it. **JPEG has no alpha**, so it\n * is flattened onto the resolved paper colour rather than onto black, which is\n * what a transparent PNG becomes when a format with no transparency is asked\n * to hold it.\n *\n * **The share card** is a 1200 × 630 frame with the title on it and the whole\n * diagram letterboxed inside — never cropped. A card that cropped to fill the\n * frame would be a picture of a different diagram.\n */\nexport function DiagramExportMenu({\n targetRef,\n title,\n trigger,\n className,\n onExport,\n onResult,\n background,\n}: DiagramExportMenuProps) {\n const [busy, setBusy] = useState<ExportFormat | null>(null)\n\n async function run(format: ExportFormat) {\n const source = onExport ? 'caller' : 'built-in'\n setBusy(format)\n try {\n if (onExport) {\n await onExport(format)\n } else {\n await exportFigure(targetRef.current, title, format, { background })\n }\n // `ok` says the pipeline finished, and `source` says whose. Reporting a\n // bare success for a handler that may have done nothing was this menu\n // claiming to know something only the caller can.\n onResult?.({ format, ok: true, source })\n } catch (cause) {\n // Never swallowed: a download is something the reader just asked for, and\n // a click that quietly does nothing is indistinguishable from a broken\n // button. The caller gets the error to put in front of them.\n onResult?.({ format, ok: false, error: cause as Error, source })\n } finally {\n setBusy(null)\n }\n }\n\n const groups = [...new Set(ITEMS.map((item) => item.group))]\n\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n {trigger ?? (\n <Button size=\"sm\" variant=\"secondary\" className={className}>\n Export\n </Button>\n )}\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-64\">\n {groups.map((group, index) => (\n <div key={group}>\n {index > 0 && <DropdownMenuSeparator />}\n <DropdownMenuLabel>{group}</DropdownMenuLabel>\n {/* Menu ROWS, not buttons that happen to sit inside a menu. Radix\n gives an item roving focus, typeahead and close-on-pick; a\n plain <button> inside a role=\"menu\" gets none of the three and\n leaves the menu open over the file it just wrote. */}\n {ITEMS.filter((item) => item.group === group).map((item) => (\n <DropdownMenuItem\n key={item.format}\n disabled={busy !== null}\n onSelect={() => void run(item.format)}\n className={cn('flex-col items-start gap-0.5')}\n >\n <span className=\"text-[13px] leading-tight text-(--ink)\">\n {item.title}\n {busy === item.format && ' …'}\n </span>\n <span className=\"mono-meta text-(--ink-3-aa)\">{item.hint}</span>\n </DropdownMenuItem>\n ))}\n </div>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n )\n}\n\n/**\n * One figure, one format, one file.\n *\n * Exported so a page can wire a keyboard shortcut or its own button to exactly\n * what the menu does, without reimplementing the six steps.\n */\nexport async function exportFigure(\n target: HTMLElement | SVGSVGElement | null,\n title: string,\n format: ExportFormat,\n options: { background?: string | null } = {},\n): Promise<void> {\n const svg = findArtwork(target)\n if (!svg) throw new Error('exportFigure: found no <svg> to export inside the given element')\n\n // Read against the LIVE element, so the file comes out in whichever theme the\n // reader is looking at rather than in whichever one the code assumed.\n const paper = readToken(svg, '--diagram-surface', '#ffffff')\n const ink = readToken(svg, '--ink', '#101010')\n const font = getComputedStyle(svg).fontFamily || 'sans-serif'\n\n // `null` is a request, not a missing value: a figure dropped onto a coloured\n // page wants the page's own ground behind it, and a plate painted in every\n // format meant no export could ever give it one.\n const plate = options.background === null ? undefined : (options.background ?? paper)\n\n if (format === 'share-card') {\n const markup = serializeSvg(svg, {\n padding: 56,\n background: plate,\n title,\n titleColor: ink,\n titleFont: font,\n frame: SHARE_CARD,\n })\n const blob = await rasterize(markup, 'png', { scale: 1 })\n downloadBlob(blob, exportFilename(title, 'png', 'share-card'))\n return\n }\n\n const markup = serializeSvg(svg, { padding: 20, background: plate })\n\n if (format === 'svg') {\n downloadBlob(new Blob([markup], { type: 'image/svg+xml;charset=utf-8' }), exportFilename(title, 'svg'))\n return\n }\n\n const blob = await rasterize(markup, format, {\n scale: 2,\n // JPEG has no alpha channel, so an unflattened export lands on black.\n background: format === 'jpeg' ? paper : undefined,\n })\n downloadBlob(blob, exportFilename(title, format))\n}\n\n/**\n * The figure's own `<svg>`, not the first one in the subtree.\n *\n * The distinction is load-bearing: a toolbar sits inside the same wrapper and\n * every icon in it is an `<svg>`. Looking for the artwork marker first is what\n * stops an export of a diagram from being a picture of a chevron.\n */\nfunction findArtwork(element: HTMLElement | SVGSVGElement | null): SVGSVGElement | null {\n if (!element) return null\n if (element.tagName.toLowerCase() === 'svg') return element as SVGSVGElement\n return (\n element.querySelector<SVGSVGElement>('svg[data-diagram-artwork]') ??\n element.querySelector<SVGSVGElement>('svg')\n )\n}\n\nexport default DiagramExportMenu\n"],"mappings":"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","names":["markup","blob"]}
@@ -0,0 +1,70 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+
4
+ /** One fact about the selected thing: a short label and its value. */
5
+ interface InspectorFact {
6
+ label: string;
7
+ value: ReactNode;
8
+ /** Sets the value in mono — an id, a path, a version, a port. */
9
+ mono?: boolean;
10
+ }
11
+ /** A relationship the selected thing takes part in. */
12
+ interface InspectorLink {
13
+ id?: string;
14
+ /** Which way it runs, relative to the selected node. */
15
+ direction: 'in' | 'out';
16
+ label: string;
17
+ /** What sits at the other end. */
18
+ peer: string;
19
+ onSelect?: () => void;
20
+ }
21
+ interface DiagramInspectorProps {
22
+ /** The kicker over the title — what KIND of thing this panel is showing. */
23
+ eyebrow?: string;
24
+ title: string;
25
+ description?: ReactNode;
26
+ facts?: InspectorFact[];
27
+ links?: InspectorLink[];
28
+ /** Buttons under the facts — copy a link, open the source. */
29
+ actions?: ReactNode;
30
+ onClose?: () => void;
31
+ className?: string;
32
+ /** Renders as a floating panel over the surface rather than in the flow. */
33
+ floating?: boolean;
34
+ }
35
+ /**
36
+ * What the reader just picked, written out.
37
+ *
38
+ * A diagram can hold about eight words per node before it stops being a diagram
39
+ * and starts being a document with lines drawn on it. Everything past those
40
+ * eight — the port, the owning team, the file it was read out of, the six
41
+ * relationships it takes part in — belongs beside the picture rather than
42
+ * inside it, and this is that place.
43
+ *
44
+ * NOT A DIALOG, and that is deliberate. An inspector is a REGION that changes
45
+ * with the selection, not a modal. Giving it `role="dialog"` would trap focus
46
+ * and demand dismissal for something the reader never opened — they clicked a
47
+ * node, and the panel followed. So it is a labelled region with
48
+ * `aria-live="polite"`, which is what makes a screen reader announce the new
49
+ * selection without stealing the cursor from whatever the reader was doing.
50
+ *
51
+ * The relationships are real buttons when they carry `onSelect`, which is how
52
+ * the diagram becomes navigable from the keyboard: a reader can walk the graph
53
+ * peer by peer without ever touching the picture.
54
+ *
55
+ * @example
56
+ * <DiagramInspector
57
+ * eyebrow="Service"
58
+ * title="API Server"
59
+ * description="FastAPI, behind the load balancer."
60
+ * facts={[
61
+ * { label: 'Port', value: '8000', mono: true },
62
+ * { label: 'Id', value: 'api', mono: true },
63
+ * ]}
64
+ * links={[{ direction: 'out', label: 'SQL', peer: 'PostgreSQL' }]}
65
+ * onClose={() => setSelected(null)}
66
+ * />
67
+ */
68
+ declare function DiagramInspector({ eyebrow, title, description, facts, links, actions, onClose, className, floating, }: DiagramInspectorProps): react.JSX.Element;
69
+
70
+ export { DiagramInspector, type DiagramInspectorProps, type InspectorFact, type InspectorLink, DiagramInspector as default };