@marimo-team/islands 0.19.12-dev1 → 0.20.0

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 (202) hide show
  1. package/dist/Combination-Du-o_hC9.js +11897 -0
  2. package/dist/{ConnectedDataExplorerComponent-CkXO-pKy.js → ConnectedDataExplorerComponent-BMiGWK57.js} +19 -18
  3. package/dist/{_baseIsEqual-CBSjxu-D.js → _baseIsEqual-DN5YkPnl.js} +1 -1
  4. package/dist/{_baseProperty-BVGrW_NZ.js → _baseProperty-6juuyX7Z.js} +5 -5
  5. package/dist/{_baseUniq-4lqa8rDi.js → _baseUniq-BlF21ach.js} +1 -1
  6. package/dist/{any-language-editor-t_VsTNa-.js → any-language-editor-1OMbohwD.js} +19 -19
  7. package/dist/architecture-U656AL7Q-Jd2CvPgJ.js +6 -0
  8. package/dist/{architectureDiagram-VXUJARFQ-DmJQhcJb.js → architectureDiagram-VXUJARFQ-DhN0C3Xf.js} +15 -15
  9. package/dist/{blockDiagram-VD42YOAC-CRofISJs.js → blockDiagram-VD42YOAC-DrBkIcbV.js} +7 -7
  10. package/dist/{button-Cy0ElmIm.js → button-KYalaJYu.js} +783 -200
  11. package/dist/{c4Diagram-YG6GDRKO-Deqoag4I.js → c4Diagram-YG6GDRKO-pWt4zmu0.js} +4 -4
  12. package/dist/{channel-CMsnebrL.js → channel-C56Jz8EL.js} +1 -1
  13. package/dist/{check-DkNR52Mm.js → check-C50jsehH.js} +1 -1
  14. package/dist/{chunk-76Q3JFCE-jPuajZH_.js → chunk-76Q3JFCE-CQ6a2yGJ.js} +1 -1
  15. package/dist/{chunk-ABZYJK2D-BGWvKte3.js → chunk-ABZYJK2D-BwNsaa1P.js} +1 -1
  16. package/dist/{chunk-ATLVNIR6-BYZB6C5T.js → chunk-ATLVNIR6-DtFMAawc.js} +1 -1
  17. package/dist/{chunk-B4BG7PRW-CwYUp6Uj.js → chunk-B4BG7PRW-lfWcLlzS.js} +4 -4
  18. package/dist/{chunk-DI55MBZ5-Gyro6dvN.js → chunk-DI55MBZ5-RhhAimfG.js} +4 -4
  19. package/dist/{chunk-EXTU4WIE-BlA7aWEw.js → chunk-EXTU4WIE-Bmo660a9.js} +1 -1
  20. package/dist/{chunk-FPAJGGOC-CduL34ft.js → chunk-FPAJGGOC-quWdfNUB.js} +7 -7
  21. package/dist/{chunk-FWNWRKHM-C-2TI4gt.js → chunk-FWNWRKHM-DmrwhAQr.js} +1 -1
  22. package/dist/{chunk-JA3XYJ7Z-Cm-pccR-.js → chunk-JA3XYJ7Z-n8UTzfok.js} +2 -2
  23. package/dist/{chunk-JZLCHNYA-CoLqqXMe.js → chunk-JZLCHNYA-ChKqHUdB.js} +4 -4
  24. package/dist/{chunk-LBM3YZW2-DWgQiioW.js → chunk-LBM3YZW2-BkxsqkNK.js} +1 -1
  25. package/dist/{chunk-LHMN2FUI-Dj_AHSvI.js → chunk-LHMN2FUI-CgYPnxfN.js} +1 -1
  26. package/dist/{chunk-N4CR4FBY-ByLbY9L-.js → chunk-N4CR4FBY-BxOHGL3P.js} +5 -5
  27. package/dist/{chunk-O7ZBX7Z2-CRZ8i1rP.js → chunk-O7ZBX7Z2-CdpLwOP0.js} +1 -1
  28. package/dist/{chunk-QN33PNHL-eodIiY9F.js → chunk-QN33PNHL-Dda-55xY.js} +1 -1
  29. package/dist/{chunk-QXUST7PY-CuJlDW6A.js → chunk-QXUST7PY-ClIEpoCT.js} +5 -5
  30. package/dist/{chunk-S3R3BYOJ-CwDGYMVf.js → chunk-S3R3BYOJ-C_1SJcWo.js} +3 -3
  31. package/dist/{chunk-S6J4BHB3-DNVMr0_v.js → chunk-S6J4BHB3-zfWjyfUg.js} +1 -1
  32. package/dist/{chunk-T53DSG4Q-BFWkpOX5.js → chunk-T53DSG4Q-Bewz1tiU.js} +1 -1
  33. package/dist/{chunk-TZMSLE5B-DFDr5FCr.js → chunk-TZMSLE5B-XW3duOft.js} +1 -1
  34. package/dist/{classDiagram-2ON5EDUG-BDBX9etk.js → classDiagram-2ON5EDUG-CUJlS_eo.js} +10 -10
  35. package/dist/{classDiagram-v2-WZHVMYZB-Bzj_L_BF.js → classDiagram-v2-WZHVMYZB-BhSPpbkE.js} +10 -10
  36. package/dist/{clone-Cc_6PW77.js → clone-V9hndNcj.js} +1 -1
  37. package/dist/{constants-DrOu5vvd.js → constants-BGRTDzdW.js} +2 -2
  38. package/dist/{copy-DRaXIb_a.js → copy-oc-FcZzt.js} +2 -2
  39. package/dist/{dagre-6UL2VRFP-r2rSdJYL.js → dagre-6UL2VRFP-BArPH353.js} +11 -11
  40. package/dist/{dagre-D2F8UdM6.js → dagre-Dcgyn_Uy.js} +15 -15
  41. package/dist/{diagram-PSM6KHXK-BpxVUe9U.js → diagram-PSM6KHXK-B1xAkr9y.js} +16 -16
  42. package/dist/{diagram-QEK2KX5R-q3dHUcp6.js → diagram-QEK2KX5R-CaoqwzPb.js} +14 -14
  43. package/dist/{diagram-S2PKOQOG-MDBKrxSC.js → diagram-S2PKOQOG-NXCsFLvR.js} +14 -14
  44. package/dist/dist-B8Y11RWn.js +1381 -0
  45. package/dist/dist-BA-HK7pI.js +5 -0
  46. package/dist/dist-BD5GU948.js +5 -0
  47. package/dist/{dist-BfactX3G.js → dist-BGzkWRSl.js} +4 -4
  48. package/dist/dist-BIYmAsND.js +5 -0
  49. package/dist/{dist-CmZYrgd_.js → dist-BUEi7EKT.js} +1 -1
  50. package/dist/{dist-B94MxrQS.js → dist-B_i29Q6L.js} +2 -2
  51. package/dist/dist-BcKTJXJi.js +5 -0
  52. package/dist/dist-BgnrtcWg.js +8 -0
  53. package/dist/{dist-glA_fIK_.js → dist-BoagoQQw.js} +2 -2
  54. package/dist/{dist-C2-m5aEk.js → dist-BswsDM4k.js} +2 -2
  55. package/dist/dist-C1njTlBq.js +5 -0
  56. package/dist/{dist-B2-r9y-0.js → dist-C5QB1NtD.js} +3 -3
  57. package/dist/{dist-Crk9ejOy.js → dist-CD7uLx0M.js} +2 -2
  58. package/dist/{dist-B4tYJP_i.js → dist-CMOy93xY.js} +2 -2
  59. package/dist/dist-CSKHwJYH.js +5 -0
  60. package/dist/dist-CSKKyiIq.js +5 -0
  61. package/dist/{dist-iiugPhCC.js → dist-C_9IMrtt.js} +1 -1
  62. package/dist/{dist-CE43BRmt.js → dist-Cb3iqED3.js} +1 -1
  63. package/dist/{dist-Dit9tk8a.js → dist-CoZ8kKKW.js} +1 -1
  64. package/dist/{dist-B5ATpkxy.js → dist-CrAYcS_4.js} +2 -2
  65. package/dist/dist-CrQ_pOuK.js +6 -0
  66. package/dist/dist-Cskx1daf.js +5 -0
  67. package/dist/dist-D4i0Ef34.js +8 -0
  68. package/dist/{dist-T4g7Sr6e.js → dist-D8EhXZ4S.js} +3 -3
  69. package/dist/{dist-CJrHMxlI.js → dist-DOLQQtWK.js} +3 -3
  70. package/dist/dist-DOcn61TX.js +8 -0
  71. package/dist/{dist-DqJdzAYM.js → dist-Dmr_nXF6.js} +2 -2
  72. package/dist/{dist-yVJ4xE5n.js → dist-DpAbrLuF.js} +5 -5
  73. package/dist/{dist-CcOGT46m.js → dist-DrC0QKFK.js} +1 -1
  74. package/dist/{dist-BYmtF1W6.js → dist-Dv2Phbh5.js} +2 -2
  75. package/dist/dist-DwMejAPB.js +6 -0
  76. package/dist/dist-DzSe1wby.js +8 -0
  77. package/dist/{dist-BbBnU4tG.js → dist-EZFqUJhh.js} +1 -1
  78. package/dist/{dist-Cgf353Ki.js → dist-Ey9hP8-j.js} +1 -1
  79. package/dist/{dist-BLwfpZD-.js → dist-IlWGXVjO.js} +2 -2
  80. package/dist/{dist-DOil6y-3.js → dist-LNp8svLl.js} +4 -4
  81. package/dist/{dist-CPTE45iS.js → dist-W6TdeACj.js} +1 -1
  82. package/dist/{dist-Dc1SFk5I.js → dist-a6Obzr07.js} +2 -2
  83. package/dist/{dist-Bsv_ARko.js → dist-bz6WguLy.js} +2 -2
  84. package/dist/{dist-CC9VUnXd.js → dist-iDeoXzdN.js} +1 -1
  85. package/dist/{dist-BoAHOW2l.js → dist-iyBCcLRa.js} +2 -2
  86. package/dist/{dist-CkEUrAus.js → dist-xCB683Dh.js} +2 -2
  87. package/dist/{erDiagram-Q2GNP2WA-CX1XdqVD.js → erDiagram-Q2GNP2WA-DWCa11g5.js} +10 -10
  88. package/dist/error-banner-vCG-EbUQ.js +619 -0
  89. package/dist/{esm-BAS2d2Ad.js → esm-DZSk8vt3.js} +27 -27
  90. package/dist/{flatten-eGRGXrC3.js → flatten-CWZjF1fc.js} +1 -1
  91. package/dist/{flowDiagram-NV44I4VS-BCj-ONTw.js → flowDiagram-NV44I4VS-BQ5PQs4L.js} +10 -10
  92. package/dist/{ganttDiagram-JELNMOA3-D1l5ewiQ.js → ganttDiagram-JELNMOA3-NTOuNWeT.js} +3 -3
  93. package/dist/{gitGraph-F6HP7TQM-CDM3aU-T.js → gitGraph-F6HP7TQM-DfRNsaDw.js} +3 -3
  94. package/dist/{gitGraphDiagram-NY62KEGX-KdZh0iiW.js → gitGraphDiagram-NY62KEGX-CYke62Ot.js} +13 -13
  95. package/dist/{glide-data-editor-2RvcPqmc.js → glide-data-editor-DttqGjrT.js} +571 -572
  96. package/dist/{graphlib-7UgfJadv.js → graphlib-CwMnCnQ9.js} +8 -8
  97. package/dist/{info-NVLQJR56-CoL1x1Fy.js → info-NVLQJR56-CUaoPtis.js} +3 -3
  98. package/dist/{infoDiagram-WHAUD3N6-PSH7lQ0D.js → infoDiagram-WHAUD3N6-B42WjAPh.js} +13 -13
  99. package/dist/{isEmpty-DQXRKNtW.js → isEmpty-6z2uv6gM.js} +2 -2
  100. package/dist/{isString-Clqvtgmo.js → isString-D6abkXrl.js} +1 -1
  101. package/dist/{isSymbol-TWXhTa8k.js → isSymbol-hk7foJ70.js} +1 -1
  102. package/dist/{journeyDiagram-XKPGCS4Q-BrTAxQ1J.js → journeyDiagram-XKPGCS4Q-ahXD97kr.js} +3 -3
  103. package/dist/{kanban-definition-3W4ZIXB7-BoYCDp_9.js → kanban-definition-3W4ZIXB7-CiTIpnhy.js} +7 -7
  104. package/dist/{label-CxU5JNBW.js → label-Cc5tEavt.js} +250 -250
  105. package/dist/{loader-C0-eIoas.js → loader-Cob3XFOw.js} +2 -2
  106. package/dist/main.js +1791 -1056
  107. package/dist/{memoize-Bag7B41I.js → memoize-Ckyqzyu_.js} +1 -1
  108. package/dist/{merge-Dl1bfxsj.js → merge-Db4Uulx4.js} +1 -1
  109. package/dist/{mermaid-C2cSe5YL.js → mermaid-B5xl_2hx.js} +73 -62
  110. package/dist/{mermaid-parser.core-D20zFbMa.js → mermaid-parser.core-BXj7Il0J.js} +8 -8
  111. package/dist/{min-Bg4bqmiD.js → min-ypdVXicC.js} +4 -4
  112. package/dist/{mindmap-definition-VGOIOE7T-CmRjsKEt.js → mindmap-definition-VGOIOE7T-Mni766A_.js} +9 -9
  113. package/dist/{now-mivqkCIv.js → now-Dwu5ou19.js} +2 -2
  114. package/dist/{once-BqS42WgZ.js → once-C9dA9qgQ.js} +1 -1
  115. package/dist/{packet-BFZMPI3H-C6aZmgV-.js → packet-BFZMPI3H-DHtQCusE.js} +3 -3
  116. package/dist/{pie-7BOR55EZ-NB6xYwcB.js → pie-7BOR55EZ-2sVLYbpR.js} +3 -3
  117. package/dist/{pieDiagram-ADFJNKIX-CtxQlnsU.js → pieDiagram-ADFJNKIX-PbXpgT8_.js} +14 -14
  118. package/dist/{quadrantDiagram-AYHSOK5B-DllnB2Hl.js → quadrantDiagram-AYHSOK5B-BtXGnx8i.js} +2 -2
  119. package/dist/{radar-NHE76QYJ-RKhErikV.js → radar-NHE76QYJ-Be0pEUux.js} +3 -3
  120. package/dist/{range-LoQMRQIX.js → range-D9jxVFd_.js} +5 -5
  121. package/dist/{reduce-B9mZDxPo.js → reduce-C6NEPj6s.js} +4 -4
  122. package/dist/{requirementDiagram-UZGBJVZJ-D36MI1k0.js → requirementDiagram-UZGBJVZJ-DxzXQRgq.js} +9 -9
  123. package/dist/{sankeyDiagram-TZEHDZUN-D1mygNPC.js → sankeyDiagram-TZEHDZUN-D-I7dJ0_.js} +2 -2
  124. package/dist/{sequenceDiagram-WL72ISMW-CWdn91Rf.js → sequenceDiagram-WL72ISMW-VDme2ljw.js} +4 -4
  125. package/dist/{slides-component-DfwLApNr.js → slides-component-ql7-5GDI.js} +2 -2
  126. package/dist/{spec-HoYHAQo2.js → spec-GwhMEXwK.js} +8 -9
  127. package/dist/{stateDiagram-FKZM4ZOC-CPxroWXd.js → stateDiagram-FKZM4ZOC-g3GI1EcK.js} +12 -12
  128. package/dist/{stateDiagram-v2-4FDKWEC3-BpM9Q54b.js → stateDiagram-v2-4FDKWEC3-7i6jBXe6.js} +10 -10
  129. package/dist/stex-D2rme5UG.js +4 -0
  130. package/dist/style.css +1 -1
  131. package/dist/{timeline-definition-IT6M3QCI-CVnRHx_t.js → timeline-definition-IT6M3QCI-bhvLlX_b.js} +2 -2
  132. package/dist/{toString-C4TLO6FA.js → toString-BwTJvlyD.js} +2 -2
  133. package/dist/tooltip-CL8m4f9y.js +404 -0
  134. package/dist/{treemap-KMMF4GRG-B37ugcLd.js → treemap-KMMF4GRG-Ba9ifjpG.js} +3 -3
  135. package/dist/{types-Ckva8JJq.js → types-Dsh6yC4B.js} +412 -413
  136. package/dist/{useAsyncData-dr8GazGv.js → useAsyncData-BPpyKjTJ.js} +2 -2
  137. package/dist/{useDeepCompareMemoize-ChviuF5n.js → useDeepCompareMemoize-C8Ms87P-.js} +18 -19
  138. package/dist/{useIframeCapabilities-DurI5SJh.js → useIframeCapabilities-C7z8VrZ1.js} +2 -2
  139. package/dist/{useTheme-SlKl8MlS.js → useTheme-Cq-gIssy.js} +299 -300
  140. package/dist/{vega-component-CnG0vAjf.js → vega-component-B5sxdjMq.js} +10 -10
  141. package/dist/{xychartDiagram-PRI3JC2R-BltwMWKC.js → xychartDiagram-PRI3JC2R-CFxuifYY.js} +5 -5
  142. package/package.json +1 -1
  143. package/src/components/editor/Output.tsx +8 -6
  144. package/src/components/editor/__tests__/Output.test.tsx +59 -0
  145. package/src/components/editor/chrome/__tests__/state.test.ts +321 -0
  146. package/src/components/editor/chrome/state.ts +27 -2
  147. package/src/components/editor/file-tree/upload.tsx +46 -23
  148. package/src/components/editor/links/cell-link.tsx +3 -2
  149. package/src/components/editor/output/console/ConsoleOutput.tsx +13 -3
  150. package/src/components/pages/gallery-page.tsx +1 -1
  151. package/src/components/pages/home-page.tsx +5 -3
  152. package/src/components/tracing/tracing.tsx +50 -39
  153. package/src/core/documentation/DocHoverTarget.tsx +23 -0
  154. package/src/core/documentation/doc-lookup.ts +50 -0
  155. package/src/core/islands/main.ts +1 -0
  156. package/src/core/websocket/useMarimoKernelConnection.tsx +3 -0
  157. package/src/css/app/Cell.css +5 -0
  158. package/src/mount.tsx +2 -2
  159. package/src/plugins/core/RenderHTML.tsx +15 -0
  160. package/src/plugins/core/__test__/registerReactComponent.test.ts +204 -0
  161. package/src/plugins/core/registerReactComponent.tsx +33 -0
  162. package/src/plugins/impl/MatrixPlugin.tsx +275 -0
  163. package/src/plugins/impl/__tests__/MatrixPlugin.test.tsx +415 -0
  164. package/src/plugins/impl/anywidget/model.ts +1 -2
  165. package/src/plugins/impl/matplotlib/MatplotlibPlugin.tsx +70 -0
  166. package/src/plugins/impl/matplotlib/__tests__/matplotlib-renderer.test.ts +152 -0
  167. package/src/plugins/impl/matplotlib/matplotlib-renderer.ts +781 -0
  168. package/src/plugins/impl/matrix.css +45 -0
  169. package/src/plugins/layout/mermaid/mermaid.tsx +11 -3
  170. package/src/plugins/plugins.ts +4 -0
  171. package/src/utils/__tests__/download.test.tsx +47 -0
  172. package/src/utils/download.ts +13 -1
  173. package/src/utils/links.ts +1 -1
  174. package/src/utils/urls.ts +1 -1
  175. package/dist/Combination-BTMrlhzT.js +0 -2611
  176. package/dist/architecture-U656AL7Q-COfwZju8.js +0 -6
  177. package/dist/dist-4YNZxwMI.js +0 -8
  178. package/dist/dist-7nR3r2kG.js +0 -5
  179. package/dist/dist-B2gkyT3r.js +0 -5
  180. package/dist/dist-B8G3I6vJ.js +0 -8
  181. package/dist/dist-BJ96Ykfp.js +0 -8
  182. package/dist/dist-BKLIWGw4.js +0 -5
  183. package/dist/dist-Bf3ou00A.js +0 -6
  184. package/dist/dist-BvkKXuPm.js +0 -5
  185. package/dist/dist-C6NJ3n6r.js +0 -5
  186. package/dist/dist-CecLPYY5.js +0 -5
  187. package/dist/dist-Ch0SwRzK.js +0 -5
  188. package/dist/dist-D6eWHiFh.js +0 -6
  189. package/dist/dist-DCQ710Bv.js +0 -5
  190. package/dist/dist-P_pkS5f-.js +0 -8
  191. package/dist/error-banner-D2zjeN_a.js +0 -1015
  192. package/dist/hotkeys-B5WnGZXF.js +0 -587
  193. package/dist/stex-ChDHQs3R.js +0 -4
  194. package/dist/zod-bjADtMKr.js +0 -10663
  195. /package/dist/{_arrayReduce-DlK7U3Q6.js → _arrayReduce-REKcIEj3.js} +0 -0
  196. /package/dist/{_baseFor-DSVmVciX.js → _baseFor-B69PDbIz.js} +0 -0
  197. /package/dist/{_hasUnicode-Bz2x6u6r.js → _hasUnicode-DrSAc5A5.js} +0 -0
  198. /package/dist/{dist-r8ecBV-v.js → dist-CUOuFgHt.js} +0 -0
  199. /package/dist/{invariant-D9QLJ4SZ.js → invariant-D-K49MfV.js} +0 -0
  200. /package/dist/{main-DhFbkwoC.js → main-DmxVpB19.js} +0 -0
  201. /package/dist/{purify.es-Brw-U87Q.js → purify.es-D4vaFt5N.js} +0 -0
  202. /package/dist/{stex-DrxP7bb3.js → stex-DIvyJfNO.js} +0 -0
