@marimo-team/frontend 0.23.17-dev4 → 0.23.17-dev40

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 (315) hide show
  1. package/dist/assets/{CellStatus-DIW20_Z8.js → CellStatus-XlxL4ctu.js} +1 -1
  2. package/dist/assets/{ConnectedDataExplorerComponent-C4zEgCZh.js → ConnectedDataExplorerComponent-BF5CJvB2.js} +1 -1
  3. package/dist/assets/{ErrorBoundary-DzTD8LVq.js → ErrorBoundary-BfMsWVQU.js} +1 -1
  4. package/dist/assets/ImageComparisonComponent-CukPDP1p.js +1 -0
  5. package/dist/assets/{ImperativeModal-fY-ZfT0b.js → ImperativeModal-Ci0icDPt.js} +1 -1
  6. package/dist/assets/{JsonOutput-Bk2exFh0.js → JsonOutput-CWfu9qbN.js} +13 -13
  7. package/dist/assets/{LazyAnyLanguageCodeMirror-A-Ant1Ad.js → LazyAnyLanguageCodeMirror-Dcvy_p3-.js} +2 -2
  8. package/dist/assets/{MarimoErrorOutput-L_o2Knzo.js → MarimoErrorOutput-B9QSRg6T.js} +5 -5
  9. package/dist/assets/{RSPContexts-WiCgGm__.js → RSPContexts-IrDQoFki.js} +1 -1
  10. package/dist/assets/{RenderHTML-BTDLQMKg.js → RenderHTML-CQC8I348.js} +1 -1
  11. package/dist/assets/{RunButton-DiHX9YjB.js → RunButton-DRxV8u_T.js} +1 -1
  12. package/dist/assets/add-cell-with-ai-GZi9Icll.js +107 -0
  13. package/dist/assets/{add-connection-dialog-BZaJAQCM.js → add-connection-dialog-D2WiF1f8.js} +31 -31
  14. package/dist/assets/{agent-panel-CRxiqCgP.js → agent-panel-BWIkm6ln.js} +6 -6
  15. package/dist/assets/ai-model-dropdown-BmcXWjub.js +5 -0
  16. package/dist/assets/{alert-dialog-Bb8Lz8ko.js → alert-dialog-DMh8_LWc.js} +1 -1
  17. package/dist/assets/{any-language-editor-BIeaxtdD.js → any-language-editor-DUQN91an.js} +1 -1
  18. package/dist/assets/{app-config-button-BS4LSo1l.js → app-config-button-QHaORuxs.js} +1 -1
  19. package/dist/assets/{blob-B9eg-RfN.js → blob-BzgGkt3B.js} +1 -1
  20. package/dist/assets/{button-DDGiD1NW.js → button-BB1AV4Rb.js} +1 -1
  21. package/dist/assets/{cache-panel-B2B1ePao.js → cache-panel-hpL5o4Y7.js} +1 -1
  22. package/dist/assets/{capabilities-DoEg0XwL.js → capabilities-LI6_PoMn.js} +1 -1
  23. package/dist/assets/{cell-editor-OC7h_nZ3.js → cell-editor-Cwor7oUx.js} +11 -11
  24. package/dist/assets/{cell-link-D0aELsL_.js → cell-link-uPmFMRTh.js} +1 -1
  25. package/dist/assets/cells-BE1kpmPF.css +2 -0
  26. package/dist/assets/{cells-DLYs3NiJ.js → cells-DxL32Atg.js} +60 -60
  27. package/dist/assets/chat-display-CH-dFAC3.js +1 -0
  28. package/dist/assets/chat-panel-DLgorYjE.js +4 -0
  29. package/dist/assets/chat-ui-DDkwFM3p.js +4 -0
  30. package/dist/assets/{column-preview-CnqeJb-x.js → column-preview-Dw1ASLnm.js} +1 -1
  31. package/dist/assets/{command-Dqxie4gq.js → command-CPFHYoYk.js} +1 -1
  32. package/dist/assets/command-palette--Mo9OaH2.js +1 -0
  33. package/dist/assets/{common-D_beLbpm.js → common-aHaDUD0-.js} +1 -1
  34. package/dist/assets/{components-CxEGXaTB.js → components-BQ3c1B_F.js} +1 -1
  35. package/dist/assets/components-Bm1d4vm9.js +1 -0
  36. package/dist/assets/config-Bm5B9wGP.js +1 -0
  37. package/dist/assets/{config-BJVx3eBW.js → config-Cbkt6Rb1.js} +1 -1
  38. package/dist/assets/{copy-Bxb1_Gtw.js → copy-B-LLR1Yy.js} +1 -1
  39. package/dist/assets/{copy-icon-neDQUo4Q.js → copy-icon-DWG8Y-xW.js} +1 -1
  40. package/dist/assets/{createReducer-DGx5Af-C.js → createReducer-DNehfY0t.js} +1 -1
  41. package/dist/assets/{datasource-Bfg8PKXp.js → datasource-DfQvKiJ1.js} +2 -2
  42. package/dist/assets/{dates-C2LWzsCZ.js → dates-BzOh4kS_.js} +1 -1
  43. package/dist/assets/defaultLocale-t77eoiwF.js +1 -0
  44. package/dist/assets/{dependency-graph-panel-CB5ie0zu.js → dependency-graph-panel-gbgUGtYS.js} +2 -2
  45. package/dist/assets/{diagram-PSM6KHXK-DIXxW5au.js → diagram-PSM6KHXK-CrTUa1Xy.js} +1 -1
  46. package/dist/assets/{dialog-BCVepWLJ.js → dialog-88bLE398.js} +1 -1
  47. package/dist/assets/{dist-CPjTeT8p.js → dist-6qzebh8f.js} +1 -1
  48. package/dist/assets/dist-BLqRx0W-.js +1 -0
  49. package/dist/assets/{dist-BP4hpPAE.js → dist-BVr4HQPR.js} +1 -1
  50. package/dist/assets/{dist-DnjsPBRJ.js → dist-BWXIz8tq.js} +1 -1
  51. package/dist/assets/{dist-Cros4IAo.js → dist-BWbS__vR.js} +1 -1
  52. package/dist/assets/dist-BZehnCk2.js +1 -0
  53. package/dist/assets/dist-BaRgF7DU.js +1 -0
  54. package/dist/assets/{dist-DYEuVkEX.js → dist-BblMEWTQ.js} +1 -1
  55. package/dist/assets/dist-BlMeFaGB.js +1 -0
  56. package/dist/assets/{dist-CrKffqgx.js → dist-BmYa2llD.js} +1 -1
  57. package/dist/assets/{dist-9o0Xjd5v.js → dist-Btp8vRFr.js} +1 -1
  58. package/dist/assets/{dist-C-3FVUQA.js → dist-BvMWjDxm.js} +1 -1
  59. package/dist/assets/{dist-DeJBvhAc.js → dist-ByfZ_2N7.js} +1 -1
  60. package/dist/assets/dist-C-g2AKOo.js +1 -0
  61. package/dist/assets/{dist-DQl27SFq.js → dist-C52BqSeO.js} +1 -1
  62. package/dist/assets/{dist-CiVShZKM.js → dist-C7rDfTPJ.js} +1 -1
  63. package/dist/assets/{dist-BKgHjzML.js → dist-C830fIIj.js} +1 -1
  64. package/dist/assets/{dist-fLBYOt9R.js → dist-CHN_sQZw.js} +1 -1
  65. package/dist/assets/dist-CLFhXj8x.js +1 -0
  66. package/dist/assets/{dist-D47O3FBX.js → dist-CLQWOmD7.js} +1 -1
  67. package/dist/assets/dist-CO4dulTL.js +1 -0
  68. package/dist/assets/dist-CPw3iq_l.js +1 -0
  69. package/dist/assets/dist-CQtN-vVI.js +1 -0
  70. package/dist/assets/dist-CZE9FE_5.js +1 -0
  71. package/dist/assets/dist-CexuM4NA.js +1 -0
  72. package/dist/assets/{dist-DJuRuTXr.js → dist-CfiLpZZj.js} +1 -1
  73. package/dist/assets/dist-CjSt6gRp.js +1 -0
  74. package/dist/assets/dist-Cr4q2U0d.js +1 -0
  75. package/dist/assets/{dist-C6_DArtj.js → dist-CxaoIFrU.js} +1 -1
  76. package/dist/assets/{dist-aRc_dRed.js → dist-D5nYeU7Q.js} +1 -1
  77. package/dist/assets/{dist-BJZAIIze.js → dist-DX04AOlt.js} +1 -1
  78. package/dist/assets/dist-DkZos4_s.js +1 -0
  79. package/dist/assets/{dist-DI1-Z60I.js → dist-DmCNk9gq.js} +1 -1
  80. package/dist/assets/{dist-kLs0VbW_.js → dist-DmjT6iaH.js} +1 -1
  81. package/dist/assets/{dist-CvWC93qL.js → dist-DrY8fZeD.js} +1 -1
  82. package/dist/assets/{dist-l4RTzHX-.js → dist-Drexw1Ir.js} +1 -1
  83. package/dist/assets/{dist-DrBqBDHF.js → dist-DtNSqHzW.js} +1 -1
  84. package/dist/assets/dist-DxQA7Dn1.js +1 -0
  85. package/dist/assets/{dist-BLN8PNJV.js → dist-Dza6FJtV.js} +1 -1
  86. package/dist/assets/{dist-8SH6SWEG.js → dist-HrXjBuXP.js} +1 -1
  87. package/dist/assets/dist-J00F7mRY.js +1 -0
  88. package/dist/assets/{dist-DcwTQG1A.js → dist-Jbd9zUbK.js} +1 -1
  89. package/dist/assets/{dist-CZvbHnyT.js → dist-bvfx5OQE.js} +1 -1
  90. package/dist/assets/{dist-12S8I4CH.js → dist-gUHullA_.js} +11 -11
  91. package/dist/assets/dist-nYKCNtEX.js +1 -0
  92. package/dist/assets/dist-oK8dffBy.js +1 -0
  93. package/dist/assets/{documentation-panel-Dii7ouzj.js → documentation-panel-DotG4o5W.js} +1 -1
  94. package/dist/assets/{download-DenADH0w.js → download-Tklnz6pn.js} +3 -3
  95. package/dist/assets/{dropdown-menu-dHmZR8bI.js → dropdown-menu-BGBlPKNz.js} +1 -1
  96. package/dist/assets/{edit-page-B0EhuiM-.js → edit-page-DhZPSJ6P.js} +6 -6
  97. package/dist/assets/{error-banner-BSxEOIn2.js → error-banner-BkIr-0dH.js} +1 -1
  98. package/dist/assets/{error-entries-4XYBryls.js → error-entries-BvUR_22-.js} +3 -3
  99. package/dist/assets/{error-panel-xYZ4lX0A.js → error-panel-Upmm4kG5.js} +1 -1
  100. package/dist/assets/{errors-PgHhDa-M.js → errors-DCPV979Q.js} +1 -1
  101. package/dist/assets/{es-gYgfF8y5.js → es-Dl2xAwTv.js} +1 -1
  102. package/dist/assets/{esm-B30N1GS0.js → esm-Bmzgrl_5.js} +1 -1
  103. package/dist/assets/{esm-BDGraAqp.js → esm-Nb9m4lqg.js} +1 -1
  104. package/dist/assets/{field-BsxD35VE.js → field-BFgvUZyh.js} +1 -1
  105. package/dist/assets/file-explorer-panel-BulU96v_.js +37 -0
  106. package/dist/assets/{file-icons-mgXTzM1c.js → file-icons-NkObNkr_.js} +1 -1
  107. package/dist/assets/{file-name-input-Dq7Z_QmU.js → file-name-input-BQJ9U9lU.js} +1 -1
  108. package/dist/assets/{floating-outline-ypOHt2wD.js → floating-outline-BHmregl1.js} +1 -1
  109. package/dist/assets/{focus-0L1nX7M9.js → focus-BBiJct2G.js} +1 -1
  110. package/dist/assets/{form-BsW7zr4t.js → form-BIUQB1NI.js} +1 -1
  111. package/dist/assets/{form-oz4Z280Z.js → form-D-Bo8F9Q.js} +1 -1
  112. package/dist/assets/{formats-DzmLpc9A.js → formats-DNYtnQk7.js} +1 -1
  113. package/dist/assets/{formatting-COz4XEaB.js → formatting-qNOrQYRN.js} +1 -1
  114. package/dist/assets/{fullscreen-CTDqNQr6.js → fullscreen-B51geQ_6.js} +1 -1
  115. package/dist/assets/{gallery-page-DbuxknAw.js → gallery-page-DBHHOraF.js} +1 -1
  116. package/dist/assets/{ganttDiagram-JELNMOA3-DaS60lKW.js → ganttDiagram-JELNMOA3-C9KBLmAA.js} +1 -1
  117. package/dist/assets/{glide-data-editor-CejKKgnN.js → glide-data-editor-CHN4-xUu.js} +1 -1
  118. package/dist/assets/{home-page-BOP1dDcM.js → home-page-Dvm2dFdU.js} +2 -2
  119. package/dist/assets/{html-to-image-D_C-hjtC.js → html-to-image-ChNWEUlR.js} +1 -1
  120. package/dist/assets/index-BPwg3EAk.js +38 -0
  121. package/dist/assets/index-Dttsymhw.css +2 -0
  122. package/dist/assets/{input-JxFwu-3G.js → input-_AaMXWgD.js} +1 -1
  123. package/dist/assets/{kiosk-mode-BvKMuigM.js → kiosk-mode-RTwj6XH4.js} +1 -1
  124. package/dist/assets/{label-CqjrpwRC.js → label-DUD3SZnk.js} +1 -1
  125. package/dist/assets/{layout-yfmf2ZBx.js → layout-D7Yzn4XY.js} +4 -4
  126. package/dist/assets/{linear-BPv4krGt.js → linear-B1ROUUtm.js} +1 -1
  127. package/dist/assets/{links-ClNQSIal.js → links-DBc28bsl.js} +1 -1
  128. package/dist/assets/{logs-panel-OMd1Z9CL.js → logs-panel-Cdcjc_fq.js} +1 -1
  129. package/dist/assets/markdown-export-CNu5ZTPR.js +1 -0
  130. package/dist/assets/{markdown-renderer-BfLNJGNj.js → markdown-renderer-DpskAUxO.js} +1 -1
  131. package/dist/assets/{menu-items-CCi6aOPm.js → menu-items-Db-bgz92.js} +1 -1
  132. package/dist/assets/{mermaid-B8gNHyGy.js → mermaid-OU2hLna0.js} +3 -3
  133. package/dist/assets/{name-cell-input-COtjRghz.js → name-cell-input-DoxL2EXI.js} +1 -1
  134. package/dist/assets/{numbers-rmawRqQP.js → numbers-D9g2ArIv.js} +1 -1
  135. package/dist/assets/{outline-panel-CIGf4SLb.js → outline-panel-BxjI-YSj.js} +1 -1
  136. package/dist/assets/{packages-panel-DLuMuNuG.js → packages-panel-IN-yS3e2.js} +1 -1
  137. package/dist/assets/{pair-with-agent-modal-C5OhJ8rX.js → pair-with-agent-modal-1b5Eh8Xo.js} +1 -1
  138. package/dist/assets/{panel-accordion-state-D1UP17Lk.js → panel-accordion-state-BkWYwckU.js} +1 -1
  139. package/dist/assets/{panels-B0w23XJS.js → panels-C5PIEySE.js} +2 -2
  140. package/dist/assets/{popover-Bcz1Zikb.js → popover-DHbixh3D.js} +1 -1
  141. package/dist/assets/{precisionRound-i2ImZxgc.js → precisionRound-DORAUfVY.js} +1 -1
  142. package/dist/assets/process-output-DJonUYp2.js +1 -0
  143. package/dist/assets/{quadrantDiagram-AYHSOK5B-DJuGB95-.js → quadrantDiagram-AYHSOK5B-Duf8sGaa.js} +1 -1
  144. package/dist/assets/{radio-group-BGXYy3u5.js → radio-group-ByLzIKjm.js} +1 -1
  145. package/dist/assets/react-vega-J-J2akIt.js +191 -0
  146. package/dist/assets/{readonly-code-display-DdABj8tH.js → readonly-code-display-B_ZHScA7.js} +1 -1
  147. package/dist/assets/{readonly-python-code-DUjqXwhi.js → readonly-python-code-C4GeGU7O.js} +1 -1
  148. package/dist/assets/{renderShortcut-Bl8_FVn8.js → renderShortcut-DnqsU4zj.js} +1 -1
  149. package/dist/assets/{reveal-component-BjgfnO81.js → reveal-component-BPaqYFXU.js} +2 -2
  150. package/dist/assets/{run-page-DDty5GvQ.js → run-page-kWVbQ6KV.js} +1 -1
  151. package/dist/assets/{runs-s9t2f_Tg.js → runs-C6avq6Gi.js} +1 -1
  152. package/dist/assets/scratchpad-panel-l1iGto9s.js +1 -0
  153. package/dist/assets/{scroll-to-bottom-button-C8yfT4wq.js → scroll-to-bottom-button-CRMPUjYZ.js} +1 -1
  154. package/dist/assets/{secrets-panel-Cz17_MLf.js → secrets-panel-DmAwH7vI.js} +1 -1
  155. package/dist/assets/{select-BP75_5jJ.js → select-Clmc8Hw2.js} +1 -1
  156. package/dist/assets/{session-BmukRjSi.js → session-BtBr2zr3.js} +1 -1
  157. package/dist/assets/{session-panel-aMoQPTCB.js → session-panel-CBmLYzFL.js} +1 -1
  158. package/dist/assets/{share-852X_cqy.js → share-DSOv_SvY.js} +1 -1
  159. package/dist/assets/{skeleton-CPuDx7yn.js → skeleton-DhhF_mJP.js} +1 -1
  160. package/dist/assets/{snippets-panel-DBDXr-XP.js → snippets-panel-l_HnIWUj.js} +1 -1
  161. package/dist/assets/{spec-C7opZZRp.js → spec-CdDzA5ef.js} +1 -1
  162. package/dist/assets/{state-DCtLlFJq.js → state-B4lC29et.js} +1 -1
  163. package/dist/assets/{state-C4kiwedg.js → state-BkoVfBqt.js} +1 -1
  164. package/dist/assets/{state-Brswj8bD.js → state-COqqdiUM.js} +1 -1
  165. package/dist/assets/{state-hlEqpZfH.js → state-D9xpDCxa.js} +2 -2
  166. package/dist/assets/{strings--BknHX9C.js → strings-DMKwTex-.js} +1 -1
  167. package/dist/assets/{swiper-component-DXW8snlt.js → swiper-component-pSAP3cRf.js} +1 -1
  168. package/dist/assets/switch-8FxRs-OU.js +1 -0
  169. package/dist/assets/{terminal-CaHTVNrz.js → terminal-DPQJ6hNg.js} +1 -1
  170. package/dist/assets/{textarea-DtCcemo9.js → textarea-BzWvGIrJ.js} +1 -1
  171. package/dist/assets/{time-p47i6wvp.js → time-BO-naW1O.js} +1 -1
  172. package/dist/assets/tooltip-BMi4WFDl.js +61 -0
  173. package/dist/assets/{tooltip-4bZlU7jl.js → tooltip-BcgbFR7q.js} +1 -1
  174. package/dist/assets/{tracing-CStM7RGI.js → tracing-DCuWLKnk.js} +1 -1
  175. package/dist/assets/{tracing-panel-BTLzu8L-.js → tracing-panel-dVtThkzL.js} +2 -2
  176. package/dist/assets/{tree-actions-BwwVyp3l.js → tree-actions-o2iyMmKN.js} +1 -1
  177. package/dist/assets/{useBoolean-DIi7jQLa.js → useBoolean-sASJEpGA.js} +1 -1
  178. package/dist/assets/{useCellActionButton-uxVyv3JK.js → useCellActionButton-CrFbi8en.js} +1 -1
  179. package/dist/assets/{useDeleteCell-Cy23OP4e.js → useDeleteCell-DH0weyin.js} +1 -1
  180. package/dist/assets/{useDependencyPanelTab-DNfjp_-X.js → useDependencyPanelTab-OeFV2bye.js} +1 -1
  181. package/dist/assets/{useEventListener-C0195JsF.js → useEventListener-B_wSLgFR.js} +1 -1
  182. package/dist/assets/{useHotkey-DLZvrRZT.js → useHotkey-DJyYmk5P.js} +1 -1
  183. package/dist/assets/{useIframeCapabilities-j6fR_dBA.js → useIframeCapabilities-D_6fB_8O.js} +1 -1
  184. package/dist/assets/{useInstallPackage-WiYBwhUO.js → useInstallPackage-DyNrAhrx.js} +1 -1
  185. package/dist/assets/{useInterval-CnQfyMa9.js → useInterval-CjGsg4ie.js} +1 -1
  186. package/dist/assets/useNotebookActions-CylmZilh.js +12 -0
  187. package/dist/assets/useRunCells-8xYRh7wH.js +1 -0
  188. package/dist/assets/{useSplitCell-BK5mnkkL.js → useSplitCell-BvgvIZaz.js} +1 -1
  189. package/dist/assets/{useTheme-Bxv9B31g.js → useTheme-BR31Gjbc.js} +1 -1
  190. package/dist/assets/utils-C5VWChv9.js +1 -0
  191. package/dist/assets/{vega-component-BGiCExka.js → vega-component-DG9G1qk2.js} +1 -1
  192. package/dist/assets/{vega-embed-container-DxSqUOCK.js → vega-embed-container-BJRQVehC.js} +1 -1
  193. package/dist/assets/vega-loader.browser-CAfmqKEc.js +6 -0
  194. package/dist/assets/{write-secret-modal-Dtm66yVf.js → write-secret-modal-DwCUuNRy.js} +1 -1
  195. package/dist/assets/{xychartDiagram-PRI3JC2R-B8A0hi-Q.js → xychartDiagram-PRI3JC2R-ClPpSFDI.js} +1 -1
  196. package/dist/assets/{zod-H_cgTO0M.js → zod-CIOjj3iA.js} +5 -5
  197. package/dist/index.html +87 -87
  198. package/package.json +6 -6
  199. package/src/__mocks__/requests.ts +5 -0
  200. package/src/components/app-config/user-config-form.tsx +26 -0
  201. package/src/components/chat/__tests__/chat-abort.test.ts +51 -0
  202. package/src/components/chat/chat-abort.ts +44 -0
  203. package/src/components/chat/chat-display.tsx +17 -0
  204. package/src/components/chat/chat-panel.tsx +85 -37
  205. package/src/components/chat/chat-utils.ts +1 -1
  206. package/src/components/data-table/charts/__tests__/schemas.test.ts +35 -0
  207. package/src/components/data-table/charts/schemas.ts +5 -2
  208. package/src/components/databases/icons/huggingface.svg +8 -0
  209. package/src/components/datasets/__tests__/icons.test.ts +21 -0
  210. package/src/components/datasets/icons.tsx +10 -0
  211. package/src/components/datasources/__tests__/components.test.tsx +29 -0
  212. package/src/components/datasources/components.tsx +8 -3
  213. package/src/components/editor/actions/export-dialog/__tests__/export-dialog.test.tsx +35 -0
  214. package/src/components/editor/actions/export-dialog/__tests__/state.test.ts +61 -0
  215. package/src/components/editor/actions/export-dialog/export-command.ts +4 -9
  216. package/src/components/editor/actions/export-dialog/export-dialog.tsx +27 -23
  217. package/src/components/editor/actions/export-dialog/format-notice.tsx +28 -0
  218. package/src/components/editor/actions/export-dialog/state.ts +23 -5
  219. package/src/components/editor/actions/export-dialog/use-export-dialog.ts +7 -15
  220. package/src/components/editor/actions/useConfigActions.tsx +24 -0
  221. package/src/components/editor/ai/add-cell-with-ai.tsx +2 -2
  222. package/src/components/editor/ai/ai-completion-editor.tsx +2 -2
  223. package/src/components/editor/chrome/panels/outline/__tests__/useActiveOutline.test.ts +26 -0
  224. package/src/components/editor/code/__tests__/readonly-python-code.test.tsx +114 -2
  225. package/src/components/editor/connections/storage/__tests__/__snapshots__/as-code.test.ts.snap +14 -0
  226. package/src/components/editor/connections/storage/__tests__/as-code.test.ts +20 -0
  227. package/src/components/editor/connections/storage/add-storage-form.tsx +10 -0
  228. package/src/components/editor/connections/storage/as-code.ts +19 -1
  229. package/src/components/editor/connections/storage/schemas.ts +19 -0
  230. package/src/components/storage/__tests__/storage-snippets.test.ts +88 -0
  231. package/src/components/storage/components.tsx +2 -0
  232. package/src/components/storage/storage-snippets.ts +58 -0
  233. package/src/core/__tests__/mode.test.ts +85 -0
  234. package/src/core/ai/constants.ts +7 -0
  235. package/src/core/ai/staged-cells.ts +3 -0
  236. package/src/core/codemirror/__tests__/setup.test.ts +1 -1
  237. package/src/core/codemirror/cm.ts +2 -2
  238. package/src/core/codemirror/code-lens/__tests__/extension.test.ts +4 -5
  239. package/src/core/codemirror/code-lens/extension.ts +3 -3
  240. package/src/core/codemirror/find-replace/search-highlight.ts +8 -3
  241. package/src/core/codemirror/language/languages/sql/__tests__/renderers.test.tsx +44 -0
  242. package/src/core/codemirror/language/languages/sql/renderers.tsx +3 -3
  243. package/src/core/codemirror/theme/__tests__/light.test.ts +59 -123
  244. package/src/core/codemirror/theme/dark.ts +6 -5
  245. package/src/core/codemirror/theme/light.ts +6 -4
  246. package/src/core/config/__tests__/config-schema.test.ts +2 -0
  247. package/src/core/config/config-schema.ts +1 -0
  248. package/src/core/config/feature-flag.tsx +0 -2
  249. package/src/core/dom/outline.ts +25 -2
  250. package/src/core/hotkeys/hotkeys.ts +2 -0
  251. package/src/core/mode.ts +11 -9
  252. package/src/core/network/__tests__/api.test.ts +132 -17
  253. package/src/core/network/__tests__/export-filename.test.ts +43 -0
  254. package/src/core/network/__tests__/requests-lazy.test.ts +1 -0
  255. package/src/core/network/api.ts +47 -12
  256. package/src/core/network/export-filename.ts +37 -0
  257. package/src/core/network/markdown-export.ts +19 -0
  258. package/src/core/network/requests-network.ts +29 -5
  259. package/src/core/network/types.ts +3 -0
  260. package/src/core/storage/types.ts +1 -0
  261. package/src/css/app/codemirror.css +11 -7
  262. package/src/css/globals.css +8 -0
  263. package/src/plugins/impl/DataEditorPlugin.tsx +2 -9
  264. package/src/plugins/impl/__tests__/DataEditorPlugin.test.ts +18 -0
  265. package/src/plugins/impl/chat/chat-ui.tsx +51 -3
  266. package/src/plugins/impl/data-frames/__tests__/schema.test.ts +30 -0
  267. package/src/plugins/impl/data-frames/schema.ts +4 -1
  268. package/src/plugins/impl/image-comparison/ImageComparisonComponent.tsx +20 -1
  269. package/src/plugins/impl/image-comparison/__tests__/ImageComparisonComponent.test.tsx +58 -0
  270. package/src/stories/theme.stories.tsx +71 -1
  271. package/dist/assets/ImageComparisonComponent-COvcNAUu.js +0 -1
  272. package/dist/assets/add-cell-with-ai-BU5EDuHt.js +0 -107
  273. package/dist/assets/ai-model-dropdown-CLCa-gsg.js +0 -5
  274. package/dist/assets/cells-BVzo2MHf.css +0 -2
  275. package/dist/assets/chat-display-7CzBSZRm.js +0 -1
  276. package/dist/assets/chat-panel-Dd5WiR1a.js +0 -4
  277. package/dist/assets/chat-ui-CYqqqoSC.js +0 -4
  278. package/dist/assets/command-palette-BBlSC8AZ.js +0 -1
  279. package/dist/assets/components-K_Mi0vdC.js +0 -1
  280. package/dist/assets/config-DAL-jiRM.js +0 -1
  281. package/dist/assets/defaultLocale-DOVIEEPN.js +0 -1
  282. package/dist/assets/dist-B99-E4qp.js +0 -1
  283. package/dist/assets/dist-Bfm9U3vp.js +0 -1
  284. package/dist/assets/dist-BiwuSVkx.js +0 -1
  285. package/dist/assets/dist-BrZjJXDA.js +0 -1
  286. package/dist/assets/dist-CaZf6AoY.js +0 -1
  287. package/dist/assets/dist-Cgoy5550.js +0 -1
  288. package/dist/assets/dist-Csf5i4KY.js +0 -1
  289. package/dist/assets/dist-D6Iobl3P.js +0 -1
  290. package/dist/assets/dist-D6WgndTj.js +0 -1
  291. package/dist/assets/dist-DcXy-1eJ.js +0 -1
  292. package/dist/assets/dist-DsfPGbgk.js +0 -1
  293. package/dist/assets/dist-DtbtRAqz.js +0 -1
  294. package/dist/assets/dist-I3qeQWuP.js +0 -1
  295. package/dist/assets/dist-aeKlrIMv.js +0 -1
  296. package/dist/assets/dist-gDi2I7IJ.js +0 -1
  297. package/dist/assets/dist-miDa4-cz.js +0 -1
  298. package/dist/assets/dist-nDEBxpL6.js +0 -1
  299. package/dist/assets/dist-oUL4XFdB.js +0 -1
  300. package/dist/assets/file-explorer-panel-4h-BKKaM.js +0 -26
  301. package/dist/assets/house-DS0r7dNZ.js +0 -1
  302. package/dist/assets/index-BXlfeAku.js +0 -38
  303. package/dist/assets/index-DYycoCjG.css +0 -2
  304. package/dist/assets/process-output-Cm_vuKBw.js +0 -1
  305. package/dist/assets/react-vega-BLc5RhC7.js +0 -188
  306. package/dist/assets/scratchpad-panel-DJkO5OrI.js +0 -1
  307. package/dist/assets/switch-CONP-vq2.js +0 -1
  308. package/dist/assets/tooltip-B7U4xkIN.js +0 -61
  309. package/dist/assets/useNotebookActions-BHaEvPEM.js +0 -12
  310. package/dist/assets/useRunCells-kmbsc2R5.js +0 -1
  311. package/dist/assets/utils-BWBBpCkT.js +0 -1
  312. package/dist/assets/vega-loader.browser-Dun7Qe4B.js +0 -6
  313. /package/dist/assets/{bundle.esm-D0TWQ6iT.js → bundle.esm-CvMga-X6.js} +0 -0
  314. /package/dist/assets/{icons-CIPE8AIY.js → icons-NFYKylj6.js} +0 -0
  315. /package/dist/assets/{useDeepCompareMemoize-CSMoa-ga.js → useDeepCompareMemoize-BLRnan2Y.js} +0 -0
