@marimo-team/islands 0.25.3-dev141 → 0.25.3-dev142

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 (472) hide show
  1. package/dist/{ConnectedDataExplorerComponent-CNSxdKyJ.js → ConnectedDataExplorerComponent-BAiSVaHj.js} +17 -15
  2. package/dist/{ErrorBoundary-Cg59O3xL.js → ErrorBoundary-DxdeETpm.js} +16 -16
  3. package/dist/{abnfDiagram-VCTEODGH-DQDjNXt3.js → abnfDiagram-VCTEODGH-BBHN92-F.js} +2 -2
  4. package/dist/any-language-editor-3E24SnCV.js +75 -0
  5. package/dist/{architectureDiagram-5GKGNRK7-Nmls7Wxn.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-BgQNn4dS.js +1 -0
  8. package/dist/assets/worker-FKe3RHT4.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-BDpymeoe.js → chat-ui-CvB8Map3.js} +1195 -1195
  12. package/dist/{check-EDwSwP89.js → check-SpOMoCLP.js} +1 -1
  13. package/dist/{chunk-IMKFNOWR-CoIFEo-1.js → chunk-IMKFNOWR-hJXey5p2.js} +1 -1
  14. package/dist/{chunk-SHT3W25Y-lHiGx3ZC.js → chunk-SHT3W25Y-JMzTmmVW.js} +1 -1
  15. package/dist/{chunk-SVP7TREG-BvsHIrY_.js → chunk-SVP7TREG-BIyoHTXi.js} +1 -1
  16. package/dist/{chunk-TICWLB2K-BC8YIm_n.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-Bt98lkkv.js → classDiagram-ZZMXUADV-DB1kOPS6.js} +2 -2
  19. package/dist/{classDiagram-v2-VYDZK3BY-D9bfbcC3.js → classDiagram-v2-VYDZK3BY-CPaFL0OB.js} +2 -2
  20. package/dist/{code-visibility-Da3iitn_.js → code-visibility-CO8abjLo.js} +17 -17
  21. package/dist/{common-C6FHcu5p.js → common-CbFJQhaI.js} +6341 -5870
  22. package/dist/{copy-icon-Ft7t9bqz.js → copy-icon-C1trdvVt.js} +3 -3
  23. package/dist/{cynefinDiagram-5FMLGOSQ-CgWra7wt.js → cynefinDiagram-5FMLGOSQ-CF93uliH.js} +1 -1
  24. package/dist/{dagre-GXQ25YYZ-CFZ8-35c.js → dagre-GXQ25YYZ-BBB-_p9D.js} +1 -1
  25. package/dist/{data-grid-overlay-editor-mJt3Pg4S.js → data-grid-overlay-editor-DBjVUS2K.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-lCNQrW7d.js → diagram-S7CK7UJ4-BynCnbyt.js} +1 -1
  28. package/dist/{diagram-UQ7AKVKN-CQPQ6pP_.js → diagram-UQ7AKVKN-svOxikMa.js} +1 -1
  29. package/dist/{diagram-VX7I27RA-Cf3b0llz.js → diagram-VX7I27RA-BVQSnPx0.js} +1 -1
  30. package/dist/{diagram-Z3DM3KII-Bt4QLrpn.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-GWcGCcxE.js} +1277 -1229
  34. package/dist/{ebnfDiagram-PWID7BFC-D3NAyPFY.js → ebnfDiagram-PWID7BFC-C9tq9-Qo.js} +2 -2
  35. package/dist/{erDiagram-RLTQ6QDP-Tlu09_Nm.js → erDiagram-RLTQ6QDP-BiaeB7Fi.js} +1 -1
  36. package/dist/{error-banner-Da600cwz.js → error-banner-D40pmfQQ.js} +86 -81
  37. package/dist/{cells-C8iCYvLU.js → error-entries-CmFDQ9CA.js} +1718 -1621
  38. package/dist/{feedback-button-DaPwHIdM.js → feedback-button-CWQoIUKQ.js} +122 -123
  39. package/dist/{flowDiagram-HODETNUW-CAYbpJmV.js → flowDiagram-HODETNUW-f-sLm87k.js} +2 -2
  40. package/dist/{formats-BGh2thRc.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-Ctz2mH6k.js → glide-data-editor-BxT8eGip.js} +370 -368
  43. package/dist/{glide-utils-DJyqh0EM.js → glide-utils-Dz_F4pAY.js} +16 -16
  44. package/dist/{globals-EpFoRj_E.js → globals-f0UxWZkr.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-CZY1pIZH.js → html-to-image-BrC8OJcj.js} +634 -597
  47. package/dist/{infoDiagram-27XIBGKW-XrDxhr0x.js → infoDiagram-27XIBGKW-BHrWywBe.js} +1 -1
  48. package/dist/{ishikawaDiagram-5VMMS53U-CD1muzov.js → ishikawaDiagram-5VMMS53U-Da4s0Reg.js} +1 -1
  49. package/dist/{kanban-definition-UXKFOSKX-Dg9W142_.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 +7548 -7226
  54. package/dist/{mermaid-CiAXjLpA.js → mermaid-ClwTt7OD.js} +38 -38
  55. package/dist/mermaid-HWGCJPDP-Du3-HPQA.js +2 -0
  56. package/dist/{mindmap-definition-YA3MSWOX-DgCidgYJ.js → mindmap-definition-YA3MSWOX-D60oMZtT.js} +1 -1
  57. package/dist/{number-overlay-editor-CtxrummL.js → number-overlay-editor-Dso2H7DV.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-coOYYKB6.js → pegDiagram-XKGWAZYB-B9wFpMga.js} +2 -2
  61. package/dist/{pieDiagram-E7YTZNPT-BnXTc3bj.js → pieDiagram-E7YTZNPT-BZwvMuBT.js} +1 -1
  62. package/dist/precisionRound-DhAzeRcm.js +49 -0
  63. package/dist/{process-output-e4jj9Ijt.js → process-output-Bw-vzyoy.js} +4 -4
  64. package/dist/{quadrantDiagram-AXDQQJYC-Ccw0xMjN.js → quadrantDiagram-AXDQQJYC-Blrm7V7_.js} +1 -1
  65. package/dist/{railroadDiagram-O6MQD6OU-B28-u83n.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-CdYCTMu0.js → requirementDiagram-BXWQKSXE-CL5dYARV.js} +1 -1
  68. package/dist/{reveal-component-DrIXvR_7.js → reveal-component-DMglhlng.js} +14 -14
  69. package/dist/{select-BButFi-d.js → select-CkF_GBxO.js} +537 -458
  70. package/dist/{sequenceDiagram-WJ2MYXX4-CM3aRAe_.js → sequenceDiagram-WJ2MYXX4-BSUCRd2x.js} +1 -1
  71. package/dist/{spec-BwarBpUJ.js → spec-DiSh_qJC.js} +41 -11
  72. package/dist/{stateDiagram-D77RDMKH-DOZUMVj8.js → stateDiagram-D77RDMKH-B-1Hvyb0.js} +2 -2
  73. package/dist/{stateDiagram-v2-MP3YSRHH-CISYxma6.js → stateDiagram-v2-MP3YSRHH--HtiR4yB.js} +2 -2
  74. package/dist/style.css +1 -1
  75. package/dist/{swimlanes-42K2YHIH-BkSppxp8.js → swimlanes-42K2YHIH-Bdfhrd-2.js} +1 -1
  76. package/dist/{swimlanesDiagram-VR7AAH4N-c8G5pGxC.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-CaMQayqK.js → timeline-definition-24CTP7MA-Cs4EfFD3.js} +1 -1
  80. package/dist/{toDate-BNJf01u1.js → toDate-DGvRHwTk.js} +57 -54
  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-DG29TAB7.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-CF8pfaK3.js → vennDiagram-4TSXK5OY-DRu0vRjO.js} +1 -1
  94. package/dist/{wardleyDiagram-VM6X3IG4-dGPu39d-.js → wardleyDiagram-VM6X3IG4-DzbDxwDY.js} +1 -1
  95. package/dist/{xychartDiagram-S5SC5T6Z-CP6-9q56.js → xychartDiagram-S5SC5T6Z-CLWj6ZFQ.js} +2 -2
  96. package/package.json +15 -12
  97. package/src/__tests__/fixtures/{pair-preview.ts → pair-config.ts} +3 -2
  98. package/src/__tests__/host-imports.test.ts +101 -0
  99. package/src/__tests__/module-entry.test.ts +32 -0
  100. package/src/__tests__/mount.test.ts +8 -8
  101. package/src/components/ai/__tests__/ai-model-dropdown.test.tsx +63 -0
  102. package/src/components/ai/ai-model-dropdown.tsx +11 -6
  103. package/src/components/app-config/ai-config.tsx +7 -2
  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/TableTopBar.tsx +2 -0
  113. package/src/components/data-table/__tests__/chart-spec-model.test.ts +195 -195
  114. package/src/components/data-table/__tests__/column-explorer.test.tsx +27 -1
  115. package/src/components/data-table/__tests__/columns.test.tsx +105 -0
  116. package/src/components/data-table/__tests__/context-menu-interaction.test.tsx +130 -0
  117. package/src/components/data-table/__tests__/context-menu.test.ts +176 -0
  118. package/src/components/data-table/__tests__/data-table.test.tsx +1 -0
  119. package/src/components/data-table/__tests__/date-filter-inputs.test.tsx +24 -1
  120. package/src/components/data-table/__tests__/date-popover.test.tsx +72 -0
  121. package/src/components/data-table/__tests__/export-actions.test.tsx +434 -6
  122. package/src/components/data-table/__tests__/keyboard-selection.test.tsx +188 -0
  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 +161 -46
  140. package/src/components/data-table/data-table.tsx +13 -1
  141. package/src/components/data-table/date-filter-inputs.tsx +15 -42
  142. package/src/components/data-table/date-popover.tsx +6 -4
  143. package/src/components/data-table/export-actions.tsx +267 -163
  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/schemas.ts +14 -36
  151. package/src/components/data-table/search-highlight.tsx +79 -0
  152. package/src/components/data-table/types.ts +1 -0
  153. package/src/components/data-table/url-detector.tsx +49 -9
  154. package/src/components/data-table/utils.ts +18 -0
  155. package/src/components/datasources/__tests__/column-actions.test.tsx +97 -0
  156. package/src/components/datasources/__tests__/column-preview.test.tsx +98 -5
  157. package/src/components/datasources/__tests__/datasources-interactions.test.tsx +181 -0
  158. package/src/components/datasources/__tests__/table-list.test.tsx +118 -0
  159. package/src/components/datasources/__tests__/utils.test.ts +57 -6
  160. package/src/components/datasources/column-preview.tsx +13 -4
  161. package/src/components/datasources/datasources.tsx +51 -39
  162. package/src/components/datasources/utils.ts +32 -23
  163. package/src/components/debugger/debugger-code.tsx +5 -0
  164. package/src/components/dependency-graph/dependency-graph-tree.tsx +8 -8
  165. package/src/components/dependency-graph/elements.ts +24 -21
  166. package/src/components/dependency-graph/panels.tsx +2 -1
  167. package/src/components/dependency-graph/utils/__tests__/layout.test.ts +18 -11
  168. package/src/components/editor/Output.tsx +9 -2
  169. package/src/components/editor/SortableCell.tsx +1 -3
  170. package/src/components/editor/__tests__/dynamic-favicon.test.tsx +111 -18
  171. package/src/components/editor/__tests__/package-alert.test.tsx +22 -1
  172. package/src/components/editor/actions/__tests__/pair-with-agent-commands.test.ts +80 -252
  173. package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +210 -34
  174. package/src/components/editor/actions/pair-with-agent-commands.ts +42 -51
  175. package/src/components/editor/actions/pair-with-agent-modal.tsx +100 -37
  176. package/src/components/editor/ai/add-cell-with-ai.tsx +3 -3
  177. package/src/components/editor/ai/ai-completion-editor.tsx +1 -0
  178. package/src/components/editor/alerts/startup-logs-alert.tsx +1 -0
  179. package/src/components/editor/cell/CreateCellButton.tsx +8 -10
  180. package/src/components/editor/cell/DeleteButton.tsx +1 -0
  181. package/src/components/editor/cell/__tests__/useShouldShowInterrupt.test.ts +39 -0
  182. package/src/components/editor/cell/code/__tests__/language-toggle.test.tsx +54 -0
  183. package/src/components/editor/cell/code/cell-editor.tsx +1 -1
  184. package/src/components/editor/cell/code/language-toggle.tsx +8 -7
  185. package/src/components/editor/cell/collapse.tsx +5 -3
  186. package/src/components/editor/cell/useShouldShowInterrupt.ts +2 -12
  187. package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +30 -1
  188. package/src/components/editor/chrome/panels/cache-panel.tsx +1 -0
  189. package/src/components/editor/chrome/panels/error-panel.tsx +1 -0
  190. package/src/components/editor/chrome/panels/outline/__tests__/outline-keyboard.test.tsx +84 -0
  191. package/src/components/editor/chrome/panels/outline/floating-outline.tsx +39 -14
  192. package/src/components/editor/chrome/panels/outline/useActiveOutline.tsx +3 -2
  193. package/src/components/editor/chrome/panels/packages-panel.tsx +4 -7
  194. package/src/components/editor/chrome/panels/secrets-panel.tsx +1 -0
  195. package/src/components/editor/chrome/panels/snippets-panel.tsx +5 -1
  196. package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +366 -0
  197. package/src/components/editor/chrome/wrapper/__tests__/pending-ai-cells.test.tsx +58 -1
  198. package/src/components/editor/chrome/wrapper/footer-item.tsx +32 -24
  199. package/src/components/editor/chrome/wrapper/footer-items/attachment-status.tsx +237 -0
  200. package/src/components/editor/chrome/wrapper/footer-items/runtime-settings.tsx +1 -7
  201. package/src/components/editor/chrome/wrapper/footer.tsx +2 -0
  202. package/src/components/editor/chrome/wrapper/pending-ai-cells.tsx +12 -2
  203. package/src/components/editor/chrome/wrapper/storage.ts +1 -1
  204. package/src/components/editor/connections/components.tsx +1 -1
  205. package/src/components/editor/controls/__tests__/command-palette.test.tsx +53 -0
  206. package/src/components/editor/controls/__tests__/keyboard-shortcuts.test.tsx +61 -0
  207. package/src/components/editor/controls/command-palette.tsx +22 -10
  208. package/src/components/editor/controls/keyboard-shortcuts.tsx +14 -5
  209. package/src/components/editor/dynamic-favicon.tsx +21 -40
  210. package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +537 -0
  211. package/src/components/editor/errors/send-error-report-button.tsx +69 -0
  212. package/src/components/editor/file-tree/__tests__/file-explorer-interactions.test.tsx +6 -0
  213. package/src/components/editor/file-tree/__tests__/file-explorer-navigation.test.tsx +47 -0
  214. package/src/components/editor/file-tree/__tests__/use-file-search.test.tsx +91 -0
  215. package/src/components/editor/file-tree/file-explorer.tsx +45 -8
  216. package/src/components/editor/file-tree/file-name-input.tsx +1 -0
  217. package/src/components/editor/file-tree/file-viewer.tsx +4 -2
  218. package/src/components/editor/file-tree/use-file-search.ts +35 -21
  219. package/src/components/editor/header/status.tsx +28 -5
  220. package/src/components/editor/links/__tests__/cell-link.test.tsx +146 -0
  221. package/src/components/editor/links/cell-link.tsx +14 -5
  222. package/src/components/editor/navigation/focus-utils.ts +4 -4
  223. package/src/components/editor/notebook-banner.tsx +1 -0
  224. package/src/components/editor/notebook-cell.tsx +11 -2
  225. package/src/components/editor/output/MarimoErrorOutput.tsx +21 -5
  226. package/src/components/editor/output/MarimoTracebackOutput.tsx +54 -37
  227. package/src/components/editor/output/__tests__/ansi-reduce.test.ts +18 -0
  228. package/src/components/editor/output/__tests__/traceback.test.tsx +27 -1
  229. package/src/components/editor/output/ansi-reduce.ts +36 -17
  230. package/src/components/editor/output/console/ConsoleOutput.tsx +19 -25
  231. package/src/components/editor/package-alert.tsx +14 -7
  232. package/src/components/editor/renderers/grid-layout/grid-layout.tsx +12 -6
  233. package/src/components/editor/renderers/vertical-layout/sidebar/toggle.tsx +7 -1
  234. package/src/components/home/components.tsx +17 -31
  235. package/src/components/icons/__tests__/large-spinner.test.tsx +39 -0
  236. package/src/components/icons/large-spinner.tsx +3 -14
  237. package/src/components/icons/spinner.tsx +1 -1
  238. package/src/components/mcp/mcp-status-indicator.tsx +1 -1
  239. package/src/components/pages/__tests__/home-page.test.tsx +116 -0
  240. package/src/components/pages/home-page.tsx +164 -91
  241. package/src/components/scratchpad/scratchpad.test.tsx +95 -0
  242. package/src/components/scratchpad/scratchpad.tsx +16 -3
  243. package/src/components/slides/reveal-component.tsx +4 -2
  244. package/src/components/slides/slide-cell-view.tsx +1 -1
  245. package/src/components/slides/slide-form.tsx +1 -0
  246. package/src/components/tracing/tracing-keyboard.test.tsx +85 -0
  247. package/src/components/tracing/tracing.tsx +45 -41
  248. package/src/components/ui/__tests__/draggable-popover.test.tsx +24 -0
  249. package/src/components/ui/__tests__/range-slider.test.tsx +165 -0
  250. package/src/components/ui/alert-dialog.tsx +1 -1
  251. package/src/components/ui/draggable-popover.tsx +24 -2
  252. package/src/components/ui/range-slider.tsx +22 -9
  253. package/src/core/ai/context/providers/datasource.ts +6 -2
  254. package/src/core/ai/tools/base.ts +16 -7
  255. package/src/core/ai/tools/edit-notebook-tool.ts +47 -45
  256. package/src/core/ai/tools/registry.ts +13 -8
  257. package/src/core/ai/tools/sample-tool.ts +6 -7
  258. package/src/core/attachments/__tests__/toast.test.tsx +58 -0
  259. package/src/core/attachments/state.ts +18 -0
  260. package/src/core/attachments/toast.ts +15 -0
  261. package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +4 -0
  262. package/src/core/cells/__tests__/cell.test.ts +39 -0
  263. package/src/core/cells/__tests__/cells.test.ts +55 -1
  264. package/src/core/cells/__tests__/collapseConsoleOutputs.test.ts +54 -0
  265. package/src/core/cells/__tests__/error-report.test.ts +139 -0
  266. package/src/core/cells/__tests__/notebook-outline.test.ts +113 -0
  267. package/src/core/cells/__tests__/session.test.ts +167 -37
  268. package/src/core/cells/cell.ts +14 -3
  269. package/src/core/cells/cells.ts +3 -1
  270. package/src/core/cells/error-report.ts +63 -0
  271. package/src/core/cells/pending-delete-service.ts +2 -6
  272. package/src/core/cells/session.ts +29 -2
  273. package/src/core/cells/types.ts +10 -1
  274. package/src/core/codemirror/__tests__/setup.test.ts +29 -29
  275. package/src/core/codemirror/cells/__tests__/extensions.test.ts +15 -0
  276. package/src/core/codemirror/cells/extensions.ts +7 -3
  277. package/src/core/codemirror/keymaps/vim.ts +2 -6
  278. package/src/core/codemirror/language/__tests__/sql.test.ts +22 -20
  279. package/src/core/codemirror/language/extension.ts +8 -8
  280. package/src/core/codemirror/language/languages/markdown.ts +8 -6
  281. package/src/core/codemirror/language/languages/python.ts +16 -10
  282. package/src/core/codemirror/language/languages/sql/sql.ts +9 -7
  283. package/src/core/codemirror/language/panel/panel.tsx +1 -0
  284. package/src/core/codemirror/language/types.ts +7 -7
  285. package/src/core/codemirror/rtc/extension.ts +24 -12
  286. package/src/core/codemirror/rtc/loro/awareness.ts +34 -19
  287. package/src/core/codemirror/rtc/loro/sync.ts +1 -0
  288. package/src/core/codemirror/utils.ts +1 -0
  289. package/src/core/config/pair.ts +5 -3
  290. package/src/core/datasets/__tests__/data-source-discovery.test.ts +4 -4
  291. package/src/core/datasets/data-source-discovery.ts +4 -6
  292. package/src/core/document/__tests__/codemirror.test.ts +485 -0
  293. package/src/core/document/__tests__/draft.test.ts +322 -0
  294. package/src/core/document/__tests__/fixtures.ts +92 -0
  295. package/src/core/document/__tests__/hooks.test.tsx +386 -0
  296. package/src/core/document/__tests__/lifecycle-scenarios.test.ts +61 -0
  297. package/src/core/document/__tests__/model.test.ts +191 -0
  298. package/src/core/document/__tests__/protocol-fixtures.ts +38 -0
  299. package/src/core/document/__tests__/recovery-scenarios.test.ts +230 -0
  300. package/src/core/document/__tests__/scenario-fixture.ts +43 -0
  301. package/src/core/document/__tests__/scenarios.test.ts +205 -0
  302. package/src/core/document/__tests__/state.test.ts +438 -0
  303. package/src/core/document/__tests__/submission.test.ts +203 -0
  304. package/src/core/document/__tests__/test-host.test.ts +92 -0
  305. package/src/core/document/__tests__/test-host.ts +247 -0
  306. package/src/core/document/__tests__/test-notebook.ts +149 -0
  307. package/src/core/document/__tests__/user.ts +61 -0
  308. package/src/core/document/codemirror.ts +311 -0
  309. package/src/core/document/draft.ts +472 -0
  310. package/src/core/document/hooks.tsx +118 -0
  311. package/src/core/document/index.ts +15 -0
  312. package/src/core/document/model.ts +55 -0
  313. package/src/core/document/select.ts +41 -0
  314. package/src/core/document/state.ts +374 -0
  315. package/src/core/document/sync.ts +369 -0
  316. package/src/core/edit-app.tsx +2 -2
  317. package/src/core/host/AGENTS.md +12 -0
  318. package/src/core/host/__tests__/attachments.test.ts +56 -0
  319. package/src/core/host/__tests__/document.test.ts +126 -0
  320. package/src/core/host/__tests__/fixtures.ts +79 -0
  321. package/src/core/host/__tests__/replica.test.ts +550 -0
  322. package/src/core/host/__tests__/static.test.ts +175 -0
  323. package/src/core/host/__tests__/wasm.test.ts +166 -0
  324. package/src/core/host/backend.ts +19 -0
  325. package/src/core/host/connection.ts +42 -0
  326. package/src/core/host/index.ts +14 -0
  327. package/src/core/host/replica.ts +286 -0
  328. package/src/core/host/resources/attachments.ts +22 -0
  329. package/src/core/host/resources/document.ts +101 -0
  330. package/src/core/host/static.ts +77 -0
  331. package/src/core/host/wasm.ts +97 -0
  332. package/src/core/hotkeys/actions.ts +2 -4
  333. package/src/core/islands/__tests__/bootstrap.test.ts +17 -17
  334. package/src/core/islands/__tests__/bridge.test.ts +31 -26
  335. package/src/core/islands/bootstrap.ts +22 -51
  336. package/src/core/islands/bridge.ts +31 -20
  337. package/src/core/islands/components/__tests__/IslandControls.test.tsx +7 -5
  338. package/src/core/islands/worker/__tests__/controller.test.ts +62 -4
  339. package/src/core/islands/worker/controller.ts +4 -10
  340. package/src/core/islands/worker/worker.tsx +33 -33
  341. package/src/core/kernel/__tests__/RuntimeState.test.ts +1 -1
  342. package/src/core/network/connection.ts +4 -2
  343. package/src/core/network/useConnectionNotice.ts +8 -2
  344. package/src/core/run-app.tsx +2 -2
  345. package/src/core/runtime/__tests__/createWsUrl.test.ts +77 -7
  346. package/src/core/runtime/__tests__/runtime.test.ts +33 -15
  347. package/src/core/runtime/runtime.ts +39 -30
  348. package/src/core/saving/save-component.tsx +12 -3
  349. package/src/core/state/__tests__/jotai.test.tsx +108 -0
  350. package/src/core/state/jotai.ts +1 -1
  351. package/src/core/static/__tests__/files.test.ts +38 -10
  352. package/src/core/static/files.ts +2 -0
  353. package/src/core/wasm/__tests__/bridge.test.ts +213 -50
  354. package/src/core/wasm/__tests__/worker.test.ts +187 -0
  355. package/src/core/wasm/bridge.ts +81 -52
  356. package/src/core/wasm/rpc.ts +15 -3
  357. package/src/core/wasm/store.ts +3 -3
  358. package/src/core/wasm/worker/bootstrap.ts +55 -51
  359. package/src/core/wasm/worker/getMarimoWheel.ts +1 -1
  360. package/src/core/wasm/worker/save-worker.ts +9 -10
  361. package/src/core/wasm/worker/types.ts +29 -18
  362. package/src/core/wasm/worker/worker.ts +97 -44
  363. package/src/core/websocket/__tests__/backend.test.ts +235 -0
  364. package/src/core/websocket/__tests__/{useMarimoKernelConnection.test.ts → connection-state.test.ts} +15 -29
  365. package/src/core/websocket/__tests__/useNotebookReplica.hook.test.tsx +141 -0
  366. package/src/core/websocket/__tests__/{useMarimoKernelConnection.hook.test.tsx → useNotebookSession.hook.test.tsx} +197 -184
  367. package/src/core/websocket/backend.ts +129 -0
  368. package/src/core/websocket/connection-state.ts +60 -0
  369. package/src/core/websocket/state.ts +7 -0
  370. package/src/core/websocket/transports/__tests__/notebook.test.ts +405 -0
  371. package/src/core/websocket/transports/notebook.ts +159 -0
  372. package/src/core/websocket/types.ts +9 -39
  373. package/src/core/websocket/useNotebookReplica.ts +69 -0
  374. package/src/core/websocket/{useMarimoKernelConnection.tsx → useNotebookSession.tsx} +63 -228
  375. package/src/css/globals.css +5 -0
  376. package/src/hooks/__tests__/useAsyncData.test.tsx +148 -0
  377. package/src/hooks/__tests__/useDebounce.test.tsx +51 -0
  378. package/src/hooks/__tests__/useDeepCompareMemoize.test.tsx +39 -0
  379. package/src/hooks/__tests__/useDelayElapsed.test.tsx +28 -0
  380. package/src/hooks/__tests__/useInternalStateWithSync.test.tsx +20 -0
  381. package/src/hooks/__tests__/useScript.test.tsx +77 -0
  382. package/src/hooks/__tests__/useStateForKey.test.tsx +257 -0
  383. package/src/hooks/useAsyncData.ts +3 -3
  384. package/src/hooks/useDataSourceDiscovery.ts +6 -4
  385. package/src/hooks/useDebounce.ts +6 -10
  386. package/src/hooks/useDeepCompareMemoize.ts +1 -1
  387. package/src/hooks/useDelayElapsed.ts +14 -10
  388. package/src/hooks/useElapsedTime.ts +3 -3
  389. package/src/hooks/useEventListener.ts +1 -2
  390. package/src/hooks/useInputHistory.ts +4 -7
  391. package/src/hooks/useInternalStateWithSync.ts +6 -6
  392. package/src/hooks/useResizeObserver.ts +4 -7
  393. package/src/hooks/useScript.ts +67 -70
  394. package/src/hooks/useStateForKey.ts +93 -0
  395. package/src/mount.tsx +3 -3
  396. package/src/plugins/__tests__/plugin-object-schemas.test.ts +104 -0
  397. package/src/plugins/core/registerReactComponent.tsx +1 -1
  398. package/src/plugins/impl/CodeEditorPlugin.tsx +1 -1
  399. package/src/plugins/impl/DataEditorPlugin.tsx +1 -1
  400. package/src/plugins/impl/DataTablePlugin.tsx +29 -33
  401. package/src/plugins/impl/DropdownPlugin.tsx +2 -6
  402. package/src/plugins/impl/FileBrowserPlugin.tsx +4 -16
  403. package/src/plugins/impl/FormPlugin.tsx +1 -1
  404. package/src/plugins/impl/RangeSliderPlugin.tsx +16 -2
  405. package/src/plugins/impl/RefreshPlugin.tsx +1 -0
  406. package/src/plugins/impl/SliderPlugin.tsx +6 -8
  407. package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +201 -1
  408. package/src/plugins/impl/__tests__/FileBrowserPlugin.test.tsx +45 -0
  409. package/src/plugins/impl/__tests__/RangeSliderPlugin.test.tsx +109 -0
  410. package/src/plugins/impl/__tests__/SliderPlugin.test.tsx +24 -3
  411. package/src/plugins/impl/anywidget/__tests__/registry.test.ts +2 -0
  412. package/src/plugins/impl/anywidget/__tests__/widget-binding.test.ts +241 -2
  413. package/src/plugins/impl/anywidget/runtime.ts +21 -9
  414. package/src/plugins/impl/anywidget/widget-binding.ts +160 -82
  415. package/src/plugins/impl/chat/__tests__/chat-prompts.test.tsx +93 -0
  416. package/src/plugins/impl/chat/chat-ui.tsx +14 -11
  417. package/src/plugins/impl/code/any-language-editor.tsx +2 -2
  418. package/src/plugins/impl/data-editor/glide-data-editor.tsx +8 -5
  419. package/src/plugins/impl/data-editor/glide-utils.ts +3 -1
  420. package/src/plugins/impl/data-explorer/ConnectedDataExplorerComponent.tsx +1 -1
  421. package/src/plugins/impl/data-explorer/components/column-summary.tsx +4 -2
  422. package/src/plugins/impl/data-explorer/state/reducer.ts +1 -1
  423. package/src/plugins/impl/data-frames/DataFramePlugin.tsx +1 -2
  424. package/src/plugins/impl/data-frames/__tests__/sidebar.test.tsx +160 -0
  425. package/src/plugins/impl/data-frames/forms/__tests__/__snapshots__/form.test.tsx.snap +66 -66
  426. package/src/plugins/impl/data-frames/panel.tsx +43 -16
  427. package/src/plugins/impl/mpl-interactive/MplInteractivePlugin.tsx +5 -3
  428. package/src/plugins/impl/panel/PanelPlugin.tsx +16 -17
  429. package/src/plugins/impl/panel/__tests__/utils.test.ts +39 -1
  430. package/src/plugins/impl/panel/utils.ts +17 -0
  431. package/src/plugins/impl/plotly/PlotlyPlugin.tsx +2 -5
  432. package/src/plugins/impl/plotly/selection.ts +25 -19
  433. package/src/plugins/impl/vega/VegaPlugin.tsx +2 -3
  434. package/src/plugins/impl/vega/make-selectable.ts +4 -7
  435. package/src/plugins/layout/RoutesPlugin.tsx +1 -1
  436. package/src/stories/__tests__/editor-story.test.tsx +33 -0
  437. package/src/stories/button.stories.tsx +1 -0
  438. package/src/stories/editor.stories.tsx +24 -17
  439. package/src/stories/form-wrapper.stories.tsx +2 -2
  440. package/src/stories/select.stories.tsx +2 -2
  441. package/src/stories/switchable-multi-select.stories.tsx +1 -1
  442. package/src/utils/__tests__/arrays.test.ts +35 -0
  443. package/src/utils/__tests__/batch-requests.test.ts +3 -3
  444. package/src/utils/__tests__/dates.test.ts +116 -0
  445. package/src/utils/__tests__/numbers.test.ts +105 -1
  446. package/src/utils/__tests__/objects.test.ts +21 -0
  447. package/src/utils/arrays.ts +10 -0
  448. package/src/utils/blob.ts +1 -1
  449. package/src/utils/dates.ts +94 -0
  450. package/src/utils/numbers.ts +79 -18
  451. package/src/utils/objects.ts +3 -7
  452. package/src/utils/once.ts +19 -19
  453. package/src/utils/tracer.ts +9 -5
  454. package/src/utils/zod-utils.ts +1 -3
  455. package/dist/any-language-editor-B9REGlD9.js +0 -76
  456. package/dist/assets/__vite-browser-external-BXYkiFsk.js +0 -1
  457. package/dist/assets/worker-HPY9fbhz.js +0 -114
  458. package/dist/error-entries-CzoYkIQZ.js +0 -56
  459. package/dist/mermaid-HWGCJPDP-ByCsZnYF.js +0 -2
  460. package/dist/precisionRound-DMIVQrt2.js +0 -97
  461. package/src/core/state/__tests__/jotai.test.ts +0 -25
  462. package/src/core/wasm/worker/message-buffer.ts +0 -33
  463. package/src/core/websocket/__tests__/useWebSocket.test.ts +0 -49
  464. package/src/core/websocket/transports/__tests__/basic.test.ts +0 -275
  465. package/src/core/websocket/transports/__tests__/sse.test.ts +0 -338
  466. package/src/core/websocket/transports/__tests__/transport.test.ts +0 -308
  467. package/src/core/websocket/transports/__tests__/ws.test.ts +0 -125
  468. package/src/core/websocket/transports/basic.ts +0 -102
  469. package/src/core/websocket/transports/sse.ts +0 -308
  470. package/src/core/websocket/transports/transport.ts +0 -61
  471. package/src/core/websocket/transports/ws.ts +0 -98
  472. package/src/core/websocket/useWebSocket.tsx +0 -88
