@marimo-team/islands 0.25.3-dev28 → 0.25.3-dev284

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 (841) hide show
  1. package/dist/{ConnectedDataExplorerComponent-7EaP9VWb.js → ConnectedDataExplorerComponent-CErm132k.js} +116 -113
  2. package/dist/{ErrorBoundary-CUdSBv06.js → ErrorBoundary-DlAypzMw.js} +76 -82
  3. package/dist/_baseSet-BfI5FjBM.js +328 -0
  4. package/dist/{abnfDiagram-VCTEODGH-z_OSynK8.js → abnfDiagram-VCTEODGH-d-Qn2fwb.js} +2 -2
  5. package/dist/any-language-editor-6k6F12Z3.js +75 -0
  6. package/dist/{architectureDiagram-5GKGNRK7-Cs8J8BZr.js → architectureDiagram-5GKGNRK7-DxragHYv.js} +1 -1
  7. package/dist/{arrays-CN1djC4k.js → arrays-DoQ-AILx.js} +15 -27
  8. package/dist/assertNever-B-YzWzbL.js +507 -0
  9. package/dist/assets/__vite-browser-external-BKBpimif.js +1 -0
  10. package/dist/assets/worker-CgoMtD-_.js +113 -0
  11. package/dist/{blockDiagram-I7D4REHJ-Cg25UrxT.js → blockDiagram-I7D4REHJ-Dkhe6_PP.js} +1 -1
  12. package/dist/{button-CvjkwdSk.js → button-DZuNnIZO.js} +56 -48
  13. package/dist/{capabilities-CQ5IjhzL.js → capabilities-D9FskwsK.js} +2 -2
  14. package/dist/{chat-ui-BzjAsTFN.js → chat-ui-ByGe2rP1.js} +2100 -1810
  15. package/dist/{check-BMKBBrdc.js → check-XFzZeH-y.js} +1 -1
  16. package/dist/{chunk-IMKFNOWR-aoSFasqF.js → chunk-IMKFNOWR-C0x0feFg.js} +1 -1
  17. package/dist/{chunk-SHT3W25Y-CMaybO3O.js → chunk-SHT3W25Y-D0ZdASqB.js} +1 -1
  18. package/dist/{chunk-SVP7TREG-BNBrTAw1.js → chunk-SVP7TREG-BHjL96zx.js} +1 -1
  19. package/dist/{chunk-TICWLB2K-DXNxvUJn.js → chunk-TICWLB2K-CzcrwGfp.js} +1 -1
  20. package/dist/{classDiagram-ZZMXUADV-DXdCCDOg.js → classDiagram-ZZMXUADV-BTqFAZTP.js} +2 -2
  21. package/dist/{classDiagram-v2-VYDZK3BY-DyZqrtLP.js → classDiagram-v2-VYDZK3BY-EPXb0Elt.js} +2 -2
  22. package/dist/{code-visibility-BuxqA3v5.js → code-visibility-kxOpbw86.js} +43 -50
  23. package/dist/{common-xtZHxsM2.js → common-58GnHt6Y.js} +10605 -10045
  24. package/dist/config-C8MFHQ2E.js +637 -0
  25. package/dist/{copy-icon-BgUO8jsi.js → copy-icon-CtllOdmg.js} +3 -3
  26. package/dist/createReducer-DRqwmaTc.js +67 -0
  27. package/dist/{cynefinDiagram-5FMLGOSQ-DWHuKJs8.js → cynefinDiagram-5FMLGOSQ-CBSD344q.js} +1 -1
  28. package/dist/{dagre-GXQ25YYZ-DYLu-OCU.js → dagre-GXQ25YYZ-JItjh3Ge.js} +3 -3
  29. package/dist/{dagre-BGSfXNEb.js → dagre-mqiMAsFY.js} +531 -680
  30. package/dist/{data-grid-overlay-editor-qnbf5J94.js → data-grid-overlay-editor-CeO8C9Le.js} +1 -1
  31. package/dist/{debounce-C3jyATpc.js → debounce-DrhhziJv.js} +2 -2
  32. package/dist/{diagram-S7CK7UJ4-PwwcUOHP.js → diagram-S7CK7UJ4-CTD9xTVb.js} +1 -1
  33. package/dist/{diagram-UQ7AKVKN-CvJbL_IM.js → diagram-UQ7AKVKN-DTfnFai8.js} +1 -1
  34. package/dist/{diagram-VX7I27RA-B5RkqnzN.js → diagram-VX7I27RA-BcSnucV-.js} +1 -1
  35. package/dist/{diagram-Z3DM3KII-CyijwTR2.js → diagram-Z3DM3KII-C2ATIE8A.js} +1 -1
  36. package/dist/{dialog-Cc479ZEH.js → dialog-CaYm5imE.js} +32 -32
  37. package/dist/{dist-Bvs24R_E.js → dist-11T5wq3_.js} +31 -30
  38. package/dist/{dist-F8Ep36rk.js → dist-17ugwGZc.js} +59 -58
  39. package/dist/{dist-BMQf5zIS.js → dist-7cRjf--y.js} +123 -122
  40. package/dist/{dist-diIx5qJW.js → dist-B8pkfkzc.js} +31 -30
  41. package/dist/{dist-CN_BeG2I.js → dist-B9UoM_wq.js} +58 -57
  42. package/dist/{dist-CLdpGQVr.js → dist-BGuMTs1i.js} +118 -117
  43. package/dist/{dist-BK4Oj99o.js → dist-BHW11fni.js} +18 -17
  44. package/dist/dist-BLY4m1Wi.js +850 -0
  45. package/dist/{dist-PXYI_yuP.js → dist-BNlcVoxs.js} +24 -23
  46. package/dist/{dist-D6d86eNJ.js → dist-BQYe5Bju.js} +47 -46
  47. package/dist/{dist-CkTTIdBe.js → dist-BX1mn8na.js} +337 -337
  48. package/dist/{dist-DiDibOYr.js → dist-Bh0PTKUW.js} +151 -150
  49. package/dist/{dist-CSJ0MDRw.js → dist-CKaXK92p.js} +56 -55
  50. package/dist/{dist-C8fAKSH1.js → dist-CLhwfhpJ.js} +53 -52
  51. package/dist/{dist-DoE4XJBC.js → dist-CYeqK4Xw.js} +9801 -10649
  52. package/dist/{dist-BLfKmR-l.js → dist-Cxlp3WH6.js} +16 -15
  53. package/dist/{dist-D-wwqlzJ.js → dist-DSJPGj42.js} +158 -157
  54. package/dist/{dist-BorMD-uX.js → dist-DU9ICsWZ.js} +75 -74
  55. package/dist/{dist-DBtSl3sf.js → dist-DWv0ALBi.js} +47 -46
  56. package/dist/{dist-OR1OnN-y.js → dist-DiBpWEsB.js} +31 -30
  57. package/dist/{dist-CrSP_H1M.js → dist-DiLFTlnl.js} +242 -242
  58. package/dist/{dist-CN8zI-XC.js → dist-X9CsotJl.js} +239 -239
  59. package/dist/{dist-CSbh1PNP.js → dist-cVDV5IuI.js} +23 -22
  60. package/dist/{dist-BuaRHxOc.js → dist-fRfaOoq8.js} +44 -43
  61. package/dist/{dist-Cg7gzw-a.js → dist-gTTI-9Px.js} +78 -77
  62. package/dist/{dropdown-menu-BuE2b8Yb.js → dropdown-menu-Bb0bFOIk.js} +2904 -1493
  63. package/dist/{ebnfDiagram-PWID7BFC-DG90ELFp.js → ebnfDiagram-PWID7BFC-B7B9JPxc.js} +2 -2
  64. package/dist/{erDiagram-RLTQ6QDP-FMo_SpyX.js → erDiagram-RLTQ6QDP-v6hDno1O.js} +1 -1
  65. package/dist/{error-banner-CFPHFw4h.js → error-banner-CHCdHVFB.js} +87 -87
  66. package/dist/error-entries-9U4BFHjf.js +2586 -0
  67. package/dist/{esm-AMFssoNp.js → esm-BDqqT86j.js} +329 -329
  68. package/dist/{esm-D2gTsXEV.js → esm-BgKjMvWw.js} +530 -529
  69. package/dist/feedback-button-CU5tLok7.js +374 -0
  70. package/dist/{flowDiagram-HODETNUW-DQ4JuadE.js → flowDiagram-HODETNUW-aIFlAtOy.js} +2 -2
  71. package/dist/{formats-Bv_5N2Ff.js → formats-PzBQMUnr.js} +6 -6
  72. package/dist/{glide-data-editor-DQ_cEi05.js → glide-data-editor-D-EO3BiP.js} +6630 -2846
  73. package/dist/{glide-utils-pisbYVjH.js → glide-utils-BSQr4d90.js} +107 -76
  74. package/dist/{globals-D7vITxEX.js → globals-BscgCh7D.js} +3 -3
  75. package/dist/{graphlib-BmY_CCV9.js → graphlib-nlwT3Wvy.js} +177 -220
  76. package/dist/hasIn-C20JsQ2m.js +28 -0
  77. package/dist/{error-entries-j85LP88u.js → html-to-image-BYh_MorV.js} +26294 -26451
  78. package/dist/{infoDiagram-27XIBGKW-CRSDtRrd.js → infoDiagram-27XIBGKW-D6YHLeCP.js} +1 -1
  79. package/dist/isArrayLikeObject-B-HXCsex.js +626 -0
  80. package/dist/isEmpty-CdX7MTE1.js +14 -0
  81. package/dist/{ishikawaDiagram-5VMMS53U-DxLEUldB.js → ishikawaDiagram-5VMMS53U-D1ZvprOX.js} +1 -1
  82. package/dist/{kanban-definition-UXKFOSKX-BLhvu8o4.js → kanban-definition-UXKFOSKX-Bna_3BMr.js} +1 -1
  83. package/dist/{loader-BPdQhmlq.js → loader-DZvtzQoD.js} +3 -3
  84. package/dist/main.js +10276 -8171
  85. package/dist/{mermaid-Bjm6C1qN.js → mermaid-A36unKXJ.js} +62 -62
  86. package/dist/{mindmap-definition-YA3MSWOX-g2LwxnYQ.js → mindmap-definition-YA3MSWOX-CyVCs__G.js} +1 -1
  87. package/dist/{now-jM8-M7IR.js → now-BKZCLE5P.js} +12 -13
  88. package/dist/{number-overlay-editor-DJHB-CGK.js → number-overlay-editor-Dtmaj1ET.js} +1 -1
  89. package/dist/{objects-CzUgoMIF.js → objects-DCmMvZtn.js} +1 -3
  90. package/dist/{once-BtE-wKRL.js → once-NnpX_HnX.js} +1 -1
  91. package/dist/{pegDiagram-XKGWAZYB-CpMAt3Qs.js → pegDiagram-XKGWAZYB-9hNBD2wZ.js} +2 -2
  92. package/dist/pencil-DYUlSKxZ.js +10 -0
  93. package/dist/pick-tCszMUbm.js +35 -0
  94. package/dist/{pieDiagram-E7YTZNPT-CVTthoZo.js → pieDiagram-E7YTZNPT-8tr-NYo7.js} +1 -1
  95. package/dist/{process-output-DGzGHArA.js → process-output-bS3MGJ3f.js} +4 -4
  96. package/dist/{railroadDiagram-O6MQD6OU-ao8cqqFB.js → railroadDiagram-O6MQD6OU-Dzg1dyyp.js} +2 -2
  97. package/dist/{react-vega-BDpbvBCb.js → react-vega-C4T9Rb_w.js} +1 -1
  98. package/dist/{requirementDiagram-BXWQKSXE--J8vyfm-.js → requirementDiagram-BXWQKSXE-B7sNqwGG.js} +1 -1
  99. package/dist/{reveal-component-BxYUpAPg.js → reveal-component-DDr66wjo.js} +1151 -1128
  100. package/dist/select-DsoP1qe8.js +1200 -0
  101. package/dist/{sequenceDiagram-WJ2MYXX4-CiQbn62x.js → sequenceDiagram-WJ2MYXX4-oP9shbaG.js} +1 -1
  102. package/dist/{spec-D_u2ipzF.js → spec-BZMDxAu7.js} +41 -11
  103. package/dist/{stateDiagram-D77RDMKH-DVEeGs5b.js → stateDiagram-D77RDMKH-CjCv4ogv.js} +4 -4
  104. package/dist/{stateDiagram-v2-MP3YSRHH-1OQRe6iA.js → stateDiagram-v2-MP3YSRHH-BW9X162a.js} +2 -2
  105. package/dist/static-state-D-Q-Ajaa.js +72 -0
  106. package/dist/style.css +1 -1
  107. package/dist/{swimlanes-42K2YHIH-CqkGoGqn.js → swimlanes-42K2YHIH-Ckfs9Wrn.js} +1 -1
  108. package/dist/{swimlanesDiagram-VR7AAH4N-CszjkrKo.js → swimlanesDiagram-VR7AAH4N-DpTNG9YP.js} +2 -2
  109. package/dist/{swiper-component-DKntyn8p.js → swiper-component-DQbK_ZWm.js} +7 -7
  110. package/dist/{timeline-definition-24CTP7MA-DLRUNVAx.js → timeline-definition-24CTP7MA-BXaBlV6U.js} +1 -1
  111. package/dist/toast-BhjIy3Tq.js +491 -0
  112. package/dist/{tooltip-CtBZ5q8p.js → tooltip-DMMiOn2f.js} +321 -297
  113. package/dist/use-restore-focus-CosX7-gx.js +7568 -0
  114. package/dist/{useAsyncData-BneN349z.js → useAsyncData-Ci4AvJff.js} +18 -17
  115. package/dist/{useDateFormatter-DkajGwwd.js → useDateFormatter-CPlava52.js} +2 -2
  116. package/dist/{useDeepCompareMemoize-Z_7R8wTt.js → useDeepCompareMemoize-NaaWoFlX.js} +1 -1
  117. package/dist/useEvent-BRH-aCqi.js +19 -0
  118. package/dist/{useIframeCapabilities-DBETvvPl.js → useIframeCapabilities-BXhDDoCG.js} +1 -1
  119. package/dist/useLifecycle-SniivYmC.js +112 -0
  120. package/dist/{vega-component-DPd52jvr.js → vega-component-DIxaDQNs.js} +161 -160
  121. package/dist/{vega-embed-container-BgKnsDbB.js → vega-embed-container--LYZqguO.js} +2 -2
  122. package/dist/{vega-loader.browser-CpbZAXub.js → vega-loader.browser-B9Pz0EGP.js} +1 -1
  123. package/dist/{vennDiagram-4TSXK5OY-CypgoBVZ.js → vennDiagram-4TSXK5OY-C-hQ4iNJ.js} +1 -1
  124. package/dist/{wardleyDiagram-VM6X3IG4-B6xjiQ_g.js → wardleyDiagram-VM6X3IG4-C2MQ_Umv.js} +1 -1
  125. package/dist/{xychartDiagram-S5SC5T6Z-e0PefDVs.js → xychartDiagram-S5SC5T6Z-BtTTBn7Q.js} +1 -1
  126. package/package.json +16 -20
  127. package/src/__mocks__/document.tsx +63 -0
  128. package/src/__mocks__/executions.ts +197 -0
  129. package/src/__mocks__/notebook.ts +3 -7
  130. package/src/__mocks__/partial.ts +26 -0
  131. package/src/__mocks__/requests.ts +1 -4
  132. package/src/__tests__/CellStatus.test.tsx +25 -26
  133. package/src/__tests__/fixtures/pair-config.ts +1 -1
  134. package/src/__tests__/main.test.tsx +24 -1
  135. package/src/__tests__/module-entry.test.ts +44 -0
  136. package/src/__tests__/mount.test.ts +1 -21
  137. package/src/__tests__/{host-imports.test.ts → replica-imports.test.ts} +12 -11
  138. package/src/components/ai/__tests__/ai-model-dropdown.test.tsx +63 -0
  139. package/src/components/ai/ai-model-dropdown.tsx +11 -6
  140. package/src/components/app-config/__tests__/state.test.tsx +50 -0
  141. package/src/components/app-config/ai-config.tsx +156 -150
  142. package/src/components/app-config/app-config-button.tsx +6 -5
  143. package/src/components/app-config/app-config-form.tsx +5 -9
  144. package/src/components/app-config/common.tsx +7 -9
  145. package/src/components/app-config/data-form.tsx +112 -113
  146. package/src/components/app-config/mcp-config.tsx +30 -53
  147. package/src/components/app-config/user-config-form.css +91 -0
  148. package/src/components/app-config/user-config-form.tsx +266 -349
  149. package/src/components/chat/__tests__/capabilities-popover.test.tsx +0 -1
  150. package/src/components/chat/__tests__/chat-display.test.tsx +196 -0
  151. package/src/components/chat/__tests__/chat-markdown.test.tsx +43 -0
  152. package/src/components/chat/__tests__/chat-utils.test.ts +93 -1
  153. package/src/components/chat/__tests__/message-queue.test.tsx +17 -0
  154. package/src/components/chat/__tests__/notebook-access-selector.test.tsx +64 -0
  155. package/src/components/chat/__tests__/turn-timing.test.ts +84 -0
  156. package/src/components/chat/__tests__/use-assistant-turn.test.ts +95 -0
  157. package/src/components/chat/__tests__/user-message.test.tsx +27 -0
  158. package/src/components/chat/acp/__tests__/agent-panel-initialization.test.tsx +1 -1
  159. package/src/components/chat/acp/__tests__/agent-panel.test.tsx +22 -17
  160. package/src/components/chat/acp/__tests__/session-tabs.test.tsx +43 -0
  161. package/src/components/chat/acp/__tests__/state.test.ts +2 -2
  162. package/src/components/chat/acp/agent-panel.tsx +6 -3
  163. package/src/components/chat/acp/blocks.tsx +13 -12
  164. package/src/components/chat/acp/common.tsx +4 -5
  165. package/src/components/chat/acp/model-selector.tsx +1 -1
  166. package/src/components/chat/acp/session-tabs.tsx +8 -3
  167. package/src/components/chat/agent-activity.tsx +132 -0
  168. package/src/components/chat/capabilities-popover.tsx +2 -11
  169. package/src/components/chat/chat-components.tsx +9 -4
  170. package/src/components/chat/chat-display.tsx +131 -57
  171. package/src/components/chat/chat-history-popover.tsx +6 -1
  172. package/src/components/chat/chat-markdown.tsx +15 -0
  173. package/src/components/chat/chat-panel.tsx +216 -294
  174. package/src/components/chat/chat-typography.css +108 -0
  175. package/src/components/chat/chat-utils.ts +24 -35
  176. package/src/components/chat/notebook-access-selector.stories.tsx +22 -0
  177. package/src/components/chat/notebook-access-selector.tsx +100 -0
  178. package/src/components/chat/tool-call/__tests__/tool-labels.test.ts +76 -0
  179. package/src/components/chat/tool-call/__tests__/tool-step.test.tsx +57 -0
  180. package/src/components/chat/tool-call/shared.ts +6 -0
  181. package/src/components/chat/tool-call/tool-call-view.tsx +22 -76
  182. package/src/components/chat/tool-call/tool-labels.ts +172 -0
  183. package/src/components/chat/tool-call/tool-result.tsx +1 -1
  184. package/src/components/chat/tool-call/tool-step.tsx +145 -0
  185. package/src/components/chat/turn-timing.ts +73 -0
  186. package/src/components/chat/use-assistant-turn.ts +65 -0
  187. package/src/components/chat/use-chat-generation.ts +20 -0
  188. package/src/components/chat/user-message.tsx +145 -0
  189. package/src/components/data-table/TableBottomBar.tsx +4 -1
  190. package/src/components/data-table/TableTopBar.tsx +2 -0
  191. package/src/components/data-table/__tests__/chart-spec-model.test.ts +195 -195
  192. package/src/components/data-table/__tests__/column-explorer.test.tsx +27 -1
  193. package/src/components/data-table/__tests__/columns.test.tsx +105 -0
  194. package/src/components/data-table/__tests__/context-menu-interaction.test.tsx +130 -0
  195. package/src/components/data-table/__tests__/data-table.test.tsx +1 -0
  196. package/src/components/data-table/__tests__/date-popover.test.tsx +72 -0
  197. package/src/components/data-table/__tests__/export-actions.test.tsx +105 -0
  198. package/src/components/data-table/__tests__/keyboard-selection.test.tsx +188 -0
  199. package/src/components/data-table/__tests__/pagination.test.tsx +28 -1
  200. package/src/components/data-table/__tests__/search-highlight.test.tsx +109 -0
  201. package/src/components/data-table/__tests__/url-detector.test.tsx +74 -0
  202. package/src/components/data-table/cell-selection/feature.ts +2 -2
  203. package/src/components/data-table/cell-selection/types.ts +2 -2
  204. package/src/components/data-table/charts/__tests__/chart-panel.test.tsx +74 -1
  205. package/src/components/data-table/charts/__tests__/spec-snapshot.test.ts +20 -20
  206. package/src/components/data-table/charts/__tests__/spec.test.ts +78 -52
  207. package/src/components/data-table/charts/__tests__/storage.test.ts +4 -1
  208. package/src/components/data-table/charts/chart-spec/spec.ts +68 -43
  209. package/src/components/data-table/charts/charts.tsx +40 -30
  210. package/src/components/data-table/charts/storage.ts +16 -5
  211. package/src/components/data-table/column-explorer-panel/column-explorer.tsx +1 -1
  212. package/src/components/data-table/column-formatting/types.ts +1 -2
  213. package/src/components/data-table/column-summary/chart-spec-model.tsx +23 -16
  214. package/src/components/data-table/column-summary/column-summary.tsx +2 -4
  215. package/src/components/data-table/column-visibility-dropdown.tsx +8 -13
  216. package/src/components/data-table/columns.tsx +45 -46
  217. package/src/components/data-table/context-menu.tsx +36 -20
  218. package/src/components/data-table/data-table.tsx +17 -1
  219. package/src/components/data-table/date-popover.tsx +6 -4
  220. package/src/components/data-table/export-actions.tsx +76 -48
  221. package/src/components/data-table/pagination.tsx +44 -33
  222. package/src/components/data-table/range-focus/atoms.ts +4 -6
  223. package/src/components/data-table/range-focus/use-cell-range-selection.ts +65 -38
  224. package/src/components/data-table/range-focus/use-scroll-into-view.ts +21 -2
  225. package/src/components/data-table/renderers.tsx +16 -7
  226. package/src/components/data-table/row-viewer-panel/__tests__/row-viewer.test.tsx +24 -1
  227. package/src/components/data-table/row-viewer-panel/row-viewer.tsx +4 -4
  228. package/src/components/data-table/search-highlight.tsx +79 -0
  229. package/src/components/data-table/types.ts +1 -0
  230. package/src/components/data-table/url-detector.tsx +49 -9
  231. package/src/components/datasources/__tests__/column-actions.test.tsx +97 -0
  232. package/src/components/datasources/__tests__/column-preview.test.tsx +96 -5
  233. package/src/components/datasources/__tests__/datasources-interactions.test.tsx +181 -0
  234. package/src/components/datasources/__tests__/table-list.test.tsx +118 -0
  235. package/src/components/datasources/__tests__/utils.test.ts +57 -6
  236. package/src/components/datasources/column-preview.tsx +17 -12
  237. package/src/components/datasources/datasources.tsx +56 -43
  238. package/src/components/datasources/utils.ts +32 -23
  239. package/src/components/debugger/debugger-code.tsx +5 -0
  240. package/src/components/dependency-graph/custom-node.tsx +1 -1
  241. package/src/components/dependency-graph/dependency-graph-tree.tsx +8 -8
  242. package/src/components/dependency-graph/elements.ts +24 -21
  243. package/src/components/dependency-graph/minimap-content.tsx +15 -20
  244. package/src/components/dependency-graph/panels.tsx +4 -2
  245. package/src/components/dependency-graph/utils/__tests__/layout.test.ts +18 -11
  246. package/src/components/editor/SortableCell.tsx +1 -3
  247. package/src/components/editor/__tests__/Output.test.tsx +50 -32
  248. package/src/{core/attachments/__tests__/toast.test.tsx → components/editor/__tests__/agent-toast.test.tsx} +1 -1
  249. package/src/components/editor/__tests__/data-attributes.test.tsx +28 -23
  250. package/src/components/editor/__tests__/dynamic-favicon.test.tsx +111 -18
  251. package/src/components/editor/__tests__/output-persistence.test.tsx +28 -9
  252. package/src/components/editor/__tests__/package-alert.test.tsx +22 -1
  253. package/src/components/editor/actions/__tests__/name-cell-input.test.tsx +69 -0
  254. package/src/components/editor/actions/__tests__/pair-with-agent-commands.test.ts +21 -40
  255. package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +41 -14
  256. package/src/components/editor/actions/__tests__/pair-with-agent.test.tsx +6 -24
  257. package/src/components/editor/actions/__tests__/useCellActionButton.test.tsx +131 -0
  258. package/src/components/editor/actions/name-cell-input.tsx +4 -3
  259. package/src/components/editor/actions/pair-with-agent-commands.ts +10 -7
  260. package/src/components/editor/actions/pair-with-agent-modal.tsx +29 -30
  261. package/src/components/editor/actions/pair-with-agent.tsx +16 -39
  262. package/src/components/editor/actions/useCellActionButton.tsx +47 -35
  263. package/src/components/editor/actions/useConfigActions.tsx +3 -4
  264. package/src/components/editor/actions/useNotebookActions.tsx +25 -29
  265. package/src/components/editor/actions/useRestartKernel.tsx +1 -1
  266. package/src/components/editor/actions/useSetCodeVisibility.ts +11 -12
  267. package/src/components/editor/actions/useSetOutputsExpanded.ts +14 -19
  268. package/src/components/editor/ai/ai-completion-editor.tsx +2 -1
  269. package/src/components/editor/ai/completion-handlers.tsx +0 -37
  270. package/src/components/editor/ai/prompt-input.tsx +189 -0
  271. package/src/components/editor/alerts/__tests__/connection-notice.test.tsx +1 -1
  272. package/src/components/editor/alerts/connection-notice.tsx +1 -1
  273. package/src/components/editor/alerts/startup-logs-alert.tsx +1 -0
  274. package/src/components/editor/alerts/stdin-blocking-alert.tsx +4 -2
  275. package/src/components/editor/app-container.tsx +1 -1
  276. package/src/components/editor/boundary/__tests__/ErrorBoundary.test.tsx +1 -1
  277. package/src/components/editor/cell/CellStatus.tsx +28 -23
  278. package/src/components/editor/cell/CreateCellButton.tsx +15 -21
  279. package/src/components/editor/cell/DeleteButton.tsx +3 -2
  280. package/src/components/editor/cell/RunButton.tsx +18 -8
  281. package/src/components/editor/cell/StagedAICell.tsx +52 -137
  282. package/src/components/editor/cell/StopButton.tsx +2 -2
  283. package/src/components/editor/cell/__tests__/useRunCells.test.tsx +169 -0
  284. package/src/components/editor/cell/__tests__/useShouldShowInterrupt.test.ts +39 -0
  285. package/src/components/editor/cell/cell-actions.tsx +12 -1
  286. package/src/components/editor/cell/cell-context-menu.tsx +4 -1
  287. package/src/components/editor/cell/code/__tests__/language-toggle.test.tsx +54 -0
  288. package/src/components/editor/cell/code/cell-editor.tsx +65 -169
  289. package/src/components/editor/cell/code/language-toggle.tsx +8 -7
  290. package/src/components/editor/cell/collapse.tsx +16 -6
  291. package/src/components/editor/cell/useAddCell.ts +4 -8
  292. package/src/components/editor/cell/useDeleteCell.tsx +10 -36
  293. package/src/components/editor/cell/useRunCells.ts +49 -8
  294. package/src/components/editor/cell/useShouldShowInterrupt.ts +2 -12
  295. package/src/components/editor/cell/useSplitCell.tsx +10 -6
  296. package/src/components/editor/chrome/components/__tests__/feedback-button.test.tsx +1 -1
  297. package/src/components/editor/chrome/components/feedback-button.tsx +2 -8
  298. package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +31 -2
  299. package/src/components/editor/chrome/panels/__tests__/runtime-details.test.tsx +1 -1
  300. package/src/components/editor/chrome/panels/__tests__/sandbox-recovery.test.tsx +1 -1
  301. package/src/components/editor/chrome/panels/cache-panel.tsx +1 -0
  302. package/src/components/editor/chrome/panels/dependency-graph-panel.tsx +1 -1
  303. package/src/components/editor/chrome/panels/logs-panel.tsx +2 -1
  304. package/src/components/editor/chrome/panels/outline/__tests__/outline-keyboard.test.tsx +84 -0
  305. package/src/components/editor/chrome/panels/outline/floating-outline.tsx +39 -14
  306. package/src/components/editor/chrome/panels/outline/useActiveOutline.tsx +3 -2
  307. package/src/components/editor/chrome/panels/packages-panel.tsx +4 -7
  308. package/src/components/editor/chrome/panels/panel-header.tsx +13 -0
  309. package/src/components/editor/chrome/panels/secrets-panel.tsx +1 -0
  310. package/src/components/editor/chrome/panels/session-panel.tsx +1 -1
  311. package/src/components/editor/chrome/panels/snippets-panel.tsx +10 -12
  312. package/src/components/editor/chrome/panels/variable-panel.tsx +1 -5
  313. package/src/components/editor/chrome/types.ts +1 -1
  314. package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +7 -9
  315. package/src/components/editor/chrome/wrapper/__tests__/document-sync-status.test.tsx +92 -0
  316. package/src/components/editor/chrome/wrapper/__tests__/pending-ai-cells.test.tsx +265 -37
  317. package/src/components/editor/chrome/wrapper/__tests__/useAskAiAboutCell.test.tsx +58 -0
  318. package/src/components/editor/chrome/wrapper/app-chrome.tsx +91 -74
  319. package/src/components/editor/chrome/wrapper/footer-items/attachment-status.tsx +4 -4
  320. package/src/components/editor/chrome/wrapper/footer-items/backend-status.tsx +1 -1
  321. package/src/components/editor/chrome/wrapper/footer-items/document-sync-status.tsx +54 -0
  322. package/src/components/editor/chrome/wrapper/footer-items/lsp-status.tsx +1 -1
  323. package/src/components/editor/chrome/wrapper/footer-items/machine-stats.tsx +1 -1
  324. package/src/components/editor/chrome/wrapper/footer-items/runtime-settings.tsx +1 -7
  325. package/src/components/editor/chrome/wrapper/footer.tsx +2 -2
  326. package/src/components/editor/chrome/wrapper/minimap-state.ts +1 -1
  327. package/src/components/editor/chrome/wrapper/pending-ai-cells.tsx +143 -72
  328. package/src/components/editor/chrome/wrapper/pending-ai-changes-badge.tsx +25 -0
  329. package/src/components/editor/chrome/wrapper/sidebar.tsx +8 -4
  330. package/src/components/editor/chrome/wrapper/storage.ts +1 -1
  331. package/src/components/editor/chrome/wrapper/useAskAiAboutCell.ts +36 -0
  332. package/src/components/editor/columns/__tests__/cell-column.test.tsx +1 -1
  333. package/src/components/editor/columns/cell-column.tsx +1 -1
  334. package/src/components/editor/columns/sortable-column.tsx +2 -2
  335. package/src/components/editor/connections/components.tsx +5 -10
  336. package/src/components/editor/controls/Controls.tsx +4 -4
  337. package/src/components/editor/controls/__tests__/command-palette.test.tsx +53 -0
  338. package/src/components/editor/controls/__tests__/keyboard-shortcuts.test.tsx +61 -0
  339. package/src/components/editor/controls/__tests__/notebook-menu-dropdown.test.tsx +10 -4
  340. package/src/components/editor/controls/command-palette.tsx +22 -10
  341. package/src/components/editor/controls/keyboard-shortcuts.tsx +14 -5
  342. package/src/components/editor/documentation.css +34 -11
  343. package/src/components/editor/dynamic-favicon.tsx +21 -40
  344. package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +26 -22
  345. package/src/components/editor/errors/auto-fix.tsx +4 -8
  346. package/src/components/editor/errors/send-error-report-button.tsx +3 -6
  347. package/src/components/editor/file-tree/__tests__/file-explorer-interactions.test.tsx +6 -0
  348. package/src/components/editor/file-tree/__tests__/file-explorer-navigation.test.tsx +47 -0
  349. package/src/components/editor/file-tree/__tests__/use-file-search.test.tsx +91 -0
  350. package/src/components/editor/file-tree/file-explorer.tsx +48 -15
  351. package/src/components/editor/file-tree/file-name-input.tsx +1 -0
  352. package/src/components/editor/file-tree/file-viewer.tsx +4 -2
  353. package/src/components/editor/file-tree/use-file-search.ts +35 -21
  354. package/src/components/editor/header/__tests__/filename-form.test.tsx +1 -4
  355. package/src/components/editor/header/__tests__/status.test.tsx +5 -3
  356. package/src/components/editor/header/app-header.tsx +1 -1
  357. package/src/components/editor/header/filename-form.tsx +1 -1
  358. package/src/components/editor/header/status.tsx +44 -22
  359. package/src/components/editor/links/__tests__/cell-link.test.tsx +148 -0
  360. package/src/components/editor/links/cell-link-list.tsx +1 -1
  361. package/src/components/editor/links/cell-link.tsx +21 -11
  362. package/src/components/editor/navigation/__tests__/clipboard.test.ts +60 -38
  363. package/src/components/editor/navigation/__tests__/navigation.test.ts +108 -85
  364. package/src/components/editor/navigation/__tests__/selection.test.ts +43 -21
  365. package/src/components/editor/navigation/clipboard.ts +17 -14
  366. package/src/components/editor/navigation/focus-utils.ts +23 -4
  367. package/src/components/editor/navigation/multi-cell-action-toolbar.tsx +17 -33
  368. package/src/components/editor/navigation/navigation.ts +74 -72
  369. package/src/components/editor/navigation/selection.ts +30 -29
  370. package/src/components/editor/notebook-banner.tsx +1 -0
  371. package/src/components/editor/notebook-cell.tsx +126 -152
  372. package/src/components/editor/output/MarimoErrorOutput.tsx +2 -1
  373. package/src/components/editor/output/MarimoTracebackOutput.tsx +19 -17
  374. package/src/components/editor/output/Outputs.css +8 -0
  375. package/src/components/editor/output/VideoOutput.tsx +6 -0
  376. package/src/components/editor/output/__tests__/VideoOutput.test.tsx +131 -0
  377. package/src/components/editor/output/__tests__/traceback.test.tsx +27 -1
  378. package/src/components/editor/output/ansi-reduce.ts +33 -20
  379. package/src/components/editor/output/console/ConsoleOutput.tsx +19 -25
  380. package/src/components/editor/output/console/__tests__/ConsoleOutput.test.tsx +125 -69
  381. package/src/components/editor/package-alert.tsx +14 -7
  382. package/src/components/editor/renderers/__tests__/add-cell-buttons.test.tsx +18 -0
  383. package/src/components/editor/renderers/cell-array.tsx +32 -100
  384. package/src/components/editor/renderers/grid-layout/__tests__/grid-layout.test.tsx +75 -0
  385. package/src/components/editor/renderers/grid-layout/grid-layout.tsx +28 -22
  386. package/src/components/editor/renderers/slides-layout/__tests__/plugin.test.ts +0 -4
  387. package/src/components/editor/renderers/slides-layout/slides-layout.tsx +1 -1
  388. package/src/components/editor/renderers/vertical-layout/__tests__/useFocusFirstEditor.test.ts +4 -4
  389. package/src/components/editor/renderers/vertical-layout/sidebar/toggle.tsx +7 -1
  390. package/src/components/editor/renderers/vertical-layout/useFocusFirstEditor.ts +1 -1
  391. package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +19 -37
  392. package/src/components/editor/stdin-blocking-alert.tsx +4 -2
  393. package/src/components/home/components.tsx +17 -31
  394. package/src/components/icons/__tests__/large-spinner.test.tsx +39 -0
  395. package/src/components/icons/large-spinner.tsx +3 -14
  396. package/src/components/icons/spinner.tsx +1 -1
  397. package/src/components/markdown/markdown-renderer.tsx +39 -28
  398. package/src/components/mcp/mcp-status-indicator.tsx +18 -14
  399. package/src/components/pages/__tests__/home-page.test.tsx +116 -0
  400. package/src/components/pages/edit-page.tsx +19 -17
  401. package/src/components/pages/home-page.tsx +164 -91
  402. package/src/components/scratchpad/scratchpad.test.tsx +89 -0
  403. package/src/components/scratchpad/scratchpad.tsx +47 -33
  404. package/src/components/scratchpad/state.ts +7 -0
  405. package/src/components/slides/__tests__/minimap-actions.test.tsx +22 -29
  406. package/src/components/slides/__tests__/minimap.test.ts +38 -19
  407. package/src/components/slides/__tests__/slide-scroll-hint.test.tsx +37 -0
  408. package/src/components/slides/minimap.tsx +22 -14
  409. package/src/components/slides/reveal-component.tsx +73 -36
  410. package/src/components/slides/reveal-slides.css +5 -4
  411. package/src/components/slides/slide-cell-view.tsx +37 -34
  412. package/src/components/slides/slide-form.tsx +1 -0
  413. package/src/components/slides/slide-scroll-hint.tsx +3 -1
  414. package/src/components/slides/slide.tsx +3 -4
  415. package/src/components/sort/SortableCellsProvider.tsx +8 -25
  416. package/src/components/tracing/tracing-keyboard.test.tsx +85 -0
  417. package/src/components/tracing/tracing.tsx +46 -42
  418. package/src/components/ui/__tests__/draggable-popover.test.tsx +24 -0
  419. package/src/components/ui/__tests__/fullscreen.test.tsx +44 -2
  420. package/src/components/ui/__tests__/range-slider.test.tsx +165 -0
  421. package/src/components/ui/alert-dialog.tsx +1 -1
  422. package/src/components/ui/draggable-popover.tsx +24 -2
  423. package/src/components/ui/fullscreen.tsx +25 -1
  424. package/src/components/ui/range-slider.tsx +22 -9
  425. package/src/components/ui/toast.tsx +1 -1
  426. package/src/core/MarimoApp.tsx +3 -1
  427. package/src/core/ai/__tests__/staged-cells.test.ts +89 -608
  428. package/src/core/ai/context/context.ts +2 -0
  429. package/src/core/ai/context/providers/__tests__/cell-output.test.ts +8 -12
  430. package/src/core/ai/context/providers/__tests__/cell.test.ts +49 -0
  431. package/src/core/ai/context/providers/cell-output.ts +2 -2
  432. package/src/core/ai/context/providers/cell.ts +54 -0
  433. package/src/core/ai/context/providers/datasource.ts +6 -2
  434. package/src/core/ai/staged-cells.ts +56 -323
  435. package/src/core/ai/state.ts +3 -0
  436. package/src/core/ai/tools/__tests__/edit-notebook-tool.test.ts +40 -88
  437. package/src/core/ai/tools/__tests__/registry.test.ts +17 -0
  438. package/src/core/ai/tools/base.ts +18 -15
  439. package/src/core/ai/tools/edit-notebook-tool.ts +86 -80
  440. package/src/core/ai/tools/registry.ts +13 -10
  441. package/src/core/ai/tools/sample-tool.ts +6 -7
  442. package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +4 -527
  443. package/src/core/cells/__tests__/actions.test.ts +623 -35
  444. package/src/core/cells/__tests__/add-missing-import.test.ts +7 -8
  445. package/src/core/cells/__tests__/arrange.test.ts +432 -0
  446. package/src/core/cells/__tests__/cell.test.ts +148 -150
  447. package/src/core/cells/__tests__/cells.test.ts +390 -2925
  448. package/src/core/cells/__tests__/collapseConsoleOutputs.test.ts +54 -0
  449. package/src/core/cells/__tests__/document.test.ts +158 -0
  450. package/src/core/cells/__tests__/editor.test.ts +57 -0
  451. package/src/core/cells/__tests__/flush-document.test.ts +40 -0
  452. package/src/core/cells/__tests__/harness.ts +106 -0
  453. package/src/core/cells/__tests__/layout.test.ts +176 -0
  454. package/src/core/cells/__tests__/logs.test.ts +0 -13
  455. package/src/core/cells/__tests__/notebook-outline.test.ts +122 -0
  456. package/src/core/cells/__tests__/outputs.test.tsx +71 -53
  457. package/src/core/cells/__tests__/pending-delete-service.test.tsx +11 -20
  458. package/src/core/cells/__tests__/session.test.ts +445 -211
  459. package/src/core/cells/__tests__/utils.test.ts +135 -337
  460. package/src/core/cells/actions.ts +677 -6
  461. package/src/core/cells/add-missing-import.ts +47 -43
  462. package/src/core/cells/arrange.ts +529 -0
  463. package/src/core/cells/cell.ts +78 -146
  464. package/src/core/cells/cells.ts +116 -1457
  465. package/src/core/cells/columns.ts +38 -0
  466. package/src/core/cells/document.ts +167 -0
  467. package/src/core/cells/edit.ts +30 -0
  468. package/src/core/cells/editor.ts +82 -0
  469. package/src/core/cells/focus.ts +27 -6
  470. package/src/core/cells/layout.ts +221 -0
  471. package/src/core/cells/outputs.ts +12 -28
  472. package/src/core/cells/pending-delete-service.ts +8 -14
  473. package/src/core/cells/scrollCellIntoView.ts +6 -0
  474. package/src/core/cells/session.ts +76 -53
  475. package/src/core/cells/types.ts +21 -51
  476. package/src/core/cells/utils.ts +85 -87
  477. package/src/core/codemirror/__tests__/format.test.ts +0 -34
  478. package/src/core/codemirror/__tests__/setup.test.ts +61 -29
  479. package/src/core/codemirror/ai/__tests__/resources.test.ts +117 -0
  480. package/src/core/codemirror/ai/resources.ts +1 -1
  481. package/src/core/codemirror/cells/__tests__/extensions.test.ts +2 -6
  482. package/src/core/codemirror/cells/extensions.ts +18 -30
  483. package/src/core/codemirror/cells/state.ts +2 -6
  484. package/src/core/codemirror/cm.ts +5 -1
  485. package/src/core/codemirror/config/types.ts +1 -1
  486. package/src/core/codemirror/copilot/__tests__/getCodes.test.ts +6 -7
  487. package/src/core/codemirror/copilot/getCodes.ts +2 -2
  488. package/src/core/codemirror/format.ts +6 -23
  489. package/src/core/codemirror/keymaps/vim.ts +4 -8
  490. package/src/core/codemirror/language/__tests__/sql.test.ts +22 -20
  491. package/src/core/codemirror/language/__tests__/utils.test.ts +187 -15
  492. package/src/core/codemirror/language/cell-code.ts +114 -0
  493. package/src/core/codemirror/language/extension.ts +52 -35
  494. package/src/core/codemirror/language/languages/markdown.ts +8 -6
  495. package/src/core/codemirror/language/languages/python.ts +19 -13
  496. package/src/core/codemirror/language/languages/sql/sql.ts +16 -24
  497. package/src/core/codemirror/language/panel/panel.tsx +1 -0
  498. package/src/core/codemirror/language/types.ts +7 -7
  499. package/src/core/codemirror/language/utils.ts +9 -39
  500. package/src/core/codemirror/lsp/__tests__/notebook-lsp.test.ts +1 -2
  501. package/src/core/codemirror/lsp/notebook-lsp.ts +6 -5
  502. package/src/core/codemirror/lsp/utils.ts +1 -1
  503. package/src/core/codemirror/misc/__tests__/paste.test.ts +3 -1
  504. package/src/core/codemirror/misc/paste.ts +1 -1
  505. package/src/core/codemirror/placeholder/__tests__/extensions.test.ts +75 -0
  506. package/src/core/codemirror/placeholder/extensions.ts +80 -19
  507. package/src/core/codemirror/readonly/__tests__/extension.test.ts +1 -1
  508. package/src/core/codemirror/readonly/extension.ts +2 -2
  509. package/src/core/codemirror/utils.ts +1 -0
  510. package/src/core/config/__tests__/app-config.test.ts +64 -0
  511. package/src/core/config/__tests__/config-schema.test.ts +56 -6
  512. package/src/core/config/config-schema.ts +17 -4
  513. package/src/core/config/config.ts +65 -20
  514. package/src/core/config/feature-flag.tsx +0 -2
  515. package/src/core/datasets/__tests__/data-source-discovery.test.ts +5 -5
  516. package/src/core/datasets/data-source-discovery.ts +4 -6
  517. package/src/core/documentation/doc-lookup.ts +1 -1
  518. package/src/core/edit-app.tsx +22 -28
  519. package/src/core/errors/__tests__/error-entries.test.ts +40 -1
  520. package/src/core/errors/error-entries.ts +6 -4
  521. package/src/core/executions/__tests__/atoms.test.ts +98 -0
  522. package/src/core/executions/__tests__/freshness.test.ts +482 -0
  523. package/src/core/executions/__tests__/hooks.test.tsx +106 -0
  524. package/src/core/executions/__tests__/select.test.ts +268 -0
  525. package/src/core/executions/atoms.ts +228 -0
  526. package/src/core/executions/freshness.ts +171 -0
  527. package/src/core/executions/hooks.ts +47 -0
  528. package/src/core/executions/index.ts +33 -0
  529. package/src/core/executions/select.ts +175 -0
  530. package/src/core/export/__tests__/hooks.test.ts +0 -5
  531. package/src/core/export/hooks.ts +1 -1
  532. package/src/core/export/layout.ts +2 -2
  533. package/src/core/hotkeys/actions.ts +2 -4
  534. package/src/core/hotkeys/hotkeys.ts +2 -1
  535. package/src/core/islands/__tests__/bootstrap.test.ts +69 -17
  536. package/src/core/islands/__tests__/bridge.test.ts +85 -27
  537. package/src/core/islands/bootstrap.ts +49 -54
  538. package/src/core/islands/bridge.ts +84 -25
  539. package/src/core/islands/components/__tests__/IslandControls.test.tsx +7 -5
  540. package/src/core/islands/components/__tests__/web-components.test.tsx +63 -0
  541. package/src/core/islands/components/output-wrapper.tsx +5 -5
  542. package/src/core/islands/components/web-components.tsx +12 -1
  543. package/src/core/islands/worker/__tests__/controller.test.ts +62 -4
  544. package/src/core/islands/worker/controller.ts +4 -10
  545. package/src/core/islands/worker/worker.tsx +33 -33
  546. package/src/core/kernel/__tests__/RuntimeState.test.ts +1 -1
  547. package/src/core/kernel/__tests__/handlers.test.ts +7 -157
  548. package/src/core/kernel/handlers.ts +13 -79
  549. package/src/core/kernel/state.ts +0 -15
  550. package/src/core/lifecycle/render-policy.ts +12 -26
  551. package/src/core/meta/__tests__/code-visibility.test.tsx +78 -70
  552. package/src/core/meta/code-visibility.ts +18 -9
  553. package/src/core/network/__tests__/connection.test.ts +1 -1
  554. package/src/core/network/__tests__/requests-lazy.test.ts +6 -8
  555. package/src/core/network/connection-notice.ts +1 -1
  556. package/src/core/network/connection.ts +1 -1
  557. package/src/core/network/requests-lazy.ts +5 -8
  558. package/src/core/network/requests-network.ts +5 -31
  559. package/src/core/network/requests-static.ts +1 -4
  560. package/src/core/network/requests-toasting.tsx +1 -4
  561. package/src/core/network/types.ts +6 -14
  562. package/src/core/network/useConnectionNotice.ts +8 -2
  563. package/src/core/notebook/__tests__/operations.test.ts +51 -0
  564. package/src/core/notebook/attachments.ts +38 -0
  565. package/src/core/notebook/document/__tests__/codemirror.test.ts +759 -0
  566. package/src/core/notebook/document/__tests__/draft.test.ts +363 -0
  567. package/src/core/notebook/document/__tests__/fixtures.ts +92 -0
  568. package/src/core/notebook/document/__tests__/hooks.test.tsx +391 -0
  569. package/src/core/notebook/document/__tests__/lifecycle-scenarios.test.ts +61 -0
  570. package/src/core/notebook/document/__tests__/model.test.ts +247 -0
  571. package/src/core/notebook/document/__tests__/protocol-fixtures.ts +38 -0
  572. package/src/core/notebook/document/__tests__/recovery-scenarios.test.ts +230 -0
  573. package/src/core/notebook/document/__tests__/scenario-fixture.ts +43 -0
  574. package/src/core/notebook/document/__tests__/scenarios.test.ts +205 -0
  575. package/src/core/notebook/document/__tests__/state.test.ts +459 -0
  576. package/src/core/notebook/document/__tests__/submission.test.ts +203 -0
  577. package/src/core/notebook/document/__tests__/test-host.test.ts +92 -0
  578. package/src/core/notebook/document/__tests__/test-host.ts +247 -0
  579. package/src/core/notebook/document/__tests__/test-notebook.ts +149 -0
  580. package/src/core/notebook/document/__tests__/user.ts +61 -0
  581. package/src/core/notebook/document/codemirror.ts +216 -0
  582. package/src/core/notebook/document/draft.ts +502 -0
  583. package/src/core/notebook/document/hooks.tsx +118 -0
  584. package/src/core/notebook/document/index.ts +24 -0
  585. package/src/core/notebook/document/model.ts +102 -0
  586. package/src/core/notebook/document/select.ts +41 -0
  587. package/src/core/notebook/document/state.ts +378 -0
  588. package/src/core/notebook/document/sync.ts +369 -0
  589. package/src/core/notebook/index.ts +19 -0
  590. package/src/core/notebook/operations.ts +30 -0
  591. package/src/core/notebook/replica/AGENTS.md +11 -0
  592. package/src/core/{host → notebook/replica}/__tests__/attachments.test.ts +9 -10
  593. package/src/core/{host → notebook/replica}/__tests__/document.test.ts +18 -16
  594. package/src/core/notebook/replica/__tests__/executions.test.ts +200 -0
  595. package/src/core/notebook/replica/__tests__/fixtures.ts +79 -0
  596. package/src/core/notebook/replica/__tests__/replica.test.ts +559 -0
  597. package/src/core/notebook/replica/__tests__/results.test.ts +193 -0
  598. package/src/core/notebook/replica/__tests__/runtime.test.ts +78 -0
  599. package/src/core/notebook/replica/backend.ts +19 -0
  600. package/src/core/notebook/replica/connection.ts +42 -0
  601. package/src/core/notebook/replica/index.ts +20 -0
  602. package/src/core/notebook/replica/replica.ts +336 -0
  603. package/src/core/{host → notebook/replica}/resources/attachments.ts +6 -3
  604. package/src/core/{host → notebook/replica}/resources/document.ts +3 -3
  605. package/src/core/notebook/replica/resources/executions.ts +157 -0
  606. package/src/core/notebook/replica/resources/results.ts +48 -0
  607. package/src/core/notebook/replica/resources/runtime.ts +14 -0
  608. package/src/core/packages/__tests__/useSandboxController.test.tsx +1 -1
  609. package/src/core/packages/useSandboxController.ts +1 -1
  610. package/src/core/run-app.tsx +49 -51
  611. package/src/core/runtime/__tests__/adapter.test.ts +4 -4
  612. package/src/core/runtime/__tests__/createWsUrl.test.ts +77 -7
  613. package/src/core/runtime/__tests__/runtime.test.ts +32 -74
  614. package/src/core/runtime/adapter.ts +5 -5
  615. package/src/core/runtime/config.ts +2 -2
  616. package/src/core/runtime/runtime.ts +33 -46
  617. package/src/core/runtime/types.ts +0 -2
  618. package/src/core/saving/__tests__/document.test.tsx +123 -0
  619. package/src/core/saving/__tests__/state.test.ts +111 -0
  620. package/src/core/saving/__tests__/useAutoSave.test.tsx +67 -0
  621. package/src/core/saving/document.ts +33 -0
  622. package/src/core/saving/filename.ts +1 -1
  623. package/src/core/saving/save-component.tsx +39 -60
  624. package/src/core/saving/state.ts +38 -39
  625. package/src/core/saving/useAutoSave.ts +17 -53
  626. package/src/core/session/__tests__/backend.test.ts +236 -0
  627. package/src/core/session/__tests__/useNotebookReplica.hook.test.tsx +186 -0
  628. package/src/core/{websocket → session}/__tests__/useNotebookSession.hook.test.tsx +245 -262
  629. package/src/core/session/active-notebook.ts +28 -0
  630. package/src/core/session/backend.ts +123 -0
  631. package/src/core/session/embedded.ts +7 -0
  632. package/src/core/{websocket → session}/types.ts +2 -2
  633. package/src/core/session/useNotebookReplica.ts +74 -0
  634. package/src/core/{websocket → session}/useNotebookSession.tsx +80 -67
  635. package/src/core/state/__tests__/jotai.test.tsx +108 -0
  636. package/src/core/state/jotai.ts +1 -1
  637. package/src/core/static/__tests__/files.test.ts +38 -10
  638. package/src/core/static/files.ts +2 -0
  639. package/src/core/{websocket → transports}/__tests__/connection-state.test.ts +1 -13
  640. package/src/core/transports/__tests__/server.test.ts +408 -0
  641. package/src/core/transports/__tests__/static.test.ts +306 -0
  642. package/src/core/transports/__tests__/wasm.test.ts +166 -0
  643. package/src/core/{websocket → transports}/connection-state.ts +9 -16
  644. package/src/core/transports/server.ts +152 -0
  645. package/src/core/transports/static.ts +166 -0
  646. package/src/core/transports/wasm.ts +97 -0
  647. package/src/core/vscode/__tests__/vscode-bindings.test.ts +125 -0
  648. package/src/core/vscode/vscode-bindings.ts +14 -2
  649. package/src/core/wasm/__tests__/bridge.test.ts +210 -64
  650. package/src/core/wasm/__tests__/worker.test.ts +188 -0
  651. package/src/core/wasm/bridge.ts +120 -73
  652. package/src/core/wasm/rpc.ts +15 -3
  653. package/src/core/wasm/store.ts +3 -3
  654. package/src/core/wasm/worker/bootstrap.ts +55 -51
  655. package/src/core/wasm/worker/getMarimoWheel.ts +1 -1
  656. package/src/core/wasm/worker/save-worker.ts +9 -10
  657. package/src/core/wasm/worker/types.ts +30 -18
  658. package/src/core/wasm/worker/worker.ts +118 -72
  659. package/src/css/app/Cell.css +7 -36
  660. package/src/css/app/codemirror.css +1 -1
  661. package/src/css/common.css +1 -1
  662. package/src/css/globals.css +72 -24
  663. package/src/hooks/__tests__/useAsyncData.test.tsx +148 -0
  664. package/src/hooks/__tests__/useDebounce.test.tsx +51 -0
  665. package/src/hooks/__tests__/useDeepCompareMemoize.test.tsx +39 -0
  666. package/src/hooks/__tests__/useDelayElapsed.test.tsx +28 -0
  667. package/src/hooks/__tests__/useInternalStateWithSync.test.tsx +20 -0
  668. package/src/hooks/__tests__/useScript.test.tsx +77 -0
  669. package/src/hooks/__tests__/useStateForKey.test.tsx +257 -0
  670. package/src/hooks/useAsyncData.ts +3 -3
  671. package/src/hooks/useDataSourceDiscovery.ts +6 -4
  672. package/src/hooks/useDebounce.ts +6 -10
  673. package/src/hooks/useDeepCompareMemoize.ts +1 -1
  674. package/src/hooks/useDelayElapsed.ts +14 -10
  675. package/src/hooks/useElapsedTime.ts +3 -3
  676. package/src/hooks/useEventListener.ts +1 -2
  677. package/src/hooks/useInputHistory.ts +4 -7
  678. package/src/hooks/useInternalStateWithSync.ts +6 -6
  679. package/src/hooks/useResizeObserver.ts +4 -7
  680. package/src/hooks/useScript.ts +67 -70
  681. package/src/hooks/useStateForKey.ts +93 -0
  682. package/src/mount.tsx +20 -15
  683. package/src/plugins/__tests__/plugin-object-schemas.test.ts +104 -0
  684. package/src/plugins/core/RenderHTML.tsx +3 -5
  685. package/src/plugins/core/__test__/RenderHTML-copy.test.tsx +60 -0
  686. package/src/plugins/core/registerReactComponent.tsx +5 -14
  687. package/src/plugins/impl/CodeEditorPlugin.tsx +1 -1
  688. package/src/plugins/impl/DataEditorPlugin.tsx +34 -13
  689. package/src/plugins/impl/DataTablePlugin.tsx +69 -37
  690. package/src/plugins/impl/DropdownPlugin.tsx +2 -6
  691. package/src/plugins/impl/FileBrowserPlugin.tsx +4 -16
  692. package/src/plugins/impl/FormPlugin.tsx +1 -1
  693. package/src/plugins/impl/RangeSliderPlugin.tsx +16 -2
  694. package/src/plugins/impl/RefreshPlugin.tsx +1 -0
  695. package/src/plugins/impl/SliderPlugin.tsx +6 -8
  696. package/src/plugins/impl/__tests__/DataEditorPlugin.test.ts +93 -4
  697. package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +387 -1
  698. package/src/plugins/impl/__tests__/FileBrowserPlugin.test.tsx +45 -0
  699. package/src/plugins/impl/__tests__/RangeSliderPlugin.test.tsx +109 -0
  700. package/src/plugins/impl/__tests__/SliderPlugin.test.tsx +24 -3
  701. package/src/plugins/impl/anywidget/__tests__/registry.test.ts +153 -0
  702. package/src/plugins/impl/anywidget/__tests__/widget-binding.test.ts +241 -2
  703. package/src/plugins/impl/anywidget/model.ts +3 -1
  704. package/src/plugins/impl/anywidget/registry.ts +18 -14
  705. package/src/plugins/impl/anywidget/runtime.ts +21 -9
  706. package/src/plugins/impl/anywidget/widget-binding.ts +160 -82
  707. package/src/plugins/impl/chat/__tests__/chat-prompts.test.tsx +93 -0
  708. package/src/plugins/impl/chat/chat-ui.tsx +17 -15
  709. package/src/plugins/impl/code/any-language-editor.tsx +2 -2
  710. package/src/plugins/impl/common/error-banner.tsx +5 -3
  711. package/src/plugins/impl/data-editor/__tests__/column-types.test.ts +42 -0
  712. package/src/plugins/impl/data-editor/__tests__/editor-state.test.ts +52 -1
  713. package/src/plugins/impl/data-editor/__tests__/glide-data-editor.test.tsx +120 -4
  714. package/src/plugins/impl/data-editor/__tests__/glide-utils.test.ts +116 -0
  715. package/src/plugins/impl/data-editor/column-types.ts +55 -0
  716. package/src/plugins/impl/data-editor/editor-state.ts +18 -2
  717. package/src/plugins/impl/data-editor/glide-data-editor.tsx +74 -12
  718. package/src/plugins/impl/data-editor/glide-utils.ts +39 -38
  719. package/src/plugins/impl/data-editor/types.ts +5 -0
  720. package/src/plugins/impl/data-explorer/ConnectedDataExplorerComponent.tsx +1 -1
  721. package/src/plugins/impl/data-explorer/components/column-summary.tsx +4 -2
  722. package/src/plugins/impl/data-explorer/state/reducer.ts +1 -1
  723. package/src/plugins/impl/data-frames/DataFramePlugin.tsx +1 -2
  724. package/src/plugins/impl/data-frames/__tests__/sidebar.test.tsx +160 -0
  725. package/src/plugins/impl/data-frames/forms/__tests__/__snapshots__/form.test.tsx.snap +66 -66
  726. package/src/plugins/impl/data-frames/panel.tsx +44 -16
  727. package/src/plugins/impl/mpl-interactive/MplInteractivePlugin.tsx +5 -3
  728. package/src/plugins/impl/panel/PanelPlugin.tsx +16 -17
  729. package/src/plugins/impl/panel/__tests__/utils.test.ts +39 -1
  730. package/src/plugins/impl/panel/utils.ts +17 -0
  731. package/src/plugins/impl/plotly/PlotlyPlugin.tsx +2 -5
  732. package/src/plugins/impl/plotly/selection.ts +25 -19
  733. package/src/plugins/impl/vega/VegaPlugin.tsx +2 -3
  734. package/src/plugins/impl/vega/make-selectable.ts +4 -7
  735. package/src/plugins/layout/DownloadPlugin.tsx +7 -11
  736. package/src/plugins/layout/RoutesPlugin.tsx +1 -1
  737. package/src/plugins/layout/__test__/DownloadPlugin.test.tsx +205 -0
  738. package/src/stories/__tests__/editor-story.test.tsx +33 -0
  739. package/src/stories/button.stories.tsx +1 -0
  740. package/src/stories/cell-status.stories.tsx +29 -24
  741. package/src/stories/cell.stories.tsx +46 -46
  742. package/src/stories/data-table.stories.tsx +31 -0
  743. package/src/stories/editor.stories.tsx +24 -17
  744. package/src/stories/form-wrapper.stories.tsx +2 -2
  745. package/src/stories/layout/vertical/one-column.stories.tsx +28 -42
  746. package/src/stories/runtime-story.tsx +23 -17
  747. package/src/stories/select.stories.tsx +2 -2
  748. package/src/stories/switchable-multi-select.stories.tsx +1 -1
  749. package/src/theme/__tests__/islands-theme.test.tsx +276 -0
  750. package/src/theme/namespace.tsx +4 -3
  751. package/src/theme/useTheme.ts +71 -41
  752. package/src/utils/__tests__/arrays.test.ts +35 -0
  753. package/src/utils/__tests__/batch-requests.test.ts +3 -3
  754. package/src/utils/__tests__/{createReducer.test.ts → createReducer.test.tsx} +127 -1
  755. package/src/utils/__tests__/download.test.tsx +183 -23
  756. package/src/utils/__tests__/numbers.test.ts +105 -1
  757. package/src/utils/__tests__/objects.test.ts +21 -0
  758. package/src/utils/arrays.ts +11 -2
  759. package/src/utils/blob.ts +1 -1
  760. package/src/utils/createReducer.ts +115 -79
  761. package/src/utils/download.ts +67 -6
  762. package/src/utils/maps.ts +0 -3
  763. package/src/utils/numbers.ts +79 -18
  764. package/src/utils/objects.ts +4 -9
  765. package/src/utils/time.ts +10 -0
  766. package/src/utils/tracer.ts +9 -5
  767. package/src/utils/zod-utils.ts +1 -3
  768. package/dist/_baseMerge-BiGRwuqv.js +0 -165
  769. package/dist/any-language-editor-D4q-IFcM.js +0 -76
  770. package/dist/assets/__vite-browser-external-BXYkiFsk.js +0 -1
  771. package/dist/assets/worker-HPY9fbhz.js +0 -114
  772. package/dist/feedback-button-g9eH5l7Q.js +0 -379
  773. package/dist/hasIn-o2Us0uVx.js +0 -132
  774. package/dist/html-to-image-B5H-xKRH.js +0 -3251
  775. package/dist/isArrayLikeObject-BZl-15ir.js +0 -450
  776. package/dist/isEmpty-BSZ1LaUo.js +0 -35
  777. package/dist/isSymbol-kQcfYbNX.js +0 -8
  778. package/dist/label-DoysmLMW.js +0 -281
  779. package/dist/pick-CJeoxYOS.js +0 -51
  780. package/dist/select-DemoKT4G.js +0 -1344
  781. package/dist/toDate-BvdkR0aH.js +0 -1185
  782. package/dist/toast-PkdZraM8.js +0 -1830
  783. package/dist/use-restore-focus-CKjfFjLM.js +0 -6369
  784. package/dist/useLifecycle-Dfq8UD-b.js +0 -169
  785. package/dist/useTheme-Bu5bBgrh.js +0 -904
  786. package/src/components/chat/reasoning-accordion.tsx +0 -61
  787. package/src/components/chat/tool-call/tool-error-card.tsx +0 -81
  788. package/src/components/chat/tool-call/tool-history-row.tsx +0 -174
  789. package/src/components/editor/ai/__tests__/staged-cell-submission.test.ts +0 -106
  790. package/src/components/editor/ai/add-cell-with-ai.tsx +0 -506
  791. package/src/components/editor/ai/staged-cell-submission.ts +0 -46
  792. package/src/components/editor/cell/__tests__/StagedAICell.test.tsx +0 -64
  793. package/src/components/editor/cell/code/__tests__/rtc-editor-config.test.ts +0 -50
  794. package/src/components/editor/chrome/wrapper/footer-items/rtc-status.tsx +0 -65
  795. package/src/components/editor/navigation/__tests__/state.test.ts +0 -81
  796. package/src/components/editor/navigation/state.ts +0 -65
  797. package/src/components/icons/loading-ellipsis.tsx +0 -46
  798. package/src/core/ai/tools/__tests__/run-cells-tool.test.ts +0 -656
  799. package/src/core/ai/tools/run-cells-tool.ts +0 -303
  800. package/src/core/attachments/state.ts +0 -18
  801. package/src/core/cells/__tests__/apply-transaction.test.ts +0 -991
  802. package/src/core/cells/__tests__/document-changes.test.ts +0 -1140
  803. package/src/core/cells/__tests__/document-roundtrip.test.ts +0 -392
  804. package/src/core/cells/document-changes.ts +0 -944
  805. package/src/core/codemirror/rtc/extension.ts +0 -453
  806. package/src/core/codemirror/rtc/loro/__tests__/sync.test.ts +0 -52
  807. package/src/core/codemirror/rtc/loro/awareness.ts +0 -531
  808. package/src/core/codemirror/rtc/loro/colors.ts +0 -38
  809. package/src/core/codemirror/rtc/loro/sync.ts +0 -168
  810. package/src/core/host/AGENTS.md +0 -11
  811. package/src/core/host/__tests__/events.test.ts +0 -46
  812. package/src/core/host/__tests__/fixtures.ts +0 -39
  813. package/src/core/host/__tests__/notebook-client.test.ts +0 -433
  814. package/src/core/host/__tests__/protocol.test.ts +0 -214
  815. package/src/core/host/__tests__/stream.test.ts +0 -209
  816. package/src/core/host/connection.ts +0 -81
  817. package/src/core/host/events.ts +0 -61
  818. package/src/core/host/notebook-client.ts +0 -290
  819. package/src/core/host/protocol.ts +0 -1945
  820. package/src/core/host/resources/resource.ts +0 -47
  821. package/src/core/host/stream.ts +0 -211
  822. package/src/core/rtc/state.ts +0 -34
  823. package/src/core/state/__tests__/jotai.test.ts +0 -25
  824. package/src/core/wasm/worker/message-buffer.ts +0 -33
  825. package/src/core/websocket/__tests__/createConnectionTransport.test.ts +0 -49
  826. package/src/core/websocket/__tests__/useNotebookClient.hook.test.tsx +0 -170
  827. package/src/core/websocket/state.ts +0 -7
  828. package/src/core/websocket/transports/__tests__/basic.test.ts +0 -275
  829. package/src/core/websocket/transports/__tests__/sse.test.ts +0 -335
  830. package/src/core/websocket/transports/__tests__/transport.test.ts +0 -308
  831. package/src/core/websocket/transports/__tests__/ws.test.ts +0 -125
  832. package/src/core/websocket/transports/basic.ts +0 -102
  833. package/src/core/websocket/transports/create.ts +0 -31
  834. package/src/core/websocket/transports/sse.ts +0 -308
  835. package/src/core/websocket/transports/transport.ts +0 -50
  836. package/src/core/websocket/transports/ws.ts +0 -98
  837. package/src/core/websocket/useNotebookClient.ts +0 -88
  838. package/src/utils/__tests__/id-tree.test.ts +0 -2145
  839. package/src/utils/id-tree.tsx +0 -1134
  840. /package/src/{core/attachments/toast.ts → components/editor/agent-toast.ts} +0 -0
  841. /package/src/core/{websocket → session}/connection-utils.ts +0 -0
