@marimo-team/islands 0.25.3-dev15 → 0.25.3-dev150

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 (502) hide show
  1. package/dist/{ConnectedDataExplorerComponent-CNMliVzw.js → ConnectedDataExplorerComponent-BAiSVaHj.js} +17 -15
  2. package/dist/{ErrorBoundary-z1rqMOCq.js → ErrorBoundary-C2DyVAla.js} +16 -16
  3. package/dist/{abnfDiagram-VCTEODGH-D-Ez91gV.js → abnfDiagram-VCTEODGH-BBHN92-F.js} +2 -2
  4. package/dist/any-language-editor-3E24SnCV.js +75 -0
  5. package/dist/{architectureDiagram-5GKGNRK7-CTpcNtO8.js → architectureDiagram-5GKGNRK7-C0Rd64qh.js} +1 -1
  6. package/dist/{arrays-CN1djC4k.js → arrays-R6JsImiT.js} +8 -0
  7. package/dist/assets/__vite-browser-external-BgQNn4dS.js +1 -0
  8. package/dist/assets/worker-FKe3RHT4.js +113 -0
  9. package/dist/{button-CvjkwdSk.js → button-BQRdqQi1.js} +199 -192
  10. package/dist/{capabilities-CQ5IjhzL.js → capabilities-CI4xUPsp.js} +2 -2
  11. package/dist/{chat-ui-DaHynk8g.js → chat-ui-CvB8Map3.js} +1142 -1141
  12. package/dist/{check-EDwSwP89.js → check-SpOMoCLP.js} +1 -1
  13. package/dist/{chunk-IMKFNOWR-DGxi-zeo.js → chunk-IMKFNOWR-hJXey5p2.js} +1 -1
  14. package/dist/{chunk-SHT3W25Y-CV9J3rAB.js → chunk-SHT3W25Y-JMzTmmVW.js} +1 -1
  15. package/dist/{chunk-SVP7TREG-BL-9Fqgg.js → chunk-SVP7TREG-BIyoHTXi.js} +1 -1
  16. package/dist/{chunk-TICWLB2K-Cu_XMjDM.js → chunk-TICWLB2K-CCzH2Uwa.js} +1 -1
  17. package/dist/{chunk-YOKDWASO-DPZk8vhS.js → chunk-YOKDWASO-DKplTrBY.js} +20 -20
  18. package/dist/{classDiagram-ZZMXUADV-BXXV13Hq.js → classDiagram-ZZMXUADV-DB1kOPS6.js} +2 -2
  19. package/dist/{classDiagram-v2-VYDZK3BY-B7FXJ1Cp.js → classDiagram-v2-VYDZK3BY-CPaFL0OB.js} +2 -2
  20. package/dist/{code-visibility-BFMjPeTv.js → code-visibility-DIEAT978.js} +6 -6
  21. package/dist/{common-JHV-U5GC.js → common-BA_SF-5o.js} +6827 -6647
  22. package/dist/{copy-icon-Ft7t9bqz.js → copy-icon-C1trdvVt.js} +3 -3
  23. package/dist/{cynefinDiagram-5FMLGOSQ-CH96rwba.js → cynefinDiagram-5FMLGOSQ-CF93uliH.js} +1 -1
  24. package/dist/{dagre-GXQ25YYZ-BD3PmpFY.js → dagre-GXQ25YYZ-BBB-_p9D.js} +1 -1
  25. package/dist/{data-grid-overlay-editor-BkL0DXBi.js → data-grid-overlay-editor-Byuku-gi.js} +5 -5
  26. package/dist/{data-grid-types-BH_r7g7l.js → data-grid-types-0PJ-9N5n.js} +1 -1
  27. package/dist/{diagram-S7CK7UJ4-BsSJJldB.js → diagram-S7CK7UJ4-BynCnbyt.js} +1 -1
  28. package/dist/{diagram-UQ7AKVKN-muQtNZc4.js → diagram-UQ7AKVKN-svOxikMa.js} +1 -1
  29. package/dist/{diagram-VX7I27RA-CdhuZAqR.js → diagram-VX7I27RA-BVQSnPx0.js} +1 -1
  30. package/dist/{diagram-Z3DM3KII-BD8mXjOj.js → diagram-Z3DM3KII-nR1J0BH5.js} +1 -1
  31. package/dist/{dialog-CEBFZNyc.js → dialog-DgeqyrEZ.js} +3 -3
  32. package/dist/{dist-DpBpA_n1.js → dist-Lm-D-Vr6.js} +3 -3
  33. package/dist/{dropdown-menu-CP_qCYHC.js → dropdown-menu-GWcGCcxE.js} +1277 -1229
  34. package/dist/{ebnfDiagram-PWID7BFC-BIN1sweE.js → ebnfDiagram-PWID7BFC-C9tq9-Qo.js} +2 -2
  35. package/dist/{erDiagram-RLTQ6QDP-Ctp5cfoN.js → erDiagram-RLTQ6QDP-BiaeB7Fi.js} +1 -1
  36. package/dist/{error-banner-D-WCIiMt.js → error-banner-D40pmfQQ.js} +83 -83
  37. package/dist/{error-entries-Bv6bZPF3.js → error-entries-CmFDQ9CA.js} +1018 -977
  38. package/dist/{feedback-button-do3RdfYX.js → feedback-button-BwgAjZDg.js} +23 -23
  39. package/dist/{flowDiagram-HODETNUW-3EfLrlFb.js → flowDiagram-HODETNUW-f-sLm87k.js} +2 -2
  40. package/dist/{formats-27CW_UP4.js → formats-CCxaLPNs.js} +3 -3
  41. package/dist/{ganttDiagram-EL5Y4UJY-BQT16JSc.js → ganttDiagram-EL5Y4UJY-Dh_4cBJZ.js} +2 -2
  42. package/dist/{glide-data-editor-B2KXsLT5.js → glide-data-editor-BCX4vz1V.js} +370 -368
  43. package/dist/{glide-utils-DJyqh0EM.js → glide-utils-Dz_F4pAY.js} +16 -16
  44. package/dist/{globals-Cnjg2Own.js → globals-2FASSt-d.js} +3 -3
  45. package/dist/{highlighted-body-KPVGNVTW-zwjq21tf.js → highlighted-body-KPVGNVTW-3Ziwfz-V.js} +2 -2
  46. package/dist/{html-to-image-Bf8QMGi4.js → html-to-image-BrC8OJcj.js} +632 -595
  47. package/dist/{infoDiagram-27XIBGKW-DipQoHxl.js → infoDiagram-27XIBGKW-BHrWywBe.js} +1 -1
  48. package/dist/{ishikawaDiagram-5VMMS53U-DgFMjSGz.js → ishikawaDiagram-5VMMS53U-Da4s0Reg.js} +1 -1
  49. package/dist/{kanban-definition-UXKFOSKX-w1ACVjgl.js → kanban-definition-UXKFOSKX-DDf1GVLj.js} +1 -1
  50. package/dist/{label-BDKF3Afc.js → label-D9_-zSg-.js} +3 -3
  51. package/dist/{linear-Cnaq3Tfb.js → linear-DWERh7eI.js} +121 -73
  52. package/dist/{loader-BQOfjTIJ.js → loader-DOVB0Syl.js} +12 -12
  53. package/dist/main.js +7553 -7232
  54. package/dist/{mermaid-CHo49Yv-.js → mermaid-ClwTt7OD.js} +38 -38
  55. package/dist/mermaid-HWGCJPDP-Du3-HPQA.js +2 -0
  56. package/dist/{mindmap-definition-YA3MSWOX-CrT0KSfX.js → mindmap-definition-YA3MSWOX-D60oMZtT.js} +1 -1
  57. package/dist/{number-overlay-editor-DBsAEYkJ.js → number-overlay-editor-6JHsXwu3.js} +3 -3
  58. package/dist/{objects-CzUgoMIF.js → objects-DCmMvZtn.js} +1 -3
  59. package/dist/{once-BtE-wKRL.js → once-eG8Vmvjd.js} +1 -1
  60. package/dist/{pegDiagram-XKGWAZYB-BqvywNJA.js → pegDiagram-XKGWAZYB-B9wFpMga.js} +2 -2
  61. package/dist/{pieDiagram-E7YTZNPT-BfxJ_XCc.js → pieDiagram-E7YTZNPT-BZwvMuBT.js} +1 -1
  62. package/dist/precisionRound-DhAzeRcm.js +49 -0
  63. package/dist/{process-output-BZg56CCb.js → process-output-Bw-vzyoy.js} +5 -5
  64. package/dist/{quadrantDiagram-AXDQQJYC-Ccw0xMjN.js → quadrantDiagram-AXDQQJYC-Blrm7V7_.js} +1 -1
  65. package/dist/{railroadDiagram-O6MQD6OU-8vFLVTPu.js → railroadDiagram-O6MQD6OU-DqhZGsN2.js} +2 -2
  66. package/dist/{react-vega-BED0DjiB.js → react-vega-C4T9Rb_w.js} +12284 -12270
  67. package/dist/{requirementDiagram-BXWQKSXE-a_MxGvxe.js → requirementDiagram-BXWQKSXE-CL5dYARV.js} +1 -1
  68. package/dist/{reveal-component-IkMCCFzA.js → reveal-component-BGe2tbbp.js} +14 -14
  69. package/dist/{select-BButFi-d.js → select-CkF_GBxO.js} +537 -458
  70. package/dist/{sequenceDiagram-WJ2MYXX4-CXFMnxGh.js → sequenceDiagram-WJ2MYXX4-BSUCRd2x.js} +1 -1
  71. package/dist/{spec-BwarBpUJ.js → spec-DiSh_qJC.js} +41 -11
  72. package/dist/{stateDiagram-D77RDMKH-DQeA_CwI.js → stateDiagram-D77RDMKH-B-1Hvyb0.js} +2 -2
  73. package/dist/{stateDiagram-v2-MP3YSRHH-BxxQucm1.js → stateDiagram-v2-MP3YSRHH--HtiR4yB.js} +2 -2
  74. package/dist/style.css +1 -1
  75. package/dist/{swimlanes-42K2YHIH-Do6L-cYg.js → swimlanes-42K2YHIH-Bdfhrd-2.js} +1 -1
  76. package/dist/{swimlanesDiagram-VR7AAH4N-DUzPvSVU.js → swimlanesDiagram-VR7AAH4N-BJIfxSz0.js} +2 -2
  77. package/dist/{swiper-component-DKntyn8p.js → swiper-component-BkCm-kLg.js} +2 -2
  78. package/dist/{time-BieKwdYS.js → time-BaMbJs4z.js} +18 -18
  79. package/dist/{timeline-definition-24CTP7MA-D3c46WM5.js → timeline-definition-24CTP7MA-Cs4EfFD3.js} +1 -1
  80. package/dist/{toDate-UtiwmnH2.js → toDate-DGvRHwTk.js} +19 -18
  81. package/dist/{toast-B9UMEi8u.js → toast-Btbve3Lm.js} +231 -213
  82. package/dist/{tooltip-BO1REbqs.js → tooltip-CJ4Rqasp.js} +327 -303
  83. package/dist/{use-restore-focus-BEM7sFWl.js → use-restore-focus-CmGwRNId.js} +1482 -1433
  84. package/dist/{useAsyncData-Deh5kszT.js → useAsyncData-BmVgnMP5.js} +15 -14
  85. package/dist/{useDateFormatter-D8XruNGG.js → useDateFormatter-eCL_SdeG.js} +2 -2
  86. package/dist/{useDeepCompareMemoize-DXwzRINI.js → useDeepCompareMemoize-Ch_AeGu6.js} +1 -1
  87. package/dist/{useIframeCapabilities-DBETvvPl.js → useIframeCapabilities-CSdWn6NP.js} +1 -1
  88. package/dist/{useLifecycle-j7D9uD1o.js → useLifecycle-B5BX921H.js} +3 -3
  89. package/dist/{useTheme-CRnjkku0.js → useTheme-Bq9pi_vz.js} +6 -6
  90. package/dist/{vega-component-BeF9dfyO.js → vega-component-BgyZ-COt.js} +147 -147
  91. package/dist/{vega-embed-container-BNHE18G9.js → vega-embed-container--LYZqguO.js} +2 -2
  92. package/dist/{vega-loader.browser-DNST6uVf.js → vega-loader.browser-B9Pz0EGP.js} +298 -507
  93. package/dist/{vennDiagram-4TSXK5OY-BSZD8JcZ.js → vennDiagram-4TSXK5OY-DRu0vRjO.js} +1 -1
  94. package/dist/{wardleyDiagram-VM6X3IG4-48cUWkfA.js → wardleyDiagram-VM6X3IG4-DzbDxwDY.js} +1 -1
  95. package/dist/{xychartDiagram-S5SC5T6Z-CswYgYeu.js → xychartDiagram-S5SC5T6Z-CLWj6ZFQ.js} +2 -2
  96. package/package.json +16 -16
  97. package/src/__tests__/module-entry.test.ts +44 -0
  98. package/src/__tests__/mount.test.ts +1 -21
  99. package/src/__tests__/{host-imports.test.ts → replica-imports.test.ts} +12 -11
  100. package/src/components/ai/__tests__/ai-model-dropdown.test.tsx +63 -0
  101. package/src/components/ai/ai-model-dropdown.tsx +11 -6
  102. package/src/components/app-config/ai-config.tsx +7 -2
  103. package/src/components/chat/__tests__/message-queue.test.tsx +17 -0
  104. package/src/components/chat/acp/__tests__/session-tabs.test.tsx +43 -0
  105. package/src/components/chat/acp/__tests__/state.test.ts +2 -2
  106. package/src/components/chat/acp/blocks.tsx +3 -2
  107. package/src/components/chat/acp/session-tabs.tsx +8 -3
  108. package/src/components/chat/chat-components.tsx +2 -0
  109. package/src/components/chat/chat-panel.tsx +9 -8
  110. package/src/components/chat/chat-utils.ts +4 -8
  111. package/src/components/data-table/TableTopBar.tsx +2 -0
  112. package/src/components/data-table/__tests__/chart-spec-model.test.ts +195 -195
  113. package/src/components/data-table/__tests__/column-explorer.test.tsx +27 -1
  114. package/src/components/data-table/__tests__/columns.test.tsx +105 -0
  115. package/src/components/data-table/__tests__/context-menu-interaction.test.tsx +130 -0
  116. package/src/components/data-table/__tests__/data-table.test.tsx +1 -0
  117. package/src/components/data-table/__tests__/date-popover.test.tsx +72 -0
  118. package/src/components/data-table/__tests__/export-actions.test.tsx +105 -0
  119. package/src/components/data-table/__tests__/keyboard-selection.test.tsx +188 -0
  120. package/src/components/data-table/__tests__/search-highlight.test.tsx +109 -0
  121. package/src/components/data-table/__tests__/url-detector.test.tsx +74 -0
  122. package/src/components/data-table/cell-selection/feature.ts +2 -2
  123. package/src/components/data-table/cell-selection/types.ts +2 -2
  124. package/src/components/data-table/charts/__tests__/chart-panel.test.tsx +74 -1
  125. package/src/components/data-table/charts/__tests__/spec-snapshot.test.ts +20 -20
  126. package/src/components/data-table/charts/__tests__/spec.test.ts +78 -52
  127. package/src/components/data-table/charts/__tests__/storage.test.ts +4 -1
  128. package/src/components/data-table/charts/chart-spec/spec.ts +68 -43
  129. package/src/components/data-table/charts/charts.tsx +40 -30
  130. package/src/components/data-table/charts/storage.ts +16 -5
  131. package/src/components/data-table/column-explorer-panel/column-explorer.tsx +1 -1
  132. package/src/components/data-table/column-summary/chart-spec-model.tsx +23 -16
  133. package/src/components/data-table/column-summary/column-summary.tsx +2 -4
  134. package/src/components/data-table/column-visibility-dropdown.tsx +8 -13
  135. package/src/components/data-table/columns.tsx +45 -46
  136. package/src/components/data-table/context-menu.tsx +36 -20
  137. package/src/components/data-table/data-table.tsx +13 -1
  138. package/src/components/data-table/date-popover.tsx +6 -4
  139. package/src/components/data-table/export-actions.tsx +76 -48
  140. package/src/components/data-table/range-focus/atoms.ts +4 -6
  141. package/src/components/data-table/range-focus/use-cell-range-selection.ts +65 -38
  142. package/src/components/data-table/range-focus/use-scroll-into-view.ts +21 -2
  143. package/src/components/data-table/renderers.tsx +16 -7
  144. package/src/components/data-table/row-viewer-panel/__tests__/row-viewer.test.tsx +24 -1
  145. package/src/components/data-table/row-viewer-panel/row-viewer.tsx +4 -4
  146. package/src/components/data-table/search-highlight.tsx +79 -0
  147. package/src/components/data-table/types.ts +1 -0
  148. package/src/components/data-table/url-detector.tsx +49 -9
  149. package/src/components/datasources/__tests__/column-actions.test.tsx +97 -0
  150. package/src/components/datasources/__tests__/column-preview.test.tsx +98 -5
  151. package/src/components/datasources/__tests__/datasources-interactions.test.tsx +181 -0
  152. package/src/components/datasources/__tests__/table-list.test.tsx +118 -0
  153. package/src/components/datasources/__tests__/utils.test.ts +57 -6
  154. package/src/components/datasources/column-preview.tsx +13 -4
  155. package/src/components/datasources/datasources.tsx +51 -39
  156. package/src/components/datasources/utils.ts +32 -23
  157. package/src/components/debugger/debugger-code.tsx +5 -0
  158. package/src/components/dependency-graph/dependency-graph-tree.tsx +8 -8
  159. package/src/components/dependency-graph/elements.ts +24 -21
  160. package/src/components/dependency-graph/panels.tsx +2 -1
  161. package/src/components/dependency-graph/utils/__tests__/layout.test.ts +18 -11
  162. package/src/components/editor/SortableCell.tsx +1 -3
  163. package/src/{core/attachments/__tests__/toast.test.tsx → components/editor/__tests__/agent-toast.test.tsx} +1 -1
  164. package/src/components/editor/__tests__/dynamic-favicon.test.tsx +111 -18
  165. package/src/components/editor/__tests__/package-alert.test.tsx +22 -1
  166. package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +24 -0
  167. package/src/components/editor/actions/pair-with-agent-modal.tsx +18 -15
  168. package/src/components/editor/actions/useRestartKernel.tsx +1 -1
  169. package/src/components/editor/ai/add-cell-with-ai.tsx +3 -3
  170. package/src/components/editor/ai/ai-completion-editor.tsx +1 -0
  171. package/src/components/editor/alerts/__tests__/connection-notice.test.tsx +1 -1
  172. package/src/components/editor/alerts/connection-notice.tsx +1 -1
  173. package/src/components/editor/alerts/startup-logs-alert.tsx +1 -0
  174. package/src/components/editor/app-container.tsx +1 -1
  175. package/src/components/editor/boundary/__tests__/ErrorBoundary.test.tsx +1 -1
  176. package/src/components/editor/cell/CreateCellButton.tsx +10 -12
  177. package/src/components/editor/cell/DeleteButton.tsx +3 -2
  178. package/src/components/editor/cell/RunButton.tsx +2 -2
  179. package/src/components/editor/cell/StopButton.tsx +2 -2
  180. package/src/components/editor/cell/__tests__/useShouldShowInterrupt.test.ts +39 -0
  181. package/src/components/editor/cell/code/__tests__/language-toggle.test.tsx +54 -0
  182. package/src/components/editor/cell/code/cell-editor.tsx +2 -2
  183. package/src/components/editor/cell/code/language-toggle.tsx +8 -7
  184. package/src/components/editor/cell/collapse.tsx +5 -3
  185. package/src/components/editor/cell/useShouldShowInterrupt.ts +2 -12
  186. package/src/components/editor/chrome/components/__tests__/feedback-button.test.tsx +1 -1
  187. package/src/components/editor/chrome/components/feedback-button.tsx +1 -1
  188. package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +31 -2
  189. package/src/components/editor/chrome/panels/__tests__/runtime-details.test.tsx +1 -1
  190. package/src/components/editor/chrome/panels/__tests__/sandbox-recovery.test.tsx +1 -1
  191. package/src/components/editor/chrome/panels/cache-panel.tsx +1 -0
  192. package/src/components/editor/chrome/panels/outline/__tests__/outline-keyboard.test.tsx +84 -0
  193. package/src/components/editor/chrome/panels/outline/floating-outline.tsx +39 -14
  194. package/src/components/editor/chrome/panels/outline/useActiveOutline.tsx +3 -2
  195. package/src/components/editor/chrome/panels/packages-panel.tsx +4 -7
  196. package/src/components/editor/chrome/panels/secrets-panel.tsx +1 -0
  197. package/src/components/editor/chrome/panels/snippets-panel.tsx +5 -1
  198. package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +9 -18
  199. package/src/components/editor/chrome/wrapper/__tests__/pending-ai-cells.test.tsx +58 -1
  200. package/src/components/editor/chrome/wrapper/footer-items/attachment-status.tsx +4 -4
  201. package/src/components/editor/chrome/wrapper/footer-items/backend-status.tsx +1 -1
  202. package/src/components/editor/chrome/wrapper/footer-items/lsp-status.tsx +1 -1
  203. package/src/components/editor/chrome/wrapper/footer-items/machine-stats.tsx +1 -1
  204. package/src/components/editor/chrome/wrapper/footer-items/runtime-settings.tsx +1 -7
  205. package/src/components/editor/chrome/wrapper/pending-ai-cells.tsx +12 -2
  206. package/src/components/editor/chrome/wrapper/storage.ts +1 -1
  207. package/src/components/editor/connections/components.tsx +1 -1
  208. package/src/components/editor/controls/Controls.tsx +2 -2
  209. package/src/components/editor/controls/__tests__/command-palette.test.tsx +53 -0
  210. package/src/components/editor/controls/__tests__/keyboard-shortcuts.test.tsx +61 -0
  211. package/src/components/editor/controls/__tests__/notebook-menu-dropdown.test.tsx +1 -1
  212. package/src/components/editor/controls/command-palette.tsx +22 -10
  213. package/src/components/editor/controls/keyboard-shortcuts.tsx +14 -5
  214. package/src/components/editor/dynamic-favicon.tsx +21 -40
  215. package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +9 -23
  216. package/src/components/editor/errors/send-error-report-button.tsx +3 -6
  217. package/src/components/editor/file-tree/__tests__/file-explorer-interactions.test.tsx +6 -0
  218. package/src/components/editor/file-tree/__tests__/file-explorer-navigation.test.tsx +47 -0
  219. package/src/components/editor/file-tree/__tests__/use-file-search.test.tsx +91 -0
  220. package/src/components/editor/file-tree/file-explorer.tsx +45 -8
  221. package/src/components/editor/file-tree/file-name-input.tsx +1 -0
  222. package/src/components/editor/file-tree/file-viewer.tsx +4 -2
  223. package/src/components/editor/file-tree/use-file-search.ts +35 -21
  224. package/src/components/editor/header/__tests__/status.test.tsx +5 -3
  225. package/src/components/editor/header/app-header.tsx +1 -1
  226. package/src/components/editor/header/status.tsx +40 -20
  227. package/src/components/editor/links/__tests__/cell-link.test.tsx +146 -0
  228. package/src/components/editor/links/cell-link.tsx +14 -5
  229. package/src/components/editor/navigation/focus-utils.ts +4 -4
  230. package/src/components/editor/notebook-banner.tsx +1 -0
  231. package/src/components/editor/notebook-cell.tsx +12 -3
  232. package/src/components/editor/output/MarimoTracebackOutput.tsx +19 -17
  233. package/src/components/editor/output/__tests__/traceback.test.tsx +27 -1
  234. package/src/components/editor/output/ansi-reduce.ts +33 -20
  235. package/src/components/editor/output/console/ConsoleOutput.tsx +19 -25
  236. package/src/components/editor/package-alert.tsx +14 -7
  237. package/src/components/editor/renderers/grid-layout/grid-layout.tsx +12 -6
  238. package/src/components/editor/renderers/vertical-layout/sidebar/toggle.tsx +7 -1
  239. package/src/components/home/components.tsx +17 -31
  240. package/src/components/icons/__tests__/large-spinner.test.tsx +39 -0
  241. package/src/components/icons/large-spinner.tsx +3 -14
  242. package/src/components/icons/spinner.tsx +1 -1
  243. package/src/components/mcp/mcp-status-indicator.tsx +1 -1
  244. package/src/components/pages/__tests__/home-page.test.tsx +116 -0
  245. package/src/components/pages/home-page.tsx +164 -91
  246. package/src/components/scratchpad/scratchpad.test.tsx +95 -0
  247. package/src/components/scratchpad/scratchpad.tsx +16 -3
  248. package/src/components/slides/reveal-component.tsx +4 -2
  249. package/src/components/slides/slide-cell-view.tsx +1 -1
  250. package/src/components/slides/slide-form.tsx +1 -0
  251. package/src/components/tracing/tracing-keyboard.test.tsx +85 -0
  252. package/src/components/tracing/tracing.tsx +45 -41
  253. package/src/components/ui/__tests__/draggable-popover.test.tsx +24 -0
  254. package/src/components/ui/__tests__/range-slider.test.tsx +165 -0
  255. package/src/components/ui/alert-dialog.tsx +1 -1
  256. package/src/components/ui/draggable-popover.tsx +24 -2
  257. package/src/components/ui/range-slider.tsx +22 -9
  258. package/src/core/ai/context/providers/datasource.ts +6 -2
  259. package/src/core/ai/tools/base.ts +16 -7
  260. package/src/core/ai/tools/edit-notebook-tool.ts +47 -45
  261. package/src/core/ai/tools/registry.ts +13 -8
  262. package/src/core/ai/tools/sample-tool.ts +6 -7
  263. package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +4 -0
  264. package/src/core/cells/__tests__/cell.test.ts +39 -0
  265. package/src/core/cells/__tests__/cells.test.ts +3 -1
  266. package/src/core/cells/__tests__/collapseConsoleOutputs.test.ts +54 -0
  267. package/src/core/cells/__tests__/notebook-outline.test.ts +113 -0
  268. package/src/core/cells/__tests__/session.test.ts +117 -36
  269. package/src/core/cells/cell.ts +14 -3
  270. package/src/core/cells/cells.ts +3 -1
  271. package/src/core/cells/pending-delete-service.ts +2 -6
  272. package/src/core/cells/session.ts +1 -1
  273. package/src/core/cells/types.ts +10 -1
  274. package/src/core/codemirror/__tests__/setup.test.ts +29 -29
  275. package/src/core/codemirror/cells/__tests__/extensions.test.ts +15 -0
  276. package/src/core/codemirror/cells/extensions.ts +7 -3
  277. package/src/core/codemirror/keymaps/vim.ts +2 -6
  278. package/src/core/codemirror/language/__tests__/sql.test.ts +22 -20
  279. package/src/core/codemirror/language/extension.ts +8 -8
  280. package/src/core/codemirror/language/languages/markdown.ts +8 -6
  281. package/src/core/codemirror/language/languages/python.ts +16 -10
  282. package/src/core/codemirror/language/languages/sql/sql.ts +9 -7
  283. package/src/core/codemirror/language/panel/panel.tsx +1 -0
  284. package/src/core/codemirror/language/types.ts +7 -7
  285. package/src/core/codemirror/readonly/__tests__/extension.test.ts +1 -1
  286. package/src/core/codemirror/readonly/extension.ts +2 -2
  287. package/src/core/codemirror/rtc/extension.ts +24 -12
  288. package/src/core/codemirror/rtc/loro/awareness.ts +34 -19
  289. package/src/core/codemirror/rtc/loro/sync.ts +1 -0
  290. package/src/core/codemirror/utils.ts +1 -0
  291. package/src/core/datasets/__tests__/data-source-discovery.test.ts +5 -5
  292. package/src/core/datasets/data-source-discovery.ts +4 -6
  293. package/src/core/edit-app.tsx +2 -2
  294. package/src/core/export/hooks.ts +1 -1
  295. package/src/core/hotkeys/actions.ts +2 -4
  296. package/src/core/islands/__tests__/bootstrap.test.ts +16 -42
  297. package/src/core/islands/__tests__/bridge.test.ts +31 -26
  298. package/src/core/islands/bootstrap.ts +22 -52
  299. package/src/core/islands/bridge.ts +31 -20
  300. package/src/core/islands/components/__tests__/IslandControls.test.tsx +7 -5
  301. package/src/core/islands/worker/__tests__/controller.test.ts +62 -4
  302. package/src/core/islands/worker/controller.ts +4 -10
  303. package/src/core/islands/worker/worker.tsx +33 -33
  304. package/src/core/kernel/__tests__/RuntimeState.test.ts +1 -1
  305. package/src/core/network/__tests__/connection.test.ts +1 -1
  306. package/src/core/network/connection-notice.ts +1 -1
  307. package/src/core/network/connection.ts +5 -3
  308. package/src/core/network/requests-lazy.ts +1 -1
  309. package/src/core/network/useConnectionNotice.ts +8 -2
  310. package/src/core/notebook/attachments.ts +38 -0
  311. package/src/core/notebook/document/__tests__/codemirror.test.ts +485 -0
  312. package/src/core/notebook/document/__tests__/draft.test.ts +322 -0
  313. package/src/core/notebook/document/__tests__/fixtures.ts +92 -0
  314. package/src/core/notebook/document/__tests__/hooks.test.tsx +391 -0
  315. package/src/core/notebook/document/__tests__/lifecycle-scenarios.test.ts +61 -0
  316. package/src/core/notebook/document/__tests__/model.test.ts +191 -0
  317. package/src/core/notebook/document/__tests__/protocol-fixtures.ts +38 -0
  318. package/src/core/notebook/document/__tests__/recovery-scenarios.test.ts +230 -0
  319. package/src/core/notebook/document/__tests__/scenario-fixture.ts +43 -0
  320. package/src/core/notebook/document/__tests__/scenarios.test.ts +205 -0
  321. package/src/core/notebook/document/__tests__/state.test.ts +438 -0
  322. package/src/core/notebook/document/__tests__/submission.test.ts +203 -0
  323. package/src/core/notebook/document/__tests__/test-host.test.ts +92 -0
  324. package/src/core/notebook/document/__tests__/test-host.ts +247 -0
  325. package/src/core/notebook/document/__tests__/test-notebook.ts +149 -0
  326. package/src/core/notebook/document/__tests__/user.ts +61 -0
  327. package/src/core/notebook/document/codemirror.ts +311 -0
  328. package/src/core/notebook/document/draft.ts +472 -0
  329. package/src/core/notebook/document/hooks.tsx +118 -0
  330. package/src/core/notebook/document/index.ts +15 -0
  331. package/src/core/notebook/document/model.ts +55 -0
  332. package/src/core/notebook/document/select.ts +41 -0
  333. package/src/core/notebook/document/state.ts +374 -0
  334. package/src/core/notebook/document/sync.ts +369 -0
  335. package/src/core/notebook/index.ts +18 -0
  336. package/src/core/notebook/replica/AGENTS.md +11 -0
  337. package/src/core/notebook/replica/__tests__/attachments.test.ts +56 -0
  338. package/src/core/notebook/replica/__tests__/document.test.ts +126 -0
  339. package/src/core/notebook/replica/__tests__/fixtures.ts +79 -0
  340. package/src/core/notebook/replica/__tests__/replica.test.ts +550 -0
  341. package/src/core/notebook/replica/backend.ts +19 -0
  342. package/src/core/notebook/replica/connection.ts +42 -0
  343. package/src/core/notebook/replica/index.ts +13 -0
  344. package/src/core/notebook/replica/replica.ts +286 -0
  345. package/src/core/notebook/replica/resources/attachments.ts +22 -0
  346. package/src/core/notebook/replica/resources/document.ts +101 -0
  347. package/src/core/packages/__tests__/useSandboxController.test.tsx +1 -1
  348. package/src/core/packages/useSandboxController.ts +1 -1
  349. package/src/core/run-app.tsx +3 -3
  350. package/src/core/runtime/__tests__/adapter.test.ts +1 -1
  351. package/src/core/runtime/__tests__/createWsUrl.test.ts +77 -7
  352. package/src/core/runtime/__tests__/runtime.test.ts +32 -58
  353. package/src/core/runtime/adapter.ts +1 -1
  354. package/src/core/runtime/config.ts +2 -2
  355. package/src/core/runtime/runtime.ts +38 -42
  356. package/src/core/runtime/types.ts +0 -2
  357. package/src/core/saving/filename.ts +1 -1
  358. package/src/core/saving/save-component.tsx +13 -4
  359. package/src/core/saving/useAutoSave.ts +1 -1
  360. package/src/core/session/__tests__/backend.test.ts +235 -0
  361. package/src/core/session/__tests__/useNotebookReplica.hook.test.tsx +183 -0
  362. package/src/core/session/__tests__/useNotebookSession.hook.test.tsx +609 -0
  363. package/src/core/session/active-notebook.ts +7 -0
  364. package/src/core/session/backend.ts +125 -0
  365. package/src/core/{websocket → session}/types.ts +2 -2
  366. package/src/core/session/useNotebookReplica.ts +72 -0
  367. package/src/core/{websocket → session}/useNotebookSession.tsx +18 -25
  368. package/src/core/state/__tests__/jotai.test.tsx +108 -0
  369. package/src/core/state/jotai.ts +1 -1
  370. package/src/core/static/__tests__/files.test.ts +38 -10
  371. package/src/core/static/files.ts +2 -0
  372. package/src/core/{websocket → transports}/__tests__/connection-state.test.ts +1 -13
  373. package/src/core/transports/__tests__/server.test.ts +408 -0
  374. package/src/core/transports/__tests__/static.test.ts +175 -0
  375. package/src/core/transports/__tests__/wasm.test.ts +166 -0
  376. package/src/core/{websocket → transports}/connection-state.ts +9 -16
  377. package/src/core/transports/server.ts +152 -0
  378. package/src/core/transports/static.ts +84 -0
  379. package/src/core/transports/wasm.ts +97 -0
  380. package/src/core/wasm/__tests__/bridge.test.ts +213 -50
  381. package/src/core/wasm/__tests__/worker.test.ts +187 -0
  382. package/src/core/wasm/bridge.ts +81 -52
  383. package/src/core/wasm/rpc.ts +15 -3
  384. package/src/core/wasm/store.ts +3 -3
  385. package/src/core/wasm/worker/bootstrap.ts +55 -51
  386. package/src/core/wasm/worker/getMarimoWheel.ts +1 -1
  387. package/src/core/wasm/worker/save-worker.ts +9 -10
  388. package/src/core/wasm/worker/types.ts +29 -18
  389. package/src/core/wasm/worker/worker.ts +97 -44
  390. package/src/css/globals.css +5 -0
  391. package/src/hooks/__tests__/useAsyncData.test.tsx +148 -0
  392. package/src/hooks/__tests__/useDebounce.test.tsx +51 -0
  393. package/src/hooks/__tests__/useDeepCompareMemoize.test.tsx +39 -0
  394. package/src/hooks/__tests__/useDelayElapsed.test.tsx +28 -0
  395. package/src/hooks/__tests__/useInternalStateWithSync.test.tsx +20 -0
  396. package/src/hooks/__tests__/useScript.test.tsx +77 -0
  397. package/src/hooks/__tests__/useStateForKey.test.tsx +257 -0
  398. package/src/hooks/useAsyncData.ts +3 -3
  399. package/src/hooks/useDataSourceDiscovery.ts +6 -4
  400. package/src/hooks/useDebounce.ts +6 -10
  401. package/src/hooks/useDeepCompareMemoize.ts +1 -1
  402. package/src/hooks/useDelayElapsed.ts +14 -10
  403. package/src/hooks/useElapsedTime.ts +3 -3
  404. package/src/hooks/useEventListener.ts +1 -2
  405. package/src/hooks/useInputHistory.ts +4 -7
  406. package/src/hooks/useInternalStateWithSync.ts +6 -6
  407. package/src/hooks/useResizeObserver.ts +4 -7
  408. package/src/hooks/useScript.ts +67 -70
  409. package/src/hooks/useStateForKey.ts +93 -0
  410. package/src/mount.tsx +1 -5
  411. package/src/plugins/__tests__/plugin-object-schemas.test.ts +104 -0
  412. package/src/plugins/core/registerReactComponent.tsx +1 -1
  413. package/src/plugins/impl/CodeEditorPlugin.tsx +1 -1
  414. package/src/plugins/impl/DataEditorPlugin.tsx +1 -1
  415. package/src/plugins/impl/DataTablePlugin.tsx +29 -33
  416. package/src/plugins/impl/DropdownPlugin.tsx +2 -6
  417. package/src/plugins/impl/FileBrowserPlugin.tsx +4 -16
  418. package/src/plugins/impl/FormPlugin.tsx +1 -1
  419. package/src/plugins/impl/RangeSliderPlugin.tsx +16 -2
  420. package/src/plugins/impl/RefreshPlugin.tsx +1 -0
  421. package/src/plugins/impl/SliderPlugin.tsx +6 -8
  422. package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +117 -1
  423. package/src/plugins/impl/__tests__/FileBrowserPlugin.test.tsx +45 -0
  424. package/src/plugins/impl/__tests__/RangeSliderPlugin.test.tsx +109 -0
  425. package/src/plugins/impl/__tests__/SliderPlugin.test.tsx +24 -3
  426. package/src/plugins/impl/anywidget/__tests__/registry.test.ts +2 -0
  427. package/src/plugins/impl/anywidget/__tests__/widget-binding.test.ts +241 -2
  428. package/src/plugins/impl/anywidget/runtime.ts +21 -9
  429. package/src/plugins/impl/anywidget/widget-binding.ts +160 -82
  430. package/src/plugins/impl/chat/__tests__/chat-prompts.test.tsx +93 -0
  431. package/src/plugins/impl/chat/chat-ui.tsx +14 -11
  432. package/src/plugins/impl/code/any-language-editor.tsx +2 -2
  433. package/src/plugins/impl/data-editor/glide-data-editor.tsx +8 -5
  434. package/src/plugins/impl/data-editor/glide-utils.ts +3 -1
  435. package/src/plugins/impl/data-explorer/ConnectedDataExplorerComponent.tsx +1 -1
  436. package/src/plugins/impl/data-explorer/components/column-summary.tsx +4 -2
  437. package/src/plugins/impl/data-explorer/state/reducer.ts +1 -1
  438. package/src/plugins/impl/data-frames/DataFramePlugin.tsx +1 -2
  439. package/src/plugins/impl/data-frames/__tests__/sidebar.test.tsx +160 -0
  440. package/src/plugins/impl/data-frames/forms/__tests__/__snapshots__/form.test.tsx.snap +66 -66
  441. package/src/plugins/impl/data-frames/panel.tsx +43 -16
  442. package/src/plugins/impl/mpl-interactive/MplInteractivePlugin.tsx +5 -3
  443. package/src/plugins/impl/panel/PanelPlugin.tsx +16 -17
  444. package/src/plugins/impl/panel/__tests__/utils.test.ts +39 -1
  445. package/src/plugins/impl/panel/utils.ts +17 -0
  446. package/src/plugins/impl/plotly/PlotlyPlugin.tsx +2 -5
  447. package/src/plugins/impl/plotly/selection.ts +25 -19
  448. package/src/plugins/impl/vega/VegaPlugin.tsx +2 -3
  449. package/src/plugins/impl/vega/make-selectable.ts +4 -7
  450. package/src/plugins/layout/RoutesPlugin.tsx +1 -1
  451. package/src/stories/__tests__/editor-story.test.tsx +33 -0
  452. package/src/stories/button.stories.tsx +1 -0
  453. package/src/stories/cell.stories.tsx +1 -1
  454. package/src/stories/editor.stories.tsx +24 -17
  455. package/src/stories/form-wrapper.stories.tsx +2 -2
  456. package/src/stories/layout/vertical/one-column.stories.tsx +1 -1
  457. package/src/stories/runtime-story.tsx +1 -1
  458. package/src/stories/select.stories.tsx +2 -2
  459. package/src/stories/switchable-multi-select.stories.tsx +1 -1
  460. package/src/utils/__tests__/arrays.test.ts +35 -0
  461. package/src/utils/__tests__/batch-requests.test.ts +3 -3
  462. package/src/utils/__tests__/numbers.test.ts +105 -1
  463. package/src/utils/__tests__/objects.test.ts +21 -0
  464. package/src/utils/arrays.ts +11 -2
  465. package/src/utils/blob.ts +1 -1
  466. package/src/utils/maps.ts +0 -3
  467. package/src/utils/numbers.ts +79 -18
  468. package/src/utils/objects.ts +4 -9
  469. package/src/utils/once.ts +19 -19
  470. package/src/utils/tracer.ts +9 -5
  471. package/src/utils/zod-utils.ts +1 -3
  472. package/dist/any-language-editor-DZ_YTVus.js +0 -76
  473. package/dist/assets/__vite-browser-external-BXYkiFsk.js +0 -1
  474. package/dist/assets/worker-HPY9fbhz.js +0 -114
  475. package/dist/mermaid-HWGCJPDP-ByCsZnYF.js +0 -2
  476. package/dist/precisionRound-DMIVQrt2.js +0 -97
  477. package/src/core/attachments/state.ts +0 -21
  478. package/src/core/host/AGENTS.md +0 -6
  479. package/src/core/host/__tests__/events.test.ts +0 -46
  480. package/src/core/host/__tests__/notebook-client.test.ts +0 -191
  481. package/src/core/host/__tests__/protocol.test.ts +0 -214
  482. package/src/core/host/connection.ts +0 -81
  483. package/src/core/host/events.ts +0 -60
  484. package/src/core/host/notebook-client.ts +0 -226
  485. package/src/core/host/protocol.ts +0 -1943
  486. package/src/core/state/__tests__/jotai.test.ts +0 -25
  487. package/src/core/wasm/worker/message-buffer.ts +0 -33
  488. package/src/core/websocket/__tests__/createConnectionTransport.test.ts +0 -49
  489. package/src/core/websocket/__tests__/useNotebookClient.hook.test.tsx +0 -140
  490. package/src/core/websocket/__tests__/useNotebookSession.hook.test.tsx +0 -883
  491. package/src/core/websocket/transports/__tests__/basic.test.ts +0 -275
  492. package/src/core/websocket/transports/__tests__/sse.test.ts +0 -335
  493. package/src/core/websocket/transports/__tests__/transport.test.ts +0 -308
  494. package/src/core/websocket/transports/__tests__/ws.test.ts +0 -125
  495. package/src/core/websocket/transports/basic.ts +0 -102
  496. package/src/core/websocket/transports/create.ts +0 -31
  497. package/src/core/websocket/transports/sse.ts +0 -308
  498. package/src/core/websocket/transports/transport.ts +0 -50
  499. package/src/core/websocket/transports/ws.ts +0 -98
  500. package/src/core/websocket/useNotebookClient.ts +0 -80
  501. /package/src/{core/attachments/toast.ts → components/editor/agent-toast.ts} +0 -0
  502. /package/src/core/{websocket → session}/connection-utils.ts +0 -0
