@marimo-team/islands 0.24.3-dev4 → 0.24.3-dev40

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 (365) hide show
  1. package/dist/{ConnectedDataExplorerComponent-C9OF_75w.js → ConnectedDataExplorerComponent-KVhjDMSe.js} +1066 -1066
  2. package/dist/ErrorBoundary-CacTZz7K.js +200 -0
  3. package/dist/{ImageComparisonComponent-2a9TMMm2.js → ImageComparisonComponent-RspMflu6.js} +3 -3
  4. package/dist/{Plot-C-9MQeGP.js → Plot-CTyjbQDT.js} +21032 -20666
  5. package/dist/_baseMerge-BiGRwuqv.js +165 -0
  6. package/dist/{abnfDiagram-N423BO3Z-CQKwzX8t.js → abnfDiagram-VCTEODGH-CpN0FRda.js} +17 -18
  7. package/dist/{any-language-editor-DmQ7dyCx.js → any-language-editor-CycIVIeP.js} +17 -17
  8. package/dist/arc-7ra3iNdN.js +81 -0
  9. package/dist/architecture-7GRP2DOG-BWLylgiA.js +3 -0
  10. package/dist/{architectureDiagram-T3A2C74G-CncrDpfa.js → architectureDiagram-5GKGNRK7-DDyj5SRB.js} +25 -25
  11. package/dist/assets/__vite-browser-external-BajL8aoc.js +1 -0
  12. package/dist/assets/worker-BemUy60C.js +109 -0
  13. package/dist/blockDiagram-I7D4REHJ-BbXEXJRu.js +1922 -0
  14. package/dist/{button-CKBkqR9J.js → button-DdXAm8iz.js} +5 -5
  15. package/dist/{c4Diagram-5PPSVZJV-DarUk3Yq.js → c4Diagram-7LVT6UL2-DcLsbQmR.js} +696 -668
  16. package/dist/{capabilities-CMBBJTcc.js → capabilities-B3ce_GNu.js} +1 -1
  17. package/dist/{html-to-image-BhNoIBSU.js → cells-BSgNCBZ8.js} +6740 -10126
  18. package/dist/{channel-CyiYUzJv.js → channel-B5XnTarr.js} +1 -1
  19. package/dist/chat-ui-CygzVJ9s.js +5786 -0
  20. package/dist/{check-CHig47QV.js → check-B7cIK6gb.js} +1 -1
  21. package/dist/{chunk-2Q5K7J3B-BMuJl2jB.js → chunk-2Q5K7J3B-BMkUkeuy.js} +2 -2
  22. package/dist/chunk-4HAMMTFA-D2qv8E0X.js +4188 -0
  23. package/dist/{chunk-5VM5RSS4-Bcq0ftdy.js → chunk-5VM5RSS4-DKsmPCL6.js} +2 -2
  24. package/dist/{chunk-NSK5VX7P-CDTMcJB0.js → chunk-75Z2AOVW-5_HsvXwr.js} +23 -23
  25. package/dist/{chunk-I66GZJ75-BqyTCAPf.js → chunk-DU6HZSFF-BUgAHvnM.js} +21 -4
  26. package/dist/{chunk-2GRJ4B5K-DgO8Qudq.js → chunk-F27PBJKO-DHmYWZXr.js} +4 -4
  27. package/dist/{chunk-KEIR6QF5-C4KX3XYh.js → chunk-FOHPRMQF-BpYR8RqP.js} +4 -4
  28. package/dist/{chunk-WRU74C26-BpWsbiDK.js → chunk-GMAD6QVW-Czynz8aA.js} +486 -258
  29. package/dist/chunk-GVQU2GXP-yQEdulRS.js +37 -0
  30. package/dist/{chunk-5RXB4S5H-tLNUVDdO.js → chunk-IMKFNOWR-BfwTebD0.js} +11 -11
  31. package/dist/{chunk-JWPE2WC7-BJwjGUnF.js → chunk-JWPE2WC7-DJvv1t8U.js} +2 -2
  32. package/dist/chunk-L3NEJ4N5-DwRWnDDK.js +245 -0
  33. package/dist/{chunk-7Z6QIM7H-Dc18hFxH.js → chunk-OSK3NFVY-BBKU_Ign.js} +222 -144
  34. package/dist/{chunk-W5SLKNZC-DLnvpEtV.js → chunk-P2QGCYS3-C0iGU29N.js} +20 -20
  35. package/dist/{chunk-KBJHAD2P-BuqWqkcW.js → chunk-POPQ4Y6H-BCbS3Ozc.js} +4 -4
  36. package/dist/{chunk-4I5QYGJK-DpytHuZQ.js → chunk-PWAF6VOD-CTFCL0jI.js} +4 -4
  37. package/dist/{chunk-JQJVKLGR-CgqCwe8E.js → chunk-SHT3W25Y-BspJEJ5H.js} +109 -63
  38. package/dist/{chunk-6Q2QTUOP-DmTcEBMn.js → chunk-SVP7TREG-DX3rFNTe.js} +5 -5
  39. package/dist/{chunk-GF5L2VYU-B0wgCQgS.js → chunk-TICWLB2K-fVF7Uujg.js} +13 -13
  40. package/dist/{chunk-XXDRQBXY-D73H4IN0.js → chunk-XXDRQBXY-Dnl5VBqG.js} +3 -3
  41. package/dist/{chunk-Y2CYZVJY-DoegUj_e.js → chunk-Y2CYZVJY-DKKS1VyV.js} +1 -1
  42. package/dist/{chunk-5FQGJX7Z-B85XCPv6.js → chunk-YOKDWASO-DrWbKnxA.js} +6925 -5460
  43. package/dist/classDiagram-ZZMXUADV-Fe-V14Sc.js +30 -0
  44. package/dist/classDiagram-v2-VYDZK3BY-xTDg7lOs.js +30 -0
  45. package/dist/code-visibility-BBNwvBcV.js +138 -0
  46. package/dist/{common-BDlauhCU.js → common-DM3lblo7.js} +8853 -12653
  47. package/dist/{compiler-runtime-CAH9RLw2.js → compiler-runtime-Cj8ZWiNS.js} +2 -2
  48. package/dist/copy-icon-CLdENY__.js +190 -0
  49. package/dist/{cose-bilkent-JH36ORCC-FUWHkXFX.js → cose-bilkent-JH36ORCC-_LyRKVqm.js} +2 -2
  50. package/dist/cubic-OaEBhQPK.js +1404 -0
  51. package/dist/cynefin-OW5HDTMX-obq-i80l.js +3 -0
  52. package/dist/{cynefinDiagram-MW4NZA55-BXSFK93J.js → cynefinDiagram-5FMLGOSQ-CFQrqzSX.js} +10 -10
  53. package/dist/{dagre-CGyalH38.js → dagre-BGSfXNEb.js} +678 -537
  54. package/dist/dagre-GXQ25YYZ-xHNAUTyG.js +425 -0
  55. package/dist/{data-grid-overlay-editor-DZM4BAaI.js → data-grid-overlay-editor-DjRvSwGI.js} +8 -9
  56. package/dist/{data-grid-types-BnZwWG8z.js → data-grid-types-BucVmDDp.js} +1 -1
  57. package/dist/{defaultLocale-DKaU_opG.js → defaultLocale-JI_lnoCe.js} +1 -1
  58. package/dist/{diagram-7IWD3JNH-BjfzDNvj.js → diagram-S7CK7UJ4-Bt1rPbAx.js} +40 -39
  59. package/dist/{diagram-UB23O5K3-DDg-7wLA.js → diagram-UQ7AKVKN-D1sq5FoZ.js} +10 -10
  60. package/dist/{diagram-B4RE2ZJO-CKccR9eE.js → diagram-VSXAHHWV-BKpmG23x.js} +10 -10
  61. package/dist/{diagram-Q27KOJAE-Zhz01ss7.js → diagram-VX7I27RA-BMwGFVD6.js} +21 -21
  62. package/dist/{diagram-LBJQPF4R-CJ-bOruh.js → diagram-Z3DM3KII--KLDR9Pl.js} +10 -10
  63. package/dist/dialog-CgNq6y9-.js +94 -0
  64. package/dist/{dist-dAqZAPcF.js → dist-1HDa70oE.js} +3 -3
  65. package/dist/{dist-Re9sOPOx.js → dist-9x1ZRe-e.js} +2 -2
  66. package/dist/{dist-DEeLqRlU.js → dist-B1vYLK28.js} +1 -1
  67. package/dist/{dist-C5Bn52W4.js → dist-B8D7McG3.js} +3 -3
  68. package/dist/{dist-_XvgytiS.js → dist-BHCHt1NI.js} +1 -1
  69. package/dist/{dist-CRjAdnqF.js → dist-BRxfaJgs.js} +3 -3
  70. package/dist/{dist-Byj2bV8o.js → dist-BVGtK7Ht.js} +1 -1
  71. package/dist/{dist-BIN5LKyE.js → dist-BaHjhkpZ.js} +2 -2
  72. package/dist/{dist-DVFbIwc5.js → dist-BtKIs9N-.js} +1 -1
  73. package/dist/{dist-BrQGcfQ9.js → dist-C2KzV-vf.js} +2 -2
  74. package/dist/{dist-DJFiGP2k.js → dist-C2wDlYdD.js} +2 -2
  75. package/dist/{dist-BVRM2y1P.js → dist-C8xMLVYi.js} +2 -2
  76. package/dist/dist-CMh_W4y4.js +538 -0
  77. package/dist/{extends-BPB6_lTK.js → dist-CO1AwwG_.js} +516 -561
  78. package/dist/{dist-BzN60Z2s.js → dist-CPGxfVGX.js} +1 -1
  79. package/dist/{dist-C7v-9nUK.js → dist-Cm3iAFMD.js} +3 -3
  80. package/dist/{dist-CGAxEiaM.js → dist-Cs2FijYk.js} +2 -2
  81. package/dist/{dist-hXd65f0_.js → dist-D1PpnuZB.js} +1 -1
  82. package/dist/{dist-VdvbxG72.js → dist-DHOTQar0.js} +1 -1
  83. package/dist/{dist-DcgzXCfy.js → dist-DPcEtqmG.js} +2 -2
  84. package/dist/{dist-D4Z_qdP4.js → dist-Df0wKI0F.js} +1 -1
  85. package/dist/{dist-qhLI-SHd.js → dist-DszxuxS1.js} +2 -2
  86. package/dist/{dist-BEmkrGoQ.js → dist-Kxgjxakw.js} +1 -1
  87. package/dist/{dist-BbSMhhD2.js → dist-KzZ7WELx.js} +2 -2
  88. package/dist/{dist-jWUab2d1.js → dist-lA1Cttkb.js} +1 -1
  89. package/dist/dropdown-menu-CZgjYSAh.js +4502 -0
  90. package/dist/{ebnfDiagram-BXEA7PRR-Dm75pCAS.js → ebnfDiagram-PWID7BFC-WnG4cvSQ.js} +17 -18
  91. package/dist/{erDiagram-JOGREHBK-rVDG3VSQ.js → erDiagram-RLTQ6QDP-DhzKiYHc.js} +1037 -798
  92. package/dist/error-banner-B94dcDmP.js +391 -0
  93. package/dist/error-entries-iJn-Nryx.js +56 -0
  94. package/dist/{esm-CR1nhb1c.js → esm-BElI3qKH.js} +22 -22
  95. package/dist/{esm-C1d0g6Bv.js → esm-G4Fhsgng.js} +51 -37
  96. package/dist/{esm-CesmlDrs.js → esm-h8f7-1CJ.js} +423 -423
  97. package/dist/eventmodeling-NTZA5JFV-D7nP7Uyu.js +3 -0
  98. package/dist/feedback-button-BkFFL-wb.js +380 -0
  99. package/dist/flowDiagram-HODETNUW-6dMdB8pR.js +16 -0
  100. package/dist/{formats-CJMVUfb3.js → formats-BR5iGb4s.js} +8 -8
  101. package/dist/{ganttDiagram-PKOTCBZU-9D-LXF6P.js → ganttDiagram-EL5Y4UJY-DbzEBLbl.js} +15 -15
  102. package/dist/gitGraph-4MIJSDKK-Dy9uQXGh.js +3 -0
  103. package/dist/{gitGraphDiagram-DS77QQ5N-BfpsXTLf.js → gitGraphDiagram-WWUBYQGX-BMf0iKi1.js} +10 -10
  104. package/dist/{glide-data-editor-sPZ8fUFn.js → glide-data-editor-CeAOHG6x.js} +2143 -1910
  105. package/dist/{glide-utils-NShAPPtn.js → glide-utils-B3rUT5PI.js} +15 -15
  106. package/dist/globals-C74RodJy.js +19 -0
  107. package/dist/highlighted-body-KPVGNVTW-Ad3MKZ18.js +38 -0
  108. package/dist/html-to-image-kpuRfdfZ.js +3253 -0
  109. package/dist/info-A6RAGUB7-DKoayiHY.js +3 -0
  110. package/dist/{infoDiagram-6WML65LV-DqCTXHVy.js → infoDiagram-27XIBGKW-BagtCX_5.js} +7 -7
  111. package/dist/{ishikawaDiagram-WSZJBQD7-BCUYmZ1v.js → ishikawaDiagram-5VMMS53U-DByDehYy.js} +8 -8
  112. package/dist/{journeyDiagram-NVQOT4AX-BXB6M4zN.js → journeyDiagram-3NMN7TZE-D2LcKDUO.js} +8 -8
  113. package/dist/{jsx-runtime-CpoKNRgu.js → jsx-runtime-DgDPJV9S.js} +1 -1
  114. package/dist/{kanban-definition-27J2QSJJ-CGRhrDUX.js → kanban-definition-UXKFOSKX-BxwkbBI_.js} +29 -29
  115. package/dist/katex-DFBiSucW.js +21840 -0
  116. package/dist/{label-lwzzBHUW.js → label-BJ49YTvk.js} +6 -6
  117. package/dist/{linear-D5r_1YqD.js → linear-Cnaq3Tfb.js} +4 -4
  118. package/dist/{loader-VZ__bv4g.js → loader-D2W4oM-3.js} +15 -15
  119. package/dist/main.js +8105 -13485
  120. package/dist/mermaid-DPNuhemn.js +3084 -0
  121. package/dist/mermaid-HWGCJPDP-DYCzSILp.js +2 -0
  122. package/dist/mermaid-parser.core-DxuSfYwJ.js +638 -0
  123. package/dist/{mhchem-BjwrxcV-.js → mhchem-DWCF06w9.js} +1 -1
  124. package/dist/{micromark-factory-space-DruVGzeG.js → micromark-factory-space-DGxAzmw8.js} +2 -2
  125. package/dist/{mindmap-definition-FAOFIHXS-C9gRhzZq.js → mindmap-definition-YA3MSWOX-Cgvojw3y.js} +17 -17
  126. package/dist/{node-sql-parser-DesT1xNw.js → node-sql-parser-BiZOJ-FS.js} +0 -3
  127. package/dist/{number-overlay-editor-De2CabLL.js → number-overlay-editor-DDyIkEk3.js} +5 -5
  128. package/dist/objectWithoutPropertiesLoose-BRDcTsfB.js +23 -0
  129. package/dist/packet-AYTQ26CC-Yw7ajchJ.js +3 -0
  130. package/dist/{pegDiagram-VL7TDLO6-Df9IUjMD.js → pegDiagram-XKGWAZYB-DahHhYyN.js} +19 -20
  131. package/dist/{pick-SnHlmIBN.js → pick-CJeoxYOS.js} +1 -1
  132. package/dist/pie-WAS4IAKB-BKdKOAkL.js +3 -0
  133. package/dist/{pieDiagram-7S7Q4E2Y-BmRzQ8ZP.js → pieDiagram-E7YTZNPT-CCtX0oCK.js} +29 -31
  134. package/dist/{process-output-CpK6fX_C.js → process-output-DFe2tVIY.js} +7 -6
  135. package/dist/purify.es-DgKyQviY.js +805 -0
  136. package/dist/{quadrantDiagram-CIZ2JOQS-wFBvWcxS.js → quadrantDiagram-AXDQQJYC-DftINo6g.js} +5 -5
  137. package/dist/radar-RG4KPBEZ-ScR_D_Ts.js +3 -0
  138. package/dist/railroad-74A4TZTK-DQvPSdo5.js +3 -0
  139. package/dist/railroad-abnf-HS5TGJTU-CsJQG2O8.js +3 -0
  140. package/dist/railroad-ebnf-LZEXJU2U-BgEobkSn.js +3 -0
  141. package/dist/railroad-peg-WCYAUIDC-DjLAjofI.js +3 -0
  142. package/dist/{railroadDiagram-AXF67PYL-DxbIatiN.js → railroadDiagram-O6MQD6OU-Du5NsPUb.js} +17 -18
  143. package/dist/{react-dom-C94wmq5w.js → react-dom-BUgJ-ab_.js} +3 -3
  144. package/dist/{react-DMWcHin-.js → react-ofJbxM4x.js} +2 -2
  145. package/dist/{react-vega-DSD5aT7_.js → react-vega-5AcMW8gG.js} +10012 -9369
  146. package/dist/{requirementDiagram-LRYGKXZP-X_x-Y713.js → requirementDiagram-BXWQKSXE-0pmmxL6_.js} +20 -19
  147. package/dist/{reveal-component-BthxSdRc.js → reveal-component-CRy68FxJ.js} +1303 -1237
  148. package/dist/{sankeyDiagram-W5VNT64P-BYJALbbm.js → sankeyDiagram-P5KCCOFB-CDKR5TJH.js} +4 -4
  149. package/dist/{select-3qHpFKF9.js → select-C6plhmZr.js} +317 -562
  150. package/dist/{sequenceDiagram-SI44F4Z6-BiPBe3Sv.js → sequenceDiagram-WJ2MYXX4-DB-ChWwU.js} +11 -11
  151. package/dist/{sizeCapture-X5ZJPWSS-BKLDeh5H.js → sizeCapture-INFHLROL-Dx5SpkQF.js} +2 -2
  152. package/dist/{spec-CutWdqo3.js → spec-CAYwFeV6.js} +4 -4
  153. package/dist/{src-C31ngt98.js → src-CA_m4ONl.js} +298 -303
  154. package/dist/{stateDiagram-OKZ733FA-B7QyNBxH.js → stateDiagram-D77RDMKH-orN6AwMx.js} +19 -20
  155. package/dist/stateDiagram-v2-MP3YSRHH-o05_rJN8.js +30 -0
  156. package/dist/style.css +2 -2
  157. package/dist/{swimlanes-SLNWSIFB-B4f8VP3b.js → swimlanes-42K2YHIH-BkSDcqDT.js} +1501 -1591
  158. package/dist/swimlanesDiagram-VR7AAH4N-D_nFQfRc.js +30 -0
  159. package/dist/{swiper-component-C90YjhaV.js → swiper-component-IOUowglM.js} +39 -26
  160. package/dist/{time-aWGfjN8x.js → time-BieKwdYS.js} +42 -42
  161. package/dist/{timeline-definition-Z64GVDOM-Dacq_5lr.js → timeline-definition-24CTP7MA-BuqJUIRZ.js} +7 -7
  162. package/dist/{toDate-D-Rx3ux5.js → toDate-WXg1yLcI.js} +10 -9
  163. package/dist/toast-DvY70eFX.js +1834 -0
  164. package/dist/tooltip-qB8NlSqV.js +1742 -0
  165. package/dist/treeView-Q6P3EWNA-DuRV97YF.js +3 -0
  166. package/dist/treemap-WGGIJYW6-AsLq-uwC.js +3 -0
  167. package/dist/use-restore-focus-D5D9xEjZ.js +5946 -0
  168. package/dist/{use-vega-container-remeasure-DUW4tRxq.js → use-vega-container-remeasure-4DwJbPWu.js} +2 -2
  169. package/dist/{useAsyncData-CH6-CgJF.js → useAsyncData-BD6AStMt.js} +3 -3
  170. package/dist/{useDateFormatter-Cp9Jdxls.js → useDateFormatter-BRTPsUK7.js} +8 -8
  171. package/dist/{useDeepCompareMemoize-JC87C_jO.js → useDeepCompareMemoize-D_HNdyvI.js} +2 -2
  172. package/dist/{useIframeCapabilities-De3C-9b4.js → useIframeCapabilities-CVtNkI3Y.js} +2 -2
  173. package/dist/{useLifecycle-B4RGlyf0.js → useLifecycle-DPgiiwwU.js} +6 -6
  174. package/dist/useNumberFormatter-BfDpa21j.js +251 -0
  175. package/dist/useTheme-DzXmYZ5V.js +904 -0
  176. package/dist/{vega-component-CPehZbUb.js → vega-component-D0QXopU8.js} +34 -34
  177. package/dist/{vega-embed-container-TJ5z5dZw.js → vega-embed-container-CbnTccZi.js} +6 -6
  178. package/dist/vega-loader.browser-D95WfUcj.js +1139 -0
  179. package/dist/{vennDiagram-T6HMQDX7-D-Rcq3Iu.js → vennDiagram-4TSXK5OY-CTUlJ2PK.js} +9 -9
  180. package/dist/wardley-WFR3VGLG-Na-tFhQJ.js +3 -0
  181. package/dist/{wardleyDiagram-T6FBY63Y-Z-Og7_Wd.js → wardleyDiagram-VM6X3IG4-3evLUb0M.js} +10 -10
  182. package/dist/{xychartDiagram-ELKLHX3M-BDJ2dITA.js → xychartDiagram-S5SC5T6Z-DL2E3K8-.js} +248 -139
  183. package/package.json +54 -57
  184. package/src/__mocks__/requests.ts +7 -0
  185. package/src/__tests__/__snapshots__/CellStatus.test.tsx.snap +12 -12
  186. package/src/components/chat/acp/__tests__/agent-panel.test.tsx +270 -0
  187. package/src/components/chat/acp/agent-panel.tsx +45 -44
  188. package/src/components/data-table/column-explorer-panel/column-explorer.tsx +2 -6
  189. package/src/components/data-table/column-summary/chart-spec-model.tsx +1 -1
  190. package/src/components/data-table/column-visibility-dropdown.tsx +9 -3
  191. package/src/components/data-table/columns.tsx +64 -67
  192. package/src/components/datasources/components.tsx +4 -2
  193. package/src/components/editor/RecoveryButton.tsx +1 -0
  194. package/src/components/editor/alerts/connecting-alert.tsx +1 -66
  195. package/src/components/editor/alerts/connection-notice.tsx +140 -0
  196. package/src/components/editor/app-container.tsx +9 -3
  197. package/src/components/editor/boundary/ErrorBoundary.tsx +38 -16
  198. package/src/components/editor/boundary/__tests__/ErrorBoundary.test.tsx +311 -0
  199. package/src/components/editor/chrome/components/__tests__/feedback-button.test.tsx +94 -0
  200. package/src/components/editor/chrome/components/feedback-button.tsx +63 -42
  201. package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +114 -7
  202. package/src/components/editor/chrome/panels/__tests__/sandbox-recovery.test.tsx +165 -0
  203. package/src/components/editor/chrome/panels/packages-panel.tsx +93 -166
  204. package/src/components/editor/chrome/panels/sandbox-controller.tsx +106 -0
  205. package/src/components/editor/chrome/panels/sandbox-panel.tsx +173 -0
  206. package/src/components/editor/chrome/wrapper/footer-items/backend-status.tsx +3 -1
  207. package/src/components/editor/code/__tests__/readonly-diff.test.tsx +65 -0
  208. package/src/components/editor/code/readonly-diff.tsx +1 -1
  209. package/src/components/editor/file-tree/__tests__/tree-actions.test.tsx +54 -0
  210. package/src/components/editor/file-tree/tree-actions.tsx +11 -14
  211. package/src/components/editor/header/__tests__/status.test.tsx +8 -20
  212. package/src/components/editor/header/filename-input.tsx +2 -0
  213. package/src/components/editor/header/status.tsx +5 -3
  214. package/src/components/editor/notebook-cell.tsx +4 -2
  215. package/src/components/editor/output/JsonOutput.tsx +182 -632
  216. package/src/components/editor/output/Outputs.css +50 -17
  217. package/src/components/editor/output/__tests__/JsonOutput-interactions.test.tsx +254 -0
  218. package/src/components/editor/output/__tests__/JsonOutput-mimetype.test.tsx +41 -46
  219. package/src/components/editor/output/__tests__/{json-output.test.ts → json-output-formatting.test.ts} +113 -75
  220. package/src/components/editor/output/__tests__/json-output-pagination.test.ts +252 -0
  221. package/src/components/editor/output/json-output/formatting.ts +167 -0
  222. package/src/components/editor/output/json-output/pagination.ts +155 -0
  223. package/src/components/editor/output/json-output/renderers.tsx +196 -0
  224. package/src/components/editor/output/json-output/themes.ts +52 -0
  225. package/src/components/editor/renderers/cell-array.tsx +1 -5
  226. package/src/components/home/components.tsx +17 -0
  227. package/src/components/pages/gallery-page.tsx +2 -4
  228. package/src/components/pages/home-page.tsx +2 -4
  229. package/src/components/slides/slide-form.tsx +1 -0
  230. package/src/components/slides/slide-notes-editor.tsx +1 -0
  231. package/src/components/terminal/terminal.tsx +4 -4
  232. package/src/components/tracing/tracing-spec.ts +1 -1
  233. package/src/components/ui/table.tsx +1 -1
  234. package/src/components/ui/use-toast.ts +8 -0
  235. package/src/core/MarimoApp.tsx +0 -2
  236. package/src/core/cells/__tests__/cell.test.ts +61 -0
  237. package/src/core/cells/cell.ts +9 -1
  238. package/src/core/cells/pending-delete-service.ts +12 -16
  239. package/src/core/codemirror/language/__tests__/sql.test.ts +70 -2
  240. package/src/core/codemirror/language/languages/sql/sql.ts +16 -1
  241. package/src/core/codemirror/rtc/extension.ts +5 -2
  242. package/src/core/datasets/__tests__/data-source.test.ts +2 -0
  243. package/src/core/datasets/data-source-connections.ts +15 -10
  244. package/src/core/edit-app.tsx +8 -5
  245. package/src/core/errors/state.ts +1 -2
  246. package/src/core/islands/__tests__/parse.test.ts +140 -7
  247. package/src/core/islands/bootstrap.ts +1 -0
  248. package/src/core/islands/bridge.ts +3 -0
  249. package/src/core/islands/parse.ts +20 -0
  250. package/src/core/network/connection-notice.ts +84 -0
  251. package/src/core/network/requests-lazy.ts +3 -0
  252. package/src/core/network/requests-network.ts +12 -0
  253. package/src/core/network/requests-static.ts +3 -0
  254. package/src/core/network/requests-toasting.tsx +3 -0
  255. package/src/core/network/resolve.ts +2 -1
  256. package/src/core/network/types.ts +7 -0
  257. package/src/core/packages/__tests__/usePackageAction.test.tsx +129 -0
  258. package/src/core/packages/__tests__/useSandboxController.test.tsx +165 -0
  259. package/src/core/packages/package-data.ts +35 -0
  260. package/src/core/packages/sandbox-state.ts +15 -0
  261. package/src/core/packages/toast-components.tsx +11 -0
  262. package/src/core/packages/useInstallPackage.ts +1 -1
  263. package/src/core/packages/usePackageAction.ts +51 -0
  264. package/src/core/packages/usePackageDependencies.ts +22 -0
  265. package/src/core/packages/useSandboxController.ts +188 -0
  266. package/src/core/run-app.tsx +2 -0
  267. package/src/core/runtime/__tests__/runtime.test.ts +11 -0
  268. package/src/core/runtime/runtime.ts +7 -2
  269. package/src/core/wasm/bridge.ts +11 -0
  270. package/src/core/websocket/__tests__/useMarimoKernelConnection.hook.test.tsx +212 -2
  271. package/src/core/websocket/transports/transport.ts +1 -1
  272. package/src/core/websocket/transports/ws.ts +1 -1
  273. package/src/core/websocket/types.ts +11 -1
  274. package/src/core/websocket/useMarimoKernelConnection.tsx +43 -8
  275. package/src/css/app/Cell.css +2 -0
  276. package/src/hooks/useDebounce.ts +2 -0
  277. package/src/plugins/core/BadPlugin.tsx +3 -14
  278. package/src/plugins/core/RenderHTML.tsx +22 -2
  279. package/src/plugins/core/__test__/RenderHTML.test.ts +187 -0
  280. package/src/plugins/core/registerReactComponent.tsx +0 -1
  281. package/src/plugins/impl/FormPlugin.tsx +2 -0
  282. package/src/plugins/impl/MatrixPlugin.tsx +2 -0
  283. package/src/plugins/impl/RangeSliderPlugin.tsx +2 -0
  284. package/src/plugins/impl/SliderPlugin.tsx +2 -0
  285. package/src/plugins/impl/data-frames/panel.tsx +1 -0
  286. package/src/plugins/layout/AccordionPlugin.tsx +7 -2
  287. package/src/plugins/layout/JsonOutputPlugin.tsx +7 -10
  288. package/src/plugins/layout/__test__/AccordionPlugin.test.tsx +66 -0
  289. package/src/stories/sandbox.stories.tsx +308 -0
  290. package/dist/ErrorBoundary-CR-ZQN_c.js +0 -125
  291. package/dist/_baseMerge-Dv8bBloN.js +0 -73
  292. package/dist/_initCloneObject---vediN2.js +0 -95
  293. package/dist/arc-DOVLrCPd.js +0 -82
  294. package/dist/architecture-TIHT7OUA-MiJpPpQW.js +0 -3
  295. package/dist/array-CApNbSU9.js +0 -6
  296. package/dist/assets/__vite-browser-external-BdKGjRZ4.js +0 -1
  297. package/dist/assets/worker-DlIBfp9S.js +0 -98
  298. package/dist/blockDiagram-VBNYF7ZC-Bc--I65C.js +0 -3158
  299. package/dist/chat-ui-DjTMZRVP.js +0 -8455
  300. package/dist/chunk-3NCLNEKW-BIp_lB_4.js +0 -14
  301. package/dist/chunk-5FCAYU7R-C_HO9A1J.js +0 -23
  302. package/dist/chunk-5HE753X5-Bixm_MV-.js +0 -38
  303. package/dist/chunk-5JV3BV7I-CH5J3iZY.js +0 -54
  304. package/dist/chunk-5TONJI2A-DG6gDiSS.js +0 -62
  305. package/dist/chunk-7BUUIJ7U-5afmIPeo.js +0 -82
  306. package/dist/chunk-BIQX33UG-7TMBUf89.js +0 -23
  307. package/dist/chunk-CQNSW5MT-Cnqqsxt-.js +0 -38
  308. package/dist/chunk-CYSBUYHQ-BAbymFHR.js +0 -23
  309. package/dist/chunk-EMLP6XTP-Bsz-QL77.js +0 -23
  310. package/dist/chunk-J7OUQ5F2-CM_meqZH.js +0 -57
  311. package/dist/chunk-JG7HCLWE-DUnCDgPk.js +0 -62
  312. package/dist/chunk-MOZMSUNE-BG6IHvzT.js +0 -35
  313. package/dist/chunk-OSBZ3O6U-CYwj_bvR.js +0 -23
  314. package/dist/chunk-QBLGF6JB-B4vJ4j8J.js +0 -23
  315. package/dist/chunk-QR6OTTB3-D4wH2ldi.js +0 -3738
  316. package/dist/chunk-R7FJI6CG-Blf8FcmZ.js +0 -64
  317. package/dist/chunk-RYQCIY6F-BWbIlBIH.js +0 -241
  318. package/dist/chunk-U6XO7XAA-COw445DS.js +0 -63
  319. package/dist/chunk-UBXNYLIW-BHigVU4H.js +0 -32
  320. package/dist/chunk-YOTPTUD7-Dn044Q_d.js +0 -30
  321. package/dist/chunk-ZIRB5QZD-BR4TMfOa.js +0 -1153
  322. package/dist/classDiagram-JCYQIIEL-DmXSLxe_.js +0 -30
  323. package/dist/classDiagram-v2-OCEON4UE-CooqVexA.js +0 -30
  324. package/dist/click-outside-container-D_URcm3D.js +0 -167
  325. package/dist/code-block-37QAKDTI-CQrzBOr-.js +0 -107
  326. package/dist/copy-CD7S58CY.js +0 -141
  327. package/dist/cynefin-VYW2F7L2-CeHAxOEX.js +0 -3
  328. package/dist/dagre-VZM6K2ZE-CJggWPTJ.js +0 -328
  329. package/dist/dist-3_o3T78I.js +0 -1360
  330. package/dist/dist-ABQjhuek.js +0 -64
  331. package/dist/dist-CO5R6VMi.js +0 -538
  332. package/dist/dropdown-menu-DhCfcscX.js +0 -5858
  333. package/dist/emotion-is-prop-valid.esm-M57VsT8j.js +0 -14
  334. package/dist/error-banner-CsXbEEjo.js +0 -611
  335. package/dist/eventmodeling-45OFAUF4-B_lWZ-Pd.js +0 -3
  336. package/dist/flowDiagram-UKHOOZJN-g1AmrZzM.js +0 -17
  337. package/dist/gitGraph-TEB2WS4Q-Dj-eXUph.js +0 -3
  338. package/dist/info-DKCQHKI2-Bhb-pe2e.js +0 -3
  339. package/dist/line-CuKjIQa1.js +0 -34
  340. package/dist/map-BJpOfPlL.js +0 -149
  341. package/dist/math-BXtKgYah.js +0 -10
  342. package/dist/mermaid-4DMBBIKO-Ddp9TgNp.js +0 -2
  343. package/dist/mermaid-Bi23Ibnw.js +0 -1173
  344. package/dist/mermaid-parser.core-Din9FvSi.js +0 -99
  345. package/dist/packet-7NZHBO7P-fbkIIyKE.js +0 -3
  346. package/dist/path-BNHuHZP8.js +0 -87
  347. package/dist/pie-RZYD4A2V-CLRIsxY1.js +0 -3
  348. package/dist/purify.es-CB-YO1Hn.js +0 -774
  349. package/dist/radar-I7S5WNFK-BGzCbgXu.js +0 -3
  350. package/dist/railroad-3IZDKUUU-CvQIIcuU.js +0 -3
  351. package/dist/railroad-abnf-AHOZXSZD-CMwtlNtu.js +0 -3
  352. package/dist/railroad-ebnf-EBAXGLYW-n1QUOAsh.js +0 -3
  353. package/dist/railroad-peg-LSFZ7HO6-PjnrpmCs.js +0 -3
  354. package/dist/schemas-DzK6U94N.js +0 -6057
  355. package/dist/stateDiagram-v2-UEYNNEHI-BTRUs8Il.js +0 -30
  356. package/dist/step-CKczO72Q.js +0 -670
  357. package/dist/swimlanesDiagram-ULZ7WXOC-NBmgBl9G.js +0 -31
  358. package/dist/timer-DpDyj9G0.js +0 -596
  359. package/dist/tooltip-Dt77sBeT.js +0 -390
  360. package/dist/treeView-QDETBFTQ-DU8LwHuf.js +0 -3
  361. package/dist/treemap-6X3UGDF4-DUk4-k_O.js +0 -3
  362. package/dist/useTheme-CVJ0UOf2.js +0 -841
  363. package/dist/vega-loader.browser-DEggZzni.js +0 -1083
  364. package/dist/wardley-OPB4EBWU-Db-AaGas.js +0 -3
  365. package/src/components/editor/output/EmotionCacheProvider.tsx +0 -32
