@marimo-team/frontend 0.23.16-dev3 → 0.23.16-dev34

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 (230) hide show
  1. package/dist/assets/{CellStatus-BalYv0pK.js → CellStatus-CmojVYzm.js} +1 -1
  2. package/dist/assets/{ConnectedDataExplorerComponent-DI7mV-Gw.js → ConnectedDataExplorerComponent-CMaZapRd.js} +1 -1
  3. package/dist/assets/{ErrorBoundary-C7_5jjWi.js → ErrorBoundary-DzTD8LVq.js} +1 -1
  4. package/dist/assets/{ImperativeModal-BIavOiUv.js → ImperativeModal-fY-ZfT0b.js} +1 -1
  5. package/dist/assets/{JsonOutput-CkCD-FgM.js → JsonOutput-BwwRpByA.js} +12 -12
  6. package/dist/assets/{LazyAnyLanguageCodeMirror-sVm_hz8A.js → LazyAnyLanguageCodeMirror-B2JvSXP-.js} +2 -2
  7. package/dist/assets/{MarimoErrorOutput-DO2k2fA5.js → MarimoErrorOutput-BWte8vjk.js} +4 -4
  8. package/dist/assets/{RSPContexts-CgC81f0I.js → RSPContexts-WiCgGm__.js} +1 -1
  9. package/dist/assets/RenderHTML-DRltV71H.js +1 -0
  10. package/dist/assets/{RunButton-BKRupqr7.js → RunButton-DIegd2mN.js} +1 -1
  11. package/dist/assets/{add-cell-with-ai-CEZtaZCK.js → add-cell-with-ai-BBFD-M9V.js} +17 -17
  12. package/dist/assets/add-connection-dialog-CECJ8507.js +170 -0
  13. package/dist/assets/{agent-panel-CHB6t7Zy.js → agent-panel-YaS0WXP1.js} +5 -5
  14. package/dist/assets/ai-model-dropdown-BpVBUZnz.js +5 -0
  15. package/dist/assets/{alert-dialog-BXegNC0I.js → alert-dialog-Bb8Lz8ko.js} +1 -1
  16. package/dist/assets/{any-language-editor-DCpZRrpx.js → any-language-editor--tRHs8C_.js} +1 -1
  17. package/dist/assets/{app-config-button-NrM9ywXA.js → app-config-button-BW-w01aF.js} +1 -1
  18. package/dist/assets/{button-eIihcbUw.js → button-DDGiD1NW.js} +1 -1
  19. package/dist/assets/{cache-panel-wllSTlbM.js → cache-panel-DcQUeUpg.js} +1 -1
  20. package/dist/assets/{capabilities-B09j1Eor.js → capabilities-DoEg0XwL.js} +1 -1
  21. package/dist/assets/{cell-editor-B5JVq7_q.js → cell-editor-EV-1IccQ.js} +10 -10
  22. package/dist/assets/cell-link-C5gpAn3R.js +1 -0
  23. package/dist/assets/cells-6yd8nOAu.js +231 -0
  24. package/dist/assets/{chat-display-BG08ltQ3.js → chat-display-8BbC5B-D.js} +1 -1
  25. package/dist/assets/{chat-panel-P2CzCtXJ.js → chat-panel-CMOZp5xW.js} +3 -3
  26. package/dist/assets/{chat-ui-fAJZAoTD.js → chat-ui-DWhJ2Ozy.js} +1 -1
  27. package/dist/assets/{column-preview-Dne1dIt0.js → column-preview-DeXESYpM.js} +1 -1
  28. package/dist/assets/{command-CAJLPEBq.js → command-Dqxie4gq.js} +1 -1
  29. package/dist/assets/{command-palette-iOCqUIs1.js → command-palette-Dx6AkiPd.js} +1 -1
  30. package/dist/assets/{common-CD6Mj8n6.js → common-XVELED_O.js} +1 -1
  31. package/dist/assets/components-BLHpmhXx.js +1 -0
  32. package/dist/assets/config-B-Q7J_6d.js +1 -0
  33. package/dist/assets/{config-COUOIPEq.js → config-BJVx3eBW.js} +1 -1
  34. package/dist/assets/{copy-BtTOqPTS.js → copy-Bxb1_Gtw.js} +1 -1
  35. package/dist/assets/{copy-icon-C2pG0ZNK.js → copy-icon-neDQUo4Q.js} +1 -1
  36. package/dist/assets/{createReducer-C7kZ8zEe.js → createReducer-DGx5Af-C.js} +1 -1
  37. package/dist/assets/{datasource-ySAiC9gr.js → datasource-CTqa8bf-.js} +2 -2
  38. package/dist/assets/{dates-D-kIKDYA.js → dates-C2LWzsCZ.js} +1 -1
  39. package/dist/assets/{dependency-graph-panel-BF23EwcD.js → dependency-graph-panel-C4Nzcddp.js} +4 -4
  40. package/dist/assets/{dialog-D1DEsRZ2.js → dialog-BCVepWLJ.js} +1 -1
  41. package/dist/assets/{dist-ByVeMWkr.js → dist-BLN8PNJV.js} +1 -1
  42. package/dist/assets/dist-CiVShZKM.js +2 -0
  43. package/dist/assets/{documentation-panel-e9Fi9R7e.js → documentation-panel-Bym4G7IS.js} +1 -1
  44. package/dist/assets/download-HKgqJX4l.js +9 -0
  45. package/dist/assets/{dropdown-menu-BFZLnWeO.js → dropdown-menu-dHmZR8bI.js} +1 -1
  46. package/dist/assets/{edit-page-DSEkwJ4O.js → edit-page-Jtp6Qqjl.js} +6 -6
  47. package/dist/assets/{error-banner-B6eIlxzg.js → error-banner-BSxEOIn2.js} +1 -1
  48. package/dist/assets/{error-entries-Birw-8VX.js → error-entries-CxyvaxEM.js} +2 -2
  49. package/dist/assets/{error-panel-DdsUL6b7.js → error-panel-CfSy7uUr.js} +1 -1
  50. package/dist/assets/{es-1D83cgiu.js → es-gYgfF8y5.js} +1 -1
  51. package/dist/assets/{esm-BL7jQmzn.js → esm-B30N1GS0.js} +1 -1
  52. package/dist/assets/{field-BeZbVzXA.js → field-BsxD35VE.js} +1 -1
  53. package/dist/assets/file-explorer-panel-Dd5Xn2TK.js +26 -0
  54. package/dist/assets/{file-icons-Cb54mIL8.js → file-icons-CSjJ3JC6.js} +1 -1
  55. package/dist/assets/{file-name-input-BWFcK1ey.js → file-name-input-CrKtrXMl.js} +1 -1
  56. package/dist/assets/floating-outline-ByQ8vvif.js +1 -0
  57. package/dist/assets/focus-CVONl-Dy.js +1 -0
  58. package/dist/assets/{form-B6BkSCJb.js → form-Ca3yd_Od.js} +1 -1
  59. package/dist/assets/{formats-ClEFGPy-.js → formats-DzmLpc9A.js} +1 -1
  60. package/dist/assets/{formatting-B_41gSF5.js → formatting-CDf6w1Kt.js} +1 -1
  61. package/dist/assets/{fullscreen-dUjPYOmm.js → fullscreen-CTDqNQr6.js} +1 -1
  62. package/dist/assets/{gallery-page-CWQQAYpa.js → gallery-page-CJ3ms1Hn.js} +1 -1
  63. package/dist/assets/{glide-data-editor-lMARFWHS.js → glide-data-editor-j2igs9Y_.js} +1 -1
  64. package/dist/assets/home-page-FpAawj9s.js +4 -0
  65. package/dist/assets/{html-to-image-CwmX28T9.js → html-to-image-C-4ysbGe.js} +2 -2
  66. package/dist/assets/index-CiJmYQBT.js +38 -0
  67. package/dist/assets/index-xs-zSSrX.css +2 -0
  68. package/dist/assets/{input-CqrUjynb.js → input-JxFwu-3G.js} +1 -1
  69. package/dist/assets/kiosk-mode-BumOFEtY.js +1 -0
  70. package/dist/assets/{label-BElwWzf0.js → label-CqjrpwRC.js} +1 -1
  71. package/dist/assets/{layout-CUlZijwG.js → layout-Dtql_o_T.js} +5 -5
  72. package/dist/assets/{links-CToBjIIj.js → links-CZr5bjvo.js} +1 -1
  73. package/dist/assets/{logs-panel-iFV4ZJ9b.js → logs-panel-5u1wUBn1.js} +1 -1
  74. package/dist/assets/{markdown-renderer-QvXG0CRk.js → markdown-renderer-dABDqZDw.js} +2 -2
  75. package/dist/assets/{menu-items-DbSLBfXR.js → menu-items-CCi6aOPm.js} +1 -1
  76. package/dist/assets/{mermaid-Bh0flEIb.js → mermaid-CYN0wOyG.js} +1 -1
  77. package/dist/assets/name-cell-input-Y4knmEIU.js +1 -0
  78. package/dist/assets/node-sql-parser-Si4oZ2P-.js +68 -0
  79. package/dist/assets/{numbers-CeeaFKqu.js → numbers-rmawRqQP.js} +1 -1
  80. package/dist/assets/outline-panel-CQ_KWLip.js +1 -0
  81. package/dist/assets/{packages-panel-BzQF60rG.js → packages-panel-Ijt7d51P.js} +1 -1
  82. package/dist/assets/pair-with-agent-modal-BCRSACei.js +4 -0
  83. package/dist/assets/panels-DfU1Oikj.js +4 -0
  84. package/dist/assets/{popover-DrHQ3KEt.js → popover-Bcz1Zikb.js} +1 -1
  85. package/dist/assets/process-output-Dd11YI04.js +1 -0
  86. package/dist/assets/{radio-group-CLBjGkt5.js → radio-group-22eWJ9fv.js} +1 -1
  87. package/dist/assets/readonly-code-display-BjK-TEri.js +1 -0
  88. package/dist/assets/{readonly-python-code-CW_G5e4E.js → readonly-python-code-n_TrLmsR.js} +1 -1
  89. package/dist/assets/{renderShortcut-BZLr8D7H.js → renderShortcut-Bl8_FVn8.js} +1 -1
  90. package/dist/assets/{reveal-component-BLMTmd8T.js → reveal-component-CCLTV1q6.js} +60 -60
  91. package/dist/assets/{run-page-B6pgDYEJ.js → run-page-DbezK7Qs.js} +1 -1
  92. package/dist/assets/{runs-D51Xwrsk.js → runs-DBTXqJHS.js} +1 -1
  93. package/dist/assets/scratchpad-panel-C2wAEv5n.js +1 -0
  94. package/dist/assets/{scroll-to-bottom-button-Cgr10kaJ.js → scroll-to-bottom-button-C8yfT4wq.js} +1 -1
  95. package/dist/assets/{secrets-panel-Cc5KDBk6.js → secrets-panel-6J6wcuVR.js} +1 -1
  96. package/dist/assets/{select-B5TFNjmU.js → select-BP75_5jJ.js} +1 -1
  97. package/dist/assets/{session-CEFnN9Cq.js → session-BmukRjSi.js} +1 -1
  98. package/dist/assets/{session-panel-Y0Oon0sD.js → session-panel-BZGNcZmp.js} +1 -1
  99. package/dist/assets/{share-CYpcX4N4.js → share-852X_cqy.js} +1 -1
  100. package/dist/assets/{skeleton-D3QPxqWC.js → skeleton-BzlKcq21.js} +1 -1
  101. package/dist/assets/slides-DkEgzTJB.css +1 -0
  102. package/dist/assets/{snippets-panel-CXpGflMO.js → snippets-panel-DNaNtkR0.js} +1 -1
  103. package/dist/assets/{spec-CzqcmYQk.js → spec-C7opZZRp.js} +1 -1
  104. package/dist/assets/{state-BvdK8eiw.js → state-D10U2MwA.js} +1 -1
  105. package/dist/assets/{state-WBgc7MiA.js → state-DDag0B_B.js} +1 -1
  106. package/dist/assets/state-DFPAfLw7.js +3 -0
  107. package/dist/assets/{state-DBzEQtWz.js → state-jCdh8Dcx.js} +1 -1
  108. package/dist/assets/{strings-Djnbwmfl.js → strings--BknHX9C.js} +1 -1
  109. package/dist/assets/{swiper-component-DInvCxhI.js → swiper-component-g8EXE911.js} +1 -1
  110. package/dist/assets/switch-OVajJBPR.js +1 -0
  111. package/dist/assets/{terminal-D3IQBBYo.js → terminal-217O8hlZ.js} +1 -1
  112. package/dist/assets/{textarea-CpFfEUet.js → textarea-DuEtSOCl.js} +1 -1
  113. package/dist/assets/{tooltip-CFX4fYHU.js → tooltip-4bZlU7jl.js} +1 -1
  114. package/dist/assets/{tooltip-HLb6RxVp.js → tooltip-B7U4xkIN.js} +1 -1
  115. package/dist/assets/{tracing-panel-BU-o_LCd.js → tracing-panel-rVAa3En0.js} +2 -2
  116. package/dist/assets/{tracing-CKn3gWHw.js → tracing-ttUd3J7U.js} +1 -1
  117. package/dist/assets/{tree-actions-CsC2XmGo.js → tree-actions-UCr1tMHw.js} +1 -1
  118. package/dist/assets/{useBoolean-D89u30n-.js → useBoolean-CFj86iac.js} +1 -1
  119. package/dist/assets/useCellActionButton-CJknR0XY.js +1 -0
  120. package/dist/assets/{useDeleteCell-EPH4kcG7.js → useDeleteCell-PrgejW8A.js} +1 -1
  121. package/dist/assets/{useDependencyPanelTab-CZnK7N2L.js → useDependencyPanelTab-5jc-_-hy.js} +1 -1
  122. package/dist/assets/useEventListener-C0195JsF.js +1 -0
  123. package/dist/assets/{useHotkey-DIuRH8Wu.js → useHotkey-DLZvrRZT.js} +1 -1
  124. package/dist/assets/{useIframeCapabilities-Ccb4fvKc.js → useIframeCapabilities-j6fR_dBA.js} +1 -1
  125. package/dist/assets/{useInstallPackage-BRbUAUFn.js → useInstallPackage-WiYBwhUO.js} +1 -1
  126. package/dist/assets/{useInterval-CC0vKpWm.js → useInterval-CWBTUy5W.js} +1 -1
  127. package/dist/assets/useNotebookActions-4vw_NiRs.js +12 -0
  128. package/dist/assets/useRunCells-DHsd7HGL.js +1 -0
  129. package/dist/assets/{useSplitCell-9Qz93yPZ.js → useSplitCell-MZpER0Bh.js} +1 -1
  130. package/dist/assets/{useTheme-KXqAyDPC.js → useTheme-Bxv9B31g.js} +1 -1
  131. package/dist/assets/{utils-Z80SV4_d.js → utils-BWBBpCkT.js} +1 -1
  132. package/dist/assets/{vega-component-Cie3ag_T.js → vega-component-Db8KjZJG.js} +1 -1
  133. package/dist/assets/{write-secret-modal-DEEn7sZV.js → write-secret-modal-3-v-dHVj.js} +1 -1
  134. package/dist/index.html +68 -68
  135. package/package.json +2 -2
  136. package/src/__mocks__/requests.ts +45 -4
  137. package/src/components/data-table/__tests__/__snapshots__/chart-spec-model.test.ts.snap +174 -21
  138. package/src/components/data-table/__tests__/chart-spec-model.test.ts +102 -1
  139. package/src/components/data-table/column-summary/chart-spec-model.tsx +163 -103
  140. package/src/components/data-table/column-summary/legacy-chart-spec.ts +15 -7
  141. package/src/components/data-table/column-summary/utils.ts +26 -7
  142. package/src/components/data-table/types.ts +1 -1
  143. package/src/components/editor/actions/__tests__/pair-with-agent-commands.test.ts +99 -18
  144. package/src/components/editor/actions/__tests__/pdf-export.test.ts +0 -4
  145. package/src/components/editor/actions/pair-with-agent-commands.ts +21 -10
  146. package/src/components/editor/actions/pair-with-agent-modal.tsx +2 -2
  147. package/src/components/editor/actions/pdf-export.ts +1 -4
  148. package/src/components/editor/actions/useNotebookActions.tsx +7 -13
  149. package/src/components/editor/connections/storage/__tests__/__snapshots__/as-code.test.ts.snap +50 -0
  150. package/src/components/editor/connections/storage/__tests__/as-code.test.ts +87 -8
  151. package/src/components/editor/connections/storage/as-code.ts +40 -21
  152. package/src/components/editor/connections/storage/schemas.ts +83 -42
  153. package/src/components/editor/file-tree/__tests__/file-render-mode.test.ts +27 -0
  154. package/src/components/editor/file-tree/__tests__/file-viewer.test.tsx +150 -0
  155. package/src/components/editor/file-tree/file-preview-metadata.tsx +51 -0
  156. package/src/components/editor/file-tree/file-render-mode.ts +32 -0
  157. package/src/components/editor/file-tree/file-viewer.tsx +76 -50
  158. package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +1 -1
  159. package/src/components/slides/__tests__/cell-output-block.test.tsx +40 -0
  160. package/src/components/slides/__tests__/slide-cell-view.test.tsx +33 -0
  161. package/src/components/slides/__tests__/slide-scroll-hint.test.tsx +198 -0
  162. package/src/components/slides/reveal-component.tsx +25 -19
  163. package/src/components/slides/slide-cell-view.tsx +1 -1
  164. package/src/components/slides/slide-scroll-hint.tsx +161 -0
  165. package/src/components/slides/slides.css +4 -2
  166. package/src/components/static-html/share-modal.tsx +1 -1
  167. package/src/components/ui/input.tsx +1 -1
  168. package/src/core/codemirror/find-replace/__tests__/navigate.test.ts +42 -30
  169. package/src/core/codemirror/find-replace/navigate.ts +2 -0
  170. package/src/core/codemirror/go-to-definition/__tests__/commands.test.ts +24 -3
  171. package/src/core/codemirror/go-to-definition/commands.ts +3 -0
  172. package/src/core/codemirror/keymaps/__tests__/keymaps.test.ts +31 -2
  173. package/src/core/codemirror/keymaps/keymaps.ts +12 -0
  174. package/src/core/codemirror/language/__tests__/sql.test.ts +443 -61
  175. package/src/core/codemirror/language/languages/sql/completion-store.ts +5 -1
  176. package/src/core/codemirror/language/languages/sql/sql.ts +210 -3
  177. package/src/core/codemirror/utils.ts +28 -0
  178. package/src/core/hotkeys/hotkeys.ts +0 -2
  179. package/src/core/islands/__tests__/bridge.test.ts +107 -0
  180. package/src/core/islands/bridge.ts +16 -0
  181. package/src/core/mode.ts +10 -0
  182. package/src/core/network/__tests__/api.test.ts +42 -0
  183. package/src/core/network/__tests__/connection.test.ts +45 -0
  184. package/src/core/network/__tests__/requests-lazy.test.ts +32 -1
  185. package/src/core/network/__tests__/requests-network.test.ts +21 -0
  186. package/src/core/network/api.ts +27 -0
  187. package/src/core/network/connection.ts +15 -0
  188. package/src/core/network/requests-lazy.ts +1 -0
  189. package/src/core/network/requests-network.ts +14 -8
  190. package/src/core/network/requests-static.ts +1 -0
  191. package/src/core/network/requests-toasting.tsx +1 -0
  192. package/src/core/network/types.ts +25 -7
  193. package/src/core/static/download-html.ts +5 -14
  194. package/src/core/wasm/bridge.ts +4 -2
  195. package/src/core/wasm/worker/types.ts +5 -2
  196. package/src/plugins/impl/FormPlugin.tsx +1 -1
  197. package/src/plugins/impl/__tests__/FormPlugin.test.tsx +162 -0
  198. package/src/utils/__tests__/download.test.tsx +24 -1
  199. package/src/utils/download.ts +11 -6
  200. package/dist/assets/RenderHTML-BCvU9dYz.js +0 -1
  201. package/dist/assets/add-connection-dialog-B-jCRNTN.js +0 -170
  202. package/dist/assets/ai-model-dropdown-DXgLphFa.js +0 -5
  203. package/dist/assets/cell-link-DqBkyAFe.js +0 -1
  204. package/dist/assets/cells-Cf7bHHq3.js +0 -229
  205. package/dist/assets/components-BH6gDVv6.js +0 -1
  206. package/dist/assets/config-BbOTTCYY.js +0 -1
  207. package/dist/assets/dist-BCMBu0rO.js +0 -2
  208. package/dist/assets/download-BomYt3nl.js +0 -9
  209. package/dist/assets/file-explorer-panel-BfXM2BOd.js +0 -26
  210. package/dist/assets/floating-outline-wz-WSRgP.js +0 -1
  211. package/dist/assets/focus-DiEyfiAG.js +0 -1
  212. package/dist/assets/home-page-DJ4jJsZj.js +0 -4
  213. package/dist/assets/index-A71LAt6h.css +0 -2
  214. package/dist/assets/index-Dbc5mg7a.js +0 -38
  215. package/dist/assets/kiosk-mode-ABm3oYgq.js +0 -1
  216. package/dist/assets/name-cell-input-C6cukQ9k.js +0 -1
  217. package/dist/assets/node-sql-parser-BWgUjcfV.js +0 -68
  218. package/dist/assets/outline-panel-BPlg9_CE.js +0 -1
  219. package/dist/assets/pair-with-agent-modal-CKOYwOYw.js +0 -4
  220. package/dist/assets/panels-Dj45fW-v.js +0 -4
  221. package/dist/assets/process-output-BEhIGfkO.js +0 -1
  222. package/dist/assets/readonly-code-display-D63oRtBP.js +0 -1
  223. package/dist/assets/scratchpad-panel-B0mEqBNk.js +0 -1
  224. package/dist/assets/slides-MCbMDtlY.css +0 -1
  225. package/dist/assets/state-BZQc7xxL.js +0 -3
  226. package/dist/assets/switch-CvweaCXq.js +0 -1
  227. package/dist/assets/useCellActionButton-CRfIEVB5.js +0 -1
  228. package/dist/assets/useEventListener-B_w3A283.js +0 -1
  229. package/dist/assets/useNotebookActions-DTXAoukG.js +0 -12
  230. package/dist/assets/useRunCells-CSSF_mJ0.js +0 -1
