@marimo-team/islands 0.16.1 → 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 (238) hide show
  1. package/dist/{ConnectedDataExplorerComponent-DyqLQGPc.js → ConnectedDataExplorerComponent-CareOso9.js} +2 -2
  2. package/dist/{ImageComparisonComponent-CQDGJfUA.js → ImageComparisonComponent-I_Z738Uj.js} +1 -1
  3. package/dist/{_baseUniq-B2Nna6Kt.js → _baseUniq-D-Kb4EU4.js} +1 -1
  4. package/dist/{any-language-editor-D-wq0tOG.js → any-language-editor-BRWmYor8.js} +1 -1
  5. package/dist/{architectureDiagram-W76B3OCA-C6tdnMBf.js → architectureDiagram-W76B3OCA-bZJcJYGH.js} +4 -4
  6. package/dist/assets/{worker-B0C57BK8.js → worker-DMlIUTIq.js} +18 -17
  7. package/dist/{blockDiagram-QIGZ2CNN-IagL8LCN.js → blockDiagram-QIGZ2CNN-DyXjO8fR.js} +5 -5
  8. package/dist/{c4Diagram-FPNF74CW-D3_lIWUP.js → c4Diagram-FPNF74CW-Bfs9ui2r.js} +2 -2
  9. package/dist/{channel-DCJI_DKk.js → channel-CMup9X3Z.js} +1 -1
  10. package/dist/{chunk-4BX2VUAB-B2DrODwN.js → chunk-4BX2VUAB-CW-ni6M_.js} +1 -1
  11. package/dist/{chunk-55IACEB6-BUWDsQ-t.js → chunk-55IACEB6-Bj-Indya.js} +1 -1
  12. package/dist/{chunk-FMBD7UC4-BExPNFv1.js → chunk-FMBD7UC4-9IC8qSSk.js} +1 -1
  13. package/dist/{chunk-K7UQS3LO-Cixi-Yko.js → chunk-K7UQS3LO-aapkEuWN.js} +4 -4
  14. package/dist/{chunk-QN33PNHL-B83MtvER.js → chunk-QN33PNHL-Bo5dJ5T5.js} +1 -1
  15. package/dist/{chunk-QZHKN3VN-CXvbu85X.js → chunk-QZHKN3VN-BkMzjJYY.js} +1 -1
  16. package/dist/{chunk-TVAH2DTR-CpiumCHg.js → chunk-TVAH2DTR-Wqy_C_Rn.js} +3 -3
  17. package/dist/{chunk-TZMSLE5B-DIzaZjcI.js → chunk-TZMSLE5B-DjBmEAUz.js} +1 -1
  18. package/dist/{classDiagram-v2-RKCZMP56-DyN5HPdk.js → classDiagram-KNZD7YFC-DUsaN1O4.js} +2 -2
  19. package/dist/{classDiagram-KNZD7YFC-DyN5HPdk.js → classDiagram-v2-RKCZMP56-DUsaN1O4.js} +2 -2
  20. package/dist/{clone-DrJYap2i.js → clone-Dkt_7KOK.js} +1 -1
  21. package/dist/{cose-bilkent-S5V4N54A-D39b4WrQ.js → cose-bilkent-S5V4N54A-ClBuGZWI.js} +2 -2
  22. package/dist/{dagre-5GWH7T2D-BLjRxDpS.js → dagre-5GWH7T2D-BzmDIGaM.js} +6 -6
  23. package/dist/{data-grid-overlay-editor-DTALqerV.js → data-grid-overlay-editor-NiU9Ea77.js} +2 -2
  24. package/dist/{diagram-N5W7TBWH-MM8AIKGR.js → diagram-N5W7TBWH-BlO1yw_g.js} +5 -5
  25. package/dist/{diagram-QEK2KX5R-BZGarWuJ.js → diagram-QEK2KX5R-BvK83LUx.js} +3 -3
  26. package/dist/{diagram-S2PKOQOG-CnPinN9Q.js → diagram-S2PKOQOG-DvBzRYd7.js} +3 -3
  27. package/dist/{dockerfile-U8DnCJ4X.js → dockerfile-CPQG2tLO.js} +1 -1
  28. package/dist/{erDiagram-AWTI2OKA-CvDVbxOO.js → erDiagram-AWTI2OKA-Doy9FRTX.js} +4 -4
  29. package/dist/{flowDiagram-PVAE7QVJ-C2uuBTZS.js → flowDiagram-PVAE7QVJ-D_tX_HU1.js} +5 -5
  30. package/dist/{ganttDiagram-OWAHRB6G-BEff10RF.js → ganttDiagram-OWAHRB6G-CV03BHVY.js} +4 -4
  31. package/dist/{gitGraphDiagram-NY62KEGX-wggu0kb2.js → gitGraphDiagram-NY62KEGX-w3szEguZ.js} +4 -4
  32. package/dist/{glide-data-editor-Bqh5_dzJ.js → glide-data-editor-akznFrmp.js} +3 -3
  33. package/dist/{graph-DKpp_wzf.js → graph-CjrrDHdT.js} +3 -3
  34. package/dist/index-CIJJs0Tu.js +40382 -0
  35. package/dist/{index-DzJ_YPCG.js → index-DMo6cbcV.js} +3 -3
  36. package/dist/{index-4XruEJkp.js → index-DlV2CtJb.js} +1 -1
  37. package/dist/{index-DdfF_cLK.js → index-Y-Vbae6Z.js} +1 -1
  38. package/dist/{infoDiagram-STP46IZ2-DF7KW-Op.js → infoDiagram-STP46IZ2-BcBV2j75.js} +2 -2
  39. package/dist/{journeyDiagram-BIP6EPQ6-B_jmhmqd.js → journeyDiagram-BIP6EPQ6-BTGMSgvB.js} +3 -3
  40. package/dist/{kanban-definition-6OIFK2YF-B-M9FTyw.js → kanban-definition-6OIFK2YF-aopNqZ1Y.js} +2 -2
  41. package/dist/{layout-C4oVYZZD.js → layout-Dvo9pb_w.js} +4 -4
  42. package/dist/{linear-C-HCGr0T.js → linear-CHnELER9.js} +1 -1
  43. package/dist/{main-B9x2-9f2.js → main-kLZGkzVQ.js} +41787 -40423
  44. package/dist/main.js +1 -1
  45. package/dist/{mermaid-BE4cM3Qs.js → mermaid-DgM4_4bD.js} +30 -30
  46. package/dist/{min-DTpHJ698.js → min-cX4DuL_n.js} +2 -2
  47. package/dist/{mindmap-definition-Q6HEUPPD-Cpd-hO1E.js → mindmap-definition-Q6HEUPPD-DZjbYryy.js} +3 -3
  48. package/dist/{number-overlay-editor-CvURA2Ud.js → number-overlay-editor-8MpIObf7.js} +2 -2
  49. package/dist/{pieDiagram-ADFJNKIX-D9f_f6fn.js → pieDiagram-ADFJNKIX-D6L1IYAc.js} +3 -3
  50. package/dist/{quadrantDiagram-LMRXKWRM-DgllE7xw.js → quadrantDiagram-LMRXKWRM-nOyuc3Bf.js} +2 -2
  51. package/dist/{react-plotly-BU-JRJSi.js → react-plotly-ChkfYiVe.js} +1 -1
  52. package/dist/{requirementDiagram-4UW4RH46-Dk_G8eUb.js → requirementDiagram-4UW4RH46-OVV8wsju.js} +3 -3
  53. package/dist/{sankeyDiagram-GR3RE2ED-BhLIhDc1.js → sankeyDiagram-GR3RE2ED-qZHMdnE_.js} +1 -1
  54. package/dist/{sequenceDiagram-C3RYC4MD-DHoZdMFJ.js → sequenceDiagram-C3RYC4MD-D0bOqf-t.js} +3 -3
  55. package/dist/{slides-component-DXAgdf7K.js → slides-component-CNzLDdA3.js} +1 -1
  56. package/dist/{stateDiagram-KXAO66HF-C1Ie-7Xf.js → stateDiagram-KXAO66HF-CFNCnNJS.js} +4 -4
  57. package/dist/{stateDiagram-v2-UMBNRL4Z--CRuIHtM.js → stateDiagram-v2-UMBNRL4Z-CnIh27m8.js} +2 -2
  58. package/dist/style.css +1 -1
  59. package/dist/{time-yQjlGPwa.js → time-Z7CJSfOW.js} +2 -2
  60. package/dist/{timeline-definition-XQNQX7LJ-D_PjxB1B.js → timeline-definition-XQNQX7LJ-BYLMfrvK.js} +1 -1
  61. package/dist/{treemap-75Q7IDZK--NYqQjUZ.js → treemap-75Q7IDZK-BEh1HacP.js} +5 -5
  62. package/dist/{vega-component-CCUOMM5K.js → vega-component-iMjXd3tD.js} +2 -2
  63. package/dist/{xychartDiagram-6GGTOJPD-WLKsEnzs.js → xychartDiagram-6GGTOJPD-Bmi13ZSG.js} +2 -2
  64. package/package.json +8 -8
  65. package/src/__mocks__/common.ts +5 -3
  66. package/src/__mocks__/notebook.ts +2 -2
  67. package/src/__mocks__/requests.ts +1 -0
  68. package/src/__tests__/main.test.tsx +2 -2
  69. package/src/components/ai/ai-provider-icon.tsx +2 -0
  70. package/src/components/app-config/ai-config.tsx +32 -1
  71. package/src/components/app-config/common.tsx +2 -2
  72. package/src/components/app-config/user-config-form.tsx +26 -0
  73. package/src/components/audio/audio-recorder.tsx +0 -1
  74. package/src/components/chat/acp/blocks.tsx +2 -2
  75. package/src/components/chat/acp/thread.tsx +3 -5
  76. package/src/components/chat/acp/utils.ts +5 -5
  77. package/src/components/chat/chat-panel.tsx +1 -1
  78. package/src/components/data-table/__tests__/columns.test.tsx +38 -0
  79. package/src/components/data-table/__tests__/data-table.test.tsx +2 -2
  80. package/src/components/data-table/cell-hover-template/feature.ts +1 -1
  81. package/src/components/data-table/cell-hover-template/types.ts +1 -1
  82. package/src/components/data-table/charts/__tests__/altair-generator.test.ts +1 -1
  83. package/src/components/data-table/charts/chart-spec/tooltips.ts +3 -3
  84. package/src/components/data-table/charts/components/chart-items.tsx +1 -1
  85. package/src/components/data-table/charts/components/form-fields.tsx +2 -2
  86. package/src/components/data-table/charts/constants.ts +1 -1
  87. package/src/components/data-table/column-explorer-panel/column-explorer.tsx +1 -1
  88. package/src/components/data-table/column-summary/chart-spec-model.tsx +2 -2
  89. package/src/components/data-table/columns.tsx +22 -3
  90. package/src/components/data-table/data-table.tsx +35 -3
  91. package/src/components/data-table/date-popover.tsx +1 -1
  92. package/src/components/data-table/download-actions.tsx +1 -1
  93. package/src/components/data-table/range-focus/__tests__/utils.test.ts +5 -5
  94. package/src/components/data-table/renderers.tsx +22 -13
  95. package/src/components/data-table/row-viewer-panel/row-viewer.tsx +1 -1
  96. package/src/components/data-table/schemas.ts +16 -0
  97. package/src/components/data-table/types.ts +4 -3
  98. package/src/components/datasources/column-preview.tsx +9 -6
  99. package/src/components/debugger/debugger-code.tsx +1 -1
  100. package/src/components/dependency-graph/custom-node.tsx +15 -6
  101. package/src/components/dependency-graph/dependency-graph-minimap.tsx +2 -2
  102. package/src/components/dependency-graph/dependency-graph-tree.tsx +2 -2
  103. package/src/components/dependency-graph/dependency-graph.tsx +1 -1
  104. package/src/components/dependency-graph/elements.ts +7 -7
  105. package/src/components/dependency-graph/utils/changes.ts +4 -4
  106. package/src/components/editor/Cell.tsx +7 -1
  107. package/src/components/editor/ai/transport/chat-transport.tsx +1 -1
  108. package/src/components/editor/chrome/panels/outline/useActiveOutline.tsx +1 -1
  109. package/src/components/editor/chrome/panels/packages-panel.tsx +1 -1
  110. package/src/components/editor/columns/storage.ts +1 -1
  111. package/src/components/editor/database/__tests__/__snapshots__/as-code.test.ts.snap +36 -0
  112. package/src/components/editor/database/__tests__/as-code.test.ts +30 -7
  113. package/src/components/editor/database/add-database-form.tsx +11 -0
  114. package/src/components/editor/database/as-code.ts +104 -5
  115. package/src/components/editor/database/schemas.ts +36 -18
  116. package/src/components/editor/errors/auto-fix.tsx +12 -2
  117. package/src/components/editor/errors/sql-validation-errors.tsx +40 -0
  118. package/src/components/editor/navigation/clipboard.ts +2 -2
  119. package/src/components/editor/output/ConsoleOutput.tsx +14 -2
  120. package/src/components/editor/output/JsonOutput.tsx +1 -1
  121. package/src/components/editor/output/MarimoErrorOutput.tsx +60 -1
  122. package/src/components/editor/output/MarimoTracebackOutput.tsx +17 -2
  123. package/src/components/editor/renderers/grid-layout/types.ts +2 -2
  124. package/src/components/editor/renderers/plugins.ts +1 -1
  125. package/src/components/editor/renderers/types.ts +1 -1
  126. package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +7 -7
  127. package/src/components/forms/form.tsx +5 -5
  128. package/src/components/ui/links.tsx +1 -0
  129. package/src/core/ai/__tests__/model-registry.test.ts +0 -10
  130. package/src/core/ai/context/providers/cell-output.ts +1 -18
  131. package/src/core/ai/context/providers/error.ts +2 -2
  132. package/src/core/ai/ids/ids.ts +1 -0
  133. package/src/core/ai/model-registry.ts +2 -1
  134. package/src/core/cells/cells.ts +5 -5
  135. package/src/core/cells/logs.ts +1 -1
  136. package/src/core/cells/types.ts +1 -1
  137. package/src/core/codemirror/__tests__/format.test.ts +6 -0
  138. package/src/core/codemirror/cells/traceback-decorations.ts +1 -1
  139. package/src/core/codemirror/editing/commands.ts +2 -2
  140. package/src/core/codemirror/find-replace/navigate.ts +1 -1
  141. package/src/core/codemirror/language/__tests__/extension.test.ts +24 -0
  142. package/src/core/codemirror/language/__tests__/sql-validation.test.ts +133 -0
  143. package/src/core/codemirror/language/__tests__/sql.test.ts +764 -79
  144. package/src/core/codemirror/language/languages/markdown.ts +4 -1
  145. package/src/core/codemirror/language/languages/sql/banner-validation-errors.ts +85 -0
  146. package/src/core/codemirror/language/languages/sql/completion-builder.ts +160 -0
  147. package/src/core/codemirror/language/languages/sql/completion-sources.tsx +9 -3
  148. package/src/core/codemirror/language/languages/sql/completion-store.ts +46 -50
  149. package/src/core/codemirror/language/languages/sql/renderers.tsx +485 -0
  150. package/src/core/codemirror/language/languages/sql/sql-mode.ts +20 -0
  151. package/src/core/codemirror/language/languages/sql/sql.ts +218 -4
  152. package/src/core/codemirror/language/languages/sql/utils.ts +4 -1
  153. package/src/core/codemirror/language/panel/panel.tsx +8 -2
  154. package/src/core/codemirror/language/panel/sql.tsx +86 -4
  155. package/src/core/codemirror/language/utils/ast.ts +3 -3
  156. package/src/core/codemirror/lsp/federated-lsp.ts +4 -4
  157. package/src/core/codemirror/lsp/lens.ts +4 -4
  158. package/src/core/codemirror/lsp/notebook-lsp.ts +1 -1
  159. package/src/core/codemirror/lsp/types.ts +1 -1
  160. package/src/core/codemirror/markdown/completions.ts +1 -1
  161. package/src/core/codemirror/reactive-references/analyzer.ts +2 -2
  162. package/src/core/codemirror/rtc/loro/awareness.ts +1 -1
  163. package/src/core/config/config-schema.ts +1 -0
  164. package/src/core/config/feature-flag.tsx +6 -2
  165. package/src/core/datasets/request-registry.ts +24 -1
  166. package/src/core/dom/events.ts +1 -1
  167. package/src/core/dom/outline.ts +2 -2
  168. package/src/core/dom/uiregistry.ts +2 -8
  169. package/src/core/errors/__tests__/errors.test.ts +22 -4
  170. package/src/core/errors/errors.ts +29 -1
  171. package/src/core/errors/state.ts +1 -1
  172. package/src/core/islands/bridge.ts +1 -0
  173. package/src/core/islands/main.ts +3 -2
  174. package/src/core/islands/parse.ts +1 -3
  175. package/src/core/kernel/messages.ts +2 -1
  176. package/src/core/network/CachingRequestRegistry.ts +74 -0
  177. package/src/core/network/DeferredRequestRegistry.ts +3 -1
  178. package/src/core/network/__tests__/CachingRequestRegistry.test.ts +73 -0
  179. package/src/core/network/requests-network.ts +7 -0
  180. package/src/core/network/requests-static.ts +1 -0
  181. package/src/core/network/requests-toasting.ts +1 -0
  182. package/src/core/network/types.ts +3 -1
  183. package/src/core/variables/state.ts +2 -2
  184. package/src/core/wasm/__tests__/state.test.ts +1 -1
  185. package/src/core/wasm/bridge.ts +5 -0
  186. package/src/core/websocket/useMarimoWebSocket.tsx +9 -2
  187. package/src/custom.d.ts +1 -1
  188. package/src/hooks/useCellRenderCount.ts +1 -0
  189. package/src/hooks/useResizeHandle.ts +4 -1
  190. package/src/plugins/core/RenderHTML.tsx +1 -2
  191. package/src/plugins/core/registerReactComponent.tsx +23 -19
  192. package/src/plugins/impl/DataTablePlugin.tsx +18 -6
  193. package/src/plugins/impl/FileUploadPlugin.tsx +1 -1
  194. package/src/plugins/impl/RefreshPlugin.tsx +1 -1
  195. package/src/plugins/impl/SliderPlugin.tsx +4 -0
  196. package/src/plugins/impl/anywidget/AnyWidgetPlugin.tsx +27 -9
  197. package/src/plugins/impl/anywidget/__tests__/AnyWidgetPlugin.test.tsx +58 -2
  198. package/src/plugins/impl/anywidget/__tests__/model.test.ts +3 -4
  199. package/src/plugins/impl/anywidget/model.ts +2 -3
  200. package/src/plugins/impl/data-editor/types.ts +1 -1
  201. package/src/plugins/impl/data-explorer/components/query-form.tsx +1 -1
  202. package/src/plugins/impl/data-frames/DataFramePlugin.tsx +17 -5
  203. package/src/plugins/impl/data-frames/types.ts +1 -1
  204. package/src/plugins/impl/panel/PanelPlugin.tsx +2 -2
  205. package/src/plugins/impl/plotly/PlotlyPlugin.tsx +3 -3
  206. package/src/plugins/impl/vega/__tests__/loader.test.ts +2 -2
  207. package/src/plugins/impl/vega/loader.ts +1 -1
  208. package/src/plugins/impl/vega/vega-component.tsx +1 -1
  209. package/src/plugins/impl/vega/vega-loader.ts +2 -2
  210. package/src/plugins/layout/NavigationMenuPlugin.tsx +1 -1
  211. package/src/plugins/layout/RoutesPlugin.tsx +1 -2
  212. package/src/plugins/plugins.ts +2 -2
  213. package/src/stories/dataframe.stories.tsx +2 -0
  214. package/src/utils/Logger.ts +1 -1
  215. package/src/utils/__tests__/data-views.test.ts +30 -68
  216. package/src/utils/__tests__/dom.test.ts +167 -0
  217. package/src/utils/__tests__/id-tree.test.ts +49 -1
  218. package/src/utils/__tests__/storage.test.ts +1 -1
  219. package/src/utils/__tests__/traceback.test.ts +13 -2
  220. package/src/utils/arrays.ts +1 -1
  221. package/src/utils/createReducer.ts +1 -5
  222. package/src/utils/data-views.ts +6 -19
  223. package/src/utils/dom.ts +55 -0
  224. package/src/utils/edit-distance.ts +1 -1
  225. package/src/utils/fileToBase64.ts +1 -1
  226. package/src/utils/id-tree.tsx +20 -18
  227. package/src/utils/json/base64.ts +13 -0
  228. package/src/utils/json/json-parser.ts +2 -2
  229. package/src/utils/lru.ts +4 -0
  230. package/src/utils/mergeRefs.ts +1 -1
  231. package/src/utils/objects.ts +3 -3
  232. package/src/utils/pluralize.ts +1 -1
  233. package/src/utils/routes.ts +2 -2
  234. package/src/utils/sets.ts +1 -1
  235. package/src/utils/traceback.ts +45 -15
  236. package/src/utils/tracer.ts +11 -9
  237. package/dist/index-DW0BCGJE.js +0 -40315
  238. package/src/__tests__/lru.test.ts +0 -74
