@marimo-team/islands 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 (437) hide show
  1. package/dist/{ConnectedDataExplorerComponent-CNMliVzw.js → ConnectedDataExplorerComponent-BAiSVaHj.js} +17 -15
  2. package/dist/{ErrorBoundary-BWYjMPq8.js → ErrorBoundary-CImYa86e.js} +16 -16
  3. package/dist/{abnfDiagram-VCTEODGH-D-Ez91gV.js → abnfDiagram-VCTEODGH-BBHN92-F.js} +2 -2
  4. package/dist/any-language-editor-3E24SnCV.js +75 -0
  5. package/dist/{architectureDiagram-5GKGNRK7-CTpcNtO8.js → architectureDiagram-5GKGNRK7-C0Rd64qh.js} +1 -1
  6. package/dist/{arrays-CN1djC4k.js → arrays-R6JsImiT.js} +8 -0
  7. package/dist/assets/__vite-browser-external-SaDYcg7x.js +1 -0
  8. package/dist/assets/{worker-HPY9fbhz.js → worker-CfME3uyi.js} +3 -2
  9. package/dist/{button-CvjkwdSk.js → button-BQRdqQi1.js} +199 -192
  10. package/dist/{capabilities-CQ5IjhzL.js → capabilities-CI4xUPsp.js} +2 -2
  11. package/dist/{chat-ui-DaHynk8g.js → chat-ui-DqcauBr6.js} +1142 -1141
  12. package/dist/{check-EDwSwP89.js → check-SpOMoCLP.js} +1 -1
  13. package/dist/{chunk-IMKFNOWR-DGxi-zeo.js → chunk-IMKFNOWR-hJXey5p2.js} +1 -1
  14. package/dist/{chunk-SHT3W25Y-CV9J3rAB.js → chunk-SHT3W25Y-JMzTmmVW.js} +1 -1
  15. package/dist/{chunk-SVP7TREG-BL-9Fqgg.js → chunk-SVP7TREG-BIyoHTXi.js} +1 -1
  16. package/dist/{chunk-TICWLB2K-Cu_XMjDM.js → chunk-TICWLB2K-CCzH2Uwa.js} +1 -1
  17. package/dist/{chunk-YOKDWASO-DPZk8vhS.js → chunk-YOKDWASO-DKplTrBY.js} +20 -20
  18. package/dist/{classDiagram-ZZMXUADV-BXXV13Hq.js → classDiagram-ZZMXUADV-DB1kOPS6.js} +2 -2
  19. package/dist/{classDiagram-v2-VYDZK3BY-B7FXJ1Cp.js → classDiagram-v2-VYDZK3BY-CPaFL0OB.js} +2 -2
  20. package/dist/{code-visibility-O0uJkFyz.js → code-visibility-BZoWT5f1.js} +6 -6
  21. package/dist/{common-PAvTWnXO.js → common-CGmIoGK3.js} +6762 -6586
  22. package/dist/{copy-icon-Ft7t9bqz.js → copy-icon-C1trdvVt.js} +3 -3
  23. package/dist/{cynefinDiagram-5FMLGOSQ-CH96rwba.js → cynefinDiagram-5FMLGOSQ-CF93uliH.js} +1 -1
  24. package/dist/{dagre-GXQ25YYZ-BD3PmpFY.js → dagre-GXQ25YYZ-BBB-_p9D.js} +1 -1
  25. package/dist/{data-grid-overlay-editor-DeWySy0K.js → data-grid-overlay-editor-CtOna148.js} +5 -5
  26. package/dist/{data-grid-types-BH_r7g7l.js → data-grid-types-0PJ-9N5n.js} +1 -1
  27. package/dist/{diagram-S7CK7UJ4-BsSJJldB.js → diagram-S7CK7UJ4-BynCnbyt.js} +1 -1
  28. package/dist/{diagram-UQ7AKVKN-muQtNZc4.js → diagram-UQ7AKVKN-svOxikMa.js} +1 -1
  29. package/dist/{diagram-VX7I27RA-CdhuZAqR.js → diagram-VX7I27RA-BVQSnPx0.js} +1 -1
  30. package/dist/{diagram-Z3DM3KII-BD8mXjOj.js → diagram-Z3DM3KII-nR1J0BH5.js} +1 -1
  31. package/dist/{dialog-CEBFZNyc.js → dialog-DgeqyrEZ.js} +3 -3
  32. package/dist/{dist-DpBpA_n1.js → dist-Lm-D-Vr6.js} +3 -3
  33. package/dist/{dropdown-menu-CP_qCYHC.js → dropdown-menu-CgL3fm8P.js} +1262 -1229
  34. package/dist/{ebnfDiagram-PWID7BFC-BIN1sweE.js → ebnfDiagram-PWID7BFC-C9tq9-Qo.js} +2 -2
  35. package/dist/{erDiagram-RLTQ6QDP-Ctp5cfoN.js → erDiagram-RLTQ6QDP-BiaeB7Fi.js} +1 -1
  36. package/dist/{error-banner-D-WCIiMt.js → error-banner-D40pmfQQ.js} +83 -83
  37. package/dist/{error-entries-Bv6bZPF3.js → error-entries-CmFDQ9CA.js} +1018 -977
  38. package/dist/{feedback-button-WbN_sdUE.js → feedback-button-7ZbDdjfz.js} +23 -23
  39. package/dist/{flowDiagram-HODETNUW-3EfLrlFb.js → flowDiagram-HODETNUW-f-sLm87k.js} +2 -2
  40. package/dist/{formats-27CW_UP4.js → formats-CCxaLPNs.js} +3 -3
  41. package/dist/{ganttDiagram-EL5Y4UJY-BQT16JSc.js → ganttDiagram-EL5Y4UJY-Dh_4cBJZ.js} +2 -2
  42. package/dist/{glide-data-editor-_oc1WE9x.js → glide-data-editor-D72zmCan.js} +370 -368
  43. package/dist/{glide-utils-DJyqh0EM.js → glide-utils-Dz_F4pAY.js} +16 -16
  44. package/dist/{globals-C-fUiztk.js → globals-DE2S2io9.js} +3 -3
  45. package/dist/{highlighted-body-KPVGNVTW-zwjq21tf.js → highlighted-body-KPVGNVTW-3Ziwfz-V.js} +2 -2
  46. package/dist/{html-to-image-Bf8QMGi4.js → html-to-image-CfgsWObF.js} +630 -593
  47. package/dist/{infoDiagram-27XIBGKW-DipQoHxl.js → infoDiagram-27XIBGKW-BHrWywBe.js} +1 -1
  48. package/dist/{ishikawaDiagram-5VMMS53U-DgFMjSGz.js → ishikawaDiagram-5VMMS53U-Da4s0Reg.js} +1 -1
  49. package/dist/{kanban-definition-UXKFOSKX-w1ACVjgl.js → kanban-definition-UXKFOSKX-DDf1GVLj.js} +1 -1
  50. package/dist/{label-BDKF3Afc.js → label-D9_-zSg-.js} +3 -3
  51. package/dist/{linear-Cnaq3Tfb.js → linear-DWERh7eI.js} +121 -73
  52. package/dist/{loader-BQOfjTIJ.js → loader-DOVB0Syl.js} +12 -12
  53. package/dist/main.js +7441 -7115
  54. package/dist/{mermaid-CHo49Yv-.js → mermaid-ClwTt7OD.js} +38 -38
  55. package/dist/mermaid-HWGCJPDP-Du3-HPQA.js +2 -0
  56. package/dist/{mindmap-definition-YA3MSWOX-CrT0KSfX.js → mindmap-definition-YA3MSWOX-D60oMZtT.js} +1 -1
  57. package/dist/{number-overlay-editor-DdPkN3h1.js → number-overlay-editor-Br0lPnFE.js} +3 -3
  58. package/dist/{objects-CzUgoMIF.js → objects-DCmMvZtn.js} +1 -3
  59. package/dist/{once-BtE-wKRL.js → once-eG8Vmvjd.js} +1 -1
  60. package/dist/{pegDiagram-XKGWAZYB-BqvywNJA.js → pegDiagram-XKGWAZYB-B9wFpMga.js} +2 -2
  61. package/dist/{pieDiagram-E7YTZNPT-BfxJ_XCc.js → pieDiagram-E7YTZNPT-BZwvMuBT.js} +1 -1
  62. package/dist/precisionRound-DhAzeRcm.js +49 -0
  63. package/dist/{process-output-BZg56CCb.js → process-output-B64ZYWuI.js} +5 -5
  64. package/dist/{quadrantDiagram-AXDQQJYC-Ccw0xMjN.js → quadrantDiagram-AXDQQJYC-Blrm7V7_.js} +1 -1
  65. package/dist/{railroadDiagram-O6MQD6OU-8vFLVTPu.js → railroadDiagram-O6MQD6OU-DqhZGsN2.js} +2 -2
  66. package/dist/{react-vega-BED0DjiB.js → react-vega-C4T9Rb_w.js} +12284 -12270
  67. package/dist/{requirementDiagram-BXWQKSXE-a_MxGvxe.js → requirementDiagram-BXWQKSXE-CL5dYARV.js} +1 -1
  68. package/dist/{reveal-component-Bs9X_N3n.js → reveal-component-DnbPU885.js} +14 -14
  69. package/dist/{select-BButFi-d.js → select-CkF_GBxO.js} +537 -458
  70. package/dist/{sequenceDiagram-WJ2MYXX4-CXFMnxGh.js → sequenceDiagram-WJ2MYXX4-BSUCRd2x.js} +1 -1
  71. package/dist/{spec-BwarBpUJ.js → spec-DiSh_qJC.js} +41 -11
  72. package/dist/{stateDiagram-D77RDMKH-DQeA_CwI.js → stateDiagram-D77RDMKH-B-1Hvyb0.js} +2 -2
  73. package/dist/{stateDiagram-v2-MP3YSRHH-BxxQucm1.js → stateDiagram-v2-MP3YSRHH--HtiR4yB.js} +2 -2
  74. package/dist/style.css +1 -1
  75. package/dist/{swimlanes-42K2YHIH-Do6L-cYg.js → swimlanes-42K2YHIH-Bdfhrd-2.js} +1 -1
  76. package/dist/{swimlanesDiagram-VR7AAH4N-DUzPvSVU.js → swimlanesDiagram-VR7AAH4N-BJIfxSz0.js} +2 -2
  77. package/dist/{swiper-component-DKntyn8p.js → swiper-component-BkCm-kLg.js} +2 -2
  78. package/dist/{time-BieKwdYS.js → time-BaMbJs4z.js} +18 -18
  79. package/dist/{timeline-definition-24CTP7MA-D3c46WM5.js → timeline-definition-24CTP7MA-Cs4EfFD3.js} +1 -1
  80. package/dist/{toDate-UtiwmnH2.js → toDate-DGvRHwTk.js} +19 -18
  81. package/dist/{toast-B9UMEi8u.js → toast-Btbve3Lm.js} +231 -213
  82. package/dist/{tooltip-BO1REbqs.js → tooltip-CJ4Rqasp.js} +327 -303
  83. package/dist/{use-restore-focus-BEM7sFWl.js → use-restore-focus-CmGwRNId.js} +1482 -1433
  84. package/dist/{useAsyncData-Deh5kszT.js → useAsyncData-BmVgnMP5.js} +15 -14
  85. package/dist/{useDateFormatter-D8XruNGG.js → useDateFormatter-eCL_SdeG.js} +2 -2
  86. package/dist/{useDeepCompareMemoize-DXwzRINI.js → useDeepCompareMemoize-Ch_AeGu6.js} +1 -1
  87. package/dist/{useIframeCapabilities-DBETvvPl.js → useIframeCapabilities-CSdWn6NP.js} +1 -1
  88. package/dist/{useLifecycle-j7D9uD1o.js → useLifecycle-B5BX921H.js} +3 -3
  89. package/dist/{useTheme-CRnjkku0.js → useTheme-Bq9pi_vz.js} +6 -6
  90. package/dist/{vega-component-BeF9dfyO.js → vega-component-BgyZ-COt.js} +147 -147
  91. package/dist/{vega-embed-container-BNHE18G9.js → vega-embed-container--LYZqguO.js} +2 -2
  92. package/dist/{vega-loader.browser-DNST6uVf.js → vega-loader.browser-B9Pz0EGP.js} +298 -507
  93. package/dist/{vennDiagram-4TSXK5OY-BSZD8JcZ.js → vennDiagram-4TSXK5OY-DRu0vRjO.js} +1 -1
  94. package/dist/{wardleyDiagram-VM6X3IG4-48cUWkfA.js → wardleyDiagram-VM6X3IG4-DzbDxwDY.js} +1 -1
  95. package/dist/{xychartDiagram-S5SC5T6Z-CswYgYeu.js → xychartDiagram-S5SC5T6Z-CLWj6ZFQ.js} +2 -2
  96. package/package.json +14 -14
  97. package/src/__tests__/host-imports.test.ts +1 -0
  98. package/src/__tests__/module-entry.test.ts +32 -0
  99. package/src/__tests__/mount.test.ts +0 -20
  100. package/src/components/ai/__tests__/ai-model-dropdown.test.tsx +63 -0
  101. package/src/components/ai/ai-model-dropdown.tsx +11 -6
  102. package/src/components/app-config/ai-config.tsx +7 -2
  103. package/src/components/chat/__tests__/message-queue.test.tsx +17 -0
  104. package/src/components/chat/acp/__tests__/session-tabs.test.tsx +43 -0
  105. package/src/components/chat/acp/__tests__/state.test.ts +2 -2
  106. package/src/components/chat/acp/blocks.tsx +3 -2
  107. package/src/components/chat/acp/session-tabs.tsx +8 -3
  108. package/src/components/chat/chat-components.tsx +2 -0
  109. package/src/components/chat/chat-panel.tsx +9 -8
  110. package/src/components/chat/chat-utils.ts +4 -8
  111. package/src/components/data-table/TableTopBar.tsx +2 -0
  112. package/src/components/data-table/__tests__/chart-spec-model.test.ts +195 -195
  113. package/src/components/data-table/__tests__/column-explorer.test.tsx +27 -1
  114. package/src/components/data-table/__tests__/columns.test.tsx +105 -0
  115. package/src/components/data-table/__tests__/context-menu-interaction.test.tsx +130 -0
  116. package/src/components/data-table/__tests__/data-table.test.tsx +1 -0
  117. package/src/components/data-table/__tests__/date-popover.test.tsx +72 -0
  118. package/src/components/data-table/__tests__/export-actions.test.tsx +105 -0
  119. package/src/components/data-table/__tests__/keyboard-selection.test.tsx +188 -0
  120. package/src/components/data-table/__tests__/search-highlight.test.tsx +109 -0
  121. package/src/components/data-table/__tests__/url-detector.test.tsx +74 -0
  122. package/src/components/data-table/cell-selection/feature.ts +2 -2
  123. package/src/components/data-table/cell-selection/types.ts +2 -2
  124. package/src/components/data-table/charts/__tests__/chart-panel.test.tsx +74 -1
  125. package/src/components/data-table/charts/__tests__/spec-snapshot.test.ts +20 -20
  126. package/src/components/data-table/charts/__tests__/spec.test.ts +78 -52
  127. package/src/components/data-table/charts/__tests__/storage.test.ts +4 -1
  128. package/src/components/data-table/charts/chart-spec/spec.ts +68 -43
  129. package/src/components/data-table/charts/charts.tsx +40 -30
  130. package/src/components/data-table/charts/storage.ts +16 -5
  131. package/src/components/data-table/column-explorer-panel/column-explorer.tsx +1 -1
  132. package/src/components/data-table/column-summary/chart-spec-model.tsx +23 -16
  133. package/src/components/data-table/column-summary/column-summary.tsx +2 -4
  134. package/src/components/data-table/column-visibility-dropdown.tsx +8 -13
  135. package/src/components/data-table/columns.tsx +45 -46
  136. package/src/components/data-table/context-menu.tsx +36 -20
  137. package/src/components/data-table/data-table.tsx +13 -1
  138. package/src/components/data-table/date-popover.tsx +6 -4
  139. package/src/components/data-table/export-actions.tsx +76 -48
  140. package/src/components/data-table/range-focus/atoms.ts +4 -6
  141. package/src/components/data-table/range-focus/use-cell-range-selection.ts +65 -38
  142. package/src/components/data-table/range-focus/use-scroll-into-view.ts +21 -2
  143. package/src/components/data-table/renderers.tsx +16 -7
  144. package/src/components/data-table/row-viewer-panel/__tests__/row-viewer.test.tsx +24 -1
  145. package/src/components/data-table/row-viewer-panel/row-viewer.tsx +4 -4
  146. package/src/components/data-table/search-highlight.tsx +79 -0
  147. package/src/components/data-table/types.ts +1 -0
  148. package/src/components/data-table/url-detector.tsx +49 -9
  149. package/src/components/datasources/__tests__/column-actions.test.tsx +97 -0
  150. package/src/components/datasources/__tests__/column-preview.test.tsx +63 -2
  151. package/src/components/datasources/__tests__/table-list.test.tsx +118 -0
  152. package/src/components/datasources/column-preview.tsx +8 -2
  153. package/src/components/datasources/datasources.tsx +48 -35
  154. package/src/components/debugger/debugger-code.tsx +5 -0
  155. package/src/components/dependency-graph/dependency-graph-tree.tsx +8 -8
  156. package/src/components/dependency-graph/elements.ts +24 -21
  157. package/src/components/dependency-graph/panels.tsx +2 -1
  158. package/src/components/dependency-graph/utils/__tests__/layout.test.ts +18 -11
  159. package/src/components/editor/SortableCell.tsx +1 -3
  160. package/src/components/editor/__tests__/dynamic-favicon.test.tsx +111 -18
  161. package/src/components/editor/__tests__/package-alert.test.tsx +22 -1
  162. package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +24 -0
  163. package/src/components/editor/actions/pair-with-agent-modal.tsx +18 -15
  164. package/src/components/editor/ai/add-cell-with-ai.tsx +3 -3
  165. package/src/components/editor/ai/ai-completion-editor.tsx +1 -0
  166. package/src/components/editor/alerts/startup-logs-alert.tsx +1 -0
  167. package/src/components/editor/cell/CreateCellButton.tsx +8 -10
  168. package/src/components/editor/cell/DeleteButton.tsx +1 -0
  169. package/src/components/editor/cell/__tests__/useShouldShowInterrupt.test.ts +39 -0
  170. package/src/components/editor/cell/code/__tests__/language-toggle.test.tsx +54 -0
  171. package/src/components/editor/cell/code/cell-editor.tsx +1 -1
  172. package/src/components/editor/cell/code/language-toggle.tsx +8 -7
  173. package/src/components/editor/cell/collapse.tsx +5 -3
  174. package/src/components/editor/cell/useShouldShowInterrupt.ts +2 -12
  175. package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +30 -1
  176. package/src/components/editor/chrome/panels/cache-panel.tsx +1 -0
  177. package/src/components/editor/chrome/panels/outline/__tests__/outline-keyboard.test.tsx +84 -0
  178. package/src/components/editor/chrome/panels/outline/floating-outline.tsx +39 -14
  179. package/src/components/editor/chrome/panels/outline/useActiveOutline.tsx +3 -2
  180. package/src/components/editor/chrome/panels/packages-panel.tsx +4 -7
  181. package/src/components/editor/chrome/panels/secrets-panel.tsx +1 -0
  182. package/src/components/editor/chrome/panels/snippets-panel.tsx +5 -1
  183. package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +12 -18
  184. package/src/components/editor/chrome/wrapper/__tests__/pending-ai-cells.test.tsx +58 -1
  185. package/src/components/editor/chrome/wrapper/footer-items/runtime-settings.tsx +1 -7
  186. package/src/components/editor/chrome/wrapper/pending-ai-cells.tsx +12 -2
  187. package/src/components/editor/chrome/wrapper/storage.ts +1 -1
  188. package/src/components/editor/connections/components.tsx +1 -1
  189. package/src/components/editor/controls/__tests__/command-palette.test.tsx +53 -0
  190. package/src/components/editor/controls/__tests__/keyboard-shortcuts.test.tsx +61 -0
  191. package/src/components/editor/controls/command-palette.tsx +22 -10
  192. package/src/components/editor/controls/keyboard-shortcuts.tsx +14 -5
  193. package/src/components/editor/dynamic-favicon.tsx +21 -40
  194. package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +12 -23
  195. package/src/components/editor/file-tree/__tests__/file-explorer-interactions.test.tsx +6 -0
  196. package/src/components/editor/file-tree/__tests__/file-explorer-navigation.test.tsx +47 -0
  197. package/src/components/editor/file-tree/__tests__/use-file-search.test.tsx +91 -0
  198. package/src/components/editor/file-tree/file-explorer.tsx +45 -8
  199. package/src/components/editor/file-tree/file-name-input.tsx +1 -0
  200. package/src/components/editor/file-tree/file-viewer.tsx +4 -2
  201. package/src/components/editor/file-tree/use-file-search.ts +35 -21
  202. package/src/components/editor/header/status.tsx +28 -5
  203. package/src/components/editor/links/__tests__/cell-link.test.tsx +146 -0
  204. package/src/components/editor/links/cell-link.tsx +14 -5
  205. package/src/components/editor/navigation/focus-utils.ts +4 -4
  206. package/src/components/editor/notebook-banner.tsx +1 -0
  207. package/src/components/editor/notebook-cell.tsx +11 -2
  208. package/src/components/editor/output/MarimoTracebackOutput.tsx +19 -17
  209. package/src/components/editor/output/__tests__/traceback.test.tsx +27 -1
  210. package/src/components/editor/output/ansi-reduce.ts +33 -20
  211. package/src/components/editor/output/console/ConsoleOutput.tsx +19 -25
  212. package/src/components/editor/package-alert.tsx +14 -7
  213. package/src/components/editor/renderers/grid-layout/grid-layout.tsx +12 -6
  214. package/src/components/editor/renderers/vertical-layout/sidebar/toggle.tsx +7 -1
  215. package/src/components/home/components.tsx +17 -31
  216. package/src/components/icons/__tests__/large-spinner.test.tsx +39 -0
  217. package/src/components/icons/large-spinner.tsx +3 -14
  218. package/src/components/icons/spinner.tsx +1 -1
  219. package/src/components/mcp/mcp-status-indicator.tsx +1 -1
  220. package/src/components/pages/__tests__/home-page.test.tsx +116 -0
  221. package/src/components/pages/home-page.tsx +164 -91
  222. package/src/components/scratchpad/scratchpad.test.tsx +95 -0
  223. package/src/components/scratchpad/scratchpad.tsx +16 -3
  224. package/src/components/slides/reveal-component.tsx +4 -2
  225. package/src/components/slides/slide-cell-view.tsx +1 -1
  226. package/src/components/slides/slide-form.tsx +1 -0
  227. package/src/components/tracing/tracing-keyboard.test.tsx +85 -0
  228. package/src/components/tracing/tracing.tsx +45 -41
  229. package/src/components/ui/__tests__/draggable-popover.test.tsx +24 -0
  230. package/src/components/ui/alert-dialog.tsx +1 -1
  231. package/src/components/ui/draggable-popover.tsx +24 -2
  232. package/src/core/ai/context/providers/datasource.ts +6 -2
  233. package/src/core/ai/tools/base.ts +16 -7
  234. package/src/core/ai/tools/edit-notebook-tool.ts +47 -45
  235. package/src/core/ai/tools/registry.ts +13 -8
  236. package/src/core/ai/tools/sample-tool.ts +6 -7
  237. package/src/core/attachments/state.ts +5 -8
  238. package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +4 -0
  239. package/src/core/cells/__tests__/cell.test.ts +39 -0
  240. package/src/core/cells/__tests__/cells.test.ts +3 -1
  241. package/src/core/cells/__tests__/collapseConsoleOutputs.test.ts +54 -0
  242. package/src/core/cells/__tests__/notebook-outline.test.ts +113 -0
  243. package/src/core/cells/__tests__/session.test.ts +117 -36
  244. package/src/core/cells/cell.ts +14 -3
  245. package/src/core/cells/cells.ts +3 -1
  246. package/src/core/cells/pending-delete-service.ts +2 -6
  247. package/src/core/cells/session.ts +1 -1
  248. package/src/core/cells/types.ts +10 -1
  249. package/src/core/codemirror/__tests__/setup.test.ts +29 -29
  250. package/src/core/codemirror/cells/__tests__/extensions.test.ts +15 -0
  251. package/src/core/codemirror/cells/extensions.ts +7 -3
  252. package/src/core/codemirror/keymaps/vim.ts +2 -6
  253. package/src/core/codemirror/language/__tests__/sql.test.ts +22 -20
  254. package/src/core/codemirror/language/extension.ts +8 -8
  255. package/src/core/codemirror/language/languages/markdown.ts +8 -6
  256. package/src/core/codemirror/language/languages/python.ts +16 -10
  257. package/src/core/codemirror/language/languages/sql/sql.ts +9 -7
  258. package/src/core/codemirror/language/panel/panel.tsx +1 -0
  259. package/src/core/codemirror/language/types.ts +7 -7
  260. package/src/core/codemirror/rtc/extension.ts +24 -12
  261. package/src/core/codemirror/rtc/loro/awareness.ts +34 -19
  262. package/src/core/codemirror/rtc/loro/sync.ts +1 -0
  263. package/src/core/codemirror/utils.ts +1 -0
  264. package/src/core/datasets/__tests__/data-source-discovery.test.ts +4 -4
  265. package/src/core/datasets/data-source-discovery.ts +4 -6
  266. package/src/core/document/__tests__/codemirror.test.ts +485 -0
  267. package/src/core/document/__tests__/draft.test.ts +322 -0
  268. package/src/core/document/__tests__/fixtures.ts +92 -0
  269. package/src/core/document/__tests__/hooks.test.tsx +386 -0
  270. package/src/core/document/__tests__/lifecycle-scenarios.test.ts +61 -0
  271. package/src/core/document/__tests__/model.test.ts +191 -0
  272. package/src/core/document/__tests__/protocol-fixtures.ts +38 -0
  273. package/src/core/document/__tests__/recovery-scenarios.test.ts +230 -0
  274. package/src/core/document/__tests__/scenario-fixture.ts +43 -0
  275. package/src/core/document/__tests__/scenarios.test.ts +205 -0
  276. package/src/core/document/__tests__/state.test.ts +438 -0
  277. package/src/core/document/__tests__/submission.test.ts +203 -0
  278. package/src/core/document/__tests__/test-host.test.ts +92 -0
  279. package/src/core/document/__tests__/test-host.ts +247 -0
  280. package/src/core/document/__tests__/test-notebook.ts +149 -0
  281. package/src/core/document/__tests__/user.ts +61 -0
  282. package/src/core/document/codemirror.ts +311 -0
  283. package/src/core/document/draft.ts +472 -0
  284. package/src/core/document/hooks.tsx +118 -0
  285. package/src/core/document/index.ts +15 -0
  286. package/src/core/document/model.ts +55 -0
  287. package/src/core/document/select.ts +41 -0
  288. package/src/core/document/state.ts +374 -0
  289. package/src/core/document/sync.ts +369 -0
  290. package/src/core/host/AGENTS.md +8 -2
  291. package/src/core/host/__tests__/attachments.test.ts +56 -0
  292. package/src/core/host/__tests__/document.test.ts +126 -0
  293. package/src/core/host/__tests__/fixtures.ts +71 -0
  294. package/src/core/host/__tests__/replica.test.ts +550 -0
  295. package/src/core/host/__tests__/static.test.ts +169 -0
  296. package/src/core/host/__tests__/wasm.test.ts +162 -0
  297. package/src/core/host/backend.ts +19 -0
  298. package/src/core/host/connection.ts +1 -40
  299. package/src/core/host/index.ts +14 -0
  300. package/src/core/host/replica.ts +286 -0
  301. package/src/core/host/resources/attachments.ts +22 -0
  302. package/src/core/host/resources/document.ts +101 -0
  303. package/src/core/host/static.ts +72 -0
  304. package/src/core/host/wasm.ts +102 -0
  305. package/src/core/hotkeys/actions.ts +2 -4
  306. package/src/core/islands/__tests__/bootstrap.test.ts +0 -26
  307. package/src/core/islands/bootstrap.ts +0 -1
  308. package/src/core/islands/components/__tests__/IslandControls.test.tsx +7 -5
  309. package/src/core/kernel/__tests__/RuntimeState.test.ts +1 -1
  310. package/src/core/network/connection.ts +4 -2
  311. package/src/core/network/useConnectionNotice.ts +8 -2
  312. package/src/core/runtime/__tests__/createWsUrl.test.ts +77 -7
  313. package/src/core/runtime/__tests__/runtime.test.ts +32 -58
  314. package/src/core/runtime/runtime.ts +38 -42
  315. package/src/core/runtime/types.ts +0 -2
  316. package/src/core/state/__tests__/jotai.test.tsx +108 -0
  317. package/src/core/state/jotai.ts +1 -1
  318. package/src/core/static/__tests__/files.test.ts +38 -10
  319. package/src/core/static/files.ts +2 -0
  320. package/src/core/wasm/bridge.ts +38 -10
  321. package/src/core/wasm/store.ts +3 -3
  322. package/src/core/wasm/worker/bootstrap.ts +2 -1
  323. package/src/core/wasm/worker/getMarimoWheel.ts +1 -1
  324. package/src/core/wasm/worker/types.ts +11 -0
  325. package/src/core/wasm/worker/worker.ts +48 -0
  326. package/src/core/websocket/__tests__/backend.test.ts +200 -0
  327. package/src/core/websocket/__tests__/connection-state.test.ts +0 -12
  328. package/src/core/websocket/__tests__/useNotebookReplica.hook.test.tsx +141 -0
  329. package/src/core/websocket/__tests__/useNotebookSession.hook.test.tsx +154 -428
  330. package/src/core/websocket/backend.ts +106 -0
  331. package/src/core/websocket/connection-state.ts +1 -11
  332. package/src/core/websocket/state.ts +7 -0
  333. package/src/core/websocket/transports/__tests__/notebook.test.ts +445 -0
  334. package/src/core/websocket/transports/notebook.ts +190 -0
  335. package/src/core/websocket/types.ts +2 -2
  336. package/src/core/websocket/useNotebookReplica.ts +69 -0
  337. package/src/core/websocket/useNotebookSession.tsx +10 -22
  338. package/src/css/globals.css +5 -0
  339. package/src/hooks/__tests__/useAsyncData.test.tsx +148 -0
  340. package/src/hooks/__tests__/useDebounce.test.tsx +51 -0
  341. package/src/hooks/__tests__/useDeepCompareMemoize.test.tsx +39 -0
  342. package/src/hooks/__tests__/useDelayElapsed.test.tsx +28 -0
  343. package/src/hooks/__tests__/useInternalStateWithSync.test.tsx +20 -0
  344. package/src/hooks/__tests__/useScript.test.tsx +77 -0
  345. package/src/hooks/__tests__/useStateForKey.test.tsx +257 -0
  346. package/src/hooks/useAsyncData.ts +3 -3
  347. package/src/hooks/useDataSourceDiscovery.ts +6 -4
  348. package/src/hooks/useDebounce.ts +6 -10
  349. package/src/hooks/useDeepCompareMemoize.ts +1 -1
  350. package/src/hooks/useDelayElapsed.ts +14 -10
  351. package/src/hooks/useElapsedTime.ts +3 -3
  352. package/src/hooks/useEventListener.ts +1 -2
  353. package/src/hooks/useInputHistory.ts +4 -7
  354. package/src/hooks/useInternalStateWithSync.ts +6 -6
  355. package/src/hooks/useResizeObserver.ts +4 -7
  356. package/src/hooks/useScript.ts +67 -70
  357. package/src/hooks/useStateForKey.ts +93 -0
  358. package/src/mount.tsx +0 -4
  359. package/src/plugins/__tests__/plugin-object-schemas.test.ts +104 -0
  360. package/src/plugins/core/registerReactComponent.tsx +1 -1
  361. package/src/plugins/impl/CodeEditorPlugin.tsx +1 -1
  362. package/src/plugins/impl/DataEditorPlugin.tsx +1 -1
  363. package/src/plugins/impl/DataTablePlugin.tsx +29 -33
  364. package/src/plugins/impl/DropdownPlugin.tsx +2 -6
  365. package/src/plugins/impl/FileBrowserPlugin.tsx +4 -16
  366. package/src/plugins/impl/FormPlugin.tsx +1 -1
  367. package/src/plugins/impl/RangeSliderPlugin.tsx +0 -1
  368. package/src/plugins/impl/RefreshPlugin.tsx +1 -0
  369. package/src/plugins/impl/SliderPlugin.tsx +6 -8
  370. package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +117 -1
  371. package/src/plugins/impl/__tests__/FileBrowserPlugin.test.tsx +45 -0
  372. package/src/plugins/impl/__tests__/SliderPlugin.test.tsx +24 -3
  373. package/src/plugins/impl/anywidget/__tests__/registry.test.ts +2 -0
  374. package/src/plugins/impl/anywidget/__tests__/widget-binding.test.ts +241 -2
  375. package/src/plugins/impl/anywidget/runtime.ts +21 -9
  376. package/src/plugins/impl/anywidget/widget-binding.ts +160 -82
  377. package/src/plugins/impl/chat/__tests__/chat-prompts.test.tsx +93 -0
  378. package/src/plugins/impl/chat/chat-ui.tsx +14 -11
  379. package/src/plugins/impl/code/any-language-editor.tsx +2 -2
  380. package/src/plugins/impl/data-editor/glide-data-editor.tsx +8 -5
  381. package/src/plugins/impl/data-editor/glide-utils.ts +3 -1
  382. package/src/plugins/impl/data-explorer/ConnectedDataExplorerComponent.tsx +1 -1
  383. package/src/plugins/impl/data-explorer/components/column-summary.tsx +4 -2
  384. package/src/plugins/impl/data-explorer/state/reducer.ts +1 -1
  385. package/src/plugins/impl/data-frames/DataFramePlugin.tsx +1 -2
  386. package/src/plugins/impl/data-frames/__tests__/sidebar.test.tsx +160 -0
  387. package/src/plugins/impl/data-frames/forms/__tests__/__snapshots__/form.test.tsx.snap +66 -66
  388. package/src/plugins/impl/data-frames/panel.tsx +43 -16
  389. package/src/plugins/impl/mpl-interactive/MplInteractivePlugin.tsx +5 -3
  390. package/src/plugins/impl/panel/PanelPlugin.tsx +16 -17
  391. package/src/plugins/impl/panel/__tests__/utils.test.ts +39 -1
  392. package/src/plugins/impl/panel/utils.ts +17 -0
  393. package/src/plugins/impl/plotly/PlotlyPlugin.tsx +2 -5
  394. package/src/plugins/impl/plotly/selection.ts +25 -19
  395. package/src/plugins/impl/vega/VegaPlugin.tsx +2 -3
  396. package/src/plugins/impl/vega/make-selectable.ts +4 -7
  397. package/src/plugins/layout/RoutesPlugin.tsx +1 -1
  398. package/src/stories/__tests__/editor-story.test.tsx +33 -0
  399. package/src/stories/button.stories.tsx +1 -0
  400. package/src/stories/editor.stories.tsx +24 -17
  401. package/src/stories/form-wrapper.stories.tsx +2 -2
  402. package/src/stories/select.stories.tsx +2 -2
  403. package/src/stories/switchable-multi-select.stories.tsx +1 -1
  404. package/src/utils/__tests__/arrays.test.ts +35 -0
  405. package/src/utils/__tests__/batch-requests.test.ts +3 -3
  406. package/src/utils/__tests__/numbers.test.ts +48 -1
  407. package/src/utils/__tests__/objects.test.ts +21 -0
  408. package/src/utils/arrays.ts +10 -0
  409. package/src/utils/blob.ts +1 -1
  410. package/src/utils/numbers.ts +28 -17
  411. package/src/utils/objects.ts +3 -7
  412. package/src/utils/once.ts +19 -19
  413. package/src/utils/tracer.ts +9 -5
  414. package/src/utils/zod-utils.ts +1 -3
  415. package/dist/any-language-editor-DZ_YTVus.js +0 -76
  416. package/dist/assets/__vite-browser-external-BXYkiFsk.js +0 -1
  417. package/dist/mermaid-HWGCJPDP-ByCsZnYF.js +0 -2
  418. package/dist/precisionRound-DMIVQrt2.js +0 -97
  419. package/src/core/host/__tests__/events.test.ts +0 -46
  420. package/src/core/host/__tests__/notebook-client.test.ts +0 -191
  421. package/src/core/host/__tests__/protocol.test.ts +0 -214
  422. package/src/core/host/events.ts +0 -60
  423. package/src/core/host/notebook-client.ts +0 -226
  424. package/src/core/host/protocol.ts +0 -1943
  425. package/src/core/state/__tests__/jotai.test.ts +0 -25
  426. package/src/core/websocket/__tests__/createConnectionTransport.test.ts +0 -49
  427. package/src/core/websocket/__tests__/useNotebookClient.hook.test.tsx +0 -140
  428. package/src/core/websocket/transports/__tests__/basic.test.ts +0 -275
  429. package/src/core/websocket/transports/__tests__/sse.test.ts +0 -335
  430. package/src/core/websocket/transports/__tests__/transport.test.ts +0 -308
  431. package/src/core/websocket/transports/__tests__/ws.test.ts +0 -125
  432. package/src/core/websocket/transports/basic.ts +0 -102
  433. package/src/core/websocket/transports/create.ts +0 -31
  434. package/src/core/websocket/transports/sse.ts +0 -308
  435. package/src/core/websocket/transports/transport.ts +0 -50
  436. package/src/core/websocket/transports/ws.ts +0 -98
  437. package/src/core/websocket/useNotebookClient.ts +0 -80
