@marimo-team/frontend 0.25.3-dev13 → 0.25.3-dev134

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 (565) hide show
  1. package/dist/assets/CellStatus-DC6FFQOg.js +1 -0
  2. package/dist/assets/{ConnectedDataExplorerComponent-TprnxZeJ.js → ConnectedDataExplorerComponent-BV1mVpaE.js} +1 -1
  3. package/dist/assets/{ErrorBoundary-CM13wrd8.js → ErrorBoundary-Ce-Ujlwj.js} +2 -2
  4. package/dist/assets/JsonOutput-CfEBzaqW.js +11 -0
  5. package/dist/assets/{LazyAnyLanguageCodeMirror-B4fCknq1.js → LazyAnyLanguageCodeMirror-DoxWX_Wp.js} +2 -2
  6. package/dist/assets/MarimoErrorOutput-Bh6o9zle.js +10 -0
  7. package/dist/assets/{RenderHTML-DX6i99Op.js → RenderHTML-COGZsXQV.js} +1 -1
  8. package/dist/assets/RunButton-DQ51plUv.js +1 -0
  9. package/dist/assets/__vite-browser-external-BynxqS4w.js +1 -0
  10. package/dist/assets/__vite-browser-external-DTOT9NDN.js +1 -0
  11. package/dist/assets/{add-cell-with-ai-BpD_790A.js → add-cell-with-ai-D7gtGUsg.js} +6 -6
  12. package/dist/assets/{add-connection-dialog-B1BXCjex.js → add-connection-dialog-CpMCc97S.js} +1 -1
  13. package/dist/assets/{agent-panel-CDFUc1uu.js → agent-panel-BSFYqJ9B.js} +3 -3
  14. package/dist/assets/ai-model-dropdown-D575Vid8.js +13 -0
  15. package/dist/assets/alert-dialog-8YiYLrfr.js +1 -0
  16. package/dist/assets/any-language-editor-BWmz0P_C.js +1 -0
  17. package/dist/assets/{app-config-button-AdV-QDD7.js → app-config-button-Cfcg_5Bz.js} +1 -1
  18. package/dist/assets/arrays-0R2reF2y.js +1 -0
  19. package/dist/assets/{blob-BG8yoQmx.js → blob-CszMu60N.js} +1 -1
  20. package/dist/assets/{button-COI-NGrx.js → button-Cl7haVNh.js} +1 -1
  21. package/dist/assets/{cache-panel-DTY1dqvi.js → cache-panel-DuBsA6oO.js} +1 -1
  22. package/dist/assets/{capabilities-BJvf-MD4.js → capabilities-DcB-Dd0T.js} +1 -1
  23. package/dist/assets/cell-editor-BLpOlLnM.js +42 -0
  24. package/dist/assets/cell-link-Cj0gGaMV.js +1 -0
  25. package/dist/assets/cells-SvbLY10y.js +220 -0
  26. package/dist/assets/{chat-display-DhgI2MS9.js → chat-display-D43_g3TX.js} +1 -1
  27. package/dist/assets/chat-panel-B_HAJMwx.js +4 -0
  28. package/dist/assets/chat-ui-BEcYCphu.js +4 -0
  29. package/dist/assets/circle-B0vOU-Dh.js +1 -0
  30. package/dist/assets/{code-visibility-BJImdpDi.js → code-visibility-D1Wf7-vD.js} +1 -1
  31. package/dist/assets/column-preview-CrzaJKk8.js +1 -0
  32. package/dist/assets/command-palette-Bm9yIVcD.js +1 -0
  33. package/dist/assets/{common-Sl3uthjg.js → common-BJyUxCzv.js} +1 -1
  34. package/dist/assets/{components-CskjK6xZ.js → components-9p_w2C6e.js} +1 -1
  35. package/dist/assets/components-B6gDDccL.js +1 -0
  36. package/dist/assets/{components-3cou6Mka.js → components-D1f_Cz4i.js} +1 -1
  37. package/dist/assets/config-7IjqNuVp.js +1 -0
  38. package/dist/assets/config-BC32AAdw.js +1 -0
  39. package/dist/assets/{config-CA1V-wjh.js → config-CXnC3MRv.js} +1 -1
  40. package/dist/assets/{copy-D8O_CYCG.js → copy-74EjuZso.js} +1 -1
  41. package/dist/assets/{copy-icon-BIiRXh_3.js → copy-icon-Craaocbu.js} +1 -1
  42. package/dist/assets/{createReducer-DgRODTW6.js → createReducer-B04CIO-e.js} +1 -1
  43. package/dist/assets/{datasource-DfbNb83e.js → datasource-C8KSVfSD.js} +1 -1
  44. package/dist/assets/{dates-CfmdTL6B.js → dates-CaPZVGtm.js} +1 -1
  45. package/dist/assets/{dependency-graph-panel-L5cBE5sl.js → dependency-graph-panel-BhmyJbUl.js} +3 -3
  46. package/dist/assets/dist-gaK_BThd.js +1 -0
  47. package/dist/assets/{documentation-panel-D7S8JIc2.js → documentation-panel-DcVG0Q0d.js} +1 -1
  48. package/dist/assets/download-Bx52VRE4.js +9 -0
  49. package/dist/assets/dropdown-menu-Cwi17BoX.js +1 -0
  50. package/dist/assets/edit-page-DUrwOHR-.js +9 -0
  51. package/dist/assets/{error-banner-COZ7SfYV.js → error-banner-m3mastc-.js} +1 -1
  52. package/dist/assets/{error-entries-CLnPDv7b.js → error-entries-11OuHzUF.js} +1 -1
  53. package/dist/assets/{error-panel-D2xrYLue.js → error-panel-XJy5S25F.js} +1 -1
  54. package/dist/assets/{es-Cqst59WX.js → es-Rg27gAEy.js} +1 -1
  55. package/dist/assets/{feedback-button-DzPunIqU.js → feedback-button-B7jh_B4w.js} +1 -1
  56. package/dist/assets/feedback-button-C-eKtxXE.js +1 -0
  57. package/dist/assets/file-explorer-panel-CnVxbF4A.js +1 -0
  58. package/dist/assets/{file-name-input-B4fRaQkI.js → file-name-input-BJuqjQqH.js} +2 -2
  59. package/dist/assets/floating-outline-8yyZ-MOJ.js +1 -0
  60. package/dist/assets/{formats-yZ06h1Pl.js → formats-CYyvbiZA.js} +1 -1
  61. package/dist/assets/{formatting-BqrP-n49.js → formatting-DW2u7HXI.js} +1 -1
  62. package/dist/assets/fullscreen-D-ldV-19.js +41 -0
  63. package/dist/assets/{gallery-page-BefvdsM7.js → gallery-page-DJvGHePf.js} +1 -1
  64. package/dist/assets/{ganttDiagram-EL5Y4UJY-2n7Ig5Jm.js → ganttDiagram-EL5Y4UJY-CYqkCfFG.js} +1 -1
  65. package/dist/assets/{glide-data-editor-C3eaYlXg.js → glide-data-editor-Cyc8DHZx.js} +2 -2
  66. package/dist/assets/{glide-utils-CIrAxdjv.js → glide-utils-DsTeJ7WA.js} +2 -2
  67. package/dist/assets/home-page-tRZJmSU6.js +1 -0
  68. package/dist/assets/{hooks-DzML9nez.js → hooks-CxGwybGw.js} +1 -1
  69. package/dist/assets/{html-to-image-DK9Zl1lU.js → html-to-image-BW0fcUS8.js} +1 -1
  70. package/dist/assets/index-CoxbqhGH.js +30 -0
  71. package/dist/assets/index-u08m0hQE.css +2 -0
  72. package/dist/assets/{input-BoBxR50a.js → input-DLA5Dhn3.js} +2 -2
  73. package/dist/assets/kiosk-mode-Cbxffiz9.js +1 -0
  74. package/dist/assets/{layout-Ck1RoL1B.js → layout-DvXJxBiL.js} +4 -4
  75. package/dist/assets/linear-Bma8J3iM.js +1 -0
  76. package/dist/assets/links-UwsB2RA1.js +1 -0
  77. package/dist/assets/{logs-panel-BRytaWaO.js → logs-panel-KtsY7TrR.js} +1 -1
  78. package/dist/assets/{markdown-renderer-DX0Xrrfy.js → markdown-renderer-DUgV7_fm.js} +1 -1
  79. package/dist/assets/menu-items-yD7IV3V4.js +1 -0
  80. package/dist/assets/{mermaid-C4EiVqcd.js → mermaid-DmekSy8J.js} +2 -2
  81. package/dist/assets/{name-cell-input-DlpTvPLa.js → name-cell-input-C3-Cj-Ek.js} +1 -1
  82. package/dist/assets/numbers-DJK15_4S.js +1 -0
  83. package/dist/assets/{objects-snlMn4yk.js → objects-rLPlQ3X_.js} +1 -1
  84. package/dist/assets/{once-Haz_uxEL.js → once-6KNTRW0u.js} +1 -1
  85. package/dist/assets/{outline-panel-B2WIpz-1.js → outline-panel-BzyY4JA6.js} +1 -1
  86. package/dist/assets/{package-data-dv4FBHxR.js → package-data-CKPmBDtg.js} +1 -1
  87. package/dist/assets/packages-panel-DxosbWko.js +1 -0
  88. package/dist/assets/{panel-accordion-state-BqSH5ttk.js → panel-accordion-state-BRbTib2B.js} +1 -1
  89. package/dist/assets/panels-Da3071Q4.js +1 -0
  90. package/dist/assets/precisionRound-BxlUs8G4.js +1 -0
  91. package/dist/assets/process-output-CSwx7rvo.js +1 -0
  92. package/dist/assets/{quadrantDiagram-AXDQQJYC-yBhCDI7E.js → quadrantDiagram-AXDQQJYC-D9KH5Zge.js} +1 -1
  93. package/dist/assets/react-vega-BTNeboR2.js +189 -0
  94. package/dist/assets/{readonly-code-display-MnMEoYW5.js → readonly-code-display-B9y9423Y.js} +1 -1
  95. package/dist/assets/{readonly-python-code-ggs4hAzC.js → readonly-python-code-Dgw6trlk.js} +1 -1
  96. package/dist/assets/{renderShortcut-C1JwYcqW.js → renderShortcut-Bxv9ZLnD.js} +1 -1
  97. package/dist/assets/{requests-lt0JK8Jt.js → requests-BIFR-h4M.js} +1 -1
  98. package/dist/assets/requests-DuLJN9Tf.js +1 -0
  99. package/dist/assets/{reveal-component-PiV1eQk9.js → reveal-component-B9A4wkG-.js} +37 -37
  100. package/dist/assets/run-page-C1MQ5b_A.js +1 -0
  101. package/dist/assets/{runs-BLp5jezv.js → runs-C8uOPNAW.js} +1 -1
  102. package/dist/assets/{runtime-details-state-CoL-p0MC.js → runtime-details-state-D_hOQL9T.js} +1 -1
  103. package/dist/assets/{save-worker-BbUSwZRG.js → save-worker-UwdGcNK5.js} +2 -1
  104. package/dist/assets/scratchpad-panel-CBCsPmMW.js +1 -0
  105. package/dist/assets/{scroll-to-bottom-button-1FyowpWV.js → scroll-to-bottom-button-CidjyqiH.js} +1 -1
  106. package/dist/assets/secrets-panel-ConQpchl.js +1 -0
  107. package/dist/assets/select-CgWQvW1F.js +1 -0
  108. package/dist/assets/{session-DqoWmb7X.js → session-BHgGkjDm.js} +1 -1
  109. package/dist/assets/session-panel-CiHCKPlx.js +1 -0
  110. package/dist/assets/snippets-panel-C0VDE_4i.js +1 -0
  111. package/dist/assets/{spec-BfSBcO5B.js → spec-DFtwOyXT.js} +1 -1
  112. package/dist/assets/state-BYETGwDZ.js +1 -0
  113. package/dist/assets/{state-uwRvJLb8.js → state-CZu2E2Lb.js} +1 -1
  114. package/dist/assets/{state-C3oDoyFe.js → state-ClBFtZxn.js} +1 -1
  115. package/dist/assets/{state-DxQnC_1I.js → state-CrLrURYG.js} +1 -1
  116. package/dist/assets/{state-DtGB7xeJ.js → state-DrraMmqz.js} +1 -1
  117. package/dist/assets/state-DvU2AiFx.js +107 -0
  118. package/dist/assets/{strings-B2vv-ENv.js → strings-3lNX212T.js} +1 -1
  119. package/dist/assets/{swiper-component-a07domrZ.js → swiper-component-BwGv-Pu1.js} +1 -1
  120. package/dist/assets/switch-D2gdC_ep.js +1 -0
  121. package/dist/assets/tabs-BLczoY3k.js +1 -0
  122. package/dist/assets/{terminal-wnSHYT8N.js → terminal-DLgGyFIP.js} +1 -1
  123. package/dist/assets/{time-MgToa8VM.js → time-DswSXBDk.js} +1 -1
  124. package/dist/assets/toast-trUlkvx3.js +1 -0
  125. package/dist/assets/tooltip-B5lAA_jn.js +1 -0
  126. package/dist/assets/tooltip-BiQdpXA0.js +61 -0
  127. package/dist/assets/tracing-C5d51Nmi.js +1 -0
  128. package/dist/assets/tracing-panel-B89R8dWE.js +2 -0
  129. package/dist/assets/{tree-actions-DnUokmva.js → tree-actions-Cph8FVzh.js} +1 -1
  130. package/dist/assets/use-restore-focus-DMSzeuUr.js +1 -0
  131. package/dist/assets/{useAddCell-IN24thff.js → useAddCell-VPkB72pn.js} +1 -1
  132. package/dist/assets/useAsyncData-qvoPYHtx.js +1 -0
  133. package/dist/assets/useBoolean-BkeUcn1n.js +1 -0
  134. package/dist/assets/useCellActionButton--zI3GFnt.js +1 -0
  135. package/dist/assets/useDebounce-DwTQ_1Pu.js +1 -0
  136. package/dist/assets/{useDeepCompareMemoize-DFIVN9dn.js → useDeepCompareMemoize-B2DVK2De.js} +1 -1
  137. package/dist/assets/useDelayElapsed-BlsDT4TV.js +1 -0
  138. package/dist/assets/{useDeleteCell-fQlM46Y2.js → useDeleteCell-CjewKeSr.js} +1 -1
  139. package/dist/assets/{useDependencyPanelTab-ImXYINm_.js → useDependencyPanelTab-Bt_ka-wl.js} +1 -1
  140. package/dist/assets/{useEvent-Dfcbgjhg.js → useEvent-BDjgaBtz.js} +1 -1
  141. package/dist/assets/{useEventListener-D9cftcHj.js → useEventListener-DPOTkksd.js} +1 -1
  142. package/dist/assets/{useIframeCapabilities-CwTkczVN.js → useIframeCapabilities-sHeZt_EA.js} +1 -1
  143. package/dist/assets/{useInstallPackage-BW06eter.js → useInstallPackage-BfNKN33w.js} +1 -1
  144. package/dist/assets/{useInterval--x3s1VpI.js → useInterval-BcdwHHB5.js} +1 -1
  145. package/dist/assets/useNotebookActions-DV2S6KaG.js +1 -0
  146. package/dist/assets/{useRunCells-DSJPXE-j.js → useRunCells-BupCZpzk.js} +1 -1
  147. package/dist/assets/{useSplitCell-DQyMF5gd.js → useSplitCell-CaLIR0TS.js} +1 -1
  148. package/dist/assets/useStateForKey-CGMJQT8x.js +1 -0
  149. package/dist/assets/{useTheme-n8Dj1zH7.js → useTheme-C2dnh2vM.js} +1 -1
  150. package/dist/assets/{utils-CaQYvazl.js → utils-DfElfBpz.js} +1 -1
  151. package/dist/assets/vega-component-DJf6ho22.js +1 -0
  152. package/dist/assets/{vega-embed-container-DJPNkHPb.js → vega-embed-container-DtArSJz1.js} +1 -1
  153. package/dist/assets/vega-loader.browser-Bu25TKax.js +6 -0
  154. package/dist/assets/worker-jm2VC7pS.js +159 -0
  155. package/dist/assets/{write-secret-modal-DzKAInL2.js → write-secret-modal-CBi2vNK_.js} +1 -1
  156. package/dist/assets/{xychartDiagram-S5SC5T6Z-KbGzG6-u.js → xychartDiagram-S5SC5T6Z-CeRnBS5e.js} +1 -1
  157. package/dist/index.html +81 -79
  158. package/package.json +14 -14
  159. package/src/__tests__/host-imports.test.ts +1 -0
  160. package/src/__tests__/module-entry.test.ts +32 -0
  161. package/src/__tests__/mount.test.ts +0 -20
  162. package/src/components/ai/__tests__/ai-model-dropdown.test.tsx +63 -0
  163. package/src/components/ai/ai-model-dropdown.tsx +11 -6
  164. package/src/components/app-config/ai-config.tsx +7 -2
  165. package/src/components/chat/__tests__/message-queue.test.tsx +17 -0
  166. package/src/components/chat/acp/__tests__/session-tabs.test.tsx +43 -0
  167. package/src/components/chat/acp/__tests__/state.test.ts +2 -2
  168. package/src/components/chat/acp/blocks.tsx +3 -2
  169. package/src/components/chat/acp/session-tabs.tsx +8 -3
  170. package/src/components/chat/chat-components.tsx +2 -0
  171. package/src/components/chat/chat-panel.tsx +9 -8
  172. package/src/components/chat/chat-utils.ts +4 -8
  173. package/src/components/data-table/TableTopBar.tsx +2 -0
  174. package/src/components/data-table/__tests__/chart-spec-model.test.ts +195 -195
  175. package/src/components/data-table/__tests__/column-explorer.test.tsx +27 -1
  176. package/src/components/data-table/__tests__/columns.test.tsx +105 -0
  177. package/src/components/data-table/__tests__/context-menu-interaction.test.tsx +130 -0
  178. package/src/components/data-table/__tests__/data-table.test.tsx +1 -0
  179. package/src/components/data-table/__tests__/date-popover.test.tsx +72 -0
  180. package/src/components/data-table/__tests__/export-actions.test.tsx +105 -0
  181. package/src/components/data-table/__tests__/keyboard-selection.test.tsx +188 -0
  182. package/src/components/data-table/__tests__/search-highlight.test.tsx +109 -0
  183. package/src/components/data-table/__tests__/url-detector.test.tsx +74 -0
  184. package/src/components/data-table/cell-selection/feature.ts +2 -2
  185. package/src/components/data-table/cell-selection/types.ts +2 -2
  186. package/src/components/data-table/charts/__tests__/chart-panel.test.tsx +74 -1
  187. package/src/components/data-table/charts/__tests__/spec-snapshot.test.ts +20 -20
  188. package/src/components/data-table/charts/__tests__/spec.test.ts +78 -52
  189. package/src/components/data-table/charts/__tests__/storage.test.ts +4 -1
  190. package/src/components/data-table/charts/chart-spec/spec.ts +68 -43
  191. package/src/components/data-table/charts/charts.tsx +40 -30
  192. package/src/components/data-table/charts/storage.ts +16 -5
  193. package/src/components/data-table/column-explorer-panel/column-explorer.tsx +1 -1
  194. package/src/components/data-table/column-summary/chart-spec-model.tsx +23 -16
  195. package/src/components/data-table/column-summary/column-summary.tsx +2 -4
  196. package/src/components/data-table/column-visibility-dropdown.tsx +8 -13
  197. package/src/components/data-table/columns.tsx +45 -46
  198. package/src/components/data-table/context-menu.tsx +36 -20
  199. package/src/components/data-table/data-table.tsx +13 -1
  200. package/src/components/data-table/date-popover.tsx +6 -4
  201. package/src/components/data-table/export-actions.tsx +76 -48
  202. package/src/components/data-table/range-focus/atoms.ts +4 -6
  203. package/src/components/data-table/range-focus/use-cell-range-selection.ts +65 -38
  204. package/src/components/data-table/range-focus/use-scroll-into-view.ts +21 -2
  205. package/src/components/data-table/renderers.tsx +16 -7
  206. package/src/components/data-table/row-viewer-panel/__tests__/row-viewer.test.tsx +24 -1
  207. package/src/components/data-table/row-viewer-panel/row-viewer.tsx +4 -4
  208. package/src/components/data-table/search-highlight.tsx +79 -0
  209. package/src/components/data-table/types.ts +1 -0
  210. package/src/components/data-table/url-detector.tsx +49 -9
  211. package/src/components/datasources/__tests__/column-actions.test.tsx +97 -0
  212. package/src/components/datasources/__tests__/column-preview.test.tsx +63 -2
  213. package/src/components/datasources/__tests__/table-list.test.tsx +118 -0
  214. package/src/components/datasources/column-preview.tsx +8 -2
  215. package/src/components/datasources/datasources.tsx +48 -35
  216. package/src/components/debugger/debugger-code.tsx +5 -0
  217. package/src/components/dependency-graph/dependency-graph-tree.tsx +8 -8
  218. package/src/components/dependency-graph/elements.ts +24 -21
  219. package/src/components/dependency-graph/panels.tsx +2 -1
  220. package/src/components/dependency-graph/utils/__tests__/layout.test.ts +18 -11
  221. package/src/components/editor/SortableCell.tsx +1 -3
  222. package/src/components/editor/__tests__/dynamic-favicon.test.tsx +111 -18
  223. package/src/components/editor/__tests__/package-alert.test.tsx +22 -1
  224. package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +24 -0
  225. package/src/components/editor/actions/pair-with-agent-modal.tsx +18 -15
  226. package/src/components/editor/ai/add-cell-with-ai.tsx +3 -3
  227. package/src/components/editor/ai/ai-completion-editor.tsx +1 -0
  228. package/src/components/editor/alerts/startup-logs-alert.tsx +1 -0
  229. package/src/components/editor/cell/CreateCellButton.tsx +8 -10
  230. package/src/components/editor/cell/DeleteButton.tsx +1 -0
  231. package/src/components/editor/cell/__tests__/useShouldShowInterrupt.test.ts +39 -0
  232. package/src/components/editor/cell/code/__tests__/language-toggle.test.tsx +54 -0
  233. package/src/components/editor/cell/code/cell-editor.tsx +1 -1
  234. package/src/components/editor/cell/code/language-toggle.tsx +8 -7
  235. package/src/components/editor/cell/collapse.tsx +5 -3
  236. package/src/components/editor/cell/useShouldShowInterrupt.ts +2 -12
  237. package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +30 -1
  238. package/src/components/editor/chrome/panels/cache-panel.tsx +1 -0
  239. package/src/components/editor/chrome/panels/outline/__tests__/outline-keyboard.test.tsx +84 -0
  240. package/src/components/editor/chrome/panels/outline/floating-outline.tsx +39 -14
  241. package/src/components/editor/chrome/panels/outline/useActiveOutline.tsx +3 -2
  242. package/src/components/editor/chrome/panels/packages-panel.tsx +4 -7
  243. package/src/components/editor/chrome/panels/secrets-panel.tsx +1 -0
  244. package/src/components/editor/chrome/panels/snippets-panel.tsx +5 -1
  245. package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +12 -18
  246. package/src/components/editor/chrome/wrapper/__tests__/pending-ai-cells.test.tsx +58 -1
  247. package/src/components/editor/chrome/wrapper/footer-items/runtime-settings.tsx +1 -7
  248. package/src/components/editor/chrome/wrapper/pending-ai-cells.tsx +12 -2
  249. package/src/components/editor/chrome/wrapper/storage.ts +1 -1
  250. package/src/components/editor/connections/components.tsx +1 -1
  251. package/src/components/editor/controls/__tests__/command-palette.test.tsx +53 -0
  252. package/src/components/editor/controls/__tests__/keyboard-shortcuts.test.tsx +61 -0
  253. package/src/components/editor/controls/command-palette.tsx +22 -10
  254. package/src/components/editor/controls/keyboard-shortcuts.tsx +14 -5
  255. package/src/components/editor/dynamic-favicon.tsx +21 -40
  256. package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +12 -23
  257. package/src/components/editor/file-tree/__tests__/file-explorer-interactions.test.tsx +6 -0
  258. package/src/components/editor/file-tree/__tests__/file-explorer-navigation.test.tsx +47 -0
  259. package/src/components/editor/file-tree/__tests__/use-file-search.test.tsx +91 -0
  260. package/src/components/editor/file-tree/file-explorer.tsx +45 -8
  261. package/src/components/editor/file-tree/file-name-input.tsx +1 -0
  262. package/src/components/editor/file-tree/file-viewer.tsx +4 -2
  263. package/src/components/editor/file-tree/use-file-search.ts +35 -21
  264. package/src/components/editor/header/status.tsx +28 -5
  265. package/src/components/editor/links/__tests__/cell-link.test.tsx +146 -0
  266. package/src/components/editor/links/cell-link.tsx +14 -5
  267. package/src/components/editor/navigation/focus-utils.ts +4 -4
  268. package/src/components/editor/notebook-banner.tsx +1 -0
  269. package/src/components/editor/notebook-cell.tsx +11 -2
  270. package/src/components/editor/output/MarimoTracebackOutput.tsx +19 -17
  271. package/src/components/editor/output/__tests__/traceback.test.tsx +27 -1
  272. package/src/components/editor/output/ansi-reduce.ts +33 -20
  273. package/src/components/editor/output/console/ConsoleOutput.tsx +19 -25
  274. package/src/components/editor/package-alert.tsx +14 -7
  275. package/src/components/editor/renderers/grid-layout/grid-layout.tsx +12 -6
  276. package/src/components/editor/renderers/vertical-layout/sidebar/toggle.tsx +7 -1
  277. package/src/components/home/components.tsx +17 -31
  278. package/src/components/icons/__tests__/large-spinner.test.tsx +39 -0
  279. package/src/components/icons/large-spinner.tsx +3 -14
  280. package/src/components/icons/spinner.tsx +1 -1
  281. package/src/components/mcp/mcp-status-indicator.tsx +1 -1
  282. package/src/components/pages/__tests__/home-page.test.tsx +116 -0
  283. package/src/components/pages/home-page.tsx +164 -91
  284. package/src/components/scratchpad/scratchpad.test.tsx +95 -0
  285. package/src/components/scratchpad/scratchpad.tsx +16 -3
  286. package/src/components/slides/reveal-component.tsx +4 -2
  287. package/src/components/slides/slide-cell-view.tsx +1 -1
  288. package/src/components/slides/slide-form.tsx +1 -0
  289. package/src/components/tracing/tracing-keyboard.test.tsx +85 -0
  290. package/src/components/tracing/tracing.tsx +45 -41
  291. package/src/components/ui/__tests__/draggable-popover.test.tsx +24 -0
  292. package/src/components/ui/alert-dialog.tsx +1 -1
  293. package/src/components/ui/draggable-popover.tsx +24 -2
  294. package/src/core/ai/context/providers/datasource.ts +6 -2
  295. package/src/core/ai/tools/base.ts +16 -7
  296. package/src/core/ai/tools/edit-notebook-tool.ts +47 -45
  297. package/src/core/ai/tools/registry.ts +13 -8
  298. package/src/core/ai/tools/sample-tool.ts +6 -7
  299. package/src/core/attachments/state.ts +5 -8
  300. package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +4 -0
  301. package/src/core/cells/__tests__/cell.test.ts +39 -0
  302. package/src/core/cells/__tests__/cells.test.ts +3 -1
  303. package/src/core/cells/__tests__/collapseConsoleOutputs.test.ts +54 -0
  304. package/src/core/cells/__tests__/notebook-outline.test.ts +113 -0
  305. package/src/core/cells/__tests__/session.test.ts +117 -36
  306. package/src/core/cells/cell.ts +14 -3
  307. package/src/core/cells/cells.ts +3 -1
  308. package/src/core/cells/pending-delete-service.ts +2 -6
  309. package/src/core/cells/session.ts +1 -1
  310. package/src/core/cells/types.ts +10 -1
  311. package/src/core/codemirror/__tests__/setup.test.ts +29 -29
  312. package/src/core/codemirror/cells/__tests__/extensions.test.ts +15 -0
  313. package/src/core/codemirror/cells/extensions.ts +7 -3
  314. package/src/core/codemirror/keymaps/vim.ts +2 -6
  315. package/src/core/codemirror/language/__tests__/sql.test.ts +22 -20
  316. package/src/core/codemirror/language/extension.ts +8 -8
  317. package/src/core/codemirror/language/languages/markdown.ts +8 -6
  318. package/src/core/codemirror/language/languages/python.ts +16 -10
  319. package/src/core/codemirror/language/languages/sql/sql.ts +9 -7
  320. package/src/core/codemirror/language/panel/panel.tsx +1 -0
  321. package/src/core/codemirror/language/types.ts +7 -7
  322. package/src/core/codemirror/rtc/extension.ts +24 -12
  323. package/src/core/codemirror/rtc/loro/awareness.ts +34 -19
  324. package/src/core/codemirror/rtc/loro/sync.ts +1 -0
  325. package/src/core/codemirror/utils.ts +1 -0
  326. package/src/core/datasets/__tests__/data-source-discovery.test.ts +4 -4
  327. package/src/core/datasets/data-source-discovery.ts +4 -6
  328. package/src/core/document/__tests__/codemirror.test.ts +485 -0
  329. package/src/core/document/__tests__/draft.test.ts +322 -0
  330. package/src/core/document/__tests__/fixtures.ts +92 -0
  331. package/src/core/document/__tests__/hooks.test.tsx +386 -0
  332. package/src/core/document/__tests__/lifecycle-scenarios.test.ts +61 -0
  333. package/src/core/document/__tests__/model.test.ts +191 -0
  334. package/src/core/document/__tests__/protocol-fixtures.ts +38 -0
  335. package/src/core/document/__tests__/recovery-scenarios.test.ts +230 -0
  336. package/src/core/document/__tests__/scenario-fixture.ts +43 -0
  337. package/src/core/document/__tests__/scenarios.test.ts +205 -0
  338. package/src/core/document/__tests__/state.test.ts +438 -0
  339. package/src/core/document/__tests__/submission.test.ts +203 -0
  340. package/src/core/document/__tests__/test-host.test.ts +92 -0
  341. package/src/core/document/__tests__/test-host.ts +247 -0
  342. package/src/core/document/__tests__/test-notebook.ts +149 -0
  343. package/src/core/document/__tests__/user.ts +61 -0
  344. package/src/core/document/codemirror.ts +311 -0
  345. package/src/core/document/draft.ts +472 -0
  346. package/src/core/document/hooks.tsx +118 -0
  347. package/src/core/document/index.ts +15 -0
  348. package/src/core/document/model.ts +55 -0
  349. package/src/core/document/select.ts +41 -0
  350. package/src/core/document/state.ts +374 -0
  351. package/src/core/document/sync.ts +369 -0
  352. package/src/core/host/AGENTS.md +8 -2
  353. package/src/core/host/__tests__/attachments.test.ts +56 -0
  354. package/src/core/host/__tests__/document.test.ts +126 -0
  355. package/src/core/host/__tests__/fixtures.ts +71 -0
  356. package/src/core/host/__tests__/replica.test.ts +550 -0
  357. package/src/core/host/__tests__/static.test.ts +169 -0
  358. package/src/core/host/__tests__/wasm.test.ts +162 -0
  359. package/src/core/host/backend.ts +19 -0
  360. package/src/core/host/connection.ts +1 -40
  361. package/src/core/host/index.ts +14 -0
  362. package/src/core/host/replica.ts +286 -0
  363. package/src/core/host/resources/attachments.ts +22 -0
  364. package/src/core/host/resources/document.ts +101 -0
  365. package/src/core/host/static.ts +72 -0
  366. package/src/core/host/wasm.ts +102 -0
  367. package/src/core/hotkeys/actions.ts +2 -4
  368. package/src/core/islands/__tests__/bootstrap.test.ts +0 -26
  369. package/src/core/islands/bootstrap.ts +0 -1
  370. package/src/core/islands/components/__tests__/IslandControls.test.tsx +7 -5
  371. package/src/core/kernel/__tests__/RuntimeState.test.ts +1 -1
  372. package/src/core/network/connection.ts +4 -2
  373. package/src/core/network/useConnectionNotice.ts +8 -2
  374. package/src/core/runtime/__tests__/createWsUrl.test.ts +77 -7
  375. package/src/core/runtime/__tests__/runtime.test.ts +32 -58
  376. package/src/core/runtime/runtime.ts +38 -42
  377. package/src/core/runtime/types.ts +0 -2
  378. package/src/core/state/__tests__/jotai.test.tsx +108 -0
  379. package/src/core/state/jotai.ts +1 -1
  380. package/src/core/static/__tests__/files.test.ts +38 -10
  381. package/src/core/static/files.ts +2 -0
  382. package/src/core/wasm/bridge.ts +38 -10
  383. package/src/core/wasm/store.ts +3 -3
  384. package/src/core/wasm/worker/bootstrap.ts +2 -1
  385. package/src/core/wasm/worker/getMarimoWheel.ts +1 -1
  386. package/src/core/wasm/worker/types.ts +11 -0
  387. package/src/core/wasm/worker/worker.ts +48 -0
  388. package/src/core/websocket/__tests__/backend.test.ts +200 -0
  389. package/src/core/websocket/__tests__/connection-state.test.ts +0 -12
  390. package/src/core/websocket/__tests__/useNotebookReplica.hook.test.tsx +141 -0
  391. package/src/core/websocket/__tests__/useNotebookSession.hook.test.tsx +154 -428
  392. package/src/core/websocket/backend.ts +106 -0
  393. package/src/core/websocket/connection-state.ts +1 -11
  394. package/src/core/websocket/state.ts +7 -0
  395. package/src/core/websocket/transports/__tests__/notebook.test.ts +445 -0
  396. package/src/core/websocket/transports/notebook.ts +190 -0
  397. package/src/core/websocket/types.ts +2 -2
  398. package/src/core/websocket/useNotebookReplica.ts +69 -0
  399. package/src/core/websocket/useNotebookSession.tsx +10 -22
  400. package/src/css/globals.css +5 -0
  401. package/src/hooks/__tests__/useAsyncData.test.tsx +148 -0
  402. package/src/hooks/__tests__/useDebounce.test.tsx +51 -0
  403. package/src/hooks/__tests__/useDeepCompareMemoize.test.tsx +39 -0
  404. package/src/hooks/__tests__/useDelayElapsed.test.tsx +28 -0
  405. package/src/hooks/__tests__/useInternalStateWithSync.test.tsx +20 -0
  406. package/src/hooks/__tests__/useScript.test.tsx +77 -0
  407. package/src/hooks/__tests__/useStateForKey.test.tsx +257 -0
  408. package/src/hooks/useAsyncData.ts +3 -3
  409. package/src/hooks/useDataSourceDiscovery.ts +6 -4
  410. package/src/hooks/useDebounce.ts +6 -10
  411. package/src/hooks/useDeepCompareMemoize.ts +1 -1
  412. package/src/hooks/useDelayElapsed.ts +14 -10
  413. package/src/hooks/useElapsedTime.ts +3 -3
  414. package/src/hooks/useEventListener.ts +1 -2
  415. package/src/hooks/useInputHistory.ts +4 -7
  416. package/src/hooks/useInternalStateWithSync.ts +6 -6
  417. package/src/hooks/useResizeObserver.ts +4 -7
  418. package/src/hooks/useScript.ts +67 -70
  419. package/src/hooks/useStateForKey.ts +93 -0
  420. package/src/mount.tsx +0 -4
  421. package/src/plugins/__tests__/plugin-object-schemas.test.ts +104 -0
  422. package/src/plugins/core/registerReactComponent.tsx +1 -1
  423. package/src/plugins/impl/CodeEditorPlugin.tsx +1 -1
  424. package/src/plugins/impl/DataEditorPlugin.tsx +1 -1
  425. package/src/plugins/impl/DataTablePlugin.tsx +29 -33
  426. package/src/plugins/impl/DropdownPlugin.tsx +2 -6
  427. package/src/plugins/impl/FileBrowserPlugin.tsx +4 -16
  428. package/src/plugins/impl/FormPlugin.tsx +1 -1
  429. package/src/plugins/impl/RangeSliderPlugin.tsx +0 -1
  430. package/src/plugins/impl/RefreshPlugin.tsx +1 -0
  431. package/src/plugins/impl/SliderPlugin.tsx +6 -8
  432. package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +117 -1
  433. package/src/plugins/impl/__tests__/FileBrowserPlugin.test.tsx +45 -0
  434. package/src/plugins/impl/__tests__/SliderPlugin.test.tsx +24 -3
  435. package/src/plugins/impl/anywidget/__tests__/registry.test.ts +2 -0
  436. package/src/plugins/impl/anywidget/__tests__/widget-binding.test.ts +241 -2
  437. package/src/plugins/impl/anywidget/runtime.ts +21 -9
  438. package/src/plugins/impl/anywidget/widget-binding.ts +160 -82
  439. package/src/plugins/impl/chat/__tests__/chat-prompts.test.tsx +93 -0
  440. package/src/plugins/impl/chat/chat-ui.tsx +14 -11
  441. package/src/plugins/impl/code/any-language-editor.tsx +2 -2
  442. package/src/plugins/impl/data-editor/glide-data-editor.tsx +8 -5
  443. package/src/plugins/impl/data-editor/glide-utils.ts +3 -1
  444. package/src/plugins/impl/data-explorer/ConnectedDataExplorerComponent.tsx +1 -1
  445. package/src/plugins/impl/data-explorer/components/column-summary.tsx +4 -2
  446. package/src/plugins/impl/data-explorer/state/reducer.ts +1 -1
  447. package/src/plugins/impl/data-frames/DataFramePlugin.tsx +1 -2
  448. package/src/plugins/impl/data-frames/__tests__/sidebar.test.tsx +160 -0
  449. package/src/plugins/impl/data-frames/forms/__tests__/__snapshots__/form.test.tsx.snap +66 -66
  450. package/src/plugins/impl/data-frames/panel.tsx +43 -16
  451. package/src/plugins/impl/mpl-interactive/MplInteractivePlugin.tsx +5 -3
  452. package/src/plugins/impl/panel/PanelPlugin.tsx +16 -17
  453. package/src/plugins/impl/panel/__tests__/utils.test.ts +39 -1
  454. package/src/plugins/impl/panel/utils.ts +17 -0
  455. package/src/plugins/impl/plotly/PlotlyPlugin.tsx +2 -5
  456. package/src/plugins/impl/plotly/selection.ts +25 -19
  457. package/src/plugins/impl/vega/VegaPlugin.tsx +2 -3
  458. package/src/plugins/impl/vega/make-selectable.ts +4 -7
  459. package/src/plugins/layout/RoutesPlugin.tsx +1 -1
  460. package/src/stories/__tests__/editor-story.test.tsx +33 -0
  461. package/src/stories/button.stories.tsx +1 -0
  462. package/src/stories/editor.stories.tsx +24 -17
  463. package/src/stories/form-wrapper.stories.tsx +2 -2
  464. package/src/stories/select.stories.tsx +2 -2
  465. package/src/stories/switchable-multi-select.stories.tsx +1 -1
  466. package/src/utils/__tests__/arrays.test.ts +35 -0
  467. package/src/utils/__tests__/batch-requests.test.ts +3 -3
  468. package/src/utils/__tests__/numbers.test.ts +48 -1
  469. package/src/utils/__tests__/objects.test.ts +21 -0
  470. package/src/utils/arrays.ts +10 -0
  471. package/src/utils/blob.ts +1 -1
  472. package/src/utils/numbers.ts +28 -17
  473. package/src/utils/objects.ts +3 -7
  474. package/src/utils/once.ts +19 -19
  475. package/src/utils/tracer.ts +9 -5
  476. package/src/utils/zod-utils.ts +1 -3
  477. package/dist/assets/CellStatus-DVOHpdeJ.js +0 -1
  478. package/dist/assets/JsonOutput-Cx1iyibB.js +0 -11
  479. package/dist/assets/MarimoErrorOutput-s_5GOX8k.js +0 -10
  480. package/dist/assets/RunButton-BKoh0zYG.js +0 -1
  481. package/dist/assets/__vite-browser-external-CYO_P3S8.js +0 -1
  482. package/dist/assets/__vite-browser-external-FeDVGZU8.js +0 -1
  483. package/dist/assets/ai-model-dropdown-i1vfGeVi.js +0 -13
  484. package/dist/assets/alert-dialog-BaPGzeOK.js +0 -1
  485. package/dist/assets/any-language-editor-BdTWdMCI.js +0 -1
  486. package/dist/assets/arrays-BqUx5USx.js +0 -1
  487. package/dist/assets/cell-editor-BKJ06H5F.js +0 -21
  488. package/dist/assets/cell-link-8Auigi8P.js +0 -1
  489. package/dist/assets/cells-C6YGYGdi.js +0 -220
  490. package/dist/assets/chat-panel-Cau71Ygr.js +0 -4
  491. package/dist/assets/chat-ui-C5ql1Up-.js +0 -4
  492. package/dist/assets/circle-DXf-VWZG.js +0 -1
  493. package/dist/assets/circle-play-CYIb0qT6.js +0 -1
  494. package/dist/assets/column-preview-BZ_iNhtJ.js +0 -1
  495. package/dist/assets/command-palette-DDC3kBet.js +0 -1
  496. package/dist/assets/components-YBmtV0p6.js +0 -1
  497. package/dist/assets/config-DRIpOKqh.js +0 -1
  498. package/dist/assets/config-Dy28fDwn.js +0 -1
  499. package/dist/assets/dist-iaThVDkA.js +0 -1
  500. package/dist/assets/download-C03FOElq.js +0 -9
  501. package/dist/assets/dropdown-menu-C1rnl-0o.js +0 -1
  502. package/dist/assets/edit-page-D_Rg0gQO.js +0 -9
  503. package/dist/assets/feedback-button-Kla7i5RT.js +0 -1
  504. package/dist/assets/file-explorer-panel-Uk4_QIJQ.js +0 -1
  505. package/dist/assets/floating-outline-B5MaM1JS.js +0 -1
  506. package/dist/assets/fullscreen-B9tHcfpV.js +0 -41
  507. package/dist/assets/home-page-hmvKlc1w.js +0 -4
  508. package/dist/assets/index-6b4mf8Ai.css +0 -2
  509. package/dist/assets/index-ptK1gxmo.js +0 -30
  510. package/dist/assets/kiosk-mode-BZECfg9k.js +0 -1
  511. package/dist/assets/linear-0eij_aAG.js +0 -1
  512. package/dist/assets/link-NuFVR7G9.js +0 -1
  513. package/dist/assets/links-DkkL9DVa.js +0 -1
  514. package/dist/assets/menu-items-BrwnTgyK.js +0 -1
  515. package/dist/assets/numbers-Mk3-CcoU.js +0 -1
  516. package/dist/assets/packages-panel-CI5ff9b-.js +0 -1
  517. package/dist/assets/panels-BCcZ_yLK.js +0 -107
  518. package/dist/assets/precisionRound-Bq_nmf5u.js +0 -1
  519. package/dist/assets/process-output-f3C3PD0Z.js +0 -1
  520. package/dist/assets/react-vega-C3rYGtP6.js +0 -189
  521. package/dist/assets/requests-CgSY1X4a.js +0 -1
  522. package/dist/assets/run-page-AfZhtEh1.js +0 -1
  523. package/dist/assets/scratchpad-panel-D2rWg4k8.js +0 -1
  524. package/dist/assets/secrets-panel-D7KN4tVs.js +0 -1
  525. package/dist/assets/select-DTBn5eTb.js +0 -1
  526. package/dist/assets/session-panel-CnANhcTo.js +0 -1
  527. package/dist/assets/snippets-panel-ClGHtEKQ.js +0 -1
  528. package/dist/assets/state-BAJYRhmO.js +0 -1
  529. package/dist/assets/state-CbjBtkdd.js +0 -3
  530. package/dist/assets/switch-CTaiAjrz.js +0 -1
  531. package/dist/assets/tabs-BuQlZFbt.js +0 -1
  532. package/dist/assets/toast-BGzLGnvw.js +0 -1
  533. package/dist/assets/tooltip-C9TuY18o.js +0 -1
  534. package/dist/assets/tooltip-mT3CGest.js +0 -61
  535. package/dist/assets/tracing-Dz2rYOFL.js +0 -1
  536. package/dist/assets/tracing-panel-DqQY9V91.js +0 -2
  537. package/dist/assets/use-restore-focus-BxEGwIbk.js +0 -1
  538. package/dist/assets/useAsyncData-C7aJJz0Y.js +0 -1
  539. package/dist/assets/useBoolean-d0Tty94N.js +0 -1
  540. package/dist/assets/useCellActionButton-DmaFZ4oF.js +0 -1
  541. package/dist/assets/useDebounce-B_ccBZTp.js +0 -1
  542. package/dist/assets/useNotebookActions-DHt3s-ia.js +0 -1
  543. package/dist/assets/vega-component-IouMe13t.js +0 -1
  544. package/dist/assets/vega-loader.browser-CLRkNgm7.js +0 -6
  545. package/dist/assets/worker-DTQ7oH2A.js +0 -158
  546. package/dist/assets/ws-BXWY541V.js +0 -22
  547. package/src/core/host/__tests__/events.test.ts +0 -46
  548. package/src/core/host/__tests__/notebook-client.test.ts +0 -191
  549. package/src/core/host/__tests__/protocol.test.ts +0 -214
  550. package/src/core/host/events.ts +0 -60
  551. package/src/core/host/notebook-client.ts +0 -226
  552. package/src/core/host/protocol.ts +0 -1943
  553. package/src/core/state/__tests__/jotai.test.ts +0 -25
  554. package/src/core/websocket/__tests__/createConnectionTransport.test.ts +0 -49
  555. package/src/core/websocket/__tests__/useNotebookClient.hook.test.tsx +0 -140
  556. package/src/core/websocket/transports/__tests__/basic.test.ts +0 -275
  557. package/src/core/websocket/transports/__tests__/sse.test.ts +0 -335
  558. package/src/core/websocket/transports/__tests__/transport.test.ts +0 -308
  559. package/src/core/websocket/transports/__tests__/ws.test.ts +0 -125
  560. package/src/core/websocket/transports/basic.ts +0 -102
  561. package/src/core/websocket/transports/create.ts +0 -31
  562. package/src/core/websocket/transports/sse.ts +0 -308
  563. package/src/core/websocket/transports/transport.ts +0 -50
  564. package/src/core/websocket/transports/ws.ts +0 -98
  565. package/src/core/websocket/useNotebookClient.ts +0 -80