@@ -3,7 +3,11 @@
3
3
  import dedent from "string-dedent";
4
4
  import { assertNever } from "@/utils/assertNever";
5
5
  import { isSecret, unprefixSecret } from "../secrets";
6
- import { type StorageConnection, StorageConnectionSchema } from "./schemas";
6
+ import {
7
+ type S3Auth,
8
+ type StorageConnection,
9
+ StorageConnectionSchema,
10
+ } from "./schemas";
7
11
 
8
12
  export type StorageLibrary = "obstore" | "fsspec";
9
13
 
@@ -50,6 +54,36 @@ class SecretContainer {
50
54
  }
51
55
  }
52
56
 
57
+ /**
58
+ * Credential kwargs shared by every S3-compatible store.
59
+ */
60
+ function s3AuthParams(
61
+ auth: S3Auth | undefined,
62
+ secrets: SecretContainer,
63
+ ): string[] {
64
+ // obstore polls the credential endpoint with the token read from the file,
65
+ // refreshing credentials for the lifetime of the store.
66
+ if (auth?.type === "Container credentials") {
67
+ return [
68
+ ` container_credentials_full_uri=${secrets.print("container_credentials_full_uri", auth.container_credentials_full_uri)},`,
69
+ ` container_authorization_token_file=${secrets.print("container_authorization_token_file", auth.container_authorization_token_file)},`,
70
+ ];
71
+ }
72
+
73
+ const params: string[] = [];
74
+ if (auth?.access_key_id) {
75
+ params.push(
76
+ ` access_key_id=${secrets.print("access_key_id", auth.access_key_id)},`,
77
+ );
78
+ }
79
+ if (auth?.secret_access_key) {
80
+ params.push(
81
+ ` secret_access_key=${secrets.print("secret_access_key", auth.secret_access_key)},`,
82
+ );
83
+ }
84
+ return params;
85
+ }
86
+
53
87
  function generateS3Code(
54
88
  connection: Extract<StorageConnection, { type: "s3" }>,
55
89
  secrets: SecretContainer,
@@ -61,21 +95,15 @@ function generateS3Code(
61
95
  if (connection.region) {
62
96
  params.push(` region=${secrets.print("region", connection.region)},`);
63
97
  }
64
- if (connection.access_key_id) {
65
- params.push(
66
- ` access_key_id=${secrets.print("access_key_id", connection.access_key_id)},`,
67
- );
68
- }
69
- if (connection.secret_access_key) {
70
- params.push(
71
- ` secret_access_key=${secrets.print("secret_access_key", connection.secret_access_key)},`,
72
- );
73
- }
98
+ params.push(...s3AuthParams(connection.auth, secrets));
74
99
  if (connection.endpoint_url) {
75
100
  params.push(
76
101
  ` endpoint_url=${secrets.print("endpoint_url", connection.endpoint_url)},`,
77
102
  );
78
103
  }
104
+ if (connection.allow_http) {
105
+ params.push(" allow_http=True,");
106
+ }
79
107
 
80
108
  const paramsStr = params.length > 0 ? `\n${params.join("\n")}\n` : "";
81
109
 
@@ -140,16 +168,7 @@ function generateCoreWeaveCode(
140
168
  ` region=${secrets.print("region", connection.region)},`,
141
169
  ];
142
170
 
143
- if (connection.access_key_id) {
144
- params.push(
145
- ` access_key_id=${secrets.print("access_key_id", connection.access_key_id)},`,
146
- );
147
- }
148
- if (connection.secret_access_key) {
149
- params.push(
150
- ` secret_access_key=${secrets.print("secret_access_key", connection.secret_access_key)},`,
151
- );
152
- }
171
+ params.push(...s3AuthParams(connection.auth, secrets));
153
172
 
154
173
  params.push(
155
174
  ` endpoint="https://${connection.bucket}.cwobject.com",`,
@@ -2,6 +2,83 @@
2
2
  import { z } from "zod";
3
3
  import { FieldOptions } from "@/components/forms/options";
4
4
 
5
+ /**
6
+ * Credentials shared by every S3-compatible store (Amazon S3, CoreWeave, ...).
7
+ *
8
+ * Rendered as tabs: either long-lived access keys, or a credential-vending
9
+ * endpoint (ECS/EKS task roles, CoreWeave sandboxes, ...) that obstore polls
10
+ * with a bearer token read from disk.
11
+ */
12
+ const s3AuthField = () =>
13
+ z
14
+ .discriminatedUnion("type", [
15
+ z.object({
16
+ type: z.literal("Access keys"),
17
+ access_key_id: z
18
+ .string()
19
+ .optional()
20
+ .describe(
21
+ FieldOptions.of({
22
+ label: "Access Key ID",
23
+ description: "Leave empty to use the default credential chain",
24
+ inputType: "password",
25
+ optionRegex:
26
+ "(access.?key.?id|object.?storage.?key|aws.?access.?key)",
27
+ }),
28
+ ),
29
+ secret_access_key: z
30
+ .string()
31
+ .optional()
32
+ .describe(
33
+ FieldOptions.of({
34
+ label: "Secret Access Key",
35
+ inputType: "password",
36
+ optionRegex:
37
+ "(secret.?access.?key|object.?storage.?secret|aws.?secret)",
38
+ }),
39
+ ),
40
+ }),
41
+ z.object({
42
+ type: z.literal("Container credentials"),
43
+ container_credentials_full_uri: z
44
+ .string()
45
+ .nonempty()
46
+ .describe(
47
+ FieldOptions.of({
48
+ label: "Credentials URI",
49
+ description: "Endpoint that vends credentials to the container",
50
+ placeholder: "http://169.254.170.23/v1/credentials",
51
+ optionRegex: "container.?credentials",
52
+ }),
53
+ ),
54
+ container_authorization_token_file: z
55
+ .string()
56
+ .nonempty()
57
+ .describe(
58
+ FieldOptions.of({
59
+ label: "Authorization Token File",
60
+ description: "File holding the token sent to the credentials URI",
61
+ placeholder: "/var/run/secrets/token",
62
+ optionRegex:
63
+ "(container.?authorization.?token.?file|container.?auth.?token)",
64
+ }),
65
+ ),
66
+ }),
67
+ ])
68
+ .default({ type: "Access keys" })
69
+ .describe(FieldOptions.of({ label: "Credentials", special: "tabs" }));
70
+
71
+ const allowHttpField = () =>
72
+ z
73
+ .boolean()
74
+ .default(false)
75
+ .describe(
76
+ FieldOptions.of({
77
+ label: "Allow HTTP",
78
+ description: "Required for plain-http (non-TLS) endpoints",
79
+ }),
80
+ );
81
+
5
82
  export const S3StorageSchema = z
6
83
  .object({
7
84
  type: z.literal("s3"),
@@ -25,36 +102,20 @@ export const S3StorageSchema = z
25
102
  optionRegex: "(region|aws.?region)",
26
103
  }),
27
104
  ),
28
- access_key_id: z
29
- .string()
30
- .optional()
31
- .describe(
32
- FieldOptions.of({
33
- label: "Access Key ID",
34
- inputType: "password",
35
- optionRegex: "(access.?key.?id|aws.?access.?key)",
36
- }),
37
- ),
38
- secret_access_key: z
39
- .string()
40
- .optional()
41
- .describe(
42
- FieldOptions.of({
43
- label: "Secret Access Key",
44
- inputType: "password",
45
- optionRegex: "(secret.?access.?key|aws.?secret)",
46
- }),
47
- ),
48
105
  endpoint_url: z
49
106
  .string()
50
107
  .optional()
51
108
  .describe(
52
109
  FieldOptions.of({
53
110
  label: "Endpoint URL",
111
+ description:
112
+ "Ignored if the AWS_ENDPOINT_URL_S3 environment variable is set",
54
113
  placeholder: "https://s3.amazonaws.com",
55
114
  optionRegex: "(endpoint|s3.?url|s3.?endpoint)",
56
115
  }),
57
116
  ),
117
+ allow_http: allowHttpField(),
118
+ auth: s3AuthField(),
58
119
  })