@@ -532,6 +532,18 @@ export function createNetworkRequests(): EditRequests & RunRequests {
532
532
  })
533
533
  .then(handleResponseReturnNull);
534
534
  },
535
+ getSandbox: (request) =>
536
+ getClient()
537
+ .POST("/api/packages/sandbox", { body: request, params: getParams() })
538
+ .then(handleResponse),
539
+ updateManifest: (request) =>
540
+ getClient()
541
+ .POST("/api/packages/manifest", { body: request, params: getParams() })
542
+ .then(handleResponse),
543
+ syncSandbox: (request) =>
544
+ getClient()
545
+ .POST("/api/packages/sync", { body: request, params: getParams() })
546
+ .then(handleResponse),
535
547
  addPackage: (request) => {
536
548
  return getClient()
537
549
  .POST("/api/packages/add", {
@@ -98,6 +98,9 @@ export function createStaticRequests(): EditRequests & RunRequests {
98
98
  addPackage: throwNotInEditMode,
99
99
  removePackage: throwNotInEditMode,
100
100
  getPackageList: throwNotInEditMode,
101
+ getSandbox: throwNotInEditMode,
102
+ updateManifest: throwNotInEditMode,
103
+ syncSandbox: throwNotInEditMode,
101
104
  getDependencyTree: throwNotInEditMode,
102
105
  listSecretKeys: throwNotInEditMode,
103
106
  writeSecret: throwNotInEditMode,
@@ -79,6 +79,9 @@ export function createErrorToastingRequests(
79
79
  addPackage: "Failed to add package",
80
80
  removePackage: "Failed to remove package",
81
81
  getPackageList: "Failed to get package list",
82
+ getSandbox: "",
83
+ updateManifest: "",
84
+ syncSandbox: "",
82
85
  getDependencyTree: "Failed to get dependency tree",
83
86
  listSecretKeys: "Failed to fetch secrets",
84
87
  writeSecret: "Failed to write secret",
@@ -1,4 +1,5 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
+ import { withPackageInvalidation } from "../packages/package-data";
2
3
  import { getRuntimeManager } from "../runtime/config";
3
4
  import { isStaticNotebook } from "../static/static-state";
4
5
  import { PyodideBridge } from "../wasm/bridge";
@@ -20,5 +21,5 @@ export function resolveRequestClient(): EditRequests & RunRequests {
20
21
  getRuntimeManager(),
21
22
  );
22
23
  }
23
- return createErrorToastingRequests(base);
24
+ return withPackageInvalidation(createErrorToastingRequests(base));
24
25
  }
@@ -69,6 +69,10 @@ export type ListPackagesResponse = schemas["ListPackagesResponse"];
69
69
  export type DependencyTreeResponse = schemas["DependencyTreeResponse"];
70
70
  export type DependencyTreeNode = schemas["DependencyTreeNode"];
71
71
 
72
+ export type SandboxRequest = schemas["SandboxRequest"];
73
+ export type SandboxResponse = schemas["SandboxResponse"];
74
+ export type UpdateManifestRequest = schemas["UpdateManifestRequest"];
75
+ export type SyncSandboxResponse = schemas["SyncSandboxResponse"];
72
76
  export type PackageOperationResponse = schemas["PackageOperationResponse"];
73
77
  export type InstantiateNotebookRequest = schemas["InstantiateNotebookRequest"];
74
78
  export type MarimoConfig = schemas["MarimoConfig"];
@@ -246,6 +250,9 @@ export interface EditRequests {
246
250
  autoExportAsMarkdown: (request: AutoExportAsMarkdownRequest) => Promise<null>;
247
251
  autoExportAsIPYNB: (request: AutoExportAsIPYNBRequest) => Promise<null>;
248
252
  updateCellOutputs: (request: UpdateCellOutputsRequest) => Promise<null>;
253
+ getSandbox: (request: SandboxRequest) => Promise<SandboxResponse>;
254
+ updateManifest: (request: UpdateManifestRequest) => Promise<SandboxResponse>;
255
+ syncSandbox: (request: SandboxRequest) => Promise<SyncSandboxResponse>;
249
256
  // Package requests
250
257
  getPackageList: () => Promise<ListPackagesResponse>;
251
258
  getDependencyTree: () => Promise<DependencyTreeResponse>;
@@ -0,0 +1,129 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+ import { act, renderHook } from "@testing-library/react";
3
+ import { createStore, Provider } from "jotai";
4
+ import { beforeEach, describe, expect, it, vi } from "vitest";
5
+ import { MockRequestClient } from "@/__mocks__/requests";
6
+ import { toast } from "@/components/ui/use-toast";
7
+ import { requestClientAtom } from "@/core/network/requests";
8
+ import type { PackageOperationResponse } from "@/core/network/types";
9
+ import { usePackageAction } from "../usePackageAction";
10
+
11
+ vi.mock("@/components/ui/use-toast", () => ({ toast: vi.fn() }));
12
+
13
+ function setup(action: "upgrade" | "remove") {
14
+ const client = MockRequestClient.create();
15
+ const store = createStore();
16
+ store.set(requestClientAtom, client);
17
+ return {
18
+ request: vi.mocked(
19
+ action === "upgrade" ? client.addPackage : client.removePackage,
20
+ ),
21
+ ...renderHook(
22
+ () =>
23
+ usePackageAction(action, "numpy", [{ kind: "group", value: "dev" }]),
24
+ {
25
+ wrapper: ({ children }) => (
26
+ <Provider store={store}>{children}</Provider>
27
+ ),
28
+ },
29
+ ),
30
+ };
31
+ }
32
+
33
+ describe("usePackageAction", () => {
34
+ beforeEach(() => vi.clearAllMocks());
35
+
36
+ it.each([
37
+ { action: "upgrade", title: "Package upgraded", upgrade: true },
38
+ { action: "remove", title: "Package removed" },
39
+ ] as const)(
40
+ "preserves dependency groups and prevents duplicate $action requests",
41
+ async ({ action, title, ...options }) => {
42
+ const { result, request } = setup(action);
43
+ const { promise, resolve } =
44
+ Promise.withResolvers<PackageOperationResponse>();
45
+ request.mockReturnValue(promise);
46
+ let pending: Promise<void>;
47
+ act(() => {
48
+ pending = result.current.run();
49
+ });
50
+ expect(result.current.loading).toBe(true);
51
+ await act(async () => {
52
+ await result.current.run();
53
+ });
54
+ expect(request).toHaveBeenCalledExactlyOnceWith({
55
+ package: "numpy",
56
+ group: "dev",
57
+ ...options,
58
+ });
59
+ await act(async () => {
60
+ resolve({ success: true });
61
+ await pending;
62
+ });
63
+ expect(result.current.loading).toBe(false);
64
+ expect(toast).toHaveBeenCalledExactlyOnceWith(
65
+ expect.objectContaining({ title }),
66
+ );
67
+ },
68
+ );
69
+
70
+ it("reports saved changes requiring a restart without claiming success", async () => {
71
+ const { result, request } = setup("upgrade");
72
+ request.mockResolvedValue({ success: false, restartRequired: true });
73
+ await act(async () => {
74
+ await result.current.run();
75
+ });
76
+ expect(toast).toHaveBeenCalledExactlyOnceWith(
77
+ expect.objectContaining({
78
+ title: "Changes saved — restart required",
79
+ }),
80
+ );
81
+ });
82
+
83
+ it.each([
84
+ {
85
+ response: { success: false, error: "resolver failed" },
86
+ description: "resolver failed",
87
+ },
88
+ { response: { success: false }, description: expect.any(String) },
89
+ ])(
90
+ "reports failed responses as errors: $response",
91
+ async ({ response, description }) => {
92
+ const { result, request } = setup("remove");
93
+ request.mockResolvedValue(response);
94
+ await act(async () => {
95
+ await result.current.run();
96
+ });
97
+ expect(toast).toHaveBeenCalledExactlyOnceWith(
98
+ expect.objectContaining({
99
+ title: "Failed to remove package",
100
+ description,
101
+ variant: "danger",
102
+ }),
103
+ );
104
+ },
105
+ );
106
+
107
+ it("reports request errors and allows a retry", async () => {
108
+ const { result, request } = setup("remove");
109
+ request.mockRejectedValueOnce(new Error("offline"));
110
+ await act(async () => {
111
+ await result.current.run();
112
+ });
113
+ expect(toast).toHaveBeenCalledExactlyOnceWith(
114
+ expect.objectContaining({
115
+ title: "Failed to remove package",
116
+ description: "offline",
117
+ variant: "danger",
118
+ }),
119
+ );
120
+ expect(result.current.loading).toBe(false);
121
+ request.mockResolvedValue({ success: true });
122
+ await act(async () => {
123
+ await result.current.run();
124
+ });
125
+ expect(toast).toHaveBeenLastCalledWith(
126
+ expect.objectContaining({ title: "Package removed" }),
127
+ );
128
+ });
129
+ });
@@ -0,0 +1,165 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+ import { act, renderHook, waitFor } from "@testing-library/react";
3
+ import { Provider } from "jotai";
4
+ import { beforeEach, describe, expect, it, vi } from "vitest";
5
+ import { MockRequestClient } from "@/__mocks__/requests";
6
+ import { kernelStartupErrorAtom } from "@/core/errors/state";
7
+ import { connectionAtom } from "@/core/network/connection";
8
+ import { requestClientAtom } from "@/core/network/requests";
9
+ import type {
10
+ SandboxResponse,
11
+ SyncSandboxResponse,
12
+ } from "@/core/network/types";
13
+ import { filenameAtom } from "@/core/saving/file-state";
14
+ import { store } from "@/core/state/jotai";
15
+ import { WebSocketState } from "@/core/websocket/types";
16
+ import { HTTPError } from "@/utils/errors";
17
+ import { packageDataVersionAtom } from "../package-data";
18
+ import {
19
+ sandboxActionsAtom,
20
+ sandboxAtom,
21
+ sandboxSyncAtom,
22
+ } from "../sandbox-state";
23
+ import { useSandboxController } from "../useSandboxController";
24
+
25
+ vi.mock("../toast-components", () => ({
26
+ showPackageRestartToast: vi.fn(),
27
+ showSandboxSyncToast: vi.fn(),
28
+ }));
29
+
30
+ const manifest = 'dependencies = ["numpy"]';
31
+ const sandbox: SandboxResponse = {
32
+ backend: "uv",
33
+ manifest,
34
+ filename: "notebook.py",
35
+ };
36
+
37
+ beforeEach(() => {
38
+ store.set(sandboxAtom, null);
39
+ store.set(sandboxSyncAtom, { pending: false, error: null });
40
+ store.set(sandboxActionsAtom, null);
41
+ store.set(connectionAtom, { state: WebSocketState.OPEN });
42
+ store.set(kernelStartupErrorAtom, null);
43
+ store.set(filenameAtom, "notebook.py");
44
+ store.set(packageDataVersionAtom, 0);
45
+ });
46
+
47
+ function setup() {
48
+ const client = MockRequestClient.create({
49
+ getSandbox: vi.fn().mockResolvedValue(sandbox),
50
+ updateManifest: vi.fn(async ({ contents }) => ({
51
+ ...sandbox,
52
+ manifest: contents,
53
+ })),
54
+ syncSandbox: vi.fn().mockResolvedValue({ success: true, reconnect: false }),
55
+ });
56
+ store.set(requestClientAtom, client);
57
+ const reconnect = vi.fn<() => Promise<void>>().mockResolvedValue(undefined);
58
+ return {
59
+ client,
60
+ reconnect,
61
+ ...renderHook(() => useSandboxController(reconnect), {
62
+ wrapper: ({ children }) => <Provider store={store}>{children}</Provider>,
63
+ }),
64
+ };
65
+ }
66
+
67
+ describe("useSandboxController", () => {
68
+ it("keeps a conflicting draft across closing and only discards it on explicit reload", async () => {
69
+ const { result, client } = setup();
70
+ vi.mocked(client.updateManifest).mockRejectedValue(
71
+ new HTTPError(409, "Conflict", { detail: "Manifest changed on disk" }),
72
+ );
73
+ await act(async () => {
74
+ await store.get(sandboxActionsAtom)?.editManifest();
75
+ });
76
+ act(() => result.current.setDraft("dependencies = []"));
77
+ await act(async () => {
78
+ await result.current.saveAndSync();
79
+ });
80
+ expect(client.updateManifest).toHaveBeenCalledExactlyOnceWith({
81
+ fileKey: "notebook.py",
82
+ previous: manifest,
83
+ contents: "dependencies = []",
84
+ });
85
+ expect(result.current).toMatchObject({
86
+ open: true,
87
+ draft: "dependencies = []",
88
+ dirty: true,
89
+ conflict: true,
90
+ diagnostic: "Manifest changed on disk",
91
+ pending: false,
92
+ });
93
+ expect(client.syncSandbox).not.toHaveBeenCalled();
94
+ act(() => result.current.setOpen(false));
95
+ await act(async () => {
96
+ await store.get(sandboxActionsAtom)?.editManifest();
97
+ });
98
+ expect(result.current.draft).toBe("dependencies = []");
99
+ await act(async () => {
100
+ await result.current.loadManifest();
101
+ });
102
+ expect(result.current).toMatchObject({
103
+ draft: manifest,
104
+ dirty: false,
105
+ conflict: false,
106
+ diagnostic: null,
107
+ });
108
+ });
109
+
110
+ it("shares sync progress across callers and refreshes packages without reconnecting", async () => {
111
+ const { client, reconnect } = setup();
112
+ const { promise, resolve } = Promise.withResolvers<SyncSandboxResponse>();
113
+ vi.mocked(client.syncSandbox).mockReturnValue(promise);
114
+ const actions = store.get(sandboxActionsAtom)!;
115
+ let pending: Promise<boolean>;
116
+ act(() => {
117
+ pending = actions.sync();
118
+ });
119
+ expect(store.get(sandboxSyncAtom)).toEqual({ pending: true, error: null });
120
+ await act(async () => {
121
+ expect(await actions.sync()).toBe(false);
122
+ });
123
+ expect(client.syncSandbox).toHaveBeenCalledTimes(1);
124
+ await act(async () => {
125
+ resolve({ success: true, reconnect: false });
126
+ expect(await pending).toBe(true);
127
+ });
128
+ expect(store.get(sandboxSyncAtom)).toEqual({ pending: false, error: null });
129
+ expect(store.get(packageDataVersionAtom)).toBe(1);
130
+ expect(reconnect).not.toHaveBeenCalled();
131
+ });
132
+
133
+ it("reports sync failure and clears it when retrying", async () => {
134
+ const { client } = setup();
135
+ const actions = store.get(sandboxActionsAtom)!;
136
+ vi.mocked(client.syncSandbox).mockRejectedValueOnce(new Error("offline"));
137
+ await act(async () => {
138
+ expect(await actions.sync()).toBe(false);
139
+ });
140
+ expect(store.get(sandboxSyncAtom)).toEqual({
141
+ pending: false,
142
+ error: "offline",
143
+ });
144
+ await act(async () => {
145
+ expect(await actions.sync()).toBe(true);
146
+ });
147
+ expect(store.get(sandboxSyncAtom)).toEqual({ pending: false, error: null });
148
+ });
149
+
150
+ it("ignores stale metadata after a filename change and unregisters actions on unmount", async () => {
151
+ const { client, unmount } = setup();
152
+ await waitFor(() => expect(store.get(sandboxAtom)).toEqual(sandbox));
153
+ const { promise, resolve } = Promise.withResolvers<SandboxResponse>();
154
+ vi.mocked(client.getSandbox).mockReturnValueOnce(promise);
155
+ act(() => store.set(filenameAtom, "old.py"));
156
+ const current = { ...sandbox, filename: "current.py" };
157
+ vi.mocked(client.getSandbox).mockResolvedValue(current);
158
+ act(() => store.set(filenameAtom, "current.py"));
159
+ await waitFor(() => expect(store.get(sandboxAtom)).toEqual(current));
160
+ await act(async () => resolve({ ...sandbox, filename: "old.py" }));
161
+ expect(store.get(sandboxAtom)).toEqual(current);
162
+ unmount();
163
+ expect(store.get(sandboxActionsAtom)).toBeNull();
164
+ });
165
+ });
@@ -0,0 +1,35 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+ import { atom } from "jotai";
3
+ import type {
4
+ EditRequests,
5
+ PackageOperationResponse,
6
+ RunRequests,
7
+ } from "@/core/network/types";
8
+ import { store } from "@/core/state/jotai";
9
+
10
+ export const packageDataVersionAtom = atom(0);
11
+ export function invalidatePackageData() {
12
+ store.set(packageDataVersionAtom, (version) => version + 1);
13
+ }
14
+
15
+ /** Refresh package consumers regardless of which UI initiated a mutation. */
16
+ export function withPackageInvalidation(
17
+ client: EditRequests & RunRequests,
18
+ ): EditRequests & RunRequests {
19
+ function invalidateAfter<Request, Response extends PackageOperationResponse>(
20
+ request: (body: Request) => Promise<Response>,
21
+ ) {
22
+ return async (body: Request) => {
23
+ const result = await request(body);
24
+ if (result.success || result.restartRequired) {
25
+ invalidatePackageData();
26
+ }
27
+ return result;
28
+ };
29
+ }
30
+ return {
31
+ ...client,
32
+ addPackage: invalidateAfter(client.addPackage),
33
+ removePackage: invalidateAfter(client.removePackage),
34
+ };
35
+ }
@@ -0,0 +1,15 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+ import { atom } from "jotai";
3
+ import type { SandboxResponse } from "@/core/network/types";
4
+
5
+ export const sandboxAtom = atom<SandboxResponse | null>(null);
6
+ export const sandboxSyncAtom = atom<{ pending: boolean; error: string | null }>(
7
+ {
8
+ pending: false,
9
+ error: null,
10
+ },
11
+ );
12
+ export const sandboxActionsAtom = atom<{
13
+ sync: () => Promise<boolean>;
14
+ editManifest: () => Promise<void>;
15
+ } | null>(null);
@@ -29,6 +29,17 @@ export const showPackageRestartToast = () => {
29
29
  });
30
30
  };
31
31
 
32
+ export const showSandboxSyncToast = () => {
33
+ toast({
34
+ title: "Sandbox environment synced",
35
+ description: (
36
+ <div className="text-xs text-muted-foreground">
37
+ Some Python packages may require a kernel restart to see changes.
38
+ </div>
39
+ ),
40
+ });
41
+ };
42
+
32
43
  export const showAddPackageToast = (
33
44
  packageName: string | string[],
34
45
  error?: string | null,
@@ -37,10 +37,10 @@ export function useInstallPackages(): {
37
37
  showPackageRestartToast();
38
38
  } else if (response.success) {
39
39
  showAddPackageToast(packages);
40
+ onSuccess?.();
40
41
  } else {
41
42
  showAddPackageToast(packages, response.error);
42
43
  }
43
- onSuccess?.();
44
44
  } catch (error) {
45
45
  Logger.error(error);
46
46
  } finally {
@@ -0,0 +1,51 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+ import { useRef, useState } from "react";
3
+ import useEvent from "react-use-event-hook";
4
+ import { useRequestClient } from "@/core/network/requests";
5
+ import type { DependencyTreeNode } from "@/core/network/types";
6
+ import { prettyError } from "@/utils/errors";
7
+ import {
8
+ showPackageRestartToast,
9
+ showRemovePackageToast,
10
+ showUpgradePackageToast,
11
+ } from "./toast-components";
12
+
13
+ /** Shared mutation lifecycle for package actions in both list and tree views. */
14
+ export function usePackageAction(
15
+ action: "upgrade" | "remove",
16
+ packageName: string,
17
+ tags?: DependencyTreeNode["tags"],
18
+ ) {
19
+ const { addPackage, removePackage } = useRequestClient();
20
+ const [loading, setLoading] = useState(false);
21
+ const inFlight = useRef(false);
22
+ const run = useEvent(async () => {
23
+ if (inFlight.current) {
24
+ return;
25
+ }
26
+ inFlight.current = true;
27
+ setLoading(true);
28
+ const showResult =
29
+ action === "upgrade" ? showUpgradePackageToast : showRemovePackageToast;
30
+ try {
31
+ const group = tags?.find((tag) => tag.kind === "group")?.value;
32
+ const request = { package: packageName, group };
33
+ const response = await (action === "upgrade"
34
+ ? addPackage({ ...request, upgrade: true })
35
+ : removePackage(request));
36
+ if (response.restartRequired) {
37
+ showPackageRestartToast();
38
+ } else if (response.success) {
39
+ showResult(packageName);
40
+ } else {
41
+ showResult(packageName, response.error || "Package operation failed.");
42
+ }
43
+ } catch (error) {
44
+ showResult(packageName, prettyError(error));
45
+ } finally {
46
+ inFlight.current = false;
47
+ setLoading(false);
48
+ }
49
+ });
50
+ return { loading, run };
51
+ }
@@ -0,0 +1,22 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+ import { useAtomValue } from "jotai";
3
+ import { useResolvedMarimoConfig } from "@/core/config/config";
4
+ import { useRequestClient } from "@/core/network/requests";
5
+ import { useAsyncData } from "@/hooks/useAsyncData";
6
+ import { packageDataVersionAtom } from "./package-data";
7
+
8
+ /** Load the available package views and refresh them after package mutations. */
9
+ export function usePackageDependencies() {
10
+ const packageDataVersion = useAtomValue(packageDataVersionAtom);
11
+ const [config] = useResolvedMarimoConfig();
12
+ const packageManager = config.package_management.manager;
13
+ const { getDependencyTree, getPackageList } = useRequestClient();
14
+
15
+ return useAsyncData(async () => {
16
+ const { context, tree } = await getDependencyTree();
17
+ // Both sandbox endpoints inspect the same environment; only fetch it once.
18
+ const list =
19
+ context.kind === "sandbox" ? [] : (await getPackageList()).packages;
20
+ return { list, context, tree };
21
+ }, [getDependencyTree, getPackageList, packageManager, packageDataVersion]);
22
+ }