@marimo-team/islands 0.25.3-dev18 → 0.25.3-dev182

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 (618) hide show
  1. package/dist/{ConnectedDataExplorerComponent-CNMliVzw.js → ConnectedDataExplorerComponent-Le_OBfmt.js} +21 -19
  2. package/dist/{ErrorBoundary-DP07HKNC.js → ErrorBoundary-CcSbeila.js} +19 -19
  3. package/dist/{abnfDiagram-VCTEODGH-D-Ez91gV.js → abnfDiagram-VCTEODGH-Tf1ghyQQ.js} +2 -2
  4. package/dist/any-language-editor-BVFHDDQ3.js +75 -0
  5. package/dist/{architectureDiagram-5GKGNRK7-CTpcNtO8.js → architectureDiagram-5GKGNRK7-xlnbTG0T.js} +1 -1
  6. package/dist/{arrays-CN1djC4k.js → arrays-R6JsImiT.js} +8 -0
  7. package/dist/assets/__vite-browser-external-DVeFf2e7.js +1 -0
  8. package/dist/assets/worker-C1pfNBsx.js +113 -0
  9. package/dist/{button-CvjkwdSk.js → button-BQRdqQi1.js} +199 -192
  10. package/dist/{capabilities-CQ5IjhzL.js → capabilities-CI4xUPsp.js} +2 -2
  11. package/dist/{chat-ui-DaHynk8g.js → chat-ui-X-cWrL9u.js} +541 -540
  12. package/dist/{check-EDwSwP89.js → check-C9Mp9eCK.js} +1 -1
  13. package/dist/{chunk-IMKFNOWR-DGxi-zeo.js → chunk-IMKFNOWR-Cmd4a5jy.js} +1 -1
  14. package/dist/{chunk-SHT3W25Y-CV9J3rAB.js → chunk-SHT3W25Y-mSNp-WpO.js} +1 -1
  15. package/dist/{chunk-SVP7TREG-BL-9Fqgg.js → chunk-SVP7TREG-CWC49D8X.js} +1 -1
  16. package/dist/{chunk-TICWLB2K-Cu_XMjDM.js → chunk-TICWLB2K-De9FINtG.js} +1 -1
  17. package/dist/{chunk-YOKDWASO-DPZk8vhS.js → chunk-YOKDWASO-DKplTrBY.js} +20 -20
  18. package/dist/{classDiagram-ZZMXUADV-BXXV13Hq.js → classDiagram-ZZMXUADV-DweTkYdp.js} +2 -2
  19. package/dist/{classDiagram-v2-VYDZK3BY-B7FXJ1Cp.js → classDiagram-v2-VYDZK3BY-A6MR2TiE.js} +2 -2
  20. package/dist/{code-visibility-hTy956cB.js → code-visibility-BbBLg2nW.js} +25 -25
  21. package/dist/{common-CiockEHa.js → common-BuwBk7_9.js} +6994 -6819
  22. package/dist/{copy-icon-Ft7t9bqz.js → copy-icon-De584pZ4.js} +3 -3
  23. package/dist/{cynefinDiagram-5FMLGOSQ-CH96rwba.js → cynefinDiagram-5FMLGOSQ-BSvo5le-.js} +1 -1
  24. package/dist/{dagre-GXQ25YYZ-BD3PmpFY.js → dagre-GXQ25YYZ-tXRQslO6.js} +1 -1
  25. package/dist/{data-grid-overlay-editor-XsGuhhu5.js → data-grid-overlay-editor-Bt_qy-zy.js} +5 -5
  26. package/dist/{data-grid-types-BH_r7g7l.js → data-grid-types-0PJ-9N5n.js} +1 -1
  27. package/dist/{diagram-S7CK7UJ4-BsSJJldB.js → diagram-S7CK7UJ4-r3t5VtwI.js} +1 -1
  28. package/dist/{diagram-UQ7AKVKN-muQtNZc4.js → diagram-UQ7AKVKN-qgbC8YnT.js} +1 -1
  29. package/dist/{diagram-VX7I27RA-CdhuZAqR.js → diagram-VX7I27RA-CiHxEwtp.js} +1 -1
  30. package/dist/{diagram-Z3DM3KII-BD8mXjOj.js → diagram-Z3DM3KII-D95u15Ev.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-DCMbbQ35.js} +1481 -1433
  58. package/dist/{ebnfDiagram-PWID7BFC-BIN1sweE.js → ebnfDiagram-PWID7BFC-P7e4bryK.js} +2 -2
  59. package/dist/{erDiagram-RLTQ6QDP-Ctp5cfoN.js → erDiagram-RLTQ6QDP-CUHZcmRj.js} +1 -1
  60. package/dist/{error-banner-D-WCIiMt.js → error-banner-DxJ3s8YO.js} +86 -86
  61. package/dist/{error-entries-Bv6bZPF3.js → error-entries-mpoD5t79.js} +4771 -4702
  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-CZNhgq7U.js → feedback-button-CuRYTzzX.js} +37 -37
  65. package/dist/{flowDiagram-HODETNUW-3EfLrlFb.js → flowDiagram-HODETNUW-BGZ95AzI.js} +2 -2
  66. package/dist/{formats-27CW_UP4.js → formats-DI7HEVv7.js} +3 -3
  67. package/dist/{ganttDiagram-EL5Y4UJY-BQT16JSc.js → ganttDiagram-EL5Y4UJY-Dh_4cBJZ.js} +2 -2
  68. package/dist/{glide-data-editor-DNPChdXf.js → glide-data-editor-vvFmzxva.js} +370 -368
  69. package/dist/{glide-utils-DJyqh0EM.js → glide-utils-D98laidb.js} +16 -16
  70. package/dist/{globals-DcKi3Zmn.js → globals-CIofsJK9.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-Bf8QMGi4.js → html-to-image-BoZthB4P.js} +668 -622
  73. package/dist/{infoDiagram-27XIBGKW-DipQoHxl.js → infoDiagram-27XIBGKW-BF0YX70D.js} +1 -1
  74. package/dist/{ishikawaDiagram-5VMMS53U-DgFMjSGz.js → ishikawaDiagram-5VMMS53U-_itsAAn-.js} +1 -1
  75. package/dist/{kanban-definition-UXKFOSKX-w1ACVjgl.js → kanban-definition-UXKFOSKX-VNW2Beqh.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 +9822 -7824
  80. package/dist/{mermaid-CHo49Yv-.js → mermaid-CZ6FrClS.js} +38 -38
  81. package/dist/mermaid-HWGCJPDP-Du3-HPQA.js +2 -0
  82. package/dist/{mindmap-definition-YA3MSWOX-CrT0KSfX.js → mindmap-definition-YA3MSWOX-DR4ripvd.js} +1 -1
  83. package/dist/{number-overlay-editor-GnpmNxbw.js → number-overlay-editor-BiEkIBfV.js} +3 -3
  84. package/dist/{objects-CzUgoMIF.js → objects-DCmMvZtn.js} +1 -3
  85. package/dist/{once-BtE-wKRL.js → once-eG8Vmvjd.js} +1 -1
  86. package/dist/{pegDiagram-XKGWAZYB-BqvywNJA.js → pegDiagram-XKGWAZYB-CFr3XlGl.js} +2 -2
  87. package/dist/{pieDiagram-E7YTZNPT-BfxJ_XCc.js → pieDiagram-E7YTZNPT-j91XYSLY.js} +1 -1
  88. package/dist/precisionRound-DhAzeRcm.js +49 -0
  89. package/dist/{process-output-BZg56CCb.js → process-output-DLS4Mqg_.js} +4 -4
  90. package/dist/{quadrantDiagram-AXDQQJYC-Ccw0xMjN.js → quadrantDiagram-AXDQQJYC-Blrm7V7_.js} +1 -1
  91. package/dist/{railroadDiagram-O6MQD6OU-8vFLVTPu.js → railroadDiagram-O6MQD6OU-Cd47ZV9E.js} +2 -2
  92. package/dist/{react-vega-BED0DjiB.js → react-vega-C4T9Rb_w.js} +12284 -12270
  93. package/dist/{requirementDiagram-BXWQKSXE-a_MxGvxe.js → requirementDiagram-BXWQKSXE-eJH6gLtX.js} +1 -1
  94. package/dist/{reveal-component-B1Wbl-hd.js → reveal-component-C0zVfY6v.js} +14 -14
  95. package/dist/{select-BButFi-d.js → select-2KOiReNY.js} +536 -457
  96. package/dist/{sequenceDiagram-WJ2MYXX4-CXFMnxGh.js → sequenceDiagram-WJ2MYXX4-DdfFEIrm.js} +1 -1
  97. package/dist/{spec-BwarBpUJ.js → spec-h69nC3SI.js} +41 -11
  98. package/dist/{stateDiagram-D77RDMKH-DQeA_CwI.js → stateDiagram-D77RDMKH-DkjDvip8.js} +2 -2
  99. package/dist/{stateDiagram-v2-MP3YSRHH-BxxQucm1.js → stateDiagram-v2-MP3YSRHH-DBHACnOo.js} +2 -2
  100. package/dist/style.css +1 -1
  101. package/dist/{swimlanes-42K2YHIH-Do6L-cYg.js → swimlanes-42K2YHIH-ogImlOmp.js} +1 -1
  102. package/dist/{swimlanesDiagram-VR7AAH4N-DUzPvSVU.js → swimlanesDiagram-VR7AAH4N-D6x-zsci.js} +2 -2
  103. package/dist/{swiper-component-DKntyn8p.js → swiper-component-BkCm-kLg.js} +2 -2
  104. package/dist/{time-BieKwdYS.js → time-BaMbJs4z.js} +18 -18
  105. package/dist/{timeline-definition-24CTP7MA-D3c46WM5.js → timeline-definition-24CTP7MA-DQpLtsHD.js} +1 -1
  106. package/dist/{toDate-UtiwmnH2.js → toDate-CDzoofJo.js} +37 -36
  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-Bn8SxUqM.js} +15 -14
  111. package/dist/{useDateFormatter-D8XruNGG.js → useDateFormatter-D3Tkfzej.js} +2 -2
  112. package/dist/{useDeepCompareMemoize-DXwzRINI.js → useDeepCompareMemoize-CC2tnPCk.js} +1 -1
  113. package/dist/{useIframeCapabilities-DBETvvPl.js → useIframeCapabilities-CSdWn6NP.js} +1 -1
  114. package/dist/{useLifecycle-j7D9uD1o.js → useLifecycle-BJlxHqWa.js} +28 -25
  115. package/dist/{useTheme-CRnjkku0.js → useTheme-DMfQYmlG.js} +301 -297
  116. package/dist/{vega-component-BeF9dfyO.js → vega-component-CovWHi60.js} +148 -148
  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-BSZD8JcZ.js → vennDiagram-4TSXK5OY-BD-SL_vq.js} +1 -1
  120. package/dist/{wardleyDiagram-VM6X3IG4-48cUWkfA.js → wardleyDiagram-VM6X3IG4-BDgJV4ah.js} +1 -1
  121. package/dist/{xychartDiagram-S5SC5T6Z-CswYgYeu.js → xychartDiagram-S5SC5T6Z-1hIHN6Rl.js} +2 -2
  122. package/package.json +16 -16
  123. package/src/__mocks__/document.tsx +54 -0
  124. package/src/__mocks__/notebook.ts +2 -1
  125. package/src/__mocks__/requests.ts +0 -3
  126. package/src/__tests__/module-entry.test.ts +44 -0
  127. package/src/__tests__/mount.test.ts +1 -21
  128. package/src/__tests__/{host-imports.test.ts → replica-imports.test.ts} +12 -11
  129. package/src/components/ai/__tests__/ai-model-dropdown.test.tsx +63 -0
  130. package/src/components/ai/ai-model-dropdown.tsx +11 -6
  131. package/src/components/app-config/ai-config.tsx +7 -2
  132. package/src/components/app-config/user-config-form.tsx +35 -0
  133. package/src/components/chat/__tests__/message-queue.test.tsx +17 -0
  134. package/src/components/chat/acp/__tests__/session-tabs.test.tsx +43 -0
  135. package/src/components/chat/acp/__tests__/state.test.ts +2 -2
  136. package/src/components/chat/acp/blocks.tsx +3 -2
  137. package/src/components/chat/acp/session-tabs.tsx +8 -3
  138. package/src/components/chat/chat-components.tsx +2 -0
  139. package/src/components/chat/chat-panel.tsx +12 -11
  140. package/src/components/chat/chat-utils.ts +4 -8
  141. package/src/components/data-table/TableBottomBar.tsx +4 -1
  142. package/src/components/data-table/TableTopBar.tsx +2 -0
  143. package/src/components/data-table/__tests__/chart-spec-model.test.ts +195 -195
  144. package/src/components/data-table/__tests__/column-explorer.test.tsx +27 -1
  145. package/src/components/data-table/__tests__/columns.test.tsx +105 -0
  146. package/src/components/data-table/__tests__/context-menu-interaction.test.tsx +130 -0
  147. package/src/components/data-table/__tests__/data-table.test.tsx +1 -0
  148. package/src/components/data-table/__tests__/date-popover.test.tsx +72 -0
  149. package/src/components/data-table/__tests__/export-actions.test.tsx +105 -0
  150. package/src/components/data-table/__tests__/keyboard-selection.test.tsx +188 -0
  151. package/src/components/data-table/__tests__/pagination.test.tsx +28 -1
  152. package/src/components/data-table/__tests__/search-highlight.test.tsx +109 -0
  153. package/src/components/data-table/__tests__/url-detector.test.tsx +74 -0
  154. package/src/components/data-table/cell-selection/feature.ts +2 -2
  155. package/src/components/data-table/cell-selection/types.ts +2 -2
  156. package/src/components/data-table/charts/__tests__/chart-panel.test.tsx +74 -1
  157. package/src/components/data-table/charts/__tests__/spec-snapshot.test.ts +20 -20
  158. package/src/components/data-table/charts/__tests__/spec.test.ts +78 -52
  159. package/src/components/data-table/charts/__tests__/storage.test.ts +4 -1
  160. package/src/components/data-table/charts/chart-spec/spec.ts +68 -43
  161. package/src/components/data-table/charts/charts.tsx +40 -30
  162. package/src/components/data-table/charts/storage.ts +16 -5
  163. package/src/components/data-table/column-explorer-panel/column-explorer.tsx +1 -1
  164. package/src/components/data-table/column-summary/chart-spec-model.tsx +23 -16
  165. package/src/components/data-table/column-summary/column-summary.tsx +2 -4
  166. package/src/components/data-table/column-visibility-dropdown.tsx +8 -13
  167. package/src/components/data-table/columns.tsx +45 -46
  168. package/src/components/data-table/context-menu.tsx +36 -20
  169. package/src/components/data-table/data-table.tsx +17 -1
  170. package/src/components/data-table/date-popover.tsx +6 -4
  171. package/src/components/data-table/export-actions.tsx +76 -48
  172. package/src/components/data-table/pagination.tsx +44 -33
  173. package/src/components/data-table/range-focus/atoms.ts +4 -6
  174. package/src/components/data-table/range-focus/use-cell-range-selection.ts +65 -38
  175. package/src/components/data-table/range-focus/use-scroll-into-view.ts +21 -2
  176. package/src/components/data-table/renderers.tsx +16 -7
  177. package/src/components/data-table/row-viewer-panel/__tests__/row-viewer.test.tsx +24 -1
  178. package/src/components/data-table/row-viewer-panel/row-viewer.tsx +4 -4
  179. package/src/components/data-table/search-highlight.tsx +79 -0
  180. package/src/components/data-table/types.ts +1 -0
  181. package/src/components/data-table/url-detector.tsx +49 -9
  182. package/src/components/datasources/__tests__/column-actions.test.tsx +97 -0
  183. package/src/components/datasources/__tests__/column-preview.test.tsx +96 -5
  184. package/src/components/datasources/__tests__/datasources-interactions.test.tsx +181 -0
  185. package/src/components/datasources/__tests__/table-list.test.tsx +118 -0
  186. package/src/components/datasources/__tests__/utils.test.ts +57 -6
  187. package/src/components/datasources/column-preview.tsx +17 -12
  188. package/src/components/datasources/datasources.tsx +55 -42
  189. package/src/components/datasources/utils.ts +32 -23
  190. package/src/components/debugger/debugger-code.tsx +5 -0
  191. package/src/components/dependency-graph/dependency-graph-tree.tsx +8 -8
  192. package/src/components/dependency-graph/elements.ts +24 -21
  193. package/src/components/dependency-graph/minimap-content.tsx +2 -6
  194. package/src/components/dependency-graph/panels.tsx +4 -2
  195. package/src/components/dependency-graph/utils/__tests__/layout.test.ts +18 -11
  196. package/src/components/editor/SortableCell.tsx +1 -3
  197. package/src/components/editor/__tests__/Output.test.tsx +50 -32
  198. package/src/{core/attachments/__tests__/toast.test.tsx → components/editor/__tests__/agent-toast.test.tsx} +1 -1
  199. package/src/components/editor/__tests__/data-attributes.test.tsx +28 -13
  200. package/src/components/editor/__tests__/dynamic-favicon.test.tsx +111 -18
  201. package/src/components/editor/__tests__/output-persistence.test.tsx +6 -1
  202. package/src/components/editor/__tests__/package-alert.test.tsx +22 -1
  203. package/src/components/editor/actions/__tests__/name-cell-input.test.tsx +69 -0
  204. package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +34 -8
  205. package/src/components/editor/actions/__tests__/pair-with-agent.test.tsx +2 -6
  206. package/src/components/editor/actions/name-cell-input.tsx +4 -3
  207. package/src/components/editor/actions/pair-with-agent-commands.ts +2 -2
  208. package/src/components/editor/actions/pair-with-agent-modal.tsx +19 -16
  209. package/src/components/editor/actions/useCellActionButton.tsx +20 -26
  210. package/src/components/editor/actions/useNotebookActions.tsx +14 -19
  211. package/src/components/editor/actions/useRestartKernel.tsx +1 -1
  212. package/src/components/editor/actions/useSetCodeVisibility.ts +10 -11
  213. package/src/components/editor/actions/useSetOutputsExpanded.ts +13 -18
  214. package/src/components/editor/ai/add-cell-with-ai.tsx +3 -3
  215. package/src/components/editor/ai/ai-completion-editor.tsx +1 -0
  216. package/src/components/editor/alerts/__tests__/connection-notice.test.tsx +1 -1
  217. package/src/components/editor/alerts/connection-notice.tsx +1 -1
  218. package/src/components/editor/alerts/startup-logs-alert.tsx +1 -0
  219. package/src/components/editor/app-container.tsx +1 -1
  220. package/src/components/editor/boundary/__tests__/ErrorBoundary.test.tsx +1 -1
  221. package/src/components/editor/cell/CreateCellButton.tsx +15 -21
  222. package/src/components/editor/cell/DeleteButton.tsx +3 -2
  223. package/src/components/editor/cell/RunButton.tsx +2 -2
  224. package/src/components/editor/cell/StagedAICell.tsx +10 -2
  225. package/src/components/editor/cell/StopButton.tsx +2 -2
  226. package/src/components/editor/cell/__tests__/StagedAICell.test.tsx +3 -1
  227. package/src/components/editor/cell/__tests__/useShouldShowInterrupt.test.ts +39 -0
  228. package/src/components/editor/cell/code/__tests__/language-toggle.test.tsx +54 -0
  229. package/src/components/editor/cell/code/cell-editor.tsx +22 -36
  230. package/src/components/editor/cell/code/language-toggle.tsx +8 -7
  231. package/src/components/editor/cell/collapse.tsx +5 -3
  232. package/src/components/editor/cell/useAddCell.ts +4 -8
  233. package/src/components/editor/cell/useDeleteCell.tsx +10 -36
  234. package/src/components/editor/cell/useRunCells.ts +2 -5
  235. package/src/components/editor/cell/useShouldShowInterrupt.ts +2 -12
  236. package/src/components/editor/cell/useSplitCell.tsx +2 -1
  237. package/src/components/editor/chrome/components/__tests__/feedback-button.test.tsx +1 -1
  238. package/src/components/editor/chrome/components/feedback-button.tsx +1 -1
  239. package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +31 -2
  240. package/src/components/editor/chrome/panels/__tests__/runtime-details.test.tsx +1 -1
  241. package/src/components/editor/chrome/panels/__tests__/sandbox-recovery.test.tsx +1 -1
  242. package/src/components/editor/chrome/panels/cache-panel.tsx +1 -0
  243. package/src/components/editor/chrome/panels/logs-panel.tsx +2 -1
  244. package/src/components/editor/chrome/panels/outline/__tests__/outline-keyboard.test.tsx +84 -0
  245. package/src/components/editor/chrome/panels/outline/floating-outline.tsx +39 -14
  246. package/src/components/editor/chrome/panels/outline/useActiveOutline.tsx +3 -2
  247. package/src/components/editor/chrome/panels/packages-panel.tsx +4 -7
  248. package/src/components/editor/chrome/panels/secrets-panel.tsx +1 -0
  249. package/src/components/editor/chrome/panels/snippets-panel.tsx +10 -12
  250. package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +9 -18
  251. package/src/components/editor/chrome/wrapper/__tests__/document-sync-status.test.tsx +92 -0
  252. package/src/components/editor/chrome/wrapper/__tests__/pending-ai-cells.test.tsx +61 -2
  253. package/src/components/editor/chrome/wrapper/footer-items/attachment-status.tsx +4 -4
  254. package/src/components/editor/chrome/wrapper/footer-items/backend-status.tsx +1 -1
  255. package/src/components/editor/chrome/wrapper/footer-items/document-sync-status.tsx +54 -0
  256. package/src/components/editor/chrome/wrapper/footer-items/lsp-status.tsx +1 -1
  257. package/src/components/editor/chrome/wrapper/footer-items/machine-stats.tsx +1 -1
  258. package/src/components/editor/chrome/wrapper/footer-items/runtime-settings.tsx +1 -7
  259. package/src/components/editor/chrome/wrapper/footer.tsx +2 -0
  260. package/src/components/editor/chrome/wrapper/pending-ai-cells.tsx +12 -2
  261. package/src/components/editor/chrome/wrapper/storage.ts +1 -1
  262. package/src/components/editor/columns/sortable-column.tsx +1 -1
  263. package/src/components/editor/connections/components.tsx +5 -10
  264. package/src/components/editor/controls/Controls.tsx +3 -3
  265. package/src/components/editor/controls/__tests__/command-palette.test.tsx +53 -0
  266. package/src/components/editor/controls/__tests__/keyboard-shortcuts.test.tsx +61 -0
  267. package/src/components/editor/controls/__tests__/notebook-menu-dropdown.test.tsx +10 -4
  268. package/src/components/editor/controls/command-palette.tsx +22 -10
  269. package/src/components/editor/controls/keyboard-shortcuts.tsx +14 -5
  270. package/src/components/editor/documentation.css +34 -11
  271. package/src/components/editor/dynamic-favicon.tsx +21 -40
  272. package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +9 -23
  273. package/src/components/editor/errors/auto-fix.tsx +4 -8
  274. package/src/components/editor/errors/send-error-report-button.tsx +3 -6
  275. package/src/components/editor/file-tree/__tests__/file-explorer-interactions.test.tsx +6 -0
  276. package/src/components/editor/file-tree/__tests__/file-explorer-navigation.test.tsx +47 -0
  277. package/src/components/editor/file-tree/__tests__/use-file-search.test.tsx +91 -0
  278. package/src/components/editor/file-tree/file-explorer.tsx +48 -15
  279. package/src/components/editor/file-tree/file-name-input.tsx +1 -0
  280. package/src/components/editor/file-tree/file-viewer.tsx +4 -2
  281. package/src/components/editor/file-tree/use-file-search.ts +35 -21
  282. package/src/components/editor/header/__tests__/status.test.tsx +5 -3
  283. package/src/components/editor/header/app-header.tsx +1 -1
  284. package/src/components/editor/header/status.tsx +40 -20
  285. package/src/components/editor/links/__tests__/cell-link.test.tsx +148 -0
  286. package/src/components/editor/links/cell-link.tsx +16 -6
  287. package/src/components/editor/navigation/__tests__/clipboard.test.ts +60 -38
  288. package/src/components/editor/navigation/__tests__/navigation.test.ts +73 -64
  289. package/src/components/editor/navigation/clipboard.ts +12 -11
  290. package/src/components/editor/navigation/focus-utils.ts +4 -4
  291. package/src/components/editor/navigation/multi-cell-action-toolbar.tsx +11 -20
  292. package/src/components/editor/navigation/navigation.ts +20 -28
  293. package/src/components/editor/notebook-banner.tsx +1 -0
  294. package/src/components/editor/notebook-cell.tsx +19 -13
  295. package/src/components/editor/output/MarimoErrorOutput.tsx +2 -1
  296. package/src/components/editor/output/MarimoTracebackOutput.tsx +19 -17
  297. package/src/components/editor/output/Outputs.css +8 -0
  298. package/src/components/editor/output/__tests__/traceback.test.tsx +27 -1
  299. package/src/components/editor/output/ansi-reduce.ts +33 -20
  300. package/src/components/editor/output/console/ConsoleOutput.tsx +19 -25
  301. package/src/components/editor/output/console/__tests__/ConsoleOutput.test.tsx +125 -69
  302. package/src/components/editor/package-alert.tsx +14 -7
  303. package/src/components/editor/renderers/cell-array.tsx +10 -17
  304. package/src/components/editor/renderers/grid-layout/grid-layout.tsx +12 -6
  305. package/src/components/editor/renderers/vertical-layout/sidebar/toggle.tsx +7 -1
  306. package/src/components/home/components.tsx +17 -31
  307. package/src/components/icons/__tests__/large-spinner.test.tsx +39 -0
  308. package/src/components/icons/large-spinner.tsx +3 -14
  309. package/src/components/icons/spinner.tsx +1 -1
  310. package/src/components/markdown/markdown-renderer.tsx +4 -8
  311. package/src/components/mcp/mcp-status-indicator.tsx +1 -1
  312. package/src/components/pages/__tests__/home-page.test.tsx +116 -0
  313. package/src/components/pages/edit-page.tsx +32 -17
  314. package/src/components/pages/home-page.tsx +164 -91
  315. package/src/components/scratchpad/scratchpad.test.tsx +97 -0
  316. package/src/components/scratchpad/scratchpad.tsx +20 -10
  317. package/src/components/slides/__tests__/minimap-actions.test.tsx +9 -25
  318. package/src/components/slides/minimap.tsx +7 -3
  319. package/src/components/slides/reveal-component.tsx +4 -2
  320. package/src/components/slides/slide-cell-view.tsx +4 -4
  321. package/src/components/slides/slide-form.tsx +1 -0
  322. package/src/components/sort/SortableCellsProvider.tsx +2 -1
  323. package/src/components/tracing/tracing-keyboard.test.tsx +85 -0
  324. package/src/components/tracing/tracing.tsx +45 -41
  325. package/src/components/ui/__tests__/draggable-popover.test.tsx +24 -0
  326. package/src/components/ui/__tests__/range-slider.test.tsx +165 -0
  327. package/src/components/ui/alert-dialog.tsx +1 -1
  328. package/src/components/ui/draggable-popover.tsx +24 -2
  329. package/src/components/ui/range-slider.tsx +22 -9
  330. package/src/core/ai/__tests__/staged-cells.test.ts +26 -25
  331. package/src/core/ai/context/providers/datasource.ts +6 -2
  332. package/src/core/ai/staged-cells.ts +15 -18
  333. package/src/core/ai/tools/__tests__/edit-notebook-tool.test.ts +5 -17
  334. package/src/core/ai/tools/__tests__/run-cells-tool.test.ts +2 -2
  335. package/src/core/ai/tools/base.ts +18 -9
  336. package/src/core/ai/tools/edit-notebook-tool.ts +74 -69
  337. package/src/core/ai/tools/registry.ts +13 -8
  338. package/src/core/ai/tools/sample-tool.ts +6 -7
  339. package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +4 -0
  340. package/src/core/cells/__tests__/actions.test.ts +474 -34
  341. package/src/core/cells/__tests__/add-missing-import.test.ts +7 -8
  342. package/src/core/cells/__tests__/cell.test.ts +39 -0
  343. package/src/core/cells/__tests__/cells.test.ts +260 -1165
  344. package/src/core/cells/__tests__/collapseConsoleOutputs.test.ts +54 -0
  345. package/src/core/cells/__tests__/document.test.ts +414 -0
  346. package/src/core/cells/__tests__/flush-document.test.ts +40 -0
  347. package/src/core/cells/__tests__/notebook-outline.test.ts +114 -0
  348. package/src/core/cells/__tests__/outputs.test.tsx +71 -53
  349. package/src/core/cells/__tests__/session.test.ts +117 -36
  350. package/src/core/cells/__tests__/with-document-save.test.ts +67 -0
  351. package/src/core/cells/actions.ts +391 -3
  352. package/src/core/cells/add-missing-import.ts +46 -42
  353. package/src/core/cells/cell.ts +14 -3
  354. package/src/core/cells/cells.ts +99 -599
  355. package/src/core/cells/columns.ts +38 -0
  356. package/src/core/cells/document.ts +392 -0
  357. package/src/core/cells/outputs.ts +12 -28
  358. package/src/core/cells/pending-delete-service.ts +2 -6
  359. package/src/core/cells/session.ts +1 -1
  360. package/src/core/cells/types.ts +22 -1
  361. package/src/core/codemirror/__tests__/setup.test.ts +29 -29
  362. package/src/core/codemirror/ai/__tests__/resources.test.ts +117 -0
  363. package/src/core/codemirror/ai/resources.ts +1 -1
  364. package/src/core/codemirror/cells/__tests__/extensions.test.ts +42 -1
  365. package/src/core/codemirror/cells/extensions.ts +27 -9
  366. package/src/core/codemirror/cells/state.ts +1 -1
  367. package/src/core/codemirror/format.ts +6 -5
  368. package/src/core/codemirror/keymaps/vim.ts +4 -8
  369. package/src/core/codemirror/language/__tests__/sql.test.ts +22 -20
  370. package/src/core/codemirror/language/__tests__/utils.test.ts +187 -15
  371. package/src/core/codemirror/language/cell-code.ts +114 -0
  372. package/src/core/codemirror/language/extension.ts +52 -35
  373. package/src/core/codemirror/language/languages/markdown.ts +8 -6
  374. package/src/core/codemirror/language/languages/python.ts +16 -10
  375. package/src/core/codemirror/language/languages/sql/sql.ts +16 -24
  376. package/src/core/codemirror/language/panel/panel.tsx +1 -0
  377. package/src/core/codemirror/language/types.ts +7 -7
  378. package/src/core/codemirror/language/utils.ts +9 -39
  379. package/src/core/codemirror/lsp/notebook-lsp.ts +6 -5
  380. package/src/core/codemirror/readonly/__tests__/extension.test.ts +1 -1
  381. package/src/core/codemirror/readonly/extension.ts +2 -2
  382. package/src/core/codemirror/rtc/extension.ts +24 -13
  383. package/src/core/codemirror/rtc/loro/awareness.ts +34 -19
  384. package/src/core/codemirror/rtc/loro/sync.ts +1 -0
  385. package/src/core/codemirror/utils.ts +1 -0
  386. package/src/core/config/__tests__/config-schema.test.ts +5 -3
  387. package/src/core/config/config-schema.ts +3 -0
  388. package/src/core/datasets/__tests__/data-source-discovery.test.ts +5 -5
  389. package/src/core/datasets/data-source-discovery.ts +4 -6
  390. package/src/core/edit-app.tsx +19 -23
  391. package/src/core/errors/error-entries.ts +3 -0
  392. package/src/core/export/hooks.ts +1 -1
  393. package/src/core/export/layout.ts +2 -2
  394. package/src/core/hotkeys/actions.ts +2 -4
  395. package/src/core/islands/__tests__/bootstrap.test.ts +73 -43
  396. package/src/core/islands/__tests__/bridge.test.ts +72 -25
  397. package/src/core/islands/bootstrap.ts +46 -53
  398. package/src/core/islands/bridge.ts +74 -23
  399. package/src/core/islands/components/__tests__/IslandControls.test.tsx +7 -5
  400. package/src/core/islands/worker/__tests__/controller.test.ts +62 -4
  401. package/src/core/islands/worker/controller.ts +4 -10
  402. package/src/core/islands/worker/worker.tsx +33 -33
  403. package/src/core/kernel/__tests__/RuntimeState.test.ts +1 -1
  404. package/src/core/kernel/__tests__/handlers.test.ts +7 -153
  405. package/src/core/kernel/handlers.ts +18 -59
  406. package/src/core/network/__tests__/connection.test.ts +1 -1
  407. package/src/core/network/__tests__/requests-lazy.test.ts +1 -3
  408. package/src/core/network/connection-notice.ts +1 -1
  409. package/src/core/network/connection.ts +1 -1
  410. package/src/core/network/requests-lazy.ts +1 -4
  411. package/src/core/network/requests-network.ts +0 -25
  412. package/src/core/network/requests-static.ts +0 -3
  413. package/src/core/network/requests-toasting.tsx +0 -3
  414. package/src/core/network/types.ts +0 -12
  415. package/src/core/network/useConnectionNotice.ts +8 -2
  416. package/src/core/notebook/attachments.ts +38 -0
  417. package/src/core/notebook/document/__tests__/codemirror.test.ts +552 -0
  418. package/src/core/notebook/document/__tests__/draft.test.ts +363 -0
  419. package/src/core/notebook/document/__tests__/fixtures.ts +92 -0
  420. package/src/core/notebook/document/__tests__/hold.test.ts +88 -0
  421. package/src/core/notebook/document/__tests__/hooks.test.tsx +391 -0
  422. package/src/core/notebook/document/__tests__/lifecycle-scenarios.test.ts +61 -0
  423. package/src/core/notebook/document/__tests__/model.test.ts +247 -0
  424. package/src/core/notebook/document/__tests__/protocol-fixtures.ts +38 -0
  425. package/src/core/notebook/document/__tests__/recovery-scenarios.test.ts +230 -0
  426. package/src/core/notebook/document/__tests__/scenario-fixture.ts +43 -0
  427. package/src/core/notebook/document/__tests__/scenarios.test.ts +205 -0
  428. package/src/core/notebook/document/__tests__/state.test.ts +459 -0
  429. package/src/core/notebook/document/__tests__/submission.test.ts +203 -0
  430. package/src/core/notebook/document/__tests__/test-host.test.ts +92 -0
  431. package/src/core/notebook/document/__tests__/test-host.ts +247 -0
  432. package/src/core/notebook/document/__tests__/test-notebook.ts +149 -0
  433. package/src/core/notebook/document/__tests__/user.ts +61 -0
  434. package/src/core/notebook/document/codemirror.ts +216 -0
  435. package/src/core/notebook/document/draft.ts +502 -0
  436. package/src/core/notebook/document/hooks.tsx +118 -0
  437. package/src/core/notebook/document/index.ts +24 -0
  438. package/src/core/notebook/document/model.ts +105 -0
  439. package/src/core/notebook/document/select.ts +41 -0
  440. package/src/core/notebook/document/state.ts +378 -0
  441. package/src/core/notebook/document/sync.ts +418 -0
  442. package/src/core/notebook/index.ts +18 -0
  443. package/src/core/notebook/replica/AGENTS.md +11 -0
  444. package/src/core/notebook/replica/__tests__/attachments.test.ts +56 -0
  445. package/src/core/notebook/replica/__tests__/document.test.ts +126 -0
  446. package/src/core/notebook/replica/__tests__/fixtures.ts +79 -0
  447. package/src/core/notebook/replica/__tests__/replica.test.ts +550 -0
  448. package/src/core/notebook/replica/backend.ts +19 -0
  449. package/src/core/notebook/replica/connection.ts +42 -0
  450. package/src/core/notebook/replica/index.ts +13 -0
  451. package/src/core/notebook/replica/replica.ts +286 -0
  452. package/src/core/notebook/replica/resources/attachments.ts +22 -0
  453. package/src/core/notebook/replica/resources/document.ts +101 -0
  454. package/src/core/packages/__tests__/useSandboxController.test.tsx +1 -1
  455. package/src/core/packages/useSandboxController.ts +1 -1
  456. package/src/core/run-app.tsx +48 -51
  457. package/src/core/runtime/__tests__/adapter.test.ts +1 -1
  458. package/src/core/runtime/__tests__/createWsUrl.test.ts +77 -7
  459. package/src/core/runtime/__tests__/runtime.test.ts +32 -58
  460. package/src/core/runtime/adapter.ts +1 -1
  461. package/src/core/runtime/config.ts +2 -2
  462. package/src/core/runtime/runtime.ts +38 -42
  463. package/src/core/runtime/types.ts +0 -2
  464. package/src/core/saving/filename.ts +1 -1
  465. package/src/core/saving/save-component.tsx +10 -4
  466. package/src/core/saving/useAutoSave.ts +1 -1
  467. package/src/core/session/__tests__/backend.test.ts +235 -0
  468. package/src/core/session/__tests__/useNotebookReplica.hook.test.tsx +183 -0
  469. package/src/core/session/__tests__/useNotebookSession.hook.test.tsx +663 -0
  470. package/src/core/session/active-notebook.ts +11 -0
  471. package/src/core/session/backend.ts +125 -0
  472. package/src/core/{websocket → session}/types.ts +2 -2
  473. package/src/core/session/useNotebookReplica.ts +72 -0
  474. package/src/core/{websocket → session}/useNotebookSession.tsx +74 -68
  475. package/src/core/state/__tests__/jotai.test.tsx +108 -0
  476. package/src/core/state/jotai.ts +1 -1
  477. package/src/core/static/__tests__/files.test.ts +38 -10
  478. package/src/core/static/files.ts +2 -0
  479. package/src/core/{websocket → transports}/__tests__/connection-state.test.ts +1 -13
  480. package/src/core/transports/__tests__/server.test.ts +408 -0
  481. package/src/core/transports/__tests__/static.test.ts +175 -0
  482. package/src/core/transports/__tests__/wasm.test.ts +166 -0
  483. package/src/core/{websocket → transports}/connection-state.ts +9 -16
  484. package/src/core/transports/server.ts +152 -0
  485. package/src/core/transports/static.ts +84 -0
  486. package/src/core/transports/wasm.ts +97 -0
  487. package/src/core/wasm/__tests__/bridge.test.ts +213 -50
  488. package/src/core/wasm/__tests__/worker.test.ts +187 -0
  489. package/src/core/wasm/bridge.ts +75 -69
  490. package/src/core/wasm/rpc.ts +15 -3
  491. package/src/core/wasm/store.ts +3 -3
  492. package/src/core/wasm/worker/bootstrap.ts +55 -51
  493. package/src/core/wasm/worker/getMarimoWheel.ts +1 -1
  494. package/src/core/wasm/worker/save-worker.ts +9 -10
  495. package/src/core/wasm/worker/types.ts +29 -18
  496. package/src/core/wasm/worker/worker.ts +88 -44
  497. package/src/css/globals.css +66 -22
  498. package/src/hooks/__tests__/useAsyncData.test.tsx +148 -0
  499. package/src/hooks/__tests__/useDebounce.test.tsx +51 -0
  500. package/src/hooks/__tests__/useDeepCompareMemoize.test.tsx +39 -0
  501. package/src/hooks/__tests__/useDelayElapsed.test.tsx +28 -0
  502. package/src/hooks/__tests__/useInternalStateWithSync.test.tsx +20 -0
  503. package/src/hooks/__tests__/useScript.test.tsx +77 -0
  504. package/src/hooks/__tests__/useStateForKey.test.tsx +257 -0
  505. package/src/hooks/useAsyncData.ts +3 -3
  506. package/src/hooks/useDataSourceDiscovery.ts +6 -4
  507. package/src/hooks/useDebounce.ts +6 -10
  508. package/src/hooks/useDeepCompareMemoize.ts +1 -1
  509. package/src/hooks/useDelayElapsed.ts +14 -10
  510. package/src/hooks/useElapsedTime.ts +3 -3
  511. package/src/hooks/useEventListener.ts +1 -2
  512. package/src/hooks/useInputHistory.ts +4 -7
  513. package/src/hooks/useInternalStateWithSync.ts +6 -6
  514. package/src/hooks/useResizeObserver.ts +4 -7
  515. package/src/hooks/useScript.ts +67 -70
  516. package/src/hooks/useStateForKey.ts +93 -0
  517. package/src/mount.tsx +1 -5
  518. package/src/plugins/__tests__/plugin-object-schemas.test.ts +104 -0
  519. package/src/plugins/core/registerReactComponent.tsx +1 -1
  520. package/src/plugins/impl/CodeEditorPlugin.tsx +1 -1
  521. package/src/plugins/impl/DataEditorPlugin.tsx +1 -1
  522. package/src/plugins/impl/DataTablePlugin.tsx +69 -37
  523. package/src/plugins/impl/DropdownPlugin.tsx +2 -6
  524. package/src/plugins/impl/FileBrowserPlugin.tsx +4 -16
  525. package/src/plugins/impl/FormPlugin.tsx +1 -1
  526. package/src/plugins/impl/RangeSliderPlugin.tsx +16 -2
  527. package/src/plugins/impl/RefreshPlugin.tsx +1 -0
  528. package/src/plugins/impl/SliderPlugin.tsx +6 -8
  529. package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +387 -1
  530. package/src/plugins/impl/__tests__/FileBrowserPlugin.test.tsx +45 -0
  531. package/src/plugins/impl/__tests__/RangeSliderPlugin.test.tsx +109 -0
  532. package/src/plugins/impl/__tests__/SliderPlugin.test.tsx +24 -3
  533. package/src/plugins/impl/anywidget/__tests__/registry.test.ts +153 -0
  534. package/src/plugins/impl/anywidget/__tests__/widget-binding.test.ts +241 -2
  535. package/src/plugins/impl/anywidget/model.ts +3 -1
  536. package/src/plugins/impl/anywidget/registry.ts +18 -14
  537. package/src/plugins/impl/anywidget/runtime.ts +21 -9
  538. package/src/plugins/impl/anywidget/widget-binding.ts +160 -82
  539. package/src/plugins/impl/chat/__tests__/chat-prompts.test.tsx +93 -0
  540. package/src/plugins/impl/chat/chat-ui.tsx +14 -11
  541. package/src/plugins/impl/code/any-language-editor.tsx +2 -2
  542. package/src/plugins/impl/data-editor/glide-data-editor.tsx +8 -5
  543. package/src/plugins/impl/data-editor/glide-utils.ts +3 -1
  544. package/src/plugins/impl/data-explorer/ConnectedDataExplorerComponent.tsx +1 -1
  545. package/src/plugins/impl/data-explorer/components/column-summary.tsx +4 -2
  546. package/src/plugins/impl/data-explorer/state/reducer.ts +1 -1
  547. package/src/plugins/impl/data-frames/DataFramePlugin.tsx +1 -2
  548. package/src/plugins/impl/data-frames/__tests__/sidebar.test.tsx +160 -0
  549. package/src/plugins/impl/data-frames/forms/__tests__/__snapshots__/form.test.tsx.snap +66 -66
  550. package/src/plugins/impl/data-frames/panel.tsx +43 -16
  551. package/src/plugins/impl/mpl-interactive/MplInteractivePlugin.tsx +5 -3
  552. package/src/plugins/impl/panel/PanelPlugin.tsx +16 -17
  553. package/src/plugins/impl/panel/__tests__/utils.test.ts +39 -1
  554. package/src/plugins/impl/panel/utils.ts +17 -0
  555. package/src/plugins/impl/plotly/PlotlyPlugin.tsx +2 -5
  556. package/src/plugins/impl/plotly/selection.ts +25 -19
  557. package/src/plugins/impl/vega/VegaPlugin.tsx +2 -3
  558. package/src/plugins/impl/vega/make-selectable.ts +4 -7
  559. package/src/plugins/layout/RoutesPlugin.tsx +1 -1
  560. package/src/stories/__tests__/editor-story.test.tsx +33 -0
  561. package/src/stories/button.stories.tsx +1 -0
  562. package/src/stories/cell.stories.tsx +1 -1
  563. package/src/stories/data-table.stories.tsx +31 -0
  564. package/src/stories/editor.stories.tsx +24 -17
  565. package/src/stories/form-wrapper.stories.tsx +2 -2
  566. package/src/stories/layout/vertical/one-column.stories.tsx +1 -1
  567. package/src/stories/runtime-story.tsx +23 -16
  568. package/src/stories/select.stories.tsx +2 -2
  569. package/src/stories/switchable-multi-select.stories.tsx +1 -1
  570. package/src/utils/__tests__/arrays.test.ts +35 -0
  571. package/src/utils/__tests__/batch-requests.test.ts +3 -3
  572. package/src/utils/__tests__/{createReducer.test.ts → createReducer.test.tsx} +29 -0
  573. package/src/utils/__tests__/numbers.test.ts +105 -1
  574. package/src/utils/__tests__/objects.test.ts +21 -0
  575. package/src/utils/arrays.ts +11 -2
  576. package/src/utils/blob.ts +1 -1
  577. package/src/utils/createReducer.ts +53 -26
  578. package/src/utils/maps.ts +0 -3
  579. package/src/utils/numbers.ts +79 -18
  580. package/src/utils/objects.ts +4 -9
  581. package/src/utils/once.ts +19 -19
  582. package/src/utils/tracer.ts +9 -5
  583. package/src/utils/zod-utils.ts +1 -3
  584. package/dist/any-language-editor-DZ_YTVus.js +0 -76
  585. package/dist/assets/__vite-browser-external-BXYkiFsk.js +0 -1
  586. package/dist/assets/worker-HPY9fbhz.js +0 -114
  587. package/dist/mermaid-HWGCJPDP-ByCsZnYF.js +0 -2
  588. package/dist/precisionRound-DMIVQrt2.js +0 -97
  589. package/src/core/attachments/state.ts +0 -21
  590. package/src/core/cells/__tests__/apply-transaction.test.ts +0 -991
  591. package/src/core/cells/__tests__/document-changes.test.ts +0 -1140
  592. package/src/core/cells/__tests__/document-roundtrip.test.ts +0 -392
  593. package/src/core/cells/document-changes.ts +0 -944
  594. package/src/core/host/AGENTS.md +0 -6
  595. package/src/core/host/__tests__/events.test.ts +0 -46
  596. package/src/core/host/__tests__/notebook-client.test.ts +0 -191
  597. package/src/core/host/__tests__/protocol.test.ts +0 -214
  598. package/src/core/host/connection.ts +0 -81
  599. package/src/core/host/events.ts +0 -60
  600. package/src/core/host/notebook-client.ts +0 -226
  601. package/src/core/host/protocol.ts +0 -1943
  602. package/src/core/state/__tests__/jotai.test.ts +0 -25
  603. package/src/core/wasm/worker/message-buffer.ts +0 -33
  604. package/src/core/websocket/__tests__/createConnectionTransport.test.ts +0 -49
  605. package/src/core/websocket/__tests__/useNotebookClient.hook.test.tsx +0 -140
  606. package/src/core/websocket/__tests__/useNotebookSession.hook.test.tsx +0 -883
  607. package/src/core/websocket/transports/__tests__/basic.test.ts +0 -275
  608. package/src/core/websocket/transports/__tests__/sse.test.ts +0 -335
  609. package/src/core/websocket/transports/__tests__/transport.test.ts +0 -308
  610. package/src/core/websocket/transports/__tests__/ws.test.ts +0 -125
  611. package/src/core/websocket/transports/basic.ts +0 -102
  612. package/src/core/websocket/transports/create.ts +0 -31
  613. package/src/core/websocket/transports/sse.ts +0 -308
  614. package/src/core/websocket/transports/transport.ts +0 -50
  615. package/src/core/websocket/transports/ws.ts +0 -98
  616. package/src/core/websocket/useNotebookClient.ts +0 -80
  617. /package/src/{core/attachments/toast.ts → components/editor/agent-toast.ts} +0 -0
  618. /package/src/core/{websocket → session}/connection-utils.ts +0 -0