@@ -0,0 +1,113 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { createStore } from "jotai";
4
+ import { afterEach, describe, expect, it, vi } from "vitest";
5
+ import { MockNotebook } from "@/__mocks__/notebook";
6
+ import { cellId } from "@/__tests__/branded";
7
+ import {
8
+ createNotebookActions,
9
+ notebookAtom,
10
+ notebookOutline,
11
+ notebookReducer,
12
+ } from "../cells";
13
+ import { exportedForTesting } from "../document-changes";
14
+ import type { Outline } from "../outline";
15
+
16
+ const first = cellId("first");
17
+ const second = cellId("second");
18
+ const firstOutline: Outline = {
19
+ items: [{ name: "First", level: 1, by: { id: "first" } }],
20
+ };
21
+ const secondOutline: Outline = {
22
+ items: [{ name: "Second", level: 2, by: { id: "second" } }],
23
+ };
24
+
25
+ function setup() {
26
+ const store = createStore();
27
+ store.set(
28
+ notebookAtom,
29
+ MockNotebook.notebookState({
30
+ cellData: { [first]: { code: "x = 1" }, [second]: {} },
31
+ cellRuntime: {
32
+ [first]: { outline: firstOutline },
33
+ [second]: { outline: secondOutline },
34
+ },
35
+ }),
36
+ );
37
+ const actions = createNotebookActions((action) =>
38
+ store.set(notebookAtom, (state) => notebookReducer(state, action)),
39
+ );
40
+ return { store, actions };
41
+ }
42
+
43
+ afterEach(() => {
44
+ exportedForTesting.cancelPendingChanges();
45
+ });
46
+
47
+ describe("notebookOutline", () => {
48
+ it("does not notify outline subscribers when cell code changes", () => {
49
+ const { store, actions } = setup();
50
+ const initial = store.get(notebookOutline);
51
+ const listener = vi.fn();
52
+ const unsubscribe = store.sub(notebookOutline, listener);
53
+
54
+ actions.updateCellCode({
55
+ cellId: first,
56
+ code: "x = 2",
57
+ formattingChange: false,
58
+ });
59
+
60
+ expect(store.get(notebookOutline)).toBe(initial);
61
+ expect(listener).not.toHaveBeenCalled();
62
+ unsubscribe();
63
+ });
64
+
65
+ it("updates headings when runtime outlines change or are removed", () => {
66
+ const { store } = setup();
67
+ const updated: Outline = {
68
+ items: [{ name: "Updated", level: 1, by: { id: "updated" } }],
69
+ };
70
+
71
+ for (const outline of [updated, null]) {
72
+ store.set(notebookAtom, (state) => ({
73
+ ...state,
74
+ cellRuntime: {
75
+ ...state.cellRuntime,
76
+ [first]: { ...state.cellRuntime[first], outline },
77
+ },
78
+ }));
79
+ expect(store.get(notebookOutline)).toEqual({
80
+ items: [...(outline?.items ?? []), ...secondOutline.items],
81
+ });
82
+ }
83
+ });
84
+
85
+ it("keeps headings in cell order after moving and deleting cells", () => {
86
+ const { store, actions } = setup();
87
+ actions.moveCell({ cellId: second, before: true });
88
+ expect(store.get(notebookOutline)).toEqual({
89
+ items: [...secondOutline.items, ...firstOutline.items],
90
+ });
91
+
92
+ actions.deleteCell({ cellId: second });
93
+ expect(store.get(notebookOutline)).toEqual(firstOutline);
94
+ });
95
+
96
+ it("notifies subscribers when output is replaced with the same headings", () => {
97
+ const { store } = setup();
98
+ const listener = vi.fn();
99
+ const unsubscribe = store.sub(notebookOutline, listener);
100
+
101
+ store.set(notebookAtom, (state) => ({
102
+ ...state,
103
+ cellRuntime: {
104
+ ...state.cellRuntime,
105
+ [first]: { ...state.cellRuntime[first], outline: { ...firstOutline } },
106
+ },
107
+ }));
108
+
109
+ // Consumers must reconnect to replacement heading elements in the DOM.
110
+ expect(listener).toHaveBeenCalledOnce();
111
+ unsubscribe();
112
+ });
113
+ });
@@ -46,10 +46,17 @@ describe("notebookStateFromSession", () => {
46
46
 
47
47
  const createNotebookCell = (
48
48
  id: string,
49
- code: string | null = null,
50
- name: string | null = null,
51
- config: NotebookCell["config"] | null = null,
52
- code_hash: string | null = null,
49
+ {
50
+ code = null,
51
+ name = null,
52
+ config = null,
53
+ code_hash = null,
54
+ }: {
55
+ code?: string | null;
56
+ name?: string | null;
57
+ config?: NotebookCell["config"] | null;
58
+ code_hash?: string | null;
59
+ } = {},
53
60
  ): NotebookCell => ({
54
61
  id,
55
62
  code,
@@ -87,7 +94,7 @@ describe("notebookStateFromSession", () => {
87
94
 
88
95
  it("logs error when session and notebook have different cells", () => {
89
96
  const session = createSession([createSessionCell("cell-1")]);
90
- const notebook = createNotebook([createNotebookCell("cell-2")]);
97
+ const notebook = createNotebook([createNotebookCell("cell-2", {})]);
91
98
 
92
99
  const result = notebookStateFromSession(session, notebook);
93
100
 
@@ -328,7 +335,10 @@ describe("notebookStateFromSession", () => {
328
335
  describe("notebook only scenarios", () => {
329
336
  it("creates state from notebook with single cell", () => {
330
337
  const notebook = createNotebook([
331
- createNotebookCell("cell-1", "print('hello')", "my_cell"),
338
+ createNotebookCell("cell-1", {
339
+ code: "print('hello')",
340
+ name: "my_cell",
341
+ }),
332
342
  ]);
333
343
  const result = notebookStateFromSession(null, notebook);
334
344
 
@@ -356,11 +366,15 @@ describe("notebookStateFromSession", () => {
356
366
 
357
367
  it("creates state from notebook with custom config", () => {
358
368
  const notebook = createNotebook([
359
- createNotebookCell("cell-1", "code", "name", {
360
- hide_code: true,
361
- disabled: true,
362
- column: 1,
363
- expand_output: true,
369
+ createNotebookCell("cell-1", {
370
+ code: "code",
371
+ name: "name",
372
+ config: {
373
+ hide_code: true,
374
+ disabled: true,
375
+ column: 1,
376
+ expand_output: true,
377
+ },
364
378
  }),
365
379
  ]);
366
380
  const result = notebookStateFromSession(null, notebook);
@@ -464,7 +478,10 @@ describe("notebookStateFromSession", () => {
464
478
  ),
465
479
  ]);
466
480
  const notebook = createNotebook([
467
- createNotebookCell("cell-1", "print('hello')", "my_cell"),
481
+ createNotebookCell("cell-1", {
482
+ code: "print('hello')",
483
+ name: "my_cell",
484
+ }),
468
485
  ]);
469
486
  const result = notebookStateFromSession(session, notebook);
470
487
 
@@ -495,8 +512,8 @@ describe("notebookStateFromSession", () => {
495
512
  createSessionCell("cell-2"),
496
513
  ]);
497
514
  const notebook = createNotebook([
498
- createNotebookCell("cell-2"),
499
- createNotebookCell("cell-1"),
515
+ createNotebookCell("cell-2", {}),
516
+ createNotebookCell("cell-1", {}),
500
517
  ]);
501
518
 
502
519
  const result = notebookStateFromSession(session, notebook);
@@ -514,8 +531,8 @@ describe("notebookStateFromSession", () => {
514
531
  createSessionCell("cell-2"),
515
532
  ]);
516
533
  const notebook = createNotebook([
517
- createNotebookCell("cell-1"),
518
- createNotebookCell("cell-2"),
534
+ createNotebookCell("cell-1", {}),
535
+ createNotebookCell("cell-2", {}),
519
536
  ]);
520
537
 
521
538
  const result = notebookStateFromSession(session, notebook);
@@ -669,12 +686,42 @@ describe("notebookStateFromSession", () => {
669
686
  };
670
687
  // Create notebook cells with code values 'a' through 'f'
671
688
  const notebookCells = [
672
- createNotebookCell("cell-a", "a", null, null, hashes.a),
673
- createNotebookCell("cell-b", "b", null, null, hashes.b),
674
- createNotebookCell("cell-c", "c", null, null, hashes.c),
675
- createNotebookCell("cell-d", "d", null, null, hashes.d),
676
- createNotebookCell("cell-e", "e", null, null, hashes.e),
677
- createNotebookCell("cell-f", "f", null, null, hashes.f),
689
+ createNotebookCell("cell-a", {
690
+ code: "a",
691
+ name: null,
692
+ config: null,
693
+ code_hash: hashes.a,
694
+ }),
695
+ createNotebookCell("cell-b", {
696
+ code: "b",
697
+ name: null,
698
+ config: null,
699
+ code_hash: hashes.b,
700
+ }),
701
+ createNotebookCell("cell-c", {
702
+ code: "c",
703
+ name: null,
704
+ config: null,
705
+ code_hash: hashes.c,
706
+ }),
707
+ createNotebookCell("cell-d", {
708
+ code: "d",
709
+ name: null,
710
+ config: null,
711
+ code_hash: hashes.d,
712
+ }),
713
+ createNotebookCell("cell-e", {
714
+ code: "e",
715
+ name: null,
716
+ config: null,
717
+ code_hash: hashes.e,
718
+ }),
719
+ createNotebookCell("cell-f", {
720
+ code: "f",
721
+ name: null,
722
+ config: null,
723
+ code_hash: hashes.f,
724
+ }),
678
725
  ];
679
726
  // Create session cells with matching code_hashes
680
727
  const sessionCells = [
@@ -787,11 +834,36 @@ describe("notebookStateFromSession", () => {
787
834
 
788
835
  // Notebook has cells with code 'a', 'c', 'z', 'e', 'g'
789
836
  const notebookCells = [
790
- createNotebookCell("cell-a", "a", null, null, hashes.a),
791
- createNotebookCell("cell-c", "c", null, null, hashes.c),
792
- createNotebookCell("cell-z", "z", null, null, hashes.z),
793
- createNotebookCell("cell-e", "e", null, null, hashes.e),
794
- createNotebookCell("cell-g", "g", null, null, hashes.g),
837
+ createNotebookCell("cell-a", {
838
+ code: "a",
839
+ name: null,
840
+ config: null,
841
+ code_hash: hashes.a,
842
+ }),
843
+ createNotebookCell("cell-c", {
844
+ code: "c",
845
+ name: null,
846
+ config: null,
847
+ code_hash: hashes.c,
848
+ }),
849
+ createNotebookCell("cell-z", {
850
+ code: "z",
851
+ name: null,
852
+ config: null,
853
+ code_hash: hashes.z,
854
+ }),
855
+ createNotebookCell("cell-e", {
856
+ code: "e",
857
+ name: null,
858
+ config: null,
859
+ code_hash: hashes.e,
860
+ }),
861
+ createNotebookCell("cell-g", {
862
+ code: "g",
863
+ name: null,
864
+ config: null,
865
+ code_hash: hashes.g,
866
+ }),
795
867
  ];
796
868
 
797
869
  const session = createSession(sessionCells);
@@ -908,15 +980,24 @@ describe("notebookStateFromSession", () => {
908
980
 
909
981
  // Notebook has cells: cell-1 (1 / 0, no hash), cell-2 (mo.md("Hello"), hash=moMd), cell-3 (slider, hash=slider)
910
982
  const notebookCells = [
911
- createNotebookCell("cell-1", "1 / 0", null, null, null), // no hash
912
- createNotebookCell("cell-2", 'mo.md("Hello")', null, null, hashes.moMd),
913
- createNotebookCell(
914
- "cell-3",
915
- "x = mo.ui.slider(0, 10)",
916
- null,
917
- null,
918
- hashes.slider,
919
- ),
983
+ createNotebookCell("cell-1", {
984
+ code: "1 / 0",
985
+ name: null,
986
+ config: null,
987
+ code_hash: null,
988
+ }), // no hash
989
+ createNotebookCell("cell-2", {
990
+ code: 'mo.md("Hello")',
991
+ name: null,
992
+ config: null,
993
+ code_hash: hashes.moMd,
994
+ }),
995
+ createNotebookCell("cell-3", {
996
+ code: "x = mo.ui.slider(0, 10)",
997
+ name: null,
998
+ config: null,
999
+ code_hash: hashes.slider,
1000
+ }),
920
1001
  ];
921
1002
 
922
1003
  const session = createSession(sessionCells);
@@ -7,7 +7,12 @@ import { parseOutline } from "../dom/outline";
7
7
  import type { CellMessage, OutputMessage } from "../kernel/messages";
8
8
  import type { RuntimeState } from "../network/types";
9
9
  import { collapseConsoleOutputs } from "./collapseConsoleOutputs";
10
- import type { CellRuntimeState } from "./types";
10
+ import type { CellRuntimeState, ConsoleOutputMessage } from "./types";
11
+
12
+ // Ids increase with position, so the last output holds the maximum.
13
+ function nextConsoleOutputId(outputs: ConsoleOutputMessage[]): number {
14
+ return (outputs.at(-1)?.id ?? 0) + 1;
15
+ }
11
16
 
12
17
  export function transitionCell(
13
18
  cell: CellRuntimeState,
@@ -133,6 +138,7 @@ export function transitionCell(
133
138
  mimetype: output.mimetype,
134
139
  data: output.data,
135
140
  timestamp: output.timestamp,
141
+ id: output.id,
136
142
  response: output.response ?? "",
137
143
  };
138
144
  }
@@ -146,8 +152,13 @@ export function transitionCell(
146
152
  // messageConsole is an output that needs to be appended to the
147
153
  // existing console outputs.
148
154
  consoleOutputs = Array.isArray(messageConsole)
149
- ? collapseConsoleOutputs(messageConsole)
150
- : collapseConsoleOutputs([...consoleOutputs, messageConsole]);
155
+ ? collapseConsoleOutputs(
156
+ messageConsole.map((output, i) => ({ ...output, id: i + 1 })),
157
+ )
158
+ : collapseConsoleOutputs([
159
+ ...consoleOutputs,
160
+ { ...messageConsole, id: nextConsoleOutputId(consoleOutputs) },
161
+ ]);
151
162
  }
152
163
  nextCell.consoleOutputs = consoleOutputs;
153
164
  // Derive outline from output
@@ -1114,6 +1114,7 @@ const {
1114
1114
  mimetype: stdinOutput.mimetype,
1115
1115
  data: stdinOutput.data,
1116
1116
  timestamp: stdinOutput.timestamp,
1117
+ id: stdinOutput.id,
1117
1118
  // An empty response resolves the prompt without retaining the secret.
1118
1119
  response: stdinOutput.mimetype === "text/password" ? "" : response,
1119
1120
  };
@@ -1761,7 +1762,8 @@ export const cellErrorsAtom = atom((get) => {
1761
1762
  });
1762
1763
 
1763
1764
  export const notebookOutline = atom((get) => {
1764
- const { cellIds, cellRuntime } = get(notebookAtom);
1765
+ const cellIds = get(cellIdsAtom);
1766
+ const cellRuntime = get(cellsRuntimeAtom);
1765
1767
  const outlines = cellIds.inOrderIds.map(
1766
1768
  (cellId) => cellRuntime[cellId].outline,
1767
1769
  );
@@ -134,12 +134,8 @@ export function usePendingDeleteService() {
134
134
  submit({ cellIds, deleteCell, deleteManyCells, store });
135
135
  },
136
136
  clear,
137
- get idle() {
138
- return entries.size === 0;
139
- },
140
- get shouldConfirmDelete() {
141
- return entries.size > 1;
142
- },
137
+ idle: entries.size === 0,
138
+ shouldConfirmDelete: entries.size > 1,
143
139
  };
144
140
  }
145
141
 
@@ -56,7 +56,7 @@ function mergeSessionAndNotebookCells(
56
56
  return {
57
57
  cellIds,
58
58
  sessionCellData: new Map(
59
- cellIds.map((id, idx) => [id, null] as [CellId, SessionCell | null]),
59
+ cellIds.map((id) => [id, null] as [CellId, SessionCell | null]),
60
60
  ),
61
61
  notebookCellData: new Map(
62
62
  cellIds.map(
@@ -95,7 +95,7 @@ export interface CellRuntimeState {
95
95
  /** TOC outline */
96
96
  outline: Outline | null;
97
97
  /** messages encoding the cell's console outputs. */
98
- consoleOutputs: WithResponse<OutputMessage>[];
98
+ consoleOutputs: ConsoleOutputMessage[];
99
99
  /** current status of the cell */
100
100
  status: RuntimeState;
101
101
  /** whether the cell has stale inputs*/
@@ -130,3 +130,12 @@ export type WithResponse<T> = T & {
130
130
  */
131
131
  response?: string;
132
132
  };
133
+
134
+ /**
135
+ * A console output as held by the frontend. The id is assigned when the
136
+ * output arrives and never changes, so it can identify the output while
137
+ * neighboring outputs collapse, truncate, or receive a stdin response.
138
+ */
139
+ export type ConsoleOutputMessage = WithResponse<OutputMessage> & {
140
+ id?: number;
141
+ };
@@ -100,7 +100,7 @@ function getDuplicateKeymaps(state: EditorState) {
100
100
  (keymap) => keymap,
101
101
  );
102
102
  const duplicates = Objects.fromEntries(
103
- Object.entries(groupBy).filter(([key, value]) => value.length > 1),
103
+ Object.entries(groupBy).filter(([_key, value]) => value.length > 1),
104
104
  );
105
105
  return duplicates;
106
106
  }
@@ -198,22 +198,22 @@ test("go to definition falls through to lower-priority LSP keymap", () => {
198
198
  test("placeholder adds another extension", () => {
199
199
  const opts = getOpts();
200
200
  const withAI = new PythonLanguageAdapter()
201
- .getExtension(
202
- opts.cellId,
203
- opts.completionConfig,
204
- opts.hotkeys,
205
- "marimo-import",
206
- opts.lspConfig,
207
- )
201
+ .getExtension({
202
+ cellId: opts.cellId,
203
+ completionConfig: opts.completionConfig,
204
+ hotkeys: opts.hotkeys,
205
+ placeholderType: "marimo-import",
206
+ lspConfig: opts.lspConfig,
207
+ })
208
208
  .flat();
209
209
  const withoutAI = new PythonLanguageAdapter()
210
- .getExtension(
211
- opts.cellId,
212
- opts.completionConfig,
213
- opts.hotkeys,
214
- "none",
215
- opts.lspConfig,
216
- )
210
+ .getExtension({
211
+ cellId: opts.cellId,
212
+ completionConfig: opts.completionConfig,
213
+ hotkeys: opts.hotkeys,
214
+ placeholderType: "none",
215
+ lspConfig: opts.lspConfig,
216
+ })
217
217
  .flat();
218
218
  expect(withAI.length - 1).toBe(withoutAI.length);
219
219
  });
@@ -221,22 +221,22 @@ test("placeholder adds another extension", () => {
221
221
  test("ai adds more extensions", () => {
222
222
  const opts = getOpts();
223
223
  const withAI = new PythonLanguageAdapter()
224
- .getExtension(
225
- opts.cellId,
226
- opts.completionConfig,
227
- opts.hotkeys,
228
- "ai",
229
- opts.lspConfig,
230
- )
224
+ .getExtension({
225
+ cellId: opts.cellId,
226
+ completionConfig: opts.completionConfig,
227
+ hotkeys: opts.hotkeys,
228
+ placeholderType: "ai",
229
+ lspConfig: opts.lspConfig,
230
+ })
231
231
  .flat();
232
232
  const withoutAI = new PythonLanguageAdapter()
233
- .getExtension(
234
- opts.cellId,
235
- opts.completionConfig,
236
- opts.hotkeys,
237
- "none",
238
- opts.lspConfig,
239
- )
233
+ .getExtension({
234
+ cellId: opts.cellId,
235
+ completionConfig: opts.completionConfig,
236
+ hotkeys: opts.hotkeys,
237
+ placeholderType: "none",
238
+ lspConfig: opts.lspConfig,
239
+ })
240
240
  .flat();
241
241
  expect(withAI.length - 2).toBe(withoutAI.length);
242
242
  });
@@ -68,6 +68,21 @@ describe("shouldAutorunMarkdownUpdate", () => {
68
68
  ).toBe(false);
69
69
  });
70
70
 
71
+ it("ignores remote transactions while focused", () => {
72
+ const transaction = createTransaction({
73
+ changes: { from: 0, insert: "#" },
74
+ annotations: [Transaction.remote.of(true)],
75
+ });
76
+
77
+ expect(
78
+ shouldAutorunMarkdownUpdate({
79
+ docChanged: transaction.docChanged,
80
+ transactions: [transaction],
81
+ hasFocus: true,
82
+ }),
83
+ ).toBe(false);
84
+ });
85
+
71
86
  it("ignores programmatic doc changes without a user event", () => {
72
87
  const transaction = createTransaction({
73
88
  changes: { from: 0, insert: "#" },
@@ -1,7 +1,7 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
3
  import { closeCompletion, completionStatus } from "@codemirror/autocomplete";
4
- import { type Extension, Prec } from "@codemirror/state";
4
+ import { type Extension, Prec, Transaction } from "@codemirror/state";
5
5
  import {
6
6
  EditorView,
7
7
  type KeyBinding,
@@ -382,8 +382,12 @@ function shouldAutorunMarkdownUpdate({
382
382
  }
383
383
 
384
384
  return transactions.some((tr) => {
385
- // Ignore RTC sync changes to avoid duplicate runs from remote edits.
386
- if (tr.annotation(loroSyncAnnotation) !== undefined) {
385
+ // Ignore remote changes to avoid duplicate runs. The client that made the
386
+ // edit runs it.
387
+ if (
388
+ tr.annotation(Transaction.remote) ||
389
+ tr.annotation(loroSyncAnnotation) !== undefined
390
+ ) {
387
391
  return false;
388
392
  }
389
393
 
@@ -369,7 +369,7 @@ class CodeMirrorVimSync {
369
369
  // We use onIdle to keep the focused editor snappy
370
370
  onIdle(() => {
371
371
  if (!skipBroadcast) {
372
- this.broadcastModeChange(instance, e.mode, e.subMode);
372
+ this.broadcastModeChange(instance, e.mode);
373
373
  }
374
374
  this.isBroadcasting = false;
375
375
  });
@@ -380,11 +380,7 @@ class CodeMirrorVimSync {
380
380
  this.instances.delete(instance);
381
381
  }
382
382
 
383
- public broadcastModeChange(
384
- originInstance: EditorView,
385
- mode: string,
386
- subMode?: string,
387
- ) {
383
+ public broadcastModeChange(originInstance: EditorView, mode: string) {
388
384
  invariant(
389
385
  "exitInsertMode" in Vim,
390
386
  "Vim does not have an exitInsertMode method",
@@ -42,12 +42,14 @@ const adapter = new SQLLanguageAdapter();
42
42
  const TEST_ENGINE = "test_engine" as ConnectionName;
43
43
 
44
44
  const TEST_EXTENSION_ARGS = [
45
- {} as CellId,
46
- {} as CompletionConfig,
47
- {} as HotkeyProvider,
48
- {} as PlaceholderType,
49
- {} as LSPConfig & {
50
- diagnostics: DiagnosticsConfig;
45
+ {
46
+ cellId: {} as CellId,
47
+ completionConfig: {} as CompletionConfig,
48
+ hotkeys: {} as HotkeyProvider,
49
+ placeholderType: {} as PlaceholderType,
50
+ lspConfig: {} as LSPConfig & {
51
+ diagnostics: DiagnosticsConfig;
52
+ },
51
53
  },
52
54
  ] as const;
53
55
 
@@ -930,15 +932,15 @@ describe("SQL analysis features", () => {
930
932
 
931
933
  it("reports semantic diagnostics as warnings", async () => {
932
934
  const adapter = new SQLLanguageAdapter();
933
- const extensions = adapter.getExtension(
934
- {} as CellId,
935
- {} as CompletionConfig,
936
- {} as HotkeyProvider,
937
- {} as PlaceholderType,
938
- {
935
+ const extensions = adapter.getExtension({
936
+ cellId: {} as CellId,
937
+ completionConfig: {} as CompletionConfig,
938
+ hotkeys: {} as HotkeyProvider,
939
+ placeholderType: {} as PlaceholderType,
940
+ lspConfig: {
939
941
  diagnostics: { sql_linter: true },
940
942
  } as LSPConfig & { diagnostics: DiagnosticsConfig },
941
- );
943
+ });
942
944
  const state = EditorState.create({
943
945
  doc: "SELECT missing FROM users",
944
946
  extensions: [languageMetadataField.init(() => metadata), ...extensions],
@@ -963,15 +965,15 @@ describe("SQL analysis features", () => {
963
965
 
964
966
  it("does not report unknown tables", async () => {
965
967
  const adapter = new SQLLanguageAdapter();
966
- const extensions = adapter.getExtension(
967
- {} as CellId,
968
- {} as CompletionConfig,
969
- {} as HotkeyProvider,
970
- {} as PlaceholderType,
971
- {
968
+ const extensions = adapter.getExtension({
969
+ cellId: {} as CellId,
970
+ completionConfig: {} as CompletionConfig,
971
+ hotkeys: {} as HotkeyProvider,
972
+ placeholderType: {} as PlaceholderType,
973
+ lspConfig: {
972
974
  diagnostics: { sql_linter: true },
973
975
  } as LSPConfig & { diagnostics: DiagnosticsConfig },
974
- );
976
+ });
975
977
  const state = EditorState.create({
976
978
  doc: "SELECT missing FROM users; SELECT * FROM unknown_table",
977
979
  extensions: [languageMetadataField.init(() => metadata), ...extensions],