@marimo-team/islands 0.25.3-dev16 → 0.25.3-dev167

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 (519) hide show
  1. package/dist/{ConnectedDataExplorerComponent-CNMliVzw.js → ConnectedDataExplorerComponent-BbDrlHvG.js} +17 -15
  2. package/dist/{ErrorBoundary-BEmYzeHb.js → ErrorBoundary-h9XmEWa0.js} +16 -16
  3. package/dist/{abnfDiagram-VCTEODGH-D-Ez91gV.js → abnfDiagram-VCTEODGH-Dr-fo8_z.js} +2 -2
  4. package/dist/any-language-editor-3E24SnCV.js +75 -0
  5. package/dist/{architectureDiagram-5GKGNRK7-CTpcNtO8.js → architectureDiagram-5GKGNRK7-ByKIl9GG.js} +1 -1
  6. package/dist/{arrays-CN1djC4k.js → arrays-R6JsImiT.js} +8 -0
  7. package/dist/assets/__vite-browser-external-DVeFf2e7.js +1 -0
  8. package/dist/assets/worker-C1pfNBsx.js +113 -0
  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-BsV2WH-4.js} +1143 -1142
  12. package/dist/{check-EDwSwP89.js → check-SpOMoCLP.js} +1 -1
  13. package/dist/{chunk-IMKFNOWR-DGxi-zeo.js → chunk-IMKFNOWR-IKDP18MQ.js} +1 -1
  14. package/dist/{chunk-SHT3W25Y-CV9J3rAB.js → chunk-SHT3W25Y-LkIgegwh.js} +1 -1
  15. package/dist/{chunk-SVP7TREG-BL-9Fqgg.js → chunk-SVP7TREG-DReBYtnB.js} +1 -1
  16. package/dist/{chunk-TICWLB2K-Cu_XMjDM.js → chunk-TICWLB2K-DQ0A0-1D.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-CwvXdCNE.js} +2 -2
  19. package/dist/{classDiagram-v2-VYDZK3BY-B7FXJ1Cp.js → classDiagram-v2-VYDZK3BY-D0XAxDsc.js} +2 -2
  20. package/dist/{code-visibility-D75HwL4W.js → code-visibility-C7I2fnCM.js} +6 -6
  21. package/dist/{common-hR234FGW.js → common-DZHaZLBJ.js} +6897 -6718
  22. package/dist/{copy-icon-Ft7t9bqz.js → copy-icon-C1trdvVt.js} +3 -3
  23. package/dist/{cynefinDiagram-5FMLGOSQ-CH96rwba.js → cynefinDiagram-5FMLGOSQ-BFN68nHr.js} +1 -1
  24. package/dist/{dagre-GXQ25YYZ-BD3PmpFY.js → dagre-GXQ25YYZ-CZt4rtLq.js} +1 -1
  25. package/dist/{data-grid-overlay-editor-D-NMDtk0.js → data-grid-overlay-editor-idtxmwN9.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-D5cOz47_.js} +1 -1
  28. package/dist/{diagram-UQ7AKVKN-muQtNZc4.js → diagram-UQ7AKVKN-Qgdppb4a.js} +1 -1
  29. package/dist/{diagram-VX7I27RA-CdhuZAqR.js → diagram-VX7I27RA-BsDCvily.js} +1 -1
  30. package/dist/{diagram-Z3DM3KII-BD8mXjOj.js → diagram-Z3DM3KII-igTkGjKI.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-BWnnAstC.js} +1277 -1229
  34. package/dist/{ebnfDiagram-PWID7BFC-BIN1sweE.js → ebnfDiagram-PWID7BFC-BHuZGjIr.js} +2 -2
  35. package/dist/{erDiagram-RLTQ6QDP-Ctp5cfoN.js → erDiagram-RLTQ6QDP-DKObXTVE.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-BpUJSLUZ.js} +1019 -978
  38. package/dist/{feedback-button-B3sliPJj.js → feedback-button-Ce4rnBDY.js} +23 -23
  39. package/dist/{flowDiagram-HODETNUW-3EfLrlFb.js → flowDiagram-HODETNUW-DZI_kit0.js} +2 -2
  40. package/dist/{formats-27CW_UP4.js → formats-CwJHWasD.js} +3 -3
  41. package/dist/{ganttDiagram-EL5Y4UJY-BQT16JSc.js → ganttDiagram-EL5Y4UJY-Dh_4cBJZ.js} +2 -2
  42. package/dist/{glide-data-editor-Dufy2ePW.js → glide-data-editor-DeJ7_EKL.js} +370 -368
  43. package/dist/{glide-utils-DJyqh0EM.js → glide-utils-Dz_F4pAY.js} +16 -16
  44. package/dist/{globals-B02fTTAH.js → globals-0gsyLTho.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-CQr2-IDA.js} +632 -595
  47. package/dist/{infoDiagram-27XIBGKW-DipQoHxl.js → infoDiagram-27XIBGKW-DdPl-7-n.js} +1 -1
  48. package/dist/{ishikawaDiagram-5VMMS53U-DgFMjSGz.js → ishikawaDiagram-5VMMS53U-DbyW6l59.js} +1 -1
  49. package/dist/{kanban-definition-UXKFOSKX-w1ACVjgl.js → kanban-definition-UXKFOSKX-BMX9SyBS.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 +7685 -7337
  54. package/dist/{mermaid-CHo49Yv-.js → mermaid-C8W8mTbA.js} +38 -38
  55. package/dist/mermaid-HWGCJPDP-Du3-HPQA.js +2 -0
  56. package/dist/{mindmap-definition-YA3MSWOX-CrT0KSfX.js → mindmap-definition-YA3MSWOX-Zd_tAxWL.js} +1 -1
  57. package/dist/{number-overlay-editor-EF8mCVoy.js → number-overlay-editor-20DRoFcn.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-DjYydPdo.js} +2 -2
  61. package/dist/{pieDiagram-E7YTZNPT-BfxJ_XCc.js → pieDiagram-E7YTZNPT-R5LKjO2n.js} +1 -1
  62. package/dist/precisionRound-DhAzeRcm.js +49 -0
  63. package/dist/{process-output-BZg56CCb.js → process-output-Uw-VJE5V.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-C7uNKtfm.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-CxAIzQfl.js} +1 -1
  68. package/dist/{reveal-component-Boolki8h.js → reveal-component-H3W5fYvI.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-B5tmlPd5.js} +1 -1
  71. package/dist/{spec-BwarBpUJ.js → spec-45FnujvG.js} +41 -11
  72. package/dist/{stateDiagram-D77RDMKH-DQeA_CwI.js → stateDiagram-D77RDMKH-C1hc5H7Y.js} +2 -2
  73. package/dist/{stateDiagram-v2-MP3YSRHH-BxxQucm1.js → stateDiagram-v2-MP3YSRHH-DRWeqQTn.js} +2 -2
  74. package/dist/style.css +1 -1
  75. package/dist/{swimlanes-42K2YHIH-Do6L-cYg.js → swimlanes-42K2YHIH-NTLas--s.js} +1 -1
  76. package/dist/{swimlanesDiagram-VR7AAH4N-DUzPvSVU.js → swimlanesDiagram-VR7AAH4N-ChPggljD.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-B4CZ9srJ.js} +1 -1
  80. package/dist/{toDate-UtiwmnH2.js → toDate-rySyYxqk.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-B7MiltyV.js} +15 -14
  85. package/dist/{useDateFormatter-D8XruNGG.js → useDateFormatter-eCL_SdeG.js} +2 -2
  86. package/dist/{useDeepCompareMemoize-DXwzRINI.js → useDeepCompareMemoize-DZmHBD_1.js} +1 -1
  87. package/dist/{useIframeCapabilities-DBETvvPl.js → useIframeCapabilities-CSdWn6NP.js} +1 -1
  88. package/dist/{useLifecycle-j7D9uD1o.js → useLifecycle-DRbRhzxg.js} +3 -3
  89. package/dist/{useTheme-CRnjkku0.js → useTheme-TLkqJsvo.js} +57 -54
  90. package/dist/{vega-component-BeF9dfyO.js → vega-component-DC6P127L.js} +148 -148
  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-DJIvMt44.js} +1 -1
  94. package/dist/{wardleyDiagram-VM6X3IG4-48cUWkfA.js → wardleyDiagram-VM6X3IG4-45H0zg0u.js} +1 -1
  95. package/dist/{xychartDiagram-S5SC5T6Z-CswYgYeu.js → xychartDiagram-S5SC5T6Z-ZT-XzcPN.js} +2 -2
  96. package/package.json +16 -16
  97. package/src/__tests__/module-entry.test.ts +44 -0
  98. package/src/__tests__/mount.test.ts +1 -21
  99. package/src/__tests__/{host-imports.test.ts → replica-imports.test.ts} +12 -11
  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/app-config/user-config-form.tsx +35 -0
  104. package/src/components/chat/__tests__/message-queue.test.tsx +17 -0
  105. package/src/components/chat/acp/__tests__/session-tabs.test.tsx +43 -0
  106. package/src/components/chat/acp/__tests__/state.test.ts +2 -2
  107. package/src/components/chat/acp/blocks.tsx +3 -2
  108. package/src/components/chat/acp/session-tabs.tsx +8 -3
  109. package/src/components/chat/chat-components.tsx +2 -0
  110. package/src/components/chat/chat-panel.tsx +9 -8
  111. package/src/components/chat/chat-utils.ts +4 -8
  112. package/src/components/data-table/TableBottomBar.tsx +4 -1
  113. package/src/components/data-table/TableTopBar.tsx +2 -0
  114. package/src/components/data-table/__tests__/chart-spec-model.test.ts +195 -195
  115. package/src/components/data-table/__tests__/column-explorer.test.tsx +27 -1
  116. package/src/components/data-table/__tests__/columns.test.tsx +105 -0
  117. package/src/components/data-table/__tests__/context-menu-interaction.test.tsx +130 -0
  118. package/src/components/data-table/__tests__/data-table.test.tsx +1 -0
  119. package/src/components/data-table/__tests__/date-popover.test.tsx +72 -0
  120. package/src/components/data-table/__tests__/export-actions.test.tsx +105 -0
  121. package/src/components/data-table/__tests__/keyboard-selection.test.tsx +188 -0
  122. package/src/components/data-table/__tests__/pagination.test.tsx +28 -1
  123. package/src/components/data-table/__tests__/search-highlight.test.tsx +109 -0
  124. package/src/components/data-table/__tests__/url-detector.test.tsx +74 -0
  125. package/src/components/data-table/cell-selection/feature.ts +2 -2
  126. package/src/components/data-table/cell-selection/types.ts +2 -2
  127. package/src/components/data-table/charts/__tests__/chart-panel.test.tsx +74 -1
  128. package/src/components/data-table/charts/__tests__/spec-snapshot.test.ts +20 -20
  129. package/src/components/data-table/charts/__tests__/spec.test.ts +78 -52
  130. package/src/components/data-table/charts/__tests__/storage.test.ts +4 -1
  131. package/src/components/data-table/charts/chart-spec/spec.ts +68 -43
  132. package/src/components/data-table/charts/charts.tsx +40 -30
  133. package/src/components/data-table/charts/storage.ts +16 -5
  134. package/src/components/data-table/column-explorer-panel/column-explorer.tsx +1 -1
  135. package/src/components/data-table/column-summary/chart-spec-model.tsx +23 -16
  136. package/src/components/data-table/column-summary/column-summary.tsx +2 -4
  137. package/src/components/data-table/column-visibility-dropdown.tsx +8 -13
  138. package/src/components/data-table/columns.tsx +45 -46
  139. package/src/components/data-table/context-menu.tsx +36 -20
  140. package/src/components/data-table/data-table.tsx +17 -1
  141. package/src/components/data-table/date-popover.tsx +6 -4
  142. package/src/components/data-table/export-actions.tsx +76 -48
  143. package/src/components/data-table/pagination.tsx +44 -33
  144. package/src/components/data-table/range-focus/atoms.ts +4 -6
  145. package/src/components/data-table/range-focus/use-cell-range-selection.ts +65 -38
  146. package/src/components/data-table/range-focus/use-scroll-into-view.ts +21 -2
  147. package/src/components/data-table/renderers.tsx +16 -7
  148. package/src/components/data-table/row-viewer-panel/__tests__/row-viewer.test.tsx +24 -1
  149. package/src/components/data-table/row-viewer-panel/row-viewer.tsx +4 -4
  150. package/src/components/data-table/search-highlight.tsx +79 -0
  151. package/src/components/data-table/types.ts +1 -0
  152. package/src/components/data-table/url-detector.tsx +49 -9
  153. package/src/components/datasources/__tests__/column-actions.test.tsx +97 -0
  154. package/src/components/datasources/__tests__/column-preview.test.tsx +98 -5
  155. package/src/components/datasources/__tests__/datasources-interactions.test.tsx +181 -0
  156. package/src/components/datasources/__tests__/table-list.test.tsx +118 -0
  157. package/src/components/datasources/__tests__/utils.test.ts +57 -6
  158. package/src/components/datasources/column-preview.tsx +13 -4
  159. package/src/components/datasources/datasources.tsx +51 -39
  160. package/src/components/datasources/utils.ts +32 -23
  161. package/src/components/debugger/debugger-code.tsx +5 -0
  162. package/src/components/dependency-graph/dependency-graph-tree.tsx +8 -8
  163. package/src/components/dependency-graph/elements.ts +24 -21
  164. package/src/components/dependency-graph/panels.tsx +2 -1
  165. package/src/components/dependency-graph/utils/__tests__/layout.test.ts +18 -11
  166. package/src/components/editor/SortableCell.tsx +1 -3
  167. package/src/{core/attachments/__tests__/toast.test.tsx → components/editor/__tests__/agent-toast.test.tsx} +1 -1
  168. package/src/components/editor/__tests__/data-attributes.test.tsx +6 -2
  169. package/src/components/editor/__tests__/dynamic-favicon.test.tsx +111 -18
  170. package/src/components/editor/__tests__/package-alert.test.tsx +22 -1
  171. package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +24 -0
  172. package/src/components/editor/actions/pair-with-agent-modal.tsx +18 -15
  173. package/src/components/editor/actions/useRestartKernel.tsx +1 -1
  174. package/src/components/editor/ai/add-cell-with-ai.tsx +3 -3
  175. package/src/components/editor/ai/ai-completion-editor.tsx +1 -0
  176. package/src/components/editor/alerts/__tests__/connection-notice.test.tsx +1 -1
  177. package/src/components/editor/alerts/connection-notice.tsx +1 -1
  178. package/src/components/editor/alerts/startup-logs-alert.tsx +1 -0
  179. package/src/components/editor/app-container.tsx +1 -1
  180. package/src/components/editor/boundary/__tests__/ErrorBoundary.test.tsx +1 -1
  181. package/src/components/editor/cell/CreateCellButton.tsx +10 -12
  182. package/src/components/editor/cell/DeleteButton.tsx +3 -2
  183. package/src/components/editor/cell/RunButton.tsx +2 -2
  184. package/src/components/editor/cell/StopButton.tsx +2 -2
  185. package/src/components/editor/cell/__tests__/useShouldShowInterrupt.test.ts +39 -0
  186. package/src/components/editor/cell/code/__tests__/language-toggle.test.tsx +54 -0
  187. package/src/components/editor/cell/code/cell-editor.tsx +2 -2
  188. package/src/components/editor/cell/code/language-toggle.tsx +8 -7
  189. package/src/components/editor/cell/collapse.tsx +5 -3
  190. package/src/components/editor/cell/useShouldShowInterrupt.ts +2 -12
  191. package/src/components/editor/chrome/components/__tests__/feedback-button.test.tsx +1 -1
  192. package/src/components/editor/chrome/components/feedback-button.tsx +1 -1
  193. package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +31 -2
  194. package/src/components/editor/chrome/panels/__tests__/runtime-details.test.tsx +1 -1
  195. package/src/components/editor/chrome/panels/__tests__/sandbox-recovery.test.tsx +1 -1
  196. package/src/components/editor/chrome/panels/cache-panel.tsx +1 -0
  197. package/src/components/editor/chrome/panels/outline/__tests__/outline-keyboard.test.tsx +84 -0
  198. package/src/components/editor/chrome/panels/outline/floating-outline.tsx +39 -14
  199. package/src/components/editor/chrome/panels/outline/useActiveOutline.tsx +3 -2
  200. package/src/components/editor/chrome/panels/packages-panel.tsx +4 -7
  201. package/src/components/editor/chrome/panels/secrets-panel.tsx +1 -0
  202. package/src/components/editor/chrome/panels/snippets-panel.tsx +5 -1
  203. package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +9 -18
  204. package/src/components/editor/chrome/wrapper/__tests__/pending-ai-cells.test.tsx +58 -1
  205. package/src/components/editor/chrome/wrapper/footer-items/attachment-status.tsx +4 -4
  206. package/src/components/editor/chrome/wrapper/footer-items/backend-status.tsx +1 -1
  207. package/src/components/editor/chrome/wrapper/footer-items/lsp-status.tsx +1 -1
  208. package/src/components/editor/chrome/wrapper/footer-items/machine-stats.tsx +1 -1
  209. package/src/components/editor/chrome/wrapper/footer-items/runtime-settings.tsx +1 -7
  210. package/src/components/editor/chrome/wrapper/pending-ai-cells.tsx +12 -2
  211. package/src/components/editor/chrome/wrapper/storage.ts +1 -1
  212. package/src/components/editor/connections/components.tsx +1 -1
  213. package/src/components/editor/controls/Controls.tsx +2 -2
  214. package/src/components/editor/controls/__tests__/command-palette.test.tsx +53 -0
  215. package/src/components/editor/controls/__tests__/keyboard-shortcuts.test.tsx +61 -0
  216. package/src/components/editor/controls/__tests__/notebook-menu-dropdown.test.tsx +1 -1
  217. package/src/components/editor/controls/command-palette.tsx +22 -10
  218. package/src/components/editor/controls/keyboard-shortcuts.tsx +14 -5
  219. package/src/components/editor/dynamic-favicon.tsx +21 -40
  220. package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +9 -23
  221. package/src/components/editor/errors/send-error-report-button.tsx +3 -6
  222. package/src/components/editor/file-tree/__tests__/file-explorer-interactions.test.tsx +6 -0
  223. package/src/components/editor/file-tree/__tests__/file-explorer-navigation.test.tsx +47 -0
  224. package/src/components/editor/file-tree/__tests__/use-file-search.test.tsx +91 -0
  225. package/src/components/editor/file-tree/file-explorer.tsx +45 -8
  226. package/src/components/editor/file-tree/file-name-input.tsx +1 -0
  227. package/src/components/editor/file-tree/file-viewer.tsx +4 -2
  228. package/src/components/editor/file-tree/use-file-search.ts +35 -21
  229. package/src/components/editor/header/__tests__/status.test.tsx +5 -3
  230. package/src/components/editor/header/app-header.tsx +1 -1
  231. package/src/components/editor/header/status.tsx +40 -20
  232. package/src/components/editor/links/__tests__/cell-link.test.tsx +146 -0
  233. package/src/components/editor/links/cell-link.tsx +14 -5
  234. package/src/components/editor/navigation/focus-utils.ts +4 -4
  235. package/src/components/editor/notebook-banner.tsx +1 -0
  236. package/src/components/editor/notebook-cell.tsx +12 -3
  237. package/src/components/editor/output/MarimoErrorOutput.tsx +2 -1
  238. package/src/components/editor/output/MarimoTracebackOutput.tsx +19 -17
  239. package/src/components/editor/output/Outputs.css +8 -0
  240. package/src/components/editor/output/__tests__/traceback.test.tsx +27 -1
  241. package/src/components/editor/output/ansi-reduce.ts +33 -20
  242. package/src/components/editor/output/console/ConsoleOutput.tsx +19 -25
  243. package/src/components/editor/package-alert.tsx +14 -7
  244. package/src/components/editor/renderers/grid-layout/grid-layout.tsx +12 -6
  245. package/src/components/editor/renderers/vertical-layout/sidebar/toggle.tsx +7 -1
  246. package/src/components/home/components.tsx +17 -31
  247. package/src/components/icons/__tests__/large-spinner.test.tsx +39 -0
  248. package/src/components/icons/large-spinner.tsx +3 -14
  249. package/src/components/icons/spinner.tsx +1 -1
  250. package/src/components/mcp/mcp-status-indicator.tsx +1 -1
  251. package/src/components/pages/__tests__/home-page.test.tsx +116 -0
  252. package/src/components/pages/home-page.tsx +164 -91
  253. package/src/components/scratchpad/scratchpad.test.tsx +95 -0
  254. package/src/components/scratchpad/scratchpad.tsx +16 -3
  255. package/src/components/slides/reveal-component.tsx +4 -2
  256. package/src/components/slides/slide-cell-view.tsx +1 -1
  257. package/src/components/slides/slide-form.tsx +1 -0
  258. package/src/components/tracing/tracing-keyboard.test.tsx +85 -0
  259. package/src/components/tracing/tracing.tsx +45 -41
  260. package/src/components/ui/__tests__/draggable-popover.test.tsx +24 -0
  261. package/src/components/ui/__tests__/range-slider.test.tsx +165 -0
  262. package/src/components/ui/alert-dialog.tsx +1 -1
  263. package/src/components/ui/draggable-popover.tsx +24 -2
  264. package/src/components/ui/range-slider.tsx +22 -9
  265. package/src/core/ai/context/providers/datasource.ts +6 -2
  266. package/src/core/ai/tools/base.ts +16 -7
  267. package/src/core/ai/tools/edit-notebook-tool.ts +47 -45
  268. package/src/core/ai/tools/registry.ts +13 -8
  269. package/src/core/ai/tools/sample-tool.ts +6 -7
  270. package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +4 -0
  271. package/src/core/cells/__tests__/cell.test.ts +39 -0
  272. package/src/core/cells/__tests__/cells.test.ts +3 -1
  273. package/src/core/cells/__tests__/collapseConsoleOutputs.test.ts +54 -0
  274. package/src/core/cells/__tests__/notebook-outline.test.ts +113 -0
  275. package/src/core/cells/__tests__/session.test.ts +117 -36
  276. package/src/core/cells/cell.ts +14 -3
  277. package/src/core/cells/cells.ts +3 -1
  278. package/src/core/cells/pending-delete-service.ts +2 -6
  279. package/src/core/cells/session.ts +1 -1
  280. package/src/core/cells/types.ts +10 -1
  281. package/src/core/codemirror/__tests__/setup.test.ts +29 -29
  282. package/src/core/codemirror/ai/__tests__/resources.test.ts +117 -0
  283. package/src/core/codemirror/ai/resources.ts +1 -1
  284. package/src/core/codemirror/cells/__tests__/extensions.test.ts +15 -0
  285. package/src/core/codemirror/cells/extensions.ts +7 -3
  286. package/src/core/codemirror/keymaps/vim.ts +2 -6
  287. package/src/core/codemirror/language/__tests__/sql.test.ts +22 -20
  288. package/src/core/codemirror/language/extension.ts +8 -8
  289. package/src/core/codemirror/language/languages/markdown.ts +8 -6
  290. package/src/core/codemirror/language/languages/python.ts +16 -10
  291. package/src/core/codemirror/language/languages/sql/sql.ts +9 -7
  292. package/src/core/codemirror/language/panel/panel.tsx +1 -0
  293. package/src/core/codemirror/language/types.ts +7 -7
  294. package/src/core/codemirror/readonly/__tests__/extension.test.ts +1 -1
  295. package/src/core/codemirror/readonly/extension.ts +2 -2
  296. package/src/core/codemirror/rtc/extension.ts +24 -12
  297. package/src/core/codemirror/rtc/loro/awareness.ts +34 -19
  298. package/src/core/codemirror/rtc/loro/sync.ts +1 -0
  299. package/src/core/codemirror/utils.ts +1 -0
  300. package/src/core/config/__tests__/config-schema.test.ts +5 -3
  301. package/src/core/config/config-schema.ts +3 -0
  302. package/src/core/datasets/__tests__/data-source-discovery.test.ts +5 -5
  303. package/src/core/datasets/data-source-discovery.ts +4 -6
  304. package/src/core/edit-app.tsx +2 -2
  305. package/src/core/errors/error-entries.ts +3 -0
  306. package/src/core/export/hooks.ts +1 -1
  307. package/src/core/hotkeys/actions.ts +2 -4
  308. package/src/core/islands/__tests__/bootstrap.test.ts +16 -42
  309. package/src/core/islands/__tests__/bridge.test.ts +31 -26
  310. package/src/core/islands/bootstrap.ts +22 -52
  311. package/src/core/islands/bridge.ts +31 -20
  312. package/src/core/islands/components/__tests__/IslandControls.test.tsx +7 -5
  313. package/src/core/islands/worker/__tests__/controller.test.ts +62 -4
  314. package/src/core/islands/worker/controller.ts +4 -10
  315. package/src/core/islands/worker/worker.tsx +33 -33
  316. package/src/core/kernel/__tests__/RuntimeState.test.ts +1 -1
  317. package/src/core/network/__tests__/connection.test.ts +1 -1
  318. package/src/core/network/connection-notice.ts +1 -1
  319. package/src/core/network/connection.ts +5 -3
  320. package/src/core/network/requests-lazy.ts +1 -1
  321. package/src/core/network/useConnectionNotice.ts +8 -2
  322. package/src/core/notebook/attachments.ts +38 -0
  323. package/src/core/notebook/document/__tests__/codemirror.test.ts +485 -0
  324. package/src/core/notebook/document/__tests__/draft.test.ts +322 -0
  325. package/src/core/notebook/document/__tests__/fixtures.ts +92 -0
  326. package/src/core/notebook/document/__tests__/hooks.test.tsx +391 -0
  327. package/src/core/notebook/document/__tests__/lifecycle-scenarios.test.ts +61 -0
  328. package/src/core/notebook/document/__tests__/model.test.ts +191 -0
  329. package/src/core/notebook/document/__tests__/protocol-fixtures.ts +38 -0
  330. package/src/core/notebook/document/__tests__/recovery-scenarios.test.ts +230 -0
  331. package/src/core/notebook/document/__tests__/scenario-fixture.ts +43 -0
  332. package/src/core/notebook/document/__tests__/scenarios.test.ts +205 -0
  333. package/src/core/notebook/document/__tests__/state.test.ts +438 -0
  334. package/src/core/notebook/document/__tests__/submission.test.ts +203 -0
  335. package/src/core/notebook/document/__tests__/test-host.test.ts +92 -0
  336. package/src/core/notebook/document/__tests__/test-host.ts +247 -0
  337. package/src/core/notebook/document/__tests__/test-notebook.ts +149 -0
  338. package/src/core/notebook/document/__tests__/user.ts +61 -0
  339. package/src/core/notebook/document/codemirror.ts +311 -0
  340. package/src/core/notebook/document/draft.ts +472 -0
  341. package/src/core/notebook/document/hooks.tsx +118 -0
  342. package/src/core/notebook/document/index.ts +15 -0
  343. package/src/core/notebook/document/model.ts +55 -0
  344. package/src/core/notebook/document/select.ts +41 -0
  345. package/src/core/notebook/document/state.ts +374 -0
  346. package/src/core/notebook/document/sync.ts +369 -0
  347. package/src/core/notebook/index.ts +18 -0
  348. package/src/core/notebook/replica/AGENTS.md +11 -0
  349. package/src/core/notebook/replica/__tests__/attachments.test.ts +56 -0
  350. package/src/core/notebook/replica/__tests__/document.test.ts +126 -0
  351. package/src/core/notebook/replica/__tests__/fixtures.ts +79 -0
  352. package/src/core/notebook/replica/__tests__/replica.test.ts +550 -0
  353. package/src/core/notebook/replica/backend.ts +19 -0
  354. package/src/core/notebook/replica/connection.ts +42 -0
  355. package/src/core/notebook/replica/index.ts +13 -0
  356. package/src/core/notebook/replica/replica.ts +286 -0
  357. package/src/core/notebook/replica/resources/attachments.ts +22 -0
  358. package/src/core/notebook/replica/resources/document.ts +101 -0
  359. package/src/core/packages/__tests__/useSandboxController.test.tsx +1 -1
  360. package/src/core/packages/useSandboxController.ts +1 -1
  361. package/src/core/run-app.tsx +3 -3
  362. package/src/core/runtime/__tests__/adapter.test.ts +1 -1
  363. package/src/core/runtime/__tests__/createWsUrl.test.ts +77 -7
  364. package/src/core/runtime/__tests__/runtime.test.ts +32 -58
  365. package/src/core/runtime/adapter.ts +1 -1
  366. package/src/core/runtime/config.ts +2 -2
  367. package/src/core/runtime/runtime.ts +38 -42
  368. package/src/core/runtime/types.ts +0 -2
  369. package/src/core/saving/filename.ts +1 -1
  370. package/src/core/saving/save-component.tsx +13 -4
  371. package/src/core/saving/useAutoSave.ts +1 -1
  372. package/src/core/session/__tests__/backend.test.ts +235 -0
  373. package/src/core/session/__tests__/useNotebookReplica.hook.test.tsx +183 -0
  374. package/src/core/session/__tests__/useNotebookSession.hook.test.tsx +609 -0
  375. package/src/core/session/active-notebook.ts +7 -0
  376. package/src/core/session/backend.ts +125 -0
  377. package/src/core/{websocket → session}/types.ts +2 -2
  378. package/src/core/session/useNotebookReplica.ts +72 -0
  379. package/src/core/{websocket → session}/useNotebookSession.tsx +18 -25
  380. package/src/core/state/__tests__/jotai.test.tsx +108 -0
  381. package/src/core/state/jotai.ts +1 -1
  382. package/src/core/static/__tests__/files.test.ts +38 -10
  383. package/src/core/static/files.ts +2 -0
  384. package/src/core/{websocket → transports}/__tests__/connection-state.test.ts +1 -13
  385. package/src/core/transports/__tests__/server.test.ts +408 -0
  386. package/src/core/transports/__tests__/static.test.ts +175 -0
  387. package/src/core/transports/__tests__/wasm.test.ts +166 -0
  388. package/src/core/{websocket → transports}/connection-state.ts +9 -16
  389. package/src/core/transports/server.ts +152 -0
  390. package/src/core/transports/static.ts +84 -0
  391. package/src/core/transports/wasm.ts +97 -0
  392. package/src/core/wasm/__tests__/bridge.test.ts +213 -50
  393. package/src/core/wasm/__tests__/worker.test.ts +187 -0
  394. package/src/core/wasm/bridge.ts +81 -52
  395. package/src/core/wasm/rpc.ts +15 -3
  396. package/src/core/wasm/store.ts +3 -3
  397. package/src/core/wasm/worker/bootstrap.ts +55 -51
  398. package/src/core/wasm/worker/getMarimoWheel.ts +1 -1
  399. package/src/core/wasm/worker/save-worker.ts +9 -10
  400. package/src/core/wasm/worker/types.ts +29 -18
  401. package/src/core/wasm/worker/worker.ts +97 -44
  402. package/src/css/globals.css +10 -0
  403. package/src/hooks/__tests__/useAsyncData.test.tsx +148 -0
  404. package/src/hooks/__tests__/useDebounce.test.tsx +51 -0
  405. package/src/hooks/__tests__/useDeepCompareMemoize.test.tsx +39 -0
  406. package/src/hooks/__tests__/useDelayElapsed.test.tsx +28 -0
  407. package/src/hooks/__tests__/useInternalStateWithSync.test.tsx +20 -0
  408. package/src/hooks/__tests__/useScript.test.tsx +77 -0
  409. package/src/hooks/__tests__/useStateForKey.test.tsx +257 -0
  410. package/src/hooks/useAsyncData.ts +3 -3
  411. package/src/hooks/useDataSourceDiscovery.ts +6 -4
  412. package/src/hooks/useDebounce.ts +6 -10
  413. package/src/hooks/useDeepCompareMemoize.ts +1 -1
  414. package/src/hooks/useDelayElapsed.ts +14 -10
  415. package/src/hooks/useElapsedTime.ts +3 -3
  416. package/src/hooks/useEventListener.ts +1 -2
  417. package/src/hooks/useInputHistory.ts +4 -7
  418. package/src/hooks/useInternalStateWithSync.ts +6 -6
  419. package/src/hooks/useResizeObserver.ts +4 -7
  420. package/src/hooks/useScript.ts +67 -70
  421. package/src/hooks/useStateForKey.ts +93 -0
  422. package/src/mount.tsx +1 -5
  423. package/src/plugins/__tests__/plugin-object-schemas.test.ts +104 -0
  424. package/src/plugins/core/registerReactComponent.tsx +1 -1
  425. package/src/plugins/impl/CodeEditorPlugin.tsx +1 -1
  426. package/src/plugins/impl/DataEditorPlugin.tsx +1 -1
  427. package/src/plugins/impl/DataTablePlugin.tsx +69 -37
  428. package/src/plugins/impl/DropdownPlugin.tsx +2 -6
  429. package/src/plugins/impl/FileBrowserPlugin.tsx +4 -16
  430. package/src/plugins/impl/FormPlugin.tsx +1 -1
  431. package/src/plugins/impl/RangeSliderPlugin.tsx +16 -2
  432. package/src/plugins/impl/RefreshPlugin.tsx +1 -0
  433. package/src/plugins/impl/SliderPlugin.tsx +6 -8
  434. package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +387 -1
  435. package/src/plugins/impl/__tests__/FileBrowserPlugin.test.tsx +45 -0
  436. package/src/plugins/impl/__tests__/RangeSliderPlugin.test.tsx +109 -0
  437. package/src/plugins/impl/__tests__/SliderPlugin.test.tsx +24 -3
  438. package/src/plugins/impl/anywidget/__tests__/registry.test.ts +153 -0
  439. package/src/plugins/impl/anywidget/__tests__/widget-binding.test.ts +241 -2
  440. package/src/plugins/impl/anywidget/model.ts +3 -1
  441. package/src/plugins/impl/anywidget/registry.ts +18 -14
  442. package/src/plugins/impl/anywidget/runtime.ts +21 -9
  443. package/src/plugins/impl/anywidget/widget-binding.ts +160 -82
  444. package/src/plugins/impl/chat/__tests__/chat-prompts.test.tsx +93 -0
  445. package/src/plugins/impl/chat/chat-ui.tsx +14 -11
  446. package/src/plugins/impl/code/any-language-editor.tsx +2 -2
  447. package/src/plugins/impl/data-editor/glide-data-editor.tsx +8 -5
  448. package/src/plugins/impl/data-editor/glide-utils.ts +3 -1
  449. package/src/plugins/impl/data-explorer/ConnectedDataExplorerComponent.tsx +1 -1
  450. package/src/plugins/impl/data-explorer/components/column-summary.tsx +4 -2
  451. package/src/plugins/impl/data-explorer/state/reducer.ts +1 -1
  452. package/src/plugins/impl/data-frames/DataFramePlugin.tsx +1 -2
  453. package/src/plugins/impl/data-frames/__tests__/sidebar.test.tsx +160 -0
  454. package/src/plugins/impl/data-frames/forms/__tests__/__snapshots__/form.test.tsx.snap +66 -66
  455. package/src/plugins/impl/data-frames/panel.tsx +43 -16
  456. package/src/plugins/impl/mpl-interactive/MplInteractivePlugin.tsx +5 -3
  457. package/src/plugins/impl/panel/PanelPlugin.tsx +16 -17
  458. package/src/plugins/impl/panel/__tests__/utils.test.ts +39 -1
  459. package/src/plugins/impl/panel/utils.ts +17 -0
  460. package/src/plugins/impl/plotly/PlotlyPlugin.tsx +2 -5
  461. package/src/plugins/impl/plotly/selection.ts +25 -19
  462. package/src/plugins/impl/vega/VegaPlugin.tsx +2 -3
  463. package/src/plugins/impl/vega/make-selectable.ts +4 -7
  464. package/src/plugins/layout/RoutesPlugin.tsx +1 -1
  465. package/src/stories/__tests__/editor-story.test.tsx +33 -0
  466. package/src/stories/button.stories.tsx +1 -0
  467. package/src/stories/cell.stories.tsx +1 -1
  468. package/src/stories/data-table.stories.tsx +31 -0
  469. package/src/stories/editor.stories.tsx +24 -17
  470. package/src/stories/form-wrapper.stories.tsx +2 -2
  471. package/src/stories/layout/vertical/one-column.stories.tsx +1 -1
  472. package/src/stories/runtime-story.tsx +1 -1
  473. package/src/stories/select.stories.tsx +2 -2
  474. package/src/stories/switchable-multi-select.stories.tsx +1 -1
  475. package/src/utils/__tests__/arrays.test.ts +35 -0
  476. package/src/utils/__tests__/batch-requests.test.ts +3 -3
  477. package/src/utils/__tests__/createReducer.test.ts +1 -0
  478. package/src/utils/__tests__/numbers.test.ts +105 -1
  479. package/src/utils/__tests__/objects.test.ts +21 -0
  480. package/src/utils/arrays.ts +11 -2
  481. package/src/utils/blob.ts +1 -1
  482. package/src/utils/createReducer.ts +11 -5
  483. package/src/utils/maps.ts +0 -3
  484. package/src/utils/numbers.ts +79 -18
  485. package/src/utils/objects.ts +4 -9
  486. package/src/utils/once.ts +19 -19
  487. package/src/utils/tracer.ts +9 -5
  488. package/src/utils/zod-utils.ts +1 -3
  489. package/dist/any-language-editor-DZ_YTVus.js +0 -76
  490. package/dist/assets/__vite-browser-external-BXYkiFsk.js +0 -1
  491. package/dist/assets/worker-HPY9fbhz.js +0 -114
  492. package/dist/mermaid-HWGCJPDP-ByCsZnYF.js +0 -2
  493. package/dist/precisionRound-DMIVQrt2.js +0 -97
  494. package/src/core/attachments/state.ts +0 -21
  495. package/src/core/host/AGENTS.md +0 -6
  496. package/src/core/host/__tests__/events.test.ts +0 -46
  497. package/src/core/host/__tests__/notebook-client.test.ts +0 -191
  498. package/src/core/host/__tests__/protocol.test.ts +0 -214
  499. package/src/core/host/connection.ts +0 -81
  500. package/src/core/host/events.ts +0 -60
  501. package/src/core/host/notebook-client.ts +0 -226
  502. package/src/core/host/protocol.ts +0 -1943
  503. package/src/core/state/__tests__/jotai.test.ts +0 -25
  504. package/src/core/wasm/worker/message-buffer.ts +0 -33
  505. package/src/core/websocket/__tests__/createConnectionTransport.test.ts +0 -49
  506. package/src/core/websocket/__tests__/useNotebookClient.hook.test.tsx +0 -140
  507. package/src/core/websocket/__tests__/useNotebookSession.hook.test.tsx +0 -883
  508. package/src/core/websocket/transports/__tests__/basic.test.ts +0 -275
  509. package/src/core/websocket/transports/__tests__/sse.test.ts +0 -335
  510. package/src/core/websocket/transports/__tests__/transport.test.ts +0 -308
  511. package/src/core/websocket/transports/__tests__/ws.test.ts +0 -125
  512. package/src/core/websocket/transports/basic.ts +0 -102
  513. package/src/core/websocket/transports/create.ts +0 -31
  514. package/src/core/websocket/transports/sse.ts +0 -308
  515. package/src/core/websocket/transports/transport.ts +0 -50
  516. package/src/core/websocket/transports/ws.ts +0 -98
  517. package/src/core/websocket/useNotebookClient.ts +0 -80
  518. /package/src/{core/attachments/toast.ts → components/editor/agent-toast.ts} +0 -0
  519. /package/src/core/{websocket → session}/connection-utils.ts +0 -0
@@ -1,90 +1,87 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
- import React from "react";
2
+ import { useCallback, useSyncExternalStore } from "react";
3
3
 
4
4
  type ScriptStatus = "loading" | "ready" | "error" | "unknown";
5
5
  interface ScriptOptions {
6
6
  removeOnUnmount?: boolean;
7
7
  }
8
8
 
9
+ interface ScriptEntry {
10
+ element: HTMLScriptElement;
11
+ listeners: Set<() => void>;
12
+ /** Whether useScript created the element, and so may remove it. */
13
+ owned: boolean;
14
+ }
15
+
9
16
  const defaultOptions = { removeOnUnmount: false };
10
17
 
18
+ // One entry per src, so reading the status on render is a map lookup
19
+ const scripts = new Map<string, ScriptEntry>();
20
+
21
+ function findScript(src: string): HTMLScriptElement | undefined {
22
+ // Compare attributes rather than building a selector, which `src` could break
23
+ return [...document.scripts].find(
24
+ (script) => script.getAttribute("src") === src,
25
+ );
26
+ }
27
+
28
+ function acquireScript(src: string): ScriptEntry {
29
+ const cached = scripts.get(src);
30
+ if (cached?.element.isConnected) {
31
+ return cached;
32
+ }
33
+ const existing = findScript(src);
34
+ const element = existing ?? document.createElement("script");
35
+ const entry: ScriptEntry = {
36
+ element,
37
+ listeners: new Set(),
38
+ owned: !existing,
39
+ };
40
+ const setStatus = (status: ScriptStatus) => {
41
+ element.dataset.status = status;
42
+ entry.listeners.forEach((listener) => listener());
43
+ };
44
+ // Listen for the script's lifetime rather than per subscriber, so the
45
+ // status still settles if the first subscriber unmounts mid-load.
46
+ element.addEventListener("load", () => setStatus("ready"), { once: true });
47
+ element.addEventListener("error", () => setStatus("error"), { once: true });
48
+ if (!existing) {
49
+ element.src = src;
50
+ element.async = true;
51
+ element.dataset.status = "loading";
52
+ document.body.append(element);
53
+ }
54
+ scripts.set(src, entry);
55
+ return entry;
56
+ }
57
+
11
58
  export function useScript(
12
59
  src: string,
13
60
  options: ScriptOptions = defaultOptions,
14
61
  ) {
15
- const [status, setStatus] = React.useState<ScriptStatus>(() => {
16
- const existingScript = document.querySelector<HTMLScriptElement>(
17
- `script[src="${src}"]`,
18
- );
19
- return (existingScript?.dataset.status as ScriptStatus) || "unknown";
20
- });
21
-
22
- React.useEffect(() => {
23
- let script = document.querySelector<HTMLScriptElement>(
24
- `script[src="${src}"]`,
25
- );
26
-
27
- const updateStatus = (newStatus: ScriptStatus) => {
28
- if (script) {
29
- script.dataset.status = newStatus;
30
- setStatus(newStatus);
31
- }
32
- };
33
-
34
- const handleScriptLoad = () => updateStatus("ready");
35
- const handleScriptError = () => updateStatus("error");
36
-
37
- // observes DOM changes and updates the status
38
- const observeScript = (script: HTMLScriptElement) => {
39
- const observer = new MutationObserver(() => {
40
- const newStatus = script.dataset.status;
41
- if (newStatus) {
42
- setStatus(newStatus as ScriptStatus);
43
- }
44
- });
45
- observer.observe(script, {
46
- attributes: true,
47
- attributeFilter: ["data-status"],
48
- });
49
- return observer;
50
- };
51
-
52
- if (!script) {
53
- script = document.createElement("script");
54
- script.src = src;
55
- script.async = true;
56
- updateStatus("loading");
57
- document.body.append(script);
58
-
59
- script.addEventListener("load", handleScriptLoad);
60
- script.addEventListener("error", handleScriptError);
61
-
62
- const observer = observeScript(script);
62
+ const { removeOnUnmount } = options;
63
+ const getSnapshot = useCallback((): ScriptStatus => {
64
+ const cached = scripts.get(src)?.element;
65
+ const element = cached?.isConnected ? cached : findScript(src);
66
+ return (element?.dataset.status as ScriptStatus) || "unknown";
67
+ }, [src]);
63
68
 
69
+ const subscribe = useCallback(
70
+ (onChange: () => void) => {
71
+ const entry = acquireScript(src);
72
+ entry.listeners.add(onChange);
64
73
  return () => {
65
- observer.disconnect();
66
- if (script) {
67
- script.removeEventListener("load", handleScriptLoad);
68
- script.removeEventListener("error", handleScriptError);
69
-
70
- if (options.removeOnUnmount) {
71
- script.remove();
74
+ entry.listeners.delete(onChange);
75
+ if (removeOnUnmount && entry.owned && entry.listeners.size === 0) {
76
+ entry.element.remove();
77
+ if (scripts.get(src) === entry) {
78
+ scripts.delete(src);
72
79
  }
73
80
  }
74
81
  };
75
- }
76
-
77
- const domStatus = script.dataset.status;
78
- if (domStatus) {
79
- if (domStatus === "ready" || domStatus === "error") {
80
- return;
81
- }
82
- const observer = observeScript(script);
83
- return () => observer.disconnect();
84
- }
85
-
86
- setStatus("unknown");
87
- }, [src, options.removeOnUnmount]);
82
+ },
83
+ [src, removeOnUnmount],
84
+ );
88
85
 
89
- return status;
86
+ return useSyncExternalStore(subscribe, getSnapshot);
90
87
  }
@@ -0,0 +1,93 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+ import { type SetStateAction, useCallback, useReducer } from "react";
3
+
4
+ interface KeyedState<T> {
5
+ key: unknown;
6
+ value: T;
7
+ }
8
+
9
+ type KeyedAction<T> =
10
+ | { type: "reset"; state: KeyedState<T> }
11
+ | { type: "update"; key: unknown; update: SetStateAction<T> };
12
+
13
+ function reduceKeyedState<T>(
14
+ previous: KeyedState<T>,
15
+ action: KeyedAction<T>,
16
+ ): KeyedState<T> {
17
+ if (action.type === "reset") {
18
+ return action.state;
19
+ }
20
+ if (!Object.is(previous.key, action.key)) {
21
+ return previous;
22
+ }
23
+ const value =
24
+ typeof action.update === "function"
25
+ ? (action.update as (previous: T) => T)(previous.value)
26
+ : action.update;
27
+ return Object.is(previous.value, value)
28
+ ? previous
29
+ : { key: action.key, value };
30
+ }
31
+
32
+ /**
33
+ * Local state that resets when its owner key changes.
34
+ *
35
+ * The key uses Object.is. NaN matches NaN, 0 differs from -0, and objects match
36
+ * by reference. Keep the key stable between resets. An inline object such as
37
+ * `{ id }` creates a new key on each render and causes a render loop.
38
+ *
39
+ * On a key change, this hook calls initialize() and schedules a state update
40
+ * during render. React retries the calling component before rendering its
41
+ * children. Children and effects receive the reset value without an intervening
42
+ * commit of the old value. This resets only this hook's state. The component
43
+ * stays mounted, and its other hooks keep their state.
44
+ *
45
+ * initialize must be pure. React can call it more than once in Strict Mode or
46
+ * when retrying an interrupted render. A new initializer alone does not reset
47
+ * state. The next key change uses the initializer from that render. After
48
+ * committed key changes A -> B -> A, A starts fresh instead of restoring its
49
+ * earlier value. If React abandons a pending render for B, the visible state
50
+ * for A remains intact, including edits made while B was pending.
51
+ *
52
+ * The setter accepts values or pure updater functions, like useState. To store
53
+ * a function, use setValue(() => callback). Its identity is stable for the same
54
+ * key and changes with the key. Updates from a different key are ignored,
55
+ * including their updater functions. After A -> B -> A, an old setter for A
56
+ * can update A again. This hook does not cancel requests. For request results,
57
+ * use a distinct key for each lifetime so late results stay invalid.
58
+ *
59
+ * React can still call the component for ignored or same-value updates.
60
+ * Render counts do not indicate how many changes React commits to the screen.
61
+ *
62
+ * Use useInternalStateWithSync when the external value itself supplies the
63
+ * reset value. Use this hook when a separate key controls the reset.
64
+ *
65
+ * @example
66
+ * const [expanded, setExpanded] = useStateForKey(tree, () => new Set<string>());
67
+ *
68
+ * @see https://react.dev/reference/react/useState#storing-information-from-previous-renders
69
+ */
70
+ export function useStateForKey<T>(key: unknown, initialize: () => T) {
71
+ // useState can eagerly evaluate an updater against a suspended render's
72
+ // state. A reducer checks the key when React processes each render's queue,
73
+ // so the still-visible owner can accept edits during a transition.
74
+ const [stored, dispatch] = useReducer(reduceKeyedState<T>, key, (key) => ({
75
+ key,
76
+ value: initialize(),
77
+ }));
78
+ const current = Object.is(stored.key, key)
79
+ ? stored
80
+ : { key, value: initialize() };
81
+ if (current !== stored) {
82
+ dispatch({ type: "reset", state: current });
83
+ }
84
+
85
+ const setValue = useCallback(
86
+ (update: SetStateAction<T>) => {
87
+ dispatch({ type: "update", key, update });
88
+ },
89
+ [key],
90
+ );
91
+
92
+ return [current.value, setValue] as const;
93
+ }
package/src/mount.tsx CHANGED
@@ -62,7 +62,7 @@ import {
62
62
  import { maybeRegisterVSCodeBindings } from "./core/vscode/vscode-bindings";
63
63
  import type { FileStore } from "./core/wasm/store";
64
64
  import { notebookFileStore } from "./core/wasm/store";
65
- import { ConnectionState } from "./core/websocket/types";
65
+ import { ConnectionState } from "./core/session/types";
66
66
  import {
67
67
  handleWidgetMessage,
68
68
  WIDGET_REGISTRY,
@@ -274,8 +274,6 @@ const mountOptionsSchema = z.object({
274
274
  .transform((val) => val as api.Notebook["NotebookV1"]),
275
275
  ]),
276
276
 
277
- notebookId: z.string().optional(),
278
-
279
277
  /**
280
278
  * Runtime configs
281
279
  */
@@ -286,7 +284,6 @@ const mountOptionsSchema = z.object({
286
284
  // Lazy by default, but can be overridden by the runtime config
287
285
  lazy: z.boolean().default(true),
288
286
  authToken: z.string().nullish(),
289
- notebookId: z.string().optional(),
290
287
  }),
291
288
  )
292
289
  .nullish()
@@ -367,7 +364,6 @@ function initStore(options: unknown) {
367
364
  } else {
368
365
  store.set(runtimeConfigAtom, {
369
366
  ...DEFAULT_RUNTIME_CONFIG,
370
- notebookId: parsedOptions.data.notebookId,
371
367
  lazy: false,
372
368
  serverToken: parsedOptions.data.serverToken,
373
369
  });
@@ -0,0 +1,104 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { describe, expect, it } from "vitest";
4
+ import { DataEditorPlugin } from "../impl/DataEditorPlugin";
5
+ import { DataTablePlugin } from "../impl/DataTablePlugin";
6
+ import { DataFramePlugin } from "../impl/data-frames/DataFramePlugin";
7
+ import { PlotlyPlugin } from "../impl/plotly/PlotlyPlugin";
8
+ import { VegaPlugin } from "../impl/vega/VegaPlugin";
9
+
10
+ describe("plugin object schemas", () => {
11
+ const rows = [{ value: 1, extra: { nested: true } }];
12
+ const styles = { "0": { value: { color: "red", customProperty: 1 } } };
13
+ const tableData = {
14
+ initialValue: [],
15
+ label: null,
16
+ data: rows,
17
+ totalRows: 1,
18
+ totalColumns: 1,
19
+ rowHeaders: [],
20
+ cellStyles: styles,
21
+ };
22
+
23
+ it("preserves table rows and arbitrary cell style fields", () => {
24
+ expect(DataTablePlugin.validator.parse(tableData)).toMatchObject({
25
+ data: rows,
26
+ cellStyles: styles,
27
+ pageSize: 10,
28
+ });
29
+ });
30
+
31
+ it("preserves data editor row fields", () => {
32
+ expect(
33
+ DataEditorPlugin.validator.parse({
34
+ initialValue: { edits: [] },
35
+ label: null,
36
+ data: rows,
37
+ editableColumns: "all",
38
+ }),
39
+ ).toMatchObject({ data: rows, columnNames: [] });
40
+ });
41
+
42
+ it("preserves table RPC payload fields", () => {
43
+ const functions = DataTablePlugin.functions!;
44
+ const searchResult = {
45
+ data: rows,
46
+ total_rows: 1,
47
+ cell_styles: styles,
48
+ cell_hover_texts: null,
49
+ };
50
+ expect(functions.search.output.parse(searchResult)).toEqual(searchResult);
51
+ expect(functions.get_row_ids!.input.parse({ custom: true })).toEqual({
52
+ custom: true,
53
+ });
54
+ expect(functions.get_data_url!.input.parse({ custom: true })).toEqual({
55
+ custom: true,
56
+ });
57
+ expect(
58
+ functions.get_data_url!.output.parse({ data_url: rows, format: "json" }),
59
+ ).toEqual({ data_url: rows, format: "json" });
60
+ });
61
+
62
+ it("preserves dataframe search row fields", () => {
63
+ const result = { data: rows, total_rows: 1 };
64
+ expect(DataFramePlugin.functions!.search.output.parse(result)).toEqual(
65
+ result,
66
+ );
67
+ });
68
+
69
+ it("preserves Plotly figure and config fields", () => {
70
+ const data = {
71
+ figure: { data: [], layout: { title: "Chart" } },
72
+ config: { responsive: true },
73
+ };
74
+ expect(new PlotlyPlugin().validator.parse(data)).toEqual(data);
75
+ });
76
+
77
+ it("preserves Vega spec and embed options fields and defaults", () => {
78
+ const spec = { mark: "bar", encoding: { x: { field: "value" } } };
79
+ const validator = new VegaPlugin().validator;
80
+ expect(validator.parse({ spec })).toEqual({
81
+ spec,
82
+ chartSelection: true,
83
+ fieldSelection: true,
84
+ embedOptions: {},
85
+ });
86
+ expect(
87
+ validator.parse({ spec, embedOptions: { actions: false } }).embedOptions,
88
+ ).toEqual({ actions: false });
89
+ });
90
+
91
+ it("rejects scalar values where plugin objects are required", () => {
92
+ expect(
93
+ DataTablePlugin.validator.safeParse({ ...tableData, data: [42] }).success,
94
+ ).toBe(false);
95
+ expect(
96
+ new PlotlyPlugin().validator.safeParse({ figure: 42, config: {} })
97
+ .success,
98
+ ).toBe(false);
99
+ expect(
100
+ new VegaPlugin().validator.safeParse({ spec: {}, embedOptions: 42 })
101
+ .success,
102
+ ).toBe(false);
103
+ });
104
+ });
@@ -104,7 +104,7 @@ function PluginSlotInternal<T>(
104
104
  ref: React.Ref<PluginSlotHandle>,
105
105
  ): JSX.Element {
106
106
  const [childNodes, setChildNodes] = useState<ReactNode>(children);
107
- const [value, setValue] = useState<T>(getInitialValue());
107
+ const [value, setValue] = useState<T>(getInitialValue);
108
108
  const { theme } = useTheme();
109
109
 
110
110
  const [parsedResult, setParsedResult] = useState(() => {
@@ -71,7 +71,7 @@ const CodeEditorComponent = (props: CodeEditorComponentProps) => {
71
71
  });
72
72
 
73
73
  const handleChange = useEvent((newValue: string) => {
74
- setLocalValue((_) => newValue);
74
+ setLocalValue(newValue);
75
75
  if (typeof props.debounce === "number") {
76
76
  setValueDebounced(newValue);
77
77
  } else if (!props.debounce) {
@@ -44,7 +44,7 @@ export const DataEditorPlugin = createPlugin<Edits>("marimo-data-editor", {
44
44
  ),
45
45
  }),
46
46
  label: z.string().nullable(),
47
- data: z.union([z.string(), z.array(z.object({}).passthrough())]),
47
+ data: z.union([z.string(), z.array(z.looseObject({}))]),
48
48
  fieldTypes: columnToFieldTypesSchema.nullish(),
49
49
  columnNames: z.array(z.string()).default([]),
50
50
  editableColumns: z.union([z.array(z.string()), z.literal("all")]),
@@ -231,6 +231,7 @@ type DataTableFunctions = {
231
231
  }) => Promise<{
232
232
  data: TableData<T>;
233
233
  total_rows: number | TooManyRows;
234
+ has_next_page?: boolean | null;
234
235
  cell_styles?: CellStyleState | null;
235
236
  cell_hover_texts?: Record<string, Record<string, string | null>> | null;
236
237
  raw_data?: TableData<T> | null;
@@ -258,7 +259,7 @@ export const DataTablePlugin = createPlugin<S>("marimo-table")
258
259
  z.array(z.object({ rowId: z.string(), columnName: z.string() })),
259
260
  ]),
260
261
  label: z.string().nullable(),
261
- data: z.union([z.string(), z.array(z.object({}).passthrough())]),
262
+ data: z.union([z.string(), z.array(z.looseObject({}))]),
262
263
  rawData: z.union([z.string(), z.array(z.looseObject({}))]).nullish(),
263
264
  totalRows: z.union([z.number(), z.literal(TOO_MANY_ROWS)]),
264
265
  pagination: z.boolean().default(false),
@@ -296,7 +297,7 @@ export const DataTablePlugin = createPlugin<S>("marimo-table")
296
297
  hasStableRowId: z.boolean().default(false),
297
298
  maxHeight: z.number().optional(),
298
299
  cellStyles: z
299
- .record(z.string(), z.record(z.string(), z.object({}).passthrough()))
300
+ .record(z.string(), z.record(z.string(), z.looseObject({})))
300
301
  .optional(),
301
302
  hoverTemplate: z.string().optional(),
302
303
  cellHoverTexts: cellHoverTextSchema,
@@ -341,28 +342,26 @@ export const DataTablePlugin = createPlugin<S>("marimo-table")
341
342
  )
342
343
  .output(
343
344
  z.object({
344
- data: z.union([z.string(), z.array(z.object({}).passthrough())]),
345
+ data: z.union([z.string(), z.array(z.looseObject({}))]),
345
346
  total_rows: z.union([z.number(), z.literal(TOO_MANY_ROWS)]),
347
+ has_next_page: z.boolean().nullish(),
346
348
  cell_styles: z
347
- .record(
348
- z.string(),
349
- z.record(z.string(), z.object({}).passthrough()),
350
- )
349
+ .record(z.string(), z.record(z.string(), z.looseObject({})))
351
350
  .nullable(),
352
351
  cell_hover_texts: cellHoverTextSchema.nullable(),
353
352
  raw_data: z.union([z.string(), z.array(z.looseObject({}))]).nullish(),
354
353
  }),
355
354
  ),
356
- get_row_ids: rpc.input(z.object({}).passthrough()).output(
355
+ get_row_ids: rpc.input(z.looseObject({})).output(
357
356
  z.object({
358
357
  row_ids: z.array(z.number()),
359
358
  all_rows: z.boolean(),
360
359
  error: z.string().nullable(),
361
360
  }),
362
361
  ),
363
- get_data_url: rpc.input(z.object({}).passthrough()).output(
362
+ get_data_url: rpc.input(z.looseObject({})).output(
364
363
  z.object({
365
- data_url: z.union([z.string(), z.array(z.object({}).passthrough())]),
364
+ data_url: z.union([z.string(), z.array(z.looseObject({}))]),
366
365
  format: z.enum(["csv", "json", "arrow"]),
367
366
  }),
368
367
  ),
@@ -510,6 +509,7 @@ interface DataTableSearchProps {
510
509
  filters?: ColumnFiltersState;
511
510
  setFilters?: SetFilters;
512
511
  hasStableRowId: boolean;
512
+ hasNextPage?: boolean;
513
513
  }
514
514
 
515
515
  export const LoadingDataTableComponent = memo(
@@ -546,16 +546,42 @@ export const LoadingDataTableComponent = memo(
546
546
  }
547
547
  }, [setValue, filters, searchQuery, sorting, props.hasStableRowId]);
548
548
 
549
- // If pageSize changes, reset pagination state
550
- useEffect(() => {
549
+ const [previousPageSize, setPreviousPageSize] = useState(props.pageSize);
550
+ // Reconcile before requesting data for the updated table configuration.
551
+ if (previousPageSize !== props.pageSize) {
552
+ setPreviousPageSize(props.pageSize);
551
553
  setPaginationState({ pageIndex: 0, pageSize: props.pageSize });
552
- }, [props.pageSize]);
554
+ }
555
+
556
+ const resetLazyPage = useEvent(() => {
557
+ if (props.lazy) {
558
+ setPaginationState((state) =>
559
+ state.pageIndex === 0 ? state : { ...state, pageIndex: 0 },
560
+ );
561
+ }
562
+ });
563
+
564
+ const handleSortingChange: OnChangeFn<SortingState> = useEvent(
565
+ (updater) => {
566
+ setSorting(updater);
567
+ resetLazyPage();
568
+ },
569
+ );
570
+ const handleFiltersChange: SetFilters = useEvent((updater) => {
571
+ setFilters(updater);
572
+ resetLazyPage();
573
+ });
574
+ const handleSearchQueryChange = useEvent((query: string) => {
575
+ setSearchQuery(query);
576
+ resetLazyPage();
577
+ });
553
578
 
554
579
  // Data loading
555
580
  const { data, error, isPending, isFetching } = useAsyncData<{
556
581
  rows: T[];
557
582
  rawRows?: T[];
558
583
  totalRows: number | TooManyRows;
584
+ hasNextPage?: boolean;
559
585
  cellStyles: CellStyleState | undefined | null;
560
586
  cellHoverTexts?: Record<string, Record<string, string | null>> | null;
561
587
  }>(async () => {
@@ -572,6 +598,7 @@ export const LoadingDataTableComponent = memo(
572
598
  let tableData = props.data;
573
599
  let rawTableData: TableData<T> | undefined | null = props.rawData;
574
600
  let totalRows = props.totalRows;
601
+ let hasNextPage: boolean | undefined;
575
602
  let cellStyles = props.cellStyles;
576
603
  let cellHoverTexts = props.cellHoverTexts;
577
604
 
@@ -615,6 +642,7 @@ export const LoadingDataTableComponent = memo(
615
642
  tableData = searchResults.data;
616
643
  rawTableData = searchResults.raw_data;
617
644
  totalRows = searchResults.total_rows;
645
+ hasNextPage = searchResults.has_next_page ?? undefined;
618
646
  cellStyles = searchResults.cell_styles || {};
619
647
  cellHoverTexts = searchResults.cell_hover_texts || {};
620
648
  }
@@ -627,6 +655,7 @@ export const LoadingDataTableComponent = memo(
627
655
  rows: tableData,
628
656
  rawRows: rawData,
629
657
  totalRows: totalRows,
658
+ hasNextPage,
630
659
  cellStyles,
631
660
  cellHoverTexts,
632
661
  };
@@ -697,12 +726,13 @@ export const LoadingDataTableComponent = memo(
697
726
  [search, sorting, filters, searchQuery],
698
727
  );
699
728
 
700
- // If total rows change, reset pageIndex
701
- useEffect(() => {
729
+ const [previousTotalRows, setPreviousTotalRows] = useState(data?.totalRows);
730
+ if (previousTotalRows !== data?.totalRows) {
731
+ setPreviousTotalRows(data?.totalRows);
702
732
  setPaginationState((state) =>
703
733
  state.pageIndex === 0 ? state : { ...state, pageIndex: 0 },
704
734
  );
705
- }, [data?.totalRows]);
735
+ }
706
736
 
707
737
  // Column summaries
708
738
  const { data: columnSummaries, error: columnSummariesError } = useAsyncData<
@@ -775,13 +805,16 @@ export const LoadingDataTableComponent = memo(
775
805
  rawData={data?.rawRows}
776
806
  columnSummaries={columnSummaries}
777
807
  sorting={sorting}
778
- setSorting={setSorting}
808
+ setSorting={handleSortingChange}
779
809
  searchQuery={searchQuery}
780
- setSearchQuery={setSearchQuery}
810
+ setSearchQuery={handleSearchQueryChange}
781
811
  filters={filters}
782
- setFilters={setFilters}
812
+ setFilters={handleFiltersChange}
783
813
  reloading={isFetching && !isPending}
784
814
  totalRows={data?.totalRows ?? props.totalRows}
815
+ hasNextPage={
816
+ props.lazy ? !error && (data?.hasNextPage ?? false) : undefined
817
+ }
785
818
  sizeBytes={sizeBytes}
786
819
  sizeBytesIsLoading={sizeBytesIsLoading}
787
820
  paginationState={paginationState}
@@ -831,11 +864,12 @@ const DataTableComponent = ({
831
864
  data,
832
865
  rawData,
833
866
  totalRows,
867
+ hasNextPage,
834
868
  sizeBytes,
835
869
  sizeBytesIsLoading,
836
870
  maxColumns,
837
871
  pagination,
838
- selection,
872
+ selection: requestedSelection,
839
873
  value,
840
874
  showFilters,
841
875
  showDownload,
@@ -843,7 +877,7 @@ const DataTableComponent = ({
843
877
  showColumnExplorer,
844
878
  showRowExplorer,
845
879
  showChartBuilder,
846
- showDataTypes,
880
+ showDataTypes: requestedShowDataTypes,
847
881
  rowHeaders,
848
882
  fieldTypes,
849
883
  paginationState,
@@ -916,16 +950,16 @@ const DataTableComponent = ({
916
950
  }
917
951
  const fieldTypesWithoutExternalTypes = toFieldTypes(fieldTypes);
918
952
 
919
- return new ColumnChartSpecModel(
920
- columnSummaries.data || [],
921
- fieldTypesWithoutExternalTypes,
922
- columnSummaries.stats,
923
- columnSummaries.bin_values,
924
- columnSummaries.value_counts,
925
- {
953
+ return new ColumnChartSpecModel({
954
+ data: columnSummaries.data || [],
955
+ fieldTypes: fieldTypesWithoutExternalTypes,
956
+ stats: columnSummaries.stats,
957
+ binValues: columnSummaries.bin_values,
958
+ valueCounts: columnSummaries.value_counts,
959
+ opts: {
926
960
  includeCharts: columnSummaries.show_charts,
927
961
  },
928
- );
962
+ });
929
963
  }, [fieldTypes, columnSummaries]);
930
964
 
931
965
  const rowHeaderNames = new Set(rowHeaders.map(([name]) => name));
@@ -975,15 +1009,11 @@ const DataTableComponent = ({
975
1009
  const fractionDigitsByColumn = useDeepCompareMemoize(computedFractionDigits);
976
1010
  const shownColumns = memoizedClampedFieldTypes.length;
977
1011
 
978
- // If the field types are not set, we don't show them
979
- if (!fieldTypes) {
980
- showDataTypes = false;
981
- }
1012
+ // Only show data types when they are explicitly supplied.
1013
+ const showDataTypes = Boolean(fieldTypes && requestedShowDataTypes);
982
1014
 
983
1015
  // Row/cell selection writes back to the kernel, absent in static exports.
984
- if (isStatic) {
985
- selection = null;
986
- }
1016
+ const selection = isStatic ? null : requestedSelection;
987
1017
 
988
1018
  const columns = useMemo(
989
1019
  () =>
@@ -1134,7 +1164,8 @@ const DataTableComponent = ({
1134
1164
  <>
1135
1165
  {/* When the totalRows is "too_many" and the pageSize is the same as the
1136
1166
  * number of rows, we are likely displaying all the data (could be more, but we don't know the total). */}
1137
- {totalRows === TOO_MANY_ROWS &&
1167
+ {hasNextPage === undefined &&
1168
+ totalRows === TOO_MANY_ROWS &&
1138
1169
  paginationState.pageSize === data.length && (
1139
1170
  <Banner className="mb-1 rounded">
1140
1171
  Previewing the first {paginationState.pageSize} rows.
@@ -1172,6 +1203,7 @@ const DataTableComponent = ({
1172
1203
  maxHeight={maxHeight}
1173
1204
  sorting={sorting}
1174
1205
  totalRows={totalRows}
1206
+ hasNextPage={hasNextPage}
1175
1207
  sizeBytes={sizeBytes}
1176
1208
  sizeBytesIsLoading={sizeBytesIsLoading}
1177
1209
  totalColumns={totalColumns}