@@ -0,0 +1,472 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import type { Document } from "../host";
4
+ import * as Protocol from "@marimo-team/client/protocol";
5
+
6
+ /** The fields a client edits. Null code means the host withheld source. */
7
+ export type EditableCell = Readonly<
8
+ Pick<Protocol.Cell, "id" | "code" | "name">
9
+ > & {
10
+ readonly config: Readonly<Protocol.CellConfig>;
11
+ };
12
+
13
+ type CellWithSource = EditableCell & { readonly code: string };
14
+
15
+ /** A confirmed document with a draft applied. */
16
+ export interface EditableDocument {
17
+ readonly order: readonly Protocol.CellId[];
18
+ readonly cells: ReadonlyMap<Protocol.CellId, EditableCell>;
19
+ }
20
+
21
+ /** Fields to change on a cell. A field left out keeps its value, including within config. */
22
+ export interface CellEdit {
23
+ readonly code?: string;
24
+ readonly name?: string;
25
+ readonly config?: Partial<Readonly<Protocol.CellConfig>>;
26
+ }
27
+
28
+ type MutableEdit = { -readonly [K in keyof CellEdit]: CellEdit[K] };
29
+
30
+ /** Where a created cell goes. With neither, at the end. */
31
+ export interface Placement {
32
+ readonly after?: Protocol.CellId;
33
+ readonly before?: Protocol.CellId;
34
+ }
35
+
36
+ /** What this client wants one cell to become. */
37
+ type CellDraft =
38
+ | { readonly kind: "edit"; readonly edit: CellEdit }
39
+ | {
40
+ readonly kind: "create";
41
+ readonly cell: CellWithSource;
42
+ readonly placement: Placement;
43
+ }
44
+ | { readonly kind: "delete" };
45
+
46
+ /**
47
+ * This client's changes, layered over a confirmed document.
48
+ *
49
+ * A draft is a document delta in the terms of the host's edit request:
50
+ * per-cell puts and deletes, and optionally the order as a whole. The view
51
+ * a person edits and the request that synchronizes it both derive from it.
52
+ */
53
+ export interface Draft {
54
+ readonly cells: ReadonlyMap<Protocol.CellId, CellDraft>;
55
+ /** The full order to apply, or null to follow the host's. */
56
+ readonly order: readonly Protocol.CellId[] | null;
57
+ }
58
+
59
+ export const EMPTY_DRAFT: Draft = { cells: new Map(), order: null };
60
+
61
+ export function isEmptyDraft(draft: Draft): boolean {
62
+ return draft.cells.size === 0 && draft.order === null;
63
+ }
64
+
65
+ // Merging -------------------------------------------------------------------
66
+
67
+ /** Copy the defined fields of `patch` over `base`. */
68
+ function assignDefined<T extends object>(base: T, patch: Partial<T>): T {
69
+ const result = { ...base };
70
+ for (const key in patch) {
71
+ const value = patch[key];
72
+ if (value !== undefined) {
73
+ result[key] = value;
74
+ }
75
+ }
76
+ return result;
77
+ }
78
+
79
+ /** The defined fields of `to` that differ from `from`. */
80
+ function differingFields<T extends object>(
81
+ from: T,
82
+ to: Partial<T>,
83
+ ): Partial<T> {
84
+ const result: Partial<T> = {};
85
+ for (const key in to) {
86
+ const value = to[key];
87
+ if (value !== undefined && value !== from[key]) {
88
+ result[key] = value;
89
+ }
90
+ }
91
+ return result;
92
+ }
93
+
94
+ function isEmpty(object: object): boolean {
95
+ return Object.keys(object).length === 0;
96
+ }
97
+
98
+ function sameOrder(
99
+ a: readonly Protocol.CellId[],
100
+ b: readonly Protocol.CellId[],
101
+ ): boolean {
102
+ return a.length === b.length && a.every((id, index) => id === b[index]);
103
+ }
104
+
105
+ /** Combine two edits, the later one winning field by field. */
106
+ function mergeEdit(previous: CellEdit, edit: CellEdit): CellEdit {
107
+ const merged = assignDefined(previous, edit);
108
+ return edit.config === undefined
109
+ ? merged
110
+ : { ...merged, config: assignDefined(previous.config ?? {}, edit.config) };
111
+ }
112
+
113
+ /** The cell with an edit applied, as a new object with the editable fields. */
114
+ function applyEdit(cell: CellWithSource, edit: CellEdit): CellWithSource;
115
+ function applyEdit(cell: EditableCell, edit: CellEdit): EditableCell;
116
+ function applyEdit(cell: EditableCell, edit: CellEdit): EditableCell {
117
+ return {
118
+ id: cell.id,
119
+ code: edit.code ?? cell.code,
120
+ name: edit.name ?? cell.name,
121
+ config:
122
+ edit.config === undefined
123
+ ? cell.config
124
+ : assignDefined(cell.config, edit.config),
125
+ };
126
+ }
127
+
128
+ export function sameCell(a: EditableCell, b: EditableCell): boolean {
129
+ return (
130
+ a.id === b.id &&
131
+ a.code === b.code &&
132
+ a.name === b.name &&
133
+ isEmpty(differingFields(a.config, b.config))
134
+ );
135
+ }
136
+
137
+ /** The edit that turns `from` into `to`. Empty when they are the same. */
138
+ function editBetween(from: CellWithSource, to: CellWithSource): CellEdit {
139
+ const edit: MutableEdit = {};
140
+ if (to.code !== from.code) {
141
+ edit.code = to.code;
142
+ }
143
+ if (to.name !== from.name) {
144
+ edit.name = to.name;
145
+ }
146
+ const config = differingFields(from.config, to.config);
147
+ if (!isEmpty(config)) {
148
+ edit.config = config;
149
+ }
150
+ return edit;
151
+ }
152
+
153
+ /** The fields of `edit` still waiting after `sent` was accepted. */
154
+ function remainingEdit(edit: CellEdit, sent: CellEdit): CellEdit {
155
+ const remaining: MutableEdit = {};
156
+ if (edit.code !== undefined && edit.code !== sent.code) {
157
+ remaining.code = edit.code;
158
+ }
159
+ if (edit.name !== undefined && edit.name !== sent.name) {
160
+ remaining.name = edit.name;
161
+ }
162
+ if (edit.config !== undefined) {
163
+ const config = differingFields(sent.config ?? {}, edit.config);
164
+ if (!isEmpty(config)) {
165
+ remaining.config = config;
166
+ }
167
+ }
168
+ return remaining;
169
+ }
170
+
171
+ // Transitions ---------------------------------------------------------------
172
+ //
173
+ // Transitions are total. One with no effect returns its input, so a store
174
+ // can skip the write and its notifications.
175
+
176
+ function withCell(
177
+ draft: Draft,
178
+ id: Protocol.CellId,
179
+ cell: CellDraft | undefined,
180
+ ): Draft {
181
+ const cells = new Map(draft.cells);
182
+ if (cell === undefined) {
183
+ cells.delete(id);
184
+ } else {
185
+ cells.set(id, cell);
186
+ }
187
+ return { ...draft, cells };
188
+ }
189
+
190
+ /** Place a cell, falling back to the end if its anchor has disappeared. */
191
+ function insertAt(
192
+ order: readonly Protocol.CellId[],
193
+ id: Protocol.CellId,
194
+ placement: Placement,
195
+ ): Protocol.CellId[] {
196
+ const without = order.filter((other) => other !== id);
197
+ let index = without.length;
198
+ if (placement.after !== undefined) {
199
+ const anchor = without.indexOf(placement.after);
200
+ if (anchor !== -1) {
201
+ index = anchor + 1;
202
+ }
203
+ } else if (placement.before !== undefined) {
204
+ const anchor = without.indexOf(placement.before);
205
+ if (anchor !== -1) {
206
+ index = anchor;
207
+ }
208
+ }
209
+ return [...without.slice(0, index), id, ...without.slice(index)];
210
+ }
211
+
212
+ /**
213
+ * Edit a cell's fields.
214
+ *
215
+ * An edit to a cell the host has removed is dropped: there is no state to
216
+ * apply it to. So is an edit to a cell this draft deletes.
217
+ */
218
+ export function editCell(
219
+ confirmed: Document,
220
+ draft: Draft,
221
+ id: Protocol.CellId,
222
+ edit: CellEdit,
223
+ ): Draft {
224
+ const current = draft.cells.get(id);
225
+ if (current?.kind === "delete") {
226
+ return draft;
227
+ }
228
+ if (current?.kind === "create") {
229
+ const cell = applyEdit(current.cell, edit);
230
+ return sameCell(cell, current.cell)
231
+ ? draft
232
+ : withCell(draft, id, { ...current, cell });
233
+ }
234
+ const base = confirmed.cells.get(id);
235
+ if (base === undefined) {
236
+ return draft;
237
+ }
238
+ const previous = current?.edit ?? {};
239
+ const next = mergeEdit(previous, edit);
240
+ return sameCell(applyEdit(base, previous), applyEdit(base, next))
241
+ ? draft
242
+ : withCell(draft, id, { kind: "edit", edit: next });
243
+ }
244
+
245
+ /**
246
+ * Create a cell with the id the client chose.
247
+ *
248
+ * Omitted fields take the protocol's defaults. A draft order, if any, gains
249
+ * the cell at its placement so that it stays complete.
250
+ */
251
+ export function createCell(
252
+ draft: Draft,
253
+ input: Protocol.CellInput,
254
+ placement: Placement = {},
255
+ ): Draft {
256
+ const parsed = Protocol.CellInput.parse(input);
257
+ const cell: CellWithSource = {
258
+ id: parsed.id,
259
+ code: parsed.code,
260
+ name: parsed.name,
261
+ config: parsed.config ?? Protocol.CellConfigInput.parse({}),
262
+ };
263
+ const next = withCell(draft, cell.id, { kind: "create", cell, placement });
264
+ return draft.order === null
265
+ ? next
266
+ : { ...next, order: insertAt(draft.order, cell.id, placement) };
267
+ }
268
+
269
+ /** Forget an unsent creation; otherwise retain the deletion until confirmed. */
270
+ export function deleteCell(
271
+ draft: Draft,
272
+ id: Protocol.CellId,
273
+ sent: Draft = EMPTY_DRAFT,
274
+ ): Draft {
275
+ const current = draft.cells.get(id);
276
+ if (current?.kind === "delete") {
277
+ return draft;
278
+ }
279
+ const next = withCell(
280
+ draft,
281
+ id,
282
+ current?.kind === "create" && sent.cells.get(id)?.kind !== "create"
283
+ ? undefined
284
+ : { kind: "delete" },
285
+ );
286
+ return draft.order === null
287
+ ? next
288
+ : { ...next, order: draft.order.filter((other) => other !== id) };
289
+ }
290
+
291
+ /** Replace the order. Lists each cell of the document once, as `set_order` does. */
292
+ export function setOrder(
293
+ draft: Draft,
294
+ order: readonly Protocol.CellId[],
295
+ ): Draft {
296
+ return draft.order !== null && sameOrder(draft.order, order)
297
+ ? draft
298
+ : { ...draft, order: [...order] };
299
+ }
300
+
301
+ /** Drop satisfied deletions, retaining edits to missing cells for recovery. */
302
+ export function prune(
303
+ confirmed: Document,
304
+ draft: Draft,
305
+ sent: Draft = EMPTY_DRAFT,
306
+ ): Draft {
307
+ let cells: Map<Protocol.CellId, CellDraft> | undefined;
308
+ for (const [id, cell] of draft.cells) {
309
+ if (
310
+ cell.kind === "delete" &&
311
+ !confirmed.cells.has(id) &&
312
+ sent.cells.get(id)?.kind !== "create"
313
+ ) {
314
+ cells ??= new Map(draft.cells);
315
+ cells.delete(id);
316
+ }
317
+ }
318
+ return cells === undefined ? draft : { ...draft, cells };
319
+ }
320
+
321
+ /**
322
+ * Release a draft after the accepted revision is present in the source.
323
+ *
324
+ * `sent` is the draft the accepted request was derived from. A field is
325
+ * released when its value is still the one sent; a created cell becomes an
326
+ * edit of whatever changed after it was sent. The model enforces the revision
327
+ * precondition; this pure transition assumes it has already been checked.
328
+ */
329
+ export function acknowledge(draft: Draft, sent: Draft): Draft {
330
+ let next = draft;
331
+ for (const [id, accepted] of sent.cells) {
332
+ const current = next.cells.get(id);
333
+ if (current?.kind === "delete" && accepted.kind === "delete") {
334
+ next = withCell(next, id, undefined);
335
+ } else if (current?.kind === "create" && accepted.kind === "create") {
336
+ next = withEdit(next, id, editBetween(accepted.cell, current.cell));
337
+ } else if (current?.kind === "edit" && accepted.kind === "edit") {
338
+ next = withEdit(next, id, remainingEdit(current.edit, accepted.edit));
339
+ }
340
+ }
341
+ if (
342
+ next.order !== null &&
343
+ sent.order !== null &&
344
+ sameOrder(next.order, sent.order)
345
+ ) {
346
+ next = { ...next, order: null };
347
+ }
348
+ return next;
349
+ }
350
+
351
+ function withEdit(draft: Draft, id: Protocol.CellId, edit: CellEdit): Draft {
352
+ return withCell(
353
+ draft,
354
+ id,
355
+ isEmpty(edit) ? undefined : { kind: "edit", edit },
356
+ );
357
+ }
358
+
359
+ // Projections ---------------------------------------------------------------
360
+
361
+ /** A create or edit as the edit it makes to an existing cell. */
362
+ function editOf(local: Exclude<CellDraft, { kind: "delete" }>): CellEdit {
363
+ return local.kind === "edit" ? local.edit : local.cell;
364
+ }
365
+
366
+ /**
367
+ * The confirmed document with a draft applied.
368
+ *
369
+ * A cell without a draft is the confirmed record itself, so its identity is
370
+ * stable until the host changes it. Cells the host added since a draft order
371
+ * was set go at the end; cells it removed leave the order.
372
+ */
373
+ export function viewDocument(
374
+ confirmed: Document,
375
+ draft: Draft,
376
+ ): EditableDocument {
377
+ const cells = new Map<Protocol.CellId, EditableCell>();
378
+ for (const [id, cell] of confirmed.cells) {
379
+ const local = draft.cells.get(id);
380
+ if (local?.kind === "delete") {
381
+ continue;
382
+ }
383
+ cells.set(id, local === undefined ? cell : applyEdit(cell, editOf(local)));
384
+ }
385
+ let order: readonly Protocol.CellId[] =
386
+ draft.order ?? confirmed.revision.order;
387
+ for (const [id, local] of draft.cells) {
388
+ if (local.kind === "create" && !confirmed.cells.has(id)) {
389
+ cells.set(id, local.cell);
390
+ if (draft.order === null) {
391
+ order = insertAt(order, id, local.placement);
392
+ }
393
+ }
394
+ }
395
+ const placed = new Set(order);
396
+ const unplaced = [...cells.keys()].filter((id) => !placed.has(id));
397
+ if (unplaced.length > 0 || order.some((id) => !cells.has(id))) {
398
+ order = [...order.filter((id) => cells.has(id)), ...unplaced];
399
+ }
400
+ return {
401
+ order,
402
+ cells,
403
+ };
404
+ }
405
+
406
+ /**
407
+ * The edit request that would make the host match the view.
408
+ *
409
+ * Preconditions are the confirmed etags. A put with no effect is left out.
410
+ * Cells come before the order, so one request can create a cell and then
411
+ * place it. Returns null if a put lacks source or its edited cell was removed,
412
+ * keeping the draft atomic and retaining local intent for recovery.
413
+ */
414
+ export function toChanges(
415
+ confirmed: Document,
416
+ draft: Draft,
417
+ ): Protocol.Change[] | null {
418
+ const changes: Protocol.Change[] = [];
419
+ let stagedOrder = confirmed.revision.order;
420
+ const stagedIds = new Set(stagedOrder);
421
+ for (const [id, local] of draft.cells) {
422
+ const base = confirmed.cells.get(id);
423
+ if (local.kind === "delete") {
424
+ if (base !== undefined) {
425
+ changes.push({ kind: "delete_cell", id, base_etag: base.etag });
426
+ stagedOrder = stagedOrder.filter((other) => other !== id);
427
+ stagedIds.delete(id);
428
+ }
429
+ } else if (local.kind === "create") {
430
+ const anchor = local.placement.after ?? local.placement.before;
431
+ const placement =
432
+ anchor === undefined || stagedIds.has(anchor) ? local.placement : {};
433
+ changes.push({
434
+ kind: "put_cell",
435
+ base_etag: null,
436
+ cell: local.cell,
437
+ ...placement,
438
+ });
439
+ if (base === undefined) {
440
+ stagedOrder = insertAt(stagedOrder, id, placement);
441
+ stagedIds.add(id);
442
+ }
443
+ } else if (base !== undefined) {
444
+ const cell = applyEdit(base, local.edit);
445
+ if (!sameCell(base, cell)) {
446
+ const { code } = cell;
447
+ if (code === null) {
448
+ return null;
449
+ }
450
+ changes.push({
451
+ kind: "put_cell",
452
+ base_etag: base.etag,
453
+ cell: { ...cell, code },
454
+ });
455
+ }
456
+ } else {
457
+ return null;
458
+ }
459
+ }
460
+ const { order } = viewDocument(confirmed, draft);
461
+ if (
462
+ (draft.order !== null && !sameOrder(order, confirmed.revision.order)) ||
463
+ !sameOrder(order, stagedOrder)
464
+ ) {
465
+ changes.push({
466
+ kind: "set_order",
467
+ base_etag: confirmed.revision.order_etag,
468
+ order: [...order],
469
+ });
470
+ }
471
+ return changes;
472
+ }
@@ -0,0 +1,118 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import type { CellId } from "@marimo-team/client/protocol";
4
+ import { useAtomValue, useSetAtom, useStore } from "jotai";
5
+ import { createContext, type ReactNode, useContext, useMemo } from "react";
6
+ import { cellCodeBinding } from "./codemirror";
7
+ import type { EditableCell } from "./draft";
8
+ import type { DocumentModel } from "./model";
9
+ import { selectCellField, selectOrder } from "./select";
10
+
11
+ const DocumentContext = createContext<DocumentModel | null>(null);
12
+
13
+ /**
14
+ * Expose a session-owned model to React. Construct the model with the same store
15
+ * as the enclosing Jotai Provider. The session owner controls its AbortSignal;
16
+ * mounting or unmounting this provider does not change the model's lifetime.
17
+ *
18
+ * ```tsx
19
+ * <Provider store={store}>
20
+ * <DocumentProvider model={model}>
21
+ * <Editor />
22
+ * </DocumentProvider>
23
+ * </Provider>
24
+ * ```
25
+ */
26
+ export function DocumentProvider({
27
+ model,
28
+ children,
29
+ }: {
30
+ model: DocumentModel;
31
+ children: ReactNode;
32
+ }) {
33
+ return (
34
+ <DocumentContext.Provider value={model}>
35
+ {children}
36
+ </DocumentContext.Provider>
37
+ );
38
+ }
39
+
40
+ function useDocumentModel() {
41
+ const model = useContext(DocumentContext);
42
+ if (model === null) {
43
+ throw new Error("Document hooks require a DocumentProvider");
44
+ }
45
+ return model;
46
+ }
47
+
48
+ /** Null while the document is unavailable; an empty array for an empty document. */
49
+ export function useCellOrder() {
50
+ const { documentAtom } = useDocumentModel();
51
+ return useAtomValue(useMemo(() => selectOrder(documentAtom), [documentAtom]));
52
+ }
53
+
54
+ function useCellField<K extends keyof EditableCell>(id: CellId, field: K) {
55
+ const { documentAtom } = useDocumentModel();
56
+ return useAtomValue(
57
+ useMemo(
58
+ () => selectCellField(documentAtom, id, field),
59
+ [documentAtom, id, field],
60
+ ),
61
+ );
62
+ }
63
+
64
+ /** Undefined for an absent cell; null when the host withholds its source. */
65
+ export function useCellCode(id: CellId) {
66
+ return useCellField(id, "code");
67
+ }
68
+
69
+ /** Undefined when the cell or document is unavailable. */
70
+ export function useCellName(id: CellId) {
71
+ return useCellField(id, "name");
72
+ }
73
+
74
+ /** Undefined when the cell or document is unavailable. */
75
+ export function useCellConfig(id: CellId) {
76
+ return useCellField(id, "config");
77
+ }
78
+
79
+ /**
80
+ * A CodeMirror extension that binds the editor's text to a cell's code.
81
+ * Stable until the cell id or document model changes.
82
+ *
83
+ * ```tsx
84
+ * const extension = useCellCodeExtension(cellId);
85
+ * ```
86
+ */
87
+ export function useCellCodeExtension(id: CellId) {
88
+ const model = useDocumentModel();
89
+ const store = useStore();
90
+ return useMemo(
91
+ () => cellCodeBinding({ store, model, cellId: id }),
92
+ [model, store, id],
93
+ );
94
+ }
95
+
96
+ /**
97
+ * A stable edit function. Synchronous callbacks publish atomically or roll back.
98
+ *
99
+ * ```ts
100
+ * const edit = useDocumentEdit();
101
+ * edit((tx) => tx.setCode(cellId, code));
102
+ * ```
103
+ */
104
+ export function useDocumentEdit() {
105
+ return useSetAtom(useDocumentModel().editAtom);
106
+ }
107
+
108
+ /**
109
+ * Sync status and explicit recovery. flush waits for edits, including newer
110
+ * typing, to reach the confirmed source and returns the revision for Run/Save.
111
+ * Rejections retain local edits; retry explicitly resubmits them. Requests
112
+ * with an unknown outcome reuse their original key within the retry window.
113
+ */
114
+ export function useDocumentSync() {
115
+ const model = useDocumentModel();
116
+ const status = useAtomValue(model.syncStatusAtom);
117
+ return { status, flush: model.flush, retry: model.retry };
118
+ }
@@ -0,0 +1,15 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ /** The editable document, its drafts, and its synchronization with the host. */
4
+
5
+ export {
6
+ DocumentProvider,
7
+ useCellCode,
8
+ useCellCodeExtension,
9
+ useCellConfig,
10
+ useCellName,
11
+ useCellOrder,
12
+ useDocumentEdit,
13
+ useDocumentSync,
14
+ } from "./hooks";
15
+ export { createDocumentModel, type DocumentModel } from "./model";
@@ -0,0 +1,55 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import type * as Protocol from "@marimo-team/client/protocol";
4
+ import type { Atom, createStore } from "jotai/vanilla";
5
+ import type { Document, NotebookBackend } from "../host";
6
+ import { createDocumentState, type DocumentState } from "./state";
7
+ import { createDocumentSync, type DocumentSyncStatus } from "./sync";
8
+
9
+ export type { DocumentEdit, DocumentEditor } from "./state";
10
+ export type { EditableCell, EditableDocument } from "./draft";
11
+ export type { DocumentSyncStatus } from "./sync";
12
+
13
+ /** The editable document and its synchronization with the host. */
14
+ export interface DocumentModel extends Pick<
15
+ DocumentState,
16
+ "documentAtom" | "editAtom"
17
+ > {
18
+ readonly syncStatusAtom: Atom<DocumentSyncStatus>;
19
+ /** Drain local edits and return the confirmed revision. */
20
+ flush(): Promise<Protocol.Document>;
21
+ /** Retry a rejection against fresh state, or repeat an uncertain request exactly. */
22
+ retry(): Promise<Protocol.Document>;
23
+ }
24
+
25
+ /** Create once per editing session; abort to stop editing and synchronization together. */
26
+ export function createDocumentModel({
27
+ store,
28
+ documentAtom,
29
+ backendAtom,
30
+ signal,
31
+ }: {
32
+ store: ReturnType<typeof createStore>;
33
+ /** The document the host last confirmed. */
34
+ documentAtom: Atom<Document | null>;
35
+ /** The backend that confirmed it, whose commands reach its host. */
36
+ backendAtom: Atom<NotebookBackend | null>;
37
+ signal: AbortSignal;
38
+ }): DocumentModel {
39
+ signal.throwIfAborted();
40
+ const state = createDocumentState({ store, source: documentAtom, signal });
41
+ const sync = createDocumentSync({
42
+ store,
43
+ documentAtom,
44
+ backendAtom,
45
+ state,
46
+ signal,
47
+ });
48
+ return {
49
+ documentAtom: state.documentAtom,
50
+ editAtom: state.editAtom,
51
+ syncStatusAtom: sync.statusAtom,
52
+ flush: sync.flush,
53
+ retry: sync.retry,
54
+ };
55
+ }
@@ -0,0 +1,41 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import type * as Protocol from "@marimo-team/client/protocol";
4
+ import { dequal } from "dequal";
5
+ import type { Atom } from "jotai/vanilla";
6
+ import { selectAtom } from "jotai/vanilla/utils";
7
+ import { type EditableCell, type EditableDocument, sameCell } from "./draft";
8
+
9
+ /**
10
+ * Select one cell of a document.
11
+ *
12
+ * The atom notifies when the cell's code, name, or config changes. A new host
13
+ * record with the same values leaves it unchanged. Each call creates an atom,
14
+ * so keep it for as long as its subscription.
15
+ */
16
+ export function selectCell(
17
+ document: Atom<EditableDocument | null>,
18
+ id: Protocol.CellId,
19
+ ): Atom<EditableCell | undefined> {
20
+ return selectAtom(
21
+ document,
22
+ (value) => value?.cells.get(id),
23
+ (a, b) => a === b || (a !== undefined && b !== undefined && sameCell(a, b)),
24
+ );
25
+ }
26
+
27
+ /** Select a document's cell order, null while the document is unavailable. */
28
+ export function selectOrder(
29
+ document: Atom<EditableDocument | null>,
30
+ ): Atom<readonly Protocol.CellId[] | null> {
31
+ return selectAtom(document, (value) => value?.order ?? null, dequal);
32
+ }
33
+
34
+ /** Select one field of a cell, unchanged until that field's value changes. */
35
+ export function selectCellField<K extends keyof EditableCell>(
36
+ document: Atom<EditableDocument | null>,
37
+ id: Protocol.CellId,
38
+ field: K,
39
+ ): Atom<EditableCell[K] | undefined> {
40
+ return selectAtom(document, (value) => value?.cells.get(id)?.[field], dequal);
41
+ }