@marimo-team/islands 0.24.3-dev5 → 0.24.3-dev52

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 (384) hide show
  1. package/dist/{ConnectedDataExplorerComponent-C9OF_75w.js → ConnectedDataExplorerComponent-KVhjDMSe.js} +1066 -1066
  2. package/dist/ErrorBoundary-Bfp74qVG.js +200 -0
  3. package/dist/{ImageComparisonComponent-2a9TMMm2.js → ImageComparisonComponent-Dlm5aWnH.js} +4 -4
  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-qtEE1_Zi.js +138 -0
  46. package/dist/{common-xHoep60C.js → common-BmP252R5.js} +8839 -12646
  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-DtaTjDfe.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-D_YQdd5e.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-CV4eSJ7N.js} +2143 -1910
  105. package/dist/{glide-utils-NShAPPtn.js → glide-utils-B3rUT5PI.js} +15 -15
  106. package/dist/globals-DdrpcXQY.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-DXWRr4gK.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-DwuC-61O.js → reveal-component-DLxDt6wQ.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/__tests__/fixtures/pair-preview.ts +19 -0
  187. package/src/__tests__/mount.test.ts +18 -0
  188. package/src/components/chat/acp/__tests__/agent-panel.test.tsx +270 -0
  189. package/src/components/chat/acp/agent-panel.tsx +45 -44
  190. package/src/components/data-table/column-explorer-panel/column-explorer.tsx +2 -6
  191. package/src/components/data-table/column-summary/chart-spec-model.tsx +1 -1
  192. package/src/components/data-table/columns.tsx +64 -67
  193. package/src/components/editor/RecoveryButton.tsx +1 -0
  194. package/src/components/editor/__tests__/dynamic-favicon.test.tsx +36 -2
  195. package/src/components/editor/actions/__tests__/pair-with-agent-commands.test.ts +115 -0
  196. package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +218 -0
  197. package/src/components/editor/actions/pair-with-agent-commands.ts +46 -9
  198. package/src/components/editor/actions/pair-with-agent-modal.tsx +49 -23
  199. package/src/components/editor/ai/add-cell-with-ai.tsx +1 -0
  200. package/src/components/editor/ai/ai-completion-editor.tsx +3 -1
  201. package/src/components/editor/alerts/connecting-alert.tsx +1 -66
  202. package/src/components/editor/alerts/connection-notice.tsx +140 -0
  203. package/src/components/editor/app-container.tsx +9 -3
  204. package/src/components/editor/boundary/ErrorBoundary.tsx +38 -16
  205. package/src/components/editor/boundary/__tests__/ErrorBoundary.test.tsx +311 -0
  206. package/src/components/editor/cell/code/__tests__/rtc-editor-config.test.ts +50 -0
  207. package/src/components/editor/cell/code/cell-editor.tsx +41 -42
  208. package/src/components/editor/chrome/components/__tests__/feedback-button.test.tsx +94 -0
  209. package/src/components/editor/chrome/components/feedback-button.tsx +63 -42
  210. package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +114 -7
  211. package/src/components/editor/chrome/panels/__tests__/sandbox-recovery.test.tsx +165 -0
  212. package/src/components/editor/chrome/panels/packages-panel.tsx +93 -166
  213. package/src/components/editor/chrome/panels/sandbox-controller.tsx +106 -0
  214. package/src/components/editor/chrome/panels/sandbox-panel.tsx +173 -0
  215. package/src/components/editor/chrome/wrapper/footer-items/backend-status.tsx +3 -1
  216. package/src/components/editor/code/__tests__/readonly-diff.test.tsx +65 -0
  217. package/src/components/editor/code/readonly-diff.tsx +1 -1
  218. package/src/components/editor/connections/database/__tests__/__snapshots__/as-code.test.ts.snap +98 -98
  219. package/src/components/editor/connections/database/__tests__/as-code.test.ts +11 -5
  220. package/src/components/editor/dynamic-favicon.tsx +17 -10
  221. package/src/components/editor/file-tree/__tests__/tree-actions.test.tsx +54 -0
  222. package/src/components/editor/file-tree/tree-actions.tsx +11 -14
  223. package/src/components/editor/header/__tests__/status.test.tsx +8 -20
  224. package/src/components/editor/header/filename-input.tsx +2 -0
  225. package/src/components/editor/header/status.tsx +5 -3
  226. package/src/components/editor/notebook-cell.tsx +4 -2
  227. package/src/components/editor/output/JsonOutput.tsx +182 -632
  228. package/src/components/editor/output/Outputs.css +50 -17
  229. package/src/components/editor/output/__tests__/JsonOutput-interactions.test.tsx +254 -0
  230. package/src/components/editor/output/__tests__/JsonOutput-mimetype.test.tsx +41 -46
  231. package/src/components/editor/output/__tests__/{json-output.test.ts → json-output-formatting.test.ts} +113 -75
  232. package/src/components/editor/output/__tests__/json-output-pagination.test.ts +252 -0
  233. package/src/components/editor/output/json-output/formatting.ts +167 -0
  234. package/src/components/editor/output/json-output/pagination.ts +155 -0
  235. package/src/components/editor/output/json-output/renderers.tsx +196 -0
  236. package/src/components/editor/output/json-output/themes.ts +52 -0
  237. package/src/components/editor/renderers/cell-array.tsx +1 -5
  238. package/src/components/home/components.tsx +17 -0
  239. package/src/components/pages/gallery-page.tsx +2 -4
  240. package/src/components/pages/home-page.tsx +2 -4
  241. package/src/components/slides/slide-form.tsx +1 -0
  242. package/src/components/slides/slide-notes-editor.tsx +1 -0
  243. package/src/components/terminal/terminal.tsx +4 -4
  244. package/src/components/tracing/tracing-spec.ts +1 -1
  245. package/src/components/ui/table.tsx +1 -1
  246. package/src/components/ui/use-toast.ts +8 -0
  247. package/src/core/MarimoApp.tsx +0 -2
  248. package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +259 -259
  249. package/src/core/cells/__tests__/cell.test.ts +61 -0
  250. package/src/core/cells/__tests__/cells.test.ts +37 -37
  251. package/src/core/cells/cell.ts +9 -1
  252. package/src/core/cells/pending-delete-service.ts +12 -16
  253. package/src/core/codemirror/language/__tests__/sql.test.ts +70 -2
  254. package/src/core/codemirror/language/languages/sql/sql.ts +16 -1
  255. package/src/core/codemirror/rtc/extension.ts +5 -2
  256. package/src/core/config/pair.ts +19 -0
  257. package/src/core/datasets/__tests__/data-source.test.ts +2 -0
  258. package/src/core/datasets/data-source-connections.ts +15 -10
  259. package/src/core/edit-app.tsx +8 -5
  260. package/src/core/errors/state.ts +1 -2
  261. package/src/core/islands/__tests__/parse.test.ts +140 -7
  262. package/src/core/islands/bootstrap.ts +1 -0
  263. package/src/core/islands/bridge.ts +3 -0
  264. package/src/core/islands/parse.ts +20 -0
  265. package/src/core/network/connection-notice.ts +84 -0
  266. package/src/core/network/requests-lazy.ts +3 -0
  267. package/src/core/network/requests-network.ts +12 -0
  268. package/src/core/network/requests-static.ts +3 -0
  269. package/src/core/network/requests-toasting.tsx +3 -0
  270. package/src/core/network/resolve.ts +2 -1
  271. package/src/core/network/types.ts +7 -0
  272. package/src/core/packages/__tests__/usePackageAction.test.tsx +129 -0
  273. package/src/core/packages/__tests__/useSandboxController.test.tsx +165 -0
  274. package/src/core/packages/package-data.ts +35 -0
  275. package/src/core/packages/sandbox-state.ts +15 -0
  276. package/src/core/packages/toast-components.tsx +11 -0
  277. package/src/core/packages/useInstallPackage.ts +1 -1
  278. package/src/core/packages/usePackageAction.ts +51 -0
  279. package/src/core/packages/usePackageDependencies.ts +22 -0
  280. package/src/core/packages/useSandboxController.ts +188 -0
  281. package/src/core/run-app.tsx +2 -0
  282. package/src/core/runtime/__tests__/runtime.test.ts +11 -0
  283. package/src/core/runtime/runtime.ts +7 -2
  284. package/src/core/wasm/bridge.ts +11 -0
  285. package/src/core/websocket/__tests__/useMarimoKernelConnection.hook.test.tsx +212 -2
  286. package/src/core/websocket/transports/transport.ts +1 -1
  287. package/src/core/websocket/transports/ws.ts +1 -1
  288. package/src/core/websocket/types.ts +11 -1
  289. package/src/core/websocket/useMarimoKernelConnection.tsx +43 -8
  290. package/src/css/app/Cell.css +2 -0
  291. package/src/hooks/useDebounce.ts +2 -0
  292. package/src/mount.tsx +3 -0
  293. package/src/plugins/core/BadPlugin.tsx +3 -14
  294. package/src/plugins/core/RenderHTML.tsx +22 -2
  295. package/src/plugins/core/__test__/RenderHTML.test.ts +187 -0
  296. package/src/plugins/core/registerReactComponent.tsx +0 -1
  297. package/src/plugins/impl/FormPlugin.tsx +2 -0
  298. package/src/plugins/impl/MatrixPlugin.tsx +2 -0
  299. package/src/plugins/impl/RangeSliderPlugin.tsx +2 -0
  300. package/src/plugins/impl/SliderPlugin.tsx +2 -0
  301. package/src/plugins/impl/data-editor/__tests__/glide-data-editor.test.tsx +23 -18
  302. package/src/plugins/impl/data-frames/panel.tsx +1 -0
  303. package/src/plugins/impl/image-comparison/ImageComparisonComponent.tsx +1 -1
  304. package/src/plugins/impl/image-comparison/__tests__/ImageComparisonComponent.test.tsx +15 -0
  305. package/src/plugins/layout/AccordionPlugin.tsx +7 -2
  306. package/src/plugins/layout/JsonOutputPlugin.tsx +7 -10
  307. package/src/plugins/layout/__test__/AccordionPlugin.test.tsx +66 -0
  308. package/src/stories/sandbox.stories.tsx +308 -0
  309. package/dist/ErrorBoundary-CR-ZQN_c.js +0 -125
  310. package/dist/_baseMerge-Dv8bBloN.js +0 -73
  311. package/dist/_initCloneObject---vediN2.js +0 -95
  312. package/dist/arc-DOVLrCPd.js +0 -82
  313. package/dist/architecture-TIHT7OUA-MiJpPpQW.js +0 -3
  314. package/dist/array-CApNbSU9.js +0 -6
  315. package/dist/assets/__vite-browser-external-BdKGjRZ4.js +0 -1
  316. package/dist/assets/worker-DlIBfp9S.js +0 -98
  317. package/dist/blockDiagram-VBNYF7ZC-Bc--I65C.js +0 -3158
  318. package/dist/chat-ui-DjTMZRVP.js +0 -8455
  319. package/dist/chunk-3NCLNEKW-BIp_lB_4.js +0 -14
  320. package/dist/chunk-5FCAYU7R-C_HO9A1J.js +0 -23
  321. package/dist/chunk-5HE753X5-Bixm_MV-.js +0 -38
  322. package/dist/chunk-5JV3BV7I-CH5J3iZY.js +0 -54
  323. package/dist/chunk-5TONJI2A-DG6gDiSS.js +0 -62
  324. package/dist/chunk-7BUUIJ7U-5afmIPeo.js +0 -82
  325. package/dist/chunk-BIQX33UG-7TMBUf89.js +0 -23
  326. package/dist/chunk-CQNSW5MT-Cnqqsxt-.js +0 -38
  327. package/dist/chunk-CYSBUYHQ-BAbymFHR.js +0 -23
  328. package/dist/chunk-EMLP6XTP-Bsz-QL77.js +0 -23
  329. package/dist/chunk-J7OUQ5F2-CM_meqZH.js +0 -57
  330. package/dist/chunk-JG7HCLWE-DUnCDgPk.js +0 -62
  331. package/dist/chunk-MOZMSUNE-BG6IHvzT.js +0 -35
  332. package/dist/chunk-OSBZ3O6U-CYwj_bvR.js +0 -23
  333. package/dist/chunk-QBLGF6JB-B4vJ4j8J.js +0 -23
  334. package/dist/chunk-QR6OTTB3-D4wH2ldi.js +0 -3738
  335. package/dist/chunk-R7FJI6CG-Blf8FcmZ.js +0 -64
  336. package/dist/chunk-RYQCIY6F-BWbIlBIH.js +0 -241
  337. package/dist/chunk-U6XO7XAA-COw445DS.js +0 -63
  338. package/dist/chunk-UBXNYLIW-BHigVU4H.js +0 -32
  339. package/dist/chunk-YOTPTUD7-Dn044Q_d.js +0 -30
  340. package/dist/chunk-ZIRB5QZD-BR4TMfOa.js +0 -1153
  341. package/dist/classDiagram-JCYQIIEL-DmXSLxe_.js +0 -30
  342. package/dist/classDiagram-v2-OCEON4UE-CooqVexA.js +0 -30
  343. package/dist/click-outside-container-D_URcm3D.js +0 -167
  344. package/dist/code-block-37QAKDTI-CQrzBOr-.js +0 -107
  345. package/dist/copy-CD7S58CY.js +0 -141
  346. package/dist/cynefin-VYW2F7L2-CeHAxOEX.js +0 -3
  347. package/dist/dagre-VZM6K2ZE-CJggWPTJ.js +0 -328
  348. package/dist/dist-3_o3T78I.js +0 -1360
  349. package/dist/dist-ABQjhuek.js +0 -64
  350. package/dist/dist-CO5R6VMi.js +0 -538
  351. package/dist/dropdown-menu-DhCfcscX.js +0 -5858
  352. package/dist/emotion-is-prop-valid.esm-M57VsT8j.js +0 -14
  353. package/dist/error-banner-CsXbEEjo.js +0 -611
  354. package/dist/eventmodeling-45OFAUF4-B_lWZ-Pd.js +0 -3
  355. package/dist/flowDiagram-UKHOOZJN-g1AmrZzM.js +0 -17
  356. package/dist/gitGraph-TEB2WS4Q-Dj-eXUph.js +0 -3
  357. package/dist/info-DKCQHKI2-Bhb-pe2e.js +0 -3
  358. package/dist/line-CuKjIQa1.js +0 -34
  359. package/dist/map-BJpOfPlL.js +0 -149
  360. package/dist/math-BXtKgYah.js +0 -10
  361. package/dist/mermaid-4DMBBIKO-Ddp9TgNp.js +0 -2
  362. package/dist/mermaid-Bi23Ibnw.js +0 -1173
  363. package/dist/mermaid-parser.core-Din9FvSi.js +0 -99
  364. package/dist/packet-7NZHBO7P-fbkIIyKE.js +0 -3
  365. package/dist/path-BNHuHZP8.js +0 -87
  366. package/dist/pie-RZYD4A2V-CLRIsxY1.js +0 -3
  367. package/dist/purify.es-CB-YO1Hn.js +0 -774
  368. package/dist/radar-I7S5WNFK-BGzCbgXu.js +0 -3
  369. package/dist/railroad-3IZDKUUU-CvQIIcuU.js +0 -3
  370. package/dist/railroad-abnf-AHOZXSZD-CMwtlNtu.js +0 -3
  371. package/dist/railroad-ebnf-EBAXGLYW-n1QUOAsh.js +0 -3
  372. package/dist/railroad-peg-LSFZ7HO6-PjnrpmCs.js +0 -3
  373. package/dist/schemas-DzK6U94N.js +0 -6057
  374. package/dist/stateDiagram-v2-UEYNNEHI-BTRUs8Il.js +0 -30
  375. package/dist/step-CKczO72Q.js +0 -670
  376. package/dist/swimlanesDiagram-ULZ7WXOC-NBmgBl9G.js +0 -31
  377. package/dist/timer-DpDyj9G0.js +0 -596
  378. package/dist/tooltip-Dt77sBeT.js +0 -390
  379. package/dist/treeView-QDETBFTQ-DU8LwHuf.js +0 -3
  380. package/dist/treemap-6X3UGDF4-DUk4-k_O.js +0 -3
  381. package/dist/useTheme-CVJ0UOf2.js +0 -841
  382. package/dist/vega-loader.browser-DEggZzni.js +0 -1083
  383. package/dist/wardley-OPB4EBWU-Db-AaGas.js +0 -3
  384. package/src/components/editor/output/EmotionCacheProvider.tsx +0 -32
