@keboola/design 11.9.0 → 12.0.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 (363) hide show
  1. package/AGENTS.md +34 -8
  2. package/CHANGELOG.md +155 -0
  3. package/COMPONENT-API.md +337 -289
  4. package/README.md +11 -1
  5. package/dist/{AIKit-ELIsegJz.js → AIKit-BD4r7ff-.js} +404 -243
  6. package/dist/AIKit-BD4r7ff-.js.map +1 -0
  7. package/dist/{Alert-BsIM_oFK.js → Alert-UI-CIs-1.js} +16 -16
  8. package/dist/Alert-UI-CIs-1.js.map +1 -0
  9. package/dist/{AlertModal-BGJdaRRj.js → AlertModal-Th2khjPi.js} +5 -5
  10. package/dist/AlertModal-Th2khjPi.js.map +1 -0
  11. package/dist/{AppShell-DRgJxpXW.js → AppShell-WSQabK8r.js} +15 -52
  12. package/dist/AppShell-WSQabK8r.js.map +1 -0
  13. package/dist/{AutoSaveTextarea-BeuWuszr.js → AutoSaveTextarea-C5iIr2Ts.js} +2 -2
  14. package/dist/{AutoSaveTextarea-BeuWuszr.js.map → AutoSaveTextarea-C5iIr2Ts.js.map} +1 -1
  15. package/dist/{Badge-o5jURtpB.js → Badge-DIjhwPvh.js} +4 -9
  16. package/dist/Badge-DIjhwPvh.js.map +1 -0
  17. package/dist/{BrandEditor-BsTBM3s6.js → BrandEditor-BU23Vl8V.js} +7 -7
  18. package/dist/{BrandEditor-BsTBM3s6.js.map → BrandEditor-BU23Vl8V.js.map} +1 -1
  19. package/dist/{BrandLogo-CoKBk4MI.js → BrandLogo-CKpVSZb6.js} +4 -4
  20. package/dist/BrandLogo-CKpVSZb6.js.map +1 -0
  21. package/dist/{Breadcrumb-xqEUxfwn.js → Breadcrumb-p9HtMLCc.js} +8 -14
  22. package/dist/Breadcrumb-p9HtMLCc.js.map +1 -0
  23. package/dist/{Button-DsRrU5F1.js → Button-Ca4Q3VJU.js} +31 -13
  24. package/dist/Button-Ca4Q3VJU.js.map +1 -0
  25. package/dist/{Calendar-cG5NIJ4O.js → Calendar-DFtmZzbF.js} +99 -99
  26. package/dist/Calendar-DFtmZzbF.js.map +1 -0
  27. package/dist/{Card-Dyo7055Q.js → Card-Cr9MBNHu.js} +4 -4
  28. package/dist/Card-Cr9MBNHu.js.map +1 -0
  29. package/dist/{Checkbox-Dq5fPNrO.js → Checkbox-8yIIgY7g.js} +4 -4
  30. package/dist/Checkbox-8yIIgY7g.js.map +1 -0
  31. package/dist/{Clipboard-BZitKiSA.js → Clipboard-_xf6hMDD.js} +2 -2
  32. package/dist/{Clipboard-BZitKiSA.js.map → Clipboard-_xf6hMDD.js.map} +1 -1
  33. package/dist/{ClipboardButton-KHQnPEh_.js → ClipboardButton-DHa1trtI.js} +4 -4
  34. package/dist/{ClipboardButton-KHQnPEh_.js.map → ClipboardButton-DHa1trtI.js.map} +1 -1
  35. package/dist/{CodeEditor-BJKJXA0c.js → CodeEditor-BSo-oLFG.js} +55 -165
  36. package/dist/CodeEditor-BSo-oLFG.js.map +1 -0
  37. package/dist/{ColorInput-Bj3dfPFA.js → ColorInput-DSMqrs2-.js} +12 -27
  38. package/dist/ColorInput-DSMqrs2-.js.map +1 -0
  39. package/dist/{ColorModeSwitch-CWt-DZGC.js → ColorModeSwitch-D7EuWyJz.js} +7 -17
  40. package/dist/ColorModeSwitch-D7EuWyJz.js.map +1 -0
  41. package/dist/Combobox-CxRAYOBt.js +1069 -0
  42. package/dist/Combobox-CxRAYOBt.js.map +1 -0
  43. package/dist/{Command-BDU3C1gg.js → Command-BU_cVurn.js} +4 -4
  44. package/dist/{Command-BDU3C1gg.js.map → Command-BU_cVurn.js.map} +1 -1
  45. package/dist/{CopyField-BHRQUyuF.js → CopyField-6_c-NdLO.js} +17 -53
  46. package/dist/{CopyField-BHRQUyuF.js.map → CopyField-6_c-NdLO.js.map} +1 -1
  47. package/dist/{DateInput-B6N8g0ra.js → DateInput-DyEErfJf.js} +6 -6
  48. package/dist/{DateInput-B6N8g0ra.js.map → DateInput-DyEErfJf.js.map} +1 -1
  49. package/dist/{DatePicker-nA8YdE84.js → DatePicker-CtSa9b3V.js} +3 -3
  50. package/dist/{DatePicker-nA8YdE84.js.map → DatePicker-CtSa9b3V.js.map} +1 -1
  51. package/dist/{DateRangeInput-7M9Z4cVO.js → DateRangeInput-Dg7-VUd9.js} +10 -14
  52. package/dist/DateRangeInput-Dg7-VUd9.js.map +1 -0
  53. package/dist/{DateRangePicker-DjSUFjh4.js → DateRangePicker-BoPnh4aH.js} +9 -13
  54. package/dist/DateRangePicker-BoPnh4aH.js.map +1 -0
  55. package/dist/DiffView-ppTIzh2h.js.map +1 -1
  56. package/dist/{DropdownMenu-CgVwhDvl.js → DropdownMenu-gRM-A8qU.js} +3 -3
  57. package/dist/DropdownMenu-gRM-A8qU.js.map +1 -0
  58. package/dist/{FieldItem-DZGIdiXE.js → FieldItem-2wMXQ0Qw.js} +12 -25
  59. package/dist/FieldItem-2wMXQ0Qw.js.map +1 -0
  60. package/dist/{FieldItem-vhIJW4pj.d.ts → FieldItem-CPN1xjN5.d.ts} +11 -25
  61. package/dist/{FileUpload-DYQCTGjU.js → FileUpload-C83tXuE2.js} +11 -16
  62. package/dist/{FileUpload-DYQCTGjU.js.map → FileUpload-C83tXuE2.js.map} +1 -1
  63. package/dist/{Form-CcK7dRrC.js → Form-95rVLfX8.js} +15 -23
  64. package/dist/{Form-CcK7dRrC.js.map → Form-95rVLfX8.js.map} +1 -1
  65. package/dist/{FormGroup-DR_gZ25-.js → FormGroup-CmNiAtU6.js} +11 -17
  66. package/dist/FormGroup-CmNiAtU6.js.map +1 -0
  67. package/dist/{FullScreenModal-CN9nAs72.js → FullScreenModal-CD7mYeTz.js} +15 -29
  68. package/dist/FullScreenModal-CD7mYeTz.js.map +1 -0
  69. package/dist/{HoverCard-B9ZMJxjn.js → HoverCard-B8OKu9XZ.js} +2 -2
  70. package/dist/{HoverCard-B9ZMJxjn.js.map → HoverCard-B8OKu9XZ.js.map} +1 -1
  71. package/dist/{InputGroup-Do8K2hvD.js → InputGroup-B4QOPxHl.js} +11 -13
  72. package/dist/InputGroup-B4QOPxHl.js.map +1 -0
  73. package/dist/{Kbd-LCFMumoq.js → Kbd-J4n1J6i4.js} +23 -13
  74. package/dist/Kbd-J4n1J6i4.js.map +1 -0
  75. package/dist/{MarkedText-DNGEUVWc.js → MarkedText-DlmlJO0u.js} +2 -2
  76. package/dist/{MarkedText-DNGEUVWc.js.map → MarkedText-DlmlJO0u.js.map} +1 -1
  77. package/dist/{MermaidDiagram-ZG0iiDV_.js → MermaidDiagram-BQvz6bhv.js} +16 -34
  78. package/dist/MermaidDiagram-BQvz6bhv.js.map +1 -0
  79. package/dist/{Modal-I3VWk2oo.js → Modal-Driyqs_N.js} +17 -34
  80. package/dist/Modal-Driyqs_N.js.map +1 -0
  81. package/dist/{ModalTitle-CeLM_mX9.js → ModalTitle-BxopkIoQ.js} +3 -3
  82. package/dist/ModalTitle-BxopkIoQ.js.map +1 -0
  83. package/dist/{MultiActionButtonGroup-CYZ8IgSf.js → MultiActionButtonGroup-BPnmzBtw.js} +3 -3
  84. package/dist/{MultiActionButtonGroup-CYZ8IgSf.js.map → MultiActionButtonGroup-BPnmzBtw.js.map} +1 -1
  85. package/dist/{Navbar-Liy1TPI7.js → Navbar-B_DNpv5s.js} +9 -13
  86. package/dist/{Navbar-Liy1TPI7.js.map → Navbar-B_DNpv5s.js.map} +1 -1
  87. package/dist/{OtpInput-CbbymqYs.js → OtpInput-I8B8Vjdl.js} +2 -2
  88. package/dist/{OtpInput-CbbymqYs.js.map → OtpInput-I8B8Vjdl.js.map} +1 -1
  89. package/dist/{PageHeader-Q3IpCnQd.js → PageHeader-CgfyXpf1.js} +13 -26
  90. package/dist/PageHeader-CgfyXpf1.js.map +1 -0
  91. package/dist/{PageLayout-DSVM-ASz.js → PageLayout-DiAAILuS.js} +12 -20
  92. package/dist/PageLayout-DiAAILuS.js.map +1 -0
  93. package/dist/{PageToolbar-BKVJiDAL.js → PageToolbar-DOAtAnfL.js} +12 -42
  94. package/dist/PageToolbar-DOAtAnfL.js.map +1 -0
  95. package/dist/{Pagination-C_b7Xhey.js → Pagination-LaSg-5cl.js} +3 -3
  96. package/dist/{Pagination-C_b7Xhey.js.map → Pagination-LaSg-5cl.js.map} +1 -1
  97. package/dist/{Popconfirm-5pBzT_HQ.js → Popconfirm-SLz0zodk.js} +3 -3
  98. package/dist/{Popconfirm-5pBzT_HQ.js.map → Popconfirm-SLz0zodk.js.map} +1 -1
  99. package/dist/{Popover-BEbQrC_P.js → Popover-CEk915I1.js} +4 -3
  100. package/dist/Popover-CEk915I1.js.map +1 -0
  101. package/dist/{RadioGroup-Byc2mym4.js → RadioGroup-CBPRLRN0.js} +4 -4
  102. package/dist/{RadioGroup-Byc2mym4.js.map → RadioGroup-CBPRLRN0.js.map} +1 -1
  103. package/dist/{Search-Dhig_hPD.js → Search-DmkQo716.js} +3 -3
  104. package/dist/{Search-Dhig_hPD.js.map → Search-DmkQo716.js.map} +1 -1
  105. package/dist/{SearchInput-92r2bkA7.js → SearchInput-CEfQDNl3.js} +8 -13
  106. package/dist/SearchInput-CEfQDNl3.js.map +1 -0
  107. package/dist/{SectionNav-CNLJsiVG.js → SectionNav-B_qf0AXi.js} +10 -42
  108. package/dist/SectionNav-B_qf0AXi.js.map +1 -0
  109. package/dist/{Select-DV3Hak_l.js → Select-ByRvkZ3D.js} +8 -11
  110. package/dist/{Select-DV3Hak_l.js.map → Select-ByRvkZ3D.js.map} +1 -1
  111. package/dist/{Sheet-OparjIi-.js → Sheet-C-yXk5Da.js} +5 -9
  112. package/dist/Sheet-C-yXk5Da.js.map +1 -0
  113. package/dist/{Sidebar-CLEbBtNY.js → Sidebar-Ds0E6PbR.js} +16 -33
  114. package/dist/Sidebar-Ds0E6PbR.js.map +1 -0
  115. package/dist/{Slider-DLclvRws.js → Slider-DWdu9NuM.js} +3 -3
  116. package/dist/{Slider-DLclvRws.js.map → Slider-DWdu9NuM.js.map} +1 -1
  117. package/dist/{Stepper-C8sm9ZFM.js → Stepper-BLkLhfnd.js} +6 -9
  118. package/dist/{Stepper-C8sm9ZFM.js.map → Stepper-BLkLhfnd.js.map} +1 -1
  119. package/dist/{Switch-B0WdGSjz.js → Switch-DZvNePOm.js} +2 -2
  120. package/dist/{Switch-B0WdGSjz.js.map → Switch-DZvNePOm.js.map} +1 -1
  121. package/dist/{Table-CvYuTuNU.js → Table-BaVT5Tkn.js} +6 -11
  122. package/dist/{Table-CvYuTuNU.js.map → Table-BaVT5Tkn.js.map} +1 -1
  123. package/dist/{Tabs-eB974xJf.js → Tabs-Dt9oLYHC.js} +3 -3
  124. package/dist/{Tabs-eB974xJf.js.map → Tabs-Dt9oLYHC.js.map} +1 -1
  125. package/dist/{TextInput-CFTf4Cz1.js → TextInput-CDG2XpBl.js} +10 -14
  126. package/dist/TextInput-CDG2XpBl.js.map +1 -0
  127. package/dist/{ThemeProvider-DAmNn9Sd.js → ThemeProvider-DXsxFg1U.js} +13 -31
  128. package/dist/ThemeProvider-DXsxFg1U.js.map +1 -0
  129. package/dist/{TimePicker-BGeqc-Ed.js → TimePicker-CDGYrRxi.js} +2 -2
  130. package/dist/{TimePicker-BGeqc-Ed.js.map → TimePicker-CDGYrRxi.js.map} +1 -1
  131. package/dist/{Timeline-C8-wnRRr.js → Timeline-B-I6sb0M.js} +2 -2
  132. package/dist/{Timeline-C8-wnRRr.js.map → Timeline-B-I6sb0M.js.map} +1 -1
  133. package/dist/{Toast-Dr6Uw7ao.js → Toast-PfQf3vj-.js} +2 -2
  134. package/dist/{Toast-Dr6Uw7ao.js.map → Toast-PfQf3vj-.js.map} +1 -1
  135. package/dist/{Workbench-UR2FBC7I.js → Workbench-DmZzpkj7.js} +2 -2
  136. package/dist/{Workbench-UR2FBC7I.js.map → Workbench-DmZzpkj7.js.map} +1 -1
  137. package/dist/{ZoomableSvg-BZlk3PFJ.js → ZoomableSvg-BzXVfgnI.js} +5 -19
  138. package/dist/ZoomableSvg-BzXVfgnI.js.map +1 -0
  139. package/dist/charts.d.ts +21 -52
  140. package/dist/charts.js +13 -39
  141. package/dist/charts.js.map +1 -1
  142. package/dist/{context-D7X470dy.js → context-U293r3v-.js} +5 -9
  143. package/dist/context-U293r3v-.js.map +1 -0
  144. package/dist/editors.d.ts +27 -124
  145. package/dist/editors.js +29 -71
  146. package/dist/editors.js.map +1 -1
  147. package/dist/{findLiteralMatches-BGBw8Vbl.js → findLiteralMatches-DgGLzyG0.js} +8 -20
  148. package/dist/findLiteralMatches-DgGLzyG0.js.map +1 -0
  149. package/dist/flint.d.ts +3 -6
  150. package/dist/flint.js +31 -80
  151. package/dist/flint.js.map +1 -1
  152. package/dist/{icons-Cek151jC.js → icons-wcA70iQx.js} +105 -2
  153. package/dist/icons-wcA70iQx.js.map +1 -0
  154. package/dist/icons.d.ts +2 -2
  155. package/dist/icons.js +2 -2
  156. package/dist/{index-m-kQEWiY.d.ts → index-B7k-09i1.d.ts} +14 -2
  157. package/dist/index.d.ts +1151 -1838
  158. package/dist/index.js +68 -67
  159. package/dist/internal/blocks/AIKit.js +2 -2
  160. package/dist/internal/blocks/AppShell.js +1 -1
  161. package/dist/internal/blocks/AuthLayout.js +7 -27
  162. package/dist/internal/blocks/AuthLayout.js.map +1 -1
  163. package/dist/internal/blocks/AutoSaveTextarea.js +1 -1
  164. package/dist/internal/blocks/BrandEditor.js +1 -1
  165. package/dist/internal/blocks/ChartPanel.js +4 -8
  166. package/dist/internal/blocks/ChartPanel.js.map +1 -1
  167. package/dist/internal/blocks/ChipEditor.js +13 -21
  168. package/dist/internal/blocks/ChipEditor.js.map +1 -1
  169. package/dist/internal/blocks/ClipboardButton.js +1 -1
  170. package/dist/internal/blocks/ColorInput.js +1 -1
  171. package/dist/internal/blocks/ColorModeSwitch.js +1 -1
  172. package/dist/internal/blocks/Command.js +1 -1
  173. package/dist/internal/blocks/CopyField.js +1 -1
  174. package/dist/internal/blocks/DashboardHero.js +4 -7
  175. package/dist/internal/blocks/DashboardHero.js.map +1 -1
  176. package/dist/internal/blocks/DateInput.js +1 -1
  177. package/dist/internal/blocks/DateRangeInput.js +1 -1
  178. package/dist/internal/blocks/EmptyConfigurationActionCard.js +1 -1
  179. package/dist/internal/blocks/EmptyState.js +5 -9
  180. package/dist/internal/blocks/EmptyState.js.map +1 -1
  181. package/dist/internal/blocks/ErrorState.js +5 -9
  182. package/dist/internal/blocks/ErrorState.js.map +1 -1
  183. package/dist/internal/blocks/ExportMenu.js +6 -10
  184. package/dist/internal/blocks/ExportMenu.js.map +1 -1
  185. package/dist/internal/blocks/Form/connectors/ComboboxField.js +40 -0
  186. package/dist/internal/blocks/Form/connectors/ComboboxField.js.map +1 -0
  187. package/dist/internal/blocks/Form/connectors/MultiComboboxField.js +5 -22
  188. package/dist/internal/blocks/Form/connectors/MultiComboboxField.js.map +1 -1
  189. package/dist/internal/blocks/Form.js +1 -1
  190. package/dist/internal/blocks/FormGroup.js +1 -1
  191. package/dist/internal/blocks/GoogleButton.js +1 -1
  192. package/dist/internal/blocks/MicrosoftButton.js +1 -1
  193. package/dist/internal/blocks/MultiActionButtonGroup.js +1 -1
  194. package/dist/internal/blocks/MultiCombobox.js +161 -154
  195. package/dist/internal/blocks/MultiCombobox.js.map +1 -1
  196. package/dist/internal/blocks/NumberInput.js +1 -1
  197. package/dist/internal/blocks/OtpInput.js +1 -1
  198. package/dist/internal/blocks/PageHeader.js +1 -1
  199. package/dist/internal/blocks/PageLayout.js +1 -1
  200. package/dist/internal/blocks/PageToolbar.js +1 -1
  201. package/dist/internal/blocks/PasswordInput.js +4 -4
  202. package/dist/internal/blocks/PasswordInput.js.map +1 -1
  203. package/dist/internal/blocks/SearchInput.js +1 -1
  204. package/dist/internal/blocks/SearchInputWithNavigation.js +4 -4
  205. package/dist/internal/blocks/SectionNav.js +1 -1
  206. package/dist/internal/blocks/SplitButton.js +3 -3
  207. package/dist/internal/blocks/StatCard.js +4 -8
  208. package/dist/internal/blocks/StatCard.js.map +1 -1
  209. package/dist/internal/blocks/TextInput.js +1 -1
  210. package/dist/internal/blocks/Textarea.js +2 -2
  211. package/dist/internal/blocks/TimeInput.js +5 -5
  212. package/dist/internal/blocks/TimeInput.js.map +1 -1
  213. package/dist/internal/components/Alert.js +1 -1
  214. package/dist/internal/components/AlertModal.js +1 -1
  215. package/dist/internal/components/Avatar.js +1 -1
  216. package/dist/internal/components/Badge.js +1 -1
  217. package/dist/internal/components/BrandLogo.js +1 -1
  218. package/dist/internal/components/Breadcrumb.js +1 -1
  219. package/dist/internal/components/Button.js +1 -1
  220. package/dist/internal/components/ButtonGroup.js.map +1 -1
  221. package/dist/internal/components/Calendar.js +1 -1
  222. package/dist/internal/components/Card.js +1 -1
  223. package/dist/internal/components/Checkbox.js +1 -1
  224. package/dist/internal/components/Clipboard.js +1 -1
  225. package/dist/internal/components/CollapsiblePanel.js +2 -2
  226. package/dist/internal/components/Combobox.js +2 -2
  227. package/dist/internal/components/DatePicker.js +1 -1
  228. package/dist/internal/components/DateRangePicker.js +1 -1
  229. package/dist/internal/components/DropdownMenu.js +1 -1
  230. package/dist/internal/components/FileUpload.js +1 -1
  231. package/dist/internal/components/FullScreenModal.js +1 -1
  232. package/dist/internal/components/HoverCard.js +1 -1
  233. package/dist/internal/components/InputGroup.js +1 -1
  234. package/dist/internal/components/Kbd.js +1 -1
  235. package/dist/internal/components/MarkedText.js +1 -1
  236. package/dist/internal/components/MergeEditor/ConflictStatus/ConflictStatusIndicator.js +1 -1
  237. package/dist/internal/components/Modal.js +1 -1
  238. package/dist/internal/components/Navbar.js +1 -1
  239. package/dist/internal/components/Pagination.js +1 -1
  240. package/dist/internal/components/PanelWithDetails.js +1 -1
  241. package/dist/internal/components/Popconfirm.js +1 -1
  242. package/dist/internal/components/Popover.js +1 -1
  243. package/dist/internal/components/RadioGroup.js +1 -1
  244. package/dist/internal/components/Search.js +1 -1
  245. package/dist/internal/components/Select.js +1 -1
  246. package/dist/internal/components/Separator.js.map +1 -1
  247. package/dist/internal/components/Sheet.js +1 -1
  248. package/dist/internal/components/Sidebar.js +1 -1
  249. package/dist/internal/components/Skeleton/Skeleton.js +5 -9
  250. package/dist/internal/components/Skeleton/Skeleton.js.map +1 -1
  251. package/dist/internal/components/Slider.js +1 -1
  252. package/dist/internal/components/SpotlightBackdrop.js +3 -8
  253. package/dist/internal/components/SpotlightBackdrop.js.map +1 -1
  254. package/dist/internal/components/StatusDot.js +8 -15
  255. package/dist/internal/components/StatusDot.js.map +1 -1
  256. package/dist/internal/components/Stepper.js +1 -1
  257. package/dist/internal/components/Switch.js +1 -1
  258. package/dist/internal/components/Table.js +1 -1
  259. package/dist/internal/components/Tabs.js +1 -1
  260. package/dist/internal/components/ThemeProvider.js +3 -3
  261. package/dist/internal/components/TimePicker.js +1 -1
  262. package/dist/internal/components/Timeline.js +1 -1
  263. package/dist/internal/components/Toast.js +2 -2
  264. package/dist/internal/components/Tooltip.js +1 -1
  265. package/dist/internal/components/Truncated.js +4 -8
  266. package/dist/internal/components/Truncated.js.map +1 -1
  267. package/dist/internal/components/Workbench.js +1 -1
  268. package/dist/internal/components/ZoomableSvg.js +1 -1
  269. package/dist/internal/hooks/useScrollbarPosition.js +1 -1
  270. package/dist/internal/hooks/useStableCallback.js +4 -13
  271. package/dist/internal/hooks/useStableCallback.js.map +1 -1
  272. package/dist/internal/utils.js +2 -2
  273. package/dist/markdown-editor.d.ts +15 -37
  274. package/dist/markdown-editor.js +17 -23
  275. package/dist/markdown-editor.js.map +1 -1
  276. package/dist/mermaid.d.ts +4 -6
  277. package/dist/mermaid.js +1 -1
  278. package/dist/rjsf.d.ts +9 -19
  279. package/dist/rjsf.js +44 -84
  280. package/dist/rjsf.js.map +1 -1
  281. package/dist/{styles-BAFscjSV.js → styles-gzcjOyKv.js} +6 -6
  282. package/dist/styles-gzcjOyKv.js.map +1 -0
  283. package/dist/styles.css +1 -1
  284. package/dist/svgTransforms-Dw2hRA-i.js +51 -0
  285. package/dist/svgTransforms-Dw2hRA-i.js.map +1 -0
  286. package/dist/testing.d.ts +3 -10
  287. package/dist/testing.js +5 -12
  288. package/dist/testing.js.map +1 -1
  289. package/dist/themeTokens-7DEab0kp.js +31 -0
  290. package/dist/themeTokens-7DEab0kp.js.map +1 -0
  291. package/dist/{toast-DIC0iNRf.js → toast-CyXEdaCZ.js} +3 -3
  292. package/dist/toast-CyXEdaCZ.js.map +1 -0
  293. package/dist/{themeScopeContext-D7fSyb06.js → useColorSchemeState-Cw4MCXUs.js} +14 -14
  294. package/dist/useColorSchemeState-Cw4MCXUs.js.map +1 -0
  295. package/dist/{useControllableState-M6494mCT.js → useControllableState-CUYXMsfz.js} +3 -7
  296. package/dist/useControllableState-CUYXMsfz.js.map +1 -0
  297. package/dist/useElement-D-RjrGa4.js +21 -0
  298. package/dist/useElement-D-RjrGa4.js.map +1 -0
  299. package/dist/useThemeAttributes-B199Regy.js +67 -0
  300. package/dist/useThemeAttributes-B199Regy.js.map +1 -0
  301. package/dist/{utils-BW4AE8VP.js → utils-Bd28bWgm.js} +31 -26
  302. package/dist/utils-Bd28bWgm.js.map +1 -0
  303. package/dist/vega.d.ts +8 -30
  304. package/dist/vega.js +5 -13
  305. package/dist/vega.js.map +1 -1
  306. package/dist/{vegaTheme-CSvFEMBl.js → vegaTheme-VQMWTlBw.js} +7 -21
  307. package/dist/vegaTheme-VQMWTlBw.js.map +1 -0
  308. package/package.json +10 -9
  309. package/src/styles.css +120 -14
  310. package/src/theme.css +126 -6
  311. package/dist/AIKit-ELIsegJz.js.map +0 -1
  312. package/dist/Alert-BsIM_oFK.js.map +0 -1
  313. package/dist/AlertModal-BGJdaRRj.js.map +0 -1
  314. package/dist/AppShell-DRgJxpXW.js.map +0 -1
  315. package/dist/Badge-o5jURtpB.js.map +0 -1
  316. package/dist/BrandLogo-CoKBk4MI.js.map +0 -1
  317. package/dist/Breadcrumb-xqEUxfwn.js.map +0 -1
  318. package/dist/Button-DsRrU5F1.js.map +0 -1
  319. package/dist/Calendar-cG5NIJ4O.js.map +0 -1
  320. package/dist/Card-Dyo7055Q.js.map +0 -1
  321. package/dist/Checkbox-Dq5fPNrO.js.map +0 -1
  322. package/dist/CodeEditor-BJKJXA0c.js.map +0 -1
  323. package/dist/ColorInput-Bj3dfPFA.js.map +0 -1
  324. package/dist/ColorModeSwitch-CWt-DZGC.js.map +0 -1
  325. package/dist/Combobox-D8PrEqTG.js +0 -619
  326. package/dist/Combobox-D8PrEqTG.js.map +0 -1
  327. package/dist/DateRangeInput-7M9Z4cVO.js.map +0 -1
  328. package/dist/DateRangePicker-DjSUFjh4.js.map +0 -1
  329. package/dist/DropdownMenu-CgVwhDvl.js.map +0 -1
  330. package/dist/FieldItem-DZGIdiXE.js.map +0 -1
  331. package/dist/FormGroup-DR_gZ25-.js.map +0 -1
  332. package/dist/FullScreenModal-CN9nAs72.js.map +0 -1
  333. package/dist/InputGroup-Do8K2hvD.js.map +0 -1
  334. package/dist/Kbd-LCFMumoq.js.map +0 -1
  335. package/dist/MermaidDiagram-ZG0iiDV_.js.map +0 -1
  336. package/dist/Modal-I3VWk2oo.js.map +0 -1
  337. package/dist/ModalTitle-CeLM_mX9.js.map +0 -1
  338. package/dist/PageHeader-Q3IpCnQd.js.map +0 -1
  339. package/dist/PageLayout-DSVM-ASz.js.map +0 -1
  340. package/dist/PageToolbar-BKVJiDAL.js.map +0 -1
  341. package/dist/Popover-BEbQrC_P.js.map +0 -1
  342. package/dist/SearchInput-92r2bkA7.js.map +0 -1
  343. package/dist/SectionNav-CNLJsiVG.js.map +0 -1
  344. package/dist/Sheet-OparjIi-.js.map +0 -1
  345. package/dist/Sidebar-CLEbBtNY.js.map +0 -1
  346. package/dist/TextInput-CFTf4Cz1.js.map +0 -1
  347. package/dist/ThemeProvider-DAmNn9Sd.js.map +0 -1
  348. package/dist/ZoomableSvg-BZlk3PFJ.js.map +0 -1
  349. package/dist/context-D7X470dy.js.map +0 -1
  350. package/dist/findLiteralMatches-BGBw8Vbl.js.map +0 -1
  351. package/dist/icons-Cek151jC.js.map +0 -1
  352. package/dist/styles-BAFscjSV.js.map +0 -1
  353. package/dist/svgTransforms-CJAYPk7o.js +0 -81
  354. package/dist/svgTransforms-CJAYPk7o.js.map +0 -1
  355. package/dist/themeScopeContext-D7fSyb06.js.map +0 -1
  356. package/dist/themeTokens-qP1-55O7.js +0 -119
  357. package/dist/themeTokens-qP1-55O7.js.map +0 -1
  358. package/dist/toast-DIC0iNRf.js.map +0 -1
  359. package/dist/useControllableState-M6494mCT.js.map +0 -1
  360. package/dist/useElement-BK3AAiIf.js +0 -24
  361. package/dist/useElement-BK3AAiIf.js.map +0 -1
  362. package/dist/utils-BW4AE8VP.js.map +0 -1
  363. package/dist/vegaTheme-CSvFEMBl.js.map +0 -1
