@marimo-team/frontend 0.21.2-dev5 → 0.21.2-dev53

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 (386) hide show
  1. package/dist/assets/{CellStatus-CDSBsjjF.js → CellStatus-DX8MnBMk.js} +1 -1
  2. package/dist/assets/{ConnectedDataExplorerComponent-Buaffg3u.js → ConnectedDataExplorerComponent-7yXYooWG.js} +1 -1
  3. package/dist/assets/{DeferredRequestRegistry-O6RDJKs0.js → DeferredRequestRegistry-B8jPCuU1.js} +1 -1
  4. package/dist/assets/{ImperativeModal-qEtcJ95s.js → ImperativeModal-C3CD5-Aq.js} +1 -1
  5. package/dist/assets/JsonOutput-DqjAyzM8.js +46 -0
  6. package/dist/assets/{LazyAnyLanguageCodeMirror-m8w66E4s.js → LazyAnyLanguageCodeMirror-CcCHIX38.js} +2 -2
  7. package/dist/assets/{MarimoErrorOutput-az74f3Mp.js → MarimoErrorOutput-Dat_S09q.js} +5 -5
  8. package/dist/assets/RenderHTML-C7XM39M2.js +1 -0
  9. package/dist/assets/{add-cell-with-ai-DUn4LN4W.js → add-cell-with-ai-y2xroEGf.js} +9 -9
  10. package/dist/assets/{add-connection-dialog-CBz0AUVX.js → add-connection-dialog-QDBOmil3.js} +23 -23
  11. package/dist/assets/{agent-panel-u83dsDsM.js → agent-panel-DziFrEjJ.js} +6 -6
  12. package/dist/assets/ai-model-dropdown-CiJpv5wN.js +5 -0
  13. package/dist/assets/{alert-dialog-9WfvUF7e.js → alert-dialog-ebk_-wsU.js} +1 -1
  14. package/dist/assets/{any-language-editor-DwQMnAM3.js → any-language-editor-DZYnKlJB.js} +1 -1
  15. package/dist/assets/{app-config-button-rDu-lCIB.js → app-config-button-CpAv9Tiu.js} +1 -1
  16. package/dist/assets/{architectureDiagram-VXUJARFQ-DfMHHFZ3.js → architectureDiagram-VXUJARFQ-DKmk99ac.js} +1 -1
  17. package/dist/assets/{azure-BTXHztTw.js → azure-Dw5gKmUz.js} +1 -1
  18. package/dist/assets/{blockDiagram-VD42YOAC-CuEDwbg7.js → blockDiagram-VD42YOAC-TpAhP5OQ.js} +1 -1
  19. package/dist/assets/{c4Diagram-YG6GDRKO-CD9uz7WG.js → c4Diagram-YG6GDRKO-Dg8hCyKf.js} +1 -1
  20. package/dist/assets/{cache-panel-qIxnndwr.js → cache-panel-D0OsHk_D.js} +1 -1
  21. package/dist/assets/cell-editor-RbAVpSFb.js +22 -0
  22. package/dist/assets/{cell-link-Q_UEP94Z.js → cell-link-CNgO3c-T.js} +1 -1
  23. package/dist/assets/{cells-fq1RMGX_.js → cells-39RE4UzS.js} +77 -76
  24. package/dist/assets/channel-D1wPYhc9.js +1 -0
  25. package/dist/assets/{chat-display-D_Fuuy6r.js → chat-display-BPUVrlA6.js} +1 -1
  26. package/dist/assets/{chat-panel-BO-JoQSp.js → chat-panel-esz5TfF-.js} +2 -2
  27. package/dist/assets/{chunk-5FQGJX7Z-CM1a_K7I.js → chunk-5FQGJX7Z-sGWxL7Ey.js} +3 -3
  28. package/dist/assets/{chunk-ABZYJK2D-D_Wumzom.js → chunk-ABZYJK2D-DXJ7hnjx.js} +1 -1
  29. package/dist/assets/{chunk-ATLVNIR6-BZ0lTgIn.js → chunk-ATLVNIR6-CTEqcLEc.js} +1 -1
  30. package/dist/assets/{chunk-B4BG7PRW-Dw1S6osV.js → chunk-B4BG7PRW-CkAIqtPf.js} +1 -1
  31. package/dist/assets/{chunk-DI55MBZ5-Q63FTCPP.js → chunk-DI55MBZ5-C4F9vu8o.js} +1 -1
  32. package/dist/assets/{chunk-EXTU4WIE-CfkJ_R7W.js → chunk-EXTU4WIE-BT0Py-4P.js} +1 -1
  33. package/dist/assets/{chunk-JA3XYJ7Z-B41fGeoj.js → chunk-JA3XYJ7Z-CGd7jja_.js} +1 -1
  34. package/dist/assets/{chunk-JZLCHNYA-D43EJxQw.js → chunk-JZLCHNYA-CqY-RYTj.js} +1 -1
  35. package/dist/assets/{chunk-N4CR4FBY-DCtw0-ni.js → chunk-N4CR4FBY-aUDjVzN7.js} +2 -2
  36. package/dist/assets/{chunk-QN33PNHL-CLA2rsaS.js → chunk-QN33PNHL-DHnCb5-U.js} +1 -1
  37. package/dist/assets/{chunk-QXUST7PY-De1DpbnL.js → chunk-QXUST7PY-CRO5kYCE.js} +1 -1
  38. package/dist/assets/{chunk-S3R3BYOJ-qrwvlUdq.js → chunk-S3R3BYOJ-CSHQgcuP.js} +1 -1
  39. package/dist/assets/{chunk-TZMSLE5B-C4KcThb0.js → chunk-TZMSLE5B-CE7o9asH.js} +1 -1
  40. package/dist/assets/classDiagram-2ON5EDUG-BDIQSKYv.js +1 -0
  41. package/dist/assets/classDiagram-v2-WZHVMYZB-Cdh4cVOB.js +1 -0
  42. package/dist/assets/{code-block-37QAKDTI-D4FF4cnN.js → code-block-37QAKDTI-DMpe1jNG.js} +1 -1
  43. package/dist/assets/{column-preview-WsubyItX.js → column-preview-BUKHsglA.js} +1 -1
  44. package/dist/assets/{command-rfxSsiHU.js → command-DmzEbs6L.js} +1 -1
  45. package/dist/assets/{command-palette-Bez4BqJF.js → command-palette-BX5gNpUr.js} +1 -1
  46. package/dist/assets/{common-BYwO983A.js → common-B4yshY-H.js} +1 -1
  47. package/dist/assets/{components-Ozq0825a.js → components-DCqJqyys.js} +1 -1
  48. package/dist/assets/{components-Bpgzv_5_.js → components-oKCjx6cN.js} +1 -1
  49. package/dist/assets/{config-3Aq84phF.js → config-DqxNMQDN.js} +1 -1
  50. package/dist/assets/{context-CIAP2NOh.js → context-CRmPPhx9.js} +1 -1
  51. package/dist/assets/{context-aware-panel-CfsfBlhO.js → context-aware-panel-DC0Te9Hj.js} +2 -2
  52. package/dist/assets/copy-KjcPgPw9.js +1 -0
  53. package/dist/assets/{copy-icon-BgmMM9Zg.js → copy-icon-P0kzz1Pr.js} +1 -1
  54. package/dist/assets/{dagre-6UL2VRFP-BhMnPhuU.js → dagre-6UL2VRFP-XqoNLMQ3.js} +1 -1
  55. package/dist/assets/{data-grid-overlay-editor-kbv73SQ7.js → data-grid-overlay-editor-Cus1-BFN.js} +1 -1
  56. package/dist/assets/{datasource-F9sJtuof.js → datasource-DKHG39NV.js} +2 -2
  57. package/dist/assets/{dates-CHaNfieI.js → dates-CTqMeMGY.js} +1 -1
  58. package/dist/assets/{dependency-graph-panel-AOBcX2tP.js → dependency-graph-panel-7GKT3dyD.js} +4 -4
  59. package/dist/assets/{diagram-PSM6KHXK-Cr2t4zul.js → diagram-PSM6KHXK-BuzKNNcN.js} +1 -1
  60. package/dist/assets/{diagram-QEK2KX5R-DLU_hSO4.js → diagram-QEK2KX5R-A5LBQS11.js} +1 -1
  61. package/dist/assets/{diagram-S2PKOQOG-C7EkNAO3.js → diagram-S2PKOQOG-dkJE8SA0.js} +1 -1
  62. package/dist/assets/{dialog-CVN1lcMF.js → dialog-1_jIyAb_.js} +1 -1
  63. package/dist/assets/{dist-DJBpXGro.js → dist-6P6vjf93.js} +1 -1
  64. package/dist/assets/dist-BWGxTsTP.js +1 -0
  65. package/dist/assets/{dist-CblX3KGI.js → dist-B_buFm3B.js} +1 -1
  66. package/dist/assets/{dist-CXLJUPFl.js → dist-BatEf-0f.js} +1 -1
  67. package/dist/assets/dist-C3AnARPE.js +1 -0
  68. package/dist/assets/{dist-BNC_tnlW.js → dist-C6ohaMKU.js} +1 -1
  69. package/dist/assets/{dist-BoDHthoc.js → dist-CO9Pg2Rc.js} +1 -1
  70. package/dist/assets/{dist-By6zhkxZ.js → dist-CSV6H5w_.js} +1 -1
  71. package/dist/assets/{dist-6FIAeDIu.js → dist-CYs1oP7g.js} +1 -1
  72. package/dist/assets/{dist-DbK_4_qo.js → dist-DD9ZJMK5.js} +1 -1
  73. package/dist/assets/{dist-DlTbNRBn.js → dist-DReDpc2z.js} +1 -1
  74. package/dist/assets/dist-DSlq5ZeQ.js +1 -0
  75. package/dist/assets/{dist-DrceDAhM.js → dist-DXyIIB32.js} +1 -1
  76. package/dist/assets/{dist-CcGcwrUD.js → dist-DkaderFA.js} +1 -1
  77. package/dist/assets/{dist-BiZQlfhr.js → dist-Dq3BlK4R.js} +1 -1
  78. package/dist/assets/{dist-ClzRAVN-.js → dist-Dxov2ltb.js} +1 -1
  79. package/dist/assets/{dist-BXXPfIam.js → dist-EtGG9C1A.js} +1 -1
  80. package/dist/assets/{dist-C3MmXcLA.js → dist-NjId5Gdz.js} +1 -1
  81. package/dist/assets/dist-ZM59OWYL.js +1 -0
  82. package/dist/assets/dist-_TwZ4tIe.js +1 -0
  83. package/dist/assets/dist-f9b2H4q_.js +1 -0
  84. package/dist/assets/{dist-mxGLXOFm.js → dist-feWVQ4dq.js} +1 -1
  85. package/dist/assets/{documentation-panel-D28MpqbI.js → documentation-panel-D5d_BbHV.js} +1 -1
  86. package/dist/assets/{download-B9_ToVd4.js → download-B1vXaivj.js} +4 -4
  87. package/dist/assets/{dropdown-menu-DdPu5Kfu.js → dropdown-menu-BehqiLFL.js} +1 -1
  88. package/dist/assets/{edit-page-otS-QvnC.js → edit-page-B5cAZdO7.js} +7 -7
  89. package/dist/assets/{en-US-BO8E69bZ.js → en-US-f8tajDx1.js} +1 -1
  90. package/dist/assets/{erDiagram-Q2GNP2WA-DqlEo_Us.js → erDiagram-Q2GNP2WA-CgH6udOW.js} +1 -1
  91. package/dist/assets/{error-banner-BRG6IPhC.js → error-banner-J5F3weEj.js} +1 -1
  92. package/dist/assets/{error-panel-RRoj_iOR.js → error-panel-DnhKJ5Ou.js} +1 -1
  93. package/dist/assets/{es-CzkousWe.js → es-DYI7U61K.js} +1 -1
  94. package/dist/assets/{esm-CBkHtTOV.js → esm-BC1J92im.js} +1 -1
  95. package/dist/assets/{esm-DhTsVMLM.js → esm-DKLLCu3N.js} +1 -1
  96. package/dist/assets/{field-B1jGbQlH.js → field-B08lvxnl.js} +1 -1
  97. package/dist/assets/file-explorer-panel-BUk69TND.js +26 -0
  98. package/dist/assets/{file-icons-CWLHDa1J.js → file-icons-3DTS8ZM7.js} +1 -1
  99. package/dist/assets/{floating-outline-BvQKiC0F.js → floating-outline-DmugATcB.js} +1 -1
  100. package/dist/assets/{flowDiagram-NV44I4VS-CQlIJ12H.js → flowDiagram-NV44I4VS-D85BL4rA.js} +1 -1
  101. package/dist/assets/{focus-DYwTiH9-.js → focus-DlJH7sfh.js} +1 -1
  102. package/dist/assets/{form-CrQYrsUC.js → form-760_EqmF.js} +2 -2
  103. package/dist/assets/{formats-rhOJovGE.js → formats-wYuh1bqp.js} +1 -1
  104. package/dist/assets/{formatting-B4ZCH3ol.js → formatting-CraOPe94.js} +1 -1
  105. package/dist/assets/{gallery-page-eRea0yic.js → gallery-page--paiPvYX.js} +1 -1
  106. package/dist/assets/{ganttDiagram-JELNMOA3-B3EGIuZL.js → ganttDiagram-JELNMOA3-DzCOXWlF.js} +1 -1
  107. package/dist/assets/{gitGraphDiagram-V2S2FVAM-h5SlguJK.js → gitGraphDiagram-V2S2FVAM-BUHURX6t.js} +1 -1
  108. package/dist/assets/{glide-data-editor-xt5xNZeV.js → glide-data-editor-DYdoQTO5.js} +4 -4
  109. package/dist/assets/{globals-BsV5fVR-.js → globals-7M5DRsIb.js} +1 -1
  110. package/dist/assets/home-page-CxQ1e98h.js +4 -0
  111. package/dist/assets/{hooks-CHx5dUUq.js → hooks-C3jEff2O.js} +1 -1
  112. package/dist/assets/html-to-image-DTFXm6_Z.js +2 -0
  113. package/dist/assets/{index-GjXovVsl.css → index-BmoocKR0.css} +1 -1
  114. package/dist/assets/index-DNchElHl.js +38 -0
  115. package/dist/assets/{infoDiagram-HS3SLOUP-pzpClObZ.js → infoDiagram-HS3SLOUP-GG5r0Y5G.js} +1 -1
  116. package/dist/assets/{input-CUwqpKjd.js → input-CN1ZeRYm.js} +1 -1
  117. package/dist/assets/{isValid-BGe7pJXT.js → isValid-DY4Mgbr7.js} +1 -1
  118. package/dist/assets/{journeyDiagram-XKPGCS4Q-BrdRi92v.js → journeyDiagram-XKPGCS4Q-B1dvnV4D.js} +1 -1
  119. package/dist/assets/{kanban-definition-3W4ZIXB7-B6WqgFqh.js → kanban-definition-3W4ZIXB7-CefbrXQv.js} +1 -1
  120. package/dist/assets/{kiosk-mode-DNjEnIWb.js → kiosk-mode-CyTnrzwK.js} +1 -1
  121. package/dist/assets/{layout-CwFtCyW8.js → layout-3b3tzmbb.js} +3 -3
  122. package/dist/assets/{linear-BP9rwmWK.js → linear-BDnrHGYO.js} +1 -1
  123. package/dist/assets/{links-CIQwYQ48.js → links-CptYD1FP.js} +1 -1
  124. package/dist/assets/{logs-panel-BU1tNEVc.js → logs-panel-C4k_2IHW.js} +1 -1
  125. package/dist/assets/{markdown-renderer-MR9df58W.js → markdown-renderer-DSy5IuAx.js} +1 -1
  126. package/dist/assets/{menu-items-UxKrm8hS.js → menu-items-BpHXKv51.js} +1 -1
  127. package/dist/assets/mermaid-4DMBBIKO-g7AL4SLJ.js +1 -0
  128. package/dist/assets/{mermaid-Djr3jUAB.js → mermaid-C43VbdrM.js} +3 -3
  129. package/dist/assets/{mermaid-parser.core-DR82IMb2.js → mermaid-parser.core-CdM8D_p_.js} +1 -1
  130. package/dist/assets/{mindmap-definition-VGOIOE7T-DAmJpbPM.js → mindmap-definition-VGOIOE7T-CcV7D-wF.js} +1 -1
  131. package/dist/assets/{mode-AcL8c6qH.js → mode-D-iRbN9x.js} +1 -1
  132. package/dist/assets/{multi-map-Pp1P2DOX.js → multi-map-C8RbwBrw.js} +1 -1
  133. package/dist/assets/{name-cell-input-DVFEv_aO.js → name-cell-input-Dyb0ZO4X.js} +1 -1
  134. package/dist/assets/{number-overlay-editor-D__AflXQ.js → number-overlay-editor-BBJO1mf8.js} +1 -1
  135. package/dist/assets/{outline-panel-CP0oIuiU.js → outline-panel-D7CJwO8u.js} +1 -1
  136. package/dist/assets/{packages-panel-CzRELimG.js → packages-panel-Bp1SYjgc.js} +1 -1
  137. package/dist/assets/{panels-wTweRyIv.js → panels-D0aw6jdc.js} +1 -1
  138. package/dist/assets/{pieDiagram-ADFJNKIX-DbGIFRoq.js → pieDiagram-ADFJNKIX-C1fBf15W.js} +1 -1
  139. package/dist/assets/{popover-BPGG2gPG.js → popover-Bvoif-Mg.js} +1 -1
  140. package/dist/assets/{precisionRound-BOmLQIKI.js → precisionRound-CCOoIlcP.js} +1 -1
  141. package/dist/assets/{process-output-CTVsT--B.js → process-output-BxPWHcIi.js} +1 -1
  142. package/dist/assets/{quadrantDiagram-AYHSOK5B-CLAtyple.js → quadrantDiagram-AYHSOK5B-RVooN-fX.js} +1 -1
  143. package/dist/assets/{react-vega-BZav_-2n.js → react-vega-BKRQSy0g.js} +1 -1
  144. package/dist/assets/react-vega-DXESF7qN.js +1 -0
  145. package/dist/assets/{readonly-python-code-DH-1xZGq.js → readonly-python-code-D95oshJa.js} +1 -1
  146. package/dist/assets/{renderShortcut-CnD1Dah5.js → renderShortcut-BYvKm38e.js} +1 -1
  147. package/dist/assets/{request-registry-B-7cIM_I.js → request-registry-XB2EzJHm.js} +1 -1
  148. package/dist/assets/{requirementDiagram-UZGBJVZJ-DDDTB1LD.js → requirementDiagram-UZGBJVZJ-DtqOYJpH.js} +1 -1
  149. package/dist/assets/{run-page-FCvGnICa.js → run-page-B7CASued.js} +1 -1
  150. package/dist/assets/{sankeyDiagram-TZEHDZUN-DAWPOfBw.js → sankeyDiagram-TZEHDZUN-DmcooLA8.js} +1 -1
  151. package/dist/assets/{scratchpad-panel-k9lrm-rC.js → scratchpad-panel-BIVRgYOU.js} +1 -1
  152. package/dist/assets/{secrets-panel-B1Z-6dmz.js → secrets-panel-C24hZEiO.js} +1 -1
  153. package/dist/assets/{select-CxT2Geqr.js → select-Cnd3vm9n.js} +1 -1
  154. package/dist/assets/{sequenceDiagram-WL72ISMW-D_qAidD2.js → sequenceDiagram-WL72ISMW-S2qeLb7J.js} +1 -1
  155. package/dist/assets/{session-panel-WhN0qilM.js → session-panel-CNabBHQm.js} +1 -1
  156. package/dist/assets/{share-BdH_5I58.js → share-NjpZ54PJ.js} +1 -1
  157. package/dist/assets/{snippets-panel-BCDHUuku.js → snippets-panel-D1JAeJj1.js} +1 -1
  158. package/dist/assets/{spec-DbmSqx09.js → spec-DnVBmJUh.js} +1 -1
  159. package/dist/assets/{state-Ce7CVShi.js → state-BagvlaEz.js} +1 -1
  160. package/dist/assets/{state-3V5UxC3B.js → state-Cz_wrzCz.js} +1 -1
  161. package/dist/assets/{state-CBmCYWFH.js → state-DMQXxemS.js} +1 -1
  162. package/dist/assets/{stateDiagram-FKZM4ZOC-C7qgDrGA.js → stateDiagram-FKZM4ZOC-uuQ60pIl.js} +1 -1
  163. package/dist/assets/stateDiagram-v2-4FDKWEC3-D-GWhQTG.js +1 -0
  164. package/dist/assets/stex-CruVQx-P.js +1 -0
  165. package/dist/assets/{switch-CVKxYu_0.js → switch-Bwpd2AFq.js} +1 -1
  166. package/dist/assets/{terminal-Cr7wbEjz.js → terminal-Gl8Fi44y.js} +1 -1
  167. package/dist/assets/{time-D2GKc0U6.js → time-Bw8f15NM.js} +1 -1
  168. package/dist/assets/{timeline-definition-IT6M3QCI-hd6uJKGs.js → timeline-definition-IT6M3QCI-TKWeDnSQ.js} +1 -1
  169. package/dist/assets/{toggle-RCwU-rnE.js → toggle-C_gNjXg8.js} +1 -1
  170. package/dist/assets/{tooltip-D9723Brr.js → tooltip-CR_izE8Q.js} +1 -1
  171. package/dist/assets/tracing-DwAC7DWN.js +1 -0
  172. package/dist/assets/tracing-panel-D1iNgclL.js +2 -0
  173. package/dist/assets/{tree-Ch2-GuhG.js → tree-WVrWjdwv.js} +3 -3
  174. package/dist/assets/{useAddCell-YC7rpcmD.js → useAddCell-D6Q7JziZ.js} +1 -1
  175. package/dist/assets/{useBoolean-m1e6E3Ao.js → useBoolean-CrltYVhE.js} +1 -1
  176. package/dist/assets/{useCellActionButton-GUb2fXU8.js → useCellActionButton-B2G9OaX9.js} +1 -1
  177. package/dist/assets/{useDateFormatter-CpE7XQLs.js → useDateFormatter-DOoVZUts.js} +1 -1
  178. package/dist/assets/{useDeleteCell-DRUDRiy0.js → useDeleteCell-d8veThW7.js} +1 -1
  179. package/dist/assets/{useDependencyPanelTab-nFVMlEx0.js → useDependencyPanelTab-B3iIj3MO.js} +1 -1
  180. package/dist/assets/{useInstallPackage-C9V-on2J.js → useInstallPackage-CQEr5429.js} +1 -1
  181. package/dist/assets/{useNotebookActions-ChLHy-0O.js → useNotebookActions-SAEOWcBo.js} +1 -1
  182. package/dist/assets/{useNumberFormatter-DbDKSvEd.js → useNumberFormatter-BXZcbTzH.js} +1 -1
  183. package/dist/assets/{usePress-BHGkpw8X.js → usePress-alQ5Crny.js} +1 -1
  184. package/dist/assets/{useRunCells-5m6jCnyo.js → useRunCells-Dhl8ZTGh.js} +1 -1
  185. package/dist/assets/{useSplitCell-DGD9smMq.js → useSplitCell-BSnFazbH.js} +1 -1
  186. package/dist/assets/{utils-CdjCA1J8.js → utils-C24l2A1T.js} +1 -1
  187. package/dist/assets/{vega-component-CRbeDmeM.js → vega-component-9Pf4pVZL.js} +1 -1
  188. package/dist/assets/{vega-loader.browser-CZV6_g2i.js → vega-loader.browser-BJ9oKrvH.js} +1 -1
  189. package/dist/assets/{write-secret-modal-BCvuRAFb.js → write-secret-modal-BFCsWMoW.js} +1 -1
  190. package/dist/assets/{xychartDiagram-PRI3JC2R-CXlUBSbQ.js → xychartDiagram-PRI3JC2R-CIp-yeSa.js} +1 -1
  191. package/dist/index.html +125 -125
  192. package/package.json +1 -1
  193. package/src/__mocks__/notebook.ts +9 -9
  194. package/src/__tests__/branded.ts +20 -0
  195. package/src/components/app-config/user-config-form.tsx +5 -4
  196. package/src/components/data-table/__tests__/utils.test.ts +138 -1
  197. package/src/components/data-table/charts/__tests__/storage.test.ts +7 -7
  198. package/src/components/data-table/context-menu.tsx +9 -5
  199. package/src/components/data-table/data-table.tsx +3 -0
  200. package/src/components/data-table/range-focus/__tests__/atoms.test.ts +8 -2
  201. package/src/components/data-table/range-focus/__tests__/test-utils.ts +2 -0
  202. package/src/components/data-table/range-focus/__tests__/utils.test.ts +82 -8
  203. package/src/components/data-table/range-focus/atoms.ts +2 -2
  204. package/src/components/data-table/range-focus/utils.ts +50 -12
  205. package/src/components/data-table/types.ts +7 -0
  206. package/src/components/data-table/utils.ts +87 -0
  207. package/src/components/editor/__tests__/data-attributes.test.tsx +8 -8
  208. package/src/components/editor/ai/__tests__/completion-utils.test.ts +15 -15
  209. package/src/components/editor/navigation/__tests__/clipboard.test.ts +2 -2
  210. package/src/components/editor/navigation/__tests__/selection.test.ts +7 -6
  211. package/src/components/editor/navigation/__tests__/state.test.ts +8 -7
  212. package/src/components/editor/output/MarimoErrorOutput.tsx +7 -7
  213. package/src/components/editor/output/__tests__/traceback.test.tsx +4 -4
  214. package/src/components/editor/output/console/__tests__/ConsoleOutput.test.tsx +4 -4
  215. package/src/components/editor/renderers/vertical-layout/useFocusFirstEditor.ts +8 -1
  216. package/src/components/storage/storage-file-viewer.tsx +35 -1
  217. package/src/components/storage/storage-inspector.tsx +9 -4
  218. package/src/components/storage/storage-snippets.ts +3 -3
  219. package/src/components/tracing/tracing.tsx +3 -1
  220. package/src/components/ui/range-slider.tsx +108 -1
  221. package/src/core/ai/__tests__/staged-cells.test.ts +9 -8
  222. package/src/core/ai/context/providers/__tests__/cell-output.test.ts +31 -31
  223. package/src/core/ai/context/providers/__tests__/datasource.test.ts +3 -3
  224. package/src/core/ai/context/providers/__tests__/tables.test.ts +3 -2
  225. package/src/core/ai/context/providers/__tests__/variable.test.ts +84 -63
  226. package/src/core/ai/tools/__tests__/edit-notebook-tool.test.ts +10 -9
  227. package/src/core/ai/tools/__tests__/run-cells-tool.test.ts +6 -6
  228. package/src/core/ai/tools/edit-notebook-tool.ts +3 -3
  229. package/src/core/cells/__tests__/add-missing-import.test.ts +3 -3
  230. package/src/core/cells/__tests__/cells.test.ts +192 -135
  231. package/src/core/cells/__tests__/focus.test.ts +5 -4
  232. package/src/core/cells/__tests__/logs.test.ts +13 -12
  233. package/src/core/cells/__tests__/pending-delete-service.test.tsx +3 -3
  234. package/src/core/cells/__tests__/runs.test.ts +22 -21
  235. package/src/core/cells/__tests__/scrollCellIntoView.test.ts +8 -7
  236. package/src/core/cells/__tests__/session.test.ts +23 -22
  237. package/src/core/cells/cells.ts +1 -1
  238. package/src/core/cells/ids.ts +5 -5
  239. package/src/core/cells/logs.ts +2 -2
  240. package/src/core/cells/runs.ts +6 -8
  241. package/src/core/codemirror/__tests__/format.test.ts +34 -36
  242. package/src/core/codemirror/__tests__/setup.test.ts +2 -2
  243. package/src/core/codemirror/cells/__tests__/extensions.test.ts +114 -0
  244. package/src/core/codemirror/cells/__tests__/traceback-decorations.test.ts +33 -32
  245. package/src/core/codemirror/cells/extensions.ts +66 -23
  246. package/src/core/codemirror/completion/__tests__/keymap.test.ts +15 -35
  247. package/src/core/codemirror/completion/keymap.ts +14 -4
  248. package/src/core/codemirror/copilot/__tests__/getCodes.test.ts +12 -13
  249. package/src/core/codemirror/language/__tests__/utils.test.ts +3 -3
  250. package/src/core/codemirror/language/embedded/__tests__/embedded-python.test.ts +7 -8
  251. package/src/core/codemirror/language/languages/python.ts +4 -0
  252. package/src/core/codemirror/lsp/__tests__/notebook-lsp.test.ts +4 -3
  253. package/src/core/codemirror/lsp/notebook-lsp.ts +28 -2
  254. package/src/core/codemirror/reactive-references/__tests__/analyzer.test.ts +7 -6
  255. package/src/core/codemirror/reactive-references/analyzer.ts +2 -2
  256. package/src/core/codemirror/rtc/loro/__tests__/sync.test.ts +52 -0
  257. package/src/core/codemirror/rtc/loro/sync.ts +1 -0
  258. package/src/core/datasets/__tests__/data-source.test.ts +5 -6
  259. package/src/core/datasets/state.ts +1 -1
  260. package/src/core/errors/__tests__/errors.test.ts +2 -1
  261. package/src/core/export/__tests__/hooks.test.ts +37 -36
  262. package/src/core/islands/main.ts +2 -7
  263. package/src/core/kernel/__tests__/handlers.test.ts +5 -4
  264. package/src/core/kernel/handlers.ts +7 -4
  265. package/src/core/network/DeferredRequestRegistry.ts +2 -2
  266. package/src/core/network/__tests__/CachingRequestRegistry.test.ts +9 -10
  267. package/src/core/network/__tests__/DeferredRequestRegistry.test.ts +4 -6
  268. package/src/core/static/__tests__/virtual-file-tracker.test.ts +8 -8
  269. package/src/core/static/virtual-file-tracker.ts +1 -1
  270. package/src/core/storage/__tests__/state.test.ts +31 -21
  271. package/src/core/storage/state.ts +1 -1
  272. package/src/core/variables/__tests__/state.test.ts +6 -6
  273. package/src/core/variables/types.ts +2 -2
  274. package/src/core/wasm/__tests__/state.test.ts +8 -8
  275. package/src/core/websocket/useMarimoKernelConnection.tsx +12 -15
  276. package/src/css/md-tooltip.css +4 -39
  277. package/src/css/md.css +7 -0
  278. package/src/plugins/core/RenderHTML.tsx +17 -0
  279. package/src/plugins/core/__test__/RenderHTML.test.ts +45 -0
  280. package/src/plugins/core/sanitize-html.ts +25 -18
  281. package/src/plugins/impl/DataTablePlugin.tsx +23 -2
  282. package/src/plugins/impl/SliderPlugin.tsx +1 -3
  283. package/src/plugins/impl/__tests__/SliderPlugin.test.tsx +120 -0
  284. package/src/plugins/impl/anywidget/model.ts +1 -2
  285. package/src/stories/cell.stories.tsx +8 -8
  286. package/src/stories/layout/vertical/one-column.stories.tsx +9 -8
  287. package/src/stories/log-viewer.stories.tsx +8 -8
  288. package/src/stories/variables.stories.tsx +2 -2
  289. package/src/utils/__tests__/download.test.tsx +21 -20
  290. package/src/utils/copy.ts +18 -5
  291. package/src/utils/download.ts +4 -3
  292. package/src/utils/html-to-image.ts +6 -0
  293. package/src/utils/json/base64.ts +3 -3
  294. package/src/utils/traceback.ts +5 -3
  295. package/dist/assets/JsonOutput-DKXKGKvX.js +0 -46
  296. package/dist/assets/RenderHTML-DJ8khuob.js +0 -1
  297. package/dist/assets/ai-model-dropdown-DPTa_EpU.js +0 -5
  298. package/dist/assets/cell-editor-DX7IcqNr.js +0 -23
  299. package/dist/assets/channel-CkT8Qdo2.js +0 -1
  300. package/dist/assets/classDiagram-2ON5EDUG-BhMT_rTz.js +0 -1
  301. package/dist/assets/classDiagram-v2-WZHVMYZB-D-F4WEyb.js +0 -1
  302. package/dist/assets/copy-YwM0Pd7v.js +0 -1
  303. package/dist/assets/dist-BTqMkf4j.js +0 -1
  304. package/dist/assets/dist-DHw9sXeN.js +0 -1
  305. package/dist/assets/dist-Du0qZeXz.js +0 -1
  306. package/dist/assets/dist-a0FfbvMD.js +0 -1
  307. package/dist/assets/dist-cfkfP0ni.js +0 -1
  308. package/dist/assets/dist-pzQ9JG-p.js +0 -1
  309. package/dist/assets/file-explorer-panel-CB8vF5ob.js +0 -26
  310. package/dist/assets/home-page-itW0tRmv.js +0 -4
  311. package/dist/assets/html-to-image-BnSc-Wa0.js +0 -2
  312. package/dist/assets/index-0n92c_W7.js +0 -38
  313. package/dist/assets/mermaid-4DMBBIKO-PoHnhmy8.js +0 -1
  314. package/dist/assets/react-vega-Da-Ps9UW.js +0 -1
  315. package/dist/assets/stateDiagram-v2-4FDKWEC3-CEqeIlM0.js +0 -1
  316. package/dist/assets/stex-BBWVYm-R.js +0 -1
  317. package/dist/assets/tracing-DUVd0jtl.js +0 -1
  318. package/dist/assets/tracing-panel-CuTxPn_x.js +0 -2
  319. /package/dist/assets/{Combination-Cs9nbinQ.js → Combination-CSPK4t6z.js} +0 -0
  320. /package/dist/assets/{Deferred-BMfCOLaw.js → Deferred-CfyqLOPG.js} +0 -0
  321. /package/dist/assets/{SSRProvider-DC7ElCZZ.js → SSRProvider-CwqN9FWV.js} +0 -0
  322. /package/dist/assets/{badge-hTpPIsMT.js → badge-DImLVznf.js} +0 -0
  323. /package/dist/assets/{blob-CObhN-9g.js → blob-DgooIGjS.js} +0 -0
  324. /package/dist/assets/{bot-message-square-CK6eoGWy.js → bot-message-square-Dw41U6lL.js} +0 -0
  325. /package/dist/assets/{chart-no-axes-column-DV8gdCvH.js → chart-no-axes-column-a9XtWmzk.js} +0 -0
  326. /package/dist/assets/{check-BE0hEwVo.js → check-DZA_bRpw.js} +0 -0
  327. /package/dist/assets/{chevron-right-D0GQBpTb.js → chevron-right-CvVxySQk.js} +0 -0
  328. /package/dist/assets/{circle-check-gLIOLu8x.js → circle-check-CbaVeozR.js} +0 -0
  329. /package/dist/assets/{circle-play-DYGULlKZ.js → circle-play-DYn5nR6N.js} +0 -0
  330. /package/dist/assets/{circle-plus-CGG-gArM.js → circle-plus-nCze0-py.js} +0 -0
  331. /package/dist/assets/{clipboard-paste-DoYSN8Sv.js → clipboard-paste-EHXeKq9D.js} +0 -0
  332. /package/dist/assets/{code-xml-DwHPF_nL.js → code-xml-BlrJCgNZ.js} +0 -0
  333. /package/dist/assets/{copy-CkudG0Ej.js → copy-D6N1-xc1.js} +0 -0
  334. /package/dist/assets/{database-zap-DTWCDKdn.js → database-zap-BIGMFOfP.js} +0 -0
  335. /package/dist/assets/{defaultLocale-DK1MWd7f.js → defaultLocale-CGfP-Ye3.js} +0 -0
  336. /package/dist/assets/{defaultLocale-OkOxlkkM.js → defaultLocale-CuYNS33t.js} +0 -0
  337. /package/dist/assets/{dist-8UD0A5sU.js → dist-BF9S272t.js} +0 -0
  338. /package/dist/assets/{dist-apDpadc4.js → dist-Bk1itfBD.js} +0 -0
  339. /package/dist/assets/{dist-CQqv2gQL.js → dist-Dm11d0_A.js} +0 -0
  340. /package/dist/assets/{download-DBW9RXtT.js → download-nLboiTtW.js} +0 -0
  341. /package/dist/assets/{ellipsis-0_zJdF6H.js → ellipsis-d7eaKIFn.js} +0 -0
  342. /package/dist/assets/{ellipsis-vertical-CAB7tdza.js → ellipsis-vertical-DBQ5kWTo.js} +0 -0
  343. /package/dist/assets/{emotion-is-prop-valid.esm-D1keIaYa.js → emotion-is-prop-valid.esm-D7FeWASw.js} +0 -0
  344. /package/dist/assets/{errors-Bfogio62.js → errors-0IrrdfSG.js} +0 -0
  345. /package/dist/assets/{extends-DRbCSry7.js → extends-aq1t6BkR.js} +0 -0
  346. /package/dist/assets/{eye-off-vwi9L975.js → eye-off-CF3GmvXV.js} +0 -0
  347. /package/dist/assets/{file-DzHkbIdO.js → file-C-yMeaec.js} +0 -0
  348. /package/dist/assets/{file-headphone-B5q2Ow55.js → file-headphone-CPAP8asn.js} +0 -0
  349. /package/dist/assets/{file-plus-corner-lLQw9OnR.js → file-plus-corner-ks__N1mr.js} +0 -0
  350. /package/dist/assets/{github-BVtI-3F1.js → github-CRD4USKm.js} +0 -0
  351. /package/dist/assets/{image-DXfkah9d.js → image-CfyJzBP9.js} +0 -0
  352. /package/dist/assets/{link-Cf10mh3t.js → link-_dbp0XNB.js} +0 -0
  353. /package/dist/assets/{maps-OKerBHH8.js → maps-DQsjfyTy.js} +0 -0
  354. /package/dist/assets/{numbers-CYnquDho.js → numbers-Cno6K0UF.js} +0 -0
  355. /package/dist/assets/{objectWithoutPropertiesLoose-DP4vAkvg.js → objectWithoutPropertiesLoose-Dxmp_Bd_.js} +0 -0
  356. /package/dist/assets/{ordinal-BjO5SoTk.js → ordinal-CMAUv8ku.js} +0 -0
  357. /package/dist/assets/{paths-D2lG83Oh.js → paths-BVwhPRFT.js} +0 -0
  358. /package/dist/assets/{play-DKSqmedg.js → play-Bu_0ogGD.js} +0 -0
  359. /package/dist/assets/{plus-dVmh0yTy.js → plus-DJ99CUbx.js} +0 -0
  360. /package/dist/assets/{preload-helper-BW0IMuFq.js → preload-helper-y72bE5iF.js} +0 -0
  361. /package/dist/assets/{prop-types-RrUi-pOT.js → prop-types-DRf51_gT.js} +0 -0
  362. /package/dist/assets/{purify.es-BBn8CPhf.js → purify.es-Cf8RQecB.js} +0 -0
  363. /package/dist/assets/{range-DNqFcYmr.js → range-ClqUI25v.js} +0 -0
  364. /package/dist/assets/{refresh-ccw-DLpfIr8v.js → refresh-ccw-DQ6SJ8UC.js} +0 -0
  365. /package/dist/assets/{refresh-cw-CHAHPgkx.js → refresh-cw-Dg9tCj4k.js} +0 -0
  366. /package/dist/assets/{rotate-ccw-hLlF_82X.js → rotate-ccw-BCkZViUZ.js} +0 -0
  367. /package/dist/assets/{save-8fSvKYJT.js → save-CUdcv5qm.js} +0 -0
  368. /package/dist/assets/{session-BrEm7qNv.js → session-CByuQ-M-.js} +0 -0
  369. /package/dist/assets/{settings-CzQUw9rV.js → settings-B7nhfCat.js} +0 -0
  370. /package/dist/assets/{spinner-C5JoisA7.js → spinner-pCogyRyo.js} +0 -0
  371. /package/dist/assets/{square-CxAsQQ77.js → square-KVNDGpgy.js} +0 -0
  372. /package/dist/assets/{square-function-B006EYFX.js → square-function-BMNCw7Qb.js} +0 -0
  373. /package/dist/assets/{stex-0ac7Aukl.js → stex-C6JeW1YI.js} +0 -0
  374. /package/dist/assets/{table-BCnp9gKC.js → table-Od8PbuV-.js} +0 -0
  375. /package/dist/assets/{toDate-BqKH-Jd9.js → toDate-Cfp9W_O9.js} +0 -0
  376. /package/dist/assets/{trash-2-CAoNMkjq.js → trash-2-Bc_J7TQO.js} +0 -0
  377. /package/dist/assets/{trash-BZMAQneW.js → trash-Cxd189Vw.js} +0 -0
  378. /package/dist/assets/{triangle-alert-qZo1ox6x.js → triangle-alert-CzLrbLGN.js} +0 -0
  379. /package/dist/assets/{types-C1RN112u.js → types-CLOMZuqU.js} +0 -0
  380. /package/dist/assets/{use-toast-Hc8CXlvz.js → use-toast-BtZldTi5.js} +0 -0
  381. /package/dist/assets/{useDebounce-B0dx2Gp0.js → useDebounce-DwTO_rGp.js} +0 -0
  382. /package/dist/assets/{useTheme-BYXBU1of.js → useTheme-KDW4sktg.js} +0 -0
  383. /package/dist/assets/{uuid-ClFZlR7U.js → uuid-BukULOeS.js} +0 -0
  384. /package/dist/assets/{workflow-BmeqNuSH.js → workflow-BqHyyStM.js} +0 -0
  385. /package/dist/assets/{x-BI1M8X_v.js → x-CdLP7-v3.js} +0 -0
  386. /package/dist/assets/{youtube-DE-Ej6FR.js → youtube-CfU-SnDw.js} +0 -0