@@ -44,6 +44,94 @@ describe("read-file snippet", () => {
44
44
  `);
45
45
  });
46
46
 
47
+ it("huggingface backend (dataset)", () => {
48
+ expect(
49
+ readSnippet.getCode(
50
+ makeCtx({
51
+ backendType: "huggingface",
52
+ entry: {
53
+ path: "datasets/scikit-learn/Fish/Fish.csv",
54
+ kind: "file",
55
+ size: 100,
56
+ lastModified: null,
57
+ },
58
+ }),
59
+ ),
60
+ ).toMatchInlineSnapshot(`
61
+ "from huggingface_hub import hf_hub_download
62
+
63
+ local_path = hf_hub_download(
64
+ repo_id="scikit-learn/Fish",
65
+ filename="Fish.csv",
66
+ repo_type="dataset",
67
+ )
68
+
69
+ with open(local_path, "rb") as f:
70
+ _data = f.read()
71
+ _data"
72
+ `);
73
+ });
74
+
75
+ it("huggingface backend (model)", () => {
76
+ expect(
77
+ readSnippet.getCode(
78
+ makeCtx({
79
+ backendType: "huggingface",
80
+ entry: {
81
+ path: "google-bert/bert-base-uncased/config.json",
82
+ kind: "file",
83
+ size: 100,
84
+ lastModified: null,
85
+ },
86
+ }),
87
+ ),
88
+ ).toMatchInlineSnapshot(`
89
+ "from huggingface_hub import hf_hub_download
90
+
91
+ local_path = hf_hub_download(
92
+ repo_id="google-bert/bert-base-uncased",
93
+ filename="config.json",
94
+ repo_type="model",
95
+ )
96
+
97
+ with open(local_path, "rb") as f:
98
+ _data = f.read()
99
+ _data"
100
+ `);
101
+ });
102
+
103
+ it("huggingface backend returns null for unparsable paths", () => {
104
+ expect(
105
+ readSnippet.getCode(
106
+ makeCtx({
107
+ backendType: "huggingface",
108
+ entry: {
109
+ path: "buckets/my-bucket/file.csv",
110
+ kind: "file",
111
+ size: 100,
112
+ lastModified: null,
113
+ },
114
+ }),
115
+ ),
116
+ ).toBeNull();
117
+ });
118
+
119
+ it("huggingface backend does not misparse a dataset/space root as a model repo", () => {
120
+ // These paths are missing a filename segment, so they aren't valid
121
+ // dataset/space repo files. They must not fall through and be treated
122
+ // as the model repo "datasets/scikit-learn" or "spaces/gradio".
123
+ for (const path of ["datasets/scikit-learn/Fish", "spaces/gradio/demo"]) {
124
+ expect(
125
+ readSnippet.getCode(
126
+ makeCtx({
127
+ backendType: "huggingface",
128
+ entry: { path, kind: "file", size: 100, lastModified: null },
129
+ }),
130
+ ),
131
+ ).toBeNull();
132
+ }
133
+ });
134
+
47
135
  it("returns null for directories", () => {
48
136
  expect(
49
137
  readSnippet.getCode(
@@ -9,6 +9,7 @@ import CoreweaveDarkIcon from "@marimo-team/llm-info/icons/coreweave-dark.svg?in
9
9
  import { DatabaseZapIcon, GlobeIcon, HardDriveIcon } from "lucide-react";
10
10
  import GoogleCloudIcon from "@/components/databases/icons/google-cloud-storage.svg?inline";
11
11
  import GoogleDriveIcon from "@/components/databases/icons/google-drive.svg?inline";
12
+ import HuggingfaceIcon from "@/components/databases/icons/huggingface.svg?inline";
12
13
  import { GitHubIcon } from "@/components/icons/github";
13
14
  import type { KnownStorageProtocol } from "@/core/storage/types";
14
15
  import { useTheme } from "@/theme/useTheme";
@@ -28,6 +29,7 @@ const PROTOCOL_ICONS: Record<KnownStorageProtocol, IconEntry> = {
28
29
  file: HardDriveIcon,
29
30
  "in-memory": DatabaseZapIcon,
30
31
  gdrive: { src: GoogleDriveIcon },
32
+ hf: { src: HuggingfaceIcon },
31
33
  github: GitHubIcon,
32
34
  };
33
35
 
@@ -27,6 +27,50 @@ function escapeForPythonString(value: string): string {
27
27
  return JSON.stringify(value).slice(1, -1);
28
28
  }
29
29
 
30
+ interface ParsedHfRepoPath {
31
+ repoType: "model" | "dataset" | "space";
32
+ repoId: string;
33
+ filename: string;
34
+ }
35
+
36
+ // Namespaced prefixes are reserved: they can never be the first segment of a
37
+ // model repo id, so a path like "datasets/org" that is missing a filename
38
+ // must not fall through and be misparsed as the model repo "datasets/org".
39
+ const RESERVED_PREFIXES = new Set(["datasets", "spaces", "buckets"]);
40
+
41
+ function parseHfRepoPath(path: string): ParsedHfRepoPath | null {
42
+ const parts = path.split("/").filter(Boolean);
43
+ if (parts[0] === "datasets" && parts.length >= 4) {
44
+ return {
45
+ repoType: "dataset",
46
+ repoId: `${parts[1]}/${parts[2]}`,
47
+ filename: parts.slice(3).join("/"),
48
+ };
49
+ }
50
+ if (parts[0] === "spaces" && parts.length >= 4) {
51
+ return {
52
+ repoType: "space",
53
+ repoId: `${parts[1]}/${parts[2]}`,
54
+ filename: parts.slice(3).join("/"),
55
+ };
56
+ }
57
+ if (parts.length >= 3 && !RESERVED_PREFIXES.has(parts[0])) {
58
+ return {
59
+ repoType: "model",
60
+ repoId: `${parts[0]}/${parts[1]}`,
61
+ filename: parts.slice(2).join("/"),
62
+ };
63
+ }
64
+ return null;
65
+ }
66
+
67
+ function hfHubDownloadSnippet(parsed: ParsedHfRepoPath): string {
68
+ const repoId = escapeForPythonString(parsed.repoId);
69
+ const filename = escapeForPythonString(parsed.filename);
70
+ const repoType = escapeForPythonString(parsed.repoType);
71
+ return `from huggingface_hub import hf_hub_download\n\nlocal_path = hf_hub_download(\n repo_id="${repoId}",\n filename="${filename}",\n repo_type="${repoType}",\n)`;
72
+ }
73
+
30
74
  export const STORAGE_SNIPPETS: StorageSnippet[] = [
31
75
  {
32
76
  id: "read-file",
@@ -37,6 +81,13 @@ export const STORAGE_SNIPPETS: StorageSnippet[] = [
37
81
  return null;
38
82
  }
39
83
  const path = escapeForPythonString(ctx.entry.path);
84
+ if (ctx.backendType === "huggingface") {
85
+ const parsed = parseHfRepoPath(ctx.entry.path);
86
+ if (!parsed) {
87
+ return null;
88
+ }
89
+ return `${hfHubDownloadSnippet(parsed)}\n\nwith open(local_path, "rb") as f:\n _data = f.read()\n_data`;
90
+ }
40
91
  if (ctx.backendType === "obstore") {
41
92
  return `_data = ${ctx.variableName}.get("${path}").bytes()\n_data`;
42
93
  }
@@ -52,6 +103,13 @@ export const STORAGE_SNIPPETS: StorageSnippet[] = [
52
103
  return null;
53
104
  }
54
105
  const path = escapeForPythonString(ctx.entry.path);
106
+ if (ctx.backendType === "huggingface") {
107
+ const parsed = parseHfRepoPath(ctx.entry.path);
108
+ if (!parsed) {
109
+ return null;
110
+ }
111
+ return `${hfHubDownloadSnippet(parsed)}\nlocal_path`;
112
+ }
55
113
  if (ctx.backendType === "obstore") {
56
114
  if (NOT_SIGNABLE_PROTOCOLS.has(ctx.protocol)) {
57
115
  return null;
@@ -0,0 +1,85 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
4
+ import { CellId } from "@/core/cells/ids";
5
+ import { runDuringPresentMode, viewStateAtom } from "@/core/mode";
6
+ import { store } from "@/core/state/jotai";
7
+
8
+ const requestAnimationFrameMock = vi.fn((callback: FrameRequestCallback) => {
9
+ callback(0);
10
+ return 0;
11
+ });
12
+
13
+ async function runAfterRender(fn: () => void | Promise<void>): Promise<void> {
14
+ const result = runDuringPresentMode(fn);
15
+ await vi.runAllTimersAsync();
16
+ return result;
17
+ }
18
+
19
+ describe("runDuringPresentMode", () => {
20
+ beforeEach(() => {
21
+ vi.useFakeTimers();
22
+ vi.stubGlobal("requestAnimationFrame", requestAnimationFrameMock);
23
+ requestAnimationFrameMock.mockClear();
24
+ });
25
+
26
+ afterEach(() => {
27
+ vi.useRealTimers();
28
+ vi.unstubAllGlobals();
29
+ });
30
+
31
+ it("runs in present mode and restores the captured view state", async () => {
32
+ const state = { mode: "edit" as const, cellAnchor: CellId.create() };
33
+ store.set(viewStateAtom, state);
34
+
35
+ await runAfterRender(() => {
36
+ expect(store.get(viewStateAtom)).toEqual({
37
+ ...state,
38
+ mode: "present",
39
+ });
40
+ });
41
+
42
+ expect(store.get(viewStateAtom)).toEqual(state);
43
+ expect(requestAnimationFrameMock).toHaveBeenCalledTimes(2);
44
+ });
45
+
46
+ it("restores the captured view state when the callback rejects", async () => {
47
+ const state = { mode: "edit" as const, cellAnchor: CellId.create() };
48
+ const error = new Error("capture failed");
49
+ store.set(viewStateAtom, state);
50
+
51
+ const result = runDuringPresentMode(() => Promise.reject(error));
52
+ const rejection = expect(result).rejects.toBe(error);
53
+ await vi.runAllTimersAsync();
54
+ await rejection;
55
+
56
+ expect(store.get(viewStateAtom)).toEqual(state);
57
+ });
58
+
59
+ it("runs directly when already in present mode", async () => {
60
+ const state = { mode: "present" as const, cellAnchor: CellId.create() };
61
+ store.set(viewStateAtom, state);
62
+
63
+ await runDuringPresentMode(() => {
64
+ expect(store.get(viewStateAtom)).toEqual(state);
65
+ });
66
+
67
+ expect(store.get(viewStateAtom)).toEqual(state);
68
+ expect(requestAnimationFrameMock).not.toHaveBeenCalled();
69
+ });
70
+
71
+ it.each(["read", "home", "gallery"] as const)(
72
+ "runs directly without changing %s mode",
73
+ async (mode) => {
74
+ const state = { mode, cellAnchor: CellId.create() };
75
+ store.set(viewStateAtom, state);
76
+
77
+ await runDuringPresentMode(() => {
78
+ expect(store.get(viewStateAtom)).toEqual(state);
79
+ });
80
+
81
+ expect(store.get(viewStateAtom)).toEqual(state);
82
+ expect(requestAnimationFrameMock).not.toHaveBeenCalled();
83
+ },
84
+ );
85
+ });
@@ -0,0 +1,7 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ /**
4
+ * Throttle wait (ms) for AI SDK React hooks (`useChat` / `useCompletion`).
5
+ * Limits UI re-renders while streaming tokens.
6
+ */
7
+ export const AI_SDK_UI_THROTTLE_MS = 100;
@@ -176,14 +176,17 @@ export function useStagedCells(store: JotaiStore) {
176
176
  case "message-metadata":
177
177
  case "tool-input-available":
178
178
  case "tool-output-available":
179
+ case "tool-approval-response":
179
180
  case "reasoning-start":
180
181
  case "reasoning-delta":
181
182
  case "reasoning-end":
182
183
  case "file":
184
+ case "reasoning-file":
183
185
  case "source-document":
184
186
  case "source-url":
185
187
  case "tool-input-start":
186
188
  case "tool-input-delta":
189
+ case "custom":
187
190
  Logger.debug(chunk.type, { chunk });
188
191
  break;
189
192
  default:
@@ -68,7 +68,7 @@ function getOpts() {
68
68
  diagnosticsConfig: {},
69
69
  hotkeys: new OverridingHotkeyProvider({}),
70
70
  theme: "light",
71
- displayConfig: { reference_highlighting: false },
71
+ displayConfig: { reference_highlighting: false, code_lens: true },
72
72
  inlineAiTooltip: false,
73
73
  } as const;
74
74
  }
@@ -82,7 +82,7 @@ export interface CodeMirrorSetupOpts {
82
82
  hotkeys: HotkeyProvider;
83
83
  lspConfig: LSPConfig;
84
84
  diagnosticsConfig: DiagnosticsConfig;
85
- displayConfig: Pick<DisplayConfig, "reference_highlighting">;
85
+ displayConfig: Pick<DisplayConfig, "reference_highlighting" | "code_lens">;
86
86
  inlineAiTooltip: boolean;
87
87
  /**
88
88
  * CSS selector for the element that CodeMirror tooltips (completions, hover,
@@ -200,7 +200,7 @@ export const setupCodeMirror = (opts: CodeMirrorSetupOpts): Extension[] => {
200
200
  displayConfig.reference_highlighting ?? true,
201
201
  ),
202
202
  // Inline icons linking datasources/buckets/caches to their panels
203
- codeLensBundle(cellId),
203
+ codeLensBundle(cellId, displayConfig.code_lens ?? true),
204
204
  ];
205
205
  };
206
206
 
@@ -103,7 +103,7 @@ describe("codeLensBundle", () => {
103
103
 
104
104
  beforeEach(() => {
105
105
  vi.useFakeTimers();
106
- mockFlags({ editor_code_lens: true, cache_panel: true });
106
+ mockFlags({ cache_panel: true });
107
107
  seedStore({});
108
108
  });
109
109
 
@@ -141,9 +141,8 @@ describe("codeLensBundle", () => {
141
141
  return [...v.dom.querySelectorAll<HTMLElement>(".mo-code-lens")];
142
142
  }
143
143
 
144
- it("returns an empty extension when the flag is off", () => {
145
- mockFlags({ editor_code_lens: false });
146
- expect(codeLensBundle(cellId("cell1"))).toEqual([]);
144
+ it("returns an empty extension when disabled via `display.code_lens`", () => {
145
+ expect(codeLensBundle(cellId("cell1"), false)).toEqual([]);
147
146
  });
148
147
 
149
148
  it("renders a lens at a dataframe declaration", async () => {
@@ -293,7 +292,7 @@ describe("codeLensBundle", () => {
293
292
  );
294
293
 
295
294
  it("does not render cache lenses when the cache panel is disabled", async () => {
296
- mockFlags({ editor_code_lens: true, cache_panel: false });
295
+ mockFlags({ cache_panel: false });
297
296
  const v = await mount("@mo.cache\ndef f():\n return 1");
298
297
 
299
298
  expect(lenses(v)).toHaveLength(0);
@@ -281,10 +281,10 @@ const codeLensTheme = EditorView.baseTheme({
281
281
  /**
282
282
  * Inline icons linking datasource/bucket variables and `mo.cache` /
283
283
  * `mo.persistent_cache` calls to their panels.
284
- * Gated behind the `editor_code_lens` feature flag.
284
+ * Configurable via the `display.code_lens` user config (on by default).
285
285
  */
286
- export function codeLensBundle(cellId: CellId): Extension {
287
- if (!getFeatureFlag("editor_code_lens")) {
286
+ export function codeLensBundle(cellId: CellId, enabled = true): Extension {
287
+ if (!enabled) {
288
288
  return [];
289
289
  }
290
290
  return [
@@ -140,11 +140,16 @@ export const searchHighlighter = ViewPlugin.fromClass(
140
140
  );
141
141
 
142
142
  export const highlightTheme = EditorView.baseTheme({
143
- "&light .cm-searchMatch": { backgroundColor: "#99ff7780" },
144
- "&dark .cm-searchMatch": { backgroundColor: "#22bb0070" },
143
+ "&light .cm-searchMatch": {
144
+ backgroundColor: "var(--cm-search-match-background-light)",
145
+ },
146
+ "&dark .cm-searchMatch": {
147
+ backgroundColor: "var(--cm-search-match-background-dark)",
148
+ },
145
149
 
146
150
  "&light .cm-searchMatch-selected": { backgroundColor: "transparent" },
147
151
  "&dark .cm-searchMatch.cm-searchMatch-selected": {
148
- backgroundColor: "#6199ff88 !important",
152
+ backgroundColor:
153
+ "var(--cm-search-match-selected-background-dark) !important",
149
154
  },
150
155
  });
@@ -0,0 +1,44 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { render } from "@testing-library/react";
4
+ import { describe, expect, it } from "vitest";
5
+ import type {
6
+ DataTable,
7
+ DataTableColumn,
8
+ DataType,
9
+ } from "@/core/kernel/messages";
10
+ import { renderColumnInfo, renderTableInfo } from "../renderers";
11
+
12
+ const geometryColumn = {
13
+ name: "geom",
14
+ type: "geometry" as DataType,
15
+ external_type: "geometry",
16
+ sample_values: [],
17
+ } as DataTableColumn;
18
+
19
+ describe("SQL renderers", () => {
20
+ it("renders table information with an unrecognized column type", () => {
21
+ const table: DataTable = {
22
+ name: "shapes",
23
+ type: "table",
24
+ source: "local",
25
+ source_type: "local",
26
+ num_rows: null,
27
+ num_columns: 1,
28
+ variable_name: null,
29
+ columns: [geometryColumn],
30
+ primary_keys: [],
31
+ indexes: [],
32
+ };
33
+
34
+ const result = render(renderTableInfo(table));
35
+
36
+ expect(result.container.querySelector("svg")).not.toBeNull();
37
+ });
38
+
39
+ it("renders column information with an unrecognized column type", () => {
40
+ const result = render(renderColumnInfo(geometryColumn));
41
+
42
+ expect(result.container.querySelector("svg")).not.toBeNull();
43
+ });
44
+ });
@@ -13,7 +13,7 @@ import {
13
13
  TableIcon,
14
14
  ViewIcon,
15
15
  } from "@/components/databases/namespace-icons";
16
- import { DATA_TYPE_ICON } from "@/components/datasets/icons";
16
+ import { DATA_TYPE_ICON, resolveDataType } from "@/components/datasets/icons";
17
17
  import { Badge } from "@/components/ui/badge";
18
18
  import {
19
19
  type ConnectionName,
@@ -149,7 +149,7 @@ export const renderTableInfo = (table: DataTable): React.ReactNode => {
149
149
  );
150
150
 
151
151
  const columnItems = table.columns.map((column) => {
152
- const TypeIcon = DATA_TYPE_ICON[column.type];
152
+ const TypeIcon = DATA_TYPE_ICON[resolveDataType(column.type)];
153
153
  return (
154
154
  <div
155
155
  key={column.name}
@@ -288,7 +288,7 @@ export const renderTableInfo = (table: DataTable): React.ReactNode => {
288
288
  };
289
289
 
290
290
  export const renderColumnInfo = (column: DataTableColumn): React.ReactNode => {
291
- const TypeIcon = DATA_TYPE_ICON[column.type];
291
+ const TypeIcon = DATA_TYPE_ICON[resolveDataType(column.type)];
292
292
 
293
293
  const typeBadge = (
294
294
  <Badge
@@ -1,136 +1,72 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
- import { type Tag, tags as t } from "@lezer/highlight";
4
- import { describe, expect, it } from "vitest";
3
+ import type { Extension } from "@codemirror/state";
4
+ import { EditorView } from "@codemirror/view";
5
+ import { afterEach, describe, expect, it } from "vitest";
6
+ import { darkTheme } from "../dark";
5
7
  import { lightTheme } from "../light";
6
8
 
7
- // Helper function to get theme configuration from the source
8
- const getThemeConfig = () => ({
9
- variant: "light",
10
- settings: {
11
- background: "#ffffff",
12
- foreground: "#000000",
13
- caret: "#000000",
14
- selection: "#d7d4f0",
15
- lineHighlight: "#cceeff44",
16
- gutterBackground: "var(--color-background)",
17
- gutterForeground: "var(--gray-10)",
18
- },
19
- styles: [
20
- { tag: t.comment, color: "#708090" },
21
- { tag: t.variableName, color: "#000000" },
22
- { tag: [t.string, t.special(t.brace)], color: "#a11" },
23
- { tag: t.number, color: "#164" },
24
- { tag: t.bool, color: "#219" },
25
- { tag: t.null, color: "#219" },
26
- { tag: t.keyword, color: "#708", fontWeight: 500 },
27
- { tag: t.className, color: "#00f" },
28
- { tag: t.definition(t.typeName), color: "#00f" },
29
- { tag: t.typeName, color: "#085" },
30
- { tag: t.angleBracket, color: "#000000" },
31
- { tag: t.tagName, color: "#170" },
32
- { tag: t.attributeName, color: "#00c" },
33
- { tag: t.operator, color: "#a2f", fontWeight: 500 },
34
- { tag: [t.function(t.variableName)], color: "#00c" },
35
- { tag: [t.propertyName], color: "#05a" },
36
- ],
37
- });
38
-
39
- describe("lightTheme", () => {
40
- it("should export a theme", () => {
41
- expect(lightTheme).toBeDefined();
42
- expect(typeof lightTheme).toBe("object");
43
- });
44
-
45
- describe("theme configuration", () => {
46
- const config = getThemeConfig();
47
-
48
- it("should have correct basic settings", () => {
49
- expect(config.variant).toBe("light");
50
- expect(config.settings).toEqual({
51
- background: "#ffffff",
52
- foreground: "#000000",
53
- caret: "#000000",
54
- selection: "#d7d4f0",
55
- lineHighlight: "#cceeff44",
56
- gutterBackground: "var(--color-background)",
57
- gutterForeground: "var(--gray-10)",
58
- });
59
- });
9
+ const mounted: Array<{ host: HTMLDivElement; view: EditorView }> = [];
60
10
 
61
- describe("syntax highlighting", () => {
62
- const findStyle = (tag: Tag | readonly Tag[]) =>
63
- config.styles.find((s) =>
64
- Array.isArray(s.tag) ? s.tag.includes(tag as Tag) : s.tag === tag,
65
- );
11
+ function mountTheme(extension: Extension) {
12
+ const host = document.createElement("div");
13
+ document.body.append(host);
14
+ const root = host.attachShadow({ mode: "open" });
15
+ const parent = document.createElement("div");
16
+ root.append(parent);
17
+ const view = new EditorView({ extensions: extension, parent, root });
18
+ mounted.push({ host, view });
66
19
 
67
- it("should style comments", () => {
68
- expect(findStyle(t.comment)).toEqual({
69
- tag: t.comment,
70
- color: "#708090",
71
- });
72
- });
20
+ const style = root.querySelector("style");
21
+ if (!style) {
22
+ throw new Error("CodeMirror did not mount its theme styles");
23
+ }
24
+ return { css: style.textContent ?? "", view };
25
+ }
73
26
 
74
- it("should style strings", () => {
75
- const style = findStyle(t.string);
76
- expect(style).toBeDefined();
77
- expect(style?.color).toBe("#a11");
78
- expect(Array.isArray(style?.tag)).toBe(true);
79
- expect(style?.tag).toContain(t.string);
80
- expect(style?.tag).toContain(t.special(t.brace));
81
- });
82
-
83
- it("should style numbers and boolean values", () => {
84
- expect(findStyle(t.number)).toEqual({
85
- tag: t.number,
86
- color: "#164",
87
- });
88
- expect(findStyle(t.bool)).toEqual({
89
- tag: t.bool,
90
- color: "#219",
91
- });
92
- });
93
-
94
- it("should style keywords with emphasis", () => {
95
- expect(findStyle(t.keyword)).toEqual({
96
- tag: t.keyword,
97
- color: "#708",
98
- fontWeight: 500,
99
- });
100
- });
27
+ afterEach(() => {
28
+ for (const { host, view } of mounted.splice(0)) {
29
+ view.destroy();
30
+ host.remove();
31
+ }
32
+ });
101
33
 
102
- it("should style operators with emphasis", () => {
103
- expect(findStyle(t.operator)).toEqual({
104
- tag: t.operator,
105
- color: "#a2f",
106
- fontWeight: 500,
107
- });
108
- });
34
+ const themeCases = [
35
+ {
36
+ name: "light",
37
+ extension: lightTheme,
38
+ dark: false,
39
+ selectionAlias: "var(--cm-selection-background-light)",
40
+ reactiveColor: "var(--cm-reactive-reference-color-light)",
41
+ },
42
+ {
43
+ name: "dark",
44
+ extension: darkTheme,
45
+ dark: true,
46
+ selectionAlias: "var(--cm-selection-background-dark)",
47
+ reactiveColor: "var(--cm-reactive-reference-color-dark)",
48
+ },
49
+ ] as const;
109
50
 
110
- it("should style class and type names", () => {
111
- expect(findStyle(t.className)).toEqual({
112
- tag: t.className,
113
- color: "#00f",
114
- });
115
- expect(findStyle(t.typeName)).toEqual({
116
- tag: t.typeName,
117
- color: "#085",
118
- });
119
- });
51
+ describe.each(themeCases)("$name theme", (theme) => {
52
+ it("uses the production theme tokens", () => {
53
+ const { css, view } = mountTheme(theme.extension);
120
54
 
121
- it("should style function names", () => {
122
- const style = findStyle(t.function(t.variableName));
123
- expect(style).toBeDefined();
124
- expect(style?.color).toBe("#00c");
125
- expect(Array.isArray(style?.tag)).toBe(true);
126
- });
55
+ expect(view.state.facet(EditorView.darkTheme)).toBe(theme.dark);
56
+ expect(css).toContain(
57
+ `--cm-selection-background: ${theme.selectionAlias};`,
58
+ );
59
+ expect(css).toContain("background-color: var(--cm-selection-background);");
60
+ expect(css).toContain(`color: ${theme.reactiveColor};`);
61
+ expect(css).toContain(
62
+ "border-bottom: 2px solid var(--cm-reactive-reference-border-color);",
63
+ );
127
64
 
128
- it("should style property names", () => {
129
- const style = findStyle(t.propertyName);
130
- expect(style).toBeDefined();
131
- expect(style?.color).toBe("#05a");
132
- expect(Array.isArray(style?.tag)).toBe(true);
133
- });
134
- });
65
+ const hoverRule = css
66
+ .split("\n")
67
+ .find((rule) => rule.includes(".mo-cm-reactive-reference-hover"));
68
+ expect(hoverRule).toBeDefined();
69
+ expect(hoverRule).toContain("cursor: pointer;");
70
+ expect(hoverRule).not.toContain("border-bottom");
135
71
  });
136
72
  });