@@ -59,7 +59,7 @@ import {
59
59
  shouldHidePublishedCell,
60
60
  } from "../../core/cells/utils";
61
61
  import type { UserConfig } from "../../core/config/config-schema";
62
- import { isAppInteractionDisabled } from "../../core/websocket/connection-utils";
62
+ import { isAppInteractionDisabled } from "../../core/session/connection-utils";
63
63
  import { useCellRenderCount } from "../../hooks/useCellRenderCount";
64
64
  import type { Theme } from "../../theme/useTheme";
65
65
  import { Functions } from "../../utils/functions";
@@ -525,13 +525,22 @@ const EditableCellComponent = ({
525
525
  isMarkdownCodeHidden &&
526
526
  isEmptyMarkdownContent &&
527
527
  !needsRun && (
528
+ // Preserve pointer double-click editing; keyboard and assistive clicks activate directly.
529
+ // react-doctor-disable-next-line react-doctor/prefer-tag-over-role
528
530
  <div
531
+ // eslint-disable-next-line jsx-a11y/prefer-tag-over-role
529
532
  role="button"
530
- aria-label="Double-click to edit markdown"
533
+ aria-label="Edit markdown"
531
534
  className="relative cursor-pointer px-3 py-2"
532
535
  onDoubleClick={showHiddenCodeIfMarkdown}
536
+ onClick={(e) => {
537
+ if (e.detail === 0) {
538
+ showHiddenCodeIfMarkdown();
539
+ }
540
+ }}
533
541
  onKeyDown={(e) => {
534
- if (e.key === "Enter") {
542
+ if (e.key === "Enter" || e.key === " ") {
543
+ e.preventDefault();
535
544
  showHiddenCodeIfMarkdown();
536
545
  }
537
546
  }}
@@ -248,21 +248,22 @@ export const replaceTracebackFilenames = (domNode: DOMNode) => {
248
248
  <Tooltip content={tooltipContent}>
249
249
  <button
250
250
  type="button"
251
- className="ml-1 p-1 rounded-sm hover:bg-muted transition-all inline"
251
+ aria-label={
252
+ debuggerEnabled ? "Toggle breakpoint" : "Insert breakpoint"
253
+ }
254
+ className="ml-1 p-1 rounded-sm hover:bg-muted transition-colors inline"
255
+ onClick={() => {
256
+ if (debuggerEnabled && !isStaticNotebook()) {
257
+ toggleBreakpoint(info.cellId, info.lineNumber);
258
+ return;
259
+ }
260
+ const view = getCellEditorView(info.cellId);
261
+ if (view) {
262
+ insertDebuggerAtLine(view, info.lineNumber);
263
+ }
264
+ }}
252
265
  >
253
- <BugPlayIcon
254
- onClick={() => {
255
- if (debuggerEnabled && !isStaticNotebook()) {
256
- toggleBreakpoint(info.cellId, info.lineNumber);
257
- return;
258
- }
259
- const view = getCellEditorView(info.cellId);
260
- if (view) {
261
- insertDebuggerAtLine(view, info.lineNumber);
262
- }
263
- }}
264
- className="h-3 w-3"
265
- />
266
+ <BugPlayIcon className="h-3 w-3" />
266
267
  </button>
267
268
  </Tooltip>
268
269
  )}
@@ -272,9 +273,10 @@ export const replaceTracebackFilenames = (domNode: DOMNode) => {
272
273
  }
273
274
  if (info?.kind === "file") {
274
275
  return (
275
- <div
276
+ <button
277
+ type="button"
276
278
  className="inline-block cursor-pointer text-destructive hover:underline"
277
- onClick={(_) => {
279
+ onClick={() => {
278
280
  getRequestClient().openFile({
279
281
  path: info.filePath,
280
282
  lineNumber: info.lineNumber,
@@ -282,7 +284,7 @@ export const replaceTracebackFilenames = (domNode: DOMNode) => {
282
284
  }}
283
285
  >
284
286
  <span className="nb">"{info.filePath}"</span>
285
- </div>
287
+ </button>
286
288
  );
287
289
  }
288
290
  };
@@ -1,11 +1,12 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
- import { render } from "@testing-library/react";
3
+ import { fireEvent, render, screen } from "@testing-library/react";
4
4
  import { Provider } from "jotai";
5
5
  import { beforeAll, beforeEach, describe, expect, test, vi } from "vitest";
6
6
  import { MockRequestClient } from "@/__mocks__/requests";
7
7
  import { Tracebacks } from "@/__mocks__/tracebacks";
8
8
  import { cellId } from "@/__tests__/branded";
9
+ import { toggleBreakpoint } from "@/core/codemirror/cells/debugger-state";
9
10
  import { TooltipProvider } from "@/components/ui/tooltip";
10
11
  import { initialModeAtom } from "@/core/mode";
11
12
  import { requestClientAtom } from "@/core/network/requests";
@@ -19,7 +20,32 @@ import {
19
20
 
20
21
  const cid = cellId("1");
21
22
 
23
+ vi.mock("@/core/config/feature-flag", async (importOriginal) => ({
24
+ ...(await importOriginal<typeof import("@/core/config/feature-flag")>()),
25
+ getFeatureFlag: (flag: string) => flag === "debugger",
26
+ }));
27
+ vi.mock("@/core/codemirror/cells/debugger-state", async (importOriginal) => ({
28
+ ...(await importOriginal<
29
+ typeof import("@/core/codemirror/cells/debugger-state")
30
+ >()),
31
+ toggleBreakpoint: vi.fn(),
32
+ }));
33
+
22
34
  describe("traceback component", () => {
35
+ test("activates the breakpoint from the named button itself", () => {
36
+ render(
37
+ <Provider store={store}>
38
+ <TooltipProvider>
39
+ <MarimoTracebackOutput traceback={Tracebacks.raw} cellId={cid} />
40
+ </TooltipProvider>
41
+ </Provider>,
42
+ );
43
+ const buttons = screen.getAllByRole("button", {
44
+ name: "Toggle breakpoint",
45
+ });
46
+ fireEvent.click(buttons[0]);
47
+ expect(toggleBreakpoint).toHaveBeenCalledOnce();
48
+ });
23
49
  beforeEach(() => {
24
50
  vi.resetAllMocks();
25
51
  store.set(initialModeAtom, "edit");
@@ -358,6 +358,7 @@ export class AnsiReducer {
358
358
 
359
359
  export type StringOutputMessage = Omit<OutputMessage, "data"> & {
360
360
  data: string;
361
+ id?: number;
361
362
  };
362
363
 
363
364
  /**
@@ -369,6 +370,7 @@ export class StatefulOutputMessage implements OutputMessage {
369
370
  public readonly channel: OutputMessage["channel"];
370
371
  public readonly timestamp: OutputMessage["timestamp"];
371
372
  public readonly code: OutputMessage["code"];
373
+ public readonly id: StringOutputMessage["id"];
372
374
  private ansiReducer = new AnsiReducer();
373
375
  private _data: string;
374
376
 
@@ -379,39 +381,50 @@ export class StatefulOutputMessage implements OutputMessage {
379
381
  public static create(message: StringOutputMessage): StatefulOutputMessage {
380
382
  const ansiReducer = new AnsiReducer();
381
383
  ansiReducer.append(message.data);
382
- return new StatefulOutputMessage(
383
- message.mimetype,
384
- message.channel,
385
- message.timestamp,
384
+ return new StatefulOutputMessage({
385
+ mimetype: message.mimetype,
386
+ channel: message.channel,
387
+ timestamp: message.timestamp,
386
388
  ansiReducer,
387
- message.code,
388
- );
389
+ code: message.code,
390
+ id: message.id,
391
+ });
389
392
  }
390
393
 
391
- private constructor(
392
- mimetype: OutputMessage["mimetype"],
393
- channel: OutputMessage["channel"],
394
- timestamp: OutputMessage["timestamp"],
395
- ansiReducer: AnsiReducer,
396
- code: OutputMessage["code"],
397
- ) {
394
+ private constructor({
395
+ mimetype,
396
+ channel,
397
+ timestamp,
398
+ ansiReducer,
399
+ code,
400
+ id,
401
+ }: {
402
+ mimetype: OutputMessage["mimetype"];
403
+ channel: OutputMessage["channel"];
404
+ timestamp: OutputMessage["timestamp"];
405
+ ansiReducer: AnsiReducer;
406
+ code: OutputMessage["code"];
407
+ id: StringOutputMessage["id"];
408
+ }) {
398
409
  this.mimetype = mimetype;
399
410
  this.channel = channel;
400
411
  this.timestamp = timestamp;
401
412
  this.code = code;
413
+ this.id = id;
402
414
  this.ansiReducer = ansiReducer;
403
415
  this._data = this.ansiReducer.render();
404
416
  }
405
417
 
406
418
  public appendData(chunk: string): StatefulOutputMessage {
407
419
  this.ansiReducer.append(chunk);
408
- return new StatefulOutputMessage(
409
- this.mimetype,
410
- this.channel,
411
- this.timestamp,
412
- this.ansiReducer,
413
- this.code,
414
- );
420
+ return new StatefulOutputMessage({
421
+ mimetype: this.mimetype,
422
+ channel: this.channel,
423
+ timestamp: this.timestamp,
424
+ ansiReducer: this.ansiReducer,
425
+ code: this.code,
426
+ id: this.id,
427
+ });
415
428
  }
416
429
 
417
430
  public toJSON(): StringOutputMessage {
@@ -6,7 +6,7 @@ import {
6
6
  ChevronsUpDownIcon,
7
7
  WrapTextIcon,
8
8
  } from "lucide-react";
9
- import React, { useEffect, useLayoutEffect, useRef, useState } from "react";
9
+ import React, { useEffect, useLayoutEffect, useState } from "react";
10
10
  import { ToggleButton } from "react-aria-components";
11
11
  import { DebuggerControls } from "@/components/debugger/debugger-code";
12
12
  import { CopyClipboardIcon } from "@/components/icons/copy-icon";
@@ -16,8 +16,7 @@ import { Tooltip } from "@/components/ui/tooltip";
16
16
  import type { CellId } from "@/core/cells/ids";
17
17
  import { isInternalCellName } from "@/core/cells/names";
18
18
  import { useExpandedConsoleOutput } from "@/core/cells/outputs";
19
- import type { WithResponse } from "@/core/cells/types";
20
- import type { OutputMessage } from "@/core/kernel/messages";
19
+ import type { ConsoleOutputMessage } from "@/core/cells/types";
21
20
  import {
22
21
  type UseInputHistoryReturn,
23
22
  useInputHistory,
@@ -49,31 +48,21 @@ export const CONSOLE_CLEAR_DEBOUNCE_MS = 200;
49
48
  */
50
49
  function useDebouncedConsoleOutputs<T>(outputs: T[]): T[] {
51
50
  const [debouncedOutputs, setDebouncedOutputs] = useState(outputs);
52
- const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
53
51
 
54
- // Non-empty outputs: apply immediately and cancel any pending clear
52
+ // Non-empty outputs: apply immediately. Effect cleanup cancels pending clears.
55
53
  if (outputs.length > 0 && debouncedOutputs !== outputs) {
56
- if (timerRef.current !== null) {
57
- clearTimeout(timerRef.current);
58
- timerRef.current = null;
59
- }
60
54
  setDebouncedOutputs(outputs);
61
55
  }
62
56
 
63
57
  // Empty outputs: delay the clear so new outputs can arrive first
64
58
  useEffect(() => {
65
- if (outputs.length === 0 && timerRef.current === null) {
66
- timerRef.current = setTimeout(() => {
67
- timerRef.current = null;
68
- setDebouncedOutputs([]);
69
- }, CONSOLE_CLEAR_DEBOUNCE_MS);
59
+ if (outputs.length > 0) {
60
+ return;
70
61
  }
71
- return () => {
72
- if (timerRef.current !== null) {
73
- clearTimeout(timerRef.current);
74
- timerRef.current = null;
75
- }
76
- };
62
+ const timer = setTimeout(() => {
63
+ setDebouncedOutputs([]);
64
+ }, CONSOLE_CLEAR_DEBOUNCE_MS);
65
+ return () => clearTimeout(timer);
77
66
  }, [outputs]);
78
67
 
79
68
  return debouncedOutputs;
@@ -83,7 +72,7 @@ interface Props {
83
72
  cellId: CellId;
84
73
  cellName: string;
85
74
  className?: string;
86
- consoleOutputs: WithResponse<OutputMessage>[];
75
+ consoleOutputs: ConsoleOutputMessage[];
87
76
  stale: boolean;
88
77
  interrupted: boolean;
89
78
  debuggerActive: boolean;
@@ -251,19 +240,24 @@ const ConsoleOutputInternal = (props: Props): React.ReactNode => {
251
240
  return null;
252
241
  }
253
242
 
243
+ const originalIdx = consoleOutputs.length - idx - 1;
244
+ // Outputs render in reverse, so the reversed index shifts on every
245
+ // new line. The assigned id survives collapse and truncation. The
246
+ // original position is the fallback for outputs without one.
247
+ const key = output.id ?? `position-${originalIdx}`;
248
+
254
249
  if (output.channel === "stdin") {
255
250
  invariant(
256
251
  typeof output.data === "string",
257
252
  "Expected data to be a string",
258
253
  );
259
254
 
260
- const originalIdx = consoleOutputs.length - idx - 1;
261
255
  const isPassword = output.mimetype === "text/password";
262
256
 
263
257
  if (output.response == null && lastStdInputIdx === idx) {
264
258
  return (
265
259
  <StdInput
266
- key={idx}
260
+ key={key}
267
261
  output={output.data}
268
262
  isPdb={isPdb}
269
263
  isPassword={isPassword}
@@ -278,7 +272,7 @@ const ConsoleOutputInternal = (props: Props): React.ReactNode => {
278
272
 
279
273
  return (
280
274
  <StdInputWithResponse
281
- key={idx}
275
+ key={key}
282
276
  output={output.data}
283
277
  response={output.response}
284
278
  isPassword={isPassword}
@@ -288,7 +282,7 @@ const ConsoleOutputInternal = (props: Props): React.ReactNode => {
288
282
  }
289
283
 
290
284
  return (
291
- <React.Fragment key={idx}>
285
+ <React.Fragment key={key}>
292
286
  <OutputRenderer
293
287
  cellId={cellId}
294
288
  onRefactorWithAI={onRefactorWithAI}
@@ -55,6 +55,7 @@ import {
55
55
  DropdownMenu,
56
56
  DropdownMenuCheckboxItem,
57
57
  DropdownMenuContent,
58
+ DropdownMenuItem,
58
59
  DropdownMenuTrigger,
59
60
  } from "../ui/dropdown-menu";
60
61
  import { ExternalLink } from "../ui/links";
@@ -158,6 +159,7 @@ export const PackageAlert: React.FC = () => {
158
159
  <SourceBadge source={packageAlert.source} />
159
160
  </span>
160
161
  <Button
162
+ aria-label="Dismiss missing packages alert"
161
163
  variant="text"
162
164
  data-testid="remove-banner-button"
163
165
  size="icon"
@@ -171,12 +173,12 @@ export const PackageAlert: React.FC = () => {
171
173
  <p>The following packages were not found:</p>
172
174
  <table className="list-disc ml-2 mt-1">
173
175
  <tbody>
174
- {packageAlert.packages.map((pkg, index) => {
176
+ {packageAlert.packages.map((pkg) => {
175
177
  const parsed = parsePackageSpecifier(pkg);
176
178
  const currentExtras = selectedExtras[pkg] || parsed.extras;
177
179
 
178
180
  return (
179
- <tr className="font-mono text-sm" key={index}>
181
+ <tr className="font-mono text-sm" key={pkg}>
180
182
  <td className="pr-2 py-1 align-middle">
181
183
  <BoxIcon size="1rem" />
182
184
  </td>
@@ -270,6 +272,7 @@ export const PackageAlert: React.FC = () => {
270
272
  <SourceBadge source={packageAlert.source} />
271
273
  </span>
272
274
  <Button
275
+ aria-label={`Dismiss ${title}`}
273
276
  variant="text"
274
277
  data-testid="remove-banner-button"
275
278
  size="icon"
@@ -294,7 +297,7 @@ export const PackageAlert: React.FC = () => {
294
297
  </>
295
298
  )}
296
299
  <ul className="list-disc ml-2 mt-1">
297
- {Object.entries(packageAlert.packages).map(([pkg, st], index) => (
300
+ {Object.entries(packageAlert.packages).map(([pkg, st]) => (
298
301
  <li
299
302
  className={cn(
300
303
  "flex items-center gap-1 font-mono text-sm",
@@ -305,7 +308,7 @@ export const PackageAlert: React.FC = () => {
305
308
  status === "failed" &&
306
309
  "text-muted-foreground",
307
310
  )}
308
- key={index}
311
+ key={pkg}
309
312
  >
310
313
  <ProgressIcon status={st} />
311
314
  {pkg}
@@ -590,14 +593,18 @@ const ExtrasSelector: React.FC<ExtrasSelectorProps> = ({
590
593
  <div className="p-2 bg-popover border-b border-border">
591
594
  <div className="flex flex-wrap gap-1 p-1 min-h-[24px]">
592
595
  {selectedExtras.map((extra) => (
593
- <span
596
+ <DropdownMenuItem
594
597
  key={extra}
598
+ aria-label={`Remove ${extra}`}
595
599
  className="inline-flex items-center gap-1 px-1 py-0.5 text-sm font-mono border border-muted-foreground/30 hover:border-muted-foreground/60 rounded-sm cursor-pointer group transition-colors"
596
- onClick={() => handleExtraToggle(extra, false)}
600
+ onSelect={(e) => {
601
+ e.preventDefault();
602
+ handleExtraToggle(extra, false);
603
+ }}
597
604
  >
598
605
  {extra}
599
606
  <XIcon className="w-3 h-3 opacity-60 group-hover:opacity-100" />
600
- </span>
607
+ </DropdownMenuItem>
601
608
  ))}
602
609
  </div>
603
610
  </div>
@@ -190,12 +190,18 @@ export const GridLayoutRenderer: React.FC<Props> = ({
190
190
  cells: [...cellLayouts, dropped],
191
191
  });
192
192
  }}
193
- onDragStart={(_layout, _oldItem, _newItem, _placeholder, event) => {
194
- dragProps.onDragStart(event);
195
- }}
196
- onDrag={(_layout, _oldItem, _newItem, _placeholder, event) => {
197
- dragProps.onDragMove(event);
198
- }}
193
+ onDragStart={
194
+ // oxlint-disable-next-line marimo/prefer-object-params -- React Grid Layout supplies the event as the fifth argument.
195
+ (_layout, _oldItem, _newItem, _placeholder, event) => {
196
+ dragProps.onDragStart(event);
197
+ }
198
+ }
199
+ onDrag={
200
+ // oxlint-disable-next-line marimo/prefer-object-params -- React Grid Layout supplies the event as the fifth argument.
201
+ (_layout, _oldItem, _newItem, _placeholder, event) => {
202
+ dragProps.onDragMove(event);
203
+ }
204
+ }
199
205
  onDragStop={() => {
200
206
  dragProps.onDragStop();
201
207
  }}
@@ -12,7 +12,13 @@ interface SidebarToggleProps {
12
12
  export const SidebarToggle = ({ isOpen, toggle }: SidebarToggleProps) => {
13
13
  return (
14
14
  <div className="invisible lg:visible absolute top-[12px] right-[16px] z-20">
15
- <Button onClick={toggle} className="w-10 h-8" variant="ghost" size="icon">
15
+ <Button
16
+ aria-label={isOpen ? "Collapse sidebar" : "Expand sidebar"}
17
+ onClick={toggle}
18
+ className="w-10 h-8"
19
+ variant="ghost"
20
+ size="icon"
21
+ >
16
22
  <ChevronLeft
17
23
  className={cn(
18
24
  "h-5 w-5 transition-transform ease-in-out duration-700",
@@ -12,7 +12,6 @@ import {
12
12
  GraduationCapIcon,
13
13
  GridIcon,
14
14
  LayoutIcon,
15
- LinkIcon,
16
15
  MessagesSquareIcon,
17
16
  OrbitIcon,
18
17
  PackageIcon,
@@ -82,7 +81,12 @@ export const OpenTutorialDropDown: React.FC = () => {
82
81
  return (
83
82
  <DropdownMenu>
84
83
  <DropdownMenuTrigger asChild={true}>
85
- <Button data-testid="open-tutorial-button" size="xs" variant="outline">
84
+ <Button
85
+ data-testid="open-tutorial-button"
86
+ size="sm"
87
+ variant="outline"
88
+ className="border-border text-(--gray-11) shadow-none hover:border-(--gray-7) hover:bg-(--gray-3)"
89
+ >
86
90
  <GraduationCapIcon className="w-4 h-4 mr-2" />
87
91
  Tutorials
88
92
  <CaretDownIcon className="w-3 h-3 ml-1" />
@@ -135,7 +139,7 @@ const RESOURCES = [
135
139
  url: Constants.githubPage,
136
140
  },
137
141
  {
138
- title: "Community",
142
+ title: "Discussion",
139
143
  description: "Join the marimo Discord community",
140
144
  icon: MessagesSquareIcon,
141
145
  url: Constants.discordLink,
@@ -162,9 +166,11 @@ const RESOURCES = [
162
166
 
163
167
  export const ResourceLinks: React.FC = () => {
164
168
  return (
165
- <div className="flex flex-col gap-2">
166
- <Header Icon={LinkIcon}>Resources</Header>
167
- <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
169
+ <nav
170
+ aria-label="Resources"
171
+ className="mt-12 border-t border-border/60 pt-6"
172
+ >
173
+ <div className="flex flex-wrap items-center gap-x-6 gap-y-4">
168
174
  {RESOURCES.map((resource) => (
169
175
  <a
170
176
  key={resource.title}
@@ -172,19 +178,15 @@ export const ResourceLinks: React.FC = () => {
172
178
  target="_blank"
173
179
  rel="noreferrer"
174
180
  aria-label={`${resource.title}: ${resource.description}`}
175
- className="flex items-start gap-3 py-3 px-3 rounded-lg border hover:bg-accent/20 transition-colors shadow-xs"
181
+ title={resource.description}
182
+ className="flex items-center gap-2 rounded-sm text-sm text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring"
176
183
  >
177
- <resource.icon className="w-5 h-5 mt-1.5 text-primary" />
178
- <div>
179
- <h3 className="font-medium">{resource.title}</h3>
180
- <p className="text-sm text-muted-foreground">
181
- {resource.description}
182
- </p>
183
- </div>
184
+ <resource.icon className="h-4 w-4" />
185
+ <span>{resource.title}</span>
184
186
  </a>
185
187
  ))}
186
188
  </div>
187
- </div>
189
+ </nav>
188
190
  );
189
191
  };
190
192
 
@@ -203,19 +205,3 @@ export const TruncatedWorkspaceBanner: React.FC<{
203
205
  </Banner>
204
206
  );
205
207
  };
206
-
207
- export const Header: React.FC<{
208
- Icon: React.FC<React.SVGProps<SVGSVGElement>>;
209
- control?: React.ReactNode;
210
- children: React.ReactNode;
211
- }> = ({ Icon, control, children }) => {
212
- return (
213
- <div className="flex items-center justify-between gap-2">
214
- <h2 className="flex items-center gap-2 text-xl font-semibold text-muted-foreground select-none">
215
- <Icon className="h-5 w-5" />
216
- {children}
217
- </h2>
218
- {control}
219
- </div>
220
- );
221
- };
@@ -0,0 +1,39 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { act, render, screen } from "@testing-library/react";
4
+ import { Profiler } from "react";
5
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
6
+ import { LargeSpinner } from "../large-spinner";
7
+
8
+ describe("LargeSpinner", () => {
9
+ beforeEach(() => vi.useFakeTimers());
10
+ afterEach(() => vi.useRealTimers());
11
+
12
+ it("fades the old title without a second commit before the timer fires", () => {
13
+ const onRender = vi.fn();
14
+ const spinner = (title: string) => (
15
+ <Profiler id="spinner" onRender={onRender}>
16
+ <LargeSpinner title={title} />
17
+ </Profiler>
18
+ );
19
+ const { rerender } = render(spinner("Starting"));
20
+ onRender.mockClear();
21
+
22
+ rerender(spinner("Loading"));
23
+ expect(onRender).toHaveBeenCalledOnce();
24
+ expect(screen.getByText("Starting")).toHaveClass("opacity-0");
25
+
26
+ act(() => vi.advanceTimersByTime(300));
27
+ expect(screen.getByText("Loading")).toHaveClass("opacity-100");
28
+ });
29
+
30
+ it("restores visibility if the title changes back during the fade", () => {
31
+ const { rerender } = render(<LargeSpinner title="Starting" />);
32
+ rerender(<LargeSpinner title="Loading" />);
33
+ rerender(<LargeSpinner title="Starting" />);
34
+
35
+ expect(screen.getByText("Starting")).toHaveClass("opacity-100");
36
+ act(() => vi.advanceTimersByTime(300));
37
+ expect(screen.queryByText("Loading")).not.toBeInTheDocument();
38
+ });
39
+ });
@@ -1,23 +1,12 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
3
  import { Loader2Icon } from "lucide-react";
4
- import { useEffect, useState } from "react";
4
+ import { useDebounce } from "@/hooks/useDebounce";
5
5
  import { cn } from "@/utils/cn";
6
6
 
7
7
  export const LargeSpinner = ({ title }: { title?: string }) => {
8
- const [currentTitle, setCurrentTitle] = useState(title);
9
- const [isVisible, setIsVisible] = useState(true);
10
-
11
- useEffect(() => {
12
- if (title !== currentTitle) {
13
- setIsVisible(false);
14
- const timer = setTimeout(() => {
15
- setCurrentTitle(title);
16
- setIsVisible(true);
17
- }, 300); // Wait for fade out animation to complete
18
- return () => clearTimeout(timer);
19
- }
20
- }, [title, currentTitle]);
8
+ const currentTitle = useDebounce(title, 300);
9
+ const isVisible = title === currentTitle;
21
10
 
22
11
  return (
23
12
  <div className="flex flex-col h-full flex-1 items-center justify-center p-4">
@@ -25,7 +25,7 @@ const spinnerVariants = cva("animate-spin", {
25
25
  const Spinner = React.forwardRef<
26
26
  SVGSVGElement,
27
27
  React.SVGProps<SVGSVGElement> & VariantProps<typeof spinnerVariants>
28
- >(({ className, children, centered, size, ...props }, ref) => (
28
+ >(({ className, children: _children, centered, size, ...props }, ref) => (
29
29
  <Loader2Icon
30
30
  ref={ref}
31
31
  className={cn(spinnerVariants({ centered, size }), className)}
@@ -41,7 +41,7 @@ export const MCPStatusIndicator: React.FC = () => {
41
41
  <Popover>
42
42
  <Tooltip content="MCP Status">
43
43
  <PopoverTrigger asChild={true}>
44
- <Button variant="text" size="icon">
44
+ <Button aria-label="MCP status" variant="text" size="icon">
45
45
  <PlugIcon
46
46
  className={cn(
47
47
  "h-4 w-4",