59
120
  .describe(FieldOptions.of({ direction: "two-columns" }));
60
121
 
@@ -139,28 +200,7 @@ export const CoreWeaveStorageSchema = z
139
200
  placeholder: "US-EAST-04A",
140
201
  }),
141
202
  ),
142
- access_key_id: z
143
- .string()
144
- .optional()
145
- .describe(
146
- FieldOptions.of({
147
- label: "Access Key ID",
148
- inputType: "password",
149
- optionRegex:
150
- "(access.?key.?id|object.?storage.?key|aws.?access.?key)",
151
- }),
152
- ),
153
- secret_access_key: z
154
- .string()
155
- .optional()
156
- .describe(
157
- FieldOptions.of({
158
- label: "Secret Access Key",
159
- inputType: "password",
160
- optionRegex:
161
- "(secret.?access.?key|object.?storage.?secret|aws.?secret)",
162
- }),
163
- ),
203
+ auth: s3AuthField(),
164
204
  })
165
205
  .describe(FieldOptions.of({ direction: "two-columns" }));
166
206
 
@@ -189,3 +229,4 @@ export const StorageConnectionSchema = z.discriminatedUnion("type", [
189
229
  ]);
190
230
 
191
231
  export type StorageConnection = z.infer<typeof StorageConnectionSchema>;
