@marimo-team/frontend 0.16.2 → 0.16.3

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 (293) hide show
  1. package/dist/assets/{ConnectedDataExplorerComponent-B5cPvWoQ.js → ConnectedDataExplorerComponent-Brtw1DxF.js} +1 -1
  2. package/dist/assets/{ImageComparisonComponent-CqR26LSv.js → ImageComparisonComponent-Dxl-PbZX.js} +1 -1
  3. package/dist/assets/{VegaLite-DvQDATwI.js → VegaLite-BXQF0Cx_.js} +1 -1
  4. package/dist/assets/_baseEach-BjSm9ht3.js +1 -0
  5. package/dist/assets/_baseMap-CV4Ezmtf.js +1 -0
  6. package/dist/assets/_baseUniq-Ci9yZGxz.js +1 -0
  7. package/dist/assets/{_createAggregator-ZcHkHPNJ.js → _createAggregator-VFK9K2d9.js} +1 -1
  8. package/dist/assets/{agent-panel-B91RoLct.js → agent-panel-BoscVLCT.js} +7 -7
  9. package/dist/assets/{any-language-editor-CxfHcm5h.js → any-language-editor-ChaY_VUU.js} +1 -1
  10. package/dist/assets/{architectureDiagram-W76B3OCA-BQsvK8uR.js → architectureDiagram-W76B3OCA-CueUUFYd.js} +1 -1
  11. package/dist/assets/{between-horizontal-start-BmYToIaM.js → between-horizontal-start-DAHqmLYT.js} +1 -1
  12. package/dist/assets/{blockDiagram-QIGZ2CNN-r3HgCj4w.js → blockDiagram-QIGZ2CNN-BYYygyWn.js} +1 -1
  13. package/dist/assets/{c4Diagram-FPNF74CW-BJbPNt41.js → c4Diagram-FPNF74CW-DAz3xEh1.js} +1 -1
  14. package/dist/assets/channel-6SqQ2U_X.js +1 -0
  15. package/dist/assets/chat-panel-DJkOLrw9.js +3 -0
  16. package/dist/assets/{chunk-4BX2VUAB-Dv4MZ9Hj.js → chunk-4BX2VUAB-8g-RyHdt.js} +1 -1
  17. package/dist/assets/{chunk-55IACEB6-CM4AHquB.js → chunk-55IACEB6-iWZZ8Mt6.js} +1 -1
  18. package/dist/assets/{chunk-FMBD7UC4-C_Zz0ENB.js → chunk-FMBD7UC4-knjss4wk.js} +1 -1
  19. package/dist/assets/{chunk-K7UQS3LO-DYSmiXYq.js → chunk-K7UQS3LO-DVIwPBgZ.js} +1 -1
  20. package/dist/assets/{chunk-QN33PNHL-QM4OPuQP.js → chunk-QN33PNHL-CBU8pN6I.js} +1 -1
  21. package/dist/assets/{chunk-QZHKN3VN-CfAsGyeB.js → chunk-QZHKN3VN-5ljElUF4.js} +1 -1
  22. package/dist/assets/{chunk-TVAH2DTR-6j_Cpjsi.js → chunk-TVAH2DTR-DkIdGINc.js} +1 -1
  23. package/dist/assets/{chunk-TZMSLE5B-BHslFJQE.js → chunk-TZMSLE5B-CIFOSTqh.js} +1 -1
  24. package/dist/assets/{circle-play-CK3UZRYQ.js → circle-play-BOdsbq5u.js} +1 -1
  25. package/dist/assets/classDiagram-KNZD7YFC-DVqXcTYf.js +1 -0
  26. package/dist/assets/classDiagram-v2-RKCZMP56-DVqXcTYf.js +1 -0
  27. package/dist/assets/{clear-button-C4fDVSv8.js → clear-button-GAjXl0CQ.js} +1 -1
  28. package/dist/assets/clone-DSDb0xen.js +1 -0
  29. package/dist/assets/command-palette-BUXkqoLh.js +1 -0
  30. package/dist/assets/{common-D-lbuUwz.js → common-DahoYqdi.js} +1 -1
  31. package/dist/assets/{compile-DVQe1Mzk.js → compile-Bg8uJ7vm.js} +1 -1
  32. package/dist/assets/{cose-bilkent-S5V4N54A-D-IS7WC8.js → cose-bilkent-S5V4N54A-z_0gqD9K.js} +1 -1
  33. package/dist/assets/{dagre-5GWH7T2D-lYu-tEWT.js → dagre-5GWH7T2D-BMt7CNXL.js} +1 -1
  34. package/dist/assets/{data-grid-overlay-editor-C5peOCit.js → data-grid-overlay-editor-Ctn4XtXx.js} +1 -1
  35. package/dist/assets/{datasources-panel-D3NA20uZ.js → datasources-panel-C7sqRIHs.js} +1 -1
  36. package/dist/assets/{dependency-graph-panel-BGVYOfkV.js → dependency-graph-panel-DNajptzv.js} +4 -4
  37. package/dist/assets/{diagram-N5W7TBWH-BnvIuYUp.js → diagram-N5W7TBWH-BzwvLvAy.js} +1 -1
  38. package/dist/assets/{diagram-QEK2KX5R-DemedRK3.js → diagram-QEK2KX5R-DRLJ56FS.js} +1 -1
  39. package/dist/assets/{diagram-S2PKOQOG-iiY7AuyH.js → diagram-S2PKOQOG-Bf8x4KTU.js} +1 -1
  40. package/dist/assets/{documentation-panel-C3dSwOSQ.js → documentation-panel-Dm6Ozl67.js} +1 -1
  41. package/dist/assets/edit-page-CGc9EjuG.js +140 -0
  42. package/dist/assets/{ellipsis-vertical-CazJl8M7.js → ellipsis-vertical-Bj1YXvZe.js} +1 -1
  43. package/dist/assets/{empty-state-DW308mFO.js → empty-state-CYev-D31.js} +1 -1
  44. package/dist/assets/{erDiagram-AWTI2OKA-6wQ8Ugg0.js → erDiagram-AWTI2OKA-DmgzgN_I.js} +1 -1
  45. package/dist/assets/{error-panel-D1VnJ1yP.js → error-panel-BYG4twCa.js} +1 -1
  46. package/dist/assets/{file-explorer-panel-0oVd4t-D.js → file-explorer-panel-BSMiOApi.js} +1 -1
  47. package/dist/assets/{flowDiagram-PVAE7QVJ-C55IUWjm.js → flowDiagram-PVAE7QVJ-BdRKkajr.js} +1 -1
  48. package/dist/assets/{ganttDiagram-OWAHRB6G-DmqCM6ME.js → ganttDiagram-OWAHRB6G-lfRAMnq_.js} +5 -5
  49. package/dist/assets/{gitGraphDiagram-NY62KEGX-DBvhAeM_.js → gitGraphDiagram-NY62KEGX-CQVTIrHF.js} +1 -1
  50. package/dist/assets/{glide-data-editor-CHNuHidQ.js → glide-data-editor-D5A4pou7.js} +11 -11
  51. package/dist/assets/{graph-CG6BgUWQ.js → graph-CBNo279v.js} +1 -1
  52. package/dist/assets/{home-page-dgivXuSR.js → home-page-CmdznBJR.js} +3 -3
  53. package/dist/assets/{index-BTGpssVX.js → index-0dfGh-Gj.js} +1 -1
  54. package/dist/assets/{index-C7dtgr9A.js → index-BDYVSSzB.js} +1 -1
  55. package/dist/assets/{index-C02SqeRj.js → index-B_KyDZ94.js} +1 -1
  56. package/dist/assets/{index-mkubqy9-.js → index-Bfy-I_lW.js} +1 -1
  57. package/dist/assets/{index-BelfnXwL.js → index-Bh98Tp-z.js} +1 -1
  58. package/dist/assets/{index-CAQvMTzM.js → index-BhroIwBL.js} +1 -1
  59. package/dist/assets/{index-BneyUujp.js → index-BtQtesaI.js} +1 -1
  60. package/dist/assets/index-C0iXCvyY.css +1 -0
  61. package/dist/assets/index-C1SHFMCp.js +581 -0
  62. package/dist/assets/{index-Csd6QrCV.js → index-C6DWtSls.js} +1 -1
  63. package/dist/assets/{index-BYVZlBF8.js → index-C71cdkH-.js} +1 -1
  64. package/dist/assets/{index-z4krxQ4j.js → index-CT_FTqvK.js} +1 -1
  65. package/dist/assets/{index-Db36XTG_.js → index-CU5rRr66.js} +1 -1
  66. package/dist/assets/{index-CtPksxf0.js → index-Cb6duXQm.js} +1 -1
  67. package/dist/assets/{index-DAZ-9ri2.js → index-D23e9zQj.js} +1 -1
  68. package/dist/assets/index-DUGecC2Z.js +68 -0
  69. package/dist/assets/{index-M_pBKDSe.js → index-DcGIOAQi.js} +1 -1
  70. package/dist/assets/{index-DONRrmA2.js → index-PJfa9qXY.js} +1 -1
  71. package/dist/assets/{index-sbO9UaUU.js → index-SPslPC2B.js} +1 -1
  72. package/dist/assets/{index-DdIhdEVw.js → index-VPQlo4Uz.js} +1 -1
  73. package/dist/assets/{index-_luCZMLM.js → index-qbTLKWyG.js} +1 -1
  74. package/dist/assets/infoDiagram-STP46IZ2-DBu8p9gd.js +2 -0
  75. package/dist/assets/{isEmpty-CqX_YTIf.js → isEmpty-CnOLuQIv.js} +1 -1
  76. package/dist/assets/{journeyDiagram-BIP6EPQ6-Y5w_Tqe_.js → journeyDiagram-BIP6EPQ6-6U_vHJBH.js} +1 -1
  77. package/dist/assets/{kanban-definition-6OIFK2YF-DbXs5Rxi.js → kanban-definition-6OIFK2YF-DgnR14ys.js} +1 -1
  78. package/dist/assets/{layout-BCNPDACj.js → layout-RHmq4fP9.js} +1 -1
  79. package/dist/assets/{linear-uO6UVhXt.js → linear-CLdOVPGV.js} +1 -1
  80. package/dist/assets/links-Dd1icsEk.js +7 -0
  81. package/dist/assets/{logs-panel-BEQ1eRUp.js → logs-panel-CjbuhBLx.js} +1 -1
  82. package/dist/assets/{markdown-renderer-Dmzbb00W.js → markdown-renderer-X5YJvAZq.js} +3 -3
  83. package/dist/assets/{mermaid-qRc4MXIj.js → mermaid-Bl2T5oEC.js} +1 -1
  84. package/dist/assets/{mermaid.core-CvvJtCRj.js → mermaid.core-CfukBvGI.js} +4 -4
  85. package/dist/assets/min-BXIes1Za.js +1 -0
  86. package/dist/assets/{mindmap-definition-Q6HEUPPD-G5NognM-.js → mindmap-definition-Q6HEUPPD-BXCjP4Lu.js} +1 -1
  87. package/dist/assets/{number-overlay-editor-DPr5sHFu.js → number-overlay-editor-BUyqkSes.js} +1 -1
  88. package/dist/assets/{outline-panel-gxQXvVi4.js → outline-panel-BvGcPKdd.js} +1 -1
  89. package/dist/assets/{packages-panel-B1T0VPlg.js → packages-panel-BichDQWG.js} +1 -1
  90. package/dist/assets/{pieDiagram-ADFJNKIX-DK9SHkfc.js → pieDiagram-ADFJNKIX-CMzJFIJM.js} +1 -1
  91. package/dist/assets/{quadrantDiagram-LMRXKWRM-D1DdWF8C.js → quadrantDiagram-LMRXKWRM-CfGssUlO.js} +1 -1
  92. package/dist/assets/{react-plotly-CTwajqCb.js → react-plotly-DR3hV0HW.js} +1 -1
  93. package/dist/assets/{requirementDiagram-4UW4RH46-DnjDAypr.js → requirementDiagram-4UW4RH46-CfrFolth.js} +1 -1
  94. package/dist/assets/{run-page-CQY9im22.js → run-page-Bqd_4ePD.js} +1 -1
  95. package/dist/assets/{sankeyDiagram-GR3RE2ED-B67Va-ER.js → sankeyDiagram-GR3RE2ED-D_UttKU0.js} +1 -1
  96. package/dist/assets/scratchpad-panel-D5N15ji1.js +1 -0
  97. package/dist/assets/secrets-panel-BpbnAO4R.js +1 -0
  98. package/dist/assets/{sequenceDiagram-C3RYC4MD-DiWgZPtN.js → sequenceDiagram-C3RYC4MD-MdfQQApP.js} +1 -1
  99. package/dist/assets/{slides-component-DhpPRtQp.js → slides-component-C0z7rXmk.js} +1 -1
  100. package/dist/assets/{snippets-panel-CLkBXhJ2.js → snippets-panel-wlpZ_Wzx.js} +1 -1
  101. package/dist/assets/{sortBy-D4OG7w4O.js → sortBy-BW_zNHP6.js} +1 -1
  102. package/dist/assets/{state-Dz_3JyED.js → state-CDooX-dk.js} +1 -1
  103. package/dist/assets/{stateDiagram-KXAO66HF-ByF2AULw.js → stateDiagram-KXAO66HF-H7kfw3ot.js} +1 -1
  104. package/dist/assets/stateDiagram-v2-UMBNRL4Z-YMeb9qMR.js +1 -0
  105. package/dist/assets/{storage-Dr0CC44z.js → storage-b1QCapTq.js} +6 -6
  106. package/dist/assets/{terminal-BtdissBf.js → terminal-CPV44BXz.js} +1 -1
  107. package/dist/assets/{time-DKdOTnQg.js → time-DDy3xv5Y.js} +1 -1
  108. package/dist/assets/{timeline-definition-XQNQX7LJ-DzER9bf6.js → timeline-definition-XQNQX7LJ-J-cPRT2_.js} +1 -1
  109. package/dist/assets/{tracing-Dpx5M-u3.js → tracing-3eHHRUiJ.js} +2 -2
  110. package/dist/assets/{tracing-panel-hCjBkSER.js → tracing-panel-BMgy3D7d.js} +2 -2
  111. package/dist/assets/{trash-C6Ko-g5q.js → trash--tonOuDe.js} +1 -1
  112. package/dist/assets/{tree-BHN2gcCF.js → tree-ouIGEsVg.js} +6 -6
  113. package/dist/assets/{treemap-75Q7IDZK-DR79Mhzt.js → treemap-75Q7IDZK-CzJTJ_3R.js} +20 -20
  114. package/dist/assets/{variable-panel-PFBCFz36.js → variable-panel-sFTn4Oih.js} +1 -1
  115. package/dist/assets/{vega-component-Db6-uY4C.js → vega-component-BkPkzX9r.js} +1 -1
  116. package/dist/assets/{xychartDiagram-6GGTOJPD-DWzBP3tZ.js → xychartDiagram-6GGTOJPD-BZ8WOb_8.js} +1 -1
  117. package/dist/index.html +10 -3
  118. package/package.json +6 -6
  119. package/src/__mocks__/common.ts +5 -3
  120. package/src/__mocks__/notebook.ts +2 -2
  121. package/src/__tests__/main.test.tsx +2 -2
  122. package/src/components/ai/ai-provider-icon.tsx +2 -0
  123. package/src/components/app-config/ai-config.tsx +32 -1
  124. package/src/components/app-config/common.tsx +2 -2
  125. package/src/components/app-config/user-config-form.tsx +26 -0
  126. package/src/components/audio/audio-recorder.tsx +0 -1
  127. package/src/components/chat/acp/blocks.tsx +2 -2
  128. package/src/components/chat/acp/thread.tsx +3 -5
  129. package/src/components/chat/acp/utils.ts +5 -5
  130. package/src/components/chat/chat-panel.tsx +1 -1
  131. package/src/components/data-table/__tests__/data-table.test.tsx +2 -2
  132. package/src/components/data-table/charts/__tests__/altair-generator.test.ts +1 -1
  133. package/src/components/data-table/charts/chart-spec/tooltips.ts +3 -3
  134. package/src/components/data-table/charts/components/chart-items.tsx +1 -1
  135. package/src/components/data-table/charts/components/form-fields.tsx +2 -2
  136. package/src/components/data-table/charts/constants.ts +1 -1
  137. package/src/components/data-table/column-explorer-panel/column-explorer.tsx +1 -1
  138. package/src/components/data-table/column-summary/chart-spec-model.tsx +2 -2
  139. package/src/components/data-table/columns.tsx +1 -1
  140. package/src/components/data-table/data-table.tsx +35 -3
  141. package/src/components/data-table/date-popover.tsx +1 -1
  142. package/src/components/data-table/download-actions.tsx +1 -1
  143. package/src/components/data-table/range-focus/__tests__/utils.test.ts +5 -5
  144. package/src/components/data-table/renderers.tsx +6 -5
  145. package/src/components/data-table/row-viewer-panel/row-viewer.tsx +1 -1
  146. package/src/components/data-table/types.ts +4 -3
  147. package/src/components/datasources/column-preview.tsx +9 -6
  148. package/src/components/debugger/debugger-code.tsx +1 -1
  149. package/src/components/dependency-graph/custom-node.tsx +15 -6
  150. package/src/components/dependency-graph/dependency-graph-minimap.tsx +2 -2
  151. package/src/components/dependency-graph/dependency-graph-tree.tsx +2 -2
  152. package/src/components/dependency-graph/dependency-graph.tsx +1 -1
  153. package/src/components/dependency-graph/elements.ts +7 -7
  154. package/src/components/dependency-graph/utils/changes.ts +4 -4
  155. package/src/components/editor/Cell.tsx +6 -2
  156. package/src/components/editor/ai/transport/chat-transport.tsx +1 -1
  157. package/src/components/editor/chrome/panels/outline/useActiveOutline.tsx +1 -1
  158. package/src/components/editor/chrome/panels/packages-panel.tsx +1 -1
  159. package/src/components/editor/columns/storage.ts +1 -1
  160. package/src/components/editor/database/__tests__/__snapshots__/as-code.test.ts.snap +36 -0
  161. package/src/components/editor/database/__tests__/as-code.test.ts +30 -7
  162. package/src/components/editor/database/add-database-form.tsx +11 -0
  163. package/src/components/editor/database/as-code.ts +104 -5
  164. package/src/components/editor/database/schemas.ts +36 -18
  165. package/src/components/editor/errors/auto-fix.tsx +12 -2
  166. package/src/components/editor/errors/sql-validation-errors.tsx +12 -6
  167. package/src/components/editor/navigation/clipboard.ts +2 -2
  168. package/src/components/editor/output/ConsoleOutput.tsx +1 -1
  169. package/src/components/editor/output/JsonOutput.tsx +1 -1
  170. package/src/components/editor/output/MarimoErrorOutput.tsx +25 -25
  171. package/src/components/editor/output/MarimoTracebackOutput.tsx +17 -2
  172. package/src/components/editor/renderers/grid-layout/types.ts +2 -2
  173. package/src/components/editor/renderers/plugins.ts +1 -1
  174. package/src/components/editor/renderers/types.ts +1 -1
  175. package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +7 -7
  176. package/src/components/forms/form.tsx +5 -5
  177. package/src/components/ui/links.tsx +1 -0
  178. package/src/core/ai/__tests__/model-registry.test.ts +0 -10
  179. package/src/core/ai/context/providers/error.ts +2 -2
  180. package/src/core/ai/ids/ids.ts +1 -0
  181. package/src/core/ai/model-registry.ts +2 -1
  182. package/src/core/cells/cells.ts +5 -5
  183. package/src/core/cells/logs.ts +1 -1
  184. package/src/core/cells/types.ts +1 -1
  185. package/src/core/codemirror/__tests__/format.test.ts +6 -0
  186. package/src/core/codemirror/cells/traceback-decorations.ts +1 -1
  187. package/src/core/codemirror/editing/commands.ts +2 -2
  188. package/src/core/codemirror/find-replace/navigate.ts +1 -1
  189. package/src/core/codemirror/language/__tests__/extension.test.ts +1 -1
  190. package/src/core/codemirror/language/__tests__/sql-validation.test.ts +1 -1
  191. package/src/core/codemirror/language/__tests__/sql.test.ts +764 -79
  192. package/src/core/codemirror/language/languages/markdown.ts +4 -1
  193. package/src/core/codemirror/language/languages/sql/{validation-errors.ts → banner-validation-errors.ts} +9 -3
  194. package/src/core/codemirror/language/languages/sql/completion-builder.ts +160 -0
  195. package/src/core/codemirror/language/languages/sql/completion-sources.tsx +9 -3
  196. package/src/core/codemirror/language/languages/sql/completion-store.ts +46 -50
  197. package/src/core/codemirror/language/languages/sql/renderers.tsx +485 -0
  198. package/src/core/codemirror/language/languages/sql/sql.ts +151 -24
  199. package/src/core/codemirror/language/languages/sql/utils.ts +4 -1
  200. package/src/core/codemirror/language/panel/sql.tsx +6 -1
  201. package/src/core/codemirror/language/utils/ast.ts +3 -3
  202. package/src/core/codemirror/lsp/federated-lsp.ts +4 -4
  203. package/src/core/codemirror/lsp/lens.ts +4 -4
  204. package/src/core/codemirror/lsp/notebook-lsp.ts +1 -1
  205. package/src/core/codemirror/lsp/types.ts +1 -1
  206. package/src/core/codemirror/markdown/completions.ts +1 -1
  207. package/src/core/codemirror/reactive-references/analyzer.ts +2 -2
  208. package/src/core/codemirror/rtc/loro/awareness.ts +1 -1
  209. package/src/core/config/config-schema.ts +1 -0
  210. package/src/core/config/feature-flag.tsx +3 -1
  211. package/src/core/datasets/request-registry.ts +17 -10
  212. package/src/core/dom/events.ts +1 -1
  213. package/src/core/dom/outline.ts +2 -2
  214. package/src/core/dom/uiregistry.ts +2 -8
  215. package/src/core/errors/__tests__/errors.test.ts +22 -4
  216. package/src/core/errors/errors.ts +29 -1
  217. package/src/core/errors/state.ts +1 -1
  218. package/src/core/islands/main.ts +2 -2
  219. package/src/core/islands/parse.ts +1 -3
  220. package/src/core/kernel/messages.ts +1 -1
  221. package/src/core/network/CachingRequestRegistry.ts +74 -0
  222. package/src/core/network/DeferredRequestRegistry.ts +3 -1
  223. package/src/core/network/__tests__/CachingRequestRegistry.test.ts +73 -0
  224. package/src/core/network/types.ts +1 -1
  225. package/src/core/variables/state.ts +2 -2
  226. package/src/core/wasm/__tests__/state.test.ts +1 -1
  227. package/src/core/websocket/useMarimoWebSocket.tsx +5 -2
  228. package/src/custom.d.ts +1 -1
  229. package/src/hooks/useCellRenderCount.ts +1 -0
  230. package/src/hooks/useResizeHandle.ts +4 -1
  231. package/src/plugins/core/RenderHTML.tsx +1 -2
  232. package/src/plugins/impl/DataTablePlugin.tsx +7 -2
  233. package/src/plugins/impl/FileUploadPlugin.tsx +1 -1
  234. package/src/plugins/impl/RefreshPlugin.tsx +1 -1
  235. package/src/plugins/impl/SliderPlugin.tsx +4 -0
  236. package/src/plugins/impl/anywidget/AnyWidgetPlugin.tsx +27 -9
  237. package/src/plugins/impl/anywidget/__tests__/AnyWidgetPlugin.test.tsx +58 -2
  238. package/src/plugins/impl/anywidget/__tests__/model.test.ts +3 -4
  239. package/src/plugins/impl/anywidget/model.ts +2 -3
  240. package/src/plugins/impl/data-editor/types.ts +1 -1
  241. package/src/plugins/impl/data-explorer/components/query-form.tsx +1 -1
  242. package/src/plugins/impl/data-frames/types.ts +1 -1
  243. package/src/plugins/impl/panel/PanelPlugin.tsx +2 -2
  244. package/src/plugins/impl/plotly/PlotlyPlugin.tsx +3 -3
  245. package/src/plugins/impl/vega/__tests__/loader.test.ts +2 -2
  246. package/src/plugins/impl/vega/loader.ts +1 -1
  247. package/src/plugins/impl/vega/vega-component.tsx +1 -1
  248. package/src/plugins/impl/vega/vega-loader.ts +2 -2
  249. package/src/plugins/layout/NavigationMenuPlugin.tsx +1 -1
  250. package/src/plugins/layout/RoutesPlugin.tsx +1 -2
  251. package/src/plugins/plugins.ts +2 -2
  252. package/src/utils/Logger.ts +1 -1
  253. package/src/utils/__tests__/data-views.test.ts +30 -68
  254. package/src/utils/__tests__/dom.test.ts +10 -10
  255. package/src/utils/__tests__/id-tree.test.ts +49 -1
  256. package/src/utils/__tests__/storage.test.ts +1 -1
  257. package/src/utils/__tests__/traceback.test.ts +13 -2
  258. package/src/utils/arrays.ts +1 -1
  259. package/src/utils/createReducer.ts +1 -5
  260. package/src/utils/data-views.ts +6 -19
  261. package/src/utils/edit-distance.ts +1 -1
  262. package/src/utils/fileToBase64.ts +1 -1
  263. package/src/utils/id-tree.tsx +20 -18
  264. package/src/utils/json/base64.ts +13 -0
  265. package/src/utils/json/json-parser.ts +2 -2
  266. package/src/utils/lru.ts +4 -0
  267. package/src/utils/mergeRefs.ts +1 -1
  268. package/src/utils/objects.ts +3 -3
  269. package/src/utils/pluralize.ts +1 -1
  270. package/src/utils/routes.ts +2 -2
  271. package/src/utils/sets.ts +1 -1
  272. package/src/utils/traceback.ts +45 -15
  273. package/src/utils/tracer.ts +11 -9
  274. package/dist/assets/_baseEach--KDTwKbG.js +0 -1
  275. package/dist/assets/_baseMap-Cu3o-eyO.js +0 -1
  276. package/dist/assets/_baseUniq-y7ZXnMo1.js +0 -1
  277. package/dist/assets/channel-DFaEx1fu.js +0 -1
  278. package/dist/assets/chat-panel-IoPMv8e2.js +0 -3
  279. package/dist/assets/classDiagram-KNZD7YFC-BsZtvV5O.js +0 -1
  280. package/dist/assets/classDiagram-v2-RKCZMP56-BsZtvV5O.js +0 -1
  281. package/dist/assets/clone-YBEvPE-s.js +0 -1
  282. package/dist/assets/command-palette-D7hOfvf6.js +0 -1
  283. package/dist/assets/edit-page-C5TsEeSo.js +0 -129
  284. package/dist/assets/index-CGDMlQfO.css +0 -1
  285. package/dist/assets/index-CelXfcd8.js +0 -580
  286. package/dist/assets/index-Cxyk7pt-.js +0 -68
  287. package/dist/assets/infoDiagram-STP46IZ2-wTALjfPc.js +0 -2
  288. package/dist/assets/links-Drv7cJgN.js +0 -7
  289. package/dist/assets/min-DYUOb1RR.js +0 -1
  290. package/dist/assets/scratchpad-panel-DlDfcDtW.js +0 -1
  291. package/dist/assets/secrets-panel-BDGyuGZA.js +0 -1
  292. package/dist/assets/stateDiagram-v2-UMBNRL4Z-CtBJqosP.js +0 -1
  293. package/src/__tests__/lru.test.ts +0 -74