@@ -167,6 +167,7 @@ const ConsoleOutputInternal = (props: Props): React.ReactNode => {
167
167
  );
168
168
 
169
169
  const originalIdx = consoleOutputs.length - idx - 1;
170
+ const isPassword = output.mimetype === "text/password";
170
171
 
171
172
  if (output.response == null && lastStdInputIdx === idx) {
172
173
  return (
@@ -174,6 +175,7 @@ const ConsoleOutputInternal = (props: Props): React.ReactNode => {
174
175
  key={idx}
175
176
  output={output.data}
176
177
  isPdb={isPdb}
178
+ isPassword={isPassword}
177
179
  onSubmit={(text) => onSubmitDebugger(text, originalIdx)}
178
180
  onClear={onClear}
179
181
  />
@@ -185,6 +187,7 @@ const ConsoleOutputInternal = (props: Props): React.ReactNode => {
185
187
  key={idx}
186
188
  output={output.data}
187
189
  response={output.response}
190
+ isPassword={isPassword}
188
191
  />
189
192
  );
190
193
  }
@@ -216,6 +219,7 @@ const StdInput = (props: {
216
219
  output: string;
217
220
  response?: string;
218
221
  isPdb: boolean;
222
+ isPassword?: boolean;
219
223
  }) => {
220
224
  const [value, setValue] = React.useState("");
221
225
 
@@ -231,7 +235,7 @@ const StdInput = (props: {
231
235
  data-testid="console-input"
232
236
  // This is used in <StdinBlockingAlert> to find the input
233
237
  data-stdin-blocking={true}
234
- type="text"
238
+ type={props.isPassword ? "password" : "text"}
235
239
  autoComplete="off"
236
240
  autoFocus={true}
237
241
  value={value}
@@ -277,11 +281,17 @@ const StdInput = (props: {
277
281
  );
278
282
  };
279
283
 
280
- const StdInputWithResponse = (props: { output: string; response?: string }) => {
284
+ const StdInputWithResponse = (props: {
285
+ output: string;
286
+ response?: string;
287
+ isPassword?: boolean;
288
+ }) => {
281
289
  return (
282
290
  <div className="flex gap-2 items-center">
283
291
  {renderText(props.output)}
284
- <span className="text-(--sky-11)">{props.response}</span>
292
+ {!props.isPassword && (
293
+ <span className="text-(--sky-11)">{props.response}</span>
294
+ )}
285
295
  </div>
286
296
  );
287
297
  };
@@ -72,7 +72,7 @@ const GalleryPage: React.FC = () => {
72
72
  opengraphImage && isHttpsUrl(opengraphImage)
73
73
  ? opengraphImage
74
74
  : asURL(
75
- `/og/thumbnail?file=${encodeURIComponent(relativePath)}`,
75
+ `og/thumbnail?file=${encodeURIComponent(relativePath)}`,
76
76
  ).toString();
77
77
  return {
78
78
  ...file,
@@ -326,7 +326,7 @@ const Node = ({ node, style }: NodeRendererProps<FileInfo>) => {
326
326
  return (
327
327
  <a
328
328
  className={itemClassName}
329
- href={asURL(`?file=${relativePath}`).toString()}
329
+ href={asURL(`?file=${encodeURIComponent(relativePath)}`).toString()}
330
330
  target={tabTarget(relativePath)}
331
331
  >
332
332
  {iconEl}
@@ -401,8 +401,10 @@ const MarimoFileComponent = ({ file }: { file: MarimoFile }) => {
401
401
  // We want to keep the sessionId in this case
402
402
  const isNewNotebook = isSessionId(file.path);
403
403
  const href = isNewNotebook
404
- ? asURL(`?file=${file.initializationId}&session_id=${file.path}`)
405
- : asURL(`?file=${file.path}`);
404
+ ? asURL(
405
+ `?file=${encodeURIComponent(file.initializationId ?? file.path)}&session_id=${file.path}`,
406
+ )
407
+ : asURL(`?file=${encodeURIComponent(file.path)}`);
406
408
 
407
409
  const isMarkdown = file.path.endsWith(".md");
408
410
 
@@ -32,6 +32,7 @@ import { ClearButton } from "../buttons/clear-button";
32
32
  import type { SignalListener } from "../charts/types";
33
33
  import { ElapsedTime, formatElapsedTime } from "../editor/cell/CellStatus";
34
34
  import { PanelEmptyState } from "../editor/chrome/panels/empty-state";
35
+ import { usePanelSection } from "../editor/chrome/panels/panel-context";
35
36
  import { CellLink } from "../editor/links/cell-link";
36
37
  import {
37
38
  type ChartPosition,
@@ -50,6 +51,7 @@ export const Tracing: React.FC = () => {
50
51
 
51
52
  const { theme } = useTheme();
52
53
  const [chartPosition, setChartPosition] = useState<ChartPosition>("above");
54
+ const panelSection = usePanelSection();
53
55
 
54
56
  const toggleChartPosition = () => {
55
57
  if (chartPosition === "above") {
@@ -69,47 +71,56 @@ export const Tracing: React.FC = () => {
69
71
  );
70
72
  }
71
73
 
74
+ const tracingComponent = (
75
+ <div className="py-1 px-2 overflow-y-scroll h-full">
76
+ <div className="flex flex-row justify-start gap-3">
77
+ <div className="flex flex-row gap-1 items-center">
78
+ <label htmlFor="chartPosition" className="text-xs">
79
+ Inline chart
80
+ </label>
81
+ <input
82
+ type="checkbox"
83
+ name="chartPosition"
84
+ data-testid="chartPosition"
85
+ onClick={toggleChartPosition}
86
+ defaultChecked={chartPosition === "sideBySide"}
87
+ className="h-3 cursor-pointer"
88
+ />
89
+ </div>
90
+
91
+ <ClearButton dataTestId="clear-traces-button" onClick={clearRuns} />
92
+ </div>
93
+
94
+ <div className="flex flex-col gap-3">
95
+ {newestToOldestRunIds.map((runId: RunId, index: number) => {
96
+ const run = runMap.get(runId);
97
+ if (run) {
98
+ return (
99
+ <TraceBlock
100
+ key={run.runId}
101
+ run={run}
102
+ isExpanded={expandedRuns.get(run.runId)}
103
+ isMostRecentRun={index === 0}
104
+ chartPosition={chartPosition}
105
+ theme={theme}
106
+ />
107
+ );
108
+ }
109
+ return null;
110
+ })}
111
+ </div>
112
+ </div>
113
+ );
114
+
115
+ if (panelSection === "sidebar") {
116
+ return tracingComponent;
117
+ }
118
+
119
+ // Allow the panel to be resized when in the wider developer panel
72
120
  return (
73
121
  <PanelGroup direction="horizontal" className="h-full">
74
122
  <Panel defaultSize={50} minSize={30} maxSize={80}>
75
- <div className="py-1 px-2 overflow-y-scroll h-full">
76
- <div className="flex flex-row justify-start gap-3">
77
- <div className="flex flex-row gap-1 items-center">
78
- <label htmlFor="chartPosition" className="text-xs">
79
- Inline chart
80
- </label>
81
- <input
82
- type="checkbox"
83
- name="chartPosition"
84
- data-testid="chartPosition"
85
- onClick={toggleChartPosition}
86
- defaultChecked={chartPosition === "sideBySide"}
87
- className="h-3 cursor-pointer"
88
- />
89
- </div>
90
-
91
- <ClearButton dataTestId="clear-traces-button" onClick={clearRuns} />
92
- </div>
93
-
94
- <div className="flex flex-col gap-3">
95
- {newestToOldestRunIds.map((runId: RunId, index: number) => {
96
- const run = runMap.get(runId);
97
- if (run) {
98
- return (
99
- <TraceBlock
100
- key={run.runId}
101
- run={run}
102
- isExpanded={expandedRuns.get(run.runId)}
103
- isMostRecentRun={index === 0}
104
- chartPosition={chartPosition}
105
- theme={theme}
106
- />
107
- );
108
- }
109
- return null;
110
- })}
111
- </div>
112
- </div>
123
+ {tracingComponent}
113
124
  </Panel>
114
125
  <PanelResizeHandle className="w-1 bg-border hover:bg-primary/50 transition-colors" />
115
126
  <Panel defaultSize={50}>
@@ -381,7 +392,7 @@ const TraceRow: React.FC<TraceRowProps> = ({
381
392
  <span className="text-(--gray-10) dark:text-(--gray-11)">
382
393
  [{formatLogTimestamp(cellRun.startTime)}]
383
394
  </span>
384
- <span className="text-(--gray-10) w-16">
395
+ <span className="text-(--gray-10) w-16 overflow-hidden">
385
396
  (<CellLink cellId={cellRun.cellId} />)
386
397
  </span>
387
398
  <span className="w-40 truncate -ml-1">{cellRun.code}</span>
@@ -0,0 +1,23 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import type { ReactNode } from "react";
4
+ import { useDebouncedCallback } from "@/hooks/useDebounce";
5
+ import { requestOutputDocumentation } from "./doc-lookup";
6
+
7
+ export const DocHoverTarget: React.FC<{
8
+ qualifiedName: string;
9
+ children: ReactNode;
10
+ }> = ({ qualifiedName, children }) => {
11
+ const handleMouseEnter = useDebouncedCallback(() => {
12
+ requestOutputDocumentation(qualifiedName);
13
+ }, 100);
14
+
15
+ return (
16
+ <span
17
+ onMouseEnter={handleMouseEnter}
18
+ onMouseLeave={() => handleMouseEnter.cancel()}
19
+ >
20
+ {children}
21
+ </span>
22
+ );
23
+ };
@@ -0,0 +1,50 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { getCells } from "@/core/cells/cells";
4
+ import { AUTOCOMPLETER } from "@/core/codemirror/completion/Autocompleter";
5
+ import { store } from "@/core/state/jotai";
6
+ import { Logger } from "@/utils/Logger";
7
+ import { documentationAtom } from "./state";
8
+
9
+ /**
10
+ * Request documentation for a qualified Python name (e.g. "torch.nn.Linear")
11
+ * by piggybacking on the existing code-completion system.
12
+ *
13
+ * We send the qualified name as the "document" to the autocomplete endpoint.
14
+ * Jedi resolves it and returns the docstring in the completion info.
15
+ */
16
+ export async function requestOutputDocumentation(
17
+ qualifiedName: string,
18
+ ): Promise<void> {
19
+ // We need any valid cell_id for the completion request.
20
+ const cellId = getCells().inOrderIds.at(0);
21
+ if (!cellId) {
22
+ return;
23
+ }
24
+
25
+ try {
26
+ const message = await AUTOCOMPLETER.request({
27
+ document: qualifiedName,
28
+ cellId,
29
+ });
30
+
31
+ if (!message || message.options.length === 0) {
32
+ return;
33
+ }
34
+
35
+ // Find the option matching the last segment of the qualified name
36
+ const shortName = qualifiedName.split(".").pop() ?? qualifiedName;
37
+
38
+ const defaultOption = message.options[0];
39
+ const match =
40
+ message.options.find((o) => o.name === shortName) ?? defaultOption;
41
+
42
+ if (match?.completion_info) {
43
+ store.set(documentationAtom, {
44
+ documentation: match.completion_info,
45
+ });
46
+ }
47
+ } catch (error) {
48
+ Logger.debug(`Doc lookup failed for "${qualifiedName}"`, error);
49
+ }
50
+ }
@@ -126,6 +126,7 @@ export async function initialize() {
126
126
  case "datasets":
127
127
  case "data-source-connections":
128
128
  case "validate-sql-result":
129
+ case "storage-namespaces":
129
130
  case "secret-keys-result":
130
131
  case "startup-logs":
131
132
  // Unsupported
@@ -284,6 +284,9 @@ export function useMarimoKernelConnection(opts: {
284
284
  })),
285
285
  });
286
286
  return;
287
+ case "storage-namespaces":
288
+ // TODO: Handle storage namespaces
289
+ return;
287
290
 
288
291
  case "reconnected":
289
292
  return;
@@ -314,6 +314,11 @@
314
314
 
315
315
  &.published {
316
316
  border: none;
317
+
318
+ &:hover {
319
+ border: none;
320
+ box-shadow: none;
321
+ }
317
322
  }
318
323
  }
319
324
 
package/src/mount.tsx CHANGED
@@ -59,9 +59,9 @@ import { reportVitals } from "./utils/vitals";
59
59
  let hasMounted = false;
60
60
 
61
61
  /**
62
- * Main entry point for the mairmo app.
62
+ * Main entry point for the marimo app.
63
63
  *
64
- * Sets up the mairmo app with a theme provider.
64
+ * Sets up the marimo app with a theme provider.
65
65
  */
66
66
  export function mount(options: unknown, el: Element): Error | undefined {
67
67
  if (hasMounted) {
@@ -14,6 +14,7 @@ import React, {
14
14
  } from "react";
15
15
  import { CopyClipboardIcon } from "@/components/icons/copy-icon";
16
16
  import { QueryParamPreservingLink } from "@/components/ui/query-param-preserving-link";
17
+ import { DocHoverTarget } from "@/core/documentation/DocHoverTarget";
17
18
  import { sanitizeHtml, useSanitizeHtml } from "./sanitize";
18
19
 
19
20
  type ReplacementFn = NonNullable<HTMLReactParserOptions["replace"]>;
@@ -146,6 +147,19 @@ const addCopyButtonToCodehilite: TransformFn = (
146
147
  }
147
148
  };
148
149
 
150
+ // Wrap elements with data-marimo-doc attribute in a DocHoverTarget
151
+ const wrapDocHoverTargets: TransformFn = (
152
+ reactNode: ReactNode,
153
+ domNode: DOMNode,
154
+ ): JSX.Element | undefined => {
155
+ if (domNode instanceof Element && domNode.attribs?.["data-marimo-doc"]) {
156
+ const qualifiedName = domNode.attribs["data-marimo-doc"];
157
+ return (
158
+ <DocHoverTarget qualifiedName={qualifiedName}>{reactNode}</DocHoverTarget>
159
+ );
160
+ }
161
+ };
162
+
149
163
  const CopyableCode = ({ children }: { children: ReactNode }) => {
150
164
  const ref = useRef<HTMLDivElement>(null);
151
165
  return (
@@ -224,6 +238,7 @@ function parseHtml({
224
238
  const transformFunctions: TransformFn[] = [
225
239
  addCopyButtonToCodehilite,
226
240
  preserveQueryParamsInAnchorLinks,
241
+ wrapDocHoverTargets,
227
242
  removeWrappingBodyTags,
228
243
  removeWrappingHtmlTags,
229
244
  ];
@@ -0,0 +1,204 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import ReactDOM from "react-dom/client";
4
+ import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
5
+ import { z } from "zod";
6
+ import {
7
+ isCustomMarimoElement,
8
+ registerReactComponent,
9
+ } from "../registerReactComponent";
10
+
11
+ // Each custom element name can only be registered once per jsdom window,
12
+ // so we use a counter to generate unique tag names across tests.
13
+ let tagCounter = 0;
14
+ function uniqueTag(base: string) {
15
+ return `marimo-test-${base}-${++tagCounter}`;
16
+ }
17
+
18
+ function makePlugin(tagName: string) {
19
+ return {
20
+ tagName,
21
+ validator: z.any(),
22
+ render: () => null as never,
23
+ };
24
+ }
25
+
26
+ describe("isCustomMarimoElement", () => {
27
+ test("returns false for null", () => {
28
+ expect(isCustomMarimoElement(null)).toBe(false);
29
+ });
30
+
31
+ test("returns false for a regular HTMLElement", () => {
32
+ const div = document.createElement("div");
33
+ expect(isCustomMarimoElement(div)).toBe(false);
34
+ });
35
+
36
+ test("returns false for a non-HTMLElement", () => {
37
+ const svg = document.createElementNS(
38
+ "http://www.w3.org/2000/svg",
39
+ "circle",
40
+ );
41
+ expect(isCustomMarimoElement(svg as Element)).toBe(false);
42
+ });
43
+
44
+ test("returns true for a registered marimo custom element", () => {
45
+ const tag = uniqueTag("is-custom");
46
+ registerReactComponent(makePlugin(tag));
47
+ const el = document.createElement(tag);
48
+ expect(isCustomMarimoElement(el)).toBe(true);
49
+ });
50
+
51
+ test("returns false for an element with wrong __type__ value", () => {
52
+ const div = document.createElement("div") as unknown as HTMLElement & {
53
+ __type__: string;
54
+ };
55
+ div.__type__ = "something_else";
56
+ expect(isCustomMarimoElement(div)).toBe(false);
57
+ });
58
+ });
59
+
60
+ describe("connectedCallback - light DOM nesting detection", () => {
61
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
62
+ let createRootSpy: any;
63
+ const mockRoot = {
64
+ render: vi.fn(),
65
+ unmount: vi.fn(),
66
+ };
67
+
68
+ beforeEach(() => {
69
+ createRootSpy = vi
70
+ .spyOn(ReactDOM, "createRoot")
71
+ .mockReturnValue(mockRoot as unknown as ReactDOM.Root);
72
+ });
73
+
74
+ afterEach(() => {
75
+ createRootSpy.mockRestore();
76
+ mockRoot.render.mockClear();
77
+ mockRoot.unmount.mockClear();
78
+ });
79
+
80
+ test("skips mounting when element is a light DOM child of another marimo element", () => {
81
+ const parentTag = uniqueTag("parent");
82
+ const childTag = uniqueTag("child");
83
+ registerReactComponent(makePlugin(parentTag));
84
+ registerReactComponent(makePlugin(childTag));
85
+
86
+ const parent = document.createElement(parentTag);
87
+ const child = document.createElement(childTag);
88
+ parent.append(child);
89
+
90
+ createRootSpy.mockClear();
91
+ document.body.append(parent);
92
+
93
+ // Only the parent should mount; the child is skipped because it
94
+ // detects a marimo ancestor in the light DOM.
95
+ expect(createRootSpy).toHaveBeenCalledTimes(1);
96
+ expect(createRootSpy).toHaveBeenCalledWith(parent.shadowRoot);
97
+
98
+ parent.remove();
99
+ });
100
+
101
+ test("mounts when element is not nested inside a marimo element", () => {
102
+ const tag = uniqueTag("standalone");
103
+ registerReactComponent(makePlugin(tag));
104
+
105
+ const el = document.createElement(tag);
106
+ createRootSpy.mockClear();
107
+
108
+ document.body.append(el);
109
+
110
+ expect(createRootSpy).toHaveBeenCalledTimes(1);
111
+ expect(createRootSpy).toHaveBeenCalledWith(el.shadowRoot);
112
+
113
+ el.remove();
114
+ });
115
+
116
+ test("mounts when nested inside a regular (non-marimo) element", () => {
117
+ const tag = uniqueTag("in-div");
118
+ registerReactComponent(makePlugin(tag));
119
+
120
+ const wrapper = document.createElement("div");
121
+ const el = document.createElement(tag);
122
+ wrapper.append(el);
123
+
124
+ createRootSpy.mockClear();
125
+ document.body.append(wrapper);
126
+
127
+ expect(createRootSpy).toHaveBeenCalledTimes(1);
128
+ expect(createRootSpy).toHaveBeenCalledWith(el.shadowRoot);
129
+
130
+ wrapper.remove();
131
+ });
132
+
133
+ test("skips mounting for deeply nested marimo element through non-marimo wrappers", () => {
134
+ const outerTag = uniqueTag("outer");
135
+ const innerTag = uniqueTag("inner");
136
+ registerReactComponent(makePlugin(outerTag));
137
+ registerReactComponent(makePlugin(innerTag));
138
+
139
+ const outer = document.createElement(outerTag);
140
+ const div = document.createElement("div");
141
+ const inner = document.createElement(innerTag);
142
+
143
+ // Structure: outer > div > inner
144
+ outer.append(div);
145
+ div.append(inner);
146
+
147
+ createRootSpy.mockClear();
148
+ document.body.append(outer);
149
+
150
+ // Only outer mounts; inner is skipped because a marimo ancestor
151
+ // is found when traversing through the intermediate div.
152
+ expect(createRootSpy).toHaveBeenCalledTimes(1);
153
+ expect(createRootSpy).toHaveBeenCalledWith(outer.shadowRoot);
154
+
155
+ outer.remove();
156
+ });
157
+
158
+ test("mounts both sibling marimo elements (neither is a child of the other)", () => {
159
+ const tagA = uniqueTag("sibling-a");
160
+ const tagB = uniqueTag("sibling-b");
161
+ registerReactComponent(makePlugin(tagA));
162
+ registerReactComponent(makePlugin(tagB));
163
+
164
+ const wrapper = document.createElement("div");
165
+ const a = document.createElement(tagA);
166
+ const b = document.createElement(tagB);
167
+ wrapper.append(a);
168
+ wrapper.append(b);
169
+
170
+ createRootSpy.mockClear();
171
+ document.body.append(wrapper);
172
+
173
+ // Both siblings should mount since neither is a child of the other.
174
+ expect(createRootSpy).toHaveBeenCalledTimes(2);
175
+
176
+ wrapper.remove();
177
+ });
178
+
179
+ test("mounts when element is inside the shadow DOM of another marimo element", () => {
180
+ const outerTag = uniqueTag("shadow-outer");
181
+ const innerTag = uniqueTag("shadow-inner");
182
+ registerReactComponent(makePlugin(outerTag));
183
+ registerReactComponent(makePlugin(innerTag));
184
+
185
+ const outer = document.createElement(outerTag);
186
+ const inner = document.createElement(innerTag);
187
+
188
+ // Place the inner element inside the outer element's shadow root,
189
+ // simulating how getChildren() -> renderHTML() re-creates children
190
+ // in the shadow DOM. parentElement traversal stays within the
191
+ // shadow root boundary, so inner should NOT detect outer as an
192
+ // ancestor and should mount normally.
193
+ outer.shadowRoot?.append(inner);
194
+
195
+ createRootSpy.mockClear();
196
+ document.body.append(outer);
197
+
198
+ // Both elements should mount: outer in the document, inner in the
199
+ // shadow root (parentElement traversal doesn't cross shadow boundary).
200
+ expect(createRootSpy).toHaveBeenCalledTimes(2);
201
+
202
+ outer.remove();
203
+ });
204
+ });
@@ -316,6 +316,15 @@ export function registerReactComponent<T>(plugin: IPlugin<T, unknown>): void {
316
316
  }
317
317
 
318
318
  connectedCallback() {
319
+ // Skip mounting if this element is in the light DOM of another
320
+ // marimo custom element. The parent element's shadow DOM will
321
+ // re-create this element via getChildren() -> renderHTML(), so
322
+ // this light DOM copy should remain inert to avoid duplicate
323
+ // side-effects (e.g., mo.lazy firing load() twice).
324
+ if (this.isLightDOMChildOfMarimoElement()) {
325
+ return;
326
+ }
327
+
319
328
  if (!this.mounted) {
320
329
  // Create a React root on the shadow root
321
330
  invariant(this.shadowRoot, "Shadow root should exist");
@@ -348,6 +357,30 @@ export function registerReactComponent<T>(plugin: IPlugin<T, unknown>): void {
348
357
  }
349
358
  }
350
359
 
360
+ /**
361
+ * Check if this element is in the light DOM of another marimo
362
+ * custom element. When a marimo element (e.g. marimo-tabs) has
363
+ * children that are also marimo elements (e.g. marimo-lazy), the
364
+ * browser upgrades both the light DOM originals AND the shadow DOM
365
+ * copies created by renderHTML(). The light DOM copies should not
366
+ * mount since they are not rendered (no <slot>) and would cause
367
+ * duplicate side-effects.
368
+ *
369
+ * parentElement traversal stays within the same DOM tree boundary
370
+ * (shadow root or document), so shadow DOM copies never find a
371
+ * marimo ancestor and correctly return false.
372
+ */
373
+ private isLightDOMChildOfMarimoElement(): boolean {
374
+ let parent = this.parentElement;
375
+ while (parent) {
376
+ if (isCustomMarimoElement(parent)) {
377
+ return true;
378
+ }
379
+ parent = parent.parentElement;
380
+ }
381
+ return false;
382
+ }
383
+
351
384
  /**
352
385
  * Reset the plugin initial value and data.
353
386
  * And then re-render the plugin.