@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
@@ -11,7 +11,7 @@ export const darkTheme = [
11
11
  background: "var(--cm-background)",
12
12
  foreground: "#abb2bf",
13
13
  caret: "#528bff",
14
- selection: "#3E4451",
14
+ selection: "var(--cm-selection-background)",
15
15
  lineHighlight: "#2c313c",
16
16
  gutterBackground: "var(--color-background)",
17
17
  gutterForeground: "var(--gray-10)",
@@ -36,15 +36,16 @@ export const darkTheme = [
36
36
  ],
37
37
  }),
38
38
  EditorView.theme({
39
+ "&": {
40
+ "--cm-selection-background": "var(--cm-selection-background-dark)",
41
+ },
39
42
  ".mo-cm-reactive-reference": {
40
43
  fontWeight: "400",
41
- color: "#2a7aa5",
42
- borderBottom: "2px solid #bad3de",
44
+ color: "var(--cm-reactive-reference-color-dark)",
45
+ borderBottom: "2px solid var(--cm-reactive-reference-border-color)",
43
46
  },
44
47
  ".mo-cm-reactive-reference-hover": {
45
48
  cursor: "pointer",
46
- borderBottomWidth: "3px",
47
- borderBottomColor: "#4a90a5",
48
49
  },
49
50
  }),
50
51
  ];
@@ -11,7 +11,7 @@ export const lightTheme = [
11
11
  background: "#ffffff",
12
12
  foreground: "#000000",
13
13
  caret: "#000000",
14
- selection: "#d7d4f0",
14
+ selection: "var(--cm-selection-background)",
15
15
  lineHighlight: "#cceeff44",
16
16
  gutterBackground: "var(--color-background)",
17
17
  gutterForeground: "var(--gray-10)",
@@ -45,14 +45,16 @@ export const lightTheme = [
45
45
  ],
46
46
  }),
47
47
  EditorView.theme({
48
+ "&": {
49
+ "--cm-selection-background": "var(--cm-selection-background-light)",
50
+ },
48
51
  ".mo-cm-reactive-reference": {
49
52
  fontWeight: "400",
50
- color: "#005f87",
51
- borderBottom: "2px solid #bad3de",
53
+ color: "var(--cm-reactive-reference-color-light)",
54
+ borderBottom: "2px solid var(--cm-reactive-reference-border-color)",
52
55
  },
53
56
  ".mo-cm-reactive-reference-hover": {
54
57
  cursor: "pointer",
55
- borderBottomWidth: "3px",
56
58
  },
57
59
  }),
58
60
  ];