@@ -2,13 +2,11 @@
2
2
 
3
3
  import { useAtom, useSetAtom } from "jotai";
4
4
  import {
5
- BookTextIcon,
6
5
  ChevronDownIcon,
7
6
  ChevronRightIcon,
8
7
  ChevronsDownUpIcon,
9
- ClockIcon,
10
8
  ExternalLinkIcon,
11
- PlayCircleIcon,
9
+ PlusIcon,
12
10
  PowerOffIcon,
13
11
  RefreshCcwIcon,
14
12
  SearchIcon,
@@ -44,6 +42,7 @@ import { Button } from "@/components/ui/button";
44
42
  import { Checkbox } from "@/components/ui/checkbox";
45
43
  import { DropdownMenuSeparator } from "@/components/ui/dropdown-menu";
46
44
  import { Label } from "@/components/ui/label";
45
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
47
46
  import { Tooltip } from "@/components/ui/tooltip";
48
47
  import { toast } from "@/components/ui/use-toast";
49
48
  import { getSessionId, isSessionId } from "@/core/kernel/session";
@@ -64,7 +63,6 @@ import { ConfigButton } from "../app-config/app-config-button";
64
63
  import { ErrorBoundary } from "../editor/boundary/ErrorBoundary";
65
64
  import { ShutdownButton } from "../editor/controls/shutdown-button";
66
65
  import {
67
- Header,
68
66
  OpenTutorialDropDown,
69
67
  ResourceLinks,
70
68
  TruncatedWorkspaceBanner,
@@ -83,6 +81,9 @@ function tabTarget(path: string) {
83
81
  return `${getSessionId()}-${encodeURIComponent(path)}`;
84
82
  }
85
83
 
84
+ const NOTEBOOK_TAB_CLASS =
85
+ "-mb-px rounded-none border-b-2 border-transparent px-1 py-3 text-(--gray-11) transition-colors hover:text-foreground focus-visible:ring-inset focus-visible:ring-offset-0 data-[state=active]:border-(--green-11) data-[state=active]:bg-transparent data-[state=active]:text-(--green-11) data-[state=active]:shadow-none";
86
+
86
87
  const HomePage: React.FC = () => {
87
88
  const [nonce, setNonce] = useState(0);
88
89
  const { getRecentFiles, getRunningNotebooks } = useRequestClient();
@@ -123,28 +124,75 @@ const HomePage: React.FC = () => {
123
124
  setRunningNotebooks: runningResponse.setData,
124
125
  }}
125
126
  >
126
- <div className="absolute top-3 right-5 flex gap-3 z-50">
127
- <OpenTutorialDropDown />
128
- <ConfigButton showAppConfig={false} />
129
- <ShutdownButton
130
- description={`This will shutdown the notebook server and terminate all running notebooks (${running.size}). You'll lose all data that's in memory.`}
131
- />
132
- </div>
133
- <div className="flex flex-col gap-6 max-w-6xl container pt-5 pb-20 z-10">
134
- <img src="logo.png" alt="marimo logo" className="w-48 mb-2" />
135
- <CreateNewNotebook />
136
- <ResourceLinks />
137
- <NotebookList
138
- header={<Header Icon={PlayCircleIcon}>Running notebooks</Header>}
139
- files={[...running.values()]}
140
- />
141
- <NotebookList
142
- header={<Header Icon={ClockIcon}>Recent notebooks</Header>}
143
- files={recents.files}
144
- />
145
- <ErrorBoundary>
146
- <WorkspaceNotebooks onRefreshRecents={recentsResponse.refetch} />
147
- </ErrorBoundary>
127
+ <div className="min-h-screen bg-background">
128
+ <header className="border-b border-border/60">
129
+ <div className="mx-auto flex max-w-7xl items-center justify-between gap-4 px-5 py-5 sm:px-10">
130
+ <img src="logo.png" alt="marimo" className="w-28" />
131
+ <div className="flex items-center gap-3">
132
+ <ConfigButton showAppConfig={false} />
133
+ <ShutdownButton
134
+ description={`This will shutdown the notebook server and terminate all running notebooks (${running.size}). You'll lose all data that's in memory.`}
135
+ />
136
+ </div>
137
+ </div>
138
+ </header>
139
+ <main className="mx-auto max-w-7xl px-5 py-8 sm:px-10 sm:py-10">
140
+ <div className="mb-8">
141
+ <h1 className="text-2xl font-semibold tracking-tight">
142
+ Your notebooks
143
+ </h1>
144
+ <p className="mt-2 text-sm text-muted-foreground">
145
+ Pick up where you left off, or start something new.
146
+ </p>
147
+ <div className="mt-5 flex items-center gap-3">
148
+ <CreateNewNotebook />
149
+ <OpenTutorialDropDown />
150
+ </div>
151
+ </div>
152
+ <Tabs defaultValue="workspace">
153
+ <TabsList
154
+ aria-label="Notebooks"
155
+ className="mb-5 flex h-auto w-full justify-start gap-6 rounded-none border-b border-border/70 bg-transparent p-0"
156
+ >
157
+ <TabsTrigger value="workspace" className={NOTEBOOK_TAB_CLASS}>
158
+ Workspace
159
+ </TabsTrigger>
160
+ <TabsTrigger value="recent" className={NOTEBOOK_TAB_CLASS}>
161
+ Recent
162
+ </TabsTrigger>
163
+ <TabsTrigger value="running" className={NOTEBOOK_TAB_CLASS}>
164
+ Running
165
+ <span className="ml-1.5 text-xs font-normal tabular-nums text-(--gray-11)">
166
+ {running.size}
167
+ </span>
168
+ </TabsTrigger>
169
+ </TabsList>
170
+ <TabsContent
171
+ value="workspace"
172
+ forceMount={true}
173
+ className="mt-0 data-[state=inactive]:hidden"
174
+ >
175
+ <ErrorBoundary>
176
+ <WorkspaceNotebooks
177
+ onRefreshRecents={recentsResponse.refetch}
178
+ />
179
+ </ErrorBoundary>
180
+ </TabsContent>
181
+ <TabsContent value="recent" className="mt-0">
182
+ <NotebookList
183
+ files={recents.files}
184
+ emptyMessage="Notebooks you open will appear here."
185
+ />
186
+ </TabsContent>
187
+ <TabsContent value="running" className="mt-0">
188
+ <NotebookList
189
+ files={[...running.values()]}
190
+ emptyMessage="No notebooks are running."
191
+ />
192
+ </TabsContent>
193
+ </Tabs>
194
+ <ResourceLinks />
195
+ </main>
148
196
  </div>
149
197
  </RunningNotebooksContext>
150
198
  </Suspense>
@@ -195,23 +243,37 @@ const WorkspaceNotebooks: React.FC<{ onRefreshRecents: () => void }> = ({
195
243
 
196
244
  return (
197
245
  <WorkspaceContext value={workspaceContextValue}>
198
- <div className="flex flex-col gap-2">
246
+ <div className="flex min-w-0 flex-col gap-4">
199
247
  {workspace.hasMore && (
200
248
  <TruncatedWorkspaceBanner fileCount={workspace.fileCount} />
201
249
  )}
202
- <Header
203
- Icon={BookTextIcon}
204
- control={
205
- <div className="flex items-center gap-2">
250
+ <div className="overflow-hidden rounded-lg border border-border/70 bg-background">
251
+ <div className="flex flex-wrap items-center gap-3 border-b border-border/60 bg-muted/20 p-3">
252
+ <div className="min-w-40 flex-1">
206
253
  <Input
207
254
  id="search"
255
+ aria-label="Search workspace"
208
256
  value={searchText}
209
- icon={<SearchIcon size={13} />}
257
+ icon={<SearchIcon size={15} />}
210
258
  onChange={(e) => setSearchText(e.target.value)}
211
- placeholder="Search"
212
- className="mb-0 border-border"
259
+ placeholder="Find a notebook…"
260
+ className="mb-0 h-9 rounded-md border-border/60 bg-background font-sans shadow-none hover:shadow-none focus-visible:shadow-none"
213
261
  />
214
- <CollapseAllButton />
262
+ </div>
263
+ <Button
264
+ variant="ghost"
265
+ size="icon"
266
+ onClick={() => refetch()}
267
+ aria-label="Refresh workspace"
268
+ >
269
+ {isFetching ? (
270
+ <Spinner size="small" />
271
+ ) : (
272
+ <RefreshCcwIcon className="w-4 h-4" />
273
+ )}
274
+ </Button>
275
+ <CollapseAllButton />
276
+ <div className="flex items-center gap-2">
215
277
  <Checkbox
216
278
  data-testid="include-markdown-checkbox"
217
279
  id="include-markdown"
@@ -220,23 +282,14 @@ const WorkspaceNotebooks: React.FC<{ onRefreshRecents: () => void }> = ({
220
282
  setIncludeMarkdown(Boolean(checked))
221
283
  }
222
284
  />
223
- <Label htmlFor="include-markdown">Include markdown</Label>
285
+ <Label
286
+ htmlFor="include-markdown"
287
+ className="text-xs text-muted-foreground"
288
+ >
289
+ Include markdown
290
+ </Label>
224
291
  </div>
225
- }
226
- >
227
- Workspace
228
- <Button
229
- variant="text"
230
- size="icon"
231
- className="w-4 h-4 ml-1 p-0 opacity-70 hover:opacity-100"
232
- onClick={() => refetch()}
233
- aria-label="Refresh workspace"
234
- >
235
- <RefreshCcwIcon className="w-4 h-4" />
236
- </Button>
237
- {isFetching && <Spinner size="small" />}
238
- </Header>
239
- <div className="flex flex-col divide-y divide-(--slate-3) border rounded overflow-hidden max-h-192 overflow-y-auto shadow-sm bg-background">
292
+ </div>
240
293
  <NotebookFileTree searchText={searchText} files={workspace.files} />
241
294
  </div>
242
295
  </div>
@@ -322,7 +375,7 @@ const NotebookFileTree: React.FC<{
322
375
  await handleRename(id, name);
323
376
  }}
324
377
  padding={5}
325
- rowHeight={35}
378
+ rowHeight={44}
326
379
  indent={15}
327
380
  overscanCount={1000}
328
381
  // Hide the drop cursor
@@ -343,13 +396,15 @@ const Node = ({ node, style }: NodeRendererProps<FileInfo>) => {
343
396
  : guessFileType(node.data.name);
344
397
 
345
398
  const Icon = FILE_TYPE_ICONS[fileType];
346
- const iconEl = <Icon className="w-5 h-5 shrink-0" strokeWidth={1.5} />;
399
+ const iconEl = (
400
+ <Icon className="w-4 h-4 shrink-0 text-(--gray-11)" strokeWidth={1.5} />
401
+ );
347
402
  const { root } = use(WorkspaceContext);
348
403
  const { runningNotebooks } = use(RunningNotebooksContext);
349
404
 
350
405
  const renderItem = () => {
351
406
  const itemClassName =
352
- "flex items-center pl-1 cursor-pointer hover:bg-accent/50 hover:text-accent-foreground rounded-l flex-1 overflow-hidden h-full pr-3 gap-2";
407
+ "flex items-center pl-1 cursor-pointer hover:bg-(--sage-3) hover:text-foreground rounded-l flex-1 overflow-hidden h-full pr-3 gap-2";
353
408
 
354
409
  // Inline rename input; react-arborist flips `node.isEditing` when
355
410
  // `node.edit()` is called from the FileActions menu.
@@ -413,7 +468,7 @@ const Node = ({ node, style }: NodeRendererProps<FileInfo>) => {
413
468
  <div
414
469
  style={style}
415
470
  className={cn(
416
- "flex items-center cursor-pointer ml-1 text-muted-foreground whitespace-nowrap group h-full",
471
+ "flex items-center cursor-pointer ml-1 text-sm text-foreground whitespace-nowrap group h-full",
417
472
  )}
418
473
  onClick={(evt) => {
419
474
  evt.stopPropagation();
@@ -479,21 +534,19 @@ const FolderArrow = ({ node }: { node: NodeApi<FileInfo> }) => {
479
534
  };
480
535
 
481
536
  const NotebookList: React.FC<{
482
- header: React.ReactNode;
483
537
  files: MarimoFile[];
484
- }> = ({ header, files }) => {
485
- if (files.length === 0) {
486
- return null;
487
- }
488
-
538
+ emptyMessage: string;
539
+ }> = ({ files, emptyMessage }) => {
489
540
  return (
490
- <div className="flex flex-col gap-2">
491
- {header}
492
- <div className="flex flex-col divide-y divide-(--slate-3) border rounded overflow-hidden max-h-192 overflow-y-auto shadow-sm bg-background">
493
- {files.map((file) => {
494
- return <MarimoFileComponent key={file.path} file={file} />;
495
- })}
496
- </div>
541
+ <div className="flex max-h-[500px] min-h-40 min-w-0 flex-col divide-y divide-border/50 overflow-y-auto rounded-lg border border-border/60">
542
+ {files.length === 0 && (
543
+ <p className="px-4 py-6 text-sm text-muted-foreground">
544
+ {emptyMessage}
545
+ </p>
546
+ )}
547
+ {files.map((file) => {
548
+ return <MarimoFileComponent key={file.path} file={file} />;
549
+ })}
497
550
  </div>
498
551
  );
499
552
  };
@@ -511,17 +564,19 @@ const MarimoFileComponent = ({ file }: { file: MarimoFile }) => {
511
564
  : asURL(`?file=${encodeURIComponent(file.path)}`);
512
565
 
513
566
  const isMarkdown = file.path.endsWith(".md");
567
+ const Icon = FILE_TYPE_ICONS[guessFileType(file.name)];
514
568
 
515
569
  return (
516
570
  <a
517
- className="py-1.5 px-4 hover:bg-(--blue-2) hover:text-primary transition-all duration-300 cursor-pointer group relative flex gap-4 items-center"
571
+ className="py-3 px-4 hover:bg-(--sage-3) transition-colors cursor-pointer group relative flex gap-3 items-center focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring"
518
572
  key={file.path}
519
573
  href={href.toString()}
520
574
  target={tabTarget(file.initializationId || file.path)}
521
575
  >
522
- <div className="flex flex-col justify-between flex-1">
523
- <span className="flex items-center gap-2">
524
- {file.name}
576
+ <Icon className="h-4 w-4 shrink-0 text-(--gray-11)" strokeWidth={1.5} />
577
+ <div className="flex min-w-0 flex-col gap-1 flex-1">
578
+ <span className="flex items-center gap-2 text-sm font-medium">
579
+ <span className="truncate">{file.name}</span>
525
580
  {isMarkdown && (
526
581
  <span className="opacity-80">
527
582
  <MarkdownIcon />
@@ -530,7 +585,7 @@ const MarimoFileComponent = ({ file }: { file: MarimoFile }) => {
530
585
  </span>
531
586
  <p
532
587
  title={file.path}
533
- className="text-sm text-muted-foreground overflow-hidden whitespace-nowrap text-ellipsis"
588
+ className="text-xs text-muted-foreground overflow-hidden whitespace-nowrap text-ellipsis"
534
589
  >
535
590
  {file.path}
536
591
  </p>
@@ -541,8 +596,8 @@ const MarimoFileComponent = ({ file }: { file: MarimoFile }) => {
541
596
  <SessionShutdownButton filePath={file.path} />
542
597
  </div>
543
598
  <ExternalLinkIcon
544
- size={20}
545
- className="group-hover:opacity-100 opacity-0 transition-all duration-300 text-primary"
599
+ size={14}
600
+ className="opacity-40 group-hover:opacity-100"
546
601
  />
547
602
  </div>
548
603
  {!!file.lastModified && (
@@ -571,7 +626,8 @@ const SessionShutdownButton: React.FC<{ filePath: string }> = ({
571
626
  <Button
572
627
  size={"icon"}
573
628
  variant="outline"
574
- className="opacity-80 hover:opacity-100 hover:bg-accent text-destructive border-destructive hover:border-destructive hover:text-destructive bg-background hover:bg-(--red-1)"
629
+ aria-label="Shutdown notebook"
630
+ className="border-transparent text-muted-foreground shadow-none hover:border-destructive hover:bg-(--red-1) hover:text-destructive"
575
631
  onClick={(e) => {
576
632
  e.stopPropagation();
577
633
  e.preventDefault();
@@ -611,23 +667,40 @@ const SessionShutdownButton: React.FC<{ filePath: string }> = ({
611
667
  );
612
668
  };
613
669
 
614
- const CreateNewNotebook: React.FC = () => {
615
- const url = newNotebookURL();
670
+ export const CreateNewNotebook: React.FC = () => {
671
+ // Reusing a `__new__` key reattaches to an old session; rotate it per open
672
+ const [url, setUrl] = useState(newNotebookURL);
673
+ const rotateUrl = () => {
674
+ const next = newNotebookURL();
675
+ setUrl(next);
676
+ return next;
677
+ };
678
+ const rotateDragPayload = (event: React.DragEvent<HTMLAnchorElement>) => {
679
+ // The browser copies the href into the drag payload before this handler
680
+ // runs, so the rotated URL must be written into the payload by hand
681
+ const next = rotateUrl();
682
+ event.dataTransfer.setData("text/uri-list", next);
683
+ event.dataTransfer.setData("text/plain", next);
684
+ };
616
685
  return (
617
- <a
618
- className="relative rounded-lg p-6 group
619
- text-primary hover:bg-(--blue-2) shadow-md-solid shadow-accent border bg-(--blue-1)
620
- transition-all duration-300 cursor-pointer
621
- "
622
- href={url}
623
- target="_blank"
624
- rel="noreferrer"
686
+ <Button
687
+ asChild={true}
688
+ size="sm"
689
+ className="gap-2 rounded-md border-(--brand) bg-(--brand) text-(--brand-foreground) shadow-none hover:bg-(--brand-hover)"
625
690
  >
626
- <h2 className="text-lg font-semibold">Create a new notebook</h2>
627
- <div className="group-hover:opacity-100 opacity-0 absolute right-5 top-0 bottom-0 rounded-lg flex items-center justify-center transition-all duration-300">
628
- <ExternalLinkIcon size={24} />
629
- </div>
630
- </a>
691
+ <a
692
+ href={url}
693
+ target="_blank"
694
+ rel="noreferrer"
695
+ onClick={rotateUrl}
696
+ onAuxClick={rotateUrl}
697
+ onContextMenu={rotateUrl}
698
+ onDragStart={rotateDragPayload}
699
+ >
700
+ <PlusIcon className="h-4 w-4" />
701
+ New notebook
702
+ </a>
703
+ </Button>
631
704
  );
632
705
  };
633
706
 
@@ -0,0 +1,97 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { EditorView } from "@codemirror/view";
4
+ import { act, fireEvent, render, screen } from "@testing-library/react";
5
+ import { createStore, Provider } from "jotai";
6
+ import { type ComponentProps, useEffect, useRef } from "react";
7
+ import { expect, it, vi } from "vitest";
8
+ import type { CellEditor } from "@/components/editor/cell/code/cell-editor";
9
+ import { TooltipProvider } from "@/components/ui/tooltip";
10
+ import { SCRATCH_CELL_ID } from "@/core/cells/ids";
11
+ import { ScratchPad } from "./scratchpad";
12
+ import {
13
+ historyVisibleAtom,
14
+ scratchpadHistoryAtom,
15
+ } from "./scratchpad-history";
16
+
17
+ const state = vi.hoisted(() => ({
18
+ editor: null as EditorView | null,
19
+ updateCellCode: vi.fn(),
20
+ }));
21
+
22
+ vi.mock("@/core/cells/cells", () => ({
23
+ useNotebook: () => ({ cellRuntime: {}, cellData: {} }),
24
+ }));
25
+ vi.mock("@/core/cells/actions", () => ({
26
+ useCellActions: () => ({
27
+ createCell: vi.fn(),
28
+ updateCellCode: state.updateCellCode,
29
+ }),
30
+ }));
31
+ vi.mock("@/core/cells/focus", () => ({ useLastFocusedCellId: () => null }));
32
+ vi.mock("@/core/network/requests", () => ({
33
+ useRequestClient: () => ({ sendRunScratchpad: vi.fn() }),
34
+ }));
35
+ vi.mock("@/components/editor/chrome/panels/panel-context", () => ({
36
+ usePanelOrientation: () => "vertical",
37
+ usePanelSection: () => "sidebar",
38
+ }));
39
+ vi.mock("@/components/editor/Output", () => ({ OutputArea: () => null }));
40
+ vi.mock("@/components/editor/output/console/ConsoleOutput", () => ({
41
+ ConsoleOutput: () => null,
42
+ }));
43
+ vi.mock("@/plugins/impl/code/LazyAnyLanguageCodeMirror", () => ({
44
+ LazyAnyLanguageCodeMirror: () => null,
45
+ }));
46
+ vi.mock("@/components/shortcuts/renderShortcut", () => ({
47
+ renderShortcut: () => "",
48
+ }));
49
+ vi.mock("@/components/editor/cell/code/cell-editor", () => ({
50
+ CellEditor: ({ setEditorView }: ComponentProps<typeof CellEditor>) => {
51
+ const parent = useRef<HTMLDivElement>(null);
52
+ const setView = useRef(setEditorView);
53
+ useEffect(() => {
54
+ const view = new EditorView({
55
+ parent: parent.current ?? undefined,
56
+ extensions: [
57
+ EditorView.contentAttributes.of({
58
+ "aria-label": "Scratchpad editor",
59
+ }),
60
+ ],
61
+ });
62
+ state.editor = view;
63
+ setView.current(view);
64
+ return () => {
65
+ view.destroy();
66
+ state.editor = null;
67
+ };
68
+ }, []);
69
+ return <div ref={parent} />;
70
+ },
71
+ }));
72
+
73
+ it("focuses the editor after recalling history", () => {
74
+ const store = createStore();
75
+ store.set(scratchpadHistoryAtom, ["print(42)"]);
76
+ store.set(historyVisibleAtom, true);
77
+ render(
78
+ <Provider store={store}>
79
+ <TooltipProvider>
80
+ <ScratchPad />
81
+ </TooltipProvider>
82
+ </Provider>,
83
+ );
84
+ const recall = screen.getByRole("button", { name: "Recall code" });
85
+ act(() => recall.focus());
86
+ fireEvent.click(recall, { detail: 0 });
87
+ expect(recall).not.toBeInTheDocument();
88
+ expect(
89
+ screen.getByRole("textbox", { name: "Scratchpad editor" }),
90
+ ).toHaveFocus();
91
+ expect(state.editor?.state.doc.toString()).toBe("print(42)");
92
+ expect(state.updateCellCode).toHaveBeenCalledExactlyOnceWith({
93
+ cellId: SCRATCH_CELL_ID,
94
+ code: "print(42)",
95
+ formattingChange: false,
96
+ });
97
+ });
@@ -13,7 +13,8 @@ import type React from "react";
13
13
  import { Suspense, useRef, useState } from "react";
14
14
  import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
15
15
  import useEvent from "react-use-event-hook";
16
- import { useCellActions, useNotebook } from "@/core/cells/cells";
16
+ import { useNotebook } from "@/core/cells/cells";
17
+ import { useCellActions } from "@/core/cells/actions";
17
18
  import { useLastFocusedCellId } from "@/core/cells/focus";
18
19
  import { HTMLCellId, SCRATCH_CELL_ID } from "@/core/cells/ids";
19
20
  import { DEFAULT_CELL_NAME } from "@/core/cells/names";
@@ -55,7 +56,7 @@ export const ScratchPad: React.FC = () => {
55
56
  const { theme } = useTheme();
56
57
  const ref = useRef<EditorView | null>(null);
57
58
  const lastFocusedCellId = useLastFocusedCellId();
58
- const { createNewCell, updateCellCode } = useCellActions();
59
+ const { updateCellCode, createCell } = useCellActions();
59
60
  const { sendRunScratchpad } = useRequestClient();
60
61
  const orientation = usePanelOrientation();
61
62
  const section = usePanelSection();
@@ -81,11 +82,7 @@ export const ScratchPad: React.FC = () => {
81
82
  if (!code.trim()) {
82
83
  return;
83
84
  }
84
- createNewCell({
85
- code,
86
- before: false,
87
- cellId: lastFocusedCellId ?? "__end__",
88
- });
85
+ createCell({ after: lastFocusedCellId, code });
89
86
  });
90
87
 
91
88
  const handleClearCode = useEvent(() => {
@@ -123,6 +120,7 @@ export const ScratchPad: React.FC = () => {
123
120
  insert: item,
124
121
  },
125
122
  });
123
+ ev.focus();
126
124
  }
127
125
  });
128
126
 
@@ -135,12 +133,24 @@ export const ScratchPad: React.FC = () => {
135
133
  return (
136
134
  <div className="absolute inset-0 z-100 bg-background p-3 border-none overflow-auto">
137
135
  <div className="overflow-auto flex flex-col gap-3">
138
- {history.map((item, index) => (
136
+ {history.map((item) => (
137
+ // The Recall code button provides keyboard access without making the code preview a button.
138
+ // react-doctor-disable-next-line react-doctor/click-events-have-key-events, react-doctor/no-static-element-interactions
139
139
  <div
140
- key={index}
141
- className="border rounded-md hover:shadow-sm cursor-pointer hover:border-input overflow-hidden"
140
+ key={item}
141
+ className="relative border rounded-md hover:shadow-sm cursor-pointer hover:border-input overflow-hidden"
142
142
  onClick={() => handleSelectHistoryItem(item)}
143
143
  >
144
+ <button
145
+ type="button"
146
+ className="sr-only focus:not-sr-only focus:absolute focus:right-1 focus:top-1 focus:z-10 focus:bg-background focus:px-2 focus:py-1 focus:outline focus:outline-ring"
147
+ onClick={(e) => {
148
+ e.stopPropagation();
149
+ handleSelectHistoryItem(item);
150
+ }}
151
+ >
152
+ Recall code
153
+ </button>
144
154
  <Suspense>
145
155
  <LazyAnyLanguageCodeMirror
146
156
  language="python"
@@ -13,17 +13,19 @@ const A = cellId("a");
13
13
  const B = cellId("b");
14
14
 
15
15
  // Spies shared with the hoisted module mocks below.
16
- const { createNewCell, deleteCell, moveCellToIndex } = vi.hoisted(() => ({
17
- createNewCell: vi.fn(),
16
+ const { createCell, deleteCell, moveCellToIndex } = vi.hoisted(() => ({
17
+ createCell: vi.fn(),
18
18
  deleteCell: vi.fn(),
19
19
  moveCellToIndex: vi.fn(),
20
20
  }));
21
21
 
22
+ vi.mock("@/core/cells/actions", () => ({
23
+ useCellActions: () => ({ moveCellToIndex, createCell }),
24
+ }));
22
25
  vi.mock("@/core/cells/cells", async (importOriginal) => {
23
26
  const actual = await importOriginal<typeof import("@/core/cells/cells")>();
24
27
  return {
25
28
  ...actual,
26
- useCellActions: () => ({ moveCellToIndex, createNewCell }),
27
29
  useCellIds: () => MultiColumn.from([[A, B]]),
28
30
  };
29
31
  });
@@ -91,8 +93,6 @@ function renderMinimap() {
91
93
  return { ...utils, onSlideClick };
92
94
  }
93
95
 
94
- const EMPTY_CELL = { code: "", autoFocus: false } as const;
95
-
96
96
  describe("SlidesMinimap insert lines", () => {
97
97
  it("inserts a blank cell above the first row and below any row", () => {
98
98
  renderMinimap();
@@ -102,25 +102,13 @@ describe("SlidesMinimap insert lines", () => {
102
102
  expect(inserts).toHaveLength(3);
103
103
 
104
104
  fireEvent.click(inserts[0]);
105
- expect(createNewCell).toHaveBeenLastCalledWith({
106
- cellId: A,
107
- before: true,
108
- ...EMPTY_CELL,
109
- });
105
+ expect(createCell).toHaveBeenLastCalledWith({ before: A, focus: false });
110
106
 
111
107
  fireEvent.click(inserts[1]);
112
- expect(createNewCell).toHaveBeenLastCalledWith({
113
- cellId: A,
114
- before: false,
115
- ...EMPTY_CELL,
116
- });
108
+ expect(createCell).toHaveBeenLastCalledWith({ after: A, focus: false });
117
109
 
118
110
  fireEvent.click(inserts[2]);
119
- expect(createNewCell).toHaveBeenLastCalledWith({
120
- cellId: B,
121
- before: false,
122
- ...EMPTY_CELL,
123
- });
111
+ expect(createCell).toHaveBeenLastCalledWith({ after: B, focus: false });
124
112
  });
125
113
  });
126
114
 
@@ -135,11 +123,7 @@ describe("SlidesMinimap context menu", () => {
135
123
  const { container } = renderMinimap();
136
124
  openRowMenu(container, A);
137
125
  fireEvent.click(screen.getByText("Add cell"));
138
- expect(createNewCell).toHaveBeenCalledWith({
139
- cellId: A,
140
- before: false,
141
- ...EMPTY_CELL,
142
- });
126
+ expect(createCell).toHaveBeenCalledWith({ after: A, focus: false });
143
127
  });
144
128
 
145
129
  it('"Delete cell" deletes the row\'s cell', () => {
@@ -2,7 +2,8 @@
2
2
 
3
3
  import { useDeleteCellCallback } from "@/components/editor/cell/useDeleteCell";
4
4
  import { outputIsStale } from "@/core/cells/cell";
5
- import { useCellActions, useCellIds } from "@/core/cells/cells";
5
+ import { useCellIds } from "@/core/cells/cells";
6
+ import { useCellActions } from "@/core/cells/actions";
6
7
  import type { CellId } from "@/core/cells/ids";
7
8
  import type { CellColumnId } from "@/utils/id-tree";
8
9
  import { useEffect, useRef, useState } from "react";
@@ -229,7 +230,7 @@ export const SlidesMinimap = ({
229
230
  onSlideClick,
230
231
  }: SlidesMinimapProps) => {
231
232
  const cellIds = useCellIds();
232
- const { moveCellToIndex, createNewCell } = useCellActions();
233
+ const { moveCellToIndex, createCell } = useCellActions();
233
234
  const deleteCell = useDeleteCellCallback();
234
235
  const containerRef = useRef<HTMLDivElement>(null);
235
236
  const visibleIds = useVisibleCellIds(containerRef);
@@ -240,7 +241,10 @@ export const SlidesMinimap = ({
240
241
  const dimensions = computeThumbnailDimensions(thumbnailWidth);
241
242
 
242
243
  const insertCell = (cellId: CellId, before: boolean) => {
243
- createNewCell({ cellId, before, code: "", autoFocus: false });
244
+ createCell({
245
+ ...(before ? { before: cellId } : { after: cellId }),
246
+ focus: false,
247
+ });
244
248
  };
245
249
 
246
250
  useEffect(() => {