@@ -11,7 +11,9 @@ import { parser } from "@lezer/python";
11
11
  import {
12
12
  defaultSqlHoverTheme,
13
13
  NodeSqlParser,
14
+ type NodeSqlParserResult,
14
15
  type SupportedDialects as ParserDialects,
16
+ type SqlParseError,
15
17
  sqlExtension,
16
18
  } from "@marimo-team/codemirror-sql";
17
19
  import { DuckDBDialect } from "@marimo-team/codemirror-sql/dialects";
@@ -39,16 +41,16 @@ import { parseArgsKwargs } from "../../utils/ast";
39
41
  import { indentOneTab } from "../../utils/indentOneTab";
40
42
  import type { QuotePrefixKind } from "../../utils/quotes";
41
43
  import { MarkdownLanguageAdapter } from "../markdown";
44
+ import {
45
+ clearSqlValidationError,
46
+ setSqlValidationError,
47
+ } from "./banner-validation-errors";
42
48
  import {
43
49
  customKeywordCompletionSource,
44
50
  tablesCompletionSource,
45
51
  } from "./completion-sources";
46
52
  import { SCHEMA_CACHE } from "./completion-store";
47
- import { getSQLMode } from "./sql-mode";
48
- import {
49
- clearSqlValidationError,
50
- setSqlValidationError,
51
- } from "./validation-errors";
53
+ import { getSQLMode, type SQLMode } from "./sql-mode";
52
54
 