package/dist/flint.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"flint.js","names":[],"sources":["../src/blocks/FlintChart/flintTheme.ts","../src/blocks/FlintChart/refuseUnsafeSpec.ts","../src/blocks/FlintChart/renderFlintSvg.ts","../src/blocks/FlintChart/FlintChart.tsx"],"sourcesContent":["import type { ChartAssemblyInput } from 'flint-chart';\n\nimport type { ResolvedColorScheme } from '../../components/ThemeProvider/types';\nimport { createVegaColorResolver, SERIES_TOKENS, vegaRgb } from '../../vega/vegaTokens';\n\n/**\n * How the second palette tier (series 6-10) is derived from the core hues.\n *\n * - Each core hue is pushed *away* from its own surface, so the tier stays\n * in-family but reads as a distinct series: darkened on light, lightened on\n * dark. A single \"multiply the channels\" rule would darken in both, which on\n * a dark surface moves toward the backdrop — measured, that drops contrast\n * from ~6.7:1 to ~3.7:1, while lightening raises it to ~9:1.\n * - `amount` is the fraction of the distance travelled toward black (light) or\n * white (dark).\n */\nconst TIER_TWO_SHIFT = {\n light: { towards: 0, amount: 0.38 },\n dark: { towards: 255, amount: 0.35 },\n} as const;\n\n/**\n * Move an `rgb()` string's channels a fraction of the way toward `towards`\n * (0 = black, 255 = white). Returns the input unchanged if it is not that shape.\n */\nconst shiftChannels = (colour: string, towards: number, amount: number): string => {\n const channels = colour.match(/\\d+/g);\n if (!channels || channels.length < 3) return colour;\n const shifted = channels\n .slice(0, 3)\n .map((c) => Math.round(Number(c) + (towards - Number(c)) * amount));\n return vegaRgb(shifted.join(' '));\n};\n\n/**\n * Put the design system's categorical chart palette on a Flint input.\n *\n * Series colours are resolved by flint itself during assembly (they land in the\n * Vega-Lite `color` scale's `range`), so unlike the chrome in `vegaTheme` they\n * cannot be set through the compile `config` — they ride `theme_spec.ink.series`\n * on the input instead.\n *\n * `--chart-1..5` is the decorative palette the recharts wrappers in\n * `@keboola/design/charts` already use, so a Flint chart now matches the rest of\n * the platform's data-viz, follows the colour scheme, and re-skins with a brand\n * that overrides `colors.chart` / `colorsDark.chart`.\n *\n * An author-supplied `theme_spec` wins: a fence that names a Flint house (or its\n * own spec) asked for that look deliberately, so it is passed through untouched.\n *\n * `null` is not such a spec. The input is `JSON.parse`d model output, where\n * `\"theme_spec\": null` is valid JSON, and flint dereferences the value while\n * assembling (`reading 'extends'`) — so leaving it in place throws and the chart\n * falls back to raw source. It is replaced, exactly like a missing one.\n */\nexport const withFlintSeriesPalette = (\n input: ChartAssemblyInput,\n scheme: ResolvedColorScheme,\n from?: Element | null,\n): ChartAssemblyInput => {\n if (input.theme_spec !== undefined && input.theme_spec !== null) return input;\n\n const token = createVegaColorResolver(scheme, from);\n const categorical = SERIES_TOKENS.map((name) => token(name));\n\n return {\n ...input,\n theme_spec: {\n ink: {\n // `endpointsAgainstSurface` below needs to know what the chart sits on.\n surface: { canvas: token('card') },\n // Flint's own defaults for these assume a light canvas — a title in near-black, axis\n // labels and gridlines just under it. On a dark surface that is a chart with no\n // readable numbers, so both groups come from the tokens like everything else.\n text: {\n primary: token('foreground'),\n secondary: token('muted-foreground'),\n muted: token('muted-foreground'),\n // Ink *on* a filled mark, which is the series colour — the canvas reads against it.\n inverse: token('card'),\n },\n structure: {\n axis: token('border'),\n grid: token('border'),\n frame: token('border'),\n rule: token('border'),\n },\n series: {\n categorical,\n // Past five series flint drops `categorical` wholesale for its own\n // `tableau10` — brand-blind and scheme-blind — so a six-region or\n // seven-month split would silently stop following the theme. The\n // extended set keeps those on our hues: a second tier shifted away\n // from the surface (darker on light, lighter on dark, per\n // `TIER_TWO_SHIFT`), distinct from the core five but the same family.\n categoricalExtended: [\n ...categorical,\n ...categorical.map((colour) =>\n shiftChannels(colour, TIER_TWO_SHIFT[scheme].towards, TIER_TWO_SHIFT[scheme].amount),\n ),\n ],\n // The tail past even the extended set.\n overflow: token('muted-foreground'),\n // A quantitative `color` field takes this ramp instead of the\n // categorical set. Without it flint synthesises `[black, series[0]]`,\n // which puts pure black at the low end in *both* schemes — invisible\n // cells on a dark surface.\n sequential: {\n stops: [token('accent'), token('chart-1')],\n space: 'lab',\n endpointsAgainstSurface: true,\n },\n },\n },\n },\n };\n};\n","/**\n * The boundary in front of the flint renderer: a spec is data, and nothing in it may fetch or run.\n *\n * - Checked on the **input**, before `assembleVegaLite` compiles it. The compiled Vega spec\n * carries `signals` and `expr` of vega's own making, so after compilation the model's keys and\n * the compiler's are no longer tellable apart.\n * - Refused on the **presence of a key**, never on a value looking remote: a relative `data.url`\n * is still a fetch nobody authorised, made by the reader's browser to a host the model chose.\n */\n\n/** Keys whose presence anywhere in a spec is refused: each fetches, evaluates or binds a signal. */\nconst REFUSED_KEYS = new Set(['url', 'expr', 'signal', 'signals', 'params']);\n\n/**\n * Suffix of Vega-Lite's other expression properties — `labelExpr`, `tickCountExpr`, …\n *\n * Matched by shape rather than enumerated, so a property added upstream is refused by default.\n */\nconst EXPRESSION_KEY_SUFFIX = /Expr$/;\n\n/**\n * An own property's value, or `undefined`.\n *\n * - A descriptor, so a prototype's key is not read: an inherited key is not one the model wrote.\n * - And so a getter is not *called* — the payload is model-written, and reading it must not run it.\n */\nconst ownValue = (node: unknown, key: string): unknown => {\n if (typeof node !== 'object' || node === null) return undefined;\n\n return Object.getOwnPropertyDescriptor(node, key)?.value;\n};\n\n/**\n * The chart's own rows, which the walk does not descend into — `spec.data.values`, and nowhere else.\n *\n * - Rows are data, never spec: vega reads a row's fields as datum values and evaluates none of\n * them, so a column named `url` is a column. Without this the guard refuses a chart of URLs.\n * - Flint takes exactly one dataset, at the top level (`ChartAssemblyInput['data']`), and an\n * array is what a dataset is (`data: { values: any[] }`). So this is the whole of a payload\n * that is data, and every other node is spec.\n * - The walk then skips it by **identity**. Skipping on the key name would leave `values` the\n * one word in the payload that hides a refused key, at any depth and under any parent —\n * including a nested object merely named `data`.\n */\nconst inlineRows = (spec: unknown): unknown[] | null => {\n const values = ownValue(ownValue(spec, 'data'), 'values');\n\n return Array.isArray(values) ? values : null;\n};\n\n/**\n * The top-level maps flint keys by **column name**, whose keys are therefore the model's data.\n *\n * - `semantic_types` and `field_display_names` are `Record<columnName, …>`\n * (`ChartAssemblyInput`), so a column named `url` puts `url` in a key position. Checking those\n * keys refuses \"Top landing pages by sessions\" — a chart with nothing remote in it.\n * - Only the keys are the column's. Each value is spec, and is walked like any other node.\n * - Matched by identity, for the reason `inlineRows` is: a nested object merely *named*\n * `semantic_types` is one flint never reads, so it buys no exemption.\n */\nconst COLUMN_KEYED_MAPS = ['semantic_types', 'field_display_names'];\n\nconst columnKeyedMaps = (spec: unknown): unknown[] =>\n COLUMN_KEYED_MAPS.map((key) => ownValue(spec, key)).filter(\n (map) => typeof map === 'object' && map !== null,\n );\n\n/**\n * Ceiling on the nodes the walk will visit.\n *\n * The payload is model-written, so its shape is not ours to assume. Exceeding this refuses rather\n * than passes: a spec the guard could not finish checking has not been checked.\n */\nconst MAX_SPEC_NODES = 10_000;\n\nconst TOO_LARGE = 'is too large to check';\n\n/**\n * Why this spec may not be rendered, or `null` if it may.\n *\n * - The reason reaches a console line, not a reader — the refusal itself surfaces as the source\n * fence `DiagramSource` already draws for a chart that would not render.\n * - Iterative, not recursive: a deeply nested payload must not land as a stack overflow.\n * - Nodes are charged on entering the stack, so the ceiling bounds the work, not the result.\n * - An accessor property is refused, not skipped: JSON carries none, so it is not a spec the\n * model wrote, and its value cannot be read without running it — leaving that subtree unchecked.\n */\nexport const findUnsafeSpecKey = (spec: unknown): string | null => {\n const rows = inlineRows(spec);\n const columnKeyed = columnKeyedMaps(spec);\n const stack: unknown[] = [spec];\n let visited = 1;\n\n while (stack.length > 0) {\n const node = stack.pop();\n\n if (Array.isArray(node)) {\n visited += node.length;\n if (visited > MAX_SPEC_NODES) return TOO_LARGE;\n stack.push(...node);\n continue;\n }\n\n if (typeof node !== 'object' || node === null) continue;\n\n const keysAreColumnNames = columnKeyed.includes(node);\n\n // Descriptors, not `Object.entries` or `for…in`: an inherited key is not one the model wrote,\n // and reading an own one must not *call* anything.\n for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(node))) {\n if (!keysAreColumnNames && (REFUSED_KEYS.has(key) || EXPRESSION_KEY_SUFFIX.test(key))) {\n return `carries \\`${key}\\``;\n }\n if (!('value' in descriptor)) return `carries an accessor at \\`${key}\\``;\n\n const { value } = descriptor;\n if (rows !== null && value === rows) continue;\n\n visited += 1;\n if (visited > MAX_SPEC_NODES) return TOO_LARGE;\n stack.push(value);\n }\n }\n\n return null;\n};\n","import type { ChartAssemblyInput } from 'flint-chart';\nimport type { assembleVegaLite } from 'flint-chart/vegalite';\nimport type * as vega from 'vega';\nimport type * as vegaLite from 'vega-lite';\nimport type { Config, TopLevelSpec } from 'vega-lite';\n\nimport { findUnsafeSpecKey } from './refuseUnsafeSpec';\n\n// The subset of the lazily imported modules the render pipeline needs. Kept as\n// a parameter (not module state) so the pipeline is unit-testable with a fake\n// runtime, without mocking dynamic imports.\nexport type FlintRuntime = {\n assembleVegaLite: typeof assembleVegaLite;\n vega: Pick<typeof vega, 'View' | 'parse'>;\n vegaLite: Pick<typeof vegaLite, 'compile'>;\n};\n\n/**\n * Compile a Flint `ChartAssemblyInput` to a static SVG string.\n *\n * - The input is refused before anything compiles it if it carries a key that would fetch or\n * evaluate — see `refuseUnsafeSpec.ts`. Every path to the renderer goes through here, so this\n * is the one place that check has to be.\n * - `assembleVegaLite` returns a Vega-Lite spec carrying `_`-prefixed internal\n * hint keys (`_width`, `_options`, `_transform`, …) that are not part of the\n * Vega-Lite schema — they are stripped before compiling.\n * - `config` (the scheme-following theme) rides the compile options; flint's\n * spec-side config sets no colours, so the two cannot conflict on merge.\n * - The Vega view renders headless (`renderer: 'none'`); nothing mounts to the\n * DOM, `toSVG()` is the only output.\n */\nexport const renderFlintSvg = async (\n runtime: FlintRuntime,\n input: ChartAssemblyInput,\n config: Config,\n): Promise<string> => {\n const unsafe = findUnsafeSpecKey(input);\n if (unsafe !== null) {\n throw new Error(`Refusing to render a flint spec that ${unsafe}`);\n }\n\n const assembled: Record<string, unknown> = runtime.assembleVegaLite(input);\n // `assembleVegaLite` is typed loosely upstream; once the hint keys are gone\n // the remainder is the Vega-Lite spec `compile` expects, which TypeScript\n // cannot see through the untyped boundary — hence the assertion.\n const vlSpec = Object.fromEntries(\n Object.entries(assembled).filter(([key]) => !key.startsWith('_')),\n ) as unknown as TopLevelSpec;\n const { spec } = runtime.vegaLite.compile(vlSpec, { config });\n const view = new runtime.vega.View(runtime.vega.parse(spec), { renderer: 'none' });\n try {\n await view.runAsync();\n return await view.toSVG();\n } finally {\n view.finalize();\n }\n};\n","import { useEffect, useState } from 'react';\nimport type { ReactNode } from 'react';\nimport type { ChartAssemblyInput } from 'flint-chart';\n\nimport { useThemeAttributes } from '../../components/ThemeProvider/useThemeAttributes';\nimport { ZoomableSvg } from '../../components/ZoomableSvg';\nimport { vegaTheme } from '../../vega/vegaTheme';\nimport { DiagramSkeleton } from '../DiagramShared/DiagramSkeleton';\nimport { DiagramSource } from '../DiagramShared/DiagramSource';\nimport { makeResponsive } from '../DiagramShared/svgTransforms';\n\nimport { withFlintSeriesPalette } from './flintTheme';\nimport type { FlintRuntime } from './renderFlintSvg';\nimport { renderFlintSvg } from './renderFlintSvg';\n\nlet runtimePromise: Promise<FlintRuntime> | null = null;\n\n// One memoized promise for the heavy chart stack (flint-chart + vega +\n// vega-lite), reset on failure so a transient chunk-load error can retry on the\n// next render — mirrors `getMermaid()` in `MermaidDiagram`.\nconst getFlintRuntime = (): Promise<FlintRuntime> => {\n if (!runtimePromise) {\n runtimePromise = Promise.all([\n import('flint-chart/vegalite'),\n import('vega'),\n import('vega-lite'),\n ])\n .then(([flint, vega, vegaLite]) => ({\n assembleVegaLite: flint.assembleVegaLite,\n vega,\n vegaLite,\n }))\n .catch((err) => {\n runtimePromise = null;\n throw err;\n });\n }\n return runtimePromise;\n};\n\nconst RENDER_DEBOUNCE_MS = 300;\n\n// No prefix of a JSON document parses, so while a ```flint fence streams in the\n// source is unparseable in exactly the same way a broken one is. Unparseable\n// source is treated as still-streaming (skeleton stays up) until it has been\n// unchanged this long — only then is it shown as raw source.\nconst PARSE_GRACE_MS = 1500;\n\n// Ceiling on the fence source, in characters. `JSON.parse` plus vega's compile\n// and layout are synchronous, so an oversized payload — a model dumping a raw\n// table into the block — would block the main thread and freeze the chat. The\n// prompt caps inline data at ~50 rows; this sits far above a legitimate spec of\n// that size while still refusing anything pathological.\nconst MAX_SOURCE_CHARS = 200_000;\n\n// Module-level SVG cache — persists across component remounts (e.g. tab switches\n// that cause React to remount the tree). The chart chrome colours are baked into\n// the markup, so the key is the code *plus* the theme key (scheme + brand): the\n// same spec is a different SVG under a different colour scheme or brand.\n// Values are stored already `makeResponsive`d, so the string identity is stable\n// and ZoomableSvg's sanitize memo never re-runs for an unchanged chart.\n//\n// Bounded, and evicted oldest-first: a vega SVG is mark-per-datum, so entries run\n// to hundreds of KB, and a long chat visits many distinct charts. `Map` iterates\n// in insertion order, so the first key is the oldest.\nconst MAX_CACHED_CHARTS = 24;\nconst svgCache = new Map<string, string>();\n\nconst cacheSvg = (key: string, svg: string): void => {\n // Overwriting an existing key adds no entry, so it must not evict one — two\n // instances of the same chart both missing the cache and both writing would\n // otherwise cost an unrelated chart its slot.\n if (!svgCache.has(key) && svgCache.size >= MAX_CACHED_CHARTS) {\n const oldest = svgCache.keys().next();\n if (!oldest.done) svgCache.delete(oldest.value);\n }\n svgCache.set(key, svg);\n};\n\nconst chartCacheKey = (code: string, themeKey: string): string => `${themeKey}\\n${code}`;\n\n// The theme half of a cache key — used to tell \"same theme, more source\" (a spec\n// still streaming) from a theme/brand switch, which must not keep a stale chart.\nconst prevThemeKeyOf = (key: string): string => key.slice(0, key.indexOf('\\n'));\n\ntype Props = {\n /** Raw source of a ```flint fence — a flint-chart `ChartAssemblyInput` as pure JSON\n * (`data.values` rows, optional `semantic_types`, and a `chart_spec` naming the\n * chartType and encodings). Rendered through the Vega-Lite backend. */\n code: string;\n /** Extra toolbar controls (e.g. an `ExportMenu`), forwarded to `ZoomableSvg`.\n * Receives the sanitized SVG. Omit to keep the default toolbar. */\n renderActions?: (safeSvg: string) => ReactNode;\n};\n\nexport const FlintChart = ({ code, renderActions }: Props) => {\n // Re-renders whenever the active colour scheme or brand changes, so the chart\n // chrome is re-rendered in the new palette instead of staying a light island\n // on a dark page — same contract as MermaidDiagram.\n // `scope` is the element the live tokens are read off — the enclosing `ThemeScope`'s own,\n // or `null` for `<html>`. It is part of `themeKey`, so a palette built before the scope\n // answered is never served afterwards.\n const { scheme, cacheKey: themeKey, scope } = useThemeAttributes();\n const cacheKey = chartCacheKey(code, themeKey);\n\n const [svg, setSvg] = useState<string | null>(() => svgCache.get(cacheKey) ?? null);\n const [renderFailed, setRenderFailed] = useState(false);\n\n // Synchronously reset stale state when the code or the theme changes, to avoid\n // a one-frame flash.\n //\n // A cache hit is applied immediately. On a miss the previous SVG is dropped\n // unless *only* the tail of the source grew — i.e. the same spec still\n // streaming in, where holding the last good chart avoids a skeleton flicker\n // between chunks. Anything else (a different spec, or a new theme/brand) must\n // clear: the colours are baked into the markup, so keeping it would show a\n // light chart on a dark page, and keeping a chart for a spec that is no longer\n // on screen is simply wrong.\n const [prevKey, setPrevKey] = useState(cacheKey);\n const [prevCode, setPrevCode] = useState(code);\n if (cacheKey !== prevKey) {\n const cached = svgCache.get(cacheKey);\n const sameThemeStillStreaming =\n themeKey === prevThemeKeyOf(prevKey) && code.startsWith(prevCode);\n setPrevKey(cacheKey);\n setPrevCode(code);\n if (cached) setSvg(cached);\n else if (!sameThemeStillStreaming) setSvg(null);\n setRenderFailed(false);\n }\n\n useEffect(() => {\n if (svgCache.has(cacheKey)) return;\n\n let cancelled = false;\n let graceTimer: ReturnType<typeof setTimeout> | undefined;\n\n const debounceTimer = setTimeout(() => {\n if (code.length > MAX_SOURCE_CHARS) {\n // Not a streaming artifact — a fence this size never becomes renderable,\n // so fail straight to the source instead of waiting out the grace period.\n console.error(\n `[FlintChart] source exceeds ${MAX_SOURCE_CHARS} characters (${code.length}); not rendering`,\n );\n setSvg(null);\n setRenderFailed(true);\n return;\n }\n\n let input: ChartAssemblyInput;\n try {\n input = JSON.parse(code);\n } catch (err) {\n // Likely a fence still streaming — declare failure (and log the parse\n // error) only if the code is still unchanged when the grace period ends.\n graceTimer = setTimeout(() => {\n console.debug('[FlintChart] source never became parseable JSON:', err);\n setSvg(null);\n setRenderFailed(true);\n }, PARSE_GRACE_MS - RENDER_DEBOUNCE_MS);\n return;\n }\n\n getFlintRuntime()\n .then(async (runtime) => {\n if (cancelled) return;\n // Theme resolved at render time, off the live CSS variables — the\n // scheme comes out of the same read as the cache key, so the palette\n // can never be resolved under one scheme and cached under the other.\n const svgOutput = makeResponsive(\n await renderFlintSvg(\n runtime,\n withFlintSeriesPalette(input, scheme, scope),\n vegaTheme(scheme, scope),\n ),\n );\n if (cancelled) return;\n cacheSvg(cacheKey, svgOutput);\n setSvg(svgOutput);\n setRenderFailed(false);\n })\n .catch((err) => {\n if (cancelled) return;\n console.error('[FlintChart] render failed:', err);\n setSvg(null);\n setRenderFailed(true);\n });\n }, RENDER_DEBOUNCE_MS);\n\n return () => {\n cancelled = true;\n clearTimeout(debounceTimer);\n if (graceTimer) clearTimeout(graceTimer);\n };\n }, [cacheKey, code, scheme, scope]);\n\n if (renderFailed) {\n return <DiagramSource code={code} />;\n }\n\n if (svg) {\n return (\n <ZoomableSvg\n // Keyed by the rendered SVG, not the source: while the same fence is\n // still streaming the chart is deliberately held (see above), and keying\n // on `code` would remount it on every chunk — dropping the reader's\n // zoom/pan and re-running the DOMPurify pass over an identical chart.\n key={svg}\n html={svg}\n copyText={`\\`\\`\\`flint\\n${code}\\n\\`\\`\\``}\n className=\"tw:my-3\"\n renderActions={renderActions}\n />\n );\n }\n\n return <DiagramSkeleton />;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"flint.js","names":[],"sources":["../src/blocks/FlintChart/flintTheme.ts","../src/blocks/FlintChart/refuseUnsafeSpec.ts","../src/blocks/FlintChart/renderFlintSvg.ts","../src/blocks/FlintChart/FlintChart.tsx"],"sourcesContent":["import type { ChartAssemblyInput } from 'flint-chart';\n\nimport type { ResolvedColorScheme } from '../../components/ThemeProvider/types';\nimport { createVegaColorResolver, SERIES_TOKENS, vegaRgb } from '../../vega/vegaTokens';\n\n/**\n * How the second palette tier (series 6-10) is derived from the core hues.\n *\n * - Each hue moves *away* from its surface: darker on light, lighter on dark.\n * Darkening on dark would drop contrast from ~6.7:1 to ~3.7:1.\n * - `amount` is the fraction of the distance travelled toward `towards`.\n */\nconst TIER_TWO_SHIFT = {\n light: { towards: 0, amount: 0.38 },\n dark: { towards: 255, amount: 0.35 },\n} as const;\n\n/**\n * Move an `rgb()` string's channels a fraction of the way toward `towards`\n * (0 = black, 255 = white). Returns the input unchanged if it is not that shape.\n */\nconst shiftChannels = (colour: string, towards: number, amount: number): string => {\n const channels = colour.match(/\\d+/g);\n if (!channels || channels.length < 3) return colour;\n const shifted = channels\n .slice(0, 3)\n .map((c) => Math.round(Number(c) + (towards - Number(c)) * amount));\n return vegaRgb(shifted.join(' '));\n};\n\n/**\n * Put the design system's `--chart-1..5` palette on a Flint input.\n *\n * - Flint resolves series colours during assembly, so they ride `theme_spec`, not `vegaTheme`.\n * - An author-supplied `theme_spec` is passed through untouched.\n * - A `null` `theme_spec` is replaced like a missing one — flint throws on it.\n */\nexport const withFlintSeriesPalette = (\n input: ChartAssemblyInput,\n scheme: ResolvedColorScheme,\n from?: Element | null,\n): ChartAssemblyInput => {\n if (input.theme_spec !== undefined && input.theme_spec !== null) return input;\n\n const token = createVegaColorResolver(scheme, from);\n const categorical = SERIES_TOKENS.map((name) => token(name));\n\n return {\n ...input,\n theme_spec: {\n ink: {\n // `endpointsAgainstSurface` below needs to know what the chart sits on.\n surface: { canvas: token('card') },\n // Flint's defaults assume a light canvas, so text and structure come from tokens.\n text: {\n primary: token('foreground'),\n secondary: token('muted-foreground'),\n muted: token('muted-foreground'),\n // Ink on a filled (series-coloured) mark.\n inverse: token('card'),\n },\n structure: {\n axis: token('border'),\n grid: token('border'),\n frame: token('border'),\n rule: token('border'),\n },\n series: {\n categorical,\n // Past five series flint swaps `categorical` for its theme-blind `tableau10`;\n // this keeps 6-10 on our hues (`TIER_TWO_SHIFT`).\n categoricalExtended: [\n ...categorical,\n ...categorical.map((colour) =>\n shiftChannels(colour, TIER_TWO_SHIFT[scheme].towards, TIER_TWO_SHIFT[scheme].amount),\n ),\n ],\n // The tail past even the extended set.\n overflow: token('muted-foreground'),\n // For a quantitative `color` field; flint's default starts at black, invisible on dark.\n sequential: {\n stops: [token('accent'), token('chart-1')],\n space: 'lab',\n endpointsAgainstSurface: true,\n },\n },\n },\n },\n };\n};\n","/**\n * The boundary in front of the flint renderer: nothing in a spec may fetch or run.\n *\n * - Checked on the input, before `assembleVegaLite`: compiled Vega carries its own `signals`/`expr`.\n * - Refused on a key's presence, not its value: a relative `data.url` is still a fetch.\n */\n\n/** Keys whose presence anywhere in a spec is refused: each fetches, evaluates or binds a signal. */\nconst REFUSED_KEYS = new Set(['url', 'expr', 'signal', 'signals', 'params']);\n\n/** Suffix of Vega-Lite's other expression properties; matched by shape so new ones are refused. */\nconst EXPRESSION_KEY_SUFFIX = /Expr$/;\n\n/**\n * An own property's value, or `undefined`.\n *\n * - Via a descriptor: inherited keys are ignored and a getter is never called.\n */\nconst ownValue = (node: unknown, key: string): unknown => {\n if (typeof node !== 'object' || node === null) return undefined;\n\n return Object.getOwnPropertyDescriptor(node, key)?.value;\n};\n\n/**\n * The chart's rows (`spec.data.values` only), which the walk does not descend into.\n *\n * - Rows are data vega never evaluates, so a column named `url` must not be refused.\n * - Flint takes one top-level dataset, so every other node is spec.\n * - Skipped by identity, not key name: a `values` key elsewhere must not hide a refused key.\n */\nconst inlineRows = (spec: unknown): unknown[] | null => {\n const values = ownValue(ownValue(spec, 'data'), 'values');\n\n return Array.isArray(values) ? values : null;\n};\n\n/**\n * The top-level maps flint keys by column name, so their keys are data, not spec.\n *\n * - Only the keys are exempt; each value is walked like any other node.\n * - Matched by identity, as in `inlineRows`: a nested object with the same name gets no exemption.\n */\nconst COLUMN_KEYED_MAPS = ['semantic_types', 'field_display_names'];\n\nconst columnKeyedMaps = (spec: unknown): unknown[] =>\n COLUMN_KEYED_MAPS.map((key) => ownValue(spec, key)).filter(\n (map) => typeof map === 'object' && map !== null,\n );\n\n/** Ceiling on the nodes the walk visits; exceeding it refuses, since the spec went unchecked. */\nconst MAX_SPEC_NODES = 10_000;\n\nconst TOO_LARGE = 'is too large to check';\n\n/**\n * Why this spec may not be rendered, or `null` if it may.\n *\n * - The reason goes to the console; the reader sees `DiagramSource`'s fallback.\n * - Iterative, so deep nesting cannot overflow the stack; nodes are charged on push.\n * - An accessor is refused, not skipped: its value cannot be read without running it.\n */\nexport const findUnsafeSpecKey = (spec: unknown): string | null => {\n const rows = inlineRows(spec);\n const columnKeyed = columnKeyedMaps(spec);\n const stack: unknown[] = [spec];\n let visited = 1;\n\n while (stack.length > 0) {\n const node = stack.pop();\n\n if (Array.isArray(node)) {\n visited += node.length;\n if (visited > MAX_SPEC_NODES) return TOO_LARGE;\n stack.push(...node);\n continue;\n }\n\n if (typeof node !== 'object' || node === null) continue;\n\n const keysAreColumnNames = columnKeyed.includes(node);\n\n // Descriptors: skip inherited keys and never call a getter.\n for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(node))) {\n if (!keysAreColumnNames && (REFUSED_KEYS.has(key) || EXPRESSION_KEY_SUFFIX.test(key))) {\n return `carries \\`${key}\\``;\n }\n if (!('value' in descriptor)) return `carries an accessor at \\`${key}\\``;\n\n const { value } = descriptor;\n if (rows !== null && value === rows) continue;\n\n visited += 1;\n if (visited > MAX_SPEC_NODES) return TOO_LARGE;\n stack.push(value);\n }\n }\n\n return null;\n};\n","import type { ChartAssemblyInput } from 'flint-chart';\nimport type { assembleVegaLite } from 'flint-chart/vegalite';\nimport type * as vega from 'vega';\nimport type * as vegaLite from 'vega-lite';\nimport type { Config, TopLevelSpec } from 'vega-lite';\n\nimport { findUnsafeSpecKey } from './refuseUnsafeSpec';\n\n// The lazily imported modules the pipeline needs; a parameter so tests can pass a fake runtime.\nexport type FlintRuntime = {\n assembleVegaLite: typeof assembleVegaLite;\n vega: Pick<typeof vega, 'View' | 'parse'>;\n vegaLite: Pick<typeof vegaLite, 'compile'>;\n};\n\n/**\n * Compile a Flint `ChartAssemblyInput` to a static SVG string.\n *\n * - The one place unsafe input is refused (`refuseUnsafeSpec.ts`), before anything compiles it.\n * - Flint's `_`-prefixed hint keys are not Vega-Lite schema and are stripped before compiling.\n * - `config` rides the compile options; flint's spec-side config sets no colours to conflict.\n * - Renders headless; `toSVG()` is the only output.\n */\nexport const renderFlintSvg = async (\n runtime: FlintRuntime,\n input: ChartAssemblyInput,\n config: Config,\n): Promise<string> => {\n const unsafe = findUnsafeSpecKey(input);\n if (unsafe !== null) {\n throw new Error(`Refusing to render a flint spec that ${unsafe}`);\n }\n\n const assembled: Record<string, unknown> = runtime.assembleVegaLite(input);\n // `assembleVegaLite` is typed loosely upstream; without the hint keys it is a Vega-Lite spec.\n const vlSpec = Object.fromEntries(\n Object.entries(assembled).filter(([key]) => !key.startsWith('_')),\n ) as unknown as TopLevelSpec;\n const { spec } = runtime.vegaLite.compile(vlSpec, { config });\n const view = new runtime.vega.View(runtime.vega.parse(spec), { renderer: 'none' });\n try {\n await view.runAsync();\n return await view.toSVG();\n } finally {\n view.finalize();\n }\n};\n","import { useEffect, useState } from 'react';\nimport type { ReactNode } from 'react';\nimport type { ChartAssemblyInput } from 'flint-chart';\n\nimport { useThemeAttributes } from '../../components/ThemeProvider/useThemeAttributes';\nimport { ZoomableSvg } from '../../components/ZoomableSvg';\nimport { vegaTheme } from '../../vega/vegaTheme';\nimport { DiagramSkeleton } from '../DiagramShared/DiagramSkeleton';\nimport { DiagramSource } from '../DiagramShared/DiagramSource';\nimport { makeResponsive } from '../DiagramShared/svgTransforms';\n\nimport { withFlintSeriesPalette } from './flintTheme';\nimport type { FlintRuntime } from './renderFlintSvg';\nimport { renderFlintSvg } from './renderFlintSvg';\n\nlet runtimePromise: Promise<FlintRuntime> | null = null;\n\n// Memoized chart-stack import, reset on failure so a chunk-load error can retry.\n// Mirrors `getMermaid()` in `MermaidDiagram`.\nconst getFlintRuntime = (): Promise<FlintRuntime> => {\n if (!runtimePromise) {\n runtimePromise = Promise.all([\n import('flint-chart/vegalite'),\n import('vega'),\n import('vega-lite'),\n ])\n .then(([flint, vega, vegaLite]) => ({\n assembleVegaLite: flint.assembleVegaLite,\n vega,\n vegaLite,\n }))\n .catch((err) => {\n runtimePromise = null;\n throw err;\n });\n }\n return runtimePromise;\n};\n\nconst RENDER_DEBOUNCE_MS = 300;\n\n// A streaming fence is unparseable just like a broken one, so unparseable source\n// keeps the skeleton until it has been unchanged this long, then shows as raw source.\nconst PARSE_GRACE_MS = 1500;\n\n// Parse, compile and layout are synchronous, so a larger source would freeze the chat.\n// Far above the prompt's ~50-row inline data cap.\nconst MAX_SOURCE_CHARS = 200_000;\n\n// SVG cache that survives remounts.\n// - Keyed by theme key + code: colours are baked into the markup.\n// - Values are already `makeResponsive`d, so ZoomableSvg's sanitize memo stays stable.\n// - Bounded and evicted oldest-first (`Map` insertion order): entries run to hundreds of KB.\nconst MAX_CACHED_CHARTS = 24;\nconst svgCache = new Map<string, string>();\n\nconst cacheSvg = (key: string, svg: string): void => {\n // Overwriting an existing key adds no entry, so it must not evict one.\n if (!svgCache.has(key) && svgCache.size >= MAX_CACHED_CHARTS) {\n const oldest = svgCache.keys().next();\n if (!oldest.done) svgCache.delete(oldest.value);\n }\n svgCache.set(key, svg);\n};\n\nconst chartCacheKey = (code: string, themeKey: string): string => `${themeKey}\\n${code}`;\n\n// The theme half of a cache key — tells a still-streaming spec from a theme switch.\nconst prevThemeKeyOf = (key: string): string => key.slice(0, key.indexOf('\\n'));\n\ntype Props = {\n /** Raw source of a ```flint fence: a flint-chart `ChartAssemblyInput` as JSON. */\n code: string;\n /** Extra toolbar controls (e.g. `ExportMenu`) for `ZoomableSvg`; receives the sanitized SVG. */\n renderActions?: (safeSvg: string) => ReactNode;\n};\n\nexport const FlintChart = ({ code, renderActions }: Props) => {\n // - Re-renders on a scheme or brand change, same contract as MermaidDiagram.\n // - `scope` is the element tokens are read off (`null` = `<html>`); it is part of `themeKey`.\n const { scheme, cacheKey: themeKey, scope } = useThemeAttributes();\n const cacheKey = chartCacheKey(code, themeKey);\n\n const [svg, setSvg] = useState<string | null>(() => svgCache.get(cacheKey) ?? null);\n const [renderFailed, setRenderFailed] = useState(false);\n\n // Reset stale state synchronously on a code or theme change (no one-frame flash):\n // 1. A cache hit is applied at once.\n // 2. Same theme, source only grew (still streaming): keep the last chart, no flicker.\n // 3. Anything else clears — colours are baked in, so a stale chart is wrong.\n const [prevKey, setPrevKey] = useState(cacheKey);\n const [prevCode, setPrevCode] = useState(code);\n if (cacheKey !== prevKey) {\n const cached = svgCache.get(cacheKey);\n const sameThemeStillStreaming =\n themeKey === prevThemeKeyOf(prevKey) && code.startsWith(prevCode);\n setPrevKey(cacheKey);\n setPrevCode(code);\n if (cached) setSvg(cached);\n else if (!sameThemeStillStreaming) setSvg(null);\n setRenderFailed(false);\n }\n\n useEffect(() => {\n if (svgCache.has(cacheKey)) return;\n\n let cancelled = false;\n let graceTimer: ReturnType<typeof setTimeout> | undefined;\n\n const debounceTimer = setTimeout(() => {\n if (code.length > MAX_SOURCE_CHARS) {\n // Never becomes renderable, so skip the grace period.\n console.error(\n `[FlintChart] source exceeds ${MAX_SOURCE_CHARS} characters (${code.length}); not rendering`,\n );\n setSvg(null);\n setRenderFailed(true);\n return;\n }\n\n let input: ChartAssemblyInput;\n try {\n input = JSON.parse(code);\n } catch (err) {\n // Likely still streaming — fail only if unchanged when the grace period ends.\n graceTimer = setTimeout(() => {\n console.debug('[FlintChart] source never became parseable JSON:', err);\n setSvg(null);\n setRenderFailed(true);\n }, PARSE_GRACE_MS - RENDER_DEBOUNCE_MS);\n return;\n }\n\n getFlintRuntime()\n .then(async (runtime) => {\n if (cancelled) return;\n // `scheme` comes from the same read as the cache key, so palette and key agree.\n const svgOutput = makeResponsive(\n await renderFlintSvg(\n runtime,\n withFlintSeriesPalette(input, scheme, scope),\n vegaTheme(scheme, scope),\n ),\n );\n if (cancelled) return;\n cacheSvg(cacheKey, svgOutput);\n setSvg(svgOutput);\n setRenderFailed(false);\n })\n .catch((err) => {\n if (cancelled) return;\n console.error('[FlintChart] render failed:', err);\n setSvg(null);\n setRenderFailed(true);\n });\n }, RENDER_DEBOUNCE_MS);\n\n return () => {\n cancelled = true;\n clearTimeout(debounceTimer);\n if (graceTimer) clearTimeout(graceTimer);\n };\n }, [cacheKey, code, scheme, scope]);\n\n if (renderFailed) {\n return <DiagramSource code={code} />;\n }\n\n if (svg) {\n return (\n <ZoomableSvg\n // Keyed by SVG, not `code`: a streaming chart is held, and a remount per chunk\n // would drop zoom/pan.\n key={svg}\n html={svg}\n copyText={`\\`\\`\\`flint\\n${code}\\n\\`\\`\\``}\n className=\"tw:my-3\"\n renderActions={renderActions}\n />\n );\n }\n\n return <DiagramSkeleton />;\n};\n"],"mappings":"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"}