@@ -1,6 +1,6 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
  import { historyField } from "@codemirror/commands";
3
- import { EditorState, StateEffect } from "@codemirror/state";
3
+ import { EditorState, type Extension, StateEffect } from "@codemirror/state";
4
4
  import { EditorView, ViewPlugin } from "@codemirror/view";
5
5
  import { useIntersectionObserver } from "@uidotdev/usehooks";
6
6
  import { useAtom, useAtomValue } from "jotai";
@@ -95,6 +95,21 @@ export interface CellEditorProps
95
95
  tooltipParentSelector?: string;
96
96
  }
97
97
 
98
+ export function composeRtcEditorConfig(
99
+ code: string,
100
+ extensions: Extension[],
101
+ rtc: ReturnType<typeof realTimeCollaboration> | undefined,
102
+ ): { code: string; extensions: Extension[] } {
103
+ if (!rtc) {
104
+ return { code, extensions };
105
+ }
106
+
107
+ return {
108
+ code: rtc.code,
109
+ extensions: [...extensions, rtc.extension],
110
+ };
111
+ }
112
+
98
113
  const CellEditorInternal = ({
99
114
  theme,
100
115
  showPlaceholder,
@@ -343,27 +358,31 @@ const CellEditorInternal = ({
343
358
  ]);
344
359
 
345
360
  const rtcEnabled = isRtcEnabled() && canUseRtc(cellId);
346
- const handleInitializeEditor = useEvent(() => {
347
- // If rtc is enabled, use collaborative editing
348
- if (rtcEnabled) {
349
- const rtc = realTimeCollaboration(
350
- cellId,
351
- (code) => {
352
- // It's not really a formatting change,
353
- // but this means it won't be marked as stale
354
- cellActions.updateCellCode({ cellId, code, formattingChange: true });
355
- },
356
- code,
357
- );
358
- extensions.push(rtc.extension);
359
- code = rtc.code;
361
+ const createEditorConfig = (initialCode?: string) => {
362
+ if (!rtcEnabled) {
363
+ return composeRtcEditorConfig(code, extensions, undefined);
360
364
  }
361
365
 
366
+ const rtc = realTimeCollaboration(
367
+ cellId,
368
+ (code) => {
369
+ // It's not really a formatting change,
370
+ // but this means it won't be marked as stale
371
+ cellActions.updateCellCode({ cellId, code, formattingChange: true });
372
+ },
373
+ initialCode,
374
+ );
375
+ return composeRtcEditorConfig(code, extensions, rtc);
376
+ };
377
+
378
+ const handleInitializeEditor = useEvent(() => {
379
+ const editorConfig = createEditorConfig(code);
380
+
362
381
  // Create a new editor
363
382
  const ev = new EditorView({
364
383
  state: EditorState.create({
365
- doc: code,
366
- extensions: extensions,
384
+ doc: editorConfig.code,
385
+ extensions: editorConfig.extensions,
367
386
  }),
368
387
  });
369
388
  setEditorView(ev);
@@ -377,19 +396,11 @@ const CellEditorInternal = ({
377
396
 
378
397
  const handleReconfigureEditor = useEvent(() => {
379
398
  invariant(editorViewRef.current !== null, "Editor view is not initialized");
380
- // If rtc is enabled, use collaborative editing
381
- if (rtcEnabled) {
382
- const rtc = realTimeCollaboration(cellId, (code) => {
383
- // It's not really a formatting change,
384
- // but this means it won't be marked as stale
385
- cellActions.updateCellCode({ cellId, code, formattingChange: true });
386
- });
387
- extensions.push(rtc.extension);
388
- }
399
+ const editorConfig = createEditorConfig();
389
400
 
390
401
  editorViewRef.current.dispatch({
391
402
  effects: [
392
- StateEffect.reconfigure.of([extensions]),
403
+ StateEffect.reconfigure.of([editorConfig.extensions]),
393
404
  reconfigureLanguageEffect(editorViewRef.current, {
394
405
  completionConfig: userConfig.completion,
395
406
  hotkeysProvider: new OverridingHotkeyProvider(
@@ -406,26 +417,14 @@ const CellEditorInternal = ({
406
417
 
407
418
  const handleDeserializeEditor = useEvent(() => {
408
419
  invariant(serializedEditorState, "Editor view is not initialized");
409
- if (rtcEnabled) {
410
- const rtc = realTimeCollaboration(
411
- cellId,
412
- (code) => {
413
- // It's not really a formatting change,
414
- // but this means it won't be marked as stale
415
- cellActions.updateCellCode({ cellId, code, formattingChange: true });
416
- },
417
- code,
418
- );
419
- extensions.push(rtc.extension);
420
- code = rtc.code;
421
- }
420
+ const editorConfig = createEditorConfig(code);
422
421
 
423
422
  const ev = new EditorView({
424
423
  state: EditorState.fromJSON(
425
424
  serializedEditorState,
426
425
  {
427
- doc: code,
428
- extensions: extensions,
426
+ doc: editorConfig.code,
427
+ extensions: editorConfig.extensions,
429
428
  },
430
429
  { history: historyField },
431
430
  ),
@@ -208,4 +208,98 @@ describe("FeedbackModal issue reporting", () => {
208
208
  expect(href).toContain(encodeURIComponent('"marimo": "1.2.3"'));
209
209
  });
210
210
  });
211
+
212
+ it("shows partial environment details when the request client is missing", async () => {
213
+ store.set(requestClientAtom, null);
214
+ render(<FeedbackModal onClose={vi.fn()} />, { wrapper });
215
+
216
+ await screen.findByText("Environment details");
217
+ expect(
218
+ screen.getByText("Server environment information unavailable"),
219
+ ).toBeInTheDocument();
220
+ expect(
221
+ screen.queryByText("Loading environment details…"),
222
+ ).not.toBeInTheDocument();
223
+ expect(
224
+ screen.queryByRole("button", { name: "Retry" }),
225
+ ).not.toBeInTheDocument();
226
+
227
+ const link = screen.getByRole("link", { name: "Open GitHub issue" });
228
+ const href = link.getAttribute("href") ?? "";
229
+ expect(href).toContain("&env=");
230
+ expect(href).toContain(encodeURIComponent("Environment Collection Error"));
231
+
232
+ fireEvent.click(
233
+ screen.getByRole("button", { name: "Copy environment JSON" }),
234
+ );
235
+ await waitFor(() =>
236
+ expect(copyModule.copyToClipboard).toHaveBeenCalledWith(
237
+ expect.stringContaining("Environment Collection Error"),
238
+ ),
239
+ );
240
+ });
241
+
242
+ it("disables source collection when the request client is missing", async () => {
243
+ localStorage.setItem(
244
+ "marimo:issue-report:include-code",
245
+ JSON.stringify(true),
246
+ );
247
+ store.set(connectionAtom, { state: WebSocketState.CONNECTING });
248
+ store.set(requestClientAtom, null);
249
+ render(<FeedbackModal onClose={vi.fn()} />, { wrapper });
250
+
251
+ await screen.findByText("Environment details");
252
+ expect(
253
+ screen.getByRole("checkbox", { name: "Include notebook code" }),
254
+ ).toBeDisabled();
255
+ expect(
256
+ screen.getByText("Notebook source is unavailable."),
257
+ ).toBeInTheDocument();
258
+ expect(
259
+ screen.queryByText("Connect the notebook to include its source."),
260
+ ).not.toBeInTheDocument();
261
+ const link = screen.getByRole("link", { name: "Open GitHub issue" });
262
+ expect(link.getAttribute("href") ?? "").not.toContain("reproduction-code=");
263
+ });
264
+
265
+ it("preserves partial diagnostics when the environment request throws", async () => {
266
+ store.set(
267
+ requestClientAtom,
268
+ MockRequestClient.create({
269
+ getEnvironmentInfo: vi.fn().mockImplementation(() => {
270
+ throw new Error("offline");
271
+ }),
272
+ }),
273
+ );
274
+ render(<FeedbackModal onClose={vi.fn()} />, { wrapper });
275
+
276
+ await screen.findByText("Server environment information unavailable");
277
+ expect(screen.getByRole("button", { name: "Retry" })).toBeInTheDocument();
278
+ expect(
279
+ screen.getByText(/Environment Collection Error/),
280
+ ).toBeInTheDocument();
281
+ });
282
+
283
+ it("replaces partial diagnostics after a successful environment retry", async () => {
284
+ const getEnvironmentInfo = vi
285
+ .fn()
286
+ .mockRejectedValueOnce(new Error("offline"))
287
+ .mockResolvedValueOnce(environment);
288
+ store.set(
289
+ requestClientAtom,
290
+ MockRequestClient.create({ getEnvironmentInfo }),
291
+ );
292
+ render(<FeedbackModal onClose={vi.fn()} />, { wrapper });
293
+
294
+ await screen.findByText("Server environment information unavailable");
295
+ fireEvent.click(screen.getByRole("button", { name: "Retry" }));
296
+
297
+ await screen.findByText(/"marimo": "1.2.3"/);
298
+ expect(
299
+ screen.queryByText("Server environment information unavailable"),
300
+ ).not.toBeInTheDocument();
301
+ expect(
302
+ screen.queryByRole("button", { name: "Retry" }),
303
+ ).not.toBeInTheDocument();
304
+ });
211
305
  });
@@ -6,7 +6,7 @@ const Slot = SlotPrimitive.Slot;
6
6
 
7
7
  import { useAtomValue } from "jotai";
8
8
  import { ExternalLinkIcon, TriangleAlertIcon } from "lucide-react";
9
- import React, { type PropsWithChildren, useMemo, useState } from "react";
9
+ import React, { type PropsWithChildren, useState } from "react";
10
10
  import { CopyClipboardIcon } from "@/components/icons/copy-icon";
11
11
  import { useImperativeModal } from "@/components/modal/ImperativeModal";
12
12
  import { Button } from "@/components/ui/button";
@@ -36,7 +36,7 @@ import {
36
36
  import { useNotebookCodeAvailable } from "@/core/meta/code-visibility";
37
37
  import { getMarimoVersion } from "@/core/meta/globals";
38
38
  import { connectionAtom } from "@/core/network/connection";
39
- import { useRequestClient } from "@/core/network/requests";
39
+ import { requestClientAtom } from "@/core/network/requests";
40
40
  import { filenameAtom } from "@/core/saving/file-state";
41
41
  import { store } from "@/core/state/jotai";
42
42
  import { WebSocketState } from "@/core/websocket/types";
@@ -74,6 +74,27 @@ const CollapsiblePreview: React.FC<{ content: string }> = ({ content }) => {
74
74
  );
75
75
  };
76
76
 
77
+ function getNotebookSourceUnavailableReason(args: {
78
+ hasRequestClient: boolean;
79
+ filename: string | null;
80
+ codeAvailable: boolean;
81
+ connectionState: WebSocketState;
82
+ }): string | undefined {
83
+ if (!args.hasRequestClient) {
84
+ return "Notebook source is unavailable.";
85
+ }
86
+ if (args.filename === null) {
87
+ return "Save the notebook first.";
88
+ }
89
+ if (!args.codeAvailable) {
90
+ return "Notebook source is hidden in this view.";
91
+ }
92
+ if (args.connectionState !== WebSocketState.OPEN) {
93
+ return "Connect the notebook to include its source.";
94
+ }
95
+ return undefined;
96
+ }
97
+
77
98
  export const FeedbackButton: React.FC<PropsWithChildren> = ({ children }) => {
78
99
  const { openModal, closeModal } = useImperativeModal();
79
100
 
@@ -85,17 +106,18 @@ export const FeedbackButton: React.FC<PropsWithChildren> = ({ children }) => {
85
106
  };
86
107
 
87
108
  export const FeedbackModal: React.FC<{
88
- onClose: () => void;
109
+ onClose?: () => void;
89
110
  }> = () => {
90
- const { getEnvironmentInfo, readCode } = useRequestClient();
91
- const environmentRequest = useAsyncData(
92
- async () => getEnvironmentInfo(),
93
- [getEnvironmentInfo],
94
- );
111
+ const requestClient = useAtomValue(requestClientAtom);
112
+ const environmentRequest = useAsyncData(async () => {
113
+ if (requestClient == null) {
114
+ return undefined;
115
+ }
116
+ return requestClient.getEnvironmentInfo();
117
+ }, [requestClient]);
95
118
 
96
119
  const notebook = useAtomValue(notebookAtom);
97
- // biome-ignore lint/correctness/useExhaustiveDependencies: recompute when the notebook changes
98
- const errors = useMemo(() => getCellErrorEntries(store), [notebook]);
120
+ const errors = getCellErrorEntries(store);
99
121
 
100
122
  const cells = notebook.cellIds.inOrderIds.map(
101
123
  (cellId) => notebook.cellData[cellId],
@@ -103,18 +125,13 @@ export const FeedbackModal: React.FC<{
103
125
  const codeAvailable = useNotebookCodeAvailable(cells);
104
126
  const filename = useAtomValue(filenameAtom);
105
127
  const connection = useAtomValue(connectionAtom);
106
- const notebookSourceAvailable =
107
- filename !== null &&
108
- codeAvailable &&
109
- connection.state === WebSocketState.OPEN;
110
-
111
- const notebookSourceReason = notebookSourceAvailable
112
- ? undefined
113
- : filename === null
114
- ? "Save the notebook first."
115
- : !codeAvailable
116
- ? "Notebook source is hidden in this view."
117
- : "Connect the notebook to include its source.";
128
+ const notebookSourceReason = getNotebookSourceUnavailableReason({
129
+ hasRequestClient: requestClient != null,
130
+ filename,
131
+ codeAvailable,
132
+ connectionState: connection.state,
133
+ });
134
+ const notebookSourceAvailable = notebookSourceReason === undefined;
118
135
 
119
136
  const [includeErrors, setIncludeErrors] = useLocalStorage(
120
137
  "marimo:issue-report:include-errors",
@@ -128,7 +145,7 @@ export const FeedbackModal: React.FC<{
128
145
  const environment: EnvironmentDiagnostics | undefined =
129
146
  environmentRequest.data
130
147
  ? enrichEnvironment(environmentRequest.data, navigator.userAgent)
131
- : environmentRequest.status === "error"
148
+ : environmentRequest.status === "error" || requestClient == null
132
149
  ? createPartialEnvironment(
133
150
  getMarimoVersion(),
134
151
  navigator.userAgent,
@@ -138,17 +155,16 @@ export const FeedbackModal: React.FC<{
138
155
  : undefined;
139
156
 
140
157
  const codeRequest = useAsyncData(async () => {
141
- if (!includeCode || !notebookSourceAvailable) {
158
+ if (!includeCode || !notebookSourceAvailable || requestClient == null) {
142
159
  return undefined;
143
160
  }
144
- const { contents } = await readCode();
161
+ const { contents } = await requestClient.readCode();
145
162
  return contents;
146
- }, [includeCode, notebookSourceAvailable, readCode]);
163
+ }, [includeCode, notebookSourceAvailable, requestClient]);
147
164
 
148
- const { url: githubIssueUrl, omitted } = useMemo(() => {
149
- if (!environment) {
150
- return { url: Constants.bugReportUrl, omitted: [] as string[] };
151
- }
165
+ let githubIssueUrl = Constants.bugReportUrl;
166
+ let omitted: string[] = [];
167
+ if (environment) {
152
168
  const fields: Record<string, string> = {
153
169
  env: formatEnvironmentSection(environment),
154
170
  };
@@ -158,8 +174,11 @@ export const FeedbackModal: React.FC<{
158
174
  if (includeCode && codeRequest.data) {
159
175
  fields["reproduction-code"] = formatCodeSection(codeRequest.data);
160
176
  }
161
- return buildBugReportUrl(Constants.bugReportUrl, fields);
162
- }, [environment, errors, includeErrors, includeCode, codeRequest.data]);
177
+ ({ url: githubIssueUrl, omitted } = buildBugReportUrl(
178
+ Constants.bugReportUrl,
179
+ fields,
180
+ ));
181
+ }
163
182
 
164
183
  const omittedLabels = omitted
165
184
  .map((field) =>
@@ -254,7 +273,7 @@ export const FeedbackModal: React.FC<{
254
273
  )}
255
274
  </div>
256
275
 
257
- {environmentRequest.status === "pending" && (
276
+ {environmentRequest.status === "pending" && requestClient != null && (
258
277
  <div className="flex flex-col gap-2">
259
278
  <span className="text-sm text-muted-foreground">
260
279
  Loading environment details…
@@ -265,18 +284,20 @@ export const FeedbackModal: React.FC<{
265
284
  </div>
266
285
  )}
267
286
 
268
- {environmentRequest.status === "error" && (
287
+ {(environmentRequest.status === "error" || requestClient == null) && (
269
288
  <div className="flex items-center gap-2 text-sm">
270
289
  <TriangleAlertIcon className="w-4 h-4 text-(--yellow-11) shrink-0" />
271
290
  <span>Server environment information unavailable</span>
272
- <Button
273
- type="button"
274
- variant="link"
275
- size="xs"
276
- onClick={() => environmentRequest.refetch()}
277
- >
278
- Retry
279
- </Button>
291
+ {requestClient != null && (
292
+ <Button
293
+ type="button"
294
+ variant="link"
295
+ size="xs"
296
+ onClick={() => environmentRequest.refetch()}
297
+ >
298
+ Retry
299
+ </Button>
300
+ )}
280
301
  </div>
281
302
  )}
282
303
 
@@ -1,6 +1,13 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
- import { fireEvent, render, screen } from "@testing-library/react";
3
+ import {
4
+ act,
5
+ fireEvent,
6
+ render,
7
+ screen,
8
+ waitFor,
9
+ within,
10
+ } from "@testing-library/react";
4
11
  import { Provider } from "jotai";
5
12
  import { beforeEach, describe, expect, it, vi } from "vitest";
6
13
  import { MockRequestClient } from "@/__mocks__/requests";
@@ -10,6 +17,9 @@ import type {
10
17
  DependencyTreeNode,
11
18
  DependencyTreeResponse,
12
19
  } from "@/core/network/types";
20
+ import { withPackageInvalidation } from "@/core/packages/package-data";
21
+ import { useInstallPackages } from "@/core/packages/useInstallPackage";
22
+ import { sandboxAtom, sandboxSyncAtom } from "@/core/packages/sandbox-state";
13
23
  import { store } from "@/core/state/jotai";
14
24
  import PackagesPanel from "../packages-panel";
15
25
 
@@ -51,16 +61,22 @@ function renderPanel(
51
61
  context: DependencyTreeResponse["context"],
52
62
  tree: DependencyTreeNode = emptyTree,
53
63
  ) {
54
- const getPackageList = vi.fn().mockResolvedValue({ packages: [] });
55
64
  store.set(
56
- requestClientAtom,
57
- MockRequestClient.create({
58
- getPackageList,
59
- getDependencyTree: vi.fn().mockResolvedValue({ tree, context }),
60
- }),
65
+ sandboxAtom,
66
+ context.kind === "sandbox"
67
+ ? { backend: context.backend, manifest: "", filename: "notebook.py" }
68
+ : null,
61
69
  );
70
+ store.set(sandboxSyncAtom, { pending: false, error: null });
71
+ const getPackageList = vi.fn().mockResolvedValue({ packages: [] });
72
+ const client = MockRequestClient.create({
73
+ getPackageList,
74
+ getDependencyTree: vi.fn().mockResolvedValue({ tree, context }),
75
+ });
76
+ store.set(requestClientAtom, client);
62
77
 
63
78
  return {
79
+ client,
64
80
  getPackageList,
65
81
  ...render(
66
82
  <Provider store={store}>
@@ -141,3 +157,94 @@ describe("PackagesPanel", () => {
141
157
  expect(getPackageList).toHaveBeenCalledOnce();
142
158
  });
143
159
  });
160
+
161
+ it("refreshes an open panel when a package is installed elsewhere, after installation succeeds", async () => {
162
+ const { promise, resolve } = Promise.withResolvers<{ success: boolean }>();
163
+ const client = MockRequestClient.create({
164
+ addPackage: vi.fn(() => promise),
165
+ getDependencyTree: vi
166
+ .fn()
167
+ .mockResolvedValueOnce({
168
+ tree: emptyTree,
169
+ context: { kind: "sandbox", backend: "uv" },
170
+ })
171
+ .mockResolvedValue({
172
+ tree: populatedTree,
173
+ context: { kind: "sandbox", backend: "uv" },
174
+ }),
175
+ });
176
+ store.set(sandboxAtom, {
177
+ backend: "uv",
178
+ manifest: "",
179
+ filename: "notebook.py",
180
+ });
181
+ store.set(sandboxSyncAtom, { pending: false, error: null });
182
+ store.set(requestClientAtom, withPackageInvalidation(client));
183
+ function InstallElsewhere() {
184
+ const { handleInstallPackages } = useInstallPackages();
185
+ return (
186
+ <button onClick={() => handleInstallPackages(["polars"])}>
187
+ Install from another panel
188
+ </button>
189
+ );
190
+ }
191
+ render(
192
+ <Provider store={store}>
193
+ <TooltipProvider>
194
+ <PackagesPanel />
195
+ <InstallElsewhere />
196
+ </TooltipProvider>
197
+ </Provider>,
198
+ );
199
+ await screen.findByPlaceholderText("Add packages to uv sandbox...");
200
+ fireEvent.click(
201
+ screen.getByRole("button", { name: "Install from another panel" }),
202
+ );
203
+ expect(client.getDependencyTree).toHaveBeenCalledTimes(1);
204
+ await act(async () => resolve({ success: true }));
205
+ await waitFor(() =>
206
+ expect(client.getDependencyTree).toHaveBeenCalledTimes(2),
207
+ );
208
+ expect(await screen.findByText("polars")).toBeInTheDocument();
209
+ });
210
+
211
+ it.each(["uv", "pixi", "list"] as const)(
212
+ "allows upgrading marimo but not removing it in the %s view",
213
+ async (view) => {
214
+ const tree = {
215
+ ...populatedTree,
216
+ dependencies: [
217
+ ...populatedTree.dependencies,
218
+ { name: "marimo", version: "0.24.0", tags: [], dependencies: [] },
219
+ ],
220
+ };
221
+ const { client, getPackageList } = renderPanel(
222
+ view === "list"
223
+ ? { kind: "package-manager", name: "pip" }
224
+ : { kind: "sandbox", backend: view },
225
+ tree,
226
+ );
227
+ getPackageList.mockResolvedValue({ packages: tree.dependencies });
228
+ vi.mocked(client.addPackage).mockResolvedValue({ success: true });
229
+ if (view === "list") {
230
+ fireEvent.click(await screen.findByRole("button", { name: "List" }));
231
+ }
232
+ const role = view === "list" ? "row" : "treeitem";
233
+ const marimo = within(await screen.findByRole(role, { name: /marimo/ }));
234
+ expect(marimo.getByText(/0\.24\.0/)).toBeInTheDocument();
235
+ expect(
236
+ marimo.queryByRole("button", { name: "Remove" }),
237
+ ).not.toBeInTheDocument();
238
+ const polars = within(screen.getByRole(role, { name: /polars/ }));
239
+ expect(polars.getByRole("button", { name: "Remove" })).toBeInTheDocument();
240
+ fireEvent.click(marimo.getByRole("button", { name: "Upgrade" }));
241
+ await waitFor(() =>
242
+ expect(client.addPackage).toHaveBeenCalledWith({
243
+ package: "marimo",
244
+ upgrade: true,
245
+ group: undefined,
246
+ }),
247
+ );
248
+ expect(client.removePackage).not.toHaveBeenCalled();
249
+ },
250
+ );