53
55
  const DEFAULT_DIALECT = DuckDBDialect;
54
56
  const DEFAULT_PARSER_DIALECT = "DuckDB";
@@ -245,7 +247,7 @@ export class SQLLanguageAdapter
245
247
 
246
248
  if (this.sqlLinterEnabled) {
247
249
  const theme = store.get(resolvedThemeAtom);
248
- const parser = new NodeSqlParser({
250
+ const parser = new CustomSqlParser({
249
251
  getParserOptions: (state: EditorState) => {
250
252
  return {
251
253
  database: guessParserDialect(state) ?? DEFAULT_PARSER_DIALECT,
@@ -277,6 +279,11 @@ export class SQLLanguageAdapter
277
279
  theme: defaultSqlHoverTheme(theme),
278
280
  },
279
281
  }),
282
+ EditorView.updateListener.of((update) => {
283
+ if (update.focusChanged) {
284
+ parser.setFocusState(update.view.hasFocus);
285
+ }
286
+ }),
280
287
  );
281
288
  }
282
289
 
@@ -288,6 +295,87 @@ export class SQLLanguageAdapter
288
295
  }
289
296
  }
290
297
 
298
+ class CustomSqlParser extends NodeSqlParser {
299
+ private validationTimeout: number | null = null;
300
+ private readonly VALIDATION_DELAY_MS = 300; // Wait 300ms after user stops typing
301
+ private isFocused = false; // Only validate if the editor is focused
302
+
303
+ setFocusState(focused: boolean) {
304
+ this.isFocused = focused;
305
+ }
306
+
307
+ private async validateWithDelay(
308
+ sql: string,
309
+ engine: string,
310
+ dialect: ParserDialects | null,
311
+ ): Promise<SqlParseError[]> {
312
+ // Clear any existing delay call
313
+ if (this.validationTimeout) {
314
+ window.clearTimeout(this.validationTimeout);
315
+ }
316
+
317
+ // Set up a new request to be called after the delay
318
+ return new Promise((resolve) => {
319
+ this.validationTimeout = window.setTimeout(async () => {
320
+ // Only validate if the editor is still focused
321
+ if (!this.isFocused) {
322
+ resolve([]);
323
+ return;
324
+ }
325
+
326
+ try {
327
+ const sqlMode = getSQLMode();
328
+ const result = await validateSQL(sql, engine, dialect, sqlMode);
329
+ if (result.error) {
330
+ Logger.error("Failed to validate SQL", { error: result.error });
331
+ resolve([]);
332
+ return;
333
+ }
334
+ resolve(result.parse_result?.errors ?? []);
335
+ } catch (error) {
336
+ Logger.error("Failed to validate SQL", { error });
337
+ resolve([]);
338
+ }
339
+ }, this.VALIDATION_DELAY_MS);
340
+ });
341
+ }
342
+
343
+ override async validateSql(
344
+ sql: string,
345
+ opts: { state: EditorState },
346
+ ): Promise<SqlParseError[]> {
347
+ const metadata = getSQLMetadata(opts.state);
348
+
349
+ // Only validate if the editor is focused
350
+ if (!this.isFocused) {
351
+ return [];
352
+ }
353
+
354
+ // Only perform custom validation for DuckDB
355
+ if (!INTERNAL_SQL_ENGINES.has(metadata.engine)) {
356
+ return super.validateSql(sql, opts);
357
+ }
358
+
359
+ const dialect = guessParserDialect(opts.state);
360
+ return this.validateWithDelay(sql, metadata.engine, dialect);
361
+ }
362
+
363
+ override async parse(
364
+ sql: string,
365
+ opts: { state: EditorState },
366
+ ): Promise<NodeSqlParserResult> {
367
+ const metadata = getSQLMetadata(opts.state);
368
+ const engine = metadata.engine;
369
+
370
+ // For now, always return success for DuckDB
371
+ if (engine === DUCKDB_ENGINE) {
372
+ return { success: true, errors: [] };
373
+ }
374
+
375
+ return super.parse(sql, opts);
376
+ }
377
+ }
378
+
291
379
  /**
292
380
  * Update the SQL dialect in the editor view.
293
381
  */
