@marimo-team/islands 0.25.3-dev2 → 0.25.3-dev200

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 (690) hide show
  1. package/dist/{ConnectedDataExplorerComponent-CNSxdKyJ.js → ConnectedDataExplorerComponent-BsWiIPFk.js} +22 -20
  2. package/dist/{ErrorBoundary-DWEpirf3.js → ErrorBoundary-UWARECs-.js} +19 -19
  3. package/dist/{abnfDiagram-VCTEODGH-DQDjNXt3.js → abnfDiagram-VCTEODGH-I2x4Yu25.js} +2 -2
  4. package/dist/any-language-editor-BVFHDDQ3.js +75 -0
  5. package/dist/{architectureDiagram-5GKGNRK7-Nmls7Wxn.js → architectureDiagram-5GKGNRK7-1bSQMKzK.js} +1 -1
  6. package/dist/{arrays-CN1djC4k.js → arrays-DoQ-AILx.js} +15 -27
  7. package/dist/assets/__vite-browser-external-BCJsx8QS.js +1 -0
  8. package/dist/assets/worker-DodjWT_Z.js +113 -0
  9. package/dist/{button-CvjkwdSk.js → button-BQRdqQi1.js} +199 -192
  10. package/dist/{capabilities-CQ5IjhzL.js → capabilities-C2QUNRXi.js} +2 -2
  11. package/dist/{chat-ui-BDpymeoe.js → chat-ui-DXDoSuS4.js} +667 -667
  12. package/dist/{check-EDwSwP89.js → check-C9Mp9eCK.js} +1 -1
  13. package/dist/{chunk-IMKFNOWR-CoIFEo-1.js → chunk-IMKFNOWR-fsCGAJ9G.js} +1 -1
  14. package/dist/{chunk-SHT3W25Y-lHiGx3ZC.js → chunk-SHT3W25Y-D2STW4hj.js} +1 -1
  15. package/dist/{chunk-SVP7TREG-BvsHIrY_.js → chunk-SVP7TREG-CiY7cQG-.js} +1 -1
  16. package/dist/{chunk-TICWLB2K-BC8YIm_n.js → chunk-TICWLB2K-C_t5RBp9.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-B2JvZAP4.js} +2 -2
  19. package/dist/{classDiagram-v2-VYDZK3BY-D9bfbcC3.js → classDiagram-v2-VYDZK3BY-L8VsZG5A.js} +2 -2
  20. package/dist/{code-visibility-CLNfgCjd.js → code-visibility-33fxfX1p.js} +45 -49
  21. package/dist/{common-fyD_6mT2.js → common-D09oJJ8Z.js} +7354 -6884
  22. package/dist/{copy-icon-Ft7t9bqz.js → copy-icon-De584pZ4.js} +3 -3
  23. package/dist/{cynefinDiagram-5FMLGOSQ-CgWra7wt.js → cynefinDiagram-5FMLGOSQ-DdllC264.js} +1 -1
  24. package/dist/{dagre-GXQ25YYZ-CFZ8-35c.js → dagre-GXQ25YYZ-BaWHw4_f.js} +1 -1
  25. package/dist/{data-grid-overlay-editor-BmqASKep.js → data-grid-overlay-editor-lGsInXfR.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-pFt-tUDe.js} +1 -1
  28. package/dist/{diagram-UQ7AKVKN-CQPQ6pP_.js → diagram-UQ7AKVKN-DqGJiBp4.js} +1 -1
  29. package/dist/{diagram-VX7I27RA-Cf3b0llz.js → diagram-VX7I27RA-Dr-yzMjf.js} +1 -1
  30. package/dist/{diagram-Z3DM3KII-Bt4QLrpn.js → diagram-Z3DM3KII-8xzJzv-w.js} +1 -1
  31. package/dist/{dialog-CEBFZNyc.js → dialog-DVRfQuhD.js} +25 -25
  32. package/dist/{dist-F8Ep36rk.js → dist-B1M0bGO3.js} +1 -1
  33. package/dist/{dist-D6d86eNJ.js → dist-BA13RJj5.js} +1 -1
  34. package/dist/{dist-Bvs24R_E.js → dist-BJtmSgcE.js} +48 -48
  35. package/dist/{dist-CN_BeG2I.js → dist-Ba2VIn1K.js} +53 -53
  36. package/dist/{dist-PXYI_yuP.js → dist-Bj4bmwUm.js} +12 -12
  37. package/dist/{dist-DoE4XJBC.js → dist-BmS-ZGjl.js} +8626 -8626
  38. package/dist/{dist-BorMD-uX.js → dist-Bzogh7CW.js} +87 -87
  39. package/dist/{dist-CN8zI-XC.js → dist-CThPVKaP.js} +1385 -1382
  40. package/dist/{dist-DiDibOYr.js → dist-CTi5FTiV.js} +87 -87
  41. package/dist/{dist-diIx5qJW.js → dist-Ck_C32ye.js} +2 -2
  42. package/dist/{dist-BLfKmR-l.js → dist-D1fnFp1J.js} +1 -1
  43. package/dist/{dist-OR1OnN-y.js → dist-D3x9__Fu.js} +46 -46
  44. package/dist/{dist-BMQf5zIS.js → dist-D7mrh5sO.js} +36 -36
  45. package/dist/{dist-CSJ0MDRw.js → dist-D9Q3xjiP.js} +1 -1
  46. package/dist/{dist-BuaRHxOc.js → dist-DP60Ubh8.js} +2 -2
  47. package/dist/{dist-CSbh1PNP.js → dist-DTv9KkpW.js} +12 -12
  48. package/dist/{dist-D-wwqlzJ.js → dist-DVVwOfrD.js} +109 -109
  49. package/dist/{dist-C8fAKSH1.js → dist-DYXM73-k.js} +36 -36
  50. package/dist/{dist-DBtSl3sf.js → dist-DjeybPOb.js} +1 -1
  51. package/dist/{dist-CrSP_H1M.js → dist-DtGn9N4h.js} +197 -197
  52. package/dist/{dist-CLdpGQVr.js → dist-KN80qWCP.js} +2 -2
  53. package/dist/{dist-DpBpA_n1.js → dist-Lm-D-Vr6.js} +3 -3
  54. package/dist/{dist-Cg7gzw-a.js → dist-O0qFpEJh.js} +56 -56
  55. package/dist/{dist-CkTTIdBe.js → dist-OUle2RT7.js} +207 -207
  56. package/dist/{dist-BK4Oj99o.js → dist-T5rh6JvL.js} +1 -1
  57. package/dist/{dropdown-menu-CP_qCYHC.js → dropdown-menu-NlnsmNA6.js} +1481 -1433
  58. package/dist/{ebnfDiagram-PWID7BFC-D3NAyPFY.js → ebnfDiagram-PWID7BFC-Br04Wy-E.js} +2 -2
  59. package/dist/{erDiagram-RLTQ6QDP-Tlu09_Nm.js → erDiagram-RLTQ6QDP-D5-XVgvv.js} +1 -1
  60. package/dist/{error-banner-Da600cwz.js → error-banner-DxJ3s8YO.js} +89 -84
  61. package/dist/{cells-C8iCYvLU.js → error-entries-C2Ux0Fjt.js} +7970 -8605
  62. package/dist/{esm-D2gTsXEV.js → esm-Bbw_Njjx.js} +575 -575
  63. package/dist/{esm-AMFssoNp.js → esm-mDQNEFe_.js} +354 -354
  64. package/dist/feedback-button-B0CMhVv7.js +374 -0
  65. package/dist/{flowDiagram-HODETNUW-CAYbpJmV.js → flowDiagram-HODETNUW-CVl2kREF.js} +2 -2
  66. package/dist/{formats-BGh2thRc.js → formats-BCYthwyc.js} +3 -3
  67. package/dist/{ganttDiagram-EL5Y4UJY-BQT16JSc.js → ganttDiagram-EL5Y4UJY-Dh_4cBJZ.js} +2 -2
  68. package/dist/{glide-data-editor-BkjP14aq.js → glide-data-editor-CL535A5q.js} +370 -368
  69. package/dist/{glide-utils-DJyqh0EM.js → glide-utils-D98laidb.js} +16 -16
  70. package/dist/{globals-BN2O0F8N.js → globals-Bsr6T2yD.js} +3 -3
  71. package/dist/{highlighted-body-KPVGNVTW-zwjq21tf.js → highlighted-body-KPVGNVTW-3Ziwfz-V.js} +2 -2
  72. package/dist/{html-to-image-CZY1pIZH.js → html-to-image-CkH7vdBZ.js} +478 -381
  73. package/dist/{infoDiagram-27XIBGKW-XrDxhr0x.js → infoDiagram-27XIBGKW-BJ4vNcZE.js} +1 -1
  74. package/dist/{ishikawaDiagram-5VMMS53U-CD1muzov.js → ishikawaDiagram-5VMMS53U-B_Jy1Dsm.js} +1 -1
  75. package/dist/{kanban-definition-UXKFOSKX-Dg9W142_.js → kanban-definition-UXKFOSKX-C6MkS9vw.js} +1 -1
  76. package/dist/{label-BDKF3Afc.js → label-Qvw1E97W.js} +3 -3
  77. package/dist/{linear-Cnaq3Tfb.js → linear-DWERh7eI.js} +121 -73
  78. package/dist/{loader-BQOfjTIJ.js → loader-DOVB0Syl.js} +12 -12
  79. package/dist/main.js +9706 -7708
  80. package/dist/{mermaid-CiAXjLpA.js → mermaid-Bh918qrq.js} +52 -52
  81. package/dist/mermaid-HWGCJPDP-Du3-HPQA.js +2 -0
  82. package/dist/{mindmap-definition-YA3MSWOX-DgCidgYJ.js → mindmap-definition-YA3MSWOX-BP3ZTuir.js} +1 -1
  83. package/dist/{number-overlay-editor-BEBWJnbL.js → number-overlay-editor-CzbkG4E2.js} +3 -3
  84. package/dist/{objects-CzUgoMIF.js → objects-DCmMvZtn.js} +1 -3
  85. package/dist/{once-BtE-wKRL.js → once-NnpX_HnX.js} +1 -1
  86. package/dist/{pegDiagram-XKGWAZYB-coOYYKB6.js → pegDiagram-XKGWAZYB-nNGOQtnt.js} +2 -2
  87. package/dist/{pieDiagram-E7YTZNPT-BnXTc3bj.js → pieDiagram-E7YTZNPT-CpRz5BZz.js} +1 -1
  88. package/dist/precisionRound-DhAzeRcm.js +49 -0
  89. package/dist/{process-output-e4jj9Ijt.js → process-output-OujAtFW4.js} +4 -4
  90. package/dist/{quadrantDiagram-AXDQQJYC-Ccw0xMjN.js → quadrantDiagram-AXDQQJYC-Blrm7V7_.js} +1 -1
  91. package/dist/{railroadDiagram-O6MQD6OU-B28-u83n.js → railroadDiagram-O6MQD6OU-BqOEN4I0.js} +2 -2
  92. package/dist/{react-vega-BED0DjiB.js → react-vega-C4T9Rb_w.js} +12284 -12270
  93. package/dist/{requirementDiagram-BXWQKSXE-CdYCTMu0.js → requirementDiagram-BXWQKSXE-CEfg6vu9.js} +1 -1
  94. package/dist/{reveal-component-DK69bOki.js → reveal-component-CbEK0sNX.js} +733 -710
  95. package/dist/{select-BButFi-d.js → select-2KOiReNY.js} +536 -457
  96. package/dist/{sequenceDiagram-WJ2MYXX4-CM3aRAe_.js → sequenceDiagram-WJ2MYXX4-IOmpQcOi.js} +1 -1
  97. package/dist/{spec-BwarBpUJ.js → spec-Dkp64vVQ.js} +41 -11
  98. package/dist/{stateDiagram-D77RDMKH-DOZUMVj8.js → stateDiagram-D77RDMKH-tgU-EY9_.js} +2 -2
  99. package/dist/{stateDiagram-v2-MP3YSRHH-CISYxma6.js → stateDiagram-v2-MP3YSRHH-BofhG6q1.js} +2 -2
  100. package/dist/style.css +1 -1
  101. package/dist/{swimlanes-42K2YHIH-BkSppxp8.js → swimlanes-42K2YHIH-DW4vWDBd.js} +1 -1
  102. package/dist/{swimlanesDiagram-VR7AAH4N-c8G5pGxC.js → swimlanesDiagram-VR7AAH4N-BJcrqUJr.js} +2 -2
  103. package/dist/{swiper-component-DKntyn8p.js → swiper-component-BVfnhbnt.js} +2 -2
  104. package/dist/{time-BieKwdYS.js → time-BaMbJs4z.js} +18 -18
  105. package/dist/{timeline-definition-24CTP7MA-CaMQayqK.js → timeline-definition-24CTP7MA-D_QU3NGs.js} +1 -1
  106. package/dist/{toDate-BNJf01u1.js → toDate-BNDEXbfV.js} +77 -74
  107. package/dist/{toast-B9UMEi8u.js → toast-B5t9o6s_.js} +219 -201
  108. package/dist/{tooltip-BO1REbqs.js → tooltip-s4zZxwKe.js} +325 -301
  109. package/dist/{use-restore-focus-BEM7sFWl.js → use-restore-focus-BgDTd2tc.js} +1482 -1433
  110. package/dist/{useAsyncData-Deh5kszT.js → useAsyncData-ASDni9vH.js} +15 -14
  111. package/dist/{useDateFormatter-D8XruNGG.js → useDateFormatter-D3Tkfzej.js} +2 -2
  112. package/dist/{useDeepCompareMemoize-DXwzRINI.js → useDeepCompareMemoize-B1Ogyf53.js} +1 -1
  113. package/dist/{useIframeCapabilities-DBETvvPl.js → useIframeCapabilities-6a_yDNXp.js} +1 -1
  114. package/dist/{useLifecycle-j7D9uD1o.js → useLifecycle-Bfpa2m7k.js} +28 -25
  115. package/dist/{useTheme-CRnjkku0.js → useTheme-5TwyOh3B.js} +522 -314
  116. package/dist/{vega-component-DG29TAB7.js → vega-component-BHS9UOtC.js} +158 -158
  117. package/dist/{vega-embed-container-BNHE18G9.js → vega-embed-container--LYZqguO.js} +2 -2
  118. package/dist/{vega-loader.browser-DNST6uVf.js → vega-loader.browser-B9Pz0EGP.js} +298 -507
  119. package/dist/{vennDiagram-4TSXK5OY-CF8pfaK3.js → vennDiagram-4TSXK5OY-YFYsNIAh.js} +1 -1
  120. package/dist/{wardleyDiagram-VM6X3IG4-dGPu39d-.js → wardleyDiagram-VM6X3IG4-i_n8mAWC.js} +1 -1
  121. package/dist/{xychartDiagram-S5SC5T6Z-CP6-9q56.js → xychartDiagram-S5SC5T6Z-BYGN95wW.js} +2 -2
  122. package/package.json +16 -16
  123. package/src/__mocks__/document.tsx +63 -0
  124. package/src/__mocks__/notebook.ts +3 -4
  125. package/src/__mocks__/partial.ts +26 -0
  126. package/src/__mocks__/requests.ts +0 -3
  127. package/src/__tests__/fixtures/{pair-preview.ts → pair-config.ts} +3 -2
  128. package/src/__tests__/main.test.tsx +24 -1
  129. package/src/__tests__/module-entry.test.ts +44 -0
  130. package/src/__tests__/mount.test.ts +9 -9
  131. package/src/__tests__/{host-imports.test.ts → replica-imports.test.ts} +12 -11
  132. package/src/components/ai/__tests__/ai-model-dropdown.test.tsx +63 -0
  133. package/src/components/ai/ai-model-dropdown.tsx +11 -6
  134. package/src/components/app-config/ai-config.tsx +7 -2
  135. package/src/components/app-config/app-config-form.tsx +5 -9
  136. package/src/components/app-config/user-config-form.tsx +35 -0
  137. package/src/components/chat/__tests__/message-queue.test.tsx +17 -0
  138. package/src/components/chat/__tests__/notebook-access-selector.test.tsx +64 -0
  139. package/src/components/chat/acp/__tests__/session-tabs.test.tsx +43 -0
  140. package/src/components/chat/acp/__tests__/state.test.ts +2 -2
  141. package/src/components/chat/acp/blocks.tsx +3 -2
  142. package/src/components/chat/acp/session-tabs.tsx +8 -3
  143. package/src/components/chat/chat-components.tsx +2 -0
  144. package/src/components/chat/chat-panel.tsx +20 -101
  145. package/src/components/chat/chat-utils.ts +4 -8
  146. package/src/components/chat/notebook-access-selector.stories.tsx +22 -0
  147. package/src/components/chat/notebook-access-selector.tsx +100 -0
  148. package/src/components/data-table/TableBottomBar.tsx +4 -1
  149. package/src/components/data-table/TableTopBar.tsx +2 -0
  150. package/src/components/data-table/__tests__/chart-spec-model.test.ts +195 -195
  151. package/src/components/data-table/__tests__/column-explorer.test.tsx +27 -1
  152. package/src/components/data-table/__tests__/columns.test.tsx +105 -0
  153. package/src/components/data-table/__tests__/context-menu-interaction.test.tsx +130 -0
  154. package/src/components/data-table/__tests__/context-menu.test.ts +176 -0
  155. package/src/components/data-table/__tests__/data-table.test.tsx +1 -0
  156. package/src/components/data-table/__tests__/date-filter-inputs.test.tsx +24 -1
  157. package/src/components/data-table/__tests__/date-popover.test.tsx +72 -0
  158. package/src/components/data-table/__tests__/export-actions.test.tsx +434 -6
  159. package/src/components/data-table/__tests__/keyboard-selection.test.tsx +188 -0
  160. package/src/components/data-table/__tests__/pagination.test.tsx +28 -1
  161. package/src/components/data-table/__tests__/search-highlight.test.tsx +109 -0
  162. package/src/components/data-table/__tests__/url-detector.test.tsx +74 -0
  163. package/src/components/data-table/cell-selection/feature.ts +2 -2
  164. package/src/components/data-table/cell-selection/types.ts +2 -2
  165. package/src/components/data-table/charts/__tests__/chart-panel.test.tsx +74 -1
  166. package/src/components/data-table/charts/__tests__/spec-snapshot.test.ts +20 -20
  167. package/src/components/data-table/charts/__tests__/spec.test.ts +78 -52
  168. package/src/components/data-table/charts/__tests__/storage.test.ts +4 -1
  169. package/src/components/data-table/charts/chart-spec/spec.ts +68 -43
  170. package/src/components/data-table/charts/charts.tsx +40 -30
  171. package/src/components/data-table/charts/storage.ts +16 -5
  172. package/src/components/data-table/column-explorer-panel/column-explorer.tsx +1 -1
  173. package/src/components/data-table/column-summary/chart-spec-model.tsx +23 -16
  174. package/src/components/data-table/column-summary/column-summary.tsx +2 -4
  175. package/src/components/data-table/column-visibility-dropdown.tsx +8 -13
  176. package/src/components/data-table/columns.tsx +45 -46
  177. package/src/components/data-table/context-menu.tsx +161 -46
  178. package/src/components/data-table/data-table.tsx +17 -1
  179. package/src/components/data-table/date-filter-inputs.tsx +15 -42
  180. package/src/components/data-table/date-popover.tsx +6 -4
  181. package/src/components/data-table/export-actions.tsx +267 -163
  182. package/src/components/data-table/pagination.tsx +44 -33
  183. package/src/components/data-table/range-focus/atoms.ts +4 -6
  184. package/src/components/data-table/range-focus/use-cell-range-selection.ts +65 -38
  185. package/src/components/data-table/range-focus/use-scroll-into-view.ts +21 -2
  186. package/src/components/data-table/renderers.tsx +16 -7
  187. package/src/components/data-table/row-viewer-panel/__tests__/row-viewer.test.tsx +24 -1
  188. package/src/components/data-table/row-viewer-panel/row-viewer.tsx +4 -4
  189. package/src/components/data-table/schemas.ts +14 -36
  190. package/src/components/data-table/search-highlight.tsx +79 -0
  191. package/src/components/data-table/types.ts +1 -0
  192. package/src/components/data-table/url-detector.tsx +49 -9
  193. package/src/components/data-table/utils.ts +18 -0
  194. package/src/components/datasources/__tests__/column-actions.test.tsx +97 -0
  195. package/src/components/datasources/__tests__/column-preview.test.tsx +96 -5
  196. package/src/components/datasources/__tests__/datasources-interactions.test.tsx +181 -0
  197. package/src/components/datasources/__tests__/table-list.test.tsx +118 -0
  198. package/src/components/datasources/__tests__/utils.test.ts +57 -6
  199. package/src/components/datasources/column-preview.tsx +17 -12
  200. package/src/components/datasources/datasources.tsx +56 -43
  201. package/src/components/datasources/utils.ts +32 -23
  202. package/src/components/debugger/debugger-code.tsx +5 -0
  203. package/src/components/dependency-graph/custom-node.tsx +1 -1
  204. package/src/components/dependency-graph/dependency-graph-tree.tsx +8 -8
  205. package/src/components/dependency-graph/elements.ts +24 -21
  206. package/src/components/dependency-graph/minimap-content.tsx +13 -19
  207. package/src/components/dependency-graph/panels.tsx +4 -2
  208. package/src/components/dependency-graph/utils/__tests__/layout.test.ts +18 -11
  209. package/src/components/editor/Output.tsx +9 -2
  210. package/src/components/editor/SortableCell.tsx +1 -3
  211. package/src/components/editor/__tests__/Output.test.tsx +50 -32
  212. package/src/components/editor/__tests__/agent-toast.test.tsx +58 -0
  213. package/src/components/editor/__tests__/data-attributes.test.tsx +28 -14
  214. package/src/components/editor/__tests__/dynamic-favicon.test.tsx +111 -18
  215. package/src/components/editor/__tests__/output-persistence.test.tsx +6 -2
  216. package/src/components/editor/__tests__/package-alert.test.tsx +22 -1
  217. package/src/components/editor/actions/__tests__/name-cell-input.test.tsx +69 -0
  218. package/src/components/editor/actions/__tests__/pair-with-agent-commands.test.ts +80 -252
  219. package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +218 -40
  220. package/src/components/editor/actions/__tests__/pair-with-agent.test.tsx +2 -6
  221. package/src/components/editor/actions/name-cell-input.tsx +4 -3
  222. package/src/components/editor/actions/pair-with-agent-commands.ts +44 -53
  223. package/src/components/editor/actions/pair-with-agent-modal.tsx +101 -38
  224. package/src/components/editor/actions/useCellActionButton.tsx +26 -31
  225. package/src/components/editor/actions/useConfigActions.tsx +3 -4
  226. package/src/components/editor/actions/useNotebookActions.tsx +19 -23
  227. package/src/components/editor/actions/useRestartKernel.tsx +1 -1
  228. package/src/components/editor/actions/useSetCodeVisibility.ts +11 -12
  229. package/src/components/editor/actions/useSetOutputsExpanded.ts +14 -19
  230. package/src/components/editor/agent-toast.ts +15 -0
  231. package/src/components/editor/ai/add-cell-with-ai.tsx +3 -3
  232. package/src/components/editor/ai/ai-completion-editor.tsx +1 -0
  233. package/src/components/editor/alerts/__tests__/connection-notice.test.tsx +1 -1
  234. package/src/components/editor/alerts/connection-notice.tsx +1 -1
  235. package/src/components/editor/alerts/startup-logs-alert.tsx +1 -0
  236. package/src/components/editor/alerts/stdin-blocking-alert.tsx +1 -1
  237. package/src/components/editor/app-container.tsx +1 -1
  238. package/src/components/editor/boundary/__tests__/ErrorBoundary.test.tsx +1 -1
  239. package/src/components/editor/cell/CreateCellButton.tsx +15 -21
  240. package/src/components/editor/cell/DeleteButton.tsx +3 -2
  241. package/src/components/editor/cell/RunButton.tsx +2 -2
  242. package/src/components/editor/cell/StagedAICell.tsx +10 -2
  243. package/src/components/editor/cell/StopButton.tsx +2 -2
  244. package/src/components/editor/cell/__tests__/StagedAICell.test.tsx +3 -1
  245. package/src/components/editor/cell/__tests__/useShouldShowInterrupt.test.ts +39 -0
  246. package/src/components/editor/cell/cell-actions.tsx +12 -1
  247. package/src/components/editor/cell/code/__tests__/language-toggle.test.tsx +54 -0
  248. package/src/components/editor/cell/code/cell-editor.tsx +36 -43
  249. package/src/components/editor/cell/code/language-toggle.tsx +8 -7
  250. package/src/components/editor/cell/collapse.tsx +11 -4
  251. package/src/components/editor/cell/useAddCell.ts +4 -8
  252. package/src/components/editor/cell/useDeleteCell.tsx +10 -36
  253. package/src/components/editor/cell/useRunCells.ts +2 -5
  254. package/src/components/editor/cell/useShouldShowInterrupt.ts +2 -12
  255. package/src/components/editor/cell/useSplitCell.tsx +10 -6
  256. package/src/components/editor/chrome/components/__tests__/feedback-button.test.tsx +1 -1
  257. package/src/components/editor/chrome/components/feedback-button.tsx +2 -8
  258. package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +31 -2
  259. package/src/components/editor/chrome/panels/__tests__/runtime-details.test.tsx +1 -1
  260. package/src/components/editor/chrome/panels/__tests__/sandbox-recovery.test.tsx +1 -1
  261. package/src/components/editor/chrome/panels/cache-panel.tsx +1 -0
  262. package/src/components/editor/chrome/panels/dependency-graph-panel.tsx +1 -1
  263. package/src/components/editor/chrome/panels/error-panel.tsx +1 -0
  264. package/src/components/editor/chrome/panels/logs-panel.tsx +2 -1
  265. package/src/components/editor/chrome/panels/outline/__tests__/outline-keyboard.test.tsx +84 -0
  266. package/src/components/editor/chrome/panels/outline/floating-outline.tsx +39 -14
  267. package/src/components/editor/chrome/panels/outline/useActiveOutline.tsx +3 -2
  268. package/src/components/editor/chrome/panels/packages-panel.tsx +4 -7
  269. package/src/components/editor/chrome/panels/secrets-panel.tsx +1 -0
  270. package/src/components/editor/chrome/panels/session-panel.tsx +1 -1
  271. package/src/components/editor/chrome/panels/snippets-panel.tsx +10 -12
  272. package/src/components/editor/chrome/panels/variable-panel.tsx +1 -5
  273. package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +363 -0
  274. package/src/components/editor/chrome/wrapper/__tests__/document-sync-status.test.tsx +92 -0
  275. package/src/components/editor/chrome/wrapper/__tests__/pending-ai-cells.test.tsx +61 -2
  276. package/src/components/editor/chrome/wrapper/footer-item.tsx +32 -24
  277. package/src/components/editor/chrome/wrapper/footer-items/attachment-status.tsx +237 -0
  278. package/src/components/editor/chrome/wrapper/footer-items/backend-status.tsx +1 -1
  279. package/src/components/editor/chrome/wrapper/footer-items/document-sync-status.tsx +54 -0
  280. package/src/components/editor/chrome/wrapper/footer-items/lsp-status.tsx +1 -1
  281. package/src/components/editor/chrome/wrapper/footer-items/machine-stats.tsx +1 -1
  282. package/src/components/editor/chrome/wrapper/footer-items/runtime-settings.tsx +1 -7
  283. package/src/components/editor/chrome/wrapper/footer.tsx +4 -0
  284. package/src/components/editor/chrome/wrapper/minimap-state.ts +1 -1
  285. package/src/components/editor/chrome/wrapper/pending-ai-cells.tsx +12 -2
  286. package/src/components/editor/chrome/wrapper/storage.ts +1 -1
  287. package/src/components/editor/columns/__tests__/cell-column.test.tsx +1 -1
  288. package/src/components/editor/columns/cell-column.tsx +1 -1
  289. package/src/components/editor/columns/sortable-column.tsx +2 -2
  290. package/src/components/editor/connections/components.tsx +5 -10
  291. package/src/components/editor/controls/Controls.tsx +4 -4
  292. package/src/components/editor/controls/__tests__/command-palette.test.tsx +53 -0
  293. package/src/components/editor/controls/__tests__/keyboard-shortcuts.test.tsx +61 -0
  294. package/src/components/editor/controls/__tests__/notebook-menu-dropdown.test.tsx +10 -4
  295. package/src/components/editor/controls/command-palette.tsx +22 -10
  296. package/src/components/editor/controls/keyboard-shortcuts.tsx +14 -5
  297. package/src/components/editor/documentation.css +34 -11
  298. package/src/components/editor/dynamic-favicon.tsx +21 -40
  299. package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +536 -0
  300. package/src/components/editor/errors/auto-fix.tsx +4 -8
  301. package/src/components/editor/errors/send-error-report-button.tsx +66 -0
  302. package/src/components/editor/file-tree/__tests__/file-explorer-interactions.test.tsx +6 -0
  303. package/src/components/editor/file-tree/__tests__/file-explorer-navigation.test.tsx +47 -0
  304. package/src/components/editor/file-tree/__tests__/use-file-search.test.tsx +91 -0
  305. package/src/components/editor/file-tree/file-explorer.tsx +48 -15
  306. package/src/components/editor/file-tree/file-name-input.tsx +1 -0
  307. package/src/components/editor/file-tree/file-viewer.tsx +4 -2
  308. package/src/components/editor/file-tree/use-file-search.ts +35 -21
  309. package/src/components/editor/header/__tests__/status.test.tsx +5 -3
  310. package/src/components/editor/header/app-header.tsx +1 -1
  311. package/src/components/editor/header/status.tsx +40 -20
  312. package/src/components/editor/links/__tests__/cell-link.test.tsx +148 -0
  313. package/src/components/editor/links/cell-link-list.tsx +1 -1
  314. package/src/components/editor/links/cell-link.tsx +17 -7
  315. package/src/components/editor/navigation/__tests__/clipboard.test.ts +60 -38
  316. package/src/components/editor/navigation/__tests__/navigation.test.ts +108 -85
  317. package/src/components/editor/navigation/__tests__/selection.test.ts +43 -21
  318. package/src/components/editor/navigation/clipboard.ts +17 -14
  319. package/src/components/editor/navigation/focus-utils.ts +23 -4
  320. package/src/components/editor/navigation/multi-cell-action-toolbar.tsx +17 -33
  321. package/src/components/editor/navigation/navigation.ts +71 -70
  322. package/src/components/editor/navigation/selection.ts +30 -29
  323. package/src/components/editor/notebook-banner.tsx +1 -0
  324. package/src/components/editor/notebook-cell.tsx +29 -31
  325. package/src/components/editor/output/MarimoErrorOutput.tsx +23 -6
  326. package/src/components/editor/output/MarimoTracebackOutput.tsx +54 -37
  327. package/src/components/editor/output/Outputs.css +8 -0
  328. package/src/components/editor/output/__tests__/ansi-reduce.test.ts +18 -0
  329. package/src/components/editor/output/__tests__/traceback.test.tsx +27 -1
  330. package/src/components/editor/output/ansi-reduce.ts +36 -17
  331. package/src/components/editor/output/console/ConsoleOutput.tsx +19 -25
  332. package/src/components/editor/output/console/__tests__/ConsoleOutput.test.tsx +125 -69
  333. package/src/components/editor/package-alert.tsx +14 -7
  334. package/src/components/editor/renderers/cell-array.tsx +22 -33
  335. package/src/components/editor/renderers/grid-layout/grid-layout.tsx +12 -6
  336. package/src/components/editor/renderers/slides-layout/__tests__/plugin.test.ts +0 -1
  337. package/src/components/editor/renderers/slides-layout/slides-layout.tsx +1 -1
  338. package/src/components/editor/renderers/vertical-layout/__tests__/useFocusFirstEditor.test.ts +4 -4
  339. package/src/components/editor/renderers/vertical-layout/sidebar/toggle.tsx +7 -1
  340. package/src/components/editor/renderers/vertical-layout/useFocusFirstEditor.ts +1 -1
  341. package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +1 -1
  342. package/src/components/editor/stdin-blocking-alert.tsx +1 -1
  343. package/src/components/home/components.tsx +17 -31
  344. package/src/components/icons/__tests__/large-spinner.test.tsx +39 -0
  345. package/src/components/icons/large-spinner.tsx +3 -14
  346. package/src/components/icons/spinner.tsx +1 -1
  347. package/src/components/markdown/markdown-renderer.tsx +4 -8
  348. package/src/components/mcp/mcp-status-indicator.tsx +1 -1
  349. package/src/components/pages/__tests__/home-page.test.tsx +116 -0
  350. package/src/components/pages/edit-page.tsx +19 -17
  351. package/src/components/pages/home-page.tsx +164 -91
  352. package/src/components/scratchpad/scratchpad.test.tsx +97 -0
  353. package/src/components/scratchpad/scratchpad.tsx +20 -11
  354. package/src/components/slides/__tests__/minimap-actions.test.tsx +22 -29
  355. package/src/components/slides/__tests__/minimap.test.ts +38 -19
  356. package/src/components/slides/__tests__/slide-scroll-hint.test.tsx +37 -0
  357. package/src/components/slides/minimap.tsx +17 -12
  358. package/src/components/slides/reveal-component.tsx +58 -26
  359. package/src/components/slides/reveal-slides.css +5 -4
  360. package/src/components/slides/slide-cell-view.tsx +4 -5
  361. package/src/components/slides/slide-form.tsx +1 -0
  362. package/src/components/slides/slide-scroll-hint.tsx +3 -1
  363. package/src/components/sort/SortableCellsProvider.tsx +8 -25
  364. package/src/components/tracing/tracing-keyboard.test.tsx +85 -0
  365. package/src/components/tracing/tracing.tsx +46 -42
  366. package/src/components/ui/__tests__/draggable-popover.test.tsx +24 -0
  367. package/src/components/ui/__tests__/range-slider.test.tsx +165 -0
  368. package/src/components/ui/alert-dialog.tsx +1 -1
  369. package/src/components/ui/draggable-popover.tsx +24 -2
  370. package/src/components/ui/range-slider.tsx +22 -9
  371. package/src/core/MarimoApp.tsx +1 -1
  372. package/src/core/ai/__tests__/staged-cells.test.ts +27 -26
  373. package/src/core/ai/context/providers/__tests__/cell-output.test.ts +8 -12
  374. package/src/core/ai/context/providers/cell-output.ts +2 -2
  375. package/src/core/ai/context/providers/datasource.ts +6 -2
  376. package/src/core/ai/staged-cells.ts +15 -18
  377. package/src/core/ai/tools/__tests__/edit-notebook-tool.test.ts +40 -88
  378. package/src/core/ai/tools/__tests__/registry.test.ts +17 -0
  379. package/src/core/ai/tools/base.ts +18 -15
  380. package/src/core/ai/tools/edit-notebook-tool.ts +86 -80
  381. package/src/core/ai/tools/registry.ts +13 -10
  382. package/src/core/ai/tools/sample-tool.ts +6 -7
  383. package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +4 -37
  384. package/src/core/cells/__tests__/actions.test.ts +624 -35
  385. package/src/core/cells/__tests__/add-missing-import.test.ts +7 -8
  386. package/src/core/cells/__tests__/arrange.test.ts +432 -0
  387. package/src/core/cells/__tests__/cell.test.ts +39 -0
  388. package/src/core/cells/__tests__/cells.test.ts +256 -2477
  389. package/src/core/cells/__tests__/collapseConsoleOutputs.test.ts +54 -0
  390. package/src/core/cells/__tests__/document.test.ts +341 -0
  391. package/src/core/cells/__tests__/editor.test.ts +57 -0
  392. package/src/core/cells/__tests__/error-report.test.ts +139 -0
  393. package/src/core/cells/__tests__/flush-document.test.ts +40 -0
  394. package/src/core/cells/__tests__/harness.ts +106 -0
  395. package/src/core/cells/__tests__/layout.test.ts +177 -0
  396. package/src/core/cells/__tests__/notebook-outline.test.ts +120 -0
  397. package/src/core/cells/__tests__/outputs.test.tsx +71 -53
  398. package/src/core/cells/__tests__/session.test.ts +493 -210
  399. package/src/core/cells/__tests__/utils.test.ts +9 -108
  400. package/src/core/cells/__tests__/with-document-save.test.ts +67 -0
  401. package/src/core/cells/actions.ts +636 -6
  402. package/src/core/cells/add-missing-import.ts +47 -43
  403. package/src/core/cells/arrange.ts +529 -0
  404. package/src/core/cells/cell.ts +14 -3
  405. package/src/core/cells/cells.ts +120 -1337
  406. package/src/core/cells/columns.ts +38 -0
  407. package/src/core/cells/document.ts +289 -0
  408. package/src/core/cells/edit.ts +30 -0
  409. package/src/core/cells/editor.ts +82 -0
  410. package/src/core/cells/error-report.ts +63 -0
  411. package/src/core/cells/layout.ts +221 -0
  412. package/src/core/cells/outputs.ts +12 -28
  413. package/src/core/cells/pending-delete-service.ts +3 -7
  414. package/src/core/cells/session.ts +104 -54
  415. package/src/core/cells/types.ts +22 -6
  416. package/src/core/cells/utils.ts +10 -8
  417. package/src/core/codemirror/__tests__/setup.test.ts +29 -29
  418. package/src/core/codemirror/ai/__tests__/resources.test.ts +117 -0
  419. package/src/core/codemirror/ai/resources.ts +1 -1
  420. package/src/core/codemirror/cells/__tests__/extensions.test.ts +42 -1
  421. package/src/core/codemirror/cells/extensions.ts +27 -9
  422. package/src/core/codemirror/cells/state.ts +1 -1
  423. package/src/core/codemirror/copilot/__tests__/getCodes.test.ts +6 -7
  424. package/src/core/codemirror/copilot/getCodes.ts +2 -2
  425. package/src/core/codemirror/format.ts +6 -5
  426. package/src/core/codemirror/keymaps/vim.ts +4 -8
  427. package/src/core/codemirror/language/__tests__/sql.test.ts +22 -20
  428. package/src/core/codemirror/language/__tests__/utils.test.ts +187 -15
  429. package/src/core/codemirror/language/cell-code.ts +114 -0
  430. package/src/core/codemirror/language/extension.ts +52 -35
  431. package/src/core/codemirror/language/languages/markdown.ts +8 -6
  432. package/src/core/codemirror/language/languages/python.ts +16 -10
  433. package/src/core/codemirror/language/languages/sql/sql.ts +16 -24
  434. package/src/core/codemirror/language/panel/panel.tsx +1 -0
  435. package/src/core/codemirror/language/types.ts +7 -7
  436. package/src/core/codemirror/language/utils.ts +9 -39
  437. package/src/core/codemirror/lsp/__tests__/notebook-lsp.test.ts +1 -2
  438. package/src/core/codemirror/lsp/notebook-lsp.ts +6 -5
  439. package/src/core/codemirror/lsp/utils.ts +1 -1
  440. package/src/core/codemirror/readonly/__tests__/extension.test.ts +1 -1
  441. package/src/core/codemirror/readonly/extension.ts +2 -2
  442. package/src/core/codemirror/rtc/extension.ts +24 -13
  443. package/src/core/codemirror/rtc/loro/awareness.ts +34 -19
  444. package/src/core/codemirror/rtc/loro/sync.ts +1 -0
  445. package/src/core/codemirror/utils.ts +1 -0
  446. package/src/core/config/__tests__/app-config.test.ts +64 -0
  447. package/src/core/config/__tests__/config-schema.test.ts +13 -6
  448. package/src/core/config/config-schema.ts +13 -3
  449. package/src/core/config/config.ts +26 -12
  450. package/src/core/config/pair.ts +5 -3
  451. package/src/core/datasets/__tests__/data-source-discovery.test.ts +5 -5
  452. package/src/core/datasets/data-source-discovery.ts +4 -6
  453. package/src/core/documentation/doc-lookup.ts +1 -1
  454. package/src/core/edit-app.tsx +21 -28
  455. package/src/core/errors/error-entries.ts +4 -1
  456. package/src/core/export/hooks.ts +1 -1
  457. package/src/core/export/layout.ts +2 -2
  458. package/src/core/hotkeys/actions.ts +2 -4
  459. package/src/core/islands/__tests__/bootstrap.test.ts +69 -17
  460. package/src/core/islands/__tests__/bridge.test.ts +72 -25
  461. package/src/core/islands/bootstrap.ts +46 -53
  462. package/src/core/islands/bridge.ts +74 -23
  463. package/src/core/islands/components/__tests__/IslandControls.test.tsx +7 -5
  464. package/src/core/islands/components/web-components.tsx +1 -1
  465. package/src/core/islands/worker/__tests__/controller.test.ts +62 -4
  466. package/src/core/islands/worker/controller.ts +4 -10
  467. package/src/core/islands/worker/worker.tsx +33 -33
  468. package/src/core/kernel/__tests__/RuntimeState.test.ts +1 -1
  469. package/src/core/kernel/__tests__/handlers.test.ts +7 -153
  470. package/src/core/kernel/handlers.ts +19 -72
  471. package/src/core/meta/__tests__/code-visibility.test.tsx +78 -70
  472. package/src/core/meta/code-visibility.ts +18 -9
  473. package/src/core/network/__tests__/connection.test.ts +1 -1
  474. package/src/core/network/__tests__/requests-lazy.test.ts +1 -3
  475. package/src/core/network/connection-notice.ts +1 -1
  476. package/src/core/network/connection.ts +5 -3
  477. package/src/core/network/requests-lazy.ts +1 -4
  478. package/src/core/network/requests-network.ts +0 -25
  479. package/src/core/network/requests-static.ts +0 -3
  480. package/src/core/network/requests-toasting.tsx +0 -3
  481. package/src/core/network/types.ts +0 -12
  482. package/src/core/network/useConnectionNotice.ts +8 -2
  483. package/src/core/notebook/__tests__/operations.test.ts +51 -0
  484. package/src/core/notebook/attachments.ts +38 -0
  485. package/src/core/notebook/document/__tests__/codemirror.test.ts +586 -0
  486. package/src/core/notebook/document/__tests__/draft.test.ts +363 -0
  487. package/src/core/notebook/document/__tests__/fixtures.ts +92 -0
  488. package/src/core/notebook/document/__tests__/hold.test.ts +88 -0
  489. package/src/core/notebook/document/__tests__/hooks.test.tsx +391 -0
  490. package/src/core/notebook/document/__tests__/lifecycle-scenarios.test.ts +61 -0
  491. package/src/core/notebook/document/__tests__/model.test.ts +247 -0
  492. package/src/core/notebook/document/__tests__/protocol-fixtures.ts +38 -0
  493. package/src/core/notebook/document/__tests__/recovery-scenarios.test.ts +230 -0
  494. package/src/core/notebook/document/__tests__/scenario-fixture.ts +43 -0
  495. package/src/core/notebook/document/__tests__/scenarios.test.ts +205 -0
  496. package/src/core/notebook/document/__tests__/state.test.ts +459 -0
  497. package/src/core/notebook/document/__tests__/submission.test.ts +203 -0
  498. package/src/core/notebook/document/__tests__/test-host.test.ts +92 -0
  499. package/src/core/notebook/document/__tests__/test-host.ts +247 -0
  500. package/src/core/notebook/document/__tests__/test-notebook.ts +149 -0
  501. package/src/core/notebook/document/__tests__/user.ts +61 -0
  502. package/src/core/notebook/document/codemirror.ts +216 -0
  503. package/src/core/notebook/document/draft.ts +502 -0
  504. package/src/core/notebook/document/hooks.tsx +118 -0
  505. package/src/core/notebook/document/index.ts +24 -0
  506. package/src/core/notebook/document/model.ts +105 -0
  507. package/src/core/notebook/document/select.ts +41 -0
  508. package/src/core/notebook/document/state.ts +378 -0
  509. package/src/core/notebook/document/sync.ts +418 -0
  510. package/src/core/notebook/index.ts +19 -0
  511. package/src/core/notebook/operations.ts +30 -0
  512. package/src/core/notebook/replica/AGENTS.md +11 -0
  513. package/src/core/notebook/replica/__tests__/attachments.test.ts +56 -0
  514. package/src/core/notebook/replica/__tests__/document.test.ts +126 -0
  515. package/src/core/notebook/replica/__tests__/fixtures.ts +79 -0
  516. package/src/core/notebook/replica/__tests__/replica.test.ts +550 -0
  517. package/src/core/notebook/replica/backend.ts +19 -0
  518. package/src/core/{websocket/types.ts → notebook/replica/connection.ts} +10 -15
  519. package/src/core/notebook/replica/index.ts +13 -0
  520. package/src/core/notebook/replica/replica.ts +286 -0
  521. package/src/core/notebook/replica/resources/attachments.ts +22 -0
  522. package/src/core/notebook/replica/resources/document.ts +101 -0
  523. package/src/core/packages/__tests__/useSandboxController.test.tsx +1 -1
  524. package/src/core/packages/useSandboxController.ts +1 -1
  525. package/src/core/run-app.tsx +48 -51
  526. package/src/core/runtime/__tests__/adapter.test.ts +1 -1
  527. package/src/core/runtime/__tests__/createWsUrl.test.ts +77 -7
  528. package/src/core/runtime/__tests__/runtime.test.ts +33 -15
  529. package/src/core/runtime/adapter.ts +1 -1
  530. package/src/core/runtime/config.ts +2 -2
  531. package/src/core/runtime/runtime.ts +39 -30
  532. package/src/core/saving/__tests__/state.test.ts +111 -0
  533. package/src/core/saving/__tests__/useAutoSave.test.tsx +67 -0
  534. package/src/core/saving/filename.ts +1 -1
  535. package/src/core/saving/save-component.tsx +28 -36
  536. package/src/core/saving/state.ts +38 -39
  537. package/src/core/saving/useAutoSave.ts +17 -53
  538. package/src/core/session/__tests__/backend.test.ts +236 -0
  539. package/src/core/session/__tests__/useNotebookReplica.hook.test.tsx +184 -0
  540. package/src/core/{websocket/__tests__/useMarimoKernelConnection.hook.test.tsx → session/__tests__/useNotebookSession.hook.test.tsx} +252 -185
  541. package/src/core/session/active-notebook.ts +22 -0
  542. package/src/core/session/backend.ts +123 -0
  543. package/src/core/session/embedded.ts +7 -0
  544. package/src/core/session/types.ts +17 -0
  545. package/src/core/session/useNotebookReplica.ts +74 -0
  546. package/src/core/{websocket/useMarimoKernelConnection.tsx → session/useNotebookSession.tsx} +120 -273
  547. package/src/core/state/__tests__/jotai.test.tsx +108 -0
  548. package/src/core/state/jotai.ts +1 -1
  549. package/src/core/static/__tests__/files.test.ts +38 -10
  550. package/src/core/static/files.ts +2 -0
  551. package/src/core/{websocket/__tests__/useMarimoKernelConnection.test.ts → transports/__tests__/connection-state.test.ts} +16 -30
  552. package/src/core/transports/__tests__/server.test.ts +408 -0
  553. package/src/core/transports/__tests__/static.test.ts +306 -0
  554. package/src/core/transports/__tests__/wasm.test.ts +166 -0
  555. package/src/core/transports/connection-state.ts +63 -0
  556. package/src/core/transports/server.ts +152 -0
  557. package/src/core/transports/static.ts +166 -0
  558. package/src/core/transports/wasm.ts +97 -0
  559. package/src/core/wasm/__tests__/bridge.test.ts +214 -50
  560. package/src/core/wasm/__tests__/worker.test.ts +187 -0
  561. package/src/core/wasm/bridge.ts +75 -69
  562. package/src/core/wasm/rpc.ts +15 -3
  563. package/src/core/wasm/store.ts +3 -3
  564. package/src/core/wasm/worker/bootstrap.ts +55 -51
  565. package/src/core/wasm/worker/getMarimoWheel.ts +1 -1
  566. package/src/core/wasm/worker/save-worker.ts +9 -10
  567. package/src/core/wasm/worker/types.ts +29 -18
  568. package/src/core/wasm/worker/worker.ts +88 -44
  569. package/src/css/globals.css +66 -22
  570. package/src/hooks/__tests__/useAsyncData.test.tsx +148 -0
  571. package/src/hooks/__tests__/useDebounce.test.tsx +51 -0
  572. package/src/hooks/__tests__/useDeepCompareMemoize.test.tsx +39 -0
  573. package/src/hooks/__tests__/useDelayElapsed.test.tsx +28 -0
  574. package/src/hooks/__tests__/useInternalStateWithSync.test.tsx +20 -0
  575. package/src/hooks/__tests__/useScript.test.tsx +77 -0
  576. package/src/hooks/__tests__/useStateForKey.test.tsx +257 -0
  577. package/src/hooks/useAsyncData.ts +3 -3
  578. package/src/hooks/useDataSourceDiscovery.ts +6 -4
  579. package/src/hooks/useDebounce.ts +6 -10
  580. package/src/hooks/useDeepCompareMemoize.ts +1 -1
  581. package/src/hooks/useDelayElapsed.ts +14 -10
  582. package/src/hooks/useElapsedTime.ts +3 -3
  583. package/src/hooks/useEventListener.ts +1 -2
  584. package/src/hooks/useInputHistory.ts +4 -7
  585. package/src/hooks/useInternalStateWithSync.ts +6 -6
  586. package/src/hooks/useResizeObserver.ts +4 -7
  587. package/src/hooks/useScript.ts +67 -70
  588. package/src/hooks/useStateForKey.ts +93 -0
  589. package/src/mount.tsx +23 -14
  590. package/src/plugins/__tests__/plugin-object-schemas.test.ts +104 -0
  591. package/src/plugins/core/registerReactComponent.tsx +1 -1
  592. package/src/plugins/impl/CodeEditorPlugin.tsx +1 -1
  593. package/src/plugins/impl/DataEditorPlugin.tsx +1 -1
  594. package/src/plugins/impl/DataTablePlugin.tsx +69 -37
  595. package/src/plugins/impl/DropdownPlugin.tsx +2 -6
  596. package/src/plugins/impl/FileBrowserPlugin.tsx +4 -16
  597. package/src/plugins/impl/FormPlugin.tsx +1 -1
  598. package/src/plugins/impl/RangeSliderPlugin.tsx +16 -2
  599. package/src/plugins/impl/RefreshPlugin.tsx +1 -0
  600. package/src/plugins/impl/SliderPlugin.tsx +6 -8
  601. package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +471 -1
  602. package/src/plugins/impl/__tests__/FileBrowserPlugin.test.tsx +45 -0
  603. package/src/plugins/impl/__tests__/RangeSliderPlugin.test.tsx +109 -0
  604. package/src/plugins/impl/__tests__/SliderPlugin.test.tsx +24 -3
  605. package/src/plugins/impl/anywidget/__tests__/registry.test.ts +153 -0
  606. package/src/plugins/impl/anywidget/__tests__/widget-binding.test.ts +241 -2
  607. package/src/plugins/impl/anywidget/model.ts +3 -1
  608. package/src/plugins/impl/anywidget/registry.ts +18 -14
  609. package/src/plugins/impl/anywidget/runtime.ts +21 -9
  610. package/src/plugins/impl/anywidget/widget-binding.ts +160 -82
  611. package/src/plugins/impl/chat/__tests__/chat-prompts.test.tsx +93 -0
  612. package/src/plugins/impl/chat/chat-ui.tsx +14 -11
  613. package/src/plugins/impl/code/any-language-editor.tsx +2 -2
  614. package/src/plugins/impl/data-editor/glide-data-editor.tsx +8 -5
  615. package/src/plugins/impl/data-editor/glide-utils.ts +3 -1
  616. package/src/plugins/impl/data-explorer/ConnectedDataExplorerComponent.tsx +1 -1
  617. package/src/plugins/impl/data-explorer/components/column-summary.tsx +4 -2
  618. package/src/plugins/impl/data-explorer/state/reducer.ts +1 -1
  619. package/src/plugins/impl/data-frames/DataFramePlugin.tsx +1 -2
  620. package/src/plugins/impl/data-frames/__tests__/sidebar.test.tsx +160 -0
  621. package/src/plugins/impl/data-frames/forms/__tests__/__snapshots__/form.test.tsx.snap +66 -66
  622. package/src/plugins/impl/data-frames/panel.tsx +43 -16
  623. package/src/plugins/impl/mpl-interactive/MplInteractivePlugin.tsx +5 -3
  624. package/src/plugins/impl/panel/PanelPlugin.tsx +16 -17
  625. package/src/plugins/impl/panel/__tests__/utils.test.ts +39 -1
  626. package/src/plugins/impl/panel/utils.ts +17 -0
  627. package/src/plugins/impl/plotly/PlotlyPlugin.tsx +2 -5
  628. package/src/plugins/impl/plotly/selection.ts +25 -19
  629. package/src/plugins/impl/vega/VegaPlugin.tsx +2 -3
  630. package/src/plugins/impl/vega/make-selectable.ts +4 -7
  631. package/src/plugins/layout/RoutesPlugin.tsx +1 -1
  632. package/src/stories/__tests__/editor-story.test.tsx +33 -0
  633. package/src/stories/button.stories.tsx +1 -0
  634. package/src/stories/cell.stories.tsx +2 -5
  635. package/src/stories/data-table.stories.tsx +31 -0
  636. package/src/stories/editor.stories.tsx +24 -17
  637. package/src/stories/form-wrapper.stories.tsx +2 -2
  638. package/src/stories/layout/vertical/one-column.stories.tsx +2 -5
  639. package/src/stories/runtime-story.tsx +23 -17
  640. package/src/stories/select.stories.tsx +2 -2
  641. package/src/stories/switchable-multi-select.stories.tsx +1 -1
  642. package/src/utils/__tests__/arrays.test.ts +35 -0
  643. package/src/utils/__tests__/batch-requests.test.ts +3 -3
  644. package/src/utils/__tests__/{createReducer.test.ts → createReducer.test.tsx} +29 -0
  645. package/src/utils/__tests__/dates.test.ts +116 -0
  646. package/src/utils/__tests__/numbers.test.ts +105 -1
  647. package/src/utils/__tests__/objects.test.ts +21 -0
  648. package/src/utils/arrays.ts +11 -2
  649. package/src/utils/blob.ts +1 -1
  650. package/src/utils/createReducer.ts +53 -26
  651. package/src/utils/dates.ts +94 -0
  652. package/src/utils/maps.ts +0 -3
  653. package/src/utils/numbers.ts +79 -18
  654. package/src/utils/objects.ts +4 -9
  655. package/src/utils/once.ts +19 -19
  656. package/src/utils/tracer.ts +9 -5
  657. package/src/utils/zod-utils.ts +1 -3
  658. package/dist/any-language-editor-B9REGlD9.js +0 -76
  659. package/dist/assets/__vite-browser-external-BXYkiFsk.js +0 -1
  660. package/dist/assets/worker-HPY9fbhz.js +0 -114
  661. package/dist/error-entries-CzoYkIQZ.js +0 -56
  662. package/dist/feedback-button-fND8YT59.js +0 -380
  663. package/dist/mermaid-HWGCJPDP-ByCsZnYF.js +0 -2
  664. package/dist/precisionRound-DMIVQrt2.js +0 -97
  665. package/src/components/editor/navigation/__tests__/state.test.ts +0 -81
  666. package/src/components/editor/navigation/state.ts +0 -65
  667. package/src/core/ai/tools/__tests__/run-cells-tool.test.ts +0 -656
  668. package/src/core/ai/tools/run-cells-tool.ts +0 -303
  669. package/src/core/cells/__tests__/apply-transaction.test.ts +0 -991
  670. package/src/core/cells/__tests__/document-changes.test.ts +0 -1140
  671. package/src/core/cells/__tests__/document-roundtrip.test.ts +0 -392
  672. package/src/core/cells/document-changes.ts +0 -944
  673. package/src/core/host/AGENTS.md +0 -6
  674. package/src/core/host/__tests__/protocol.test.ts +0 -214
  675. package/src/core/host/protocol.ts +0 -1943
  676. package/src/core/state/__tests__/jotai.test.ts +0 -25
  677. package/src/core/wasm/worker/message-buffer.ts +0 -33
  678. package/src/core/websocket/__tests__/useWebSocket.test.ts +0 -49
  679. package/src/core/websocket/transports/__tests__/basic.test.ts +0 -275
  680. package/src/core/websocket/transports/__tests__/sse.test.ts +0 -338
  681. package/src/core/websocket/transports/__tests__/transport.test.ts +0 -308
  682. package/src/core/websocket/transports/__tests__/ws.test.ts +0 -125
  683. package/src/core/websocket/transports/basic.ts +0 -102
  684. package/src/core/websocket/transports/sse.ts +0 -308
  685. package/src/core/websocket/transports/transport.ts +0 -61
  686. package/src/core/websocket/transports/ws.ts +0 -98
  687. package/src/core/websocket/useWebSocket.tsx +0 -88
  688. package/src/utils/__tests__/id-tree.test.ts +0 -2145
  689. package/src/utils/id-tree.tsx +0 -1134
  690. /package/src/core/{websocket → session}/connection-utils.ts +0 -0