@@ -258,7 +258,7 @@ export const DataTablePlugin = createPlugin<S>("marimo-table")
258
258
  z.array(z.object({ rowId: z.string(), columnName: z.string() })),
259
259
  ]),
260
260
  label: z.string().nullable(),
261
- data: z.union([z.string(), z.array(z.object({}).passthrough())]),
261
+ data: z.union([z.string(), z.array(z.looseObject({}))]),
262
262
  rawData: z.union([z.string(), z.array(z.looseObject({}))]).nullish(),
263
263
  totalRows: z.union([z.number(), z.literal(TOO_MANY_ROWS)]),
264
264
  pagination: z.boolean().default(false),
@@ -296,7 +296,7 @@ export const DataTablePlugin = createPlugin<S>("marimo-table")
296
296
  hasStableRowId: z.boolean().default(false),
297
297
  maxHeight: z.number().optional(),
298
298
  cellStyles: z
299
- .record(z.string(), z.record(z.string(), z.object({}).passthrough()))
299
+ .record(z.string(), z.record(z.string(), z.looseObject({})))
300
300
  .optional(),
301
301
  hoverTemplate: z.string().optional(),
302
302
  cellHoverTexts: cellHoverTextSchema,
@@ -341,28 +341,25 @@ export const DataTablePlugin = createPlugin<S>("marimo-table")
341
341
  )