@@ -568,11 +656,22 @@ function safeDedent(code: string): string {
568
656
  }
569
657
  }
570
658
 
659
+ const SQL_VALIDATION_DEBOUNCE_MS = 300;
660
+
661
+ /**
662
+ * Custom extension to run SQL queries in EXPLAIN mode on keypress.
663
+ */
571
664
  function sqlValidationExtension(): Extension {
572
- let debounceTimeout: NodeJS.Timeout | null = null;
665
+ let debounceTimeout: number | undefined;
573
666
  let lastValidationRequest: string | null = null;
574
667
 
575
668
  return EditorView.updateListener.of((update) => {
669
+ // Only run validation if the document has changed
670
+ if (!update.docChanged) {
671
+ return;
672
+ }
673
+
674
+ // Only run validation if the SQL mode is set to validate
576
675
  const sqlMode = getSQLMode();
577
676
  if (sqlMode !== "validate") {
578
677
  return;
@@ -580,12 +679,9 @@ function sqlValidationExtension(): Extension {
580
679
 
581
680
  const metadata = getSQLMetadata(update.state);
582
681
  const connectionName = metadata.engine;
583
- if (!INTERNAL_SQL_ENGINES.has(connectionName)) {
584
- // Currently only internal engines are supported
585
- return;
586
- }
587
682
 
588
- if (!update.docChanged) {
683
+ // Currently only DuckDB is supported
684
+ if (!INTERNAL_SQL_ENGINES.has(connectionName)) {
589
685
  return;
590
686
  }
591
687
 
@@ -594,11 +690,11 @@ function sqlValidationExtension(): Extension {
594
690
 
595
691
  // Clear existing timeout
596
692
  if (debounceTimeout) {
597
- clearTimeout(debounceTimeout);
693
+ window.clearTimeout(debounceTimeout);
598
694
  }
599
695
 
600
696
  // Debounce the validation call
601
- debounceTimeout = setTimeout(async () => {
697
+ debounceTimeout = window.setTimeout(async () => {
602
698
  // Skip if content hasn't changed since last validation
603
699
  if (lastValidationRequest === sqlContent) {
604
700
  return;
@@ -613,20 +709,51 @@ function sqlValidationExtension(): Extension {
613
709
  }
614
710
 
615
711
  try {
616
- const result: ValidateSQLResult = await ValidateSQL.request({
617
- engine: connectionName,
618
- query: sqlContent,
619
- });
620
-
621
- if (result.error) {
622
- const dialect = connectionNameToParserDialect(connectionName);
623
- setSqlValidationError({ cellId, error: result.error, dialect });
712
+ const dialect = connectionNameToParserDialect(connectionName);
713
+ const sqlMode = getSQLMode();
714
+ const result = await validateSQL(
715
+ sqlContent,
716
+ connectionName,
717
+ dialect,
718
+ sqlMode,
719
+ );
720
+ const validateResult = result.validate_result;
721
+
722
+ if (validateResult?.error_message) {
723
+ setSqlValidationError({
724
+ cellId,
725
+ errorMessage: validateResult.error_message,
726
+ dialect,
727
+ });
624
728
  } else {
625
729
  clearSqlValidationError(cellId);
626
730
  }
627
731
  } catch (error) {
628
- Logger.warn("Failed to validate SQL", { error });
732
+ Logger.error("Failed to validate SQL", { error });
629
733
  }
630
- }, 300);
734
+ }, SQL_VALIDATION_DEBOUNCE_MS);
735
+ });
736
+ }
737
+
738
+ /**
739
+ * Determine if we should only parse or validate an SQL query.
740
+ * The endpoint is cached, so we should use the same mode for all validation requests.
741
+ */
742
+ async function validateSQL(
743
+ sql: string,
744
+ engine: string,
745
+ dialect: ParserDialects | null,
746
+ sqlMode: SQLMode,
747
+ ): Promise<ValidateSQLResult> {
748
+ const result = await ValidateSQL.request({
749
+ onlyParse: sqlMode === "default",
750
+ engine,
751
+ dialect,
752
+ query: sql,
631
753
  });
754
+
755
+ if (result.error) {
756
+ throw new Error(result.error);
757
+ }
758
+ return result;
632
759
  }
@@ -18,8 +18,11 @@ import {
18
18
  } from "@marimo-team/codemirror-sql/dialects";
19
19
  import type { DataSourceConnection } from "@/core/kernel/messages";
20
20
 
21
+ /**
22
+ * Guess the CodeMirror SQL dialect from the backend connection dialect.
23
+ */
21
24
  export function guessDialect(
22
- connection: DataSourceConnection,
25
+ connection: Pick<DataSourceConnection, "dialect">,
23
26
  ): SQLDialect | undefined {
24
27
  switch (connection.dialect) {
25
28
  case "postgresql":
@@ -32,6 +32,7 @@ import {
32
32
  } from "@/core/datasets/engines";
33
33
  import type { DataSourceConnection } from "@/core/kernel/messages";
34
34
  import { useNonce } from "@/hooks/useNonce";
35
+ import { clearAllSqlValidationErrors } from "../languages/sql/banner-validation-errors";
35
36
  import { type SQLMode, useSQLMode } from "../languages/sql/sql-mode";
36
37
 
37
38
  interface SelectProps {
@@ -144,7 +145,11 @@ export const SQLModeSelect: React.FC = () => {
144
145
  const { sqlMode, setSQLMode } = useSQLMode();
145
146
 
146
147
  const handleToggleMode = () => {
147
- setSQLMode(sqlMode === "validate" ? "default" : "validate");
148
+ const nextMode = sqlMode === "validate" ? "default" : "validate";
149
+ if (nextMode === "default") {
150
+ clearAllSqlValidationErrors();
151
+ }
152
+ setSQLMode(nextMode);
148
153
  };
149
154
 
150
155
  const getModeIcon = (mode: SQLMode) => {
@@ -10,7 +10,7 @@ export function parseArgsKwargs(
10
10
  code: string,
11
11
  ): {
12
12
  args: SyntaxNode[];
13
- kwargs: Array<{ key: string; value: string }>;
13
+ kwargs: { key: string; value: string }[];
14
14
  } {
15
15
  // Check we are in an ArgList
16
16
  const name = argCursor.name;
@@ -54,8 +54,8 @@ function parseArgs(argCursor: TreeCursor): SyntaxNode[] {
54
54
  function parseKwargs(
55
55
  argCursor: TreeCursor,
56
56
  code: string,
57
- ): Array<{ key: string; value: string }> {
58
- const kwargs: Array<{ key: string; value: string }> = [];
57
+ ): { key: string; value: string }[] {
58
+ const kwargs: { key: string; value: string }[] = [];
59
59
  let name = argCursor.name;
60
60
 
61
61
  do {
@@ -115,7 +115,7 @@ export class FederatedLanguageServerClient implements ILanguageServerClient {
115
115
 
116
116
  async textDocumentCodeAction(
117
117
  params: LSP.CodeActionParams,
118
- ): Promise<Array<LSP.Command | LSP.CodeAction> | null> {
118
+ ): Promise<(LSP.Command | LSP.CodeAction)[] | null> {
119
119
  const client = this.firstWithCapability("codeActionProvider");
120
120
  if (client) {
121
121
  return client.textDocumentCodeAction(params);
@@ -206,9 +206,9 @@ export class FederatedLanguageServerClient implements ILanguageServerClient {
206
206
  }
207
207
 
208
208
  function mergeCompletions(
209
- results: Array<
210
- PromiseSettledResult<LSP.CompletionList | LSP.CompletionItem[] | null>
211
- >,
209
+ results: PromiseSettledResult<
210
+ LSP.CompletionList | LSP.CompletionItem[] | null
211
+ >[],
212
212
  ): LSP.CompletionList {
213
213
  const completions: LSP.CompletionItem[] = [];
214
214
  let isIncomplete = false;
@@ -22,10 +22,10 @@ export interface NotebookLens {
22
22
  reversePosition: (position: LSP.Position, cellId: CellId) => LSP.Position;
23
23
 
24
24
  /** Clip a range to the given cell */
25
- getEditsForNewText: (newText: string) => Array<{
25
+ getEditsForNewText: (newText: string) => {
26
26
  cellId: CellId;
27
27
  text: string;
28
- }>;
28
+ }[];
29
29
 
30
30
  /** Check if a range falls within the given cell */
31
31
  isInRange: (range: LSP.Range, cellId: CellId) => boolean;
@@ -84,10 +84,10 @@ export function createNotebookLens(
84
84
  throw new Error("Cannot apply rename when there are new lines");
85
85
  }
86
86
 
87
- const edits: Array<{
87
+ const edits: {
88
88
  cellId: CellId;
89
89
  text: string;
90
- }> = [];
90
+ }[] = [];
91
91
 
92
92
  for (const [cellId, code] of Objects.entries(codes)) {
93
93
  if (!cellLineOffsets.has(cellId)) {
@@ -300,7 +300,7 @@ export class NotebookLanguageServerClient implements ILanguageServerClient {
300
300
 
301
301
  textDocumentCodeAction(
302
302
  params: LSP.CodeActionParams,
303
- ): Promise<Array<LSP.Command | LSP.CodeAction> | null> {
303
+ ): Promise<(LSP.Command | LSP.CodeAction)[] | null> {
304
304
  const disabledCodeAction = true;
305
305
  if (disabledCodeAction) {
306
306
  return Promise.resolve(null);
@@ -49,7 +49,7 @@ export function isClientWithNotify(
49
49
  export function isClientWithPlugins(
50
50
  client: ILanguageServerClient,
51
51
  ): client is ILanguageServerClient & {
52
- plugins: Array<{ documentUri: string; view?: EditorView }>;
52
+ plugins: { documentUri: string; view?: EditorView }[];
53
53
  } {
54
54
  return "plugins" in client;
55
55
  }
@@ -133,7 +133,7 @@ const latexSymbolCompletionSource: CompletionSource = (context) => {
133
133
 
134
134
  // Common LaTeX symbols with their UTF-8 equivalents
135
135
  const getLatexSymbolList = once((): Completion[] => {
136
- const symbols: Array<[string, string, string]> = [
136
+ const symbols: [string, string, string][] = [
137
137
  // Greek letters
138
138
  ["alpha", "α", "Greek small letter alpha"],
139
139
  ["beta", "β", "Greek small letter beta"],
@@ -67,7 +67,7 @@ export function findReactiveVariables(options: {
67
67
  // Maps to track variable declarations and scopes
68
68
  const allDeclarations = new Map<number, Set<string>>(); // scope position -> variable names
69
69
  const scopeTypes = new Map<number, string>(); // scope position -> scope type (e.g., "ClassDefinition")
70
- const classLevelDeclarations = new Map<number, Array<[string, number]>>(); // class scope -> [varName, position] pairs
70
+ const classLevelDeclarations = new Map<number, [string, number][]>(); // class scope -> [varName, position] pairs
71
71
 
72
72
  // Class-level variables require special handling because they're evaluated sequentially
73
73
  // A variable is only available after its assignment statement completes
@@ -555,7 +555,7 @@ function extractAssignmentTargets(
555
555
  state: EditorState;
556
556
  allDeclarations: Map<number, Set<string>>;
557
557
  scopeTypes: Map<number, string>;
558
- classLevelDeclarations: Map<number, Array<[string, number]>>;
558
+ classLevelDeclarations: Map<number, [string, number][]>;
559
559
  assignmentPosition: number;
560
560
  },
561
561
  ) {
@@ -302,7 +302,7 @@ const parseAwarenessUpdate = (
302
302
  removed: PeerID[];
303
303
  },
304
304
  scopeId: ScopeId,
305
- ): Array<StateEffect<CursorEffect>> => {
305
+ ): StateEffect<CursorEffect>[] => {
306
306
  const effects = [];
307
307
  const { updated, added } = arg;
308
308
  for (const update of [...updated, ...added]) {
@@ -163,6 +163,7 @@ export const UserConfigSchema = z
163
163
  anthropic: AiConfigSchema.optional(),
164
164
  google: AiConfigSchema.optional(),
165
165
  ollama: AiConfigSchema.optional(),
166
+ openrouter: AiConfigSchema.optional(),
166
167
  open_ai_compatible: AiConfigSchema.optional(),
167
168
  azure: AiConfigSchema.optional(),
168
169
  bedrock: z
@@ -12,6 +12,7 @@ export interface ExperimentalFeatures {
12
12
  rtc_v2: boolean;
13
13
  performant_table_charts: boolean;
14
14
  mcp_docs: boolean;
15
+ chat_modes: boolean;
15
16
  sql_linter: boolean;
16
17
  external_agents: boolean;
17
18
  sql_mode: boolean;
@@ -25,7 +26,8 @@ const defaultValues: ExperimentalFeatures = {
25
26
  rtc_v2: false,
26
27
  performant_table_charts: false,
27
28
  mcp_docs: false,
28
- sql_linter: false,
29
+ chat_modes: false,
30
+ sql_linter: true,
29
31
  external_agents: import.meta.env.DEV,
30
32
  sql_mode: false,
31
33
  };
@@ -4,6 +4,7 @@ import type {
4
4
  SQLTablePreview,
5
5
  ValidateSQLResult,
6
6
  } from "../kernel/messages";
7
+ import { CachingRequestRegistry } from "../network/CachingRequestRegistry";
7
8
  import { DeferredRequestRegistry } from "../network/DeferredRequestRegistry";
8
9
  import { getRequestClient } from "../network/requests";
9
10
  import type {
@@ -38,13 +39,19 @@ export const PreviewSQLTableList = new DeferredRequestRegistry<
38
39
  });
39
40
  });
40
41
 
41
- export const ValidateSQL = new DeferredRequestRegistry<
42
- Omit<ValidateSQLRequest, "requestId">,
43
- ValidateSQLResult
44
- >("validate-sql", async (requestId, req) => {
45
- const client = getRequestClient();
46
- await client.validateSQL({
47
- requestId: requestId,
48
- ...req,
49
- });
50
- });
42
+ export const ValidateSQL = new CachingRequestRegistry(
43
+ new DeferredRequestRegistry<
44
+ Omit<ValidateSQLRequest, "requestId">,
45
+ ValidateSQLResult
46
+ >("validate-sql", async (requestId, req) => {
47
+ const client = getRequestClient();
48
+ await client.validateSQL({
49
+ requestId: requestId,
50
+ ...req,
51
+ });
52
+ }),
53
+ {
54
+ // Only keep the last 3 validation results
55
+ maxSize: 3,
56
+ },
57
+ );
@@ -43,7 +43,7 @@ export const MarimoIncomingMessageEvent = defineCustomEvent(
43
43
  )<{
44
44
  objectId: UIElementId;
45
45
  message: unknown;
46
- buffers: DataView[] | undefined;
46
+ buffers: readonly DataView[];
47
47
  }>();
48
48
  export type MarimoIncomingMessageEventType = ReturnType<
49
49
  typeof MarimoIncomingMessageEvent.create
@@ -57,7 +57,7 @@ export function headingToIdentifier(heading: Element): OutlineItem["by"] {
57
57
  return { path: `//${heading.tagName}[contains(., "${name}")]` };
58
58
  }
59
59
 
60
- export function mergeOutlines(outlines: Array<Outline | null>): Outline {
60
+ export function mergeOutlines(outlines: (Outline | null)[]): Outline {
61
61
  return {
62
62
  items: outlines.filter(Boolean).flatMap((outline) => outline.items),
63
63
  };
@@ -104,7 +104,7 @@ export function canCollapseOutline(outline: Outline | null): boolean {
104
104
  */
105
105
  export function findCollapseRange(
106
106
  startIndex: number,
107
- outlines: Array<Outline | null>,
107
+ outlines: (Outline | null)[],
108
108
  ): [number, number] | null {
109
109
  // Higher header is the lowest value
110
110
  const getHighestHeader = (outline: Outline) => {
@@ -1,8 +1,5 @@
1
1
  /* Copyright 2024 Marimo. All rights reserved. */
2
2
 
3
- import { byteStringToDataView } from "@/utils/data-views";
4
- import type { Base64String } from "@/utils/json/base64";
5
- import { typedAtob } from "@/utils/json/base64";
6
3
  import { Logger } from "@/utils/Logger";
7
4
  import type { CellId, UIElementId } from "../cells/ids";
8
5
  import {
@@ -136,15 +133,12 @@ export class UIElementRegistry {
136
133
  broadcastMessage(
137
134
  objectId: UIElementId,
138
135
  message: unknown,
139
- buffers: Base64String[] | undefined | null,
136
+ buffers: readonly DataView[],
140
137
  ): void {
141
138
  const entry = this.entries.get(objectId);
142
139
  if (entry === undefined) {
143
140
  Logger.warn("UIElementRegistry missing entry", objectId);
144
141
  } else {
145
- const toDataView = (base64: Base64String) => {
146
- return byteStringToDataView(typedAtob(base64));
147
- };
148
142
  entry.elements.forEach((element) => {
149
143
  element.dispatchEvent(
150
144
  MarimoIncomingMessageEvent.create({
@@ -153,7 +147,7 @@ export class UIElementRegistry {
153
147
  detail: {
154
148
  objectId: objectId,
155
149
  message: message,
156
- buffers: buffers ? buffers.map(toDataView) : undefined,
150
+ buffers: buffers,
157
151
  },
158
152
  }),
159
153
  );
@@ -16,6 +16,10 @@ describe("getImportCode", () => {
16
16
  });
17
17
  });
18
18
 
19
+ const opts = {
20
+ aiEnabled: true,
21
+ };
22
+
19
23
  describe("getAutoFixes", () => {
20
24
  it("returns wrap in function fix for multiple-defs error", () => {
21
25
  const error: MarimoError = {
@@ -24,7 +28,7 @@ describe("getAutoFixes", () => {
24
28
  cells: ["foo"],
25
29
  };
26
30
 
27
- const fixes = getAutoFixes(error);
31
+ const fixes = getAutoFixes(error, opts);
28
32
  expect(fixes).toHaveLength(1);
29
33
  expect(fixes[0].title).toBe("Fix: Wrap in a function");
30
34
  });
@@ -37,11 +41,25 @@ describe("getAutoFixes", () => {
37
41
  raising_cell: null,
38
42
  };
39
43
 
40
- const fixes = getAutoFixes(error);
44
+ const fixes = getAutoFixes(error, opts);
41
45
  expect(fixes).toHaveLength(1);
42
46
  expect(fixes[0].title).toBe("Fix: Add 'import numpy as np'");
43
47
  });
44
48
 
49
+ it("returns sql fix for sql-error error", () => {
50
+ const error: MarimoError = {
51
+ type: "sql-error",
52
+ msg: "syntax error",
53
+ sql_statement: "SELECT * FROM table",
54
+ };
55
+
56
+ const fixes = getAutoFixes(error, opts);
57
+ expect(fixes).toHaveLength(1);
58
+ expect(fixes[0].title).toBe("Fix with AI");
59
+
60
+ expect(getAutoFixes(error, { aiEnabled: false })).toHaveLength(0);
61
+ });
62
+
45
63
  it("returns no fixes for NameError with unknown import", () => {
46
64
  const error: MarimoError = {
47
65
  type: "exception",
@@ -50,7 +68,7 @@ describe("getAutoFixes", () => {
50
68
  raising_cell: null,
51
69
  };
52
70
 
53
- expect(getAutoFixes(error)).toHaveLength(0);
71
+ expect(getAutoFixes(error, opts)).toHaveLength(0);
54
72
  });
55
73
 
56
74
  it("returns no fixes for other error types", () => {
@@ -59,6 +77,6 @@ describe("getAutoFixes", () => {
59
77
  msg: "invalid syntax",
60
78
  };
61
79
 
62
- expect(getAutoFixes(error)).toHaveLength(0);
80
+ expect(getAutoFixes(error, opts)).toHaveLength(0);
63
81
  });
64
82
  });
@@ -12,10 +12,19 @@ export interface AutoFix {
12
12
  addCodeBelow: (code: string) => void;
13
13
  editor: EditorView | undefined;
14
14
  cellId: CellId;
15
+ setAiCompletionCell?: (cell: {
16
+ cellId: CellId;
17
+ initialPrompt?: string;
18
+ }) => void;
15
19
  }) => Promise<void>;
16
20
  }
17
21
 
18
- export function getAutoFixes(error: MarimoError): AutoFix[] {
22
+ export function getAutoFixes(
23
+ error: MarimoError,
24
+ opts: {
25
+ aiEnabled: boolean;
26
+ },
27
+ ): AutoFix[] {
19
28
  if (error.type === "multiple-defs") {
20
29
  return [
21
30
  {
@@ -57,6 +66,25 @@ export function getAutoFixes(error: MarimoError): AutoFix[] {
57
66
  ];
58
67
  }
59
68
 
69
+ if (error.type === "sql-error") {
70
+ // Only show AI fix if AI is enabled
71
+ if (!opts.aiEnabled) {
72
+ return [];
73
+ }
74
+ return [
75
+ {
76
+ title: "Fix with AI",
77
+ description: "Fix the SQL statement",
78
+ onFix: async (ctx) => {
79
+ ctx.setAiCompletionCell?.({
80
+ cellId: ctx.cellId,
81
+ initialPrompt: `Fix the SQL statement: ${error.msg}`,
82
+ });
83
+ },
84
+ },
85
+ ];
86
+ }
87
+
60
88
  return [];
61
89
  }
62
90
 
@@ -7,7 +7,7 @@ import { generateUUID } from "@/utils/uuid";
7
7
  import type { Banner } from "../kernel/messages";
8
8
 
9
9
  interface BannerState {
10
- banners: Array<Identified<Banner>>;
10
+ banners: Identified<Banner>[];
11
11
  }
12
12
 
13
13
  const { valueAtom: bannersAtom, useActions } = createReducerAndAtoms(