@@ -1,1134 +0,0 @@
1
- /* Copyright 2026 Marimo. All rights reserved. */
2
-
3
- import { Memoize } from "typescript-memoize";
4
- import { reorderColumnSizes } from "@/components/editor/columns/storage";
5
- import { SETUP_CELL_ID } from "@/core/cells/ids";
6
- import { arrayDelete, arrayInsert, arrayInsertMany, arrayMove } from "./arrays";
7
- import { Logger } from "./Logger";
8
-
9
- /**
10
- * Branded number to help with type safety
11
- */
12
- export type CellColumnIndex = number & { __brand: "CellColumnIndex" };
13
-
14
- /**
15
- * Branded string to help with type safety
16
- */
17
- export type CellColumnId = string & { __brand: "CellColumnId" };
18
- /**
19
- * Weakly-branded number, since making `__brand` required causes type errors
20
- */
21
- export type CellIndex = number & { __brand?: "CellIndex" };
22
-
23
- /**
24
- * Tree data structure for handling ids with nested children
25
- */
26
- export class TreeNode<T> {
27
- public value: T;
28
- public isCollapsed: boolean;
29
- public children: TreeNode<T>[];
30
-
31
- public constructor(value: T, isCollapsed: boolean, children: TreeNode<T>[]) {
32
- this.value = value;
33
- this.isCollapsed = isCollapsed;
34
- this.children = children;
35
- }
36
-
37
- /**
38
- * Recursively count the number of nodes in the tree
39
- */
40
- @Memoize()
41
- public geDescendantCount(): number {
42
- let count = 0;
43
- const stack = [...this.children];
44
-
45
- while (stack.length > 0) {
46
- // oxlint-disable-next-line typescript/no-non-null-assertion
47
- const node = stack.pop()!;
48
- count++;
49
-
50
- // Add children to stack
51
- for (let i = node.children.length - 1; i >= 0; i--) {
52
- stack.push(node.children[i]);
53
- }
54
- }
55
-
56
- return count;
57
- }
58
-
59
- @Memoize()
60
- public getDescendants(): T[] {
61
- const result: T[] = [];
62
- const stack = [...this.children];
63
-
64
- while (stack.length > 0) {
65
- // oxlint-disable-next-line typescript/no-non-null-assertion
66
- const node = stack.pop()!;
67
- result.push(node.value);
68
-
69
- // Add children to stack in reverse order to maintain correct traversal order
70
- for (let i = node.children.length - 1; i >= 0; i--) {
71
- stack.push(node.children[i]);
72
- }
73
- }
74
-
75
- return result;
76
- }
77
-
78
- @Memoize()
79
- public get inOrderIds(): T[] {
80
- const result: T[] = [];
81
-
82
- // Use depth-first traversal to preserve logical document order
83
- const traverse = (node: TreeNode<T>) => {
84
- result.push(node.value);
85
- for (const child of node.children) {
86
- traverse(child);
87
- }
88
- };
89
-
90
- for (const child of this.children) {
91
- traverse(child);
92
- }
93
-
94
- return result;
95
- }
96
-
97
- public toString(): string {
98
- if (this.isCollapsed) {
99
- return `${this.value} (collapsed)`;
100
- }
101
- return String(this.value);
102
- }
103
-
104
- public equals(other: TreeNode<T>): boolean {
105
- return this.value === other.value;
106
- }
107
- }
108
-
109
- let uniqueId = 0;
110
-
111
- export class CollapsibleTree<T> {
112
- public readonly nodes: TreeNode<T>[];
113
- public readonly id: CellColumnId;
114
-
115
- private constructor(nodes: TreeNode<T>[], id: CellColumnId) {
116
- this.nodes = nodes;
117
- this.id = id;
118
- }
119
-
120
- public static from<T>(ids: T[]): CollapsibleTree<T> {
121
- const id = `tree_${uniqueId++}` as CellColumnId;
122
- return new CollapsibleTree(
123
- ids.map((id) => new TreeNode(id, false, [])),
124
- id,
125
- );
126
- }
127
-
128
- /**
129
- * Create a new tree from ids, preserving structure from previous tree if possible
130
- */
131
- public static fromWithPreviousShape<T>(
132
- ids: T[],
133
- previousTree?: CollapsibleTree<T>,
134
- ): CollapsibleTree<T> {
135
- if (!previousTree) {
136
- return CollapsibleTree.from(ids);
137
- }
138
-
139
- // Reuse the previous tree's id if possible
140
- const id = previousTree.id;
141
-
142
- // Create new tree with nothing collapsed
143
- let newTree = new CollapsibleTree(
144
- ids.map((id) => new TreeNode(id, false, [])),
145
- id,
146
- );
147
-
148
- // Collapse nodes that were collapsed in the previous tree
149
- for (const id of ids) {
150
- if (previousTree.isCollapsed(id)) {
151
- const children = previousTree._nodeMap.get(id)?.children ?? [];
152
- // Find the first child that is also in the new tree, going backwards
153
- for (let i = children.length - 1; i >= 0; i--) {
154
- const child = children[i];
155
- if (newTree._nodeMap.has(child.value)) {
156
- newTree = newTree.collapse(id, child.value);
157
- break;
158
- }
159
- }
160
- }
161
- }
162
-
163
- return newTree;
164
- }
165
-
166
- public withNodes(nodes: TreeNode<T>[]): CollapsibleTree<T> {
167
- return new CollapsibleTree(nodes, this.id);
168
- }
169
-
170
- @Memoize()
171
- public get topLevelIds(): T[] {
172
- return this.nodes.map((n) => n.value);
173
- }
174
-
175
- @Memoize()
176
- public get inOrderIds(): T[] {
177
- return this.nodes.flatMap((n) => [n.value, ...n.inOrderIds]);
178
- }
179
-
180
- @Memoize()
181
- public get idSet(): Set<T> {
182
- return new Set(this.inOrderIds);
183
- }
184
-
185
- public get length(): number {
186
- return this.nodes.length;
187
- }
188
-
189
- @Memoize()
190
- public get _nodeMap(): Map<T, TreeNode<T>> {
191
- const result = new Map<T, TreeNode<T>>();
192
- for (const node of this.nodes) {
193
- result.set(node.value, node);
194
- }
195
- return result;
196
- }
197
-
198
- /**
199
- * Get the descendants of the given node
200
- *
201
- * Only works for the top-level nodes
202
- */
203
- public getDescendants(id: T): T[] {
204
- const node = this._nodeMap.get(id);
205
- if (!node) {
206
- Logger.warn(
207
- `Node ${id} not found in tree. Valid ids: ${this.topLevelIds}`,
208
- );
209
- return [];
210
- }
211
- return node.getDescendants();
212
- }
213
-
214
- /**
215
- * Check if the given node is collapsed
216
- *
217
- * Only works for the top-level nodes
218
- */
219
- public isCollapsed(id: T): boolean {
220
- const node = this._nodeMap.get(id);
221
- if (!node) {
222
- Logger.warn(
223
- `Node ${id} not found in tree. Valid ids: ${this.topLevelIds}`,
224
- );
225
- return false;
226
- }
227
- return node.isCollapsed;
228
- }
229
-
230
- /**
231
- * Get the index of the given node, or throw
232
- */
233
- public indexOfOrThrow(id: T): CellIndex {
234
- const index = this.nodes.findIndex((n) => n.value === id);
235
- if (index === -1) {
236
- throw new Error(
237
- `Node ${id} not found in tree. Valid ids: ${this.topLevelIds}`,
238
- );
239
- }
240
- return index as CellIndex;
241
- }
242
-
243
- /**
244
- * Get the top level nodes in the given range.
245
- * This does not include descendants.
246
- */
247
- public slice(start: number, end: number): T[] {
248
- return this.nodes.slice(start, end).map((n) => n.value);
249
- }
250
-
251
- /**
252
- * Move the given node to the front
253
- */
254
- public moveToFront(id: T): CollapsibleTree<T> {
255
- const index = this.indexOfOrThrow(id);
256
- return this.withNodes(arrayMove(this.nodes, index, 0));
257
- }
258
-
259
- /**
260
- * Move the given node to the back
261
- */
262
- public moveToBack(id: T): CollapsibleTree<T> {
263
- const index = this.indexOfOrThrow(id);
264
- return this.withNodes(arrayMove(this.nodes, index, this.nodes.length - 1));
265
- }
266
-
267
- /**
268
- * Collapse everything past the given node @param id
269
- * until @param until or the end of the tree
270
- */
271
- public collapse(id: T, until: T | undefined): CollapsibleTree<T> {
272
- const nodeIndex = this.nodes.findIndex((n) => n.value === id);
273
- if (nodeIndex === -1) {
274
- throw new Error(
275
- `Node ${id} not found in tree. Valid ids: ${this.topLevelIds}`,
276
- );
277
- }
278
-
279
- const untilIndex =
280
- until === undefined
281
- ? this.nodes.length
282
- : this.nodes.findIndex((n) => n.value === until);
283
-
284
- if (untilIndex === -1) {
285
- throw new Error(`Node ${until} not found in tree`);
286
- }
287
- if (untilIndex < nodeIndex) {
288
- throw new Error(`Node ${until} is before node ${id}`);
289
- }
290
-
291
- const nodes = [...this.nodes];
292
- const node = nodes[nodeIndex];
293
- if (node.isCollapsed) {
294
- throw new Error(`Node ${id} is already collapsed`);
295
- }
296
-
297
- // Fold the next nodes into the current node
298
- const children = nodes.splice(nodeIndex + 1, untilIndex - nodeIndex);
299
- nodes[nodeIndex] = new TreeNode(node.value, true, children);
300
-
301
- return this.withNodes(nodes);
302
- }
303
-
304
- /**
305
- * Collapse all nodes in the tree, including nested ones
306
- *
307
- * Only works for the top-level nodes
308
- * Does not collapse the children of already collapsed nodes
309
- */
310
- public collapseAll(
311
- collapseRanges: ({ id: T; until: T | undefined } | null)[],
312
- ): CollapsibleTree<T> {
313
- const nodes = [...this.nodes];
314
- if (collapseRanges.length === 0) {
315
- throw new Error("No collapse ranges provided");
316
- }
317
-
318
- if (collapseRanges.length !== nodes.length) {
319
- throw new Error(
320
- `Collapse ranges length ${collapseRanges.length} does not match tree length ${nodes.length}`,
321
- );
322
- }
323
-
324
- // Start from the end of the list and collapse nodes children first
325
- let nodeIndex = nodes.length - 1;
326
- while (nodeIndex >= 0) {
327
- const node = nodes[nodeIndex];
328
- const range = collapseRanges[nodeIndex];
329
-
330
- if (!node.isCollapsed && range) {
331
- const { id, until } = range;
332
- if (id !== node.value) {
333
- throw new Error(
334
- `Node ${node.value} does not match collapse range id ${id}`,
335
- );
336
- }
337
- const untilIndex =
338
- until === undefined
339
- ? nodes.length
340
- : nodes.findIndex((n) => n.value === until);
341
-
342
- if (untilIndex === -1) {
343
- throw new Error(`Node ${until} not found in tree`);
344
- }
345
- if (untilIndex < nodeIndex) {
346
- throw new Error(`Node ${until} is before node ${id}`);
347
- }
348
-
349
- // Fold the next nodes into the current node
350
- const children = nodes.splice(nodeIndex + 1, untilIndex - nodeIndex);
351
- nodes[nodeIndex] = new TreeNode(node.value, true, children);
352
- nodeIndex--;
353
- } else {
354
- // Move to the next node
355
- nodeIndex--;
356
- }
357
- }
358
-
359
- return this.withNodes(nodes);
360
- }
361
-
362
- /**
363
- * Expand a node and all of its children.
364
- * If the node is already expanded, returns the same tree (no-op).
365
- */
366
- public expand(id: T): CollapsibleTree<T> {
367
- const nodeIndex = this.nodes.findIndex((n) => n.value === id);
368
- if (nodeIndex === -1) {
369
- throw new Error(
370
- `Node ${id} not found in tree. Valid ids: ${this.topLevelIds}`,
371
- );
372
- }
373
-
374
- let nodes = [...this.nodes];
375
- const node = nodes[nodeIndex];
376
- if (!node.isCollapsed) {
377
- // Already expanded, no-op
378
- return this;
379
- }
380
-
381
- nodes[nodeIndex] = new TreeNode(node.value, false, []);
382
- nodes = arrayInsertMany(nodes, nodeIndex + 1, node.children);
383
-
384
- return this.withNodes(nodes);
385
- }
386
-
387
- /**
388
- * Expand all collapsed nodes in the tree, including nested ones
389
- */
390
- public expandAll(): CollapsibleTree<T> {
391
- let nodes = [...this.nodes];
392
- let nodeIndex = 0;
393
-
394
- while (nodeIndex < nodes.length) {
395
- const node = nodes[nodeIndex];
396
- if (node.isCollapsed) {
397
- // Replace the collapsed node with an expanded one
398
- nodes[nodeIndex] = new TreeNode(node.value, false, []);
399
- // Add the children of the collapsed node to the list
400
- nodes = arrayInsertMany(nodes, nodeIndex + 1, node.children);
401
- nodeIndex++;
402
- } else {
403
- // Move to the next node
404
- nodeIndex++;
405
- }
406
- }
407
-
408
- return this.withNodes(nodes);
409
- }
410
-
411
- /**
412
- * Move a node from one index to another
413
- */
414
- public move(fromIdx: number, toIdx: number): CollapsibleTree<T> {
415
- return this.withNodes(arrayMove(this.nodes, fromIdx, toIdx));
416
- }
417
-
418
- /**
419
- * Get the node at the given index
420
- */
421
- public at(index: number): T | undefined {
422
- return this.nodes.at(index)?.value;
423
- }
424
-
425
- /**
426
- * Get the node at the given index
427
- */
428
- public atOrThrow(index: number): T {
429
- const node = this.nodes.at(index);
430
- if (node === undefined) {
431
- throw new Error(`Node at index ${index} not found in tree`);
432
- }
433
- return node.value;
434
- }
435
-
436
- /**
437
- * Get the first node, or throw
438
- */
439
- public first(): T {
440
- return this.atOrThrow(0);
441
- }
442
-
443
- /**
444
- * Get the last node, or throw
445
- */
446
- public last(): T {
447
- return this.atOrThrow(this.nodes.length - 1);
448
- }
449
-
450
- /**
451
- * Get the next node after the given node, does not wrap.
452
- */
453
- public after(id: T): T | undefined {
454
- const index = this.indexOfOrThrow(id);
455
- if (index === this.nodes.length - 1) {
456
- return undefined;
457
- }
458
- return this.at(index + 1);
459
- }
460
-
461
- /**
462
- * Get the previous node before the given node, does not wrap.
463
- */
464
- public before(id: T): T | undefined {
465
- const index = this.indexOfOrThrow(id);
466
- if (index === 0) {
467
- return undefined;
468
- }
469
- return this.at(index - 1);
470
- }
471
-
472
- /**
473
- * Insert a node at the given index
474
- */
475
- public insert(id: T, index: number): CollapsibleTree<T> {
476
- return this.withNodes(
477
- arrayInsert(this.nodes, index, new TreeNode(id, false, [])),
478
- );
479
- }
480
-
481
- /**
482
- * Insert a node at the end
483
- */
484
- public insertAtEnd(id: T): CollapsibleTree<T> {
485
- return this.insert(id, this.nodes.length);
486
- }
487
-
488
- /**
489
- * Insert a node at the start
490
- */
491
- public insertAtStart(id: T): CollapsibleTree<T> {
492
- return this.insert(id, 0);
493
- }
494
-
495
- /**
496
- * Delete a node, expand if it was collapsed
497
- */
498
- public deleteAtIndex(idx: number): CollapsibleTree<T> {
499
- const id = this.atOrThrow(idx);
500
- // Expand the node first (if collapsed) to bring children back to top level
501
- const tree = this.expand(id);
502
- return tree.withNodes(arrayDelete(tree.nodes, idx));
503
- }
504
-
505
- public delete(id: T): CollapsibleTree<T> {
506
- const index = this.indexOfOrThrow(id);
507
- return this.deleteAtIndex(index);
508
- }
509
-
510
- /**
511
- * Get the number of nodes in the tree, not-including the given node
512
- */
513
- public getCount(id: T): number {
514
- return this._nodeMap.get(id)?.geDescendantCount() ?? 0;
515
- }
516
-
517
- /**
518
- * Find and expand the node and all of its children
519
- */
520
- public findAndExpandDeep(id: T): CollapsibleTree<T> {
521
- const found = this.find(id);
522
- if (found.length === 0) {
523
- return this;
524
- }
525
- return found.reduce<CollapsibleTree<T>>(
526
- (acc, node) => acc.expand(node),
527
- this,
528
- );
529
- }
530
-
531
- /**
532
- * Find a node, returning the path to it
533
- * With the last element being the node itself
534
- */
535
- public find(id: T): T[] {
536
- // We need to recursively find the node
537
- function findNode(nodes: TreeNode<T>[], path: T[]): T[] {
538
- for (const node of nodes) {
539
- if (node.value === id) {
540
- return [...path, id];
541
- }
542
- const result = findNode(node.children, [...path, node.value]);
543
- if (result.length > 0) {
544
- return result;
545
- }
546
- }
547
- return [];
548
- }
549
-
550
- return findNode(this.nodes, []);
551
- }
552
-
553
- /**
554
- * Split the tree into two trees
555
- * @param id the id of the node to split at
556
- * @returns a tuple of the left and right trees
557
- */
558
- public split(id: T): [CollapsibleTree<T>, CollapsibleTree<T> | undefined] {
559
- const index = this.nodes.findIndex((n) => n.value === id);
560
- if (index === -1) {
561
- throw new Error(`Node ${id} not found in tree`);
562
- }
563
- const leftNodes = this.nodes.slice(0, index);
564
- const rightNodes = this.nodes.slice(index);
565
- if (leftNodes.length === 0) {
566
- return [CollapsibleTree.from<T>([]), this.withNodes(rightNodes)];
567
- }
568
- const left = this.withNodes(leftNodes);
569
- const right = CollapsibleTree.from<T>([]).withNodes(rightNodes);
570
- return [left, right];
571
- }
572
-
573
- public equals(other: CollapsibleTree<T>): boolean {
574
- return (
575
- this.nodes.length === other.nodes.length &&
576
- this.nodes.every((n, i) => n.value === other.nodes[i].value)
577
- );
578
- }
579
-
580
- public toString(): string {
581
- let depth = 0;
582
- let result = "";
583
- const asString = (nodes: TreeNode<T>[]) => {
584
- for (const node of nodes) {
585
- result += `${" ".repeat(depth * 2)}${node.toString()}\n`;
586
- depth += 1;
587
- asString(node.children);
588
- depth -= 1;
589
- }
590
- };
591
- asString(this.nodes);
592
- return result;
593
- }
594
- }
595
-
596
- export class MultiColumn<T> {
597
- private readonly columns: readonly CollapsibleTree<T>[];
598
-
599
- public constructor(columns: readonly CollapsibleTree<T>[]) {
600
- this.columns = columns;
601
-
602
- // Ensure there is always at least one column
603
- if (columns.length === 0) {
604
- this.columns = [CollapsibleTree.from([])];
605
- }
606
- }
607
-
608
- public static from<T>(idsList: T[][]): MultiColumn<T> {
609
- return new MultiColumn(idsList.map((ids) => CollapsibleTree.from(ids)));
610
- }
611
-
612
- /**
613
- * Create a new MultiColumn from idsList,
614
- * attempting to preserve structure from previous MultiColumn if possible.
615
- */
616
- public static fromWithPreviousShape<T>(
617
- idsList: T[],
618
- previousShape: MultiColumn<T>,
619
- ): MultiColumn<T> {
620
- if (!previousShape) {
621
- return MultiColumn.from([idsList]);
622
- }
623
-
624
- // Get previous columns for reference
625
- const previousColumns = previousShape.getColumns();
626
-
627
- // Split the ids into their respective columns
628
- const splitIds: T[][] = Array.from(
629
- { length: previousColumns.length },
630
- () => [],
631
- );
632
- let lastUsedColumnIdx = 0;
633
- for (const id of idsList) {
634
- const column = previousColumns.findIndex((c) => c.idSet.has(id));
635
- if (column === -1) {
636
- // No column found, use the last used column
637
- splitIds[lastUsedColumnIdx].push(id);
638
- } else {
639
- lastUsedColumnIdx = column;
640
- splitIds[column].push(id);
641
- }
642
- }
643
-
644
- const newColumns = splitIds.map((ids, idx) =>
645
- CollapsibleTree.fromWithPreviousShape(ids, previousColumns[idx]),
646
- );
647
-
648
- return new MultiColumn(newColumns);
649
- }
650
-
651
- @Memoize()
652
- public isEmpty(): boolean {
653
- if (this.columns.length === 0) {
654
- return true;
655
- }
656
- return this.columns.every((c) => c.nodes.length === 0);
657
- }
658
-
659
- public static fromIdsAndColumns<T>(
660
- idAndColumns: [T, number | undefined | null][],
661
- ): MultiColumn<T> {
662
- // If column is undefined, use the previous column
663
- // Ensure there is always at least one column
664
- const numColumns = Math.max(
665
- 1,
666
- ...idAndColumns.map(([, column]) => (column ?? 0) + 1),
667
- );
668
- const idsList: T[][] = Array.from({ length: numColumns }, () => []);
669
-
670
- let prevColumn = 0;
671
- for (const [id, column] of idAndColumns) {
672
- // Avoid negative column indices
673
- if (column === undefined || column === null || column < 0) {
674
- idsList[prevColumn].push(id);
675
- } else {
676
- idsList[column].push(id);
677
- prevColumn = column;
678
- }
679
- }
680
-
681
- return MultiColumn.from(idsList);
682
- }
683
-
684
- @Memoize()
685
- public get topLevelIds(): T[][] {
686
- return this.columns.map((c) => c.topLevelIds);
687
- }
688
-
689
- public get iterateTopLevelIds(): Iterable<T> {
690
- const columns = this.columns;
691
-
692
- function* iter() {
693
- for (const column of columns) {
694
- for (const id of column.topLevelIds) {
695
- yield id;
696
- }
697
- }
698
- }
699
-
700
- return iter();
701
- }
702
-
703
- @Memoize()
704
- public get inOrderIds(): T[] {
705
- return this.columns.flatMap((c) => c.inOrderIds);
706
- }
707
-
708
- public get colLength(): number {
709
- return this.columns.length;
710
- }
711
-
712
- @Memoize()
713
- public get idLength(): number {
714
- return this.columns.reduce((acc, c) => acc + c.nodes.length, 0);
715
- }
716
-
717
- @Memoize()
718
- public get _columnMap(): Map<CellColumnId, CollapsibleTree<T>> {
719
- return new Map(this.columns.map((c) => [c.id, c]));
720
- }
721
-
722
- public at(idx: number): CollapsibleTree<T> | undefined {
723
- return this.columns[idx];
724
- }
725
-
726
- public get(columnId: CellColumnId): CollapsibleTree<T> | undefined {
727
- return this._columnMap.get(columnId);
728
- }
729
-
730
- public atOrThrow(idx: number): CollapsibleTree<T> {
731
- const column = this.columns[idx];
732
- if (!column) {
733
- throw new Error(`Column ${idx} not found`);
734
- }
735
- return column;
736
- }
737
-
738
- public hasOnlyOneColumn(): boolean {
739
- return this.columns.length === 1;
740
- }
741
-
742
- public getColumns(): readonly CollapsibleTree<T>[] {
743
- return this.columns;
744
- }
745
-
746
- public hasOnlyOneId(): boolean {
747
- return this.idLength === 1;
748
- }
749
-
750
- @Memoize()
751
- public getColumnIds(): CellColumnId[] {
752
- return this.columns.map((c) => c.id);
753
- }
754
-
755
- public indexOf(column: CollapsibleTree<T>): number {
756
- return this.columns.indexOf(column);
757
- }
758
-
759
- public addColumn(
760
- columnId: CellColumnId,
761
- initialIds: T[] = [],
762
- ): MultiColumn<T> {
763
- return new MultiColumn(
764
- this.columns.flatMap((c) => {
765
- if (c.id === columnId) {
766
- return [c, CollapsibleTree.from(initialIds)];
767
- }
768
- return [c];
769
- }),
770
- );
771
- }
772
-
773
- public insertBreakpoint(cellId: T): MultiColumn<T> {
774
- const column = this.findWithId(cellId);
775
- const [left, right] = column.split(cellId);
776
- const newColumns = this.columns.flatMap((c) => {
777
- if (c === column) {
778
- return [left, right].filter(Boolean);
779
- }
780
- return [c];
781
- });
782
- return new MultiColumn(newColumns);
783
- }
784
-
785
- public delete(columnId: CellColumnId): MultiColumn<T> {
786
- // Move cells to preceding column
787
- // If its the first column, move the cells to the next column
788
-
789
- // Noop if there is only one column
790
- if (this.columns.length <= 1) {
791
- return this;
792
- }
793
-
794
- const columnIndex = this.indexOfOrThrow(columnId);
795
- const targetColumnIndex = columnIndex === 0 ? 1 : columnIndex - 1;
796
-
797
- const columns = [...this.columns];
798
- const column = columns[columnIndex];
799
- columns[targetColumnIndex] = column.withNodes([
800
- ...columns[targetColumnIndex].nodes,
801
- ...column.nodes,
802
- ]);
803
- columns.splice(columnIndex, 1);
804
- return new MultiColumn(columns);
805
- }
806
-
807
- public mergeAllColumns(): MultiColumn<T> {
808
- if (this.columns.length <= 1) {
809
- return this;
810
- }
811
-
812
- const nodes = this.columns.flatMap((c) => c.nodes);
813
- const firstColumn = this.columns[0];
814
- return new MultiColumn([firstColumn.withNodes(nodes)]);
815
- }
816
-
817
- public moveWithinColumn(
818
- col: CellColumnId,
819
- fromIdx: CellIndex,
820
- toIdx: CellIndex,
821
- ): MultiColumn<T> {
822
- return this.transform(col, (c) => {
823
- return c.move(fromIdx, toIdx);
824
- });
825
- }
826
-
827
- public moveAcrossColumns(
828
- fromCol: CellColumnId,
829
- fromId: T,
830
- toCol: CellColumnId,
831
- toId: T | undefined,
832
- ): MultiColumn<T> {
833
- // Find indices once to avoid repeated lookups
834
- const fromColumnIndex = this.indexOfOrThrow(fromCol);
835
- const toColumnIndex = this.indexOfOrThrow(toCol);
836
-
837
- // Only copy the columns we're modifying
838
- const newColumns = [...this.columns];
839
- const fromColumn = newColumns[fromColumnIndex];
840
-
841
- // Find the node once
842
- const nodeIndex = fromColumn.nodes.findIndex((n) => n.value === fromId);
843
- if (nodeIndex === -1) {
844
- throw new Error(`Node ${fromId} not found in column ${fromCol}`);
845
- }
846
-
847
- const node = fromColumn.nodes[nodeIndex];
848
-
849
- // Create new columns with the changes
850
- newColumns[fromColumnIndex] = fromColumn.withNodes(
851
- fromColumn.nodes.filter((_, i) => i !== nodeIndex),
852
- );
853
-
854
- const toColumn = newColumns[toColumnIndex];
855
- if (toId) {
856
- const toIndex = toColumn.indexOfOrThrow(toId);
857
- newColumns[toColumnIndex] = toColumn.withNodes(
858
- arrayInsert(toColumn.nodes, toIndex, node),
859
- );
860
- } else {
861
- newColumns[toColumnIndex] = toColumn.withNodes([node, ...toColumn.nodes]);
862
- }
863
-
864
- return new MultiColumn(newColumns);
865
- }
866
-
867
- /**
868
- * Move multiple cells to be immediately before (or after) a target cell.
869
- * Cells are inserted in their original order.
870
- *
871
- * @throws Error if any cellId is not found in any column.
872
- * If targetId is among the moved cells, cells are inserted at the end of the target column.
873
- */
874
- public moveCellsRelativeTo(
875
- cellIds: T[],
876
- targetId: T,
877
- position: "before" | "after",
878
- ): MultiColumn<T> {
879
- if (cellIds.length === 0) {
880
- return this;
881
- }
882
-
883
- const cellIdSet = new Set(cellIds);
884
- const targetColumn = this.findWithId(targetId);
885
- const targetColIndex = this.indexOfOrThrow(targetColumn.id);
886
-
887
- // Collect nodes to move
888
- const nodesToMove: TreeNode<T>[] = [];
889
- for (const id of cellIds) {
890
- const col = this.findWithId(id);
891
- const node = col.nodes.find((n) => n.value === id);
892
- if (!node) {
893
- throw new Error(`Node ${id} not found in column ${col.id}`);
894
- }
895
- nodesToMove.push(node);
896
- }
897
-
898
- // Remove moved cells from all columns
899
- const columnsWithRemovals = this.columns.map((col) =>
900
- col.withNodes(col.nodes.filter((n) => !cellIdSet.has(n.value))),
901
- );
902
-
903
- // Find target index in the cleaned column
904
- const cleanedTargetCol = columnsWithRemovals[targetColIndex];
905
- let insertIndex = cleanedTargetCol.nodes.findIndex(
906
- (n) => n.value === targetId,
907
- );
908
-
909
- // If target was one of the moved cells, insert at end
910
- if (insertIndex === -1) {
911
- insertIndex = cleanedTargetCol.nodes.length;
912
- } else if (position === "after") {
913
- insertIndex += 1;
914
- }
915
-
916
- // Insert all moved nodes at target position
917
- const newTargetNodes = arrayInsertMany(
918
- cleanedTargetCol.nodes,
919
- insertIndex,
920
- nodesToMove,
921
- );
922
- columnsWithRemovals[targetColIndex] =
923
- cleanedTargetCol.withNodes(newTargetNodes);
924
-
925
- return new MultiColumn(columnsWithRemovals);
926
- }
927
-
928
- public indexOfOrThrow(id: CellColumnId): number {
929
- const index = this.columns.findIndex((c) => c.id === id);
930
- if (index === -1) {
931
- throw new Error(
932
- `Column ${id} not found. Possible values: ${this.getColumnIds().join(", ")}`,
933
- );
934
- }
935
- return index;
936
- }
937
-
938
- public moveColumn(
939
- fromCol: CellColumnId,
940
- toCol: CellColumnId | "_left_" | "_right_",
941
- ): MultiColumn<T> {
942
- if (fromCol === toCol) {
943
- return this;
944
- }
945
- const fromIdx = this.indexOfOrThrow(fromCol);
946
- const toIdx =
947
- toCol === "_left_"
948
- ? fromIdx - 1
949
- : toCol === "_right_"
950
- ? fromIdx + 1
951
- : this.indexOfOrThrow(toCol);
952
-
953
- reorderColumnSizes(fromIdx, toIdx);
954
- return new MultiColumn(arrayMove([...this.columns], fromIdx, toIdx));
955
- }
956
-
957
- public moveToNewColumn(cellId: T): MultiColumn<T> {
958
- const fromColumn = this.findWithId(cellId);
959
-
960
- // Create new columns array with the cell removed from its original column
961
- const columns = this.columns.map((c) => {
962
- if (c.id === fromColumn.id) {
963
- return c.delete(cellId);
964
- }
965
- return c;
966
- });
967
-
968
- // Insert into new column
969
- const newColumn = CollapsibleTree.from([cellId]);
970
- return new MultiColumn([...columns, newColumn]);
971
- }
972
-
973
- public findWithId(id: T): CollapsibleTree<T> {
974
- const found = this.columns.find((c) => {
975
- return c.inOrderIds.includes(id);
976
- });
977
- if (!found) {
978
- Logger.log(
979
- `Possible values: ${this.columns.map((c) => c.inOrderIds).join(", ")}`,
980
- );
981
- throw new Error(`Cell ${id} not found in any column`);
982
- }
983
- return found;
984
- }
985
-
986
- /**
987
- * Transform the column containing the given cell id
988
- * @param id the id of the cell to transform
989
- * @param fn the function to transform the column
990
- * @returns new MultiColumn with the transformed column
991
- * If the column was not updated, we return the object.
992
- */
993
- public transformWithCellId(
994
- id: T,
995
- fn: (tree: CollapsibleTree<T>) => CollapsibleTree<T>,
996
- ): MultiColumn<T> {
997
- let didChange = false;
998
-
999
- const columns = this.columns.map((c) => {
1000
- if (c.inOrderIds.includes(id)) {
1001
- const newColumn = fn(c);
1002
- if (c !== newColumn) {
1003
- didChange = true;
1004
- }
1005
- return newColumn;
1006
- }
1007
- return c;
1008
- });
1009
-
1010
- // Avoid unnecessary re-renders
1011
- if (!didChange) {
1012
- return this;
1013
- }
1014
-
1015
- return new MultiColumn(columns);
1016
- }
1017
-
1018
- public setupCellExists(): boolean {
1019
- return this.columns.some((c) => c.topLevelIds.includes(SETUP_CELL_ID as T));
1020
- }
1021
-
1022
- public insertId(id: T, col: CellColumnId, index: CellIndex): MultiColumn<T> {
1023
- return this.transform(col, (c) => c.insert(id, index));
1024
- }
1025
-
1026
- public deleteById(cellId: T): MultiColumn<T> {
1027
- return this.transformWithCellId(cellId, (c) => c.delete(cellId));
1028
- }
1029
-
1030
- /**
1031
- * Remove the given cells from the tree, then re-insert each at its
1032
- * (columnId, index). Used to undo a move (e.g. cut-paste) by restoring
1033
- * cells to their previous positions. Placements are sorted by
1034
- * (columnId, index) so insert order keeps indices valid.
1035
- */
1036
- public placeCells(
1037
- placements: Array<{ id: T; columnId: CellColumnId; index: CellIndex }>,
1038
- ): MultiColumn<T> {
1039
- if (placements.length === 0) {
1040
- return this;
1041
- }
1042
- let result = this.deleteById(placements[0].id);
1043
- for (const { id } of placements.slice(1)) {
1044
- result = result.deleteById(id);
1045
- }
1046
- const sorted = [...placements].toSorted((a, b) => {
1047
- if (a.columnId !== b.columnId) {
1048
- return a.columnId.localeCompare(b.columnId);
1049
- }
1050
- return (a.index as number) - (b.index as number);
1051
- });
1052
- for (const { id, columnId, index } of sorted) {
1053
- result = result.insertId(id, columnId, index);
1054
- }
1055
- return result;
1056
- }
1057
-
1058
- public compact(): MultiColumn<T> {
1059
- // Don't compact if there's only one column
1060
- if (this.columns.length === 1) {
1061
- return this;
1062
- }
1063
- // If no need to compact, return the same tree
1064
- // to avoid unnecessary re-renders
1065
- const someEmpty = this.columns.some((c) => c.nodes.length === 0);
1066
- if (!someEmpty) {
1067
- return this;
1068
- }
1069
-
1070
- return new MultiColumn(this.columns.filter((c) => c.nodes.length > 0));
1071
- }
1072
-
1073
- /**
1074
- * Transform the column with the given column id
1075
- */
1076
- public transform(
1077
- columnId: CellColumnId,
1078
- fn: (tree: CollapsibleTree<T>) => CollapsibleTree<T>,
1079
- ): MultiColumn<T> {
1080
- // Use columnMap for faster lookup
1081
- const column = this._columnMap.get(columnId);
1082
- if (!column) {
1083
- Logger.warn(`Column ${columnId} not found`);
1084
- return this;
1085
- }
1086
-
1087
- const newColumn = fn(column);
1088
- if (column === newColumn) {
1089
- return this;
1090
- }
1091
-
1092
- const newColumns = this.columns.map((c) => {
1093
- if (c.id === columnId) {
1094
- return newColumn;
1095
- }
1096
- return c;
1097
- });
1098
-
1099
- return new MultiColumn(newColumns);
1100
- }
1101
-
1102
- /**
1103
- * Apply a transformation function to all columns
1104
- * @param fn The function to transform each column
1105
- * @returns A new MultiColumn if any changes were made, otherwise this
1106
- */
1107
- public transformAll(
1108
- fn: (tree: CollapsibleTree<T>) => CollapsibleTree<T>,
1109
- ): MultiColumn<T> {
1110
- let didChange = false;
1111
-
1112
- // Apply the transformation to all columns
1113
- const newColumns = this.columns.map((column) => {
1114
- const newColumn = fn(column);
1115
- if (!column.equals(newColumn)) {
1116
- didChange = true;
1117
- }
1118
- return newColumn;
1119
- });
1120
-
1121
- // Avoid unnecessary re-renders if nothing changed
1122
- if (!didChange) {
1123
- return this;
1124
- }
1125
-
1126
- return new MultiColumn(newColumns);
1127
- }
1128
- }
1129
-
1130
- export const visibleForTesting = {
1131
- reset: () => {
1132
- uniqueId = 0;
1133
- },
1134
- };