232
+ export type S3Auth = Extract<StorageConnection, { type: "s3" }>["auth"];
@@ -0,0 +1,27 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { describe, expect, it } from "vitest";
4
+ import { getFileRenderMode } from "../file-render-mode";
5
+
6
+ describe("getFileRenderMode", () => {
7
+ it.each([
8
+ ["text/plain", false, "text"],
9
+ ["text/x-rust", false, "text"],
10
+ ["application/json", false, "text"],
11
+ ["application/xml", false, "text"],
12
+ ["text/csv", false, "csv"],
13
+ ["image/png", true, "media"],
14
+ ["video/mp4", true, "media"],
15
+ ["application/pdf", true, "media"],
16
+ ["application/x-apple-diskimage", true, "unsupported"],
17
+ ["application/zip", true, "unsupported"],
18
+ ["application/octet-stream", false, "unsupported"],
19
+ [null, false, "text"],
20
+ [null, true, "unsupported"],
21
+ ] as const)(
22
+ "maps %s with isBase64=%s to %s",
23
+ (mimeType, isBase64, expected) => {
24
+ expect(getFileRenderMode(mimeType, isBase64)).toBe(expected);
25
+ },
26
+ );
27
+ });
@@ -0,0 +1,150 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { fireEvent, render, screen, waitFor } from "@testing-library/react";
4
+ import { Provider } from "jotai";
5
+ import type React from "react";
6
+ import { beforeEach, describe, expect, it, vi } from "vitest";
7
+ import { MockRequestClient } from "@/__mocks__/requests";
8
+ import { TooltipProvider } from "@/components/ui/tooltip";
9
+ import { requestClientAtom } from "@/core/network/requests";
10
+ import type { FileDetailsResponse, FileInfo } from "@/core/network/types";
11
+ import { store } from "@/core/state/jotai";
12
+ import { FileViewer, MAX_FILE_PREVIEW_BYTES } from "../file-viewer";
13
+
14
+ vi.mock("@/plugins/impl/code/LazyAnyLanguageCodeMirror", () => ({
15
+ LazyAnyLanguageCodeMirror: ({
16
+ value,
17
+ onChange,
18
+ }: {
19
+ value?: string;
20
+ onChange?: (value: string) => void;
21
+ }) => (
22
+ <textarea
23
+ data-testid="code-editor"
24
+ value={value ?? ""}
25
+ onChange={(evt) => onChange?.(evt.target.value)}
26
+ />
27
+ ),
28
+ }));
29
+
30
+ vi.mock("@/core/wasm/utils", () => ({ isWasm: () => false }));
31
+
32
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
33
+ <Provider store={store}>
34
+ <TooltipProvider>{children}</TooltipProvider>
35
+ </Provider>
36
+ );
37
+
38
+ const file: FileInfo = {
39
+ id: "/workspace/installer.dmg",
40
+ path: "/workspace/installer.dmg",
41
+ name: "installer.dmg",
42
+ isDirectory: false,
43
+ isMarimoFile: false,
44
+ size: 20 * 1024 * 1024,
45
+ };
46
+
47
+ function renderViewer(response: FileDetailsResponse) {
48
+ const client = MockRequestClient.create({
49
+ sendFileDetails: vi.fn().mockResolvedValue(response),
50
+ });
51
+ store.set(requestClientAtom, client);
52
+ render(<FileViewer file={file} onOpenNotebook={vi.fn()} />, { wrapper });
53
+ return client;
54
+ }
55
+
56
+ describe("FileViewer bounded previews", () => {
57
+ beforeEach(() => {
58
+ store.set(requestClientAtom, null);
59
+ });
60
+
61
+ it("requests a bounded preview and shows oversized metadata", async () => {
62
+ const client = renderViewer({
63
+ file,
64
+ contents: null,
65
+ mimeType: "application/x-apple-diskimage",
66
+ isBase64: false,
67
+ isTooLarge: true,
68
+ });
69
+
70
+ expect(await screen.findByText(/too large to preview/i)).toBeVisible();
71
+ expect(screen.getByText("20 MB")).toBeVisible();
72
+ expect(screen.getByRole("button", { name: /download/i })).toBeVisible();
73
+ expect(screen.queryByTestId("code-editor")).not.toBeInTheDocument();
74
+ expect(client.sendFileDetails).toHaveBeenCalledWith({
75
+ path: file.path,
76
+ maxBytes: MAX_FILE_PREVIEW_BYTES,
77
+ });
78
+ });
79
+
80
+ it("shows unsupported metadata with a download action, not base64 text", async () => {
81
+ renderViewer({
82
+ file: { ...file, size: 4 },
83
+ contents: "AAEC",
84
+ mimeType: "application/x-apple-diskimage",
85
+ isBase64: true,
86
+ isTooLarge: false,
87
+ });
88
+
89
+ expect(await screen.findByText(/cannot be previewed/i)).toBeVisible();
90
+ expect(screen.getByRole("button", { name: /download/i })).toBeVisible();
91
+ expect(screen.queryByTestId("code-editor")).not.toBeInTheDocument();
92
+ });
93
+
94
+ it("continues to render small text files", async () => {
95
+ renderViewer({
96
+ file: { ...file, name: "notes.txt", size: 5 },
97
+ contents: "hello",
98
+ mimeType: "text/plain",
99
+ isBase64: false,
100
+ isTooLarge: false,
101
+ });
102
+
103
+ await waitFor(() => {
104
+ expect(screen.getByTestId("code-editor")).toBeInTheDocument();
105
+ });
106
+ expect(screen.queryByText(/too large to preview/i)).not.toBeInTheDocument();
107
+ });
108
+
109
+ it("preserves edits to an initially empty text file across remount", async () => {
110
+ const emptyFile: FileInfo = {
111
+ id: "/workspace/empty.txt",
112
+ path: "/workspace/empty.txt",
113
+ name: "empty.txt",
114
+ isDirectory: false,
115
+ isMarimoFile: false,
116
+ size: 0,
117
+ };
118
+ const response: FileDetailsResponse = {
119
+ file: emptyFile,
120
+ contents: "",
121
+ mimeType: "text/plain",
122
+ isBase64: false,
123
+ isTooLarge: false,
124
+ };
125
+ const client = MockRequestClient.create({
126
+ sendFileDetails: vi.fn().mockResolvedValue(response),
127
+ });
128
+ store.set(requestClientAtom, client);
129
+
130
+ const { unmount } = render(
131
+ <FileViewer file={emptyFile} onOpenNotebook={vi.fn()} />,
132
+ { wrapper },
133
+ );
134
+
135
+ const editor =
136
+ await screen.findByTestId<HTMLTextAreaElement>("code-editor");
137
+ fireEvent.change(editor, { target: { value: "draft" } });
138
+ unmount();
139
+
140
+ render(<FileViewer file={emptyFile} onOpenNotebook={vi.fn()} />, {
141
+ wrapper,
142
+ });
143
+
144
+ const reopened =
145
+ await screen.findByTestId<HTMLTextAreaElement>("code-editor");
146
+ await waitFor(() => {
147
+ expect(reopened.value).toBe("draft");
148
+ });
149
+ });
150
+ });
@@ -0,0 +1,51 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { DownloadIcon } from "lucide-react";
4
+ import { useLocale } from "react-aria";
5
+ import { Button } from "@/components/ui/button";
6
+ import type { FileInfo } from "@/core/network/types";
7
+ import { formatBytes } from "@/utils/formatting";
8
+
9
+ interface Props {
10
+ file: FileInfo;
11
+ mimeType: string;
12
+ message: string;
13
+ onDownload?: () => void;
14
+ }
15
+
16
+ export const FilePreviewMetadata = ({
17
+ file,
18
+ mimeType,
19
+ message,
20
+ onDownload,
21
+ }: Props) => {
22
+ const { locale } = useLocale();
23
+ return (
24
+ <div className="p-4 text-sm">
25
+ <div className="grid grid-cols-[auto_1fr] gap-x-4 gap-y-2">
26
+ <span className="text-muted-foreground font-medium">Path</span>
27
+ <span className="font-mono text-xs break-all">{file.path}</span>
28
+ <span className="text-muted-foreground font-medium">Type</span>
29
+ <span>{mimeType}</span>
30
+ {file.size != null && (
31
+ <>
32
+ <span className="text-muted-foreground font-medium">Size</span>
33
+ <span>{formatBytes(file.size, locale)}</span>
34
+ </>
35
+ )}
36
+ </div>
37
+ <div className="mt-4 text-muted-foreground italic">{message}</div>
38
+ {onDownload && (
39
+ <Button
40
+ variant="outline"
41
+ size="sm"
42
+ className="mt-4"
43
+ onClick={onDownload}
44
+ >
45
+ <DownloadIcon className="h-3.5 w-3.5 mr-2" />
46
+ Download
47
+ </Button>
48
+ )}
49
+ </div>
50
+ );
51
+ };
@@ -0,0 +1,32 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { isMediaMime, MIME_TO_LANGUAGE } from "./renderers";
4
+
5
+ export type FileRenderMode = "text" | "csv" | "media" | "unsupported";
6
+
7
+ export function getFileRenderMode(
8
+ mimeType: string | null | undefined,
9
+ isBase64: boolean | undefined,
10
+ ): FileRenderMode {
11
+ const mime = mimeType || "text/plain";
12
+
13
+ if (isMediaMime(mime)) {
14
+ return "media";
15
+ }
16
+ if (isBase64) {
17
+ return "unsupported";
18
+ }
19
+ if (mime === "text/csv") {
20
+ return "csv";
21
+ }
22
+ if (
23
+ mime.startsWith("text/") ||
24
+ (mime !== "default" && mime in MIME_TO_LANGUAGE)
25
+ ) {
26
+ return "text";
27
+ }
28
+ if (mimeType == null) {
29
+ return "text";
30
+ }
31
+ return "unsupported";
32
+ }