@@ -192,8 +192,24 @@ describe("generateDatabaseCode", () => {
192
192
  },
193
193
  };
194
194
 
195
+ const databricksConnection: DatabaseConnection = {
196
+ type: "databricks",
197
+ access_token: "my_access_token",
198
+ server_hostname: "localhost",
199
+ http_path: "http://localhost:8080",
200
+ };
201
+
202
+ const databricksWithCatalogSchema: DatabaseConnection = {
203
+ type: "databricks",
204
+ access_token: "my_access_token",
205
+ server_hostname: "localhost",
206
+ http_path: "http://localhost:8080",
207
+ catalog: "my_catalog",
208
+ schema: "my_schema",
209
+ };
210
+
195
211
  describe("basic connections", () => {
196
- const testCases: Array<[string, DatabaseConnection, ConnectionLibrary]> = [
212
+ const testCases: [string, DatabaseConnection, ConnectionLibrary][] = [
197
213
  ["postgres with SQLModel", basePostgres, "sqlmodel"],
198
214
  ["postgres with SQLAlchemy", basePostgres, "sqlalchemy"],
199
215
  ["mysql with SQLModel", baseMysql, "sqlmodel"],
@@ -218,9 +234,16 @@ describe("generateDatabaseCode", () => {
218
234
  ["pyspark with session", pysparkConnSession, "ibis"],
219
235
  ["redshift with DB credentials", redshiftDBConnection, "redshift"],
220
236
  ["redshift with IAM credentials", redshiftIAMConnection, "redshift"],
237
+ ["databricks", databricksConnection, "sqlalchemy"],
238
+ [
239
+ "databricks with catalog and schema",
240
+ databricksWithCatalogSchema,
241
+ "sqlalchemy",
242
+ ],
243
+ ["databricks with ibis", databricksConnection, "ibis"],
221
244
  ];
222
245
 
223
- it.each(testCases)("%s", (name, connection, orm) => {
246
+ it.each(testCases)("%s", (_name, connection, orm) => {
224
247
  expect(generateDatabaseCode(connection, orm)).toMatchSnapshot();
225
248
  });
226
249
  });
@@ -292,7 +315,7 @@ describe("generateDatabaseCode", () => {
292
315
  },
293
316
  "duckdb",
294
317
  ],
295
- ])("%s", (name, connection, orm) => {
318
+ ])("%s", (_name, connection, orm) => {
296
319
  expect(
297
320
  generateDatabaseCode(connection, orm as ConnectionLibrary),
298
321
  ).toMatchSnapshot();
@@ -300,7 +323,7 @@ describe("generateDatabaseCode", () => {
300
323
  });
301
324
 
302
325
  describe("edge cases", () => {
303
- const testCases: Array<[string, DatabaseConnection, string]> = [
326
+ const testCases: [string, DatabaseConnection, string][] = [
304
327
  [
305
328
  "ENV with special chars SQLModel",
306
329
  {
@@ -504,7 +527,7 @@ describe("generateDatabaseCode", () => {
504
527
  ],
505
528
  ];
506
529
 
507
- it.each(testCases)("%s", (name, connection, orm) => {
530
+ it.each(testCases)("%s", (_name, connection, orm) => {
508
531
  expect(
509
532
  generateDatabaseCode(connection, orm as ConnectionLibrary),
510
533
  ).toMatchSnapshot();
@@ -548,7 +571,7 @@ describe("generateDatabaseCode", () => {
548
571
  credentials_json: '{"type": "service_account", "project_id": "test"',
549
572
  },
550
573
  ],
551
- ])("%s", (name, connection) => {
574
+ ])("%s", (_name, connection) => {
552
575
  expect(generateDatabaseCode(connection, "sqlmodel")).toMatchSnapshot();
553
576
  expect(generateDatabaseCode(connection, "sqlalchemy")).toMatchSnapshot();
554
577
  });
@@ -591,7 +614,7 @@ describe("generateDatabaseCode", () => {
591
614
  "sqlmodel",
592
615
  ),
593
616
  ],
594
- ])("%s", (name, fn) => {
617
+ ])("%s", (_name, fn) => {
595
618
  expect(fn).toThrow();
596
619
  });
597
620
  });
@@ -39,6 +39,7 @@ import {
39
39
  ChdbConnectionSchema,
40
40
  ClickhouseConnectionSchema,
41
41
  type DatabaseConnection,
42
+ DatabricksConnectionSchema,
42
43
  DataFusionConnectionSchema,
43
44
  DuckDBConnectionSchema,
44
45
  IcebergConnectionSchema,
@@ -210,6 +211,16 @@ const DATABASES = [
210
211
  preferred: "redshift",
211
212
  },
212
213
  },
214
+ {
215
+ name: "Databricks",
216
+ schema: DatabricksConnectionSchema,
217
+ color: "#c41e0c",
218
+ logo: "databricks",
219
+ connectionLibraries: {
220
+ libraries: ["sqlalchemy", "sqlmodel", "ibis"],
221
+ preferred: "sqlalchemy",
222
+ },
223
+ },
213
224
  ] satisfies ConnectionSchema[];
214
225
 
215
226
  const DATA_CATALOGS = [
@@ -14,7 +14,8 @@ export type ConnectionLibrary =
14
14
  | "pyiceberg"
15
15
  | "ibis"
16
16
  | "motherduck"
17
- | "redshift";
17
+ | "redshift"
18
+ | "databricks";
18
19
 
19
20
  export const ConnectionDisplayNames: Record<ConnectionLibrary, string> = {
20
21
  sqlmodel: "SQLModel",
@@ -26,6 +27,7 @@ export const ConnectionDisplayNames: Record<ConnectionLibrary, string> = {
26
27
  ibis: "Ibis",
27
28
  motherduck: "MotherDuck",
28
29
  redshift: "Redshift",
30
+ databricks: "Databricks",
29
31
  };
30
32
 
31
33
  abstract class CodeGenerator<T extends DatabaseConnection["type"]> {
@@ -55,6 +57,9 @@ abstract class CodeGenerator<T extends DatabaseConnection["type"]> {
55
57
  case "duckdb":
56
58
  imports.add("import duckdb");
57
59
  break;
60
+ case "ibis":
61
+ imports.add("import ibis");
62
+ break;
58
63
  }
59
64
  return imports;
60
65
  }
@@ -138,10 +143,38 @@ class SecretContainer {
138
143
  return `{${value}}`;
139
144
  }
140
145
 
146
+ /**
147
+ * Generate a password variable for connection strings, supporting inline values,
148
+ * environment variable lookups, and f-string formatting.
149
+ * @param {string|undefined} password - Fallback password value.
150
+ * @param {string} passwordPlaceholder - Environment variable name.
151
+ * @param {boolean} inFString - If true, wrap for Python f-string.
152
+ * @param {string} [variableName] - Variable name (default: "password").
153
+ * @returns {string}
154
+ *
155
+ * @example
156
+ * `printPassword("token123", "DATABRICKS_ACCESS_TOKEN", true, "access_token")`
157
+ *
158
+ * Returns:
159
+ * ```python
160
+ * _access_token = os.environ.get("DATABRICKS_ACCESS_TOKEN", "token123")
161
+ * f"db://sample:{_access_token}@sample.com"
162
+ * ```
163
+ *
164
+ * @example
165
+ * `printPassword("token123", "DATABRICKS_ACCESS_TOKEN", false, "access_token")`
166
+ *
167
+ * Returns:
168
+ * ```python
169
+ * access_token = os.environ.get("DATABRICKS_ACCESS_TOKEN", "token123")
170
+ * f"db://sample:access_token@sample.com"
171
+ * ```
172
+ */
141
173
  printPassword(
142
174
  password: string | undefined,
143
175
  passwordPlaceholder: string,
144
176
  inFString: boolean,
177
+ variableName?: string,
145
178
  ): string {
146
179
  // Inline passwords should use printInFString, otherwise use print
147
180
  const printMethod = inFString
@@ -149,8 +182,8 @@ class SecretContainer {
149
182
  : this.print.bind(this);
150
183
 
151
184
  return isSecret(password)
152
- ? printMethod("password", password)
153
- : printMethod("password", passwordPlaceholder, password);
185
+ ? printMethod(variableName || "password", password)
186
+ : printMethod(variableName || "password", passwordPlaceholder, password);
154
187
  }
155
188
 
156
189
  getSecrets(): Record<string, string> {
@@ -557,7 +590,7 @@ class PyIcebergGenerator extends CodeGenerator<"iceberg"> {
557
590
  class DataFusionGenerator extends CodeGenerator<"datafusion"> {
558
591
  generateImports(): string[] {
559
592
  // To trigger installation of ibis-datafusion
560
- return ["import ibis", "from datafusion import SessionContext"];
593
+ return ["from datafusion import SessionContext"];
561
594
  }
562
595
 
563
596
  generateConnectionCode(): string {
@@ -578,7 +611,7 @@ class DataFusionGenerator extends CodeGenerator<"datafusion"> {
578
611
 
579
612
  class PySparkGenerator extends CodeGenerator<"pyspark"> {
580
613
  generateImports(): string[] {
581
- return ["import ibis", "from pyspark.sql import SparkSession"];
614
+ return ["from pyspark.sql import SparkSession"];
582
615
  }
583
616
 
584
617
  generateConnectionCode(): string {
@@ -668,6 +701,70 @@ ${formatUrlParams(params, (inner) => ` ${inner}`)},
668
701
  }
669
702
  }
670
703
 
704
+ class DatabricksGenerator extends CodeGenerator<"databricks"> {
705
+ generateImports(): string[] {
706
+ return [];
707
+ }
708
+
709
+ generateConnectionCode(): string {
710
+ const useFString = this.orm !== "ibis";
711
+
712
+ const accessToken = this.secrets.printPassword(
713
+ this.connection.access_token,
714
+ "DATABRICKS_ACCESS_TOKEN",
715
+ useFString,
716
+ "access_token",
717
+ );
718
+
719
+ const serverHostname = this.secrets.printPassword(
720
+ this.connection.server_hostname,
721
+ "DATABRICKS_SERVER_HOSTNAME",
722
+ useFString,
723
+ "server_hostname",
724
+ );
725
+
726
+ const httpPath = this.secrets.printPassword(
727
+ this.connection.http_path,
728
+ "DATABRICKS_HTTP_PATH",
729
+ useFString,
730
+ "http_path",
731
+ );
732
+
733
+ const catalog = this.connection.catalog
734
+ ? this.secrets.printInFString("catalog", this.connection.catalog)
735
+ : undefined;
736
+ const schema = this.connection.schema
737
+ ? this.secrets.printInFString("schema", this.connection.schema)
738
+ : undefined;
739
+
740
+ let BASE_URL = `databricks://token:${accessToken}@${serverHostname}?http_path=${httpPath}`;
741
+ if (catalog) {
742
+ BASE_URL += `&catalog=${catalog}`;
743
+ }
744
+ if (schema) {
745
+ BASE_URL += `&schema=${schema}`;
746
+ }
747
+
748
+ if (this.orm === "ibis") {
749
+ const catalogParam = catalog ? `\n catalog=${catalog},` : "";
750
+ const schemaParam = schema ? `\n schema=${schema},` : "";
751
+
752
+ return dedent(`
753
+ engine = ibis.databricks.connect(
754
+ server_hostname=${serverHostname},
755
+ http_path=${httpPath},${catalogParam}${schemaParam}
756
+ access_token=${accessToken}
757
+ )
758
+ `);
759
+ }
760
+
761
+ return dedent(`
762
+ DATABASE_URL = f"${BASE_URL}"
763
+ engine = ${this.orm}.create_engine(DATABASE_URL)
764
+ `);
765
+ }
766
+ }
767
+
671
768
  class CodeGeneratorFactory {
672
769
  public secrets = new SecretContainer();
673
770
 
@@ -706,6 +803,8 @@ class CodeGeneratorFactory {
706
803
  return new PySparkGenerator(connection, orm, this.secrets);
707
804
  case "redshift":
708
805
  return new RedshiftGenerator(connection, orm, this.secrets);
806
+ case "databricks":
807
+ return new DatabricksGenerator(connection, orm, this.secrets);
709
808
  default:
710
809
  assertNever(connection);
711
810
  }
@@ -16,18 +16,16 @@ function passwordField() {
16
16
  );
17
17
  }
18
18
 
19
- function tokenField() {
20
- return z
21
- .string()
22
- .optional()
23
- .describe(
24
- FieldOptions.of({
25
- label: "Token",
26
- inputType: "password",
27
- placeholder: "token",
28
- optionRegex: ".*token.*",
29
- }),
30
- );
19
+ function tokenField(label?: string, required?: boolean) {
20
+ const field = z.string().describe(
21
+ FieldOptions.of({
22
+ label: label || "Token",
23
+ inputType: "password",
24
+ placeholder: "token",
25
+ optionRegex: ".*token.*",
26
+ }),
27
+ );
28
+ return required ? field.nonempty() : field.optional();
31
29
  }
32
30
 
33
31
  function warehouseNameField() {
@@ -43,20 +41,22 @@ function warehouseNameField() {
43
41
  );
44
42
  }
45
43
 
46
- function uriField() {
47
- return z
44
+ function uriField(label?: string, required?: boolean) {
45
+ const field = z
48
46
  .string()
49
- .optional()
50
- .describe(FieldOptions.of({ label: "URI", optionRegex: ".*uri.*" }));
47
+ .describe(
48
+ FieldOptions.of({ label: label || "URI", optionRegex: ".*uri.*" }),
49
+ );
50
+ return required ? field.nonempty() : field.optional();
51
51
  }
52
52
 
53
- function hostField() {
53
+ function hostField(label?: string) {
54
54
  return z
55
55
  .string()
56
56
  .nonempty()
57
57
  .describe(
58
58
  FieldOptions.of({
59
- label: "Host",
59
+ label: label || "Host",
60
60
  placeholder: "localhost",
61
61
  optionRegex: ".*host.*",
62
62
  }),
@@ -464,6 +464,23 @@ export const RedshiftConnectionSchema = z
464
464
  })
465
465
  .describe(FieldOptions.of({ direction: "two-columns" }));
466
466
 
467
+ export const DatabricksConnectionSchema = z
468
+ .object({
469
+ type: z.literal("databricks"),
470
+ access_token: tokenField("Access Token", true),
471
+ server_hostname: hostField("Server Hostname"),
472
+ http_path: uriField("HTTP Path", true),
473
+ catalog: z
474
+ .string()
475
+ .optional()
476
+ .describe(FieldOptions.of({ label: "Catalog" })),
477
+ schema: z
478
+ .string()
479
+ .optional()
480
+ .describe(FieldOptions.of({ label: "Schema" })),
481
+ })
482
+ .describe(FieldOptions.of({ direction: "two-columns" }));
483
+
467
484
  export const DatabaseConnectionSchema = z.discriminatedUnion("type", [
468
485
  PostgresConnectionSchema,
469
486
  MySQLConnectionSchema,
@@ -480,6 +497,7 @@ export const DatabaseConnectionSchema = z.discriminatedUnion("type", [
480
497
  DataFusionConnectionSchema,
481
498
  PySparkConnectionSchema,
482
499
  RedshiftConnectionSchema,
500
+ DatabricksConnectionSchema,
483
501
  ]);
484
502
 
485
503
  export type DatabaseConnection = z.infer<typeof DatabaseConnectionSchema>;
@@ -1,24 +1,33 @@
1
1
  /* Copyright 2024 Marimo. All rights reserved. */
2
2
 
3
+ import { useAtomValue, useSetAtom } from "jotai";
3
4
  import { WrenchIcon } from "lucide-react";
4
5
  import { Button } from "@/components/ui/button";
5
6
  import { Tooltip } from "@/components/ui/tooltip";
7
+ import { aiCompletionCellAtom } from "@/core/ai/state";
6
8
  import { notebookAtom, useCellActions } from "@/core/cells/cells";
7
9
  import type { CellId } from "@/core/cells/ids";
10
+ import { aiEnabledAtom } from "@/core/config/config";
8
11
  import { getAutoFixes } from "@/core/errors/errors";
9
12
  import type { MarimoError } from "@/core/kernel/messages";
10
13
  import { store } from "@/core/state/jotai";
14
+ import { cn } from "@/utils/cn";
11
15
 
12
16
  export const AutoFixButton = ({
13
17
  errors,
14
18
  cellId,
19
+ className,
15
20
  }: {
16
21
  errors: MarimoError[];
17
22
  cellId: CellId;
18
23
  className?: string;
19
24
  }) => {
20
25
  const { createNewCell } = useCellActions();
21
- const autoFixes = errors.flatMap((error) => getAutoFixes(error));
26
+ const aiEnabled = useAtomValue(aiEnabledAtom);
27
+ const autoFixes = errors.flatMap((error) =>
28
+ getAutoFixes(error, { aiEnabled }),
29
+ );
30
+ const setAiCompletionCell = useSetAtom(aiCompletionCellAtom);
22
31
 
23
32
  if (autoFixes.length === 0) {
24
33
  return null;
@@ -33,7 +42,7 @@ export const AutoFixButton = ({
33
42
  <Button
34
43
  size="xs"
35
44
  variant="outline"
36
- className="my-2 font-normal"
45
+ className={cn("my-2 font-normal", className)}
37
46
  onClick={() => {
38
47
  const editorView =
39
48
  store.get(notebookAtom).cellHandles[cellId].current?.editorView;
@@ -48,6 +57,7 @@ export const AutoFixButton = ({
48
57
  },
49
58
  editor: editorView,
50
59
  cellId: cellId,
60
+ setAiCompletionCell,
51
61
  });
52
62
  // Focus the editor
53
63
  editorView?.focus();
@@ -0,0 +1,40 @@
1
+ /* Copyright 2024 Marimo. All rights reserved. */
2
+
3
+ import { AlertCircleIcon } from "lucide-react";
4
+ import type { CellId } from "@/core/cells/ids";
5
+ import { useSqlValidationErrorsForCell } from "@/core/codemirror/language/languages/sql/banner-validation-errors";
6
+
7
+ export const SqlValidationErrorBanner = ({
8
+ cellId,
9
+ hide,
10
+ }: {
11
+ cellId: CellId;
12
+ hide: boolean;
13
+ }) => {
14
+ const error = useSqlValidationErrorsForCell(cellId);
15
+
16
+ if (!error || hide) {
17
+ return;
18
+ }
19
+
20
+ return (
21
+ <div className="p-3 text-sm flex flex-col text-muted-foreground gap-1.5 bg-destructive/4">
22
+ <div className="flex items-start gap-1.5">
23
+ <AlertCircleIcon size={13} className="mt-[3px] text-destructive" />
24
+ <p>
25
+ <span className="font-bold text-destructive">{error.errorType}:</span>{" "}
26
+ <span className="whitespace-pre-wrap">{error.errorMessage}</span>
27
+ </p>
28
+ </div>
29
+
30
+ {error.codeblock && (
31
+ <pre
32
+ lang="sql"
33
+ className="text-xs bg-muted/80 rounded p-2 pb-0 mx-3 font-medium whitespace-pre-wrap"
34
+ >
35
+ {error.codeblock}
36
+ </pre>
37
+ )}
38
+ </div>
39
+ );
40
+ };
@@ -11,9 +11,9 @@ import { Logger } from "@/utils/Logger";
11
11
  const MARIMO_CELL_MIMETYPE = "web application/x-marimo-cell";
12
12
 
13
13
  interface ClipboardCellData {
14
- cells: Array<{
14
+ cells: {
15
15
  code: string;
16
- }>;
16
+ }[];
17
17
  version: "1.0";
18
18
  }
19
19
 
@@ -14,6 +14,7 @@ import type { OutputMessage } from "@/core/kernel/messages";
14
14
  import { useSelectAllContent } from "@/hooks/useSelectAllContent";
15
15
  import { cn } from "@/utils/cn";
16
16
  import { copyToClipboard } from "@/utils/copy";
17
+ import { ansiToPlainText, parseHtmlContent } from "@/utils/dom";
17
18
  import { invariant } from "@/utils/invariant";
18
19
  import { Strings } from "@/utils/strings";
19
20
  import { NameCellContentEditable } from "../actions/name-cell-input";
@@ -27,7 +28,7 @@ interface Props {
27
28
  cellId: CellId;
28
29
  cellName: string;
29
30
  className?: string;
30
- consoleOutputs: Array<WithResponse<OutputMessage>>;
31
+ consoleOutputs: WithResponse<OutputMessage>[];
31
32
  stale: boolean;
32
33
  debuggerActive: boolean;
33
34
  onRefactorWithAI?: (opts: { prompt: string }) => void;
@@ -124,7 +125,18 @@ const ConsoleOutputInternal = (props: Props): React.ReactNode => {
124
125
  onClick={() => {
125
126
  const text = reversedOutputs
126
127
  .filter((output) => output.channel !== "pdb")
127
- .map((output) => Strings.asString(output.data))
128
+ .map((output) => {
129
+ // If starts with `<`, then assume it's HTML
130
+ if (
131
+ typeof output.data === "string" &&
132
+ output.data.startsWith("<")
133
+ ) {
134
+ return parseHtmlContent(output.data);
135
+ }
136
+
137
+ // Otherwise, convert the ANSI to HTML, then parse as HTML
138
+ return ansiToPlainText(Strings.asString(output.data));
139
+ })
128
140
  .join("\n");
129
141
  void copyToClipboard(text);
130
142
  }}
@@ -200,7 +200,7 @@ const LEAF_RENDERERS = {
200
200
  };
201
201
 
202
202
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
203
- const MIME_TYPES: Array<DataType<any>> = Object.entries(LEAF_RENDERERS).map(
203
+ const MIME_TYPES: DataType<any>[] = Object.entries(LEAF_RENDERERS).map(
204
204
  ([leafType, render]) => ({
205
205
  is: (value) => typeof value === "string" && value.startsWith(leafType),
206
206
  PostComponent: PyCopyButton,
@@ -1,6 +1,10 @@
1
1
  /* Copyright 2024 Marimo. All rights reserved. */
2
2
 
3
- import { NotebookPenIcon, SquareArrowOutUpRightIcon } from "lucide-react";
3
+ import {
4
+ InfoIcon,
5
+ NotebookPenIcon,
6
+ SquareArrowOutUpRightIcon,
7
+ } from "lucide-react";
4
8
  import { Fragment, type JSX } from "react";
5
9
  import {
6
10
  Accordion,
@@ -72,6 +76,8 @@ export const MarimoErrorOutput = ({
72
76
  titleContents = "Ancestor stopped";
73
77
  alertVariant = "default";
74
78
  titleColor = "text-secondary-foreground";
79
+ } else if (errors.some((e) => e.type === "sql-error")) {
80
+ titleContents = "SQL error";
75
81
  } else {
76
82
  // Check for exception type
77
83
  const exceptionError = errors.find((e) => e.type === "exception");
@@ -126,6 +132,10 @@ export const MarimoErrorOutput = ({
126
132
  const unknownErrors = errors.filter(
127
133
  (e): e is Extract<MarimoError, { type: "unknown" }> => e.type === "unknown",
128
134
  );
135
+ const sqlErrors = errors.filter(
136
+ (e): e is Extract<MarimoError, { type: "sql-error" }> =>
137
+ e.type === "sql-error",
138
+ );
129
139
 
130
140
  const openScratchpad = () => {
131
141
  chromeActions.openApplication("scratchpad");
@@ -485,6 +495,55 @@ export const MarimoErrorOutput = ({
485
495
  );
486
496
  }
487
497
 
498
+ if (sqlErrors.length > 0) {
499
+ messages.push(
500
+ <div key="sql-errors">
501
+ {sqlErrors.map((error, idx) => {
502
+ const line =
503
+ error.sql_line == null ? null : Math.trunc(error?.sql_line) + 1;
504
+ const col =
505
+ error.sql_col == null ? null : Math.trunc(error?.sql_col) + 1;
506
+ return (
507
+ <div key={`sql-error-${idx}`} className="space-y-2 mt-2">
508
+ <p className="text-muted-foreground font-medium">{error.msg}</p>
509
+ {error.hint && (
510
+ <div className="flex items-start gap-2">
511
+ <InfoIcon
512
+ size={11}
513
+ className="text-muted-foreground mt-1 flex-shrink-0"
514
+ />
515
+ <p className="whitespace-pre-wrap text-sm text-muted-foreground">
516
+ {error.hint}
517
+ </p>
518
+ </div>
519
+ )}
520
+ {error.sql_statement && (
521
+ <pre
522
+ lang="sql"
523
+ className="text-xs bg-muted/80 rounded whitespace-pre-wrap p-3.5"
524
+ >
525
+ {error.sql_statement.trim()}
526
+ </pre>
527
+ )}
528
+ {line !== null && col !== null && (
529
+ <p className="text-xs text-muted-foreground">
530
+ Error at line {line}, column {col}
531
+ </p>
532
+ )}
533
+ </div>
534
+ );
535
+ })}
536
+ {cellId && (
537
+ <AutoFixButton
538
+ errors={sqlErrors}
539
+ cellId={cellId}
540
+ className="mt-2.5"
541
+ />
542
+ )}
543
+ </div>,
544
+ );
545
+ }
546
+
488
547
  return messages;
489
548
  };
490
549
 
@@ -108,7 +108,7 @@ export const MarimoTracebackOutput = ({
108
108
  Fix with AI
109
109
  </Button>
110
110
  )}
111
- {tracebackInfo && !isWasm() && (
111
+ {tracebackInfo && tracebackInfo.kind === "cell" && !isWasm() && (
112
112
  <Tooltip content={"Attach pdb to the exception point."}>
113
113
  <Button
114
114
  size="xs"
@@ -180,7 +180,7 @@ function lastLine(text: string): string {
180
180
 
181
181
  export const replaceTracebackFilenames = (domNode: DOMNode) => {
182
182
  const info = getTracebackInfo(domNode);
183
- if (info) {
183
+ if (info?.kind === "cell") {
184
184
  const tooltipContent = <InsertBreakpointContent />;
185
185
  return (
186
186
  <span className="nb">
@@ -211,6 +211,21 @@ export const replaceTracebackFilenames = (domNode: DOMNode) => {
211
211
  </span>
212
212
  );
213
213
  }
214
+ if (info?.kind === "file") {
215
+ return (
216
+ <div
217
+ className="inline-block cursor-pointer text-destructive hover:underline"
218
+ onClick={(_) => {
219
+ getRequestClient().openFile({
220
+ path: info.filePath,
221
+ lineNumber: info.lineNumber,
222
+ });
223
+ }}
224
+ >
225
+ <span className="nb">"{info.filePath}"</span>
226
+ </div>
227
+ );
228
+ }
214
229
  };
215
230
 
216
231
  export const replaceTracebackPrefix = (domNode: DOMNode) => {
@@ -68,13 +68,13 @@ export interface GridLayout extends Omit<SerializedGridLayout, "cells"> {
68
68
  /**
69
69
  * The cells in the layout.
70
70
  */
71
- cells: Array<{
71
+ cells: {
72
72
  i: string;
73
73
  x: number;
74
74
  y: number;
75
75
  w: number;
76
76
  h: number;
77
- }>;
77
+ }[];
78
78
 
79
79
  scrollableCells: Set<CellId>;
80
80
 
@@ -8,7 +8,7 @@ import { VerticalLayoutPlugin } from "./vertical-layout/vertical-layout";
8
8
 
9
9
  // If more renderers are added, we may want to consider lazy loading them.
10
10
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
11
- export const cellRendererPlugins: Array<ICellRendererPlugin<any, any>> = [
11
+ export const cellRendererPlugins: ICellRendererPlugin<any, any>[] = [
12
12
  GridLayoutPlugin,
13
13
  SlidesLayoutPlugin,
14
14
  VerticalLayoutPlugin,