@@ -16,6 +16,41 @@ import {
16
16
  } from "../arrays";
17
17
 
18
18
  describe("arrays", () => {
19
+ describe.each([
20
+ ["intersection", Arrays.intersection, [3, 3, 2], [Number.NaN]],
21
+ ["difference", Arrays.difference, [1], [1]],
22
+ ] as const)("%s", (_name, operation, expected, expectedWithNaN) => {
23
+ it("preserves order and duplicates without mutating readonly inputs", () => {
24
+ const values = Object.freeze([3, 1, 3, 2]);
25
+ const other = Object.freeze([2, 3, 3]);
26
+ expect(operation(values, other)).toEqual(expected);
27
+ expect(values).toEqual([3, 1, 3, 2]);
28
+ expect(other).toEqual([2, 3, 3]);
29
+ });
30
+
31
+ it("uses the same equality as array membership, including NaN", () => {
32
+ expect(operation([Number.NaN, 1], [Number.NaN])).toEqual(expectedWithNaN);
33
+ });
34
+
35
+ it("handles an empty source", () => {
36
+ expect(operation([], [1])).toEqual([]);
37
+ });
38
+ });
39
+
40
+ it("handles empty membership lists", () => {
41
+ expect(Arrays.intersection([1, 1, 2], [])).toEqual([]);
42
+ expect(Arrays.difference([1, 1, 2], [])).toEqual([1, 1, 2]);
43
+ });
44
+
45
+ it("compares objects by identity", () => {
46
+ const shared = { id: 1 };
47
+ const distinct = { id: 1 };
48
+ expect(Arrays.intersection([shared, distinct], [shared])).toEqual([shared]);
49
+ expect(Arrays.difference([shared, distinct], [shared])).toEqual([distinct]);
50
+ expect(Arrays.intersection([shared, distinct], [shared])[0]).toBe(shared);
51
+ expect(Arrays.difference([shared, distinct], [shared])[0]).toBe(distinct);
52
+ });
53
+
19
54
  describe("arrayDelete", () => {
20
55
  it("should delete an element at the specified index", () => {
21
56
  expect(arrayDelete([1, 2, 3], 1)).toEqual([1, 3]);
@@ -8,7 +8,7 @@ async function sleep(ms: number) {
8
8
 
9
9
  describe("batch", () => {
10
10
  it("should return the same promise for calls with the same key", async () => {
11
- const loader = vi.fn().mockImplementation(async (arg) => {
11
+ const loader = vi.fn().mockImplementation(async () => {
12
12
  await sleep(10);
13
13
  return "response";
14
14
  });
@@ -25,7 +25,7 @@ describe("batch", () => {
25
25
  });
26
26
 
27
27
  it("should call loader again after the first promise resolves", async () => {
28
- const loader = vi.fn().mockImplementation(async (arg) => {
28
+ const loader = vi.fn().mockImplementation(async () => {
29
29
  await sleep(10);
30
30
  return "response";
31
31
  });
@@ -40,7 +40,7 @@ describe("batch", () => {
40
40
  });
41
41
 
42
42
  it("should handle different keys separately", async () => {
43
- const loader = vi.fn().mockImplementation(async (arg) => {
43
+ const loader = vi.fn().mockImplementation(async () => {
44
44
  await sleep(10);
45
45
  return "response";
46
46
  });
@@ -1,4 +1,7 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
+ import { renderHook } from "@testing-library/react";
3
+ import { createStore, Provider } from "jotai";
4
+ import type { PropsWithChildren } from "react";
2
5
  import { describe, expect, it, vi } from "vitest";
3
6
  import { Logger } from "@/utils/Logger";
4
7
  import { createReducer, createReducerAndAtoms } from "../createReducer";
@@ -93,6 +96,7 @@ describe("createReducer", () => {
93
96
  const loggerSpy = vi.spyOn(Logger, "error");
94
97
 
95
98
  const state = { count: 0 };
99
+ // @ts-expect-error - exercise runtime handling of an invalid action
96
100
  const newState = reducer(state, { type: "nonexistent", payload: null });
97
101
 
98
102
  expect(newState).toBe(state);
@@ -139,6 +143,31 @@ describe("createReducerAndAtoms", () => {
139
143
  expect(middleware).toHaveBeenCalledWith(state, { count: 5 }, action);
140
144
  });
141
145
 
146
+ it("runs middleware after the write commits, so it sees the new state", () => {
147
+ const initialState = () => ({ count: 0 });
148
+ const reducers = {
149
+ increment: (state: State, payload: number) => ({
150
+ count: state.count + payload,
151
+ }),
152
+ };
153
+ const seen: number[] = [];
154
+ const { valueAtom, useActions } = createReducerAndAtoms(
155
+ initialState,
156
+ reducers,
157
+ [(_prev, next) => seen.push(store.get(valueAtom).count, next.count)],
158
+ );
159
+ const store = createStore();
160
+ const wrapper = ({ children }: PropsWithChildren) => (
161
+ <Provider store={store}>{children}</Provider>
162
+ );
163
+ const { result } = renderHook(() => useActions(), { wrapper });
164
+
165
+ result.current.increment(5);
166
+
167
+ expect(seen).toEqual([5, 5]);
168
+ expect(store.get(valueAtom)).toEqual({ count: 5 });
169
+ });
170
+
142
171
  it("should log an error for non-existent action types", () => {
143
172
  const initialState = () => ({ count: 0 });
144
173
  const reducers = {
@@ -4,6 +4,9 @@ import {
4
4
  exactDateTime,
5
5
  getDateFormat,
6
6
  getShortTimeZone,
7
+ parseLocalISODate,
8
+ parseLocalISODateTime,
9
+ parseTimeToDate,
7
10
  prettyDate,
8
11
  timeAgo,
9
12
  } from "../dates";
@@ -191,6 +194,119 @@ describe("dates", () => {
191
194
  });
192
195
  });
193
196
 
197
+ describe("parseTimeToDate", () => {
198
+ it("parses 24-hour times", () => {
199
+ expect(parseTimeToDate("14:30")).toEqual(new Date(1970, 0, 1, 14, 30));
200
+ expect(parseTimeToDate("14:30:05")).toEqual(
201
+ new Date(1970, 0, 1, 14, 30, 5),
202
+ );
203
+ });
204
+
205
+ it("parses times with an am/pm suffix", () => {
206
+ expect(parseTimeToDate("2:30 PM")).toEqual(new Date(1970, 0, 1, 14, 30));
207
+ expect(parseTimeToDate("12:00 am")).toEqual(new Date(1970, 0, 1, 0, 0));
208
+ expect(parseTimeToDate("12:00 pm")).toEqual(new Date(1970, 0, 1, 12, 0));
209
+ });
210
+
211
+ it("rejects an out-of-range hour instead of letting Date normalize it", () => {
212
+ // new Date(1970, 0, 1, 25, 0) would silently become 01:00 the next day.
213
+ expect(parseTimeToDate("25:00")).toBeUndefined();
214
+ });
215
+
216
+ it("rejects an out-of-range minute instead of letting Date normalize it", () => {
217
+ // new Date(1970, 0, 1, 12, 99) would silently become 13:39.
218
+ expect(parseTimeToDate("12:99")).toBeUndefined();
219
+ });
220
+
221
+ it("rejects an out-of-range second", () => {
222
+ expect(parseTimeToDate("12:30:99")).toBeUndefined();
223
+ });
224
+
225
+ it("requires an hour from 1 to 12 with an am/pm suffix", () => {
226
+ expect(parseTimeToDate("0:30 am")).toBeUndefined();
227
+ expect(parseTimeToDate("13:30 pm")).toBeUndefined();
228
+ });
229
+
230
+ it("parses milliseconds", () => {
231
+ expect(parseTimeToDate("14:30:05.123")).toEqual(
232
+ new Date(1970, 0, 1, 14, 30, 5, 123),
233
+ );
234
+ });
235
+
236
+ it("pads a short millisecond fraction and truncates a long one", () => {
237
+ expect(parseTimeToDate("14:30:05.5")).toEqual(
238
+ new Date(1970, 0, 1, 14, 30, 5, 500),
239
+ );
240
+ expect(parseTimeToDate("14:30:05.123456")).toEqual(
241
+ new Date(1970, 0, 1, 14, 30, 5, 123),
242
+ );
243
+ });
244
+ });
245
+
246
+ describe("parseLocalISODate", () => {
247
+ it("parses a date-only string into local midnight", () => {
248
+ expect(parseLocalISODate("2024-01-15")).toEqual(new Date(2024, 0, 15));
249
+ });
250
+
251
+ it("returns undefined for a non-date-only string", () => {
252
+ expect(parseLocalISODate("2024-01-15T00:00:00")).toBeUndefined();
253
+ expect(parseLocalISODate("")).toBeUndefined();
254
+ expect(parseLocalISODate("not-a-date")).toBeUndefined();
255
+ });
256
+
257
+ it("returns undefined for a calendar date that doesn't exist", () => {
258
+ expect(parseLocalISODate("2024-02-30")).toBeUndefined();
259
+ expect(parseLocalISODate("2024-13-01")).toBeUndefined();
260
+ });
261
+ });
262
+
263
+ describe("parseLocalISODateTime", () => {
264
+ it("parses a 'T'-separated datetime into local wall-clock time", () => {
265
+ expect(parseLocalISODateTime("2024-01-15T08:30:00")).toEqual(
266
+ new Date(2024, 0, 15, 8, 30, 0),
267
+ );
268
+ });
269
+
270
+ it("parses a space-separated datetime", () => {
271
+ expect(parseLocalISODateTime("2024-01-15 08:30:00")).toEqual(
272
+ new Date(2024, 0, 15, 8, 30, 0),
273
+ );
274
+ });
275
+
276
+ it("defaults seconds to 0 when omitted", () => {
277
+ expect(parseLocalISODateTime("2024-01-15T08:30")).toEqual(
278
+ new Date(2024, 0, 15, 8, 30, 0),
279
+ );
280
+ });
281
+
282
+ it("ignores a trailing Z instead of shifting to UTC", () => {
283
+ expect(parseLocalISODateTime("2024-01-15T08:30:00Z")).toEqual(
284
+ new Date(2024, 0, 15, 8, 30, 0),
285
+ );
286
+ });
287
+
288
+ it("ignores a trailing UTC offset instead of shifting the time", () => {
289
+ expect(parseLocalISODateTime("2024-01-15T08:30:00+05:00")).toEqual(
290
+ new Date(2024, 0, 15, 8, 30, 0),
291
+ );
292
+ expect(parseLocalISODateTime("2024-01-15T08:30:00-0500")).toEqual(
293
+ new Date(2024, 0, 15, 8, 30, 0),
294
+ );
295
+ });
296
+
297
+ it("ignores sub-second precision", () => {
298
+ expect(parseLocalISODateTime("2024-01-15T08:30:00.123")).toEqual(
299
+ new Date(2024, 0, 15, 8, 30, 0),
300
+ );
301
+ });
302
+
303
+ it("returns undefined for a date-only or unparsable string", () => {
304
+ expect(parseLocalISODateTime("2024-01-15")).toBeUndefined();
305
+ expect(parseLocalISODateTime("not-a-datetime")).toBeUndefined();
306
+ expect(parseLocalISODateTime("")).toBeUndefined();
307
+ });
308
+ });
309
+
194
310
  describe("getDateFormat", () => {
195
311
  it("returns the correct format for date", () => {
196
312
  expect(getDateFormat("2023-05-15")).toBe("yyyy-MM-dd");
@@ -1,7 +1,8 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
- import { describe, expect, it } from "vitest";
2
+ import { describe, expect, it, vi } from "vitest";
3
3
  import {
4
4
  countFractionDigits,
5
+ fractionDigitsForSlider,
5
6
  maxFractionDigitsForSteps,
6
7
  prettyEngineeringNumber,
7
8
  prettyNumber,
@@ -20,6 +21,53 @@ describe("prettyNumber", () => {
20
21
 
21
22
  const options = { locale };
22
23
 
24
+ describe("formatter reuse", () => {
25
+ it.each([
26
+ {
27
+ name: "scientific",
28
+ value: 0.000_123_4,
29
+ format: (value: number, locale: string) =>
30
+ prettyScientificNumber(value, { locale }),
31
+ },
32
+ {
33
+ name: "rounded",
34
+ value: 1234.567,
35
+ format: (value: number, locale: string) =>
36
+ prettyScientificNumber(value, { locale, shouldRound: true }),
37
+ },
38
+ {
39
+ name: "engineering",
40
+ value: 12_345,
41
+ format: prettyEngineeringNumber,
42
+ },
43
+ ])(
44
+ "reuses the $name formatter until the locale changes",
45
+ ({ format, value }) => {
46
+ const NumberFormat = Intl.NumberFormat;
47
+ const constructor = vi
48
+ .spyOn(Intl, "NumberFormat")
49
+ .mockImplementation(function (locales, options) {
50
+ return new NumberFormat(locales, options);
51
+ });
52
+ try {
53
+ const first = format(value, "fr-FR");
54
+ expect(format(value, "fr-FR")).toBe(first);
55
+ format(value * 2, "fr-FR");
56
+ expect(constructor).toHaveBeenCalledTimes(1);
57
+
58
+ format(value, "de-DE");
59
+ expect(constructor).toHaveBeenCalledTimes(2);
60
+ expect(constructor).toHaveBeenLastCalledWith(
61
+ "de-DE",
62
+ expect.any(Object),
63
+ );
64
+ } finally {
65
+ constructor.mockRestore();
66
+ }
67
+ },
68
+ );
69
+ });
70
+
23
71
  describe("prettyScientificNumber", () => {
24
72
  it("should handle special cases", () => {
25
73
  expect(prettyScientificNumber(0, options)).toBe("0");
@@ -57,6 +105,42 @@ describe("prettyScientificNumber", () => {
57
105
  "1.23456789101112",
58
106
  );
59
107
  });
108
+
109
+ it("supports caller-provided display precision", () => {
110
+ expect(
111
+ prettyScientificNumber(1.2340000000000002, {
112
+ maximumFractionDigits: 2,
113
+ locale,
114
+ }),
115
+ ).toBe("1.23");
116
+ expect(
117
+ prettyScientificNumber(1.2340000000000002, {
118
+ maximumFractionDigits: 3,
119
+ locale,
120
+ }),
121
+ ).toBe("1.234");
122
+ expect(
123
+ prettyScientificNumber(1.234, {
124
+ maximumFractionDigits: 3,
125
+ locale,
126
+ }),
127
+ ).toBe("1.234");
128
+ });
129
+
130
+ it("preserves caller-provided precision in scientific notation", () => {
131
+ expect(
132
+ prettyScientificNumber(0.0012300000000000002, {
133
+ maximumFractionDigits: 5,
134
+ locale,
135
+ }),
136
+ ).toBe("1.23e-3");
137
+ expect(
138
+ prettyScientificNumber(1_000_001.2300000001, {
139
+ maximumFractionDigits: 2,
140
+ locale,
141
+ }),
142
+ ).toBe("1.00000123e6");
143
+ });
60
144
  });
61
145
 
62
146
  describe("prettyEngineeringNumber", () => {
@@ -106,3 +190,23 @@ describe("maxFractionDigitsForSteps", () => {
106
190
  expect(maxFractionDigitsForSteps([1, 2, 3, 4], 1)).toBe(0);
107
191
  });
108
192
  });
193
+
194
+ describe("fractionDigitsForSlider", () => {
195
+ it("preserves precision for regular slider steps", () => {
196
+ expect(fractionDigitsForSlider(0.001)).toBe(3);
197
+ expect(fractionDigitsForSlider(0.01)).toBe(2);
198
+ expect(fractionDigitsForSlider(undefined)).toBe(2);
199
+ expect(fractionDigitsForSlider(undefined, null, 0, [0.1234, 0.5])).toBe(4);
200
+ });
201
+
202
+ it("infers precision from custom step values", () => {
203
+ expect(fractionDigitsForSlider(undefined, [1, 1.234, 2])).toBe(3);
204
+ expect(
205
+ fractionDigitsForSlider(undefined, [0.1, 0.2, 0.30000000000000004]),
206
+ ).toBe(1);
207
+ });
208
+
209
+ it("includes the slider origin in regular step precision", () => {
210
+ expect(fractionDigitsForSlider(1, null, 0.5)).toBe(1);
211
+ });
212
+ });
@@ -204,6 +204,27 @@ describe("Objects", () => {
204
204
  });
205
205
 
206
206
  describe("filter", () => {
207
+ it.each([42, { base_url: "https://example.com" }])(
208
+ "preserves an own __proto__ property with value %j",
209
+ (value) => {
210
+ const obj = { ["__proto__"]: value, removed: value };
211
+ const result = Objects.filter(obj, (_, key) => key !== "removed");
212
+
213
+ expect(Object.entries(result)).toEqual([["__proto__", value]]);
214
+ expect(Object.getPrototypeOf(result)).toBe(Object.prototype);
215
+ expect(Object.getOwnPropertyDescriptor(result, "__proto__")).toEqual({
216
+ value,
217
+ writable: true,
218
+ enumerable: true,
219
+ configurable: true,
220
+ });
221
+ expect(Object.keys(obj)).toEqual(["__proto__", "removed"]);
222
+ expect(Objects.filter(obj, (_, key) => key !== "__proto__")).toEqual({
223
+ removed: value,
224
+ });
225
+ },
226
+ );
227
+
207
228
  it("should filter object entries by predicate", () => {
208
229
  const obj = { a: 1, b: 2, c: 3, d: 4 };
209
230
  const result = Objects.filter(obj, (v) => v % 2 === 0);
@@ -53,8 +53,17 @@ export function arrayShallowEquals<T>(a: T[], b: T[]): boolean {
53
53
  }
54
54
 
55
55
  export const Arrays = {
56
- // oxlint-disable-next-line typescript/no-explicit-any
57
- EMPTY: [] as any,
56
+ EMPTY: [],
57
+ /** Keep values present in both arrays, preserving the first array's order and duplicates. */
58
+ intersection<T>(values: readonly T[], included: readonly T[]): T[] {
59
+ const includedSet = new Set(included);
60
+ return values.filter((value) => includedSet.has(value));
61
+ },
62
+ /** Remove values present in the second array, preserving order and duplicates. */
63
+ difference<T>(values: readonly T[], excluded: readonly T[]): T[] {
64
+ const excludedSet = new Set(excluded);
65
+ return values.filter((value) => !excludedSet.has(value));
66
+ },
58
67
  zip: <T, U>(a: T[], b: U[]): [T, U][] => {
59
68
  invariant(a.length === b.length, "Arrays must be the same length");
60
69
  return a.map((item, i) => [item, b[i]]);
package/src/utils/blob.ts CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  type DataURLString,
6
6
  } from "./json/base64";
7
7
 
8
- export function serializeBlob<T>(blob: Blob): Promise<DataURLString> {
8
+ export function serializeBlob(blob: Blob): Promise<DataURLString> {
9
9
  return new Promise<DataURLString>((resolve, reject) => {
10
10
  const reader = new FileReader();
11
11
  reader.onload = (event) => {
@@ -16,21 +16,27 @@ interface ReducerHandlers<State> {
16
16
  [k: string]: ReducerHandler<State, any>;
17
17
  }
18
18
 
19
- type ReducerActions<RH extends ReducerHandlers<any>> = {
20
- [Type in keyof RH]: RH[Type] extends ReducerHandler<any, infer Payload>
19
+ type ReducerActions<RH> = {
20
+ [Type in keyof RH]: RH[Type] extends (
21
+ state: never,
22
+ payload: infer Payload,
23
+ ) => unknown
21
24
  ? IfUnknown<Payload, () => void, (payload: Payload) => void>
22
25
  : never;
23
26
  };
24
27
 
25
28
  /** Helper for typing middleware that receives dispatched actions. */
26
29
  export type DispatchedActionOf<T> = {
27
- [Key in keyof T]: T[Key] extends (payload: infer P) => any
30
+ [Key in keyof T]: T[Key] extends (payload: infer P) => unknown
28
31
  ? { type: Key; payload: P }
29
32
  : never;
30
33
  }[keyof T & string];
31
34
 
32
35
  type ReducerActionOf<RH> = {
33
- [Type in keyof RH]: RH[Type] extends ReducerHandler<any, infer Payload>
36
+ [Type in keyof RH]: RH[Type] extends (
37
+ state: never,
38
+ payload: infer Payload,
39
+ ) => unknown
34
40
  ? { type: Type; payload: Payload }
35
41
  : never;
36
42
  }[keyof RH & string];
@@ -42,7 +48,7 @@ export interface ReducerCreatorResult<
42
48
  /**
43
49
  * The reducer function.
44
50
  */
45
- reducer: Reducer<State, any>;
51
+ reducer: Reducer<State, ReducerActionOf<RH>>;
46
52
  /**
47
53
  * Given a dispatch function, returns an object containing all the actions.
48
54
  */
@@ -98,49 +104,70 @@ export function createReducerAndAtoms<
98
104
  };
99
105
  const { reducer, createActions } = createReducer(initialState, reducers);
100
106
 
101
- const reducerWithMiddleware = (state: State, action: ReducerActionOf<RH>) => {
107
+ const reduceSafely = (state: State, action: ReducerActionOf<RH>) => {
102
108
  try {
103
- const newState = reducer(state, action);
104
- for (const mw of allMiddleware) {
105
- try {
106
- mw(state, newState, action);
107
- } catch (error) {
108
- Logger.error(`Error in middleware for action ${action.type}:`, error);
109
- }
110
- }
111
- return newState;
109
+ return reducer(state, action);
112
110
  } catch (error) {
113
111
  Logger.error(`Error in reducer for action ${action.type}:`, error);
114
112
  return state;
115
113
  }
116
114
  };
117
115
 
116
+ const runMiddleware = (
117
+ prev: State,
118
+ next: State,
119
+ action: ReducerActionOf<RH>,
120
+ ) => {
121
+ for (const mw of allMiddleware) {
122
+ try {
123
+ mw(prev, next, action);
124
+ } catch (error) {
125
+ Logger.error(`Error in middleware for action ${action.type}:`, error);
126
+ }
127
+ }
128
+ };
129
+
130
+ const reducerWithMiddleware = (state: State, action: ReducerActionOf<RH>) => {
131
+ const next = reduceSafely(state, action);
132
+ runMiddleware(state, next, action);
133
+ return next;
134
+ };
135
+
118
136
  const valueAtom = atom(initialState());
119
- // map of SetAtom => Actions
137
+ // map of SetAtom => Actions, with and without middleware
120
138
  const actionsMap = new WeakMap();
139
+ const plainActionsMap = new WeakMap();
121
140
 
122
141
  function useActions(
123
142
  options: { skipMiddleware?: boolean } = {},
124
143
  ): ReducerActions<RH> {
125
144
  const setState = useSetAtom(valueAtom);
145
+ const skipMiddleware = options.skipMiddleware === true;
146
+ const cache = skipMiddleware ? plainActionsMap : actionsMap;
126
147
 
127
- if (options.skipMiddleware === true) {
128
- return createActions((action: ReducerActionOf<RH>) => {
129
- setState((state: State) => reducer(state, action));
130
- });
131
- }
132
-
133
- if (!actionsMap.has(setState)) {
134
- actionsMap.set(
148
+ if (!cache.has(setState)) {
149
+ cache.set(
135
150
  setState,
136
151
  createActions((action: ReducerActionOf<RH>) => {
137
- setState((state: State) => reducerWithMiddleware(state, action));
152
+ // Reduce inside the write. Middleware runs once the write has
153
+ // committed and its subscribers have run, so anything it sets in
154
+ // turn observes the new state rather than the one being replaced.
155
+ let prev: State | undefined;
156
+ let next: State | undefined;
157
+ setState((state: State) => {
158
+ prev = state;
159
+ next = reduceSafely(state, action);
160
+ return next;
161
+ });
162
+ if (!skipMiddleware && prev !== undefined && next !== undefined) {
163
+ runMiddleware(prev, next, action);
164
+ }
138
165
  }),
139
166
  );
140
167
  }
141
168
 
142
169
  // oxlint-disable-next-line typescript/no-non-null-assertion
143
- return actionsMap.get(setState)!;
170
+ return cache.get(setState)!;
144
171
  }
145
172
 
146
173
  return {
@@ -165,6 +165,44 @@ export function dateToLocalISOTime(d: Date): string {
165
165
  return `${pad2(d.getHours())}:${pad2(d.getMinutes())}:${pad2(d.getSeconds())}`;
166
166
  }
167
167
 
168
+ /**
169
+ * Parse a time string into a Date anchored at the Unix epoch day. Supports
170
+ * the backend's wire format (`HH:MM:SS`, as produced by `dateToLocalISOTime`)
171
+ * as well as looser pasted text: optional seconds, optional milliseconds,
172
+ * and an optional `am`/`pm` suffix (e.g. `"14:30"`, `"2:30 PM"`,
173
+ * `"14:30:00.123"`).
174
+ */
175
+ export function parseTimeToDate(value: string): Date | undefined {
176
+ const match = /^(\d{1,2}):(\d{2})(?::(\d{2}))?(?:\.(\d+))?\s*(am|pm)?$/i.exec(
177
+ value.trim(),
178
+ );
179
+ if (!match) {
180
+ return undefined;
181
+ }
182
+ const [, hStr, mStr, sStr, msStr, ampm] = match;
183
+ let hour = Number.parseInt(hStr, 10);
184
+ const minute = Number.parseInt(mStr, 10);
185
+ const second = sStr ? Number.parseInt(sStr, 10) : 0;
186
+ const ms = msStr ? Number.parseInt(msStr.padEnd(3, "0").slice(0, 3), 10) : 0;
187
+ if (minute > 59 || second > 59) {
188
+ return undefined;
189
+ }
190
+ if (ampm) {
191
+ if (hour < 1 || hour > 12) {
192
+ return undefined;
193
+ }
194
+ const isPm = ampm.toLowerCase() === "pm";
195
+ if (hour === 12) {
196
+ hour = isPm ? 12 : 0;
197
+ } else if (isPm) {
198
+ hour += 12;
199
+ }
200
+ } else if (hour > 23) {
201
+ return undefined;
202
+ }
203
+ return new Date(1970, 0, 1, hour, minute, second, ms);
204
+ }
205
+
168
206
  /**
169
207
  * Format a Date as `YYYY-MM-DDTHH:MM:SS` (no timezone suffix) using local
170
208
  * fields. See `dateToLocalISODate` for the rationale on local vs UTC.
@@ -173,6 +211,62 @@ export function dateToLocalISODateTime(d: Date): string {
173
211
  return `${dateToLocalISODate(d)}T${dateToLocalISOTime(d)}`;
174
212
  }
175
213
 
214
+ /**
215
+ * Parse a `YYYY-MM-DD` string into a Date anchored at local midnight.
216
+ *
217
+ * Unlike `new Date("YYYY-MM-DD")`, which the spec parses as UTC midnight,
218
+ * this treats the string as a calendar date in the viewer's own timezone.
219
+ * Returns undefined if the string doesn't match, or encodes a calendar
220
+ * date that doesn't exist (e.g. month 13, Feb 30), since `Date` would
221
+ * otherwise silently roll those over into a different date.
222
+ */
223
+ export function parseLocalISODate(value: string): Date | undefined {
224
+ const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value.trim());
225
+ if (!match) {
226
+ return undefined;
227
+ }
228
+ const [, yStr, mStr, dStr] = match;
229
+ const year = Number.parseInt(yStr, 10);
230
+ const month = Number.parseInt(mStr, 10);
231
+ const day = Number.parseInt(dStr, 10);
232
+ const date = new Date(year, month - 1, day);
233
+ if (
234
+ date.getFullYear() !== year ||
235
+ date.getMonth() !== month - 1 ||
236
+ date.getDate() !== day
237
+ ) {
238
+ return undefined;
239
+ }
240
+ return date;
241
+ }
242
+
243
+ /**
244
+ * Parse an ISO-ish `YYYY-MM-DD[T ]HH:MM[:SS[.sss]][Z|+HH:MM]` datetime string
245
+ * as wall-clock local time, ignoring any trailing `Z` or UTC offset.
246
+ *
247
+ * This keeps pasted datetimes consistent with the date/time pickers' local-
248
+ * time basis: pasting `2024-01-15T08:30:00Z` displays `08:30` instead of
249
+ * being shifted by the viewer's timezone.
250
+ */
251
+ export function parseLocalISODateTime(value: string): Date | undefined {
252
+ const match =
253
+ /^(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2})(?::(\d{2})(?:\.\d+)?)?(?:Z|[+-]\d{2}:?\d{2})?$/.exec(
254
+ value.trim(),
255
+ );
256
+ if (!match) {
257
+ return undefined;
258
+ }
259
+ const [, yStr, moStr, dStr, hStr, miStr, sStr] = match;
260
+ return new Date(
261
+ Number.parseInt(yStr, 10),
262
+ Number.parseInt(moStr, 10) - 1,
263
+ Number.parseInt(dStr, 10),
264
+ Number.parseInt(hStr, 10),
265
+ Number.parseInt(miStr, 10),
266
+ sStr ? Number.parseInt(sStr, 10) : 0,
267
+ );
268
+ }
269
+
176
270
  export const supportedDateFormats = ["yyyy", "yyyy-MM", "yyyy-MM-dd"] as const;
177
271
  export type DateFormat = (typeof supportedDateFormats)[number];
178
272
 
package/src/utils/maps.ts CHANGED
@@ -1,10 +1,7 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
- /* oxlint-disable typescript/no-explicit-any */
3
2
 
4
3
  import { Logger } from "./Logger";
5
4
 
6
- type NoInfer<T> = [T][T extends any ? 0 : never];
7
-
8
5
  export const Maps = {
9
6
  /**
10
7
  * keyBy for Map, with duplicate key detection.