@@ -0,0 +1,120 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { act, fireEvent, render } from "@testing-library/react";
4
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
5
+ import type { z } from "zod";
6
+ import { SetupMocks } from "@/__mocks__/common";
7
+ import { initialModeAtom } from "@/core/mode";
8
+ import { store } from "@/core/state/jotai";
9
+ import type { IPluginProps } from "../../types";
10
+ import { SliderPlugin } from "../SliderPlugin";
11
+
12
+ SetupMocks.resizeObserver();
13
+
14
+ describe("SliderPlugin", () => {
15
+ beforeEach(() => {
16
+ vi.useFakeTimers();
17
+ store.set(initialModeAtom, "edit");
18
+ });
19
+
20
+ afterEach(() => {
21
+ vi.useRealTimers();
22
+ });
23
+
24
+ const createProps = (
25
+ debounce: boolean,
26
+ includeInput: boolean,
27
+ setValue: ReturnType<typeof vi.fn>,
28
+ ): IPluginProps<number, z.infer<typeof SliderPlugin.prototype.validator>> => {
29
+ return {
30
+ host: document.createElement("div"),
31
+ value: 5,
32
+ setValue,
33
+ data: {
34
+ initialValue: 5,
35
+ start: 0,
36
+ stop: 10,
37
+ step: 1,
38
+ label: "Test Slider",
39
+ debounce,
40
+ orientation: "horizontal" as const,
41
+ showValue: false,
42
+ fullWidth: false,
43
+ includeInput,
44
+ steps: null,
45
+ },
46
+ functions: {},
47
+ };
48
+ };
49
+
50
+ it("slider triggers setValue immediately when debounce is false", () => {
51
+ const plugin = new SliderPlugin();
52
+ const setValue = vi.fn();
53
+ const props = createProps(false, false, setValue);
54
+ const { container } = render(plugin.render(props));
55
+
56
+ act(() => {
57
+ vi.advanceTimersByTime(0);
58
+ });
59
+
60
+ const thumb = container.querySelector('[role="slider"]');
61
+ expect(thumb).toBeTruthy();
62
+
63
+ // Radix UI Slider updates on keyboard ArrowRight/ArrowLeft
64
+ act(() => {
65
+ (thumb as HTMLElement)?.focus();
66
+ fireEvent.keyDown(thumb!, { key: "ArrowRight" });
67
+ });
68
+
69
+ expect(setValue).toHaveBeenCalledWith(6);
70
+ });
71
+
72
+ it("slider does not trigger setValue immediately when debounce is true", () => {
73
+ const plugin = new SliderPlugin();
74
+ const setValue = vi.fn();
75
+ const props = createProps(true, false, setValue);
76
+ const { container } = render(plugin.render(props));
77
+
78
+ act(() => {
79
+ vi.advanceTimersByTime(0);
80
+ });
81
+
82
+ const thumb = container.querySelector('[role="slider"]');
83
+
84
+ act(() => {
85
+ (thumb as HTMLElement)?.focus();
86
+ // Simulate just a programmatic change that Radix would trigger via pointer move
87
+ // which fires onValueChange but not onValueCommit yet
88
+ // Because we can't easily separated Radix's internal pointer events in jsdom, we
89
+ // test the main issue: editable input. We can trust Radix's onValueChange vs onValueCommit.
90
+ });
91
+
92
+ // We verified above that NumberField works when debounce=true
93
+ expect(setValue).not.toHaveBeenCalled();
94
+ });
95
+
96
+ it("editable input triggers setValue immediately even when slider debounce is true", () => {
97
+ const plugin = new SliderPlugin();
98
+ const setValue = vi.fn();
99
+ const props = createProps(true, true, setValue);
100
+ const { getByRole } = render(plugin.render(props));
101
+
102
+ act(() => {
103
+ vi.advanceTimersByTime(0);
104
+ });
105
+
106
+ // The react-aria NumberField renders an input textbox.
107
+ const numericInput = getByRole("textbox");
108
+
109
+ act(() => {
110
+ // Simulate typing a new value and pressing enter
111
+ // With React-Aria NumberField, onChange fires on blur or enter
112
+ fireEvent.change(numericInput, { target: { value: "9" } });
113
+ fireEvent.blur(numericInput);
114
+ });
115
+
116
+ // Because the user explicitly typed 9 in the editable input,
117
+ // setValue should be called immediately regardless of debounce=true.
118
+ expect(setValue).toHaveBeenCalledWith(9);
119
+ });
120
+ });
@@ -323,8 +323,7 @@ export async function handleWidgetMessage(
323
323
  const msg = notification.message;
324
324
 
325
325
  // Decode base64 buffers to DataViews (present in open/update/custom messages)
326
- const base64Buffers: Base64String[] =
327
- "buffers" in msg ? (msg.buffers as Base64String[]) : [];
326
+ const base64Buffers: Base64String[] = "buffers" in msg ? msg.buffers : [];
328
327
  const buffers = base64Buffers.map(base64ToDataView);
329
328
 
330
329
  switch (msg.method) {
@@ -3,6 +3,7 @@
3
3
  import type { Meta, StoryObj } from "@storybook/react-vite";
4
4
  import { createStore, Provider } from "jotai";
5
5
  import { createRef } from "react";
6
+ import { cellId } from "@/__tests__/branded";
6
7
  import { type NotebookState, notebookAtom } from "@/core/cells/cells";
7
8
  import {
8
9
  type CellRuntimeState,
@@ -18,7 +19,6 @@ import { MultiColumn } from "@/utils/id-tree";
18
19
  import type { Milliseconds, Seconds } from "@/utils/time";
19
20
  import { Cell as EditorCell } from "../components/editor/notebook-cell";
20
21
  import { TooltipProvider } from "../components/ui/tooltip";
21
- import type { CellId } from "../core/cells/ids";
22
22
 
23
23
  type Story = StoryObj<typeof Cell>;
24
24
 
@@ -34,11 +34,11 @@ const Cell: React.FC<{
34
34
  config?: CellConfig;
35
35
  };
36
36
  }> = ({ overrides = {} }) => {
37
- const cellId = "1" as CellId;
37
+ const cid = cellId("1");
38
38
  const notebook: NotebookState = {
39
39
  cellData: {
40
- [cellId]: {
41
- id: cellId,
40
+ [cid]: {
41
+ id: cid,
42
42
  name: "cell_1",
43
43
  code: "import marimo as mo",
44
44
  edited: overrides.edited ?? false,
@@ -52,9 +52,9 @@ const Cell: React.FC<{
52
52
  lastExecutionTime: null,
53
53
  },
54
54
  },
55
- cellIds: MultiColumn.from([[cellId]]),
55
+ cellIds: MultiColumn.from([[cid]]),
56
56
  cellRuntime: {
57
- [cellId]: createCellRuntimeState({
57
+ [cid]: createCellRuntimeState({
58
58
  output: overrides.output ?? null,
59
59
  runElapsedTimeMs: overrides.runElapsedTimeMs ?? (10 as Milliseconds),
60
60
  status: overrides.status ?? "idle",
@@ -70,7 +70,7 @@ const Cell: React.FC<{
70
70
  }),
71
71
  },
72
72
  cellHandles: {
73
- [cellId]: createRef(),
73
+ [cid]: createRef(),
74
74
  },
75
75
  cellLogs: [],
76
76
  history: [],
@@ -86,7 +86,7 @@ const Cell: React.FC<{
86
86
  <Provider store={store}>
87
87
  <TooltipProvider>
88
88
  <EditorCell
89
- cellId={cellId}
89
+ cellId={cid}
90
90
  theme={"light"}
91
91
  showPlaceholder={false}
92
92
  mode={"edit"}
@@ -3,6 +3,7 @@
3
3
  import type { Meta } from "@storybook/react-vite";
4
4
  import { createStore, Provider } from "jotai";
5
5
  import { createRef } from "react";
6
+ import { cellId } from "@/__tests__/branded";
6
7
  import { type NotebookState, notebookAtom } from "@/core/cells/cells";
7
8
  import { createCellRuntimeState } from "@/core/cells/types";
8
9
  import { defaultUserConfig, parseAppConfig } from "@/core/config/config-schema";
@@ -99,8 +100,8 @@ export default {
99
100
  type W = Window & { __MARIMO_STATIC__?: { files: Record<string, unknown> } };
100
101
 
101
102
  const EditModeCodeShown = () => {
102
- const cellId = "Hbol" as CellId;
103
- const notebook = createLongReprNotebook(cellId);
103
+ const cid = cellId("Hbol");
104
+ const notebook = createLongReprNotebook(cid);
104
105
 
105
106
  const store = createStore();
106
107
  store.set(notebookAtom, notebook);
@@ -124,8 +125,8 @@ const EditModeCodeShown = () => {
124
125
  };
125
126
 
126
127
  const EditModeCodeHidden = () => {
127
- const cellId = "Hbol" as CellId;
128
- const notebook = createLongReprNotebook(cellId, true);
128
+ const cid = cellId("Hbol");
129
+ const notebook = createLongReprNotebook(cid, true);
129
130
 
130
131
  const store = createStore();
131
132
  store.set(notebookAtom, notebook);
@@ -149,8 +150,8 @@ const EditModeCodeHidden = () => {
149
150
  };
150
151
 
151
152
  const ReadModeCodeShown = () => {
152
- const cellId = "Hbol" as CellId;
153
- const notebook = createLongReprNotebook(cellId);
153
+ const cid = cellId("Hbol");
154
+ const notebook = createLongReprNotebook(cid);
154
155
 
155
156
  const store = createStore();
156
157
  store.set(notebookAtom, notebook);
@@ -168,8 +169,8 @@ const ReadModeCodeShown = () => {
168
169
  };
169
170
 
170
171
  const ReadModeCodeHidden = () => {
171
- const cellId = "Hbol" as CellId;
172
- const notebook = createLongReprNotebook(cellId);
172
+ const cid = cellId("Hbol");
173
+ const notebook = createLongReprNotebook(cid);
173
174
 
174
175
  const store = createStore();
175
176
  store.set(notebookAtom, notebook);
@@ -1,7 +1,7 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
  import type { Meta, StoryObj } from "@storybook/react-vite";
3
+ import { cellId } from "@/__tests__/branded";
3
4
  import { LogViewer } from "@/components/editor/chrome/panels/logs-panel";
4
- import type { CellId } from "@/core/cells/ids";
5
5
  import { Dialog } from "../components/ui/dialog";
6
6
  import { TooltipProvider } from "../components/ui/tooltip";
7
7
 
@@ -23,37 +23,37 @@ export const Primary: Story = {
23
23
  {
24
24
  timestamp: Date.now(),
25
25
  level: "stdout",
26
- cellId: "cell1" as CellId,
26
+ cellId: cellId("cell1"),
27
27
  message: "Hello world!",
28
28
  },
29
29
  {
30
30
  timestamp: Date.now(),
31
31
  level: "stdout",
32
- cellId: "cell1" as CellId,
32
+ cellId: cellId("cell1"),
33
33
  message: "Running cell...",
34
34
  },
35
35
  {
36
36
  timestamp: Date.now(),
37
37
  level: "stdout",
38
- cellId: "cell1" as CellId,
38
+ cellId: cellId("cell1"),
39
39
  message: "Done!",
40
40
  },
41
41
  {
42
42
  timestamp: Date.now(),
43
43
  level: "stderr",
44
- cellId: "cell2" as CellId,
44
+ cellId: cellId("cell2"),
45
45
  message: "Output is too large!",
46
46
  },
47
47
  {
48
48
  timestamp: Date.now(),
49
49
  level: "stderr",
50
- cellId: "cell2" as CellId,
50
+ cellId: cellId("cell2"),
51
51
  message: "String length is too short.".repeat(100),
52
52
  },
53
- ...Array.from({ length: 100 }).map((_, index) => ({
53
+ ...Array.from({ length: 100 }).map(() => ({
54
54
  timestamp: Date.now(),
55
55
  level: "stdout" as const,
56
- cellId: "cell1" as CellId,
56
+ cellId: cellId("cell1"),
57
57
  message: "Running cell...",
58
58
  })),
59
59
  ]}
@@ -1,7 +1,7 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
  import type { Meta, StoryObj } from "@storybook/react-vite";
3
+ import { cellId } from "@/__tests__/branded";
3
4
  import { VariableTable } from "@/components/variables/variables-table";
4
- import type { CellId } from "@/core/cells/ids";
5
5
 
6
6
  const meta: Meta<typeof VariableTable> = {
7
7
  title: "VariableTable",
@@ -51,7 +51,7 @@ export const Primary: Story = {
51
51
  <div className="max-w-4xl">
52
52
  <VariableTable
53
53
  variables={variables}
54
- cellIds={["2", "1", "3"] as CellId[]}
54
+ cellIds={[cellId("2"), cellId("1"), cellId("3")]}
55
55
  />
56
56
  </div>
57
57
  ),
@@ -1,7 +1,8 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
+
2
3
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
3
4
  import { Mocks } from "@/__mocks__/common";
4
- import type { CellId } from "@/core/cells/ids";
5
+ import { cellId } from "@/__tests__/branded";
5
6
  import { CellOutputId } from "@/core/cells/ids";
6
7
  import {
7
8
  downloadAsPDF,
@@ -218,7 +219,7 @@ describe("getImageDataUrlForCell", () => {
218
219
  beforeEach(() => {
219
220
  vi.clearAllMocks();
220
221
  mockElement = document.createElement("div");
221
- mockElement.id = CellOutputId.create("cell-1" as CellId);
222
+ mockElement.id = CellOutputId.create(cellId("cell-1"));
222
223
  document.body.append(mockElement);
223
224
  });
224
225
 
@@ -227,7 +228,7 @@ describe("getImageDataUrlForCell", () => {
227
228
  });
228
229
 
229
230
  it("should return undefined if element is not found", async () => {
230
- const result = await getImageDataUrlForCell("nonexistent" as CellId);
231
+ const result = await getImageDataUrlForCell(cellId("nonexistent"));
231
232
 
232
233
  expect(result).toBeUndefined();
233
234
  expect(Logger.error).toHaveBeenCalledWith(
@@ -238,7 +239,7 @@ describe("getImageDataUrlForCell", () => {
238
239
  it("should capture screenshot and return data URL", async () => {
239
240
  vi.mocked(toPng).mockResolvedValue(mockDataUrl);
240
241
 
241
- const result = await getImageDataUrlForCell("cell-1" as CellId);
242
+ const result = await getImageDataUrlForCell(cellId("cell-1"));
242
243
 
243
244
  expect(result).toBe(mockDataUrl);
244
245
  expect(toPng).toHaveBeenCalledWith(
@@ -253,7 +254,7 @@ describe("getImageDataUrlForCell", () => {
253
254
  it("should pass style options to prevent clipping", async () => {
254
255
  vi.mocked(toPng).mockResolvedValue(mockDataUrl);
255
256
 
256
- await getImageDataUrlForCell("cell-1" as CellId);
257
+ await getImageDataUrlForCell(cellId("cell-1"));
257
258
 
258
259
  expect(toPng).toHaveBeenCalledWith(
259
260
  mockElement,
@@ -274,7 +275,7 @@ describe("getImageDataUrlForCell", () => {
274
275
  });
275
276
  vi.mocked(toPng).mockResolvedValue(mockDataUrl);
276
277
 
277
- await getImageDataUrlForCell("cell-1" as CellId);
278
+ await getImageDataUrlForCell(cellId("cell-1"));
278
279
 
279
280
  expect(toPng).toHaveBeenCalledWith(
280
281
  mockElement,
@@ -287,7 +288,7 @@ describe("getImageDataUrlForCell", () => {
287
288
  it("should pass scrollbar hiding styles via extraStyleContent", async () => {
288
289
  vi.mocked(toPng).mockResolvedValue(mockDataUrl);
289
290
 
290
- await getImageDataUrlForCell("cell-1" as CellId);
291
+ await getImageDataUrlForCell(cellId("cell-1"));
291
292
 
292
293
  expect(toPng).toHaveBeenCalledWith(
293
294
  mockElement,
@@ -302,7 +303,7 @@ describe("getImageDataUrlForCell", () => {
302
303
  mockElement.style.maxHeight = "100px";
303
304
  vi.mocked(toPng).mockResolvedValue(mockDataUrl);
304
305
 
305
- await getImageDataUrlForCell("cell-1" as CellId);
306
+ await getImageDataUrlForCell(cellId("cell-1"));
306
307
 
307
308
  // DOM should remain unchanged
308
309
  expect(mockElement.style.overflow).toBe("hidden");
@@ -312,7 +313,7 @@ describe("getImageDataUrlForCell", () => {
312
313
  it("should throw error on failure", async () => {
313
314
  vi.mocked(toPng).mockRejectedValue(new Error("Capture failed"));
314
315
 
315
- await expect(getImageDataUrlForCell("cell-1" as CellId)).rejects.toThrow(
316
+ await expect(getImageDataUrlForCell(cellId("cell-1"))).rejects.toThrow(
316
317
  "Capture failed",
317
318
  );
318
319
  });
@@ -320,13 +321,13 @@ describe("getImageDataUrlForCell", () => {
320
321
  it("should handle concurrent captures correctly", async () => {
321
322
  // Create a second element
322
323
  const mockElement2 = document.createElement("div");
323
- mockElement2.id = CellOutputId.create("cell-2" as CellId);
324
+ mockElement2.id = CellOutputId.create(cellId("cell-2"));
324
325
  document.body.append(mockElement2);
325
326
 
326
327
  vi.mocked(toPng).mockResolvedValue(mockDataUrl);
327
328
 
328
- const capture1 = getImageDataUrlForCell("cell-1" as CellId);
329
- const capture2 = getImageDataUrlForCell("cell-2" as CellId);
329
+ const capture1 = getImageDataUrlForCell(cellId("cell-1"));
330
+ const capture2 = getImageDataUrlForCell(cellId("cell-2"));
330
331
 
331
332
  await Promise.all([capture1, capture2]);
332
333
 
@@ -437,8 +438,8 @@ describe("downloadHTMLAsImage", () => {
437
438
  await downloadHTMLAsImage({ element: mockElement, filename: "test" });
438
439
 
439
440
  expect(toast).toHaveBeenCalledWith({
440
- title: "Error",
441
- description: "Failed to download as PNG.",
441
+ title: "Failed to download as PNG",
442
+ description: "Failed",
442
443
  variant: "danger",
443
444
  });
444
445
  });
@@ -461,7 +462,7 @@ describe("downloadCellOutputAsImage", () => {
461
462
  beforeEach(() => {
462
463
  vi.clearAllMocks();
463
464
  mockElement = document.createElement("div");
464
- mockElement.id = CellOutputId.create("cell-1" as CellId);
465
+ mockElement.id = CellOutputId.create(cellId("cell-1"));
465
466
  mockAppEl = document.createElement("div");
466
467
  mockAppEl.id = "App";
467
468
  // Mock scrollTo since jsdom doesn't implement it
@@ -486,7 +487,7 @@ describe("downloadCellOutputAsImage", () => {
486
487
  });
487
488
 
488
489
  it("should show error toast if element not found", async () => {
489
- await downloadCellOutputAsImage("nonexistent" as CellId, "test");
490
+ await downloadCellOutputAsImage(cellId("nonexistent"), "test");
490
491
 
491
492
  expect(toPng).not.toHaveBeenCalled();
492
493
  expect(Logger.error).toHaveBeenCalledWith(
@@ -502,7 +503,7 @@ describe("downloadCellOutputAsImage", () => {
502
503
  it("should show error toast if toPng fails", async () => {
503
504
  vi.mocked(toPng).mockRejectedValue(new Error("Screenshot failed"));
504
505
 
505
- await downloadCellOutputAsImage("cell-1" as CellId, "result");
506
+ await downloadCellOutputAsImage(cellId("cell-1"), "result");
506
507
 
507
508
  expect(toast).toHaveBeenCalledWith({
508
509
  title: "Failed to download PNG",
@@ -514,7 +515,7 @@ describe("downloadCellOutputAsImage", () => {
514
515
  it("should download cell output as image", async () => {
515
516
  vi.mocked(toPng).mockResolvedValue(mockDataUrl);
516
517
 
517
- await downloadCellOutputAsImage("cell-1" as CellId, "result");
518
+ await downloadCellOutputAsImage(cellId("cell-1"), "result");
518
519
 
519
520
  expect(toPng).toHaveBeenCalledWith(
520
521
  mockElement,
@@ -529,7 +530,7 @@ describe("downloadCellOutputAsImage", () => {
529
530
  it("should pass style options to toPng for full content capture", async () => {
530
531
  vi.mocked(toPng).mockResolvedValue(mockDataUrl);
531
532
 
532
- await downloadCellOutputAsImage("cell-1" as CellId, "result");
533
+ await downloadCellOutputAsImage(cellId("cell-1"), "result");
533
534
 
534
535
  expect(toPng).toHaveBeenCalledWith(
535
536
  mockElement,
@@ -547,7 +548,7 @@ describe("downloadCellOutputAsImage", () => {
547
548
  mockElement.style.maxHeight = "100px";
548
549
  vi.mocked(toPng).mockResolvedValue(mockDataUrl);
549
550
 
550
- await downloadCellOutputAsImage("cell-1" as CellId, "result");
551
+ await downloadCellOutputAsImage(cellId("cell-1"), "result");
551
552
 
552
553
  // DOM should remain unchanged
553
554
  expect(mockElement.style.overflow).toBe("hidden");
package/src/utils/copy.ts CHANGED
@@ -2,21 +2,34 @@
2
2
  import { Logger } from "./Logger";
3
3
 
4
4
  /**
5
- * Tries to copy text to the clipboard using the navigator.clipboard API.
6
- * If that fails, it falls back to prompting the user to copy.
5
+ * Copy text to the clipboard. When `html` is provided, writes both
6
+ * text/html and text/plain so rich content (e.g. hyperlinks) is
7
+ * preserved when pasting into apps like Excel or Google Sheets.
7
8
  *
8
9
  * As of 2024-10-29, Safari does not support navigator.clipboard.writeText
9
10
  * when running localhost http.
10
11
  */
11
- export async function copyToClipboard(text: string) {
12
+ export async function copyToClipboard(text: string, html?: string) {
12
13
  if (navigator.clipboard === undefined) {
13
14
  Logger.warn("navigator.clipboard is not supported");
14
15
  window.prompt("Copy to clipboard: Ctrl+C, Enter", text);
15
16
  return;
16
17
  }
17
18
 
18
- await navigator.clipboard.writeText(text).catch(async () => {
19
- // Fallback to prompt
19
+ if (html && navigator.clipboard.write) {
20
+ try {
21
+ const item = new ClipboardItem({
22
+ "text/html": new Blob([html], { type: "text/html" }),
23
+ "text/plain": new Blob([text], { type: "text/plain" }),
24
+ });
25
+ await navigator.clipboard.write([item]);
26
+ return;
27
+ } catch {
28
+ Logger.warn("Failed to write rich text, falling back to plain text");
29
+ }
30
+ }
31
+
32
+ await navigator.clipboard.writeText(text).catch(() => {
20
33
  Logger.warn("Failed to copy to clipboard using navigator.clipboard");
21
34
  window.prompt("Copy to clipboard: Ctrl+C, Enter", text);
22
35
  });
@@ -156,10 +156,11 @@ export async function downloadHTMLAsImage(opts: {
156
156
  // Get screenshot
157
157
  const dataUrl = await toPng(element);
158
158
  downloadByURL(dataUrl, Filenames.toPNG(filename));
159
- } catch {
159
+ } catch (error) {
160
+ Logger.error("Error downloading as PNG", error);
160
161
  toast({
161
- title: "Error",
162
- description: "Failed to download as PNG.",
162
+ title: "Failed to download as PNG",
163
+ description: prettyError(error),
163
164
  variant: "danger",
164
165
  });
165
166
  } finally {
@@ -140,6 +140,11 @@ export const necessaryStyleProperties = [
140
140
  "cursor",
141
141
  ];
142
142
 
143
+ // 1x1 transparent PNG as a fallback for images that fail to embed (e.g., cross-origin).
144
+ // Without this, failed embeds leave external URLs in the cloned DOM, which taints the canvas.
145
+ const TRANSPARENT_PIXEL =
146
+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQI12NgAAIABQABNjN9GQAAAAlwSFlzAAAWJQAAFiUBSVIk8AAAAA0lEQVQI12P4z8BQDwAEgAF/QualIQAAAABJRU5ErkJggg==";
147
+
143
148
  /**
144
149
  * Default options for html-to-image conversions.
145
150
  * These handle common edge cases like filtering out toolbars and logging errors.
@@ -162,6 +167,7 @@ export const defaultHtmlToImageOptions: HtmlToImageOptions = {
162
167
  return true;
163
168
  }
164
169
  },
170
+ imagePlaceholder: TRANSPARENT_PIXEL,
165
171
  onImageErrorHandler: (event) => {
166
172
  Logger.error("Error loading image:", event);
167
173
  },
@@ -1,5 +1,6 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
+ import type { components } from "@marimo-team/marimo-api";
3
4
  import type { NotificationMessageData } from "@/core/kernel/messages";
4
5
  import type { TypedString } from "../typed";
5
6
 
@@ -11,9 +12,9 @@ export type JsonString<T = unknown> = TypedString<"Json"> & {
11
12
  };
12
13
 
13
14
  /**
14
- * A base64-encoded string.
15
+ * A base64-encoded string — derived from the generated OpenAPI schema.
15
16
  */
16
- export type Base64String = TypedString<"Base64">;
17
+ export type Base64String = components["schemas"]["Base64String"];
17
18
 
18
19
  /**
19
20
  * A data URL string.
@@ -120,7 +121,6 @@ export function dataViewToBase64(dataView: DataView): Base64String {
120
121
  export function safeExtractSetUIElementMessageBuffers(
121
122
  notification: NotificationMessageData<"send-ui-element-message">,
122
123
  ): readonly DataView[] {
123
- // @ts-expect-error - TypeScript doesn't know that these strings are actually base64 strings
124
124
  const strs: Base64String[] = notification.buffers ?? [];
125
125
  return strs.map(base64ToDataView);
126
126
  }
@@ -98,10 +98,12 @@ export function getTracebackInfo(domNode: DOMNode): TracebackInfo | null {
98
98
  10,
99
99
  );
100
100
  if (domNode.firstChild.nodeValue?.includes("__marimo__")) {
101
- const cellId = /__marimo__cell_(\w+)_/.exec(
101
+ const maybeCellId = /__marimo__cell_(\w+)_/.exec(
102
102
  domNode.firstChild.nodeValue,
103
- )?.[1] as CellId;
104
- if (cellId && lineNumber) {
103
+ )?.[1];
104
+ if (maybeCellId && lineNumber) {
105
+ // @ts-expect-error - Custom parser above will return valid cell ids
106
+ const cellId: CellId = maybeCellId;
105
107
  return { kind: "cell", cellId, lineNumber };
106
108
  }
107
109
  } else {