@@ -66,6 +66,7 @@ test("default UserConfig - empty", () => {
66
66
  "display": {
67
67
  "cell_output": "below",
68
68
  "code_editor_font_size": 14,
69
+ "code_lens": true,
69
70
  "dataframes": "rich",
70
71
  "default_table_max_columns": 50,
71
72
  "default_table_page_size": 10,
@@ -139,6 +140,7 @@ test("default UserConfig - one level", () => {
139
140
  "display": {
140
141
  "cell_output": "below",
141
142
  "code_editor_font_size": 14,
143
+ "code_lens": true,
142
144
  "dataframes": "rich",
143
145
  "default_table_max_columns": 50,
144
146
  "default_table_page_size": 10,
@@ -154,6 +154,7 @@ export const UserConfigSchema = z
154
154
  }),
155
155
  locale: z.string().nullable().optional(),
156
156
  reference_highlighting: z.boolean().prefault(true),
157
+ code_lens: z.boolean().prefault(true),
157
158
  })
158
159
  .prefault({}),
159
160
  package_management: z
@@ -13,7 +13,6 @@ export interface ExperimentalFeatures {
13
13
  external_agents: boolean;
14
14
  debugger: boolean; // Live frame-watching debugger (gutter breakpoints + pdb)
15
15
  line_timing: boolean; // Green active-line highlight + per-line elapsed timer
16
- editor_code_lens: boolean; // Inline icons in cell editors linking datasources/buckets/caches to their panels
17
16
  // Add new feature flags here
18
17
  }
19
18
 
@@ -25,7 +24,6 @@ const defaultValues: ExperimentalFeatures = {
25
24
  external_agents: import.meta.env.DEV,
26
25
  debugger: false,
27
26
  line_timing: false,
28
- editor_code_lens: import.meta.env.DEV,
29
27
  };
30
28
 
31
29
  export function getFeatureFlag<T extends keyof ExperimentalFeatures>(
@@ -63,13 +63,36 @@ function getOutline(html: string): Outline | null {
63
63
  return { items };
64
64
  }
65
65
 
66
+ function toXPathStringLiteral(value: string): string {
67
+ if (!value.includes('"')) {
68
+ return `"${value}"`;
69
+ }
70
+ if (!value.includes("'")) {
71
+ return `'${value}'`;
72
+ }
73
+
74
+ const segments = value.split('"');
75
+ const literals: string[] = [];
76
+ for (const [index, segment] of segments.entries()) {
77
+ if (segment) {
78
+ literals.push(`"${segment}"`);
79
+ }
80
+ if (index < segments.length - 1) {
81
+ literals.push(`'"'`);
82
+ }
83
+ }
84
+ return `concat(${literals.join(", ")})`;
85
+ }
86
+
66
87
  export function headingToIdentifier(heading: Element): OutlineItem["by"] {
67
88
  const id = heading.id;
68
89
  if (id) {
69
90
  return { id };
70
91
  }
71
- const name = heading.textContent;
72
- return { path: `//${heading.tagName}[contains(., "${name}")]` };
92
+ const name = heading.textContent ?? "";
93
+ return {
94
+ path: `//${heading.tagName}[contains(., ${toXPathStringLiteral(name)})]`,
95
+ };
73
96
  }
74
97
 
75
98
  export function mergeOutlines(outlines: (Outline | null)[]): Outline {
@@ -393,11 +393,13 @@ const DEFAULT_HOT_KEY = {
393
393
  name: "Collapse all sections",
394
394
  group: "Editing",
395
395
  key: "Mod-Shift-\\",
396
+ additionalKeywords: ["fold", "headers"],
396
397
  },
397
398
  "global.expandAllSections": {
398
399
  name: "Expand all sections",
399
400
  group: "Editing",
400
401
  key: "Mod-Shift-/",
402
+ additionalKeywords: ["unfold", "headers"],
401
403
  },
402
404
  "global.toggleMinimap": {
403
405
  name: "Toggle Minimap",
package/src/core/mode.ts CHANGED
@@ -55,20 +55,22 @@ export async function runDuringPresentMode(
55
55
  fn: () => void | Promise<void>,
56
56
  ): Promise<void> {
57
57
  const state = store.get(viewStateAtom);
58
- if (state.mode === "present") {
58
+ if (state.mode !== "edit") {
59
59
  await fn();
60
60
  return;
61
61
  }
62
62
 
63
63
  store.set(viewStateAtom, { ...state, mode: "present" });
64
- // Wait 100ms to allow the page to render
65
- await new Promise((resolve) => setTimeout(resolve, 100));
66
- // Wait 2 frames
67
- await new Promise((resolve) => requestAnimationFrame(resolve));
68
- await new Promise((resolve) => requestAnimationFrame(resolve));
69
- await fn();
70
- store.set(viewStateAtom, { ...state, mode: "edit" });
71
- return undefined;
64
+ try {
65
+ // Wait 100ms to allow the page to render
66
+ await new Promise((resolve) => setTimeout(resolve, 100));
67
+ // Wait 2 frames
68
+ await new Promise((resolve) => requestAnimationFrame(resolve));
69
+ await new Promise((resolve) => requestAnimationFrame(resolve));
70
+ await fn();
71
+ } finally {
72
+ store.set(viewStateAtom, state);
73
+ }
72
74
  }
73
75
 
74
76
  export const viewStateAtom = atom<ViewState>({
@@ -105,7 +105,10 @@ describe("API", () => {
105
105
  });
106
106
 
107
107
  await expect(
108
- API.handleExportResponse({ data: "markdown", response }),
108
+ API.handleExportResponse(
109
+ { data: "markdown", response },
110
+ { defaultFilename: "download.md" },
111
+ ),
109
112
  ).resolves.toEqual({
110
113
  contents: "markdown",
111
114
  filename: "résumé.qmd",
@@ -113,27 +116,139 @@ describe("API", () => {
113
116
  });
114
117
  });
115
118
 
116
- it("rejects export responses without a filename", async () => {
119
+ it("parses quoted Content-Disposition filenames", async () => {
120
+ const response = new Response("html", {
121
+ headers: {
122
+ "Content-Disposition": 'inline; filename="notebook.html"',
123
+ "Content-Type": "text/html; charset=utf-8",
124
+ },
125
+ });
126
+
117
127
  await expect(
118
- API.handleExportResponse({
119
- data: "markdown",
120
- response: new Response(null, {
121
- headers: { "Content-Type": "text/plain" },
122
- }),
123
- }),
124
- ).rejects.toThrow("Export response is missing a filename");
128
+ API.handleExportResponse(
129
+ { data: "html", response },
130
+ { defaultFilename: "download.html" },
131
+ ),
132
+ ).resolves.toEqual({
133
+ contents: "html",
134
+ filename: "notebook.html",
135
+ mediaType: "text/html; charset=utf-8",
136
+ });
137
+ });
138
+
139
+ it("parses unquoted Content-Disposition filenames", async () => {
140
+ const response = new Response("pdf", {
141
+ headers: {
142
+ "Content-Disposition": "attachment; filename=notebook.pdf",
143
+ "Content-Type": "application/pdf",
144
+ },
145
+ });
146
+
147
+ await expect(
148
+ API.handleExportResponse(
149
+ { data: "pdf", response },
150
+ { defaultFilename: "download.pdf" },
151
+ ),
152
+ ).resolves.toEqual({
153
+ contents: "pdf",
154
+ filename: "notebook.pdf",
155
+ mediaType: "application/pdf",
156
+ });
157
+ });
158
+
159
+ it("prefers RFC 5987 filenames over quoted fallbacks", async () => {
160
+ const response = new Response("markdown", {
161
+ headers: {
162
+ "Content-Disposition":
163
+ "attachment; filename=\"wrong.md\"; filename*=UTF-8''notebook.md",
164
+ "Content-Type": "text/plain; charset=utf-8",
165
+ },
166
+ });
167
+
168
+ await expect(
169
+ API.handleExportResponse(
170
+ { data: "markdown", response },
171
+ { defaultFilename: "download.md" },
172
+ ),
173
+ ).resolves.toEqual({
174
+ contents: "markdown",
175
+ filename: "notebook.md",
176
+ mediaType: "text/plain; charset=utf-8",
177
+ });
178
+ });
179
+
180
+ it("falls back to a default filename when headers are missing", async () => {
181
+ const response = new Response("markdown", {
182
+ headers: {
183
+ "Content-Type": "text/plain; charset=utf-8",
184
+ },
185
+ });
186
+
187
+ await expect(
188
+ API.handleExportResponse(
189
+ { data: "markdown", response },
190
+ { defaultFilename: "download.md" },
191
+ ),
192
+ ).resolves.toEqual({
193
+ contents: "markdown",
194
+ filename: "download.md",
195
+ mediaType: "text/plain; charset=utf-8",
196
+ });
197
+ });
198
+
199
+ it("falls back to a default filename when Content-Disposition is unparsable", async () => {
200
+ const response = new Response("markdown", {
201
+ headers: {
202
+ "Content-Disposition": "attachment",
203
+ "Content-Type": "text/plain; charset=utf-8",
204
+ },
205
+ });
206
+
207
+ await expect(
208
+ API.handleExportResponse(
209
+ { data: "markdown", response },
210
+ { defaultFilename: "download.md" },
211
+ ),
212
+ ).resolves.toEqual({
213
+ contents: "markdown",
214
+ filename: "download.md",
215
+ mediaType: "text/plain; charset=utf-8",
216
+ });
217
+ });
218
+
219
+ it("falls back to a default filename when RFC 5987 encoding is malformed", async () => {
220
+ const response = new Response("markdown", {
221
+ headers: {
222
+ "Content-Disposition": "attachment; filename*=UTF-8''%E0%A4%A",
223
+ "Content-Type": "text/plain; charset=utf-8",
224
+ },
225
+ });
226
+
227
+ await expect(
228
+ API.handleExportResponse(
229
+ { data: "markdown", response },
230
+ { defaultFilename: "download.md" },
231
+ ),
232
+ ).resolves.toEqual({
233
+ contents: "markdown",
234
+ filename: "download.md",
235
+ mediaType: "text/plain; charset=utf-8",
236
+ });
125
237
  });
126
238
 
127
239
  it("rejects export responses without a media type", async () => {
128
240
  await expect(
129
- API.handleExportResponse({
130
- data: "markdown",
131
- response: new Response(null, {
132
- headers: {
133
- "Content-Disposition": "attachment; filename*=UTF-8''notebook.md",
134
- },
135
- }),
136
- }),
241
+ API.handleExportResponse(
242
+ {
243
+ data: "markdown",
244
+ response: new Response(null, {
245
+ headers: {
246
+ "Content-Disposition": "attachment; filename*=UTF-8''notebook.md",
247
+ },
248
+ }),
249
+ },
250
+ { defaultFilename: "download.md" },
251
+ ),
137
252
  ).rejects.toThrow("Export response is missing a media type");
138
253
  });
139
254
  });
@@ -0,0 +1,43 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { beforeEach, describe, expect, it } from "vitest";
4
+ import { filenameAtom } from "@/core/saving/file-state";
5
+ import { store } from "@/core/state/jotai";
6
+ import {
7
+ getDefaultExportFilename,
8
+ getDefaultMarkdownExportFilename,
9
+ } from "../export-filename";
10
+
11
+ describe("default export filenames", () => {
12
+ beforeEach(() => {
13
+ store.set(filenameAtom, "folder/my_notebook.py");
14
+ });
15
+
16
+ it("derives filenames from the notebook name", () => {
17
+ expect(getDefaultExportFilename("html")).toBe("my_notebook.html");
18
+ expect(getDefaultExportFilename("pdf")).toBe("my_notebook.pdf");
19
+ });
20
+
21
+ it("handles Windows-style notebook paths", () => {
22
+ store.set(filenameAtom, "C:\\Users\\me\\my_notebook.py");
23
+ expect(getDefaultExportFilename("html")).toBe("my_notebook.html");
24
+ });
25
+
26
+ it("derives markdown filenames from the selected flavor", () => {
27
+ expect(getDefaultMarkdownExportFilename("qmd")).toBe("my_notebook.qmd");
28
+ expect(getDefaultMarkdownExportFilename("mystmd")).toBe(
29
+ "my_notebook.myst.md",
30
+ );
31
+ });
32
+
33
+ it("derives script export filenames from the notebook name", () => {
34
+ expect(getDefaultExportFilename("script.py")).toBe("my_notebook.script.py");
35
+ });
36
+
37
+ it("falls back to download.* when the notebook is unnamed", () => {
38
+ store.set(filenameAtom, null);
39
+ expect(getDefaultExportFilename("html")).toBe("download.html");
40
+ expect(getDefaultMarkdownExportFilename("qmd")).toBe("download.qmd");
41
+ expect(getDefaultExportFilename("script.py")).toBe("download.script.py");
42
+ });
43
+ });
@@ -84,6 +84,7 @@ describe("createLazyRequests", () => {
84
84
  format: "ipynb",
85
85
  dependenciesAvailable: false,
86
86
  missingPackages: ["nbformat"],
87
+ missingSetup: [],
87
88
  },
88
89
  ],
89
90
  };
@@ -16,13 +16,45 @@ function getBaseUriWithoutQueryParams(): string {
16
16
  return url.toString();
17
17
  }
18
18
 
19
- function getExportFilename(response: Response): string {
20
- const contentDisposition = response.headers.get("Content-Disposition");
21
- const match = contentDisposition?.match(/filename\*=UTF-8''([^;]+)/i);
22
- if (!match) {
23
- throw new Error("Export response is missing a filename");
19
+ function parseContentDispositionFilename(
20
+ contentDisposition: string | null,
21
+ ): string | undefined {
22
+ if (!contentDisposition) {
23
+ return undefined;
24
24
  }
25
- return decodeURIComponent(match[1]);
25
+
26
+ const rfc5987Match = contentDisposition.match(/filename\*=UTF-8''([^;]+)/i);
27
+ if (rfc5987Match) {
28
+ try {
29
+ return decodeURIComponent(rfc5987Match[1]);
30
+ } catch {
31
+ return undefined;
32
+ }
33
+ }
34
+
35
+ const quotedMatch = contentDisposition.match(/filename="([^"]+)"/i);
36
+ if (quotedMatch) {
37
+ return quotedMatch[1];
38
+ }
39
+
40
+ const unquotedMatch = contentDisposition.match(/filename=([^;]+)/i);
41
+ if (unquotedMatch) {
42
+ return unquotedMatch[1].trim();
43
+ }
44
+
45
+ return undefined;
46
+ }
47
+
48
+ function getExportFilename(response: Response): string | undefined {
49
+ const filename = parseContentDispositionFilename(
50
+ response.headers.get("Content-Disposition"),
51
+ );
52
+ if (!filename) {
53
+ Logger.warn(
54
+ "Export response is missing a readable Content-Disposition filename",
55
+ );
56
+ }
57
+ return filename;
26
58
  }
27
59
 
28
60
  /**
@@ -129,11 +161,14 @@ export const API = {
129
161
  }
130
162
  return Promise.resolve(response.data as T);
131
163
  },
132
- handleExportResponse: async <T extends BlobPart>(response: {
133
- data?: T | undefined;
134
- error?: Record<string, unknown>;
135
- response: Response;
136
- }): Promise<ExportedFile<T>> => {
164
+ handleExportResponse: async <T extends BlobPart>(
165
+ response: {
166
+ data?: T | undefined;
167
+ error?: Record<string, unknown>;
168
+ response: Response;
169
+ },
170
+ opts: { defaultFilename: string },
171
+ ): Promise<ExportedFile<T>> => {
137
172
  const contents = await API.handleResponse<T>(response);
138
173
  const mediaType = response.response.headers.get("Content-Type");
139
174
  if (!mediaType) {
@@ -142,7 +177,7 @@ export const API = {
142
177
 
143
178
  return {
144
179
  contents,
145
- filename: getExportFilename(response.response),
180
+ filename: getExportFilename(response.response) ?? opts.defaultFilename,
146
181
  mediaType,
147
182
  };
148
183
  },
@@ -0,0 +1,37 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { filenameAtom } from "@/core/saving/file-state";
4
+ import { store } from "@/core/state/jotai";
5
+ import { Filenames } from "@/utils/filenames";
6
+ import { Paths } from "@/utils/paths";
7
+ import {
8
+ DEFAULT_MARKDOWN_FLAVOR,
9
+ MARKDOWN_EXTENSIONS,
10
+ } from "./markdown-export";
11
+ import type { ExportAsMarkdownRequest } from "./types";
12
+
13
+ const DEFAULT_EXPORT_FILENAME = "download";
14
+
15
+ function getNotebookStem(): string | undefined {
16
+ const filename = store.get(filenameAtom);
17
+ if (!filename) {
18
+ return undefined;
19
+ }
20
+ const basename = Paths.basename(filename);
21
+ return Filenames.withoutExtension(basename);
22
+ }
23
+
24
+ export function getDefaultExportFilename(extension: string): string {
25
+ const stem = getNotebookStem();
26
+ if (stem) {
27
+ return `${stem}.${extension}`;
28
+ }
29
+ return `${DEFAULT_EXPORT_FILENAME}.${extension}`;
30
+ }
31
+
32
+ export function getDefaultMarkdownExportFilename(
33
+ flavor: ExportAsMarkdownRequest["flavor"],
34
+ ): string {
35
+ const extension = MARKDOWN_EXTENSIONS[flavor ?? DEFAULT_MARKDOWN_FLAVOR];
36
+ return getDefaultExportFilename(extension);
37
+ }
@@ -0,0 +1,19 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import type { MarkdownExportFlavor } from "./types";
4
+
5
+ export const MARKDOWN_EXTENSIONS = {
6
+ pymdown: "md",
7
+ qmd: "qmd",
8
+ mystmd: "myst.md",
9
+ mdx: "mdx",
10
+ } satisfies Record<MarkdownExportFlavor, string>;
11
+ export const MARKDOWN_SUFFIXES = [
12
+ ".myst.md",
13
+ ".markdown",
14
+ ".qmd",
15
+ ".mdx",
16
+ ".md",
17
+ ];
18
+
19
+ export const DEFAULT_MARKDOWN_FLAVOR: MarkdownExportFlavor = "pymdown";
@@ -3,6 +3,10 @@
3
3
  import { once } from "@/utils/once";
4
4
  import { getRuntimeManager } from "../runtime/config";
5
5
  import { API, createClientWithRuntimeManager } from "./api";
6
+ import {
7
+ getDefaultExportFilename,
8
+ getDefaultMarkdownExportFilename,
9
+ } from "./export-filename";
6
10
  import {
7
11
  waitForConnectionOpen,
8
12
  waitForConnectionOpenIfNotebook,
@@ -426,7 +430,11 @@ export function createNetworkRequests(): EditRequests & RunRequests {
426
430
  parseAs: "text",
427
431
  params: getParams(),
428
432
  })
429
- .then(handleExportResponse);
433
+ .then((response) =>
434
+ handleExportResponse(response, {
435
+ defaultFilename: getDefaultExportFilename("html"),
436
+ }),
437
+ );
430
438
  },
431
439
  exportAsMarkdown: async (request) => {
432
440
  return getClient()
@@ -435,7 +443,11 @@ export function createNetworkRequests(): EditRequests & RunRequests {
435
443
  parseAs: "text",
436
444
  params: getParams(),
437
445
  })
438
- .then(handleExportResponse);
446
+ .then((response) =>
447
+ handleExportResponse(response, {
448
+ defaultFilename: getDefaultMarkdownExportFilename(request.flavor),
449
+ }),
450
+ );
439
451
  },
440
452
  exportAsScript: async (request) => {
441
453
  return getClient()
@@ -444,7 +456,11 @@ export function createNetworkRequests(): EditRequests & RunRequests {
444
456
  parseAs: "text",
445
457
  params: getParams(),
446
458
  })
447
- .then(handleExportResponse);
459
+ .then((response) =>
460
+ handleExportResponse(response, {
461
+ defaultFilename: getDefaultExportFilename("script.py"),
462
+ }),
463
+ );
448
464
  },
449
465
  exportAsIPYNB: async (request) => {
450
466
  return getClient()
@@ -453,7 +469,11 @@ export function createNetworkRequests(): EditRequests & RunRequests {
453
469
  parseAs: "text",
454
470
  params: getParams(),
455
471
  })
456
- .then(handleExportResponse);
472
+ .then((response) =>
473
+ handleExportResponse(response, {
474
+ defaultFilename: getDefaultExportFilename("ipynb"),
475
+ }),
476
+ );
457
477
  },
458
478
  exportAsPDF: async (request) => {
459
479
  return getClient()
@@ -462,7 +482,11 @@ export function createNetworkRequests(): EditRequests & RunRequests {
462
482
  parseAs: "blob",
463
483
  params: getParams(),
464
484
  })
465
- .then(handleExportResponse);
485
+ .then((response) =>
486
+ handleExportResponse(response, {
487
+ defaultFilename: getDefaultExportFilename("pdf"),
488
+ }),
489
+ );
466
490
  },
467
491
  autoExportAsHTML: async (request) => {
468
492
  return getClient()
@@ -22,6 +22,9 @@ export type AutoExportAsMarkdownRequest =
22
22
  schemas["AutoExportAsMarkdownRequest"];
23
23
  export type ExportAsHTMLRequest = schemas["ExportAsHTMLRequest"];
24
24
  export type ExportAsMarkdownRequest = schemas["ExportAsMarkdownRequest"];
25
+ export type MarkdownExportFlavor = NonNullable<
26
+ ExportAsMarkdownRequest["flavor"]
27
+ >;
25
28
  export type ExportAsIPYNBRequest = schemas["ExportAsIPYNBRequest"];
26
29
  export type ExportAsScriptRequest = schemas["ExportAsScriptRequest"];
27
30
  export type ExportAsPDFRequest = schemas["ExportAsPDFRequest"];
@@ -61,5 +61,6 @@ export type KnownStorageProtocol =
61
61
  | "http"
62
62
  | "file"
63
63
  | "gdrive"
64
+ | "hf"
64
65
  | "in-memory"
65
66
  | "github";