@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
@@ -1 +1 @@
1
- {"version":3,"file":"editors.js","names":["keymap","EditorView","EMPTY_LINES","EMPTY_CLASSIFICATION"],"sources":["../src/components/CodeEditor/activeQueryHighlight.ts","../src/components/CodeEditor/highlighting.ts","../src/components/CodeEditor/SqlEditor.tsx","../src/components/MergeEditor/AcceptControls/AcceptButton.tsx","../src/components/MergeEditor/AcceptControls/AcceptControls.tsx","../src/components/MergeEditor/decorations.ts","../src/components/MergeEditor/MergePane.tsx","../src/components/MergeEditor/useScrollSync.ts","../src/components/MergeEditor/MergeEditor.tsx","../src/components/CodeEditor/CodeEditorField.tsx"],"sourcesContent":["import type { ChangeSet, Range } from '@codemirror/state';\nimport { Decoration, EditorView, ViewPlugin, type ViewUpdate } from '@codemirror/view';\n\nimport colors from '@keboola/tailwind-config/colors';\n\nconst activeQueryFirstLine = Decoration.line({ class: 'cm-active-query cm-active-query-first' });\nconst activeQueryMiddleLine = Decoration.line({ class: 'cm-active-query' });\nconst activeQueryLastLine = Decoration.line({ class: 'cm-active-query cm-active-query-last' });\nconst activeQuerySingleLine = Decoration.line({\n class: 'cm-active-query cm-active-query-first cm-active-query-last',\n});\n\ntype QueryBoundary = { from: number; to: number };\n\n/**\n * Given pre-computed query boundaries and a cursor position, find which query\n * the cursor is in. Returns trimmed character offsets or null.\n */\nexport const findActiveQueryRange = (\n doc: string,\n cursorPos: number,\n boundaries: QueryBoundary[],\n): { from: number; to: number } | null => {\n if (boundaries.length === 0) return null;\n\n let match: QueryBoundary | undefined = boundaries[boundaries.length - 1];\n for (const boundary of boundaries) {\n if (cursorPos <= boundary.to) {\n match = boundary;\n break;\n }\n }\n\n if (!match) return null;\n\n // Trim leading/trailing whitespace from the query range for tighter boundaries\n const queryText = doc.slice(match.from, match.to);\n const leadingWhitespace = queryText.length - queryText.trimStart().length;\n const trailingWhitespace = queryText.length - queryText.trimEnd().length;\n\n const trimmedStart = match.from + leadingWhitespace;\n const trimmedEnd = match.to - trailingWhitespace;\n\n if (trimmedStart >= trimmedEnd) return null;\n\n return { from: trimmedStart, to: trimmedEnd };\n};\n\n/**\n * Convert splitSqlQueries result (array of query strings that join back to the\n * original doc) into character-offset boundaries.\n */\nconst computeBoundaries = (queries: string[]): QueryBoundary[] => {\n const boundaries: QueryBoundary[] = [];\n let offset = 0;\n for (const query of queries) {\n boundaries.push({ from: offset, to: offset + query.length });\n offset += query.length;\n }\n return boundaries;\n};\n\n/**\n * Synchronously map cached boundaries through a changeset.\n *\n * Boundaries can outlive the document state they were computed from (the async\n * re-parse in `scheduleDocParse` is debounced, so several edits may land\n * before new boundaries are produced). When that happens a cached position\n * can exceed the changeset's pre-change length and `ChangeSet.mapPos` throws\n * `RangeError: Position X is out of range for changeset of length Y`.\n *\n * Clamp positions into the valid `[0, changes.length]` range before mapping\n * and drop degenerate boundaries so the mapping is safe even with stale cache.\n */\nexport const mapBoundariesThroughChanges = (\n boundaries: QueryBoundary[],\n changes: ChangeSet,\n): QueryBoundary[] => {\n const prevLen = changes.length;\n const mapped: QueryBoundary[] = [];\n for (const b of boundaries) {\n const from = changes.mapPos(Math.max(0, Math.min(b.from, prevLen)), -1);\n const to = changes.mapPos(Math.max(0, Math.min(b.to, prevLen)), 1);\n if (from < to) mapped.push({ from, to });\n }\n return mapped;\n};\n\nconst buildDecorations = (view: EditorView, range: { from: number; to: number }) => {\n const startLine = view.state.doc.lineAt(range.from).number;\n const endLine = view.state.doc.lineAt(range.to).number;\n\n const decorations: Range<Decoration>[] = [];\n for (let lineNum = startLine; lineNum <= endLine; lineNum++) {\n const lineStart = view.state.doc.line(lineNum).from;\n if (startLine === endLine) {\n decorations.push(activeQuerySingleLine.range(lineStart));\n } else if (lineNum === startLine) {\n decorations.push(activeQueryFirstLine.range(lineStart));\n } else if (lineNum === endLine) {\n decorations.push(activeQueryLastLine.range(lineStart));\n } else {\n decorations.push(activeQueryMiddleLine.range(lineStart));\n }\n }\n\n return Decoration.set(decorations);\n};\n\nexport const activeQueryHighlight = (splitQueries: (sql: string) => Promise<string[]>) => {\n return [\n ViewPlugin.fromClass(\n class {\n decorations = Decoration.none;\n private debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private seq = 0;\n private cachedDoc = '';\n private cachedBoundaries: QueryBoundary[] = [];\n private destroyed = false;\n\n constructor(view: EditorView) {\n this.scheduleDocParse(view);\n }\n\n update(update: ViewUpdate) {\n if (update.focusChanged && !update.view.hasFocus) {\n this.decorations = Decoration.none;\n return;\n }\n\n if (update.docChanged) {\n // Synchronously map cached boundaries through document changes\n // so decorations update immediately without waiting for async re-parse.\n // Positions are clamped inside mapBoundariesThroughChanges because\n // the async re-parse is debounced and boundaries can be stale.\n if (this.cachedBoundaries.length > 0) {\n this.cachedBoundaries = mapBoundariesThroughChanges(\n this.cachedBoundaries,\n update.changes,\n );\n this.cachedDoc = update.state.doc.toString();\n this.updateFromCache(update.view);\n }\n // Still re-parse for accurate boundaries (handles structural changes like new semicolons)\n this.scheduleDocParse(update.view);\n } else if (update.selectionSet || (update.focusChanged && update.view.hasFocus)) {\n this.updateFromCache(update.view);\n }\n }\n\n destroy() {\n this.destroyed = true;\n if (this.debounceTimer) clearTimeout(this.debounceTimer);\n }\n\n /**\n * Update decorations using cached query boundaries.\n * Only mutates this.decorations — does NOT call view.dispatch().\n * When called from update() (sync), CodeMirror picks up the new\n * decorations via the accessor. When called from the async\n * scheduleDocParse callback, the caller must dispatch separately.\n */\n private updateFromCache(view: EditorView) {\n if (!view.hasFocus) {\n this.decorations = Decoration.none;\n return;\n }\n\n const { main } = view.state.selection;\n\n if (main.from !== main.to) {\n // User has text selected — hide highlight\n this.decorations = Decoration.none;\n return;\n }\n\n const doc = view.state.doc.toString();\n if (doc !== this.cachedDoc) return;\n if (this.cachedBoundaries.length === 0) {\n this.decorations = Decoration.none;\n return;\n }\n\n const range = findActiveQueryRange(doc, main.head, this.cachedBoundaries);\n this.decorations = range ? buildDecorations(view, range) : Decoration.none;\n }\n\n /** Parse queries async (debounced) and update decorations. */\n private scheduleDocParse(view: EditorView) {\n if (this.debounceTimer) clearTimeout(this.debounceTimer);\n\n this.debounceTimer = setTimeout(async () => {\n const seq = ++this.seq;\n const doc = view.state.doc.toString();\n\n if (!doc.trim()) {\n this.cachedDoc = doc;\n this.cachedBoundaries = [];\n if (this.decorations !== Decoration.none) {\n this.decorations = Decoration.none;\n if (!this.destroyed) view.dispatch();\n }\n return;\n }\n\n let queries: string[];\n try {\n queries = await splitQueries(doc);\n } catch {\n return;\n }\n\n // Discard stale results\n if (this.destroyed || seq !== this.seq) return;\n\n this.cachedDoc = doc;\n this.cachedBoundaries = computeBoundaries(queries);\n\n this.updateFromCache(view);\n if (!this.destroyed) view.dispatch();\n }, 100);\n }\n },\n { decorations: (v) => v.decorations },\n ),\n EditorView.baseTheme({\n '.cm-active-query': {\n boxShadow: `inset -1px 0 0 0 ${colors.blue[300]}, inset 1px 0 0 0 ${colors.blue[300]}`,\n marginRight: '8px',\n },\n '.cm-active-query-first': {\n boxShadow: `inset -1px 0 0 0 ${colors.blue[300]}, inset 1px 0 0 0 ${colors.blue[300]}, inset 0 1px 0 0 ${colors.blue[300]}`,\n borderRadius: '4px 4px 0 0',\n },\n '.cm-active-query-last': {\n boxShadow: `inset -1px 0 0 0 ${colors.blue[300]}, inset 1px 0 0 0 ${colors.blue[300]}, inset 0 -1px 0 0 ${colors.blue[300]}`,\n borderRadius: '0 0 4px 4px',\n },\n '.cm-active-query-first.cm-active-query-last': {\n boxShadow: `inset 0 0 0 1px ${colors.blue[300]}`,\n borderRadius: '4px',\n },\n }),\n ];\n};\n","import type { Extension } from '@codemirror/state';\nimport { RangeSet, StateEffect, StateField } from '@codemirror/state';\nimport { Decoration, EditorView, gutter, GutterMarker } from '@codemirror/view';\n\nimport colors from '@keboola/tailwind-config/colors';\n\nimport { DARK_LINE_ALPHA } from './diffWash';\n\nexport type HighlightedLines = {\n from: number;\n to: number;\n error?: { from: number; to: number };\n processing?: { from: number; to: number };\n} | null;\n\n// Gutter markers for highlighting\nclass HighlightMarker extends GutterMarker {\n override toDOM() {\n const marker = document.createElement('div');\n marker.className = 'cm-highlight-marker';\n return marker;\n }\n}\n\nconst highlightMarker = new HighlightMarker();\n\n// Helper function to iterate over line range and apply a callback\nconst forEachLineInRange = <T>(\n doc: { lines: number; line: (n: number) => { from: number } },\n from: number,\n to: number,\n callback: (lineFrom: number) => T,\n): T[] => {\n const results: T[] = [];\n\n // Convert 0-based to 1-based for CodeMirror\n const fromLine = Math.max(1, from + 1);\n const toLine = Math.min(doc.lines, to + 1);\n\n for (let i = fromLine; i <= toLine; i++) {\n const line = doc.line(i);\n results.push(callback(line.from));\n }\n\n return results;\n};\n\n// State effect for setting highlighted lines\nconst setHighlightedLinesEffect = StateEffect.define<HighlightedLines>();\n\n// State field for storing highlighted lines info\nconst highlightedLinesField = StateField.define<HighlightedLines>({\n create() {\n return null;\n },\n update(value, transaction) {\n for (const effect of transaction.effects) {\n if (effect.is(setHighlightedLinesEffect)) {\n return effect.value;\n }\n }\n return value;\n },\n});\n\n// Gutter extension that displays highlight markers\nconst highlightGutter = gutter({\n class: 'cm-highlight-gutter',\n markers: (view): RangeSet<GutterMarker> => {\n const highlightInfo = view.state.field(highlightedLinesField, false);\n\n if (!highlightInfo) {\n return RangeSet.empty;\n }\n\n // Add blue markers for all running query lines\n const markers = forEachLineInRange(\n view.state.doc,\n highlightInfo.from,\n highlightInfo.to,\n (lineFrom) => highlightMarker.range(lineFrom),\n );\n\n return RangeSet.of(markers);\n },\n});\n\n// State field for error line background decorations\nconst errorLineDecorations = StateField.define({\n create() {\n return Decoration.none;\n },\n update(decorations, transaction) {\n decorations = decorations.map(transaction.changes);\n for (const effect of transaction.effects) {\n if (!effect.is(setHighlightedLinesEffect)) {\n continue;\n }\n\n if (!effect.value || !effect.value.error) {\n return Decoration.none;\n }\n\n // Add error line background decorations\n const decorationArray = forEachLineInRange(\n transaction.state.doc,\n effect.value.error.from,\n effect.value.error.to,\n (lineFrom) => Decoration.line({ class: 'cm-error-line' }).range(lineFrom),\n );\n\n return Decoration.set(decorationArray);\n }\n return decorations;\n },\n provide: (f) => EditorView.decorations.from(f),\n});\n\n// State field for processing line background decorations\nconst processingLineDecorations = StateField.define({\n create() {\n return Decoration.none;\n },\n update(decorations, transaction) {\n decorations = decorations.map(transaction.changes);\n for (const effect of transaction.effects) {\n if (!effect.is(setHighlightedLinesEffect)) {\n continue;\n }\n\n if (!effect.value || !effect.value.processing) {\n return Decoration.none;\n }\n\n const decorationArray = forEachLineInRange(\n transaction.state.doc,\n effect.value.processing.from,\n effect.value.processing.to,\n (lineFrom) => Decoration.line({ class: 'cm-processing-line' }).range(lineFrom),\n );\n\n return Decoration.set(decorationArray);\n }\n return decorations;\n },\n provide: (f) => EditorView.decorations.from(f),\n});\n\n/**\n * The run-state washes for the line a query occupies.\n *\n * - `&dark` keeps this a static base theme rather than a themed extension — see `search.ts` for\n * the mechanism and what sets the facet.\n * - Light paints an opaque pastel; dark tints the same `500` hue over the page, at the line alpha\n * the diff wash uses, so an opaque slab never sits under near-white text.\n * - `.cm-highlight-marker` needs no pair: `blue[500]` is 4.60:1 on the darkest surface it sits on.\n */\nconst highlightedLinesTheme = EditorView.baseTheme({\n '.cm-highlight-gutter': {\n width: '2px',\n marginRight: '2px',\n },\n '.cm-highlight-marker': {\n width: '2px',\n height: '100%',\n backgroundColor: colors.blue[500],\n },\n '.cm-error-line': {\n backgroundColor: `${colors.error[100]} !important`,\n },\n '.cm-processing-line': {\n backgroundColor: `${colors.blue[100]} !important`,\n },\n\n '&dark .cm-error-line': {\n backgroundColor: `${colors.error[500]}${DARK_LINE_ALPHA} !important`,\n },\n '&dark .cm-processing-line': {\n backgroundColor: `${colors.blue[500]}${DARK_LINE_ALPHA} !important`,\n },\n});\n\n// Install highlight lines extension\nexport function highlightingExtension(): Extension[] {\n return [\n highlightedLinesField,\n highlightGutter,\n errorLineDecorations,\n processingLineDecorations,\n highlightedLinesTheme,\n ];\n}\n\nexport function updateHighlighting(view: EditorView, lines?: HighlightedLines) {\n view.dispatch({ effects: setHighlightedLinesEffect.of(lines ?? null) });\n}\n","import { useEffect, useMemo, useRef } from 'react';\nimport type { CompletionSource } from '@codemirror/autocomplete';\nimport { defaultKeymap } from '@codemirror/commands';\nimport type { SQLNamespace } from '@codemirror/lang-sql';\nimport type {\n EditorState,\n KeyBinding,\n ReactCodeMirrorRef,\n ViewUpdate,\n} from '@uiw/react-codemirror';\nimport CodeMirror, { EditorView, keymap } from '@uiw/react-codemirror';\n\nimport { installExtensions, type SqlDialectName } from '@keboola/codemirror-lang-sql';\n\nimport { useStableCallback } from '../../hooks/useStableCallback';\nimport type { ResolvedColorScheme } from '../ThemeProvider/types';\n\nimport { activeQueryHighlight } from './activeQueryHighlight';\nimport { buildCanvasChrome, buildChromeStyles } from './editorChrome';\nimport { buildBaseThemeStyles, buildBasicSetup, buildCustomTheme } from './helpers';\nimport type { HighlightedLines } from './highlighting';\nimport { highlightingExtension, updateHighlighting } from './highlighting';\nimport { searchExtension, updateSearch } from './search';\nimport { useThemedExtension } from './tokenTheme';\nimport { useTooltipsExtension } from './tooltips';\nimport { type EditorVariable, useVariableCompletion } from './variableCompletion';\n\n// SqlEditor installs its own keymaps: `CUSTOM_KEYMAP` ahead of `defaultKeymap`\n// for Escape, a Tab/Enter-filtered completion keymap from\n// `@keboola/codemirror-lang-sql`, and search driven by props rather than the\n// CodeMirror panel.\nconst OWN_KEYMAPS = {\n defaultKeymap: false,\n completionKeymap: false,\n searchKeymap: false,\n} as const;\n\nconst CUSTOM_KEYMAP: KeyBinding[] = [\n {\n key: 'Escape',\n stopPropagation: true,\n run: (view: EditorView) => {\n view.contentDOM.blur();\n return true;\n },\n },\n];\n\nconst OWN_KEYMAP_EXTENSION = keymap.of([...defaultKeymap, ...CUSTOM_KEYMAP]);\n\n// Editor surface, focus ring, and read-only wash resolve from live `--*`\n// tokens so the SQL editor chrome re-skins with brand + dark mode.\nconst buildTheme = (scheme: ResolvedColorScheme) => ({\n ...buildBaseThemeStyles(scheme),\n ...buildChromeStyles(buildCanvasChrome()),\n});\n\ntype Props = {\n value: string;\n onChange?: (value: string) => void;\n onSelectionChange?: (text: string) => void;\n onCreateEditor?: (view: EditorView, state: EditorState) => void;\n onDestroyEditor?: () => void;\n onBlurEditor?: (view: EditorView) => void;\n onFocusEditor?: (view: EditorView) => void;\n readOnly: boolean;\n placeholder?: string;\n searchQuery?: string;\n searchMatchIndex?: number;\n queryParser?: (sql: string) => Promise<string[]>;\n autoFocus?: boolean;\n lineNumbers?: boolean;\n className?: string;\n /**\n * A schema is too large to compare by value, so this one prop keeps the\n * identity requirement every other prop here is now free of: hand the editor\n * a stable object, or it rebuilds its whole extension tree on every render.\n */\n schema?: SQLNamespace;\n /**\n * Completions the caller knows and the schema does not — a table the code writes to\n * before it exists, say. Added to the schema, keyword and variable completions; unlike\n * `CodeEditor`'s prop of the same name, it replaces nothing.\n *\n * Compared by identity: build it in a `useMemo` or the editor rebuilds its whole\n * extension tree on every render.\n */\n completionSource?: CompletionSource;\n variables?: EditorVariable[];\n defaultSchema?: string;\n defaultDatabase?: string;\n highlightLines?: HighlightedLines;\n dialect?: SqlDialectName;\n};\n\nexport const SqlEditor = ({\n value,\n onChange,\n onSelectionChange,\n onCreateEditor,\n onDestroyEditor,\n onBlurEditor,\n onFocusEditor,\n placeholder = '',\n searchQuery = '',\n searchMatchIndex,\n readOnly = false,\n queryParser,\n autoFocus = false,\n lineNumbers = true,\n className,\n schema,\n completionSource,\n variables,\n defaultDatabase,\n defaultSchema,\n highlightLines,\n dialect,\n}: Props) => {\n const editorRef = useRef<ReactCodeMirrorRef>(null);\n\n // CodeMirror holds these by identity and rebuilds its whole extension tree when one\n // moves, which is every render for the inline arrows callers pass.\n const handleChange = useStableCallback((next: string) => onChange?.(next));\n const parseQueries = useStableCallback(async (sql: string) => (await queryParser?.(sql)) ?? []);\n const handleBlur = useStableCallback((view: EditorView) => onBlurEditor?.(view));\n const handleFocus = useStableCallback((view: EditorView) => onFocusEditor?.(view));\n\n const handleUpdate = useStableCallback((update: ViewUpdate) => {\n if (update.selectionSet) {\n const { main } = update.state.selection;\n\n onSelectionChange?.(update.state.sliceDoc(main.from, main.to));\n }\n });\n\n const variableCompletion = useVariableCompletion(variables);\n\n // Whether a parser was given decides whether the active-query highlighter is\n // installed at all; which parser it is does not, so the caller's may change\n // without a reconfigure.\n const withQueryParser = queryParser !== undefined;\n\n const tooltipLayer = useTooltipsExtension();\n\n // Themes re-resolve their `--*` tokens on brand / light-dark change. The\n // syntax palette shares `CodeEditor`'s cache entry — same `buildCustomTheme`.\n const customTheme = useThemedExtension('editor:syntax', buildCustomTheme);\n // `dark` sets `EditorView.darkTheme` — see `CodeEditor`'s base theme for what depends on it.\n const themeExtension = useThemedExtension('sql-editor:chrome', (scheme) =>\n EditorView.theme(buildTheme(scheme), { dark: scheme === 'dark' }),\n );\n\n const extensions = useMemo(() => {\n return [\n EditorView.lineWrapping,\n OWN_KEYMAP_EXTENSION,\n ...installExtensions({\n schema,\n defaultDatabase,\n defaultSchema,\n variableCompletion,\n completionSource,\n dialect,\n }),\n searchExtension(),\n highlightingExtension(),\n withQueryParser ? activeQueryHighlight(parseQueries) : [],\n tooltipLayer,\n themeExtension,\n EditorView.domEventHandlers({\n blur: (_, view) => handleBlur(view),\n focus: (_, view) => handleFocus(view),\n }),\n ];\n }, [\n withQueryParser,\n parseQueries,\n schema,\n completionSource,\n defaultDatabase,\n defaultSchema,\n variableCompletion,\n dialect,\n handleBlur,\n handleFocus,\n themeExtension,\n tooltipLayer,\n ]);\n\n const defaultSetup = useMemo(() => {\n return { ...buildBasicSetup(readOnly), ...OWN_KEYMAPS, lineNumbers };\n }, [readOnly, lineNumbers]);\n\n useEffect(() => {\n if (editorRef.current?.view) {\n updateHighlighting(editorRef.current.view, highlightLines);\n }\n }, [highlightLines]);\n\n useEffect(() => {\n if (editorRef.current?.view) {\n updateSearch(editorRef.current.view, searchQuery, searchMatchIndex);\n }\n }, [searchQuery, searchMatchIndex]);\n\n // Reports once, as the editor goes away, to whichever handler the last render passed.\n const handleDestroy = useStableCallback(() => onDestroyEditor?.());\n useEffect(() => handleDestroy, [handleDestroy]);\n\n const handleCreate = useStableCallback((view: EditorView, state: EditorState) => {\n onCreateEditor?.(view, state);\n\n if (highlightLines) {\n updateHighlighting(view, highlightLines);\n }\n\n if (searchQuery) {\n updateSearch(view, searchQuery, searchMatchIndex);\n }\n });\n\n return (\n <CodeMirror\n ref={editorRef}\n theme={customTheme}\n extensions={extensions}\n value={value}\n onChange={handleChange}\n onUpdate={handleUpdate}\n onCreateEditor={handleCreate}\n autoFocus={autoFocus}\n placeholder={placeholder}\n className={className}\n readOnly={readOnly}\n editable={!readOnly}\n basicSetup={defaultSetup}\n />\n );\n};\n","import { ChevronsLeft, ChevronsRight } from '../../../icons';\nimport { Button } from '../../Button';\n\ntype Props = {\n side: 'left' | 'right';\n onAccept: () => void;\n};\n\nexport const AcceptButton = ({ side, onAccept }: Props) => {\n const label = `Accept ${side === 'left' ? 'production' : 'branch'} version`;\n const Chevron = side === 'left' ? ChevronsRight : ChevronsLeft;\n return (\n <Button onClick={onAccept} variant=\"outline\" size=\"icon-sm\" title={label} aria-label={label}>\n <Chevron className=\"tw:size-5\" />\n </Button>\n );\n};\n","import type { RefCallback } from 'react';\n\nimport { X } from '../../../icons';\nimport { cn } from '../../../utils';\nimport { Button } from '../../Button';\nimport { LINE_HEIGHT_PX, type MergeConflict, PANE_HEADER_HEIGHT_PX } from '../constants';\n\nimport { AcceptButton } from './AcceptButton';\n\ntype Props = {\n conflicts: MergeConflict[];\n side: 'left' | 'right';\n onAccept: (conflict: MergeConflict) => void;\n onDismiss?: (conflict: MergeConflict) => void;\n contentRef?: RefCallback<HTMLDivElement>;\n};\n\n// 28px column when there are conflicts to render — wide enough to fit the\n// 24px icon-xs accept chevron + dismiss icon without overflowing into the\n// adjacent panes (which use `tw:overflow-hidden` and would clip absolutely-\n// positioned children that crossed the boundary). Collapses to 0 when idle\n// so empty merges don't reserve a sliver of layout. Inner wrapper allows\n// horizontal overflow so the icon buttons' focus-visible ring stays visible\n// past the column edge. Buttons stay aligned with their source-pane chunk\n// via the parent's `translateY(-scrollTop)` follower applied to `contentRef`.\nexport const AcceptControls = ({ conflicts, side, onAccept, onDismiss, contentRef }: Props) => (\n <div\n className={cn(\n 'tw:z-10 tw:flex tw:shrink-0 tw:flex-col tw:items-center',\n conflicts.length > 0 ? 'tw:w-7' : 'tw:w-0',\n )}\n >\n {conflicts.length > 0 && (\n <>\n <div className=\"tw:shrink-0\" style={{ height: `${PANE_HEADER_HEIGHT_PX}px` }} />\n <div className=\"tw:relative tw:w-full tw:flex-1 tw:overflow-y-clip tw:overflow-x-visible\">\n <div ref={contentRef} className=\"tw:relative tw:size-full\">\n {conflicts.map((conflict) => {\n const topLine = side === 'left' ? conflict.oldStartLine : conflict.newStartLine;\n const topOffset = (topLine - 1) * LINE_HEIGHT_PX - 2;\n\n return (\n <div\n key={conflict.id}\n className=\"tw:absolute tw:inset-x-0 tw:flex tw:flex-col tw:items-center tw:gap-0.5\"\n style={{ top: `${topOffset}px` }}\n >\n <AcceptButton side={side} onAccept={() => onAccept(conflict)} />\n {onDismiss && (\n <Button\n onClick={() => onDismiss(conflict)}\n variant=\"outline\"\n size=\"icon-sm\"\n title=\"Dismiss\"\n aria-label={`Dismiss ${side === 'left' ? 'production' : 'branch'} change`}\n >\n <X className=\"tw:size-4\" />\n </Button>\n )}\n </div>\n );\n })}\n </div>\n </div>\n </>\n )}\n </div>\n);\n","import {\n type Extension,\n RangeSet,\n RangeSetBuilder,\n StateEffect,\n StateField,\n} from '@codemirror/state';\nimport {\n Decoration,\n type DecorationSet,\n EditorView,\n gutterLineClass,\n GutterMarker,\n} from '@codemirror/view';\n\nimport colors from '@keboola/tailwind-config/colors';\n\nimport { buildDiffWash } from '../CodeEditor/diffWash';\nimport type { ResolvedColorScheme } from '../ThemeProvider/types';\n\nimport type { ChangeClassification } from './constants';\nimport { CHANGE_CLASS } from './constants';\n\ntype HighlightPayload = {\n lines: Set<number>;\n classification: Map<number, ChangeClassification>;\n};\n\nconst setHighlightedLines = StateEffect.define<HighlightPayload>();\n\nconst safeDecoration = Decoration.line({ class: 'cm-merge-safe-line' });\nconst conflictDecoration = Decoration.line({ class: 'cm-merge-conflict-line' });\n\nclass SafeGutterMarker extends GutterMarker {\n override elementClass = 'cm-merge-safe-gutter';\n}\n\nclass ConflictGutterMarker extends GutterMarker {\n override elementClass = 'cm-merge-conflict-gutter';\n}\n\nconst safeGutterMarker = new SafeGutterMarker();\nconst conflictGutterMarker = new ConflictGutterMarker();\n\ntype FieldValue = {\n decorations: DecorationSet;\n gutterMarkers: RangeSet<GutterMarker>;\n};\n\nexport const createDiffHighlightExtension = (): Extension =>\n StateField.define<FieldValue>({\n create: () => ({ decorations: Decoration.none, gutterMarkers: RangeSet.empty }),\n update(value, tr) {\n // Use the last effect if multiple are dispatched in a single transaction\n let payload: HighlightPayload | null = null;\n for (const effect of tr.effects) {\n if (effect.is(setHighlightedLines)) {\n payload = effect.value;\n }\n }\n\n if (!payload) {\n return {\n decorations: value.decorations.map(tr.changes),\n gutterMarkers: value.gutterMarkers.map(tr.changes),\n };\n }\n\n const decoBuilder = new RangeSetBuilder<Decoration>();\n const gutterBuilder = new RangeSetBuilder<GutterMarker>();\n const doc = tr.state.doc;\n const { lines, classification } = payload;\n\n // Iterate the (typically small) highlighted set instead of all doc lines.\n // RangeSetBuilder requires positions in order, so sort first.\n const sorted = [...lines].filter((l) => l >= 1 && l <= doc.lines).sort((a, b) => a - b);\n for (const i of sorted) {\n const cls = classification.get(i);\n if (!cls) continue;\n\n const isSafe = cls === CHANGE_CLASS.SAFE;\n const line = doc.line(i);\n decoBuilder.add(line.from, line.from, isSafe ? safeDecoration : conflictDecoration);\n gutterBuilder.add(line.from, line.from, isSafe ? safeGutterMarker : conflictGutterMarker);\n }\n\n return {\n decorations: decoBuilder.finish(),\n gutterMarkers: gutterBuilder.finish(),\n };\n },\n provide: (f) => [\n EditorView.decorations.from(f, (val) => val.decorations),\n gutterLineClass.from(f, (val: FieldValue) => val.gutterMarkers),\n ],\n });\n\n/**\n * The highlight palette, split out of the StateField above so it can be built\n * through `useThemedExtension` — the field instance must stay stable across a\n * scheme change (a fresh one would drop the current highlights), the colours\n * must not.\n *\n * `!important` overrides CodeMirror's built-in active-line and selection\n * backgrounds. SAFE = change present on one side only (green). CONFLICT = both\n * sides edited this region (amber/warning — NOT red, since red reads as\n * \"deletion\" and the 3-pane editor only ever displays additions in each side\n * pane). The colored left border doubles as a scan-lane so changes are easy to\n * spot when zoomed out.\n *\n * Light mode paints an opaque pastel wash. Dark mode tints the same hue at low\n * alpha over the pane's token-driven surface instead — the pastel is opaque, so\n * it put the dark scheme's near-white line text and grey line numbers on a\n * light slab.\n *\n * A pure function of `scheme`, which `useThemedExtension` passes from the same\n * read that produced its cache key.\n */\nexport const buildDiffHighlightTheme = (scheme: ResolvedColorScheme): Extension => {\n const dark = scheme === 'dark';\n const { line, gutter } = buildDiffWash(scheme);\n\n // Line *content* keeps the pane's `foreground`, which clears AA on either\n // fill. The gutter's line numbers do not: `muted-foreground` on the tinted\n // gutter is 3.96:1 in light and 2.82:1 in dark, so the marker carries a\n // hue-matched number colour instead (≥6:1 in both).\n const safeNumber = dark ? colors.green[300] : colors.green[900];\n const conflictNumber = dark ? colors.warning[300] : colors.warning[800];\n\n return EditorView.theme({\n '.cm-merge-safe-line': {\n backgroundColor: `${line(colors.green[500], colors.green[100])} !important`,\n boxShadow: `inset 3px 0 0 ${colors.green[500]}`,\n },\n '.cm-merge-conflict-line': {\n backgroundColor: `${line(colors.warning[500], colors.warning[100])} !important`,\n boxShadow: `inset 3px 0 0 ${colors.warning[500]}`,\n },\n '.cm-merge-safe-gutter': {\n backgroundColor: `${gutter(colors.green[500], colors.green[200])} !important`,\n color: safeNumber,\n },\n '.cm-merge-conflict-gutter': {\n backgroundColor: `${gutter(colors.warning[500], colors.warning[200])} !important`,\n color: conflictNumber,\n },\n });\n};\n\nexport const updateHighlightedLines = (\n view: EditorView,\n lines: Set<number>,\n classification: Map<number, ChangeClassification>,\n) => {\n view.dispatch({ effects: setHighlightedLines.of({ lines, classification }) });\n};\n","import { type ReactNode, useCallback, useEffect, useMemo, useRef } from 'react';\nimport { type Extension } from '@codemirror/state';\nimport { EditorView } from '@codemirror/view';\nimport CodeMirror, { type ReactCodeMirrorRef } from '@uiw/react-codemirror';\n\nimport colors from '@keboola/tailwind-config/colors';\n\nimport { useStableCallback } from '../../hooks/useStableCallback';\nimport { cn } from '../../utils';\nimport { resolveToken } from '../../utils/themeTokens';\nimport { buildBasicSetup } from '../CodeEditor/helpers';\nimport { useThemedExtension } from '../CodeEditor/tokenTheme';\n\nimport { type ChangeClassification, LINE_HEIGHT_PX } from './constants';\nimport {\n buildDiffHighlightTheme,\n createDiffHighlightExtension,\n updateHighlightedLines,\n} from './decorations';\n\nconst EMPTY_LINES: Set<number> = new Set();\nconst EMPTY_CLASSIFICATION: Map<number, ChangeClassification> = new Map();\n\ntype Props = {\n value: string;\n highlightedLines: Set<number>;\n lineClassification: Map<number, ChangeClassification>;\n readOnly?: boolean;\n onChange?: (value: string) => void;\n header?: ReactNode;\n headerClassName?: string;\n className?: string;\n onScrollDOMReady?: (el: HTMLElement | null) => void;\n /**\n * When true, the pane renders with the error palette to signal the side has\n * been deleted upstream. The CodeMirror editor is still mounted so the diff\n * remains visible — only the background, gutter, header, and border switch.\n */\n deleted?: boolean;\n};\n\nexport const MergePane = ({\n value,\n highlightedLines,\n lineClassification,\n readOnly = false,\n onChange,\n header,\n headerClassName,\n className,\n onScrollDOMReady,\n deleted = false,\n}: Props) => {\n const editorRef = useRef<ReactCodeMirrorRef>(null);\n const handleChange = useStableCallback((next: string) => onChange?.(next));\n\n // Non-deleted pane chrome re-resolves its `--*` tokens on brand /\n // light-dark change. The deleted pane keeps the categorical error wash\n // (deletion is meaning-encoding, not chrome) — but the wash itself is a\n // light/dark pair: the opaque light-red slab left the dark scheme's\n // near-white text unreadable, so dark tints the same hue at low alpha over\n // the page instead and pins the token text colour on top of it.\n const themeExtension = useThemedExtension(`merge-pane:${deleted}`, (scheme) => {\n const dark = scheme === 'dark';\n const deletedSurface = dark ? `${colors.error[500]}1f` : colors.error[100];\n const deletedGutter = dark ? `${colors.error[500]}3d` : colors.error[200];\n // CodeMirror's stock line-number grey is only 3.46:1 on the light wash, so\n // the deleted gutter carries a hue-matched number colour of its own.\n const deletedGutterText = dark ? colors.error[300] : colors.error[700];\n\n return EditorView.theme({\n '&.cm-editor': {\n height: '100%',\n backgroundColor: deleted ? deletedSurface : resolveToken('background', '249 250 251'),\n // Pair the token-driven surface with a token-driven text color so line\n // content stays readable when the pane's `--background` goes dark.\n color: resolveToken('foreground', '30 41 59'),\n outline: 'none',\n },\n '.cm-gutters': {\n backgroundColor: deleted ? deletedGutter : resolveToken('muted', '241 245 249'),\n color: deleted ? deletedGutterText : resolveToken('muted-foreground', '100 116 139'),\n },\n '*': { lineHeight: `${LINE_HEIGHT_PX}px` },\n '.cm-lineNumbers .cm-gutterElement': { padding: '0 0 0 25px' },\n });\n });\n\n // Memoized independently of the theme so its `StateField` instance stays\n // stable when the editor reconfigures on brand / light-dark change. A fresh\n // extension would re-init the field and drop the current highlights.\n const diffHighlightExtension = useMemo(() => createDiffHighlightExtension(), []);\n\n // Its palette, by contrast, *must* rebuild on scheme change — see\n // `buildDiffHighlightTheme`.\n const diffHighlightTheme = useThemedExtension('merge-diff-highlight', buildDiffHighlightTheme);\n\n // Two of a merge view's three panes are read-only.\n const basicSetup = useMemo(\n () => ({ ...buildBasicSetup(readOnly), lineNumbers: true }),\n [readOnly],\n );\n\n const extensions = useMemo(\n (): Extension[] => [\n diffHighlightExtension,\n diffHighlightTheme,\n themeExtension,\n // Recalc viewport after every doc change so late-settling layouts (alerts\n // appearing above the editor) don't leave trailing lines blank.\n EditorView.updateListener.of((update) => {\n if (update.docChanged) {\n requestAnimationFrame(() => update.view.dispatch({}));\n }\n }),\n ],\n [diffHighlightExtension, diffHighlightTheme, themeExtension],\n );\n\n // A deleted side is a file-level conflict, not a line-level one — suppress\n // per-line markers so the pane wears only the red wash. `deleted` stays in\n // the dep list so highlights switch between the empty and populated sets when\n // the side toggles.\n useEffect(() => {\n const view = editorRef.current?.view;\n if (!view) return;\n updateHighlightedLines(\n view,\n deleted ? EMPTY_LINES : highlightedLines,\n deleted ? EMPTY_CLASSIFICATION : lineClassification,\n );\n }, [highlightedLines, lineClassification, deleted]);\n\n const handleCreateEditor = useCallback(\n (view: EditorView) => {\n updateHighlightedLines(\n view,\n deleted ? EMPTY_LINES : highlightedLines,\n deleted ? EMPTY_CLASSIFICATION : lineClassification,\n );\n onScrollDOMReady?.(view.scrollDOM);\n },\n [highlightedLines, lineClassification, onScrollDOMReady, deleted],\n );\n\n useEffect(() => {\n return () => onScrollDOMReady?.(null);\n }, [onScrollDOMReady]);\n\n // Re-measure on wrapper resize — parent dimensions settle after CM mounts\n // (fullscreen modal, alerts appearing above the editor).\n useEffect(() => {\n const view = editorRef.current?.view;\n const wrapper = view?.dom.parentElement;\n if (!view || !wrapper) return;\n\n const observer = new ResizeObserver(() => view.dispatch({}));\n observer.observe(wrapper);\n\n return () => observer.disconnect();\n }, []);\n\n return (\n <div\n className={cn(\n 'tw:flex tw:min-w-0 tw:flex-1 tw:flex-col tw:overflow-hidden',\n className,\n deleted && 'tw:border-error-300 tw:[[data-theme=dark]_&]:border-error-500/50',\n )}\n >\n {header && (\n <div\n className={cn(\n 'tw:px-4 tw:py-3',\n // Same light/dark split as the editor wash below — `PaneHeader` pins\n // `text-foreground`, which goes near-white and vanishes on the\n // opaque light-red band.\n deleted\n ? 'tw:border-b tw:border-error-200 tw:bg-error-100 tw:[[data-theme=dark]_&]:border-error-500/30 tw:[[data-theme=dark]_&]:bg-error-500/15'\n : 'tw:bg-background',\n headerClassName,\n )}\n >\n {header}\n </div>\n )}\n <div className=\"tw:flex tw:h-0 tw:flex-1 tw:flex-col tw:[&_>div]:h-full\">\n <CodeMirror\n ref={editorRef}\n height=\"100%\"\n value={value}\n onChange={handleChange}\n extensions={extensions}\n readOnly={readOnly}\n onCreateEditor={handleCreateEditor}\n basicSetup={basicSetup}\n />\n </div>\n </div>\n );\n};\n","import { useCallback, useRef } from 'react';\n\nexport const useScrollSync = () => {\n const scrollersRef = useRef<(HTMLElement | null)[]>([]);\n const followersRef = useRef<Map<string, { paneIndex: number; el: HTMLElement }>>(new Map());\n const isSyncingRef = useRef(false);\n const scrollerCallbacksRef = useRef<Map<number, (el: HTMLElement | null) => void>>(new Map());\n const followerCallbacksRef = useRef<Map<string, (el: HTMLElement | null) => void>>(new Map());\n\n const updateFollowers = useCallback(() => {\n for (const [, { paneIndex, el }] of followersRef.current) {\n const scroller = scrollersRef.current[paneIndex];\n if (!scroller) continue;\n el.style.transform = `translateY(-${scroller.scrollTop}px)`;\n }\n }, []);\n\n const handleScroll = useCallback(\n (e: Event) => {\n if (isSyncingRef.current) return;\n isSyncingRef.current = true;\n\n // Px-aligned sync. All panes share LINE_HEIGHT_PX, so matching scrollTop is the same\n // as matching the line at the top of the viewport. Previous ratio-based sync drifted\n // badly when pane heights differed (e.g. 50 lines vs 500 — half-scrolling left snapped\n // right to its midpoint, not to the equivalent line). Clamp to each target's max so\n // the source can scroll past where shorter panes bottom out without errors.\n const sourceScrollTop = (e.target as HTMLElement).scrollTop;\n\n for (const scroller of scrollersRef.current) {\n if (!scroller || scroller === e.target) continue;\n const targetMax = Math.max(0, scroller.scrollHeight - scroller.clientHeight);\n scroller.scrollTop = Math.min(sourceScrollTop, targetMax);\n }\n\n updateFollowers();\n isSyncingRef.current = false;\n },\n [updateFollowers],\n );\n\n const registerScroller = useCallback(\n (index: number) => {\n const existing = scrollerCallbacksRef.current.get(index);\n if (existing) return existing;\n\n const callback = (el: HTMLElement | null) => {\n const prev = scrollersRef.current[index];\n if (prev === el) return;\n\n prev?.removeEventListener('scroll', handleScroll);\n scrollersRef.current[index] = el;\n el?.addEventListener('scroll', handleScroll, { passive: true });\n };\n\n scrollerCallbacksRef.current.set(index, callback);\n return callback;\n },\n [handleScroll],\n );\n\n const registerFollower = useCallback((paneIndex: number, key: string) => {\n const existing = followerCallbacksRef.current.get(key);\n if (existing) return existing;\n\n const callback = (el: HTMLElement | null) => {\n if (el) {\n followersRef.current.set(key, { paneIndex, el });\n } else {\n followersRef.current.delete(key);\n }\n };\n\n followerCallbacksRef.current.set(key, callback);\n return callback;\n }, []);\n\n return { registerScroller, registerFollower };\n};\n","import type { ReactNode } from 'react';\n\nimport { cn } from '../../utils';\n\nimport { AcceptControls } from './AcceptControls/AcceptControls';\nimport type { ChangeClassification, MergeConflict } from './constants';\nimport { MergePane } from './MergePane';\nimport { useScrollSync } from './useScrollSync';\n\nconst EMPTY_LINES: Set<number> = new Set();\nconst EMPTY_CLASSIFICATION: Map<number, ChangeClassification> = new Map();\n\ntype Props = {\n mergeValue: string;\n handleMergeChange: (value: string) => void;\n leftText: string;\n rightText: string;\n leftConflicts: MergeConflict[];\n rightConflicts: MergeConflict[];\n leftHighlightedLines: Set<number>;\n rightHighlightedLines: Set<number>;\n leftClassification: Map<number, ChangeClassification>;\n rightClassification: Map<number, ChangeClassification>;\n mergeHighlightedLines: Set<number>;\n mergeClassification: Map<number, ChangeClassification>;\n acceptLeft: (conflict: MergeConflict) => void;\n acceptRight: (conflict: MergeConflict) => void;\n dismissLeft: (conflict: MergeConflict) => void;\n dismissRight: (conflict: MergeConflict) => void;\n leftHeader?: ReactNode;\n middleHeader?: ReactNode;\n middleHeaderClassName?: string;\n middleClassName?: string;\n rightHeader?: ReactNode;\n className?: string;\n /**\n * Paint the corresponding read-only pane with the error palette to signal the\n * side has been deleted upstream. The diff is still rendered so the user can\n * see what was lost; accept/dismiss controls for that side are suppressed.\n */\n leftDeleted?: boolean;\n rightDeleted?: boolean;\n};\n\nexport const MergeEditor = ({\n mergeValue,\n handleMergeChange,\n leftText,\n rightText,\n leftConflicts,\n rightConflicts,\n leftHighlightedLines,\n rightHighlightedLines,\n leftClassification,\n rightClassification,\n mergeHighlightedLines,\n mergeClassification,\n acceptLeft,\n acceptRight,\n dismissLeft,\n dismissRight,\n leftHeader,\n middleHeader,\n middleHeaderClassName,\n middleClassName,\n rightHeader,\n className,\n leftDeleted = false,\n rightDeleted = false,\n}: Props) => {\n const { registerScroller, registerFollower } = useScrollSync();\n\n // When either side is deleted the merge is a binary \"keep or drop the file\"\n // choice, not a line-level merge — suppress per-line markers in the middle\n // pane so the indicator/markers don't contradict the red deleted pane.\n const anySideDeleted = leftDeleted || rightDeleted;\n\n return (\n <div\n className={cn(\n 'tw:flex tw:h-full tw:min-h-0 tw:overflow-hidden tw:bg-background tw:gap-3',\n className,\n )}\n >\n <MergePane\n value={leftText}\n highlightedLines={leftHighlightedLines}\n lineClassification={leftClassification}\n readOnly\n header={leftHeader}\n className=\"tw:rounded-r-lg tw:border tw:border-border\"\n onScrollDOMReady={registerScroller(0)}\n deleted={leftDeleted}\n />\n\n <AcceptControls\n conflicts={leftDeleted ? [] : leftConflicts}\n side=\"left\"\n onAccept={acceptLeft}\n onDismiss={dismissLeft}\n contentRef={registerFollower(0, 'left-controls')}\n />\n\n <MergePane\n value={mergeValue}\n highlightedLines={anySideDeleted ? EMPTY_LINES : mergeHighlightedLines}\n lineClassification={anySideDeleted ? EMPTY_CLASSIFICATION : mergeClassification}\n onChange={handleMergeChange}\n header={middleHeader}\n headerClassName={middleHeaderClassName}\n className={cn('tw:border tw:border-border tw:rounded-lg', middleClassName)}\n onScrollDOMReady={registerScroller(1)}\n />\n\n <AcceptControls\n conflicts={rightDeleted ? [] : rightConflicts}\n side=\"right\"\n onAccept={acceptRight}\n onDismiss={dismissRight}\n contentRef={registerFollower(2, 'right-controls')}\n />\n\n <MergePane\n value={rightText}\n highlightedLines={rightHighlightedLines}\n lineClassification={rightClassification}\n readOnly\n header={rightHeader}\n className=\"tw:rounded-l-lg tw:border tw:border-border\"\n onScrollDOMReady={registerScroller(2)}\n deleted={rightDeleted}\n />\n </div>\n );\n};\n","import type { ComponentProps } from 'react';\n\nimport { FieldItem, type FieldItemProps } from '../../blocks/Form/connectors/FieldItem';\nimport { useFieldContext } from '../../blocks/Form/contexts';\n\nimport { CodeEditor } from './CodeEditor';\n\ntype Props = FieldItemProps &\n Omit<\n ComponentProps<typeof CodeEditor>,\n // Owned by the field / `FieldItem` — see the connector rules in AGENTS.md.\n | 'value'\n | 'onChange'\n | 'onBlur'\n | 'ariaLabel'\n | 'ariaLabelledBy'\n | 'ariaDescribedBy'\n | 'ariaInvalid'\n | 'ariaRequired'\n // Not the field's — this wrapper closes it deliberately. `readOnly` turns the editor into a\n // viewer, taking the lint marks and completions with it, which is not what a disabled form\n // control should look like. `inputLocked` stays open for a transient lock.\n | 'readOnly'\n // `className` styles the field frame; the editor box gets its own prop below.\n | 'className'\n // Derived from `required` below — whether a blank document is valid is the field's contract,\n // and a call site could otherwise set it to contradict its own validation.\n | 'allowBlankDocument'\n > & {\n /** Styles the editor box; `className` styles the field frame. */\n editorClassName?: string;\n };\n\n/**\n * Code / JSON editor field. State is the raw editor text (`string`) — never a parsed value.\n *\n * ⚠️ Shipped from **`@keboola/design/editors`**, not as a `field.*` connector on `useAppForm`, and\n * deliberately: CodeMirror plus its language modes is ~170 kB brotlied, and registering this in the root\n * form hook pulled all of it into `@keboola/design`'s main entry — every consumer paying for an editor\n * most of them never render. That is the whole reason the `editors` subpath exists.\n *\n * It still reads the field from context, so it composes exactly like a connector:\n *\n * ```tsx\n * import { CodeEditorField } from '@keboola/design/editors';\n *\n * <form.AppField name=\"schema\">{() => <CodeEditorField label=\"Schema\" />}</form.AppField>\n * ```\n *\n * Text in, text out, deliberately: a JSON field is mid-edit invalid for most of the time the user is\n * typing in it, and parsing on every keystroke would either throw away their cursor position or throw\n * away the text. Parse in the schema (or at submit) and keep the editor's own content authoritative.\n *\n * `variant=\"field\"` by default so it reads as a form control rather than a code block.\n *\n * Labelled with `aria-labelledby`, not `for`: CodeMirror's editable node is a `div[role=\"textbox\"]`,\n * which is not a labelable element.\n */\nexport const CodeEditorField = ({\n label,\n action,\n required,\n requiredMark,\n help,\n tooltip,\n className,\n 'aria-describedby': ariaDescribedBy,\n editorClassName,\n variant = 'field',\n ...editorProps\n}: Props) => {\n const field = useFieldContext<string>();\n\n return (\n <FieldItem\n label={label}\n action={action}\n required={required}\n requiredMark={requiredMark}\n labelling=\"aria-labelledby\"\n help={help}\n tooltip={tooltip}\n className={className}\n aria-describedby={ariaDescribedBy}\n >\n {(control) => (\n <CodeEditor\n {...editorProps}\n variant={variant}\n className={editorClassName}\n // Remapped rather than spread: CodeEditor takes these as camelCase props and writes them onto\n // CodeMirror's content node, since the wrapper `<div>` is not what gets focused.\n ariaLabelledBy={control['aria-labelledby']}\n ariaDescribedBy={control['aria-describedby']}\n ariaInvalid={control['aria-invalid']}\n ariaRequired={control['aria-required']}\n // An optional field starts empty and is cleared back to empty, so `jsonParseLinter`'s\n // \"Unexpected end of input\" would contradict the field's own validation. A required one\n // keeps the marker: there, empty really is wrong.\n allowBlankDocument={!required}\n value={field.state.value ?? ''}\n onChange={field.handleChange}\n onBlur={field.handleBlur}\n />\n )}\n </FieldItem>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"editors.js","names":["keymap","EditorView","EMPTY_LINES","EMPTY_CLASSIFICATION"],"sources":["../src/components/CodeEditor/activeQueryHighlight.ts","../src/components/CodeEditor/highlighting.ts","../src/components/CodeEditor/SqlEditor.tsx","../src/components/MergeEditor/AcceptControls/AcceptButton.tsx","../src/components/MergeEditor/AcceptControls/AcceptControls.tsx","../src/components/MergeEditor/decorations.ts","../src/components/MergeEditor/MergePane.tsx","../src/components/MergeEditor/useScrollSync.ts","../src/components/MergeEditor/MergeEditor.tsx","../src/components/CodeEditor/CodeEditorField.tsx"],"sourcesContent":["import type { ChangeSet, Range } from '@codemirror/state';\nimport { Decoration, EditorView, ViewPlugin, type ViewUpdate } from '@codemirror/view';\n\nimport colors from '@keboola/tailwind-config/colors';\n\nconst activeQueryFirstLine = Decoration.line({ class: 'cm-active-query cm-active-query-first' });\nconst activeQueryMiddleLine = Decoration.line({ class: 'cm-active-query' });\nconst activeQueryLastLine = Decoration.line({ class: 'cm-active-query cm-active-query-last' });\nconst activeQuerySingleLine = Decoration.line({\n class: 'cm-active-query cm-active-query-first cm-active-query-last',\n});\n\ntype QueryBoundary = { from: number; to: number };\n\n/** Whitespace-trimmed offsets of the query holding the cursor, or null. */\nexport const findActiveQueryRange = (\n doc: string,\n cursorPos: number,\n boundaries: QueryBoundary[],\n): { from: number; to: number } | null => {\n if (boundaries.length === 0) return null;\n\n let match: QueryBoundary | undefined = boundaries[boundaries.length - 1];\n for (const boundary of boundaries) {\n if (cursorPos <= boundary.to) {\n match = boundary;\n break;\n }\n }\n\n if (!match) return null;\n\n const queryText = doc.slice(match.from, match.to);\n const leadingWhitespace = queryText.length - queryText.trimStart().length;\n const trailingWhitespace = queryText.length - queryText.trimEnd().length;\n\n const trimmedStart = match.from + leadingWhitespace;\n const trimmedEnd = match.to - trailingWhitespace;\n\n if (trimmedStart >= trimmedEnd) return null;\n\n return { from: trimmedStart, to: trimmedEnd };\n};\n\n/**\n * Character-offset boundaries for the fragments a splitter reported for `doc`.\n *\n * - Bounded by `docLength`: an injected splitter is not trusted to re-join to `doc`.\n * - A fragment past the end is cut there, or dropped if it starts past it.\n * - Offsets past the document would throw in `Text.lineAt`.\n */\nexport const computeBoundaries = (queries: string[], docLength: number): QueryBoundary[] => {\n const boundaries: QueryBoundary[] = [];\n let offset = 0;\n for (const query of queries) {\n if (offset >= docLength) break;\n boundaries.push({ from: offset, to: Math.min(offset + query.length, docLength) });\n offset += query.length;\n }\n return boundaries;\n};\n\n/**\n * Synchronously map cached boundaries through a changeset.\n *\n * - Clamped to `[0, changes.length]` first: the re-parse is debounced, so boundaries can be\n * stale, and `ChangeSet.mapPos` throws a `RangeError` past the pre-change length.\n * - Degenerate boundaries are dropped.\n */\nexport const mapBoundariesThroughChanges = (\n boundaries: QueryBoundary[],\n changes: ChangeSet,\n): QueryBoundary[] => {\n const prevLen = changes.length;\n const mapped: QueryBoundary[] = [];\n for (const b of boundaries) {\n const from = changes.mapPos(Math.max(0, Math.min(b.from, prevLen)), -1);\n const to = changes.mapPos(Math.max(0, Math.min(b.to, prevLen)), 1);\n if (from < to) mapped.push({ from, to });\n }\n return mapped;\n};\n\nconst buildDecorations = (view: EditorView, range: { from: number; to: number }) => {\n const startLine = view.state.doc.lineAt(range.from).number;\n const endLine = view.state.doc.lineAt(range.to).number;\n\n const decorations: Range<Decoration>[] = [];\n for (let lineNum = startLine; lineNum <= endLine; lineNum++) {\n const lineStart = view.state.doc.line(lineNum).from;\n if (startLine === endLine) {\n decorations.push(activeQuerySingleLine.range(lineStart));\n } else if (lineNum === startLine) {\n decorations.push(activeQueryFirstLine.range(lineStart));\n } else if (lineNum === endLine) {\n decorations.push(activeQueryLastLine.range(lineStart));\n } else {\n decorations.push(activeQueryMiddleLine.range(lineStart));\n }\n }\n\n return Decoration.set(decorations);\n};\n\nexport const activeQueryHighlight = (splitQueries: (sql: string) => Promise<string[]>) => {\n return [\n ViewPlugin.fromClass(\n class {\n decorations = Decoration.none;\n private debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private seq = 0;\n private cachedDoc = '';\n private cachedBoundaries: QueryBoundary[] = [];\n private destroyed = false;\n\n constructor(view: EditorView) {\n this.scheduleDocParse(view);\n }\n\n update(update: ViewUpdate) {\n if (update.focusChanged && !update.view.hasFocus) {\n this.decorations = Decoration.none;\n return;\n }\n\n if (update.docChanged) {\n // Map the cache now so decorations don't wait for the re-parse.\n if (this.cachedBoundaries.length > 0) {\n this.cachedBoundaries = mapBoundariesThroughChanges(\n this.cachedBoundaries,\n update.changes,\n );\n this.cachedDoc = update.state.doc.toString();\n this.updateFromCache(update.view);\n }\n // Re-parse anyway: edits like a new `;` move the boundaries.\n this.scheduleDocParse(update.view);\n } else if (update.selectionSet || (update.focusChanged && update.view.hasFocus)) {\n this.updateFromCache(update.view);\n }\n }\n\n destroy() {\n this.destroyed = true;\n if (this.debounceTimer) clearTimeout(this.debounceTimer);\n }\n\n /** Sets `this.decorations` without dispatching; an async caller must dispatch. */\n private updateFromCache(view: EditorView) {\n if (!view.hasFocus) {\n this.decorations = Decoration.none;\n return;\n }\n\n const { main } = view.state.selection;\n\n if (main.from !== main.to) {\n this.decorations = Decoration.none;\n return;\n }\n\n const doc = view.state.doc.toString();\n if (doc !== this.cachedDoc) return;\n if (this.cachedBoundaries.length === 0) {\n this.decorations = Decoration.none;\n return;\n }\n\n const range = findActiveQueryRange(doc, main.head, this.cachedBoundaries);\n this.decorations = range ? buildDecorations(view, range) : Decoration.none;\n }\n\n private scheduleDocParse(view: EditorView) {\n if (this.debounceTimer) clearTimeout(this.debounceTimer);\n\n this.debounceTimer = setTimeout(async () => {\n const seq = ++this.seq;\n const doc = view.state.doc.toString();\n\n if (!doc.trim()) {\n this.cachedDoc = doc;\n this.cachedBoundaries = [];\n if (this.decorations !== Decoration.none) {\n this.decorations = Decoration.none;\n if (!this.destroyed) view.dispatch();\n }\n return;\n }\n\n let queries: string[];\n try {\n queries = await splitQueries(doc);\n } catch {\n return;\n }\n\n // Discard stale results\n if (this.destroyed || seq !== this.seq) return;\n\n this.cachedDoc = doc;\n this.cachedBoundaries = computeBoundaries(queries, doc.length);\n\n this.updateFromCache(view);\n if (!this.destroyed) view.dispatch();\n }, 100);\n }\n },\n { decorations: (v) => v.decorations },\n ),\n EditorView.baseTheme({\n '.cm-active-query': {\n boxShadow: `inset -1px 0 0 0 ${colors.blue[300]}, inset 1px 0 0 0 ${colors.blue[300]}`,\n marginRight: '8px',\n },\n '.cm-active-query-first': {\n boxShadow: `inset -1px 0 0 0 ${colors.blue[300]}, inset 1px 0 0 0 ${colors.blue[300]}, inset 0 1px 0 0 ${colors.blue[300]}`,\n borderRadius: '4px 4px 0 0',\n },\n '.cm-active-query-last': {\n boxShadow: `inset -1px 0 0 0 ${colors.blue[300]}, inset 1px 0 0 0 ${colors.blue[300]}, inset 0 -1px 0 0 ${colors.blue[300]}`,\n borderRadius: '0 0 4px 4px',\n },\n '.cm-active-query-first.cm-active-query-last': {\n boxShadow: `inset 0 0 0 1px ${colors.blue[300]}`,\n borderRadius: '4px',\n },\n }),\n ];\n};\n","import type { Extension } from '@codemirror/state';\nimport { RangeSet, StateEffect, StateField } from '@codemirror/state';\nimport { Decoration, EditorView, gutter, GutterMarker } from '@codemirror/view';\n\nimport colors from '@keboola/tailwind-config/colors';\n\nimport { DARK_LINE_ALPHA } from './diffWash';\n\nexport type HighlightedLines = {\n from: number;\n to: number;\n error?: { from: number; to: number };\n processing?: { from: number; to: number };\n} | null;\n\n// Gutter markers for highlighting\nclass HighlightMarker extends GutterMarker {\n override toDOM() {\n const marker = document.createElement('div');\n marker.className = 'cm-highlight-marker';\n return marker;\n }\n}\n\nconst highlightMarker = new HighlightMarker();\n\n// Helper function to iterate over line range and apply a callback\nconst forEachLineInRange = <T>(\n doc: { lines: number; line: (n: number) => { from: number } },\n from: number,\n to: number,\n callback: (lineFrom: number) => T,\n): T[] => {\n const results: T[] = [];\n\n // Convert 0-based to 1-based for CodeMirror\n const fromLine = Math.max(1, from + 1);\n const toLine = Math.min(doc.lines, to + 1);\n\n for (let i = fromLine; i <= toLine; i++) {\n const line = doc.line(i);\n results.push(callback(line.from));\n }\n\n return results;\n};\n\n// State effect for setting highlighted lines\nconst setHighlightedLinesEffect = StateEffect.define<HighlightedLines>();\n\n// State field for storing highlighted lines info\nconst highlightedLinesField = StateField.define<HighlightedLines>({\n create() {\n return null;\n },\n update(value, transaction) {\n for (const effect of transaction.effects) {\n if (effect.is(setHighlightedLinesEffect)) {\n return effect.value;\n }\n }\n return value;\n },\n});\n\n// Gutter extension that displays highlight markers\nconst highlightGutter = gutter({\n class: 'cm-highlight-gutter',\n markers: (view): RangeSet<GutterMarker> => {\n const highlightInfo = view.state.field(highlightedLinesField, false);\n\n if (!highlightInfo) {\n return RangeSet.empty;\n }\n\n // Add blue markers for all running query lines\n const markers = forEachLineInRange(\n view.state.doc,\n highlightInfo.from,\n highlightInfo.to,\n (lineFrom) => highlightMarker.range(lineFrom),\n );\n\n return RangeSet.of(markers);\n },\n});\n\n// State field for error line background decorations\nconst errorLineDecorations = StateField.define({\n create() {\n return Decoration.none;\n },\n update(decorations, transaction) {\n decorations = decorations.map(transaction.changes);\n for (const effect of transaction.effects) {\n if (!effect.is(setHighlightedLinesEffect)) {\n continue;\n }\n\n if (!effect.value || !effect.value.error) {\n return Decoration.none;\n }\n\n // Add error line background decorations\n const decorationArray = forEachLineInRange(\n transaction.state.doc,\n effect.value.error.from,\n effect.value.error.to,\n (lineFrom) => Decoration.line({ class: 'cm-error-line' }).range(lineFrom),\n );\n\n return Decoration.set(decorationArray);\n }\n return decorations;\n },\n provide: (f) => EditorView.decorations.from(f),\n});\n\n// State field for processing line background decorations\nconst processingLineDecorations = StateField.define({\n create() {\n return Decoration.none;\n },\n update(decorations, transaction) {\n decorations = decorations.map(transaction.changes);\n for (const effect of transaction.effects) {\n if (!effect.is(setHighlightedLinesEffect)) {\n continue;\n }\n\n if (!effect.value || !effect.value.processing) {\n return Decoration.none;\n }\n\n const decorationArray = forEachLineInRange(\n transaction.state.doc,\n effect.value.processing.from,\n effect.value.processing.to,\n (lineFrom) => Decoration.line({ class: 'cm-processing-line' }).range(lineFrom),\n );\n\n return Decoration.set(decorationArray);\n }\n return decorations;\n },\n provide: (f) => EditorView.decorations.from(f),\n});\n\n/**\n * The run-state washes for the line a query occupies.\n *\n * - `&dark` keeps this a static base theme rather than a themed extension — see `search.ts` for\n * the mechanism and what sets the facet.\n * - Light paints an opaque pastel; dark tints the same `500` hue over the page, at the line alpha\n * the diff wash uses, so an opaque slab never sits under near-white text.\n * - `.cm-highlight-marker` needs no pair: `blue[500]` is 4.60:1 on the darkest surface it sits on.\n */\nconst highlightedLinesTheme = EditorView.baseTheme({\n '.cm-highlight-gutter': {\n width: '2px',\n marginRight: '2px',\n },\n '.cm-highlight-marker': {\n width: '2px',\n height: '100%',\n backgroundColor: colors.blue[500],\n },\n '.cm-error-line': {\n backgroundColor: `${colors.error[100]} !important`,\n },\n '.cm-processing-line': {\n backgroundColor: `${colors.blue[100]} !important`,\n },\n\n '&dark .cm-error-line': {\n backgroundColor: `${colors.error[500]}${DARK_LINE_ALPHA} !important`,\n },\n '&dark .cm-processing-line': {\n backgroundColor: `${colors.blue[500]}${DARK_LINE_ALPHA} !important`,\n },\n});\n\n// Install highlight lines extension\nexport function highlightingExtension(): Extension[] {\n return [\n highlightedLinesField,\n highlightGutter,\n errorLineDecorations,\n processingLineDecorations,\n highlightedLinesTheme,\n ];\n}\n\nexport function updateHighlighting(view: EditorView, lines?: HighlightedLines) {\n view.dispatch({ effects: setHighlightedLinesEffect.of(lines ?? null) });\n}\n","import { useEffect, useMemo, useRef } from 'react';\nimport type { CompletionSource } from '@codemirror/autocomplete';\nimport { defaultKeymap } from '@codemirror/commands';\nimport type { SQLNamespace } from '@codemirror/lang-sql';\nimport type {\n EditorState,\n KeyBinding,\n ReactCodeMirrorRef,\n ViewUpdate,\n} from '@uiw/react-codemirror';\nimport CodeMirror, { EditorView, keymap } from '@uiw/react-codemirror';\n\nimport { installExtensions, type SqlDialectName } from '@keboola/codemirror-lang-sql';\n\nimport { useStableCallback } from '../../hooks/useStableCallback';\nimport type { ResolvedColorScheme } from '../ThemeProvider/types';\n\nimport { activeQueryHighlight } from './activeQueryHighlight';\nimport { buildCanvasChrome, buildChromeStyles } from './editorChrome';\nimport { buildBaseThemeStyles, buildBasicSetup, buildCustomTheme } from './helpers';\nimport type { HighlightedLines } from './highlighting';\nimport { highlightingExtension, updateHighlighting } from './highlighting';\nimport { searchExtension, updateSearch } from './search';\nimport { useThemedExtension } from './tokenTheme';\nimport { useTooltipsExtension } from './tooltips';\nimport { type EditorVariable, useVariableCompletion } from './variableCompletion';\n\n// Own keymaps: Escape ahead of `defaultKeymap`, a Tab/Enter-filtered completion keymap from\n// `@keboola/codemirror-lang-sql`, and prop-driven search instead of the panel.\nconst OWN_KEYMAPS = {\n defaultKeymap: false,\n completionKeymap: false,\n searchKeymap: false,\n} as const;\n\nconst CUSTOM_KEYMAP: KeyBinding[] = [\n {\n key: 'Escape',\n stopPropagation: true,\n run: (view: EditorView) => {\n view.contentDOM.blur();\n return true;\n },\n },\n];\n\nconst OWN_KEYMAP_EXTENSION = keymap.of([...defaultKeymap, ...CUSTOM_KEYMAP]);\n\n// Resolves live `--*` tokens, so the chrome re-skins with brand + dark mode.\nconst buildTheme = (scheme: ResolvedColorScheme) => ({\n ...buildBaseThemeStyles(scheme),\n ...buildChromeStyles(buildCanvasChrome()),\n});\n\ntype Props = {\n value: string;\n onChange?: (value: string) => void;\n onSelectionChange?: (text: string) => void;\n onCreateEditor?: (view: EditorView, state: EditorState) => void;\n onDestroyEditor?: () => void;\n onBlurEditor?: (view: EditorView) => void;\n onFocusEditor?: (view: EditorView) => void;\n readOnly: boolean;\n placeholder?: string;\n searchQuery?: string;\n searchMatchIndex?: number;\n queryParser?: (sql: string) => Promise<string[]>;\n autoFocus?: boolean;\n lineNumbers?: boolean;\n className?: string;\n /** Compared by identity: pass a stable object or the extension tree rebuilds every render. */\n schema?: SQLNamespace;\n /**\n * Added to schema/keyword/variable completions; unlike `CodeEditor`'s, replaces nothing.\n * Compared by identity: `useMemo` it or the extension tree rebuilds every render.\n */\n completionSource?: CompletionSource;\n variables?: EditorVariable[];\n defaultSchema?: string;\n defaultDatabase?: string;\n highlightLines?: HighlightedLines;\n dialect?: SqlDialectName;\n};\n\nexport const SqlEditor = ({\n value,\n onChange,\n onSelectionChange,\n onCreateEditor,\n onDestroyEditor,\n onBlurEditor,\n onFocusEditor,\n placeholder = '',\n searchQuery = '',\n searchMatchIndex,\n readOnly = false,\n queryParser,\n autoFocus = false,\n lineNumbers = true,\n className,\n schema,\n completionSource,\n variables,\n defaultDatabase,\n defaultSchema,\n highlightLines,\n dialect,\n}: Props) => {\n const editorRef = useRef<ReactCodeMirrorRef>(null);\n\n // Stable identities: CodeMirror rebuilds its extension tree when a handler changes.\n const handleChange = useStableCallback((next: string) => onChange?.(next));\n const parseQueries = useStableCallback(async (sql: string) => (await queryParser?.(sql)) ?? []);\n const handleBlur = useStableCallback((view: EditorView) => onBlurEditor?.(view));\n const handleFocus = useStableCallback((view: EditorView) => onFocusEditor?.(view));\n\n const handleUpdate = useStableCallback((update: ViewUpdate) => {\n if (update.selectionSet) {\n const { main } = update.state.selection;\n\n onSelectionChange?.(update.state.sliceDoc(main.from, main.to));\n }\n });\n\n const variableCompletion = useVariableCompletion(variables);\n\n // Only a parser's presence reconfigures; swapping it does not.\n const withQueryParser = queryParser !== undefined;\n\n const tooltipLayer = useTooltipsExtension();\n\n // Shares `CodeEditor`'s cache entry — same `buildCustomTheme`.\n const customTheme = useThemedExtension('editor:syntax', buildCustomTheme);\n // `dark` sets `EditorView.darkTheme` — see `CodeEditor`'s base theme for what depends on it.\n const themeExtension = useThemedExtension('sql-editor:chrome', (scheme) =>\n EditorView.theme(buildTheme(scheme), { dark: scheme === 'dark' }),\n );\n\n const extensions = useMemo(() => {\n return [\n EditorView.lineWrapping,\n OWN_KEYMAP_EXTENSION,\n ...installExtensions({\n schema,\n defaultDatabase,\n defaultSchema,\n variableCompletion,\n completionSource,\n dialect,\n }),\n searchExtension(),\n highlightingExtension(),\n withQueryParser ? activeQueryHighlight(parseQueries) : [],\n tooltipLayer,\n themeExtension,\n EditorView.domEventHandlers({\n blur: (_, view) => handleBlur(view),\n focus: (_, view) => handleFocus(view),\n }),\n ];\n }, [\n withQueryParser,\n parseQueries,\n schema,\n completionSource,\n defaultDatabase,\n defaultSchema,\n variableCompletion,\n dialect,\n handleBlur,\n handleFocus,\n themeExtension,\n tooltipLayer,\n ]);\n\n const defaultSetup = useMemo(() => {\n return { ...buildBasicSetup(readOnly), ...OWN_KEYMAPS, lineNumbers };\n }, [readOnly, lineNumbers]);\n\n useEffect(() => {\n if (editorRef.current?.view) {\n updateHighlighting(editorRef.current.view, highlightLines);\n }\n }, [highlightLines]);\n\n useEffect(() => {\n if (editorRef.current?.view) {\n updateSearch(editorRef.current.view, searchQuery, searchMatchIndex);\n }\n }, [searchQuery, searchMatchIndex]);\n\n // Fires once on unmount, with the latest handler.\n const handleDestroy = useStableCallback(() => onDestroyEditor?.());\n useEffect(() => handleDestroy, [handleDestroy]);\n\n const handleCreate = useStableCallback((view: EditorView, state: EditorState) => {\n onCreateEditor?.(view, state);\n\n if (highlightLines) {\n updateHighlighting(view, highlightLines);\n }\n\n if (searchQuery) {\n updateSearch(view, searchQuery, searchMatchIndex);\n }\n });\n\n return (\n <CodeMirror\n ref={editorRef}\n theme={customTheme}\n extensions={extensions}\n value={value}\n onChange={handleChange}\n onUpdate={handleUpdate}\n onCreateEditor={handleCreate}\n autoFocus={autoFocus}\n placeholder={placeholder}\n className={className}\n readOnly={readOnly}\n editable={!readOnly}\n basicSetup={defaultSetup}\n />\n );\n};\n","import { ChevronsLeft, ChevronsRight } from '../../../icons';\nimport { Button } from '../../Button';\n\ntype Props = {\n side: 'left' | 'right';\n onAccept: () => void;\n};\n\nexport const AcceptButton = ({ side, onAccept }: Props) => {\n const label = `Accept ${side === 'left' ? 'production' : 'branch'} version`;\n const Chevron = side === 'left' ? ChevronsRight : ChevronsLeft;\n return (\n <Button onClick={onAccept} variant=\"outline\" size=\"icon-sm\" title={label} aria-label={label}>\n <Chevron className=\"tw:size-5\" />\n </Button>\n );\n};\n","import type { RefCallback } from 'react';\n\nimport { X } from '../../../icons';\nimport { cn } from '../../../utils';\nimport { Button } from '../../Button';\nimport { LINE_HEIGHT_PX, type MergeConflict, PANE_HEADER_HEIGHT_PX } from '../constants';\n\nimport { AcceptButton } from './AcceptButton';\n\ntype Props = {\n conflicts: MergeConflict[];\n side: 'left' | 'right';\n onAccept: (conflict: MergeConflict) => void;\n onDismiss?: (conflict: MergeConflict) => void;\n contentRef?: RefCallback<HTMLDivElement>;\n};\n\n// Column of accept/dismiss buttons between panes.\n//\n// - 28px fits the icons without crossing into the `overflow-hidden` panes; 0 when no conflicts.\n// - Inner wrapper allows x-overflow so focus rings stay visible past the column edge.\n// - The parent scroll-syncs `contentRef` to align buttons with their chunk.\nexport const AcceptControls = ({ conflicts, side, onAccept, onDismiss, contentRef }: Props) => (\n <div\n className={cn(\n 'tw:z-10 tw:flex tw:shrink-0 tw:flex-col tw:items-center',\n conflicts.length > 0 ? 'tw:w-7' : 'tw:w-0',\n )}\n >\n {conflicts.length > 0 && (\n <>\n <div className=\"tw:shrink-0\" style={{ height: `${PANE_HEADER_HEIGHT_PX}px` }} />\n <div className=\"tw:relative tw:w-full tw:flex-1 tw:overflow-y-clip tw:overflow-x-visible\">\n <div ref={contentRef} className=\"tw:relative tw:size-full\">\n {conflicts.map((conflict) => {\n const topLine = side === 'left' ? conflict.oldStartLine : conflict.newStartLine;\n const topOffset = (topLine - 1) * LINE_HEIGHT_PX - 2;\n\n return (\n <div\n key={conflict.id}\n className=\"tw:absolute tw:inset-x-0 tw:flex tw:flex-col tw:items-center tw:gap-0.5\"\n style={{ top: `${topOffset}px` }}\n >\n <AcceptButton side={side} onAccept={() => onAccept(conflict)} />\n {onDismiss && (\n <Button\n onClick={() => onDismiss(conflict)}\n variant=\"outline\"\n size=\"icon-sm\"\n title=\"Dismiss\"\n aria-label={`Dismiss ${side === 'left' ? 'production' : 'branch'} change`}\n >\n <X className=\"tw:size-4\" />\n </Button>\n )}\n </div>\n );\n })}\n </div>\n </div>\n </>\n )}\n </div>\n);\n","import {\n type Extension,\n RangeSet,\n RangeSetBuilder,\n StateEffect,\n StateField,\n} from '@codemirror/state';\nimport {\n Decoration,\n type DecorationSet,\n EditorView,\n gutterLineClass,\n GutterMarker,\n} from '@codemirror/view';\n\nimport colors from '@keboola/tailwind-config/colors';\n\nimport { buildDiffWash } from '../CodeEditor/diffWash';\nimport type { ResolvedColorScheme } from '../ThemeProvider/types';\n\nimport type { ChangeClassification } from './constants';\nimport { CHANGE_CLASS } from './constants';\n\ntype HighlightPayload = {\n lines: Set<number>;\n classification: Map<number, ChangeClassification>;\n};\n\nconst setHighlightedLines = StateEffect.define<HighlightPayload>();\n\nconst safeDecoration = Decoration.line({ class: 'cm-merge-safe-line' });\nconst conflictDecoration = Decoration.line({ class: 'cm-merge-conflict-line' });\n\nclass SafeGutterMarker extends GutterMarker {\n override elementClass = 'cm-merge-safe-gutter';\n}\n\nclass ConflictGutterMarker extends GutterMarker {\n override elementClass = 'cm-merge-conflict-gutter';\n}\n\nconst safeGutterMarker = new SafeGutterMarker();\nconst conflictGutterMarker = new ConflictGutterMarker();\n\ntype FieldValue = {\n decorations: DecorationSet;\n gutterMarkers: RangeSet<GutterMarker>;\n};\n\nexport const createDiffHighlightExtension = (): Extension =>\n StateField.define<FieldValue>({\n create: () => ({ decorations: Decoration.none, gutterMarkers: RangeSet.empty }),\n update(value, tr) {\n // Last effect wins\n let payload: HighlightPayload | null = null;\n for (const effect of tr.effects) {\n if (effect.is(setHighlightedLines)) {\n payload = effect.value;\n }\n }\n\n if (!payload) {\n return {\n decorations: value.decorations.map(tr.changes),\n gutterMarkers: value.gutterMarkers.map(tr.changes),\n };\n }\n\n const decoBuilder = new RangeSetBuilder<Decoration>();\n const gutterBuilder = new RangeSetBuilder<GutterMarker>();\n const doc = tr.state.doc;\n const { lines, classification } = payload;\n\n // RangeSetBuilder requires positions in order\n const sorted = [...lines].filter((l) => l >= 1 && l <= doc.lines).sort((a, b) => a - b);\n for (const i of sorted) {\n const cls = classification.get(i);\n if (!cls) continue;\n\n const isSafe = cls === CHANGE_CLASS.SAFE;\n const line = doc.line(i);\n decoBuilder.add(line.from, line.from, isSafe ? safeDecoration : conflictDecoration);\n gutterBuilder.add(line.from, line.from, isSafe ? safeGutterMarker : conflictGutterMarker);\n }\n\n return {\n decorations: decoBuilder.finish(),\n gutterMarkers: gutterBuilder.finish(),\n };\n },\n provide: (f) => [\n EditorView.decorations.from(f, (val) => val.decorations),\n gutterLineClass.from(f, (val: FieldValue) => val.gutterMarkers),\n ],\n });\n\n/**\n * The highlight palette, for `useThemedExtension`; separate from the StateField, which must stay stable across schemes.\n *\n * - `!important` overrides CodeMirror's active-line and selection backgrounds.\n * - SAFE (green): one side changed. CONFLICT (amber, not red — red reads as deletion): both did.\n * - Dark mode tints at low alpha; an opaque pastel would put light text on a light slab.\n */\nexport const buildDiffHighlightTheme = (scheme: ResolvedColorScheme): Extension => {\n const dark = scheme === 'dark';\n const { line, gutter } = buildDiffWash(scheme);\n\n // `muted-foreground` fails AA on the tinted gutter; hue-matched numbers reach ≥6:1\n const safeNumber = dark ? colors.green[300] : colors.green[900];\n const conflictNumber = dark ? colors.warning[300] : colors.warning[800];\n\n return EditorView.theme({\n '.cm-merge-safe-line': {\n backgroundColor: `${line(colors.green[500], colors.green[100])} !important`,\n boxShadow: `inset 3px 0 0 ${colors.green[500]}`,\n },\n '.cm-merge-conflict-line': {\n backgroundColor: `${line(colors.warning[500], colors.warning[100])} !important`,\n boxShadow: `inset 3px 0 0 ${colors.warning[500]}`,\n },\n '.cm-merge-safe-gutter': {\n backgroundColor: `${gutter(colors.green[500], colors.green[200])} !important`,\n color: safeNumber,\n },\n '.cm-merge-conflict-gutter': {\n backgroundColor: `${gutter(colors.warning[500], colors.warning[200])} !important`,\n color: conflictNumber,\n },\n });\n};\n\nexport const updateHighlightedLines = (\n view: EditorView,\n lines: Set<number>,\n classification: Map<number, ChangeClassification>,\n) => {\n view.dispatch({ effects: setHighlightedLines.of({ lines, classification }) });\n};\n","import { type ReactNode, useCallback, useEffect, useMemo, useRef } from 'react';\nimport { type Extension } from '@codemirror/state';\nimport { EditorView } from '@codemirror/view';\nimport CodeMirror, { type ReactCodeMirrorRef } from '@uiw/react-codemirror';\n\nimport colors from '@keboola/tailwind-config/colors';\n\nimport { useStableCallback } from '../../hooks/useStableCallback';\nimport { cn } from '../../utils';\nimport { resolveToken } from '../../utils/themeTokens';\nimport { buildBasicSetup } from '../CodeEditor/helpers';\nimport { useThemedExtension } from '../CodeEditor/tokenTheme';\n\nimport { type ChangeClassification, LINE_HEIGHT_PX } from './constants';\nimport {\n buildDiffHighlightTheme,\n createDiffHighlightExtension,\n updateHighlightedLines,\n} from './decorations';\n\nconst EMPTY_LINES: Set<number> = new Set();\nconst EMPTY_CLASSIFICATION: Map<number, ChangeClassification> = new Map();\n\ntype Props = {\n value: string;\n highlightedLines: Set<number>;\n lineClassification: Map<number, ChangeClassification>;\n readOnly?: boolean;\n onChange?: (value: string) => void;\n header?: ReactNode;\n headerClassName?: string;\n className?: string;\n onScrollDOMReady?: (el: HTMLElement | null) => void;\n /** Side deleted upstream: error-red chrome, the diff still visible. */\n deleted?: boolean;\n};\n\nexport const MergePane = ({\n value,\n highlightedLines,\n lineClassification,\n readOnly = false,\n onChange,\n header,\n headerClassName,\n className,\n onScrollDOMReady,\n deleted = false,\n}: Props) => {\n const editorRef = useRef<ReactCodeMirrorRef>(null);\n const handleChange = useStableCallback((next: string) => onChange?.(next));\n\n // The deleted wash is a fixed error hue, not a token; dark mode uses a low-alpha tint\n // because an opaque light-red slab makes near-white text unreadable.\n const themeExtension = useThemedExtension(`merge-pane:${deleted}`, (scheme) => {\n const dark = scheme === 'dark';\n const deletedSurface = dark ? `${colors.error[500]}1f` : colors.error[100];\n const deletedGutter = dark ? `${colors.error[500]}3d` : colors.error[200];\n // Stock line-number grey fails contrast on the wash.\n const deletedGutterText = dark ? colors.error[300] : colors.error[700];\n\n return EditorView.theme({\n '&.cm-editor': {\n height: '100%',\n backgroundColor: deleted ? deletedSurface : resolveToken('background', '249 250 251'),\n // Token text colour so content stays readable when `--background` goes dark.\n color: resolveToken('foreground', '30 41 59'),\n outline: 'none',\n },\n '.cm-gutters': {\n backgroundColor: deleted ? deletedGutter : resolveToken('muted', '241 245 249'),\n color: deleted ? deletedGutterText : resolveToken('muted-foreground', '100 116 139'),\n },\n '*': { lineHeight: `${LINE_HEIGHT_PX}px` },\n '.cm-lineNumbers .cm-gutterElement': { padding: '0 0 0 25px' },\n });\n });\n\n // Separate from the theme: a fresh `StateField` on reconfigure would drop the highlights.\n const diffHighlightExtension = useMemo(() => createDiffHighlightExtension(), []);\n\n // Must rebuild on scheme change — see `buildDiffHighlightTheme`.\n const diffHighlightTheme = useThemedExtension('merge-diff-highlight', buildDiffHighlightTheme);\n\n const basicSetup = useMemo(\n () => ({ ...buildBasicSetup(readOnly), lineNumbers: true }),\n [readOnly],\n );\n\n const extensions = useMemo(\n (): Extension[] => [\n diffHighlightExtension,\n diffHighlightTheme,\n themeExtension,\n // Re-measure after doc changes so late-settling layouts don't leave trailing lines blank.\n EditorView.updateListener.of((update) => {\n if (update.docChanged) {\n requestAnimationFrame(() => update.view.dispatch({}));\n }\n }),\n ],\n [diffHighlightExtension, diffHighlightTheme, themeExtension],\n );\n\n // A deleted side is a file-level conflict, so it shows no line markers.\n useEffect(() => {\n const view = editorRef.current?.view;\n if (!view) return;\n updateHighlightedLines(\n view,\n deleted ? EMPTY_LINES : highlightedLines,\n deleted ? EMPTY_CLASSIFICATION : lineClassification,\n );\n }, [highlightedLines, lineClassification, deleted]);\n\n const handleCreateEditor = useCallback(\n (view: EditorView) => {\n updateHighlightedLines(\n view,\n deleted ? EMPTY_LINES : highlightedLines,\n deleted ? EMPTY_CLASSIFICATION : lineClassification,\n );\n onScrollDOMReady?.(view.scrollDOM);\n },\n [highlightedLines, lineClassification, onScrollDOMReady, deleted],\n );\n\n useEffect(() => {\n return () => onScrollDOMReady?.(null);\n }, [onScrollDOMReady]);\n\n // Parent dimensions settle after CodeMirror mounts (fullscreen modal, alerts above).\n useEffect(() => {\n const view = editorRef.current?.view;\n const wrapper = view?.dom.parentElement;\n if (!view || !wrapper) return;\n\n const observer = new ResizeObserver(() => view.dispatch({}));\n observer.observe(wrapper);\n\n return () => observer.disconnect();\n }, []);\n\n return (\n <div\n className={cn(\n 'tw:flex tw:min-w-0 tw:flex-1 tw:flex-col tw:overflow-hidden',\n className,\n deleted && 'tw:border-error-300 tw:[[data-theme=dark]_&]:border-error-500/50',\n )}\n >\n {header && (\n <div\n className={cn(\n 'tw:px-4 tw:py-3',\n // Same light/dark split as the editor wash, for the same near-white text.\n deleted\n ? 'tw:border-b tw:border-error-200 tw:bg-error-100 tw:[[data-theme=dark]_&]:border-error-500/30 tw:[[data-theme=dark]_&]:bg-error-500/15'\n : 'tw:bg-background',\n headerClassName,\n )}\n >\n {header}\n </div>\n )}\n <div className=\"tw:flex tw:h-0 tw:flex-1 tw:flex-col tw:[&_>div]:h-full\">\n <CodeMirror\n ref={editorRef}\n height=\"100%\"\n value={value}\n onChange={handleChange}\n extensions={extensions}\n readOnly={readOnly}\n onCreateEditor={handleCreateEditor}\n basicSetup={basicSetup}\n />\n </div>\n </div>\n );\n};\n","import { useCallback, useRef } from 'react';\n\nexport const useScrollSync = () => {\n const scrollersRef = useRef<(HTMLElement | null)[]>([]);\n const followersRef = useRef<Map<string, { paneIndex: number; el: HTMLElement }>>(new Map());\n const isSyncingRef = useRef(false);\n const scrollerCallbacksRef = useRef<Map<number, (el: HTMLElement | null) => void>>(new Map());\n const followerCallbacksRef = useRef<Map<string, (el: HTMLElement | null) => void>>(new Map());\n\n const updateFollowers = useCallback(() => {\n for (const [, { paneIndex, el }] of followersRef.current) {\n const scroller = scrollersRef.current[paneIndex];\n if (!scroller) continue;\n el.style.transform = `translateY(-${scroller.scrollTop}px)`;\n }\n }, []);\n\n const handleScroll = useCallback(\n (e: Event) => {\n if (isSyncingRef.current) return;\n isSyncingRef.current = true;\n\n // Px-aligned sync. All panes share LINE_HEIGHT_PX, so matching scrollTop is the same\n // as matching the line at the top of the viewport. Previous ratio-based sync drifted\n // badly when pane heights differed (e.g. 50 lines vs 500 — half-scrolling left snapped\n // right to its midpoint, not to the equivalent line). Clamp to each target's max so\n // the source can scroll past where shorter panes bottom out without errors.\n const sourceScrollTop = (e.target as HTMLElement).scrollTop;\n\n for (const scroller of scrollersRef.current) {\n if (!scroller || scroller === e.target) continue;\n const targetMax = Math.max(0, scroller.scrollHeight - scroller.clientHeight);\n scroller.scrollTop = Math.min(sourceScrollTop, targetMax);\n }\n\n updateFollowers();\n isSyncingRef.current = false;\n },\n [updateFollowers],\n );\n\n const registerScroller = useCallback(\n (index: number) => {\n const existing = scrollerCallbacksRef.current.get(index);\n if (existing) return existing;\n\n const callback = (el: HTMLElement | null) => {\n const prev = scrollersRef.current[index];\n if (prev === el) return;\n\n prev?.removeEventListener('scroll', handleScroll);\n scrollersRef.current[index] = el;\n el?.addEventListener('scroll', handleScroll, { passive: true });\n };\n\n scrollerCallbacksRef.current.set(index, callback);\n return callback;\n },\n [handleScroll],\n );\n\n const registerFollower = useCallback((paneIndex: number, key: string) => {\n const existing = followerCallbacksRef.current.get(key);\n if (existing) return existing;\n\n const callback = (el: HTMLElement | null) => {\n if (el) {\n followersRef.current.set(key, { paneIndex, el });\n } else {\n followersRef.current.delete(key);\n }\n };\n\n followerCallbacksRef.current.set(key, callback);\n return callback;\n }, []);\n\n return { registerScroller, registerFollower };\n};\n","import type { ReactNode } from 'react';\n\nimport { cn } from '../../utils';\n\nimport { AcceptControls } from './AcceptControls/AcceptControls';\nimport type { ChangeClassification, MergeConflict } from './constants';\nimport { MergePane } from './MergePane';\nimport { useScrollSync } from './useScrollSync';\n\nconst EMPTY_LINES: Set<number> = new Set();\nconst EMPTY_CLASSIFICATION: Map<number, ChangeClassification> = new Map();\n\ntype Props = {\n mergeValue: string;\n handleMergeChange: (value: string) => void;\n leftText: string;\n rightText: string;\n leftConflicts: MergeConflict[];\n rightConflicts: MergeConflict[];\n leftHighlightedLines: Set<number>;\n rightHighlightedLines: Set<number>;\n leftClassification: Map<number, ChangeClassification>;\n rightClassification: Map<number, ChangeClassification>;\n mergeHighlightedLines: Set<number>;\n mergeClassification: Map<number, ChangeClassification>;\n acceptLeft: (conflict: MergeConflict) => void;\n acceptRight: (conflict: MergeConflict) => void;\n dismissLeft: (conflict: MergeConflict) => void;\n dismissRight: (conflict: MergeConflict) => void;\n leftHeader?: ReactNode;\n middleHeader?: ReactNode;\n middleHeaderClassName?: string;\n middleClassName?: string;\n rightHeader?: ReactNode;\n className?: string;\n /** Side deleted upstream: its pane turns error-red, its accept/dismiss controls are hidden. */\n leftDeleted?: boolean;\n rightDeleted?: boolean;\n};\n\nexport const MergeEditor = ({\n mergeValue,\n handleMergeChange,\n leftText,\n rightText,\n leftConflicts,\n rightConflicts,\n leftHighlightedLines,\n rightHighlightedLines,\n leftClassification,\n rightClassification,\n mergeHighlightedLines,\n mergeClassification,\n acceptLeft,\n acceptRight,\n dismissLeft,\n dismissRight,\n leftHeader,\n middleHeader,\n middleHeaderClassName,\n middleClassName,\n rightHeader,\n className,\n leftDeleted = false,\n rightDeleted = false,\n}: Props) => {\n const { registerScroller, registerFollower } = useScrollSync();\n\n // A deleted side makes it a keep-or-drop-the-file choice, so the middle pane shows no line markers.\n const anySideDeleted = leftDeleted || rightDeleted;\n\n return (\n <div\n className={cn(\n 'tw:flex tw:h-full tw:min-h-0 tw:overflow-hidden tw:bg-background tw:gap-3',\n className,\n )}\n >\n <MergePane\n value={leftText}\n highlightedLines={leftHighlightedLines}\n lineClassification={leftClassification}\n readOnly\n header={leftHeader}\n className=\"tw:rounded-r-lg tw:border tw:border-border\"\n onScrollDOMReady={registerScroller(0)}\n deleted={leftDeleted}\n />\n\n <AcceptControls\n conflicts={leftDeleted ? [] : leftConflicts}\n side=\"left\"\n onAccept={acceptLeft}\n onDismiss={dismissLeft}\n contentRef={registerFollower(0, 'left-controls')}\n />\n\n <MergePane\n value={mergeValue}\n highlightedLines={anySideDeleted ? EMPTY_LINES : mergeHighlightedLines}\n lineClassification={anySideDeleted ? EMPTY_CLASSIFICATION : mergeClassification}\n onChange={handleMergeChange}\n header={middleHeader}\n headerClassName={middleHeaderClassName}\n className={cn('tw:border tw:border-border tw:rounded-lg', middleClassName)}\n onScrollDOMReady={registerScroller(1)}\n />\n\n <AcceptControls\n conflicts={rightDeleted ? [] : rightConflicts}\n side=\"right\"\n onAccept={acceptRight}\n onDismiss={dismissRight}\n contentRef={registerFollower(2, 'right-controls')}\n />\n\n <MergePane\n value={rightText}\n highlightedLines={rightHighlightedLines}\n lineClassification={rightClassification}\n readOnly\n header={rightHeader}\n className=\"tw:rounded-l-lg tw:border tw:border-border\"\n onScrollDOMReady={registerScroller(2)}\n deleted={rightDeleted}\n />\n </div>\n );\n};\n","import type { ComponentProps } from 'react';\n\nimport { FieldItem, type FieldItemProps } from '../../blocks/Form/connectors/FieldItem';\nimport { useFieldContext } from '../../blocks/Form/contexts';\n\nimport { CodeEditor } from './CodeEditor';\n\ntype Props = FieldItemProps &\n Omit<\n ComponentProps<typeof CodeEditor>,\n // Owned by the field / `FieldItem` — see the connector rules in AGENTS.md.\n | 'value'\n | 'onChange'\n | 'onBlur'\n | 'ariaLabel'\n | 'ariaLabelledBy'\n | 'ariaDescribedBy'\n | 'ariaInvalid'\n | 'ariaRequired'\n // Makes a viewer (no lint, no completions), not a disabled control; use `inputLocked` instead.\n | 'readOnly'\n | 'className'\n // Derived from `required`, so a call site cannot contradict its own validation.\n | 'allowBlankDocument'\n > & {\n /** Styles the editor box; `className` styles the field frame. */\n editorClassName?: string;\n };\n\n/**\n * Code / JSON editor field; state is the raw editor text, never a parsed value.\n *\n * - In `@keboola/design/editors`, not `useAppForm`: keeps CodeMirror (~170 kB) out of the main entry.\n * - Reads the field from context, so use it inside `<form.AppField>` like a connector.\n * - JSON is invalid mid-edit; parse in the schema or at submit.\n * - Labelled via `aria-labelledby`: CodeMirror's `div[role=\"textbox\"]` is not labelable.\n */\nexport const CodeEditorField = ({\n label,\n action,\n required,\n requiredMark,\n help,\n tooltip,\n className,\n 'aria-describedby': ariaDescribedBy,\n editorClassName,\n variant = 'field',\n ...editorProps\n}: Props) => {\n const field = useFieldContext<string>();\n\n return (\n <FieldItem\n label={label}\n action={action}\n required={required}\n requiredMark={requiredMark}\n labelling=\"aria-labelledby\"\n help={help}\n tooltip={tooltip}\n className={className}\n aria-describedby={ariaDescribedBy}\n >\n {(control) => (\n <CodeEditor\n {...editorProps}\n variant={variant}\n className={editorClassName}\n // Camel-cased: CodeEditor writes them onto CodeMirror's focused content node.\n ariaLabelledBy={control['aria-labelledby']}\n ariaDescribedBy={control['aria-describedby']}\n ariaInvalid={control['aria-invalid']}\n ariaRequired={control['aria-required']}\n // Empty is valid for an optional field, so hide the \"Unexpected end of input\" lint mark.\n allowBlankDocument={!required}\n value={field.state.value ?? ''}\n onChange={field.handleChange}\n onBlur={field.handleBlur}\n />\n )}\n </FieldItem>\n );\n};\n"],"mappings":"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"}
@@ -1,11 +1,8 @@
1
1
  //#region src/utils/findLiteralMatches.ts
2
2
  /**
3
- * Lowercase without changing the length of the string.
3
+ * Lowercase without changing the string's length, so reported offsets index the caller's text.
4
4
  *
5
- * - `İ` (U+0130) is the only code point in Unicode whose `toLowerCase()` is two code units, so it
6
- * folds to `I` first and reads as a plain `i`.
7
- * - Every offset this module reports indexes the caller's own text, which holds only while the
8
- * fold is length-preserving.
5
+ * - `İ` (U+0130) is the only code point lowercasing to two code units, so it folds to `I` first.
9
6
  */
10
7
  const foldCase = (text) => text.replaceAll("İ", "I").toLowerCase();
11
8
  const canContain = (text, query) => !!query && query.length <= text.length;
@@ -18,23 +15,14 @@ const scan = (haystack, needle) => {
18
15
  }
19
16
  return offsets;
20
17
  };
21
- /**
22
- * Whether `text` holds `query`, ignoring case.
23
- *
24
- * - For callers that only need the answer — it stops at the first occurrence and allocates no
25
- * offsets.
26
- */
18
+ /** Whether `text` holds `query`, ignoring case; stops at the first occurrence. */
27
19
  const hasLiteralMatch = (text, query) => canContain(text, query) && foldCase(text).includes(foldCase(query));
28
20
  /**
29
21
  * Start offsets of every occurrence of `query` in `text`, ignoring case unless `isCaseSensitive`.
30
22
  *
31
- * - The scan is literal, so every character of the query stands for itself. A search query is
32
- * pasted code as often as it is a word, and tens of kilobytes of it exceed what V8 can compile
33
- * as a regular expression.
34
- * - Occurrences do not overlap.
35
- * - An empty query matches nothing.
36
- * - CodeMirror's own `SearchCursor` does this over a `Text`, but it would reach the root barrel
37
- * the whole CodeMirror bundle is kept out of, and three of the four callers hold plain strings.
23
+ * - Literal, not a regex: pasted queries of tens of KB exceed what V8 compiles as a regex.
24
+ * - Non-overlapping; an empty query matches nothing.
25
+ * - Not CodeMirror's `SearchCursor`: it would pull CodeMirror into the root barrel.
38
26
  */
39
27
  const findLiteralMatches = (text, query, { isCaseSensitive = false } = {}) => {
40
28
  if (!canContain(text, query)) return [];
@@ -43,7 +31,7 @@ const findLiteralMatches = (text, query, { isCaseSensitive = false } = {}) => {
43
31
  /**
44
32
  * `findLiteralMatches` for many queries over one text, as `{ from, to }` ranges.
45
33
  *
46
- * - Folds `text` once, not once per term — the per-term cost is the scan alone.
34
+ * - Folds `text` once, not once per term.
47
35
  * - Ranges arrive in term order; ranges of different terms may overlap.
48
36
  */
49
37
  const findLiteralTermMatches = (text, terms, { isCaseSensitive = false } = {}) => {
@@ -59,4 +47,4 @@ const findLiteralTermMatches = (text, terms, { isCaseSensitive = false } = {}) =
59
47
  //#endregion
60
48
  export { findLiteralMatches, findLiteralTermMatches, hasLiteralMatch };
61
49
 
62
- //# sourceMappingURL=findLiteralMatches-BGBw8Vbl.js.map
50
+ //# sourceMappingURL=findLiteralMatches-DgGLzyG0.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findLiteralMatches-DgGLzyG0.js","names":[],"sources":["../src/utils/findLiteralMatches.ts"],"sourcesContent":["/**\n * Lowercase without changing the string's length, so reported offsets index the caller's text.\n *\n * - `İ` (U+0130) is the only code point lowercasing to two code units, so it folds to `I` first.\n */\nconst foldCase = (text: string) => text.replaceAll('İ', 'I').toLowerCase();\n\n// Checked before folding, so scanning a pasted document line by line costs nothing per line.\nconst canContain = (text: string, query: string) => !!query && query.length <= text.length;\n\n// Non-overlapping occurrences, verbatim — callers own the case folding.\nconst scan = (haystack: string, needle: string) => {\n const offsets: number[] = [];\n let at = haystack.indexOf(needle);\n\n while (at !== -1) {\n offsets.push(at);\n at = haystack.indexOf(needle, at + needle.length);\n }\n\n return offsets;\n};\n\n/** Whether `text` holds `query`, ignoring case; stops at the first occurrence. */\nexport const hasLiteralMatch = (text: string, query: string) =>\n canContain(text, query) && foldCase(text).includes(foldCase(query));\n\n/**\n * Start offsets of every occurrence of `query` in `text`, ignoring case unless `isCaseSensitive`.\n *\n * - Literal, not a regex: pasted queries of tens of KB exceed what V8 compiles as a regex.\n * - Non-overlapping; an empty query matches nothing.\n * - Not CodeMirror's `SearchCursor`: it would pull CodeMirror into the root barrel.\n */\nexport const findLiteralMatches = (\n text: string,\n query: string,\n { isCaseSensitive = false }: { isCaseSensitive?: boolean } = {},\n) => {\n if (!canContain(text, query)) return [];\n\n return isCaseSensitive ? scan(text, query) : scan(foldCase(text), foldCase(query));\n};\n\n/**\n * `findLiteralMatches` for many queries over one text, as `{ from, to }` ranges.\n *\n * - Folds `text` once, not once per term.\n * - Ranges arrive in term order; ranges of different terms may overlap.\n */\nexport const findLiteralTermMatches = (\n text: string,\n terms: string[],\n { isCaseSensitive = false }: { isCaseSensitive?: boolean } = {},\n) => {\n const haystack = isCaseSensitive ? text : foldCase(text);\n\n return terms.flatMap((term) => {\n if (!canContain(text, term)) return [];\n\n return scan(haystack, isCaseSensitive ? term : foldCase(term)).map((from) => ({\n from,\n to: from + term.length,\n }));\n });\n};\n"],"mappings":";;;;;;AAKA,MAAM,YAAY,SAAiB,KAAK,WAAW,KAAK,GAAG,CAAC,CAAC,YAAY;AAGzE,MAAM,cAAc,MAAc,UAAkB,CAAC,CAAC,SAAS,MAAM,UAAU,KAAK;AAGpF,MAAM,QAAQ,UAAkB,WAAmB;CACjD,MAAM,UAAoB,CAAC;CAC3B,IAAI,KAAK,SAAS,QAAQ,MAAM;CAEhC,OAAO,OAAO,IAAI;EAChB,QAAQ,KAAK,EAAE;EACf,KAAK,SAAS,QAAQ,QAAQ,KAAK,OAAO,MAAM;CAClD;CAEA,OAAO;AACT;;AAGA,MAAa,mBAAmB,MAAc,UAC5C,WAAW,MAAM,KAAK,KAAK,SAAS,IAAI,CAAC,CAAC,SAAS,SAAS,KAAK,CAAC;;;;;;;;AASpE,MAAa,sBACX,MACA,OACA,EAAE,kBAAkB,UAAyC,CAAC,MAC3D;CACH,IAAI,CAAC,WAAW,MAAM,KAAK,GAAG,OAAO,CAAC;CAEtC,OAAO,kBAAkB,KAAK,MAAM,KAAK,IAAI,KAAK,SAAS,IAAI,GAAG,SAAS,KAAK,CAAC;AACnF;;;;;;;AAQA,MAAa,0BACX,MACA,OACA,EAAE,kBAAkB,UAAyC,CAAC,MAC3D;CACH,MAAM,WAAW,kBAAkB,OAAO,SAAS,IAAI;CAEvD,OAAO,MAAM,SAAS,SAAS;EAC7B,IAAI,CAAC,WAAW,MAAM,IAAI,GAAG,OAAO,CAAC;EAErC,OAAO,KAAK,UAAU,kBAAkB,OAAO,SAAS,IAAI,CAAC,CAAC,CAAC,KAAK,UAAU;GAC5E;GACA,IAAI,OAAO,KAAK;EAClB,EAAE;CACJ,CAAC;AACH"}
package/dist/flint.d.ts CHANGED
@@ -1,15 +1,12 @@
1
1
  import { ReactNode } from "react";
2
2
  //#region src/blocks/FlintChart/FlintChart.d.ts
3
3
  type Props = {
4
- /** Raw source of a ```flint fence — a flint-chart `ChartAssemblyInput` as pure JSON
5
- * (`data.values` rows, optional `semantic_types`, and a `chart_spec` naming the
6
- * chartType and encodings). Rendered through the Vega-Lite backend. */
4
+ /** Raw source of a ```flint fence: a flint-chart `ChartAssemblyInput` as JSON. */
7
5
  code: string;
8
- /** Extra toolbar controls (e.g. an `ExportMenu`), forwarded to `ZoomableSvg`.
9
- * Receives the sanitized SVG. Omit to keep the default toolbar. */
6
+ /** Extra toolbar controls (e.g. `ExportMenu`) for `ZoomableSvg`; receives the sanitized SVG. */
10
7
  renderActions?: (safeSvg: string) => ReactNode;
11
8
  };
12
- declare const FlintChart: ({ code, renderActions }: Props) => import("react/jsx-runtime").JSX.Element;
9
+ declare const FlintChart: ({ code, renderActions }: Props) => import("react").JSX.Element;
13
10
  //#endregion
14
11
  export { FlintChart };
15
12
  //# sourceMappingURL=flint.d.ts.map
package/dist/flint.js CHANGED
@@ -1,22 +1,18 @@
1
1
  import { DiagramSkeleton } from "./internal/blocks/DiagramShared/DiagramSkeleton.js";
2
2
  import { DiagramSource } from "./DiagramSource-C5_d8Gdj.js";
3
- import { ZoomableSvg } from "./ZoomableSvg-BZlk3PFJ.js";
4
- import { useThemeAttributes } from "./themeTokens-qP1-55O7.js";
5
- import { SERIES_TOKENS, createVegaColorResolver, vegaRgb, vegaTheme } from "./vegaTheme-CSvFEMBl.js";
6
- import { makeResponsive } from "./svgTransforms-CJAYPk7o.js";
3
+ import { useThemeAttributes } from "./useThemeAttributes-B199Regy.js";
4
+ import { ZoomableSvg } from "./ZoomableSvg-BzXVfgnI.js";
5
+ import { SERIES_TOKENS, createVegaColorResolver, vegaRgb, vegaTheme } from "./vegaTheme-VQMWTlBw.js";
6
+ import { makeResponsive } from "./svgTransforms-Dw2hRA-i.js";
7
7
  import { useEffect, useState } from "react";
8
8
  import { jsx } from "react/jsx-runtime";
9
9
  //#region src/blocks/FlintChart/flintTheme.ts
10
10
  /**
11
11
  * How the second palette tier (series 6-10) is derived from the core hues.
12
12
  *
13
- * - Each core hue is pushed *away* from its own surface, so the tier stays
14
- * in-family but reads as a distinct series: darkened on light, lightened on
15
- * dark. A single "multiply the channels" rule would darken in both, which on
16
- * a dark surface moves toward the backdrop — measured, that drops contrast
17
- * from ~6.7:1 to ~3.7:1, while lightening raises it to ~9:1.
18
- * - `amount` is the fraction of the distance travelled toward black (light) or
19
- * white (dark).
13
+ * - Each hue moves *away* from its surface: darker on light, lighter on dark.
14
+ * Darkening on dark would drop contrast from ~6.7:1 to ~3.7:1.
15
+ * - `amount` is the fraction of the distance travelled toward `towards`.
20
16
  */
21
17
  const TIER_TWO_SHIFT = {
22
18
  light: {
@@ -39,25 +35,11 @@ const shiftChannels = (colour, towards, amount) => {
39
35
  return vegaRgb(shifted.join(" "));
40
36
  };
41
37
  /**
42
- * Put the design system's categorical chart palette on a Flint input.
38
+ * Put the design system's `--chart-1..5` palette on a Flint input.
43
39
  *
44
- * Series colours are resolved by flint itself during assembly (they land in the
45
- * Vega-Lite `color` scale's `range`), so unlike the chrome in `vegaTheme` they
46
- * cannot be set through the compile `config` — they ride `theme_spec.ink.series`
47
- * on the input instead.
48
- *
49
- * `--chart-1..5` is the decorative palette the recharts wrappers in
50
- * `@keboola/design/charts` already use, so a Flint chart now matches the rest of
51
- * the platform's data-viz, follows the colour scheme, and re-skins with a brand
52
- * that overrides `colors.chart` / `colorsDark.chart`.
53
- *
54
- * An author-supplied `theme_spec` wins: a fence that names a Flint house (or its
55
- * own spec) asked for that look deliberately, so it is passed through untouched.
56
- *
57
- * `null` is not such a spec. The input is `JSON.parse`d model output, where
58
- * `"theme_spec": null` is valid JSON, and flint dereferences the value while
59
- * assembling (`reading 'extends'`) — so leaving it in place throws and the chart
60
- * falls back to raw source. It is replaced, exactly like a missing one.
40
+ * - Flint resolves series colours during assembly, so they ride `theme_spec`, not `vegaTheme`.
41
+ * - An author-supplied `theme_spec` is passed through untouched.
42
+ * - A `null` `theme_spec` is replaced like a missing one — flint throws on it.
61
43
  */
62
44
  const withFlintSeriesPalette = (input, scheme, from) => {
63
45
  if (input.theme_spec !== void 0 && input.theme_spec !== null) return input;
@@ -95,13 +77,10 @@ const withFlintSeriesPalette = (input, scheme, from) => {
95
77
  //#endregion
96
78
  //#region src/blocks/FlintChart/refuseUnsafeSpec.ts
97
79
  /**
98
- * The boundary in front of the flint renderer: a spec is data, and nothing in it may fetch or run.
80
+ * The boundary in front of the flint renderer: nothing in a spec may fetch or run.
99
81
  *
100
- * - Checked on the **input**, before `assembleVegaLite` compiles it. The compiled Vega spec
101
- * carries `signals` and `expr` of vega's own making, so after compilation the model's keys and
102
- * the compiler's are no longer tellable apart.
103
- * - Refused on the **presence of a key**, never on a value looking remote: a relative `data.url`
104
- * is still a fetch nobody authorised, made by the reader's browser to a host the model chose.
82
+ * - Checked on the input, before `assembleVegaLite`: compiled Vega carries its own `signals`/`expr`.
83
+ * - Refused on a key's presence, not its value: a relative `data.url` is still a fetch.
105
84
  */
106
85
  /** Keys whose presence anywhere in a spec is refused: each fetches, evaluates or binds a signal. */
107
86
  const REFUSED_KEYS = /* @__PURE__ */ new Set([
@@ -111,67 +90,45 @@ const REFUSED_KEYS = /* @__PURE__ */ new Set([
111
90
  "signals",
112
91
  "params"
113
92
  ]);
114
- /**
115
- * Suffix of Vega-Lite's other expression properties — `labelExpr`, `tickCountExpr`, …
116
- *
117
- * Matched by shape rather than enumerated, so a property added upstream is refused by default.
118
- */
93
+ /** Suffix of Vega-Lite's other expression properties; matched by shape so new ones are refused. */
119
94
  const EXPRESSION_KEY_SUFFIX = /Expr$/;
120
95
  /**
121
96
  * An own property's value, or `undefined`.
122
97
  *
123
- * - A descriptor, so a prototype's key is not read: an inherited key is not one the model wrote.
124
- * - And so a getter is not *called* — the payload is model-written, and reading it must not run it.
98
+ * - Via a descriptor: inherited keys are ignored and a getter is never called.
125
99
  */
126
100
  const ownValue = (node, key) => {
127
101
  if (typeof node !== "object" || node === null) return void 0;
128
102
  return Object.getOwnPropertyDescriptor(node, key)?.value;
129
103
  };
130
104
  /**
131
- * The chart's own rows, which the walk does not descend into — `spec.data.values`, and nowhere else.
105
+ * The chart's rows (`spec.data.values` only), which the walk does not descend into.
132
106
  *
133
- * - Rows are data, never spec: vega reads a row's fields as datum values and evaluates none of
134
- * them, so a column named `url` is a column. Without this the guard refuses a chart of URLs.
135
- * - Flint takes exactly one dataset, at the top level (`ChartAssemblyInput['data']`), and an
136
- * array is what a dataset is (`data: { values: any[] }`). So this is the whole of a payload
137
- * that is data, and every other node is spec.
138
- * - The walk then skips it by **identity**. Skipping on the key name would leave `values` the
139
- * one word in the payload that hides a refused key, at any depth and under any parent —
140
- * including a nested object merely named `data`.
107
+ * - Rows are data vega never evaluates, so a column named `url` must not be refused.
108
+ * - Flint takes one top-level dataset, so every other node is spec.
109
+ * - Skipped by identity, not key name: a `values` key elsewhere must not hide a refused key.
141
110
  */
142
111
  const inlineRows = (spec) => {
143
112
  const values = ownValue(ownValue(spec, "data"), "values");
144
113
  return Array.isArray(values) ? values : null;
145
114
  };
146
115
  /**
147
- * The top-level maps flint keys by **column name**, whose keys are therefore the model's data.
116
+ * The top-level maps flint keys by column name, so their keys are data, not spec.
148
117
  *
149
- * - `semantic_types` and `field_display_names` are `Record<columnName, …>`
150
- * (`ChartAssemblyInput`), so a column named `url` puts `url` in a key position. Checking those
151
- * keys refuses "Top landing pages by sessions" — a chart with nothing remote in it.
152
- * - Only the keys are the column's. Each value is spec, and is walked like any other node.
153
- * - Matched by identity, for the reason `inlineRows` is: a nested object merely *named*
154
- * `semantic_types` is one flint never reads, so it buys no exemption.
118
+ * - Only the keys are exempt; each value is walked like any other node.
119
+ * - Matched by identity, as in `inlineRows`: a nested object with the same name gets no exemption.
155
120
  */
156
121
  const COLUMN_KEYED_MAPS = ["semantic_types", "field_display_names"];
157
122
  const columnKeyedMaps = (spec) => COLUMN_KEYED_MAPS.map((key) => ownValue(spec, key)).filter((map) => typeof map === "object" && map !== null);
158
- /**
159
- * Ceiling on the nodes the walk will visit.
160
- *
161
- * The payload is model-written, so its shape is not ours to assume. Exceeding this refuses rather
162
- * than passes: a spec the guard could not finish checking has not been checked.
163
- */
123
+ /** Ceiling on the nodes the walk visits; exceeding it refuses, since the spec went unchecked. */
164
124
  const MAX_SPEC_NODES = 1e4;
165
125
  const TOO_LARGE = "is too large to check";
166
126
  /**
167
127
  * Why this spec may not be rendered, or `null` if it may.
168
128
  *
169
- * - The reason reaches a console line, not a reader — the refusal itself surfaces as the source
170
- * fence `DiagramSource` already draws for a chart that would not render.
171
- * - Iterative, not recursive: a deeply nested payload must not land as a stack overflow.
172
- * - Nodes are charged on entering the stack, so the ceiling bounds the work, not the result.
173
- * - An accessor property is refused, not skipped: JSON carries none, so it is not a spec the
174
- * model wrote, and its value cannot be read without running it — leaving that subtree unchecked.
129
+ * - The reason goes to the console; the reader sees `DiagramSource`'s fallback.
130
+ * - Iterative, so deep nesting cannot overflow the stack; nodes are charged on push.
131
+ * - An accessor is refused, not skipped: its value cannot be read without running it.
175
132
  */
176
133
  const findUnsafeSpecKey = (spec) => {
177
134
  const rows = inlineRows(spec);
@@ -205,16 +162,10 @@ const findUnsafeSpecKey = (spec) => {
205
162
  /**
206
163
  * Compile a Flint `ChartAssemblyInput` to a static SVG string.
207
164
  *
208
- * - The input is refused before anything compiles it if it carries a key that would fetch or
209
- * evaluate — see `refuseUnsafeSpec.ts`. Every path to the renderer goes through here, so this
210
- * is the one place that check has to be.
211
- * - `assembleVegaLite` returns a Vega-Lite spec carrying `_`-prefixed internal
212
- * hint keys (`_width`, `_options`, `_transform`, …) that are not part of the
213
- * Vega-Lite schema — they are stripped before compiling.
214
- * - `config` (the scheme-following theme) rides the compile options; flint's
215
- * spec-side config sets no colours, so the two cannot conflict on merge.
216
- * - The Vega view renders headless (`renderer: 'none'`); nothing mounts to the
217
- * DOM, `toSVG()` is the only output.
165
+ * - The one place unsafe input is refused (`refuseUnsafeSpec.ts`), before anything compiles it.
166
+ * - Flint's `_`-prefixed hint keys are not Vega-Lite schema and are stripped before compiling.
167
+ * - `config` rides the compile options; flint's spec-side config sets no colours to conflict.
168
+ * - Renders headless; `toSVG()` is the only output.
218
169
  */
219
170
  const renderFlintSvg = async (runtime, input, config) => {
220
171
  const unsafe = findUnsafeSpecKey(input);