342
342
  .output(
343
343
  z.object({
344
- data: z.union([z.string(), z.array(z.object({}).passthrough())]),
344
+ data: z.union([z.string(), z.array(z.looseObject({}))]),
345
345
  total_rows: z.union([z.number(), z.literal(TOO_MANY_ROWS)]),
346
346
  cell_styles: z
347
- .record(
348
- z.string(),
349
- z.record(z.string(), z.object({}).passthrough()),
350
- )
347
+ .record(z.string(), z.record(z.string(), z.looseObject({})))
351
348
  .nullable(),
352
349
  cell_hover_texts: cellHoverTextSchema.nullable(),
353
350
  raw_data: z.union([z.string(), z.array(z.looseObject({}))]).nullish(),
354
351
  }),
355
352
  ),
356
- get_row_ids: rpc.input(z.object({}).passthrough()).output(
353
+ get_row_ids: rpc.input(z.looseObject({})).output(
357
354
  z.object({
358
355
  row_ids: z.array(z.number()),
359
356
  all_rows: z.boolean(),
360
357
  error: z.string().nullable(),
361
358
  }),
362
359
  ),
363
- get_data_url: rpc.input(z.object({}).passthrough()).output(
360
+ get_data_url: rpc.input(z.looseObject({})).output(
364
361
  z.object({
365
- data_url: z.union([z.string(), z.array(z.object({}).passthrough())]),
362
+ data_url: z.union([z.string(), z.array(z.looseObject({}))]),
366
363
  format: z.enum(["csv", "json", "arrow"]),
367
364
  }),
368
365
  ),
@@ -546,10 +543,12 @@ export const LoadingDataTableComponent = memo(
546
543
  }
547
544
  }, [setValue, filters, searchQuery, sorting, props.hasStableRowId]);
548
545
 
549
- // If pageSize changes, reset pagination state
550
- useEffect(() => {
546
+ const [previousPageSize, setPreviousPageSize] = useState(props.pageSize);
547
+ // Reconcile before requesting data for the updated table configuration.
548
+ if (previousPageSize !== props.pageSize) {
549
+ setPreviousPageSize(props.pageSize);
551
550
  setPaginationState({ pageIndex: 0, pageSize: props.pageSize });
552
- }, [props.pageSize]);
551
+ }
553
552
 
554
553
  // Data loading
555
554
  const { data, error, isPending, isFetching } = useAsyncData<{
@@ -697,12 +696,13 @@ export const LoadingDataTableComponent = memo(
697
696
  [search, sorting, filters, searchQuery],
698
697
  );
699
698
 
700
- // If total rows change, reset pageIndex
701
- useEffect(() => {
699
+ const [previousTotalRows, setPreviousTotalRows] = useState(data?.totalRows);
700
+ if (previousTotalRows !== data?.totalRows) {
701
+ setPreviousTotalRows(data?.totalRows);
702
702
  setPaginationState((state) =>
703
703
  state.pageIndex === 0 ? state : { ...state, pageIndex: 0 },
704
704
  );
705
- }, [data?.totalRows]);
705
+ }
706
706
 
707
707
  // Column summaries
708
708
  const { data: columnSummaries, error: columnSummariesError } = useAsyncData<
@@ -835,7 +835,7 @@ const DataTableComponent = ({
835
835
  sizeBytesIsLoading,
836
836
  maxColumns,
837
837
  pagination,
838
- selection,
838
+ selection: requestedSelection,
839
839
  value,
840
840
  showFilters,
841
841
  showDownload,
@@ -843,7 +843,7 @@ const DataTableComponent = ({
843
843
  showColumnExplorer,
844
844
  showRowExplorer,
845
845
  showChartBuilder,
846
- showDataTypes,
846
+ showDataTypes: requestedShowDataTypes,
847
847
  rowHeaders,
848
848
  fieldTypes,
849
849
  paginationState,
@@ -916,16 +916,16 @@ const DataTableComponent = ({
916
916
  }
917
917
  const fieldTypesWithoutExternalTypes = toFieldTypes(fieldTypes);
918
918
 
919
- return new ColumnChartSpecModel(
920
- columnSummaries.data || [],
921
- fieldTypesWithoutExternalTypes,
922
- columnSummaries.stats,
923
- columnSummaries.bin_values,
924
- columnSummaries.value_counts,
925
- {
919
+ return new ColumnChartSpecModel({
920
+ data: columnSummaries.data || [],
921
+ fieldTypes: fieldTypesWithoutExternalTypes,
922
+ stats: columnSummaries.stats,
923
+ binValues: columnSummaries.bin_values,
924
+ valueCounts: columnSummaries.value_counts,
925
+ opts: {
926
926
  includeCharts: columnSummaries.show_charts,
927
927
  },
928
- );
928
+ });
929
929
  }, [fieldTypes, columnSummaries]);
930
930
 
931
931
  const rowHeaderNames = new Set(rowHeaders.map(([name]) => name));
@@ -975,15 +975,11 @@ const DataTableComponent = ({
975
975
  const fractionDigitsByColumn = useDeepCompareMemoize(computedFractionDigits);
976
976
  const shownColumns = memoizedClampedFieldTypes.length;
977
977
 
978
- // If the field types are not set, we don't show them
979
- if (!fieldTypes) {
980
- showDataTypes = false;
981
- }
978
+ // Only show data types when they are explicitly supplied.
979
+ const showDataTypes = Boolean(fieldTypes && requestedShowDataTypes);
982
980
 
983
981
  // Row/cell selection writes back to the kernel, absent in static exports.
984
- if (isStatic) {
985
- selection = null;
986
- }
982
+ const selection = isStatic ? null : requestedSelection;
987
983
 
988
984
  const columns = useMemo(
989
985
  () =>
@@ -97,13 +97,9 @@ const Dropdown = (props: DropdownProps): JSX.Element => {
97
97
  id={id}
98
98
  disabled={disabled}
99
99
  >
100
- {allowSelectNone ? (
101
- <option value={EMPTY_VALUE} selected={value.length === 0}>
102
- --
103
- </option>
104
- ) : null}
100
+ {allowSelectNone ? <option value={EMPTY_VALUE}>--</option> : null}
105
101
  {options.map((option) => (
106
- <option value={option} selected={value.includes(option)} key={option}>
102
+ <option value={option} key={option}>
107
103
  {option}
108
104
  </option>
109
105
  ))}
@@ -1,7 +1,7 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
3
  import { type LucideIcon, ChevronRightIcon, CornerLeftUp } from "lucide-react";
4
- import { type JSX, useEffect, useLayoutEffect, useRef, useState } from "react";
4
+ import { type JSX, useLayoutEffect, useRef, useState } from "react";
5
5
  import { z } from "zod";
6
6
  import {
7
7
  FILE_ICON as FILE_TYPE_ICONS,
@@ -16,6 +16,7 @@ import { Table, TableBody, TableCell, TableRow } from "@/components/ui/table";
16
16
  import { toast } from "@/components/ui/use-toast";
17
17
  import { RANDOM_ID_ATTR } from "@/core/dom/ui-element-constants";
18
18
  import { useAsyncData } from "@/hooks/useAsyncData";
19
+ import { useDelayElapsed } from "@/hooks/useDelayElapsed";
19
20
  import { useInternalStateWithSync } from "@/hooks/useInternalStateWithSync";
20
21
  import { cn } from "@/utils/cn";
21
22
  import { type FilePath, PathBuilder, Paths } from "@/utils/paths";
@@ -200,7 +201,6 @@ export const FileBrowser = ({
200
201
  }: FileBrowserProps): JSX.Element | null => {
201
202
  const [path, setPath] = useInternalStateWithSync(initialPath);
202
203
  const [isUpdatingPath, setIsUpdatingPath] = useState(false);
203
- const [showLoadingOverlay, setShowLoadingOverlay] = useState(false);
204
204
  const [activeIndex, setActiveIndex] = useState(0);
205
205
  const rowRefs = useRef<(HTMLTableRowElement | null)[]>([]);
206
206
  const gridRef = useRef<HTMLTableElement | null>(null);
@@ -219,20 +219,8 @@ export const FileBrowser = ({
219
219
  return list_directory({ path: path });
220
220
  }, [path, randomId, list_directory]);
221
221
 
222
- useEffect(() => {
223
- if (!isPending) {
224
- setShowLoadingOverlay(false);
225
- return;
226
- }
227
-
228
- const timeout = window.setTimeout(() => {
229
- setShowLoadingOverlay(true);
230
- }, 200);
231
-
232
- return () => {
233
- window.clearTimeout(timeout);
234
- };
235
- }, [isPending]);
222
+ const loadingDelayElapsed = useDelayElapsed(isPending ? 200 : 0);
223
+ const showLoadingOverlay = isPending && loadingDelayElapsed;
236
224
 
237
225
  // Reset the roving tabindex whenever the listing reloads (a new path or a
238
226
  // same-path refresh) so activeIndex never points past the current rows.
@@ -212,7 +212,7 @@ const Form = ({
212
212
  // wrapped plugin; this buffered value is in contrast to the actual
213
213
  // value of the plugin, which is the value of the wrapped plugin when
214
214
  // the submit button was last clicked/activated.
215
- const [internalValue, setInternalValue] = useState<T>(
215
+ const [internalValue, setInternalValue] = useState<T>(() =>
216
216
  UI_ELEMENT_REGISTRY.lookupValue(elementId),
217
217
  );
218
218
 
@@ -5,7 +5,10 @@ import { type JSX, useEffect, useId, useState } from "react";
5
5
  import { useLocale } from "react-aria";
6
6
  import { z } from "zod";
7
7
  import { cn } from "@/utils/cn";
8
- import { prettyScientificNumber } from "@/utils/numbers";
8
+ import {
9
+ fractionDigitsForSlider,
10
+ prettyScientificNumber,
11
+ } from "@/utils/numbers";
9
12
  import { RangeSlider } from "../../components/ui/range-slider";
10
13
  import type { IPlugin, IPluginProps, Setter } from "../types";
11
14
  import { Labeled } from "./common/labeled";
@@ -87,6 +90,12 @@ const RangeSliderComponent = ({
87
90
 
88
91
  // Hold internal value
89
92
  const [internalValue, setInternalValue] = useState(value);
93
+ const fractionDigits = fractionDigitsForSlider(
94
+ step,
95
+ steps,
96
+ start,
97
+ internalValue.map(valueMap),
98
+ );
90
99
 
91
100
  // Update internal value on prop change
92
101
  useEffect(() => {
@@ -122,6 +131,7 @@ const RangeSliderComponent = ({
122
131
  min={start}
123
132
  max={stop}
124
133
  step={step}
134
+ steps={steps ?? undefined}
125
135
  orientation={orientation}
126
136
  disabled={disabled}
127
137
  // Triggered on all value changes
@@ -155,8 +165,12 @@ const RangeSliderComponent = ({
155
165
  {showValue && (
156
166
  <div className="text-xs text-muted-foreground min-w-[16px]">
157
167
  {`${prettyScientificNumber(valueMap(internalValue[0]), {
168
+ maximumFractionDigits: fractionDigits,
169
+ locale,
170
+ })}, ${prettyScientificNumber(valueMap(internalValue[1]), {
171
+ maximumFractionDigits: fractionDigits,
158
172
  locale,
159
- })}, ${prettyScientificNumber(valueMap(internalValue[1]), { locale })}`}
173
+ })}`}
160
174
  </div>
161
175
  )}
162
176
  </div>
@@ -119,6 +119,7 @@ const RefreshComponent = ({ setValue, data }: IPluginProps<Value, Data>) => {
119
119
  <Labeled label={data.label}>
120
120
  <span className="inline-flex items-center text-secondary-foreground rounded shadow-sm-solid">
121
121
  <Button
122
+ aria-label="Refresh"
122
123
  data-testid="marimo-plugin-refresh-button"
123
124
  variant="secondary"
124
125
  size="icon"
@@ -1,8 +1,9 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
- import { type JSX, useEffect, useId, useMemo, useState } from "react";
2
+ import { type JSX, useId, useMemo } from "react";
3
3
  import { useLocale } from "react-aria";
4
4
  import { z } from "zod";
5
5
  import { NumberField } from "@/components/ui/number-field";
6
+ import { useInternalStateWithSync } from "@/hooks/useInternalStateWithSync";
6
7
  import { cn } from "@/utils/cn";
7
8
  import { clamp } from "@/utils/math";
8
9
  import {
@@ -156,13 +157,10 @@ const SliderComponent = ({
156
157
  const { locale } = useLocale();
157
158
 
158
159
  // Hold internal value
159
- const [internalValue, setInternalValue] = useState(value);
160
- // Update internal value on prop change
161
- useEffect(() => {
162
- // TODO: Replace this synchronization with a controlled-state primitive.
163
- // oxlint-disable-next-line react/no-deriving-state-in-effects -- Synchronize the editable buffer when the controlled value changes.
164
- setInternalValue(value);
165
- }, [value]);
160
+ const [internalValue, setInternalValue] = useInternalStateWithSync(
161
+ value,
162
+ Object.is,
163
+ );
166
164
 
167
165
  const stepsConfig = useMemo(() => computeStepsConfig(steps), [steps]);
168
166
 
@@ -12,11 +12,13 @@ import {
12
12
  waitFor,
13
13
  } from "@testing-library/react";
14
14
  import { Provider } from "jotai";
15
+ import { useState } from "react";
15
16
  import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
16
17
  import { SetupMocks } from "@/__mocks__/common";
17
18
  import type { DownloadAsFunction } from "@/components/data-table/schemas";
18
19
  import type { FieldTypesWithExternalType } from "@/components/data-table/types";
19
20
  import { store } from "@/core/state/jotai";
21
+ import { Deferred } from "@/utils/Deferred";
20
22
  import {
21
23
  type GetDataUrl,
22
24
  type GetRowIds,
@@ -136,6 +138,90 @@ describe("LoadingDataTableComponent", () => {
136
138
  expect(downloadGeoJSON).toHaveBeenCalledTimes(1);
137
139
  });
138
140
 
141
+ it("refreshes export metadata when a dataframe transform changes the data", async () => {
142
+ const host = document.createElement("div");
143
+ const metadataFor = (name: string) => ({
144
+ geometry_columns: [{ name, encoding: "objects", crs: "EPSG:4326" }],
145
+ primary_geometry_column: name,
146
+ default_geometry_column: name,
147
+ formats: {
148
+ parquet: { available: true, reason: null, missing_packages: [] },
149
+ },
150
+ });
151
+ const getExportMetadata = vi
152
+ .fn()
153
+ .mockResolvedValueOnce(metadataFor("before"))
154
+ .mockResolvedValueOnce(metadataFor("after"));
155
+ const view = (data: string) => (
156
+ <Provider store={store}>
157
+ <TooltipProvider>
158
+ <LoadingDataTableComponent
159
+ label={null}
160
+ totalRows={1}
161
+ pagination={false}
162
+ pageSize={10}
163
+ selection={null}
164
+ showDownload={true}
165
+ showFilters={false}
166
+ showColumnSummaries={false}
167
+ showDataTypes={false}
168
+ showPageSizeSelector={false}
169
+ showColumnExplorer={false}
170
+ showRowExplorer={false}
171
+ showChartBuilder={false}
172
+ rowHeaders={[]}
173
+ fieldTypes={undefined}
174
+ totalColumns={1}
175
+ maxColumns="all"
176
+ hasStableRowId={false}
177
+ lazy={false}
178
+ host={host}
179
+ showSearch={false}
180
+ value={[]}
181
+ setValue={vi.fn()}
182
+ data={data}
183
+ search={vi.fn().mockResolvedValue({
184
+ data,
185
+ total_rows: 1,
186
+ cell_styles: null,
187
+ cell_hover_texts: null,
188
+ })}
189
+ download_as={vi.fn()}
190
+ download_geojson={vi.fn()}
191
+ get_export_metadata={getExportMetadata}
192
+ get_column_summaries={vi.fn()}
193
+ get_data_url={vi.fn() as GetDataUrl}
194
+ get_row_ids={vi.fn() as GetRowIds}
195
+ get_size_bytes={vi.fn().mockResolvedValue({ size_bytes: null })}
196
+ />
197
+ </TooltipProvider>
198
+ </Provider>
199
+ );
200
+ const { rerender } = render(
201
+ view(JSON.stringify([{ before: "POINT (1 2)" }])),
202
+ );
203
+ await waitFor(() =>
204
+ expect(
205
+ screen.getByRole("columnheader", { name: "before" }),
206
+ ).toBeInTheDocument(),
207
+ );
208
+ fireEvent.click(screen.getByTestId("export-button"));
209
+ await waitFor(() =>
210
+ expect(screen.getByTestId("export-summary-parquet")).toHaveTextContent(
211
+ "before",
212
+ ),
213
+ );
214
+
215
+ rerender(view(JSON.stringify([{ after: "POINT (3 4)" }])));
216
+
217
+ await waitFor(() =>
218
+ expect(screen.getByTestId("export-summary-parquet")).toHaveTextContent(
219
+ "after",
220
+ ),
221
+ );
222
+ expect(getExportMetadata).toHaveBeenCalledTimes(2);
223
+ });
224
+
139
225
  it("does not log duplicate keys when a pivot becomes empty", async () => {
140
226
  const host = document.createElement("div");
141
227
  const consoleError = vi
@@ -379,7 +465,7 @@ describe("LoadingDataTableComponent", () => {
379
465
  });
380
466
  });
381
467
 
382
- describe("static notebook control suppression", () => {
468
+ describe("table pagination and static notebook control suppression", () => {
383
469
  const fieldTypes: FieldTypesWithExternalType = [
384
470
  ["id", ["integer", "integer"]],
385
471
  ["name", ["string", "string"]],
@@ -454,6 +540,120 @@ describe("static notebook control suppression", () => {
454
540
  mockIsStatic.mockReturnValue(false);
455
541
  });
456
542
 
543
+ it("requests only the first page with the new configured page size", async () => {
544
+ const props = makeProps();
545
+ const view = (pageSize: number) => (
546
+ <Wrapper>
547
+ <LoadingDataTableComponent {...props} pageSize={pageSize} />
548
+ </Wrapper>
549
+ );
550
+ const { rerender } = render(view(PAGE_SIZE));
551
+ await waitFor(() =>
552
+ expect(screen.getByTestId("next-page-button")).toBeEnabled(),
553
+ );
554
+ fireEvent.click(screen.getByTestId("next-page-button"));
555
+ await waitFor(() =>
556
+ expect(props.search).toHaveBeenLastCalledWith(
557
+ expect.objectContaining({ page_number: 1, page_size: PAGE_SIZE }),
558
+ ),
559
+ );
560
+ await waitFor(() =>
561
+ expect(screen.getByTestId("next-page-button")).toBeEnabled(),
562
+ );
563
+ props.search.mockClear();
564
+
565
+ rerender(view(5));
566
+ await waitFor(() => expect(props.search).toHaveBeenCalled());
567
+ expect(
568
+ props.search.mock.calls.every(
569
+ ([request]) => request.page_number === 0 && request.page_size === 5,
570
+ ),
571
+ ).toBe(true);
572
+ });
573
+
574
+ it("preserves a new page size when a pending response changes the row count in the same batch", async () => {
575
+ const props = makeProps();
576
+ const Harness = () => {
577
+ const [pageSize, setPageSize] = useState(PAGE_SIZE);
578
+ return (
579
+ <Wrapper>
580
+ <button type="button" onClick={() => setPageSize(5)}>
581
+ Change configured page size
582
+ </button>
583
+ <LoadingDataTableComponent {...props} pageSize={pageSize} />
584
+ </Wrapper>
585
+ );
586
+ };
587
+ render(<Harness />);
588
+ await waitFor(() =>
589
+ expect(screen.getByTestId("next-page-button")).toBeEnabled(),
590
+ );
591
+ const response = {
592
+ data: firstPage,
593
+ total_rows: 25,
594
+ cell_styles: null,
595
+ cell_hover_texts: null,
596
+ };
597
+ const pendingPage = new Deferred<typeof response>();
598
+ props.search.mockReturnValueOnce(pendingPage.promise);
599
+ fireEvent.click(screen.getByTestId("next-page-button"));
600
+ await waitFor(() =>
601
+ expect(props.search).toHaveBeenLastCalledWith(
602
+ expect.objectContaining({ page_number: 1, page_size: PAGE_SIZE }),
603
+ ),
604
+ );
605
+ props.search.mockClear();
606
+
607
+ await act(async () => {
608
+ pendingPage.resolve(response);
609
+ // Settle the table-loading promises without committing the queued render.
610
+ await new Promise((resolve) => setTimeout(resolve, 0));
611
+ fireEvent.click(
612
+ screen.getByRole("button", { name: "Change configured page size" }),
613
+ );
614
+ });
615
+
616
+ await waitFor(() => expect(props.search).toHaveBeenCalled());
617
+ expect(props.search).toHaveBeenLastCalledWith(
618
+ expect.objectContaining({ page_number: 0, page_size: 5 }),
619
+ );
620
+ });
621
+
622
+ it("returns to the first page when a refreshed result changes the row count", async () => {
623
+ const props = makeProps();
624
+ const view = () => (
625
+ <Wrapper>
626
+ <LoadingDataTableComponent {...props} />
627
+ </Wrapper>
628
+ );
629
+ const { rerender } = render(view());
630
+ await waitFor(() =>
631
+ expect(screen.getByTestId("next-page-button")).toBeEnabled(),
632
+ );
633
+ fireEvent.click(screen.getByTestId("next-page-button"));
634
+ await waitFor(() =>
635
+ expect(props.search).toHaveBeenLastCalledWith(
636
+ expect.objectContaining({ page_number: 1 }),
637
+ ),
638
+ );
639
+ await waitFor(() =>
640
+ expect(screen.getByTestId("next-page-button")).toBeEnabled(),
641
+ );
642
+ props.search = vi.fn().mockResolvedValue({
643
+ data: firstPage,
644
+ total_rows: 25,
645
+ cell_styles: null,
646
+ cell_hover_texts: null,
647
+ });
648
+
649
+ rerender(view());
650
+ await waitFor(() =>
651
+ expect(props.search).toHaveBeenLastCalledWith(
652
+ expect.objectContaining({ page_number: 0, page_size: PAGE_SIZE }),
653
+ ),
654
+ );
655
+ });
656
+
457
657
  it("renders kernel-dependent controls in normal mode", async () => {
458
658
  const props = makeProps();
459
659
  render(
@@ -1,6 +1,7 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
3
  import {
4
+ act,
4
5
  fireEvent,
5
6
  render,
6
7
  screen,
@@ -88,6 +89,50 @@ beforeAll(() => {
88
89
  });
89
90
 
90
91
  describe("FileBrowserPlugin session cache recovery", () => {
92
+ it("delays the initial loading overlay and hides it when listing completes", async () => {
93
+ vi.useFakeTimers();
94
+ try {
95
+ let resolveListing: (value: {
96
+ files: MockFile[];
97
+ total_count: number;
98
+ is_truncated: boolean;
99
+ }) => void = () => undefined;
100
+ const listDirectory = vi.fn(
101
+ () =>
102
+ new Promise((resolve) => {
103
+ resolveListing = resolve;
104
+ }),
105
+ );
106
+ const { unmount } = renderBrowser({ list_directory: listDirectory });
107
+
108
+ await act(async () => {
109
+ vi.advanceTimersByTime(199);
110
+ });
111
+ expect(screen.queryByText("Listing files...")).not.toBeInTheDocument();
112
+ await act(async () => {
113
+ vi.advanceTimersByTime(1);
114
+ });
115
+ expect(screen.getByText("Listing files...")).toBeInTheDocument();
116
+
117
+ await act(async () => {
118
+ resolveListing({
119
+ files: FILES,
120
+ total_count: FILES.length,
121
+ is_truncated: false,
122
+ });
123
+ });
124
+ expect(screen.queryByText("Listing files...")).not.toBeInTheDocument();
125
+ expect(screen.getByText("docs")).toBeInTheDocument();
126
+ await act(async () => {
127
+ vi.advanceTimersByTime(200);
128
+ });
129
+ expect(screen.queryByText("Listing files...")).not.toBeInTheDocument();
130
+ unmount();
131
+ } finally {
132
+ vi.useRealTimers();
133
+ }
134
+ });
135
+
91
136
  it("retries a failed cached listing when the plugin resets", async () => {
92
137
  const host = document.createElement("marimo-file-browser");
93
138
  const wrapper = document.createElement("marimo-ui-element");