@marimo-team/frontend 0.23.10-dev2 → 0.23.10-dev21

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 (328) hide show
  1. package/dist/assets/{CellStatus-B4qIAZip.js → CellStatus-DMwpAXMZ.js} +1 -1
  2. package/dist/assets/{ConnectedDataExplorerComponent-W0UfNkj7.js → ConnectedDataExplorerComponent-LDEBEgrJ.js} +1 -1
  3. package/dist/assets/{DeferredRequestRegistry-CNldVcrP.js → DeferredRequestRegistry-CzVJLtsG.js} +1 -1
  4. package/dist/assets/{ImperativeModal-B3Th7k4R.js → ImperativeModal-BeQmePpG.js} +1 -1
  5. package/dist/assets/{JsonOutput-sim6s58u.js → JsonOutput-B70lHHvX.js} +13 -13
  6. package/dist/assets/{LazyAnyLanguageCodeMirror-BEvXb3VX.js → LazyAnyLanguageCodeMirror-Bv7Emsba.js} +2 -2
  7. package/dist/assets/{MarimoErrorOutput-IoNFtcZT.js → MarimoErrorOutput-BQo8RTib.js} +1 -1
  8. package/dist/assets/{RSPContexts-CdN1NyAt.js → RSPContexts-Clr6RnG2.js} +1 -1
  9. package/dist/assets/{RenderHTML-BpmpkBqm.js → RenderHTML-DTHZ0W_b.js} +1 -1
  10. package/dist/assets/RunButton-eAKy_Bh8.js +1 -0
  11. package/dist/assets/__vite-browser-external-CeH7R2Sf.js +1 -0
  12. package/dist/assets/__vite-browser-external-DTK_qJHK.js +1 -0
  13. package/dist/assets/{add-cell-with-ai-Da5NaDZ_.js → add-cell-with-ai-CxjV-UXz.js} +10 -10
  14. package/dist/assets/{add-connection-dialog-qtpp-Zvj.js → add-connection-dialog-BHZQ8B7f.js} +1 -1
  15. package/dist/assets/{agent-panel-CTXCTxgV.js → agent-panel-CSht0CWB.js} +1 -1
  16. package/dist/assets/{ai-model-dropdown-CDevlJTs.js → ai-model-dropdown-Bbt--cwF.js} +1 -1
  17. package/dist/assets/{any-language-editor-CXeLf_N8.js → any-language-editor-CT8HF6T6.js} +1 -1
  18. package/dist/assets/{app-config-button-BXlR4nKW.js → app-config-button-Z8gpKuxR.js} +1 -1
  19. package/dist/assets/{architectureDiagram-VXUJARFQ-Cgi4wo9P.js → architectureDiagram-VXUJARFQ-BheLn0Ym.js} +1 -1
  20. package/dist/assets/{blockDiagram-VD42YOAC-CsHap8-F.js → blockDiagram-VD42YOAC-DQbkWeS9.js} +1 -1
  21. package/dist/assets/{c4Diagram-YG6GDRKO-CZuArudM.js → c4Diagram-YG6GDRKO-i6Rgjbmb.js} +1 -1
  22. package/dist/assets/{cache-panel-DYiVMv5x.js → cache-panel-js9LAxft.js} +1 -1
  23. package/dist/assets/{cell-editor-Z3xJhFik.js → cell-editor-72C0Zygh.js} +5 -5
  24. package/dist/assets/{cell-link-D26yaUlf.js → cell-link-ChdlswWe.js} +1 -1
  25. package/dist/assets/{cells-DZTFtAVj.js → cells-B1UeqCia.js} +64 -64
  26. package/dist/assets/cells-BSWdJzbh.css +2 -0
  27. package/dist/assets/channel-CT-fBYRb.js +1 -0
  28. package/dist/assets/{chat-display-BgfGVz5S.js → chat-display-D4aawkG5.js} +1 -1
  29. package/dist/assets/{chat-panel-CXxu4TZ_.js → chat-panel-DqzUBGAQ.js} +1 -1
  30. package/dist/assets/{chat-ui-BkyiYz6q.js → chat-ui-sZqz1Jfc.js} +1 -1
  31. package/dist/assets/{chunk-5FQGJX7Z-D9iBG0F7.js → chunk-5FQGJX7Z-CfK7VoEZ.js} +3 -3
  32. package/dist/assets/{chunk-ABZYJK2D-BG7Eb4WW.js → chunk-ABZYJK2D-DimfG7ZJ.js} +1 -1
  33. package/dist/assets/{chunk-ATLVNIR6-CqLn9HI1.js → chunk-ATLVNIR6-DJKlaII_.js} +1 -1
  34. package/dist/assets/{chunk-B4BG7PRW-D4SyZNjn.js → chunk-B4BG7PRW-CBlVg3ZE.js} +1 -1
  35. package/dist/assets/{chunk-DI55MBZ5-t9uiMPiN.js → chunk-DI55MBZ5-bDEWTXNJ.js} +1 -1
  36. package/dist/assets/{chunk-EXTU4WIE-BmVk7Fyq.js → chunk-EXTU4WIE-B_Wal6ww.js} +1 -1
  37. package/dist/assets/{chunk-JA3XYJ7Z-B5b2QHOb.js → chunk-JA3XYJ7Z-BU8T1Ru_.js} +1 -1
  38. package/dist/assets/{chunk-JZLCHNYA-1_sVfAnw.js → chunk-JZLCHNYA-B3ot_8ri.js} +1 -1
  39. package/dist/assets/{chunk-N4CR4FBY-CFJoD-EQ.js → chunk-N4CR4FBY-DWtppJdE.js} +2 -2
  40. package/dist/assets/{chunk-QN33PNHL-BBE_bx9E.js → chunk-QN33PNHL-DWg4n9qk.js} +1 -1
  41. package/dist/assets/{chunk-QXUST7PY-Cu1nR-L2.js → chunk-QXUST7PY-DXSOUlP-.js} +1 -1
  42. package/dist/assets/{chunk-S3R3BYOJ-Cl0iW04v.js → chunk-S3R3BYOJ-D_Pw7wuz.js} +1 -1
  43. package/dist/assets/{chunk-TZMSLE5B-f5ix6iSd.js → chunk-TZMSLE5B-CoYomI0C.js} +1 -1
  44. package/dist/assets/classDiagram-2ON5EDUG-Cl2DZr6D.js +1 -0
  45. package/dist/assets/classDiagram-v2-WZHVMYZB-CZjujGyB.js +1 -0
  46. package/dist/assets/{code-block-37QAKDTI-B19XL3UR.js → code-block-37QAKDTI-DKLARgTX.js} +1 -1
  47. package/dist/assets/{column-preview-DEY7H3Nv.js → column-preview-BIhZkwx8.js} +1 -1
  48. package/dist/assets/{command-KARR7KMq.js → command-DUeag2QH.js} +1 -1
  49. package/dist/assets/{command-palette-DNG8Q_or.js → command-palette-DZeNDzl3.js} +1 -1
  50. package/dist/assets/{common-BMiQG5NP.js → common-6io3mKWY.js} +1 -1
  51. package/dist/assets/{components-r46WLNJG.js → components-CZp0urSr.js} +1 -1
  52. package/dist/assets/{components-DYnN18mE.js → components-oTA8dVbr.js} +1 -1
  53. package/dist/assets/{config-ClMcu_sV.js → config-DH1Le_e1.js} +1 -1
  54. package/dist/assets/{context-QkTujrKn.js → context-C1Tm_47t.js} +1 -1
  55. package/dist/assets/{copy-icon-OjtDb4gO.js → copy-icon-z0dzhmL4.js} +1 -1
  56. package/dist/assets/{dagre-6UL2VRFP-CVKXpDlr.js → dagre-6UL2VRFP-BSaSwo7-.js} +1 -1
  57. package/dist/assets/{data-grid-overlay-editor-BdDYn72O.js → data-grid-overlay-editor-B9m0-RJr.js} +1 -1
  58. package/dist/assets/{datasource-B7h8qmJZ.js → datasource-7l-qg4-b.js} +1 -1
  59. package/dist/assets/{dates-CTxr0EqQ.js → dates-DS_7IZoI.js} +1 -1
  60. package/dist/assets/{dependency-graph-panel-uTlnFEz0.js → dependency-graph-panel-CqUTS6ib.js} +1 -1
  61. package/dist/assets/{diagram-PSM6KHXK-BfzLNKp-.js → diagram-PSM6KHXK-CIQUQR45.js} +1 -1
  62. package/dist/assets/{diagram-QEK2KX5R-D_H77Ni5.js → diagram-QEK2KX5R-DZcaMxln.js} +1 -1
  63. package/dist/assets/{diagram-S2PKOQOG-DJmVBeMk.js → diagram-S2PKOQOG-Dh2iJpv3.js} +1 -1
  64. package/dist/assets/{dist-CpcTHxTt.js → dist-AZ6vC6cU.js} +1 -1
  65. package/dist/assets/{dist-CAIBlJtJ.js → dist-B0N0-Vx5.js} +1 -1
  66. package/dist/assets/{dist-BF6UNR6V.js → dist-B7WRiLJK.js} +1 -1
  67. package/dist/assets/{dist-CqEym3Sr.js → dist-BE3jO1WU.js} +1 -1
  68. package/dist/assets/{dist-CpTFpfm_.js → dist-BZr6MMV-.js} +1 -1
  69. package/dist/assets/dist-BcAHw69V.js +1 -0
  70. package/dist/assets/dist-C63_XIZr.js +1 -0
  71. package/dist/assets/dist-CDHGLvB9.js +1 -0
  72. package/dist/assets/{dist-CocyeH0q.js → dist-CSozmdJ6.js} +1 -1
  73. package/dist/assets/{dist-CKT_lJKW.js → dist-CXYKut-0.js} +1 -1
  74. package/dist/assets/{dist-CNyMUs19.js → dist-CaCigNvN.js} +1 -1
  75. package/dist/assets/{dist-CciFN1z_.js → dist-Ckx5b63P.js} +1 -1
  76. package/dist/assets/{dist-DqVEVg0c.js → dist-CtcHy8Nq.js} +1 -1
  77. package/dist/assets/dist-CtfyEjVj.js +1 -0
  78. package/dist/assets/{dist-Dr8mWwQ_.js → dist-D4Od5Ds4.js} +1 -1
  79. package/dist/assets/{dist-BQWswd1A.js → dist-D7OgrWeC.js} +1 -1
  80. package/dist/assets/{dist-CBSMxaO_.js → dist-DCqxOggh.js} +1 -1
  81. package/dist/assets/dist-DK9FT3QL.js +1 -0
  82. package/dist/assets/{dist-CJIOLS6O.js → dist-DoJEa3O2.js} +1 -1
  83. package/dist/assets/{dist-B7U5jPfs.js → dist-LFeDu06u.js} +1 -1
  84. package/dist/assets/dist-bRDVNr1s.js +1 -0
  85. package/dist/assets/{dist-_fyDen1v.js → dist-v1cdVFWt.js} +1 -1
  86. package/dist/assets/{documentation-panel-DNberbcy.js → documentation-panel-DBd3biLb.js} +1 -1
  87. package/dist/assets/download-DcQWg8-m.js +9 -0
  88. package/dist/assets/{edit-page-CJPMGRxx.js → edit-page-CzRGkZBf.js} +6 -6
  89. package/dist/assets/{erDiagram-Q2GNP2WA-ROTNwSJz.js → erDiagram-Q2GNP2WA-ChWC983-.js} +1 -1
  90. package/dist/assets/{error-banner-LdWZDbqd.js → error-banner-CLO6LFll.js} +1 -1
  91. package/dist/assets/{error-panel-D5hoQy8n.js → error-panel-BoafWEAy.js} +1 -1
  92. package/dist/assets/{es-DGgq-Wes.js → es-au8YY-8E.js} +1 -1
  93. package/dist/assets/{esm-CqWdmSnV.js → esm-Cb2bnV6o.js} +1 -1
  94. package/dist/assets/{esm-DeiyaVAJ.js → esm-Cv-OC7z7.js} +1 -1
  95. package/dist/assets/eye-off-BT-KOYV5.js +1 -0
  96. package/dist/assets/{field-zLmMOSA4.js → field-B4CdIHa9.js} +1 -1
  97. package/dist/assets/file-explorer-panel-B94q-LIo.js +26 -0
  98. package/dist/assets/{file-icons-BrqycRag.js → file-icons-8bgrYNC0.js} +1 -1
  99. package/dist/assets/{file-name-input-BNHbCr1E.js → file-name-input-C-nkVqG_.js} +1 -1
  100. package/dist/assets/{fileToBase64-DZfwJMrG.js → fileToBase64-BeYUTUzO.js} +1 -1
  101. package/dist/assets/{floating-outline-BnioYLEY.js → floating-outline-BBTN16Pi.js} +1 -1
  102. package/dist/assets/{flowDiagram-NV44I4VS--ee3IIVF.js → flowDiagram-NV44I4VS-CwJmdEwh.js} +1 -1
  103. package/dist/assets/{focus-D3CLyF2k.js → focus-C0_hrGnH.js} +1 -1
  104. package/dist/assets/{form-DdqAF_p5.js → form-Bq0lMUxv.js} +1 -1
  105. package/dist/assets/{formats-b7Sf6DAK.js → formats-DP_z0P-n.js} +1 -1
  106. package/dist/assets/{formatting-CSG9kqNb.js → formatting-XTtGIhGk.js} +1 -1
  107. package/dist/assets/{gallery-page-Bcf7fYLy.js → gallery-page-BDt-STRr.js} +1 -1
  108. package/dist/assets/{ganttDiagram-JELNMOA3-BGhmW2aP.js → ganttDiagram-JELNMOA3-CFD5UywU.js} +1 -1
  109. package/dist/assets/{gitGraphDiagram-V2S2FVAM-DXFCZcxr.js → gitGraphDiagram-V2S2FVAM-DRxIbU0j.js} +1 -1
  110. package/dist/assets/{glide-data-editor-DqKNgyfg.js → glide-data-editor-CipAqqJq.js} +4 -4
  111. package/dist/assets/{globals-7JnRMGd4.js → globals-LRz1TwrD.js} +1 -1
  112. package/dist/assets/{home-page-hvU2Ck7f.js → home-page-WYmfqTfP.js} +1 -1
  113. package/dist/assets/{hooks-CneeLUm3.js → hooks-DyYXumVE.js} +1 -1
  114. package/dist/assets/{html-to-image-BoIWRm-U.js → html-to-image-rfnXdqfv.js} +1 -1
  115. package/dist/assets/index-C1PbYyzr.js +38 -0
  116. package/dist/assets/index-DsKy-8bh.css +2 -0
  117. package/dist/assets/{infoDiagram-HS3SLOUP-CaaUieFa.js → infoDiagram-HS3SLOUP-VEIg8kOj.js} +1 -1
  118. package/dist/assets/{input-C3Hrdlqq.js → input-TSilD7AA.js} +1 -1
  119. package/dist/assets/{journeyDiagram-XKPGCS4Q-9D2JCyrN.js → journeyDiagram-XKPGCS4Q-CzBYjYDP.js} +1 -1
  120. package/dist/assets/{kanban-definition-3W4ZIXB7-DxV26rf-.js → kanban-definition-3W4ZIXB7-qf5QcOCE.js} +1 -1
  121. package/dist/assets/{kiosk-mode-CZZ1SAgt.js → kiosk-mode-Da3sO64c.js} +1 -1
  122. package/dist/assets/layout-DYQGL_5W.js +9 -0
  123. package/dist/assets/{linear-CW_ww8od.js → linear-Bg82k_or.js} +1 -1
  124. package/dist/assets/{links-1rtF1-eC.js → links-C6riF5tE.js} +1 -1
  125. package/dist/assets/{logs-panel-fbUDvNKz.js → logs-panel-CBQtSb87.js} +1 -1
  126. package/dist/assets/{markdown-renderer-BGO5zfUp.js → markdown-renderer-YEl8Z67L.js} +3 -3
  127. package/dist/assets/mermaid-4DMBBIKO-BuE4YRiK.js +1 -0
  128. package/dist/assets/{mermaid-Bes0ynI8.js → mermaid-DskEfPqD.js} +3 -3
  129. package/dist/assets/{mermaid-parser.core-aYU6qXxk.js → mermaid-parser.core-Cw_lBEej.js} +1 -1
  130. package/dist/assets/{mindmap-definition-VGOIOE7T-DTJAbAQ8.js → mindmap-definition-VGOIOE7T-b6NvhSJG.js} +1 -1
  131. package/dist/assets/{name-cell-input-IbrFfjTR.js → name-cell-input-CSSNMAt-.js} +1 -1
  132. package/dist/assets/{number-overlay-editor-DL4IwqDH.js → number-overlay-editor-DXGzRTU9.js} +1 -1
  133. package/dist/assets/{outline-panel-BQ87B9Ft.js → outline-panel-Bzu7s3vt.js} +1 -1
  134. package/dist/assets/{packages-panel-ChY1D8eZ.js → packages-panel-DsHF0wGn.js} +1 -1
  135. package/dist/assets/{pair-with-agent-modal-1tufe5MP.js → pair-with-agent-modal-Dh1WKytX.js} +1 -1
  136. package/dist/assets/{panels-DWrP0hzF.js → panels-Rpjf9H5q.js} +1 -1
  137. package/dist/assets/{pathUtils-CJjndqMI.js → pathUtils-vgBYIo5B.js} +1 -1
  138. package/dist/assets/{pieDiagram-ADFJNKIX-_3l2XiEo.js → pieDiagram-ADFJNKIX-DxRJfbR9.js} +1 -1
  139. package/dist/assets/{precisionRound-Cl9k9ZmS.js → precisionRound-NX56vp6K.js} +1 -1
  140. package/dist/assets/{process-output-CW1wS2Hr.js → process-output-D34znTpg.js} +1 -1
  141. package/dist/assets/{quadrantDiagram-AYHSOK5B-CX3AMAvR.js → quadrantDiagram-AYHSOK5B-CYmhoW3N.js} +1 -1
  142. package/dist/assets/{radio-group-9x2A1Fsc.js → radio-group-B4UJ7sCK.js} +1 -1
  143. package/dist/assets/{react-vega-CXIQBUis.js → react-vega-7FnlLMIn.js} +1 -1
  144. package/dist/assets/react-vega-hzWc6HV-.js +1 -0
  145. package/dist/assets/readonly-python-code-DRB4Oujr.js +1 -0
  146. package/dist/assets/{renderShortcut-CcFk3m01.js → renderShortcut-BTgMpqTK.js} +1 -1
  147. package/dist/assets/{request-registry--h6PWG50.js → request-registry-cDhqBe-K.js} +1 -1
  148. package/dist/assets/{requirementDiagram-UZGBJVZJ-ORHKBnkg.js → requirementDiagram-UZGBJVZJ-COy0V1k7.js} +1 -1
  149. package/dist/assets/{reveal-component-D13HX3en.js → reveal-component-CMD_BnK_.js} +3 -3
  150. package/dist/assets/{run-page-BNG5gaoM.js → run-page-BGVh53Fv.js} +1 -1
  151. package/dist/assets/{sankeyDiagram-TZEHDZUN-Dvb8kmWa.js → sankeyDiagram-TZEHDZUN-CltfYesU.js} +1 -1
  152. package/dist/assets/save-worker-Bcr7rl0C.js +77 -0
  153. package/dist/assets/scratchpad-panel-Bu5uSOnh.js +1 -0
  154. package/dist/assets/{secrets-panel-BBvdIhtU.js → secrets-panel-C-AiYcTm.js} +1 -1
  155. package/dist/assets/{sequenceDiagram-WL72ISMW-DQycpQtE.js → sequenceDiagram-WL72ISMW-poUheIRc.js} +1 -1
  156. package/dist/assets/session-panel-CUuZscI9.js +1 -0
  157. package/dist/assets/{share-DMwTZOTH.js → share-DQLjaPiK.js} +1 -1
  158. package/dist/assets/{snippets-panel-C4UEEe7X.js → snippets-panel-FZipjVGj.js} +1 -1
  159. package/dist/assets/{spec-CPQR_o92.js → spec-D1ptWKg6.js} +1 -1
  160. package/dist/assets/{state-BGDrw26V.js → state-B_c922_d.js} +1 -1
  161. package/dist/assets/{state-DTislnZA.js → state-CcAGAozT.js} +1 -1
  162. package/dist/assets/{state-D2ocm4JL.js → state-Dd_QMTIr.js} +1 -1
  163. package/dist/assets/{state-C6hNg_X-.js → state-DhERtUKY.js} +3 -3
  164. package/dist/assets/{stateDiagram-FKZM4ZOC-D78DIR_F.js → stateDiagram-FKZM4ZOC-DvWmNTbQ.js} +1 -1
  165. package/dist/assets/stateDiagram-v2-4FDKWEC3-B48rhKHl.js +1 -0
  166. package/dist/assets/stex-C8c2RsN8.js +1 -0
  167. package/dist/assets/{swiper-component-YGJlBaCp.js → swiper-component-B_R-BaT5.js} +1 -1
  168. package/dist/assets/{switch-BiU_sAcn.js → switch-NmKFCQsT.js} +1 -1
  169. package/dist/assets/{terminal-BYGihNNp.js → terminal-Brlgq9Xx.js} +1 -1
  170. package/dist/assets/{textarea-C3qk0qJ7.js → textarea-DsHy664W.js} +1 -1
  171. package/dist/assets/{time-DLdI7jB3.js → time-tROo95ox.js} +1 -1
  172. package/dist/assets/{timeline-definition-IT6M3QCI-DIywx3xf.js → timeline-definition-IT6M3QCI-CfgM6G05.js} +1 -1
  173. package/dist/assets/{tracing-B0e3S01T.js → tracing-B91zi1Cr.js} +1 -1
  174. package/dist/assets/{tracing-panel-CmLBnJCg.js → tracing-panel-B68PNYS8.js} +2 -2
  175. package/dist/assets/tree-actions-DfrTuUnZ.js +1 -0
  176. package/dist/assets/use-toast-9t59MOqR.js +1 -0
  177. package/dist/assets/{useBoolean-Bp18o6XG.js → useBoolean-DElMi9Nd.js} +1 -1
  178. package/dist/assets/useCellActionButton-DS0VmOT4.js +1 -0
  179. package/dist/assets/{useDateFormatter-ICIDGgHf.js → useDateFormatter-d3yglQmW.js} +1 -1
  180. package/dist/assets/useDeleteCell-ByueGyf0.js +1 -0
  181. package/dist/assets/{useDependencyPanelTab-DK6mAVzQ.js → useDependencyPanelTab-B-_R5nHC.js} +1 -1
  182. package/dist/assets/{useInstallPackage-DUF4IRRI.js → useInstallPackage-CKRPRNft.js} +1 -1
  183. package/dist/assets/useNotebookActions-BPxBZnuZ.js +1 -0
  184. package/dist/assets/{useNumberFormatter-ByUv-u9o.js → useNumberFormatter-CW4V-fpE.js} +1 -1
  185. package/dist/assets/{usePress-DQ_tAz5W.js → usePress-jH2RfcUG.js} +1 -1
  186. package/dist/assets/{useRunCells-Dt6DS29E.js → useRunCells-c7zq0oWm.js} +1 -1
  187. package/dist/assets/{useSplitCell-VdsEBWhq.js → useSplitCell-Dd5NLOUh.js} +1 -1
  188. package/dist/assets/{utils-DCL4n9wx.js → utils-DQRuLEof.js} +1 -1
  189. package/dist/assets/{vega-component-COOhhZ3h.js → vega-component-DqVeZqOU.js} +1 -1
  190. package/dist/assets/{vega-loader.browser-C8wT63Va.js → vega-loader.browser-xq8miGHn.js} +1 -1
  191. package/dist/assets/{worker-B38WhSlZ.js → worker-Bp53hInb.js} +6 -6
  192. package/dist/assets/{write-secret-modal-BFLlHwgU.js → write-secret-modal-BRlgVZs9.js} +1 -1
  193. package/dist/assets/{xychartDiagram-PRI3JC2R-CjS1dL8k.js → xychartDiagram-PRI3JC2R-Ij7-bbhi.js} +1 -1
  194. package/dist/index.html +117 -116
  195. package/package.json +3 -3
  196. package/src/components/data-table/__tests__/data-table.test.tsx +154 -12
  197. package/src/components/data-table/hover-tooltip/__tests__/content.test.ts +60 -0
  198. package/src/components/data-table/hover-tooltip/content.ts +44 -0
  199. package/src/components/data-table/hover-tooltip/hover-tooltip.tsx +55 -0
  200. package/src/components/data-table/hover-tooltip/use-table-hover-tooltip.ts +159 -0
  201. package/src/components/data-table/renderers.tsx +27 -43
  202. package/src/components/datasources/__tests__/filter-empty.test.ts +183 -0
  203. package/src/components/datasources/datasources.tsx +92 -3
  204. package/src/components/editor/cell/cell-context-menu.tsx +15 -2
  205. package/src/components/editor/documentation.css +16 -0
  206. package/src/components/editor/file-tree/file-explorer.tsx +8 -18
  207. package/src/components/editor/file-tree/tree-actions.tsx +46 -1
  208. package/src/components/slides/__tests__/minimap-actions.test.tsx +166 -0
  209. package/src/components/slides/minimap.tsx +127 -10
  210. package/src/components/storage/__tests__/storage-inspector.test.ts +53 -0
  211. package/src/components/storage/storage-inspector.tsx +68 -48
  212. package/src/components/ui/__tests__/use-toast.test.ts +75 -0
  213. package/src/components/ui/use-toast.ts +33 -13
  214. package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +0 -28
  215. package/src/core/cells/__tests__/cell.test.ts +29 -2
  216. package/src/core/cells/cell.ts +5 -1
  217. package/src/core/codemirror/go-to-definition/__tests__/utils.test.ts +37 -0
  218. package/src/core/codemirror/go-to-definition/commands.ts +17 -9
  219. package/src/core/codemirror/go-to-definition/utils.ts +1 -0
  220. package/src/core/codemirror/language/languages/sql/utils.ts +3 -1
  221. package/src/core/datasets/data-source-connections.ts +2 -0
  222. package/src/core/network/__tests__/requests-static.test.ts +30 -0
  223. package/src/core/network/requests-static.ts +14 -10
  224. package/src/core/wasm/worker/bootstrap.ts +12 -4
  225. package/src/plugins/layout/DownloadPlugin.tsx +1 -1
  226. package/dist/assets/RunButton-NDsrcmxR.js +0 -1
  227. package/dist/assets/__vite-browser-external-DuZehUbK.js +0 -1
  228. package/dist/assets/__vite-browser-external-Jpm67kL1.js +0 -1
  229. package/dist/assets/cells-jmgGt1lS.css +0 -2
  230. package/dist/assets/channel-6XBTrC3Q.js +0 -1
  231. package/dist/assets/classDiagram-2ON5EDUG-DlIMDu6i.js +0 -1
  232. package/dist/assets/classDiagram-v2-WZHVMYZB-CcIhhkQu.js +0 -1
  233. package/dist/assets/dist-BtL81uwZ.js +0 -1
  234. package/dist/assets/dist-Bww7hlVc.js +0 -1
  235. package/dist/assets/dist-CuCuJBdf.js +0 -1
  236. package/dist/assets/dist-W1iHBJeE.js +0 -1
  237. package/dist/assets/dist-cgiAP5sW.js +0 -1
  238. package/dist/assets/dist-vkd1160q.js +0 -1
  239. package/dist/assets/download-HxGLUdmz.js +0 -9
  240. package/dist/assets/file-explorer-panel-Dpu3jlWY.js +0 -26
  241. package/dist/assets/index-BIw7BKLH.css +0 -2
  242. package/dist/assets/index-CgI7_ZFC.js +0 -38
  243. package/dist/assets/layout-tjJkaOem.js +0 -9
  244. package/dist/assets/mermaid-4DMBBIKO-CekzuCTz.js +0 -1
  245. package/dist/assets/play-C7DcCsoO.js +0 -1
  246. package/dist/assets/react-vega-COx3Ibyn.js +0 -1
  247. package/dist/assets/readonly-python-code-CEfd-Ppu.js +0 -1
  248. package/dist/assets/save-worker-D2iQi-UK.js +0 -77
  249. package/dist/assets/scratchpad-panel-fupP7ZgK.js +0 -1
  250. package/dist/assets/session-panel-B9dcCqYr.js +0 -1
  251. package/dist/assets/stateDiagram-v2-4FDKWEC3-DX9rpmwh.js +0 -1
  252. package/dist/assets/stex-Ccczot4V.js +0 -1
  253. package/dist/assets/tree-actions-BM_EJr3E.js +0 -1
  254. package/dist/assets/use-toast-IPAtJGea.js +0 -1
  255. package/dist/assets/useCellActionButton-D2MPJYfm.js +0 -1
  256. package/dist/assets/useDeleteCell-vHE9B5l2.js +0 -1
  257. package/dist/assets/useNotebookActions-BV5iVwE4.js +0 -1
  258. /package/dist/assets/{Deferred-CP7vq682.js → Deferred-DWBqOhWa.js} +0 -0
  259. /package/dist/assets/{SSRProvider-CkFAAHXw.js → SSRProvider-BIDQNg9Q.js} +0 -0
  260. /package/dist/assets/{alert-yTS3WpF4.js → alert-DrHguQlr.js} +0 -0
  261. /package/dist/assets/{azure-B42ls0Ui.js → azure-DBVzcmvx.js} +0 -0
  262. /package/dist/assets/{badge-j3NGjqhR.js → badge-DOX1Xxa9.js} +0 -0
  263. /package/dist/assets/{blob-Bgnx1kuY.js → blob-3_FN0u9S.js} +0 -0
  264. /package/dist/assets/{bundle.esm-DjhGJy4I.js → bundle.esm-BXIlAZ6T.js} +0 -0
  265. /package/dist/assets/{card--6HRj4T9.js → card-BFWJLptH.js} +0 -0
  266. /package/dist/assets/{copy-LK56fFow.js → copy-Ch48HVPK.js} +0 -0
  267. /package/dist/assets/{defaultLocale-BLUna9fQ.js → defaultLocale-DPBdGRrH.js} +0 -0
  268. /package/dist/assets/{defaultLocale-DzliDDTm.js → defaultLocale-Lfi0pexn.js} +0 -0
  269. /package/dist/assets/{dialog-DzC_QCtT.js → dialog-DqOQT_n4.js} +0 -0
  270. /package/dist/assets/{dist-B1U1kGCR.js → dist-BU26XwgJ.js} +0 -0
  271. /package/dist/assets/{dist-ChF5Ln9c.js → dist-COs9gIu5.js} +0 -0
  272. /package/dist/assets/{dist-4ni0fia5.js → dist-DP9Si-4r.js} +0 -0
  273. /package/dist/assets/{dist-CPczQFlJ.js → dist-DkA__6UI.js} +0 -0
  274. /package/dist/assets/{emotion-is-prop-valid.esm-C59xfSYt.js → emotion-is-prop-valid.esm-Dangy3Bv.js} +0 -0
  275. /package/dist/assets/{en-US-Cb8rIK52.js → en-US-C7vnqfgk.js} +0 -0
  276. /package/dist/assets/{errors-vr57w7Ul.js → errors-iwK4b4VF.js} +0 -0
  277. /package/dist/assets/{events-CPoJAfgx.js → events-CBm-hwqS.js} +0 -0
  278. /package/dist/assets/{extends-BiFDv3jB.js → extends-Dqvpuc10.js} +0 -0
  279. /package/dist/assets/{file-BrdxGLRX.js → file-HTLbeC2b.js} +0 -0
  280. /package/dist/assets/{file-headphone-BrQspHac.js → file-headphone-B3fuktN0.js} +0 -0
  281. /package/dist/assets/{github-BKS_2Qwn.js → github-raQvpeuZ.js} +0 -0
  282. /package/dist/assets/{house-mGK3v0Mm.js → house-D2ldnAB9.js} +0 -0
  283. /package/dist/assets/{icons-Ol38nIbL.js → icons-8tfAri2V.js} +0 -0
  284. /package/dist/assets/{image-DQHXdEQn.js → image-BIibSXT6.js} +0 -0
  285. /package/dist/assets/{isValid-CklTTytn.js → isValid-DdlR4-BY.js} +0 -0
  286. /package/dist/assets/{kbd-CTUAEnEx.js → kbd-BJB2rf7K.js} +0 -0
  287. /package/dist/assets/{link-CfEtM3Rl.js → link-CoJxTwWE.js} +0 -0
  288. /package/dist/assets/{links-D1JoyKTt.js → links-B8WzCnbo.js} +0 -0
  289. /package/dist/assets/{message-circle-CWm2KnSx.js → message-circle-1YLdnr8A.js} +0 -0
  290. /package/dist/assets/{micromark-factory-space-BygYYKhs.js → micromark-factory-space-bqhKsQDn.js} +0 -0
  291. /package/dist/assets/{numbers-C3BAdHZE.js → numbers-mY4EY0yG.js} +0 -0
  292. /package/dist/assets/{objectWithoutPropertiesLoose-DfWeGRFv.js → objectWithoutPropertiesLoose-DoKw85w0.js} +0 -0
  293. /package/dist/assets/{ordinal-_nQ2r1qQ.js → ordinal-bISkqI45.js} +0 -0
  294. /package/dist/assets/{package-Tv6ztuzw.js → package-B8oXOUM-.js} +0 -0
  295. /package/dist/assets/{paths-D5zczhUD.js → paths-SFhaqGlE.js} +0 -0
  296. /package/dist/assets/{plus-BgB18UzY.js → plus-CxkHs8QM.js} +0 -0
  297. /package/dist/assets/{preload-helper-BPPi7vOr.js → preload-helper-DdZsAcJe.js} +0 -0
  298. /package/dist/assets/{prop-types-DaaA-ptl.js → prop-types-DVDiRdwc.js} +0 -0
  299. /package/dist/assets/{purify.es-B9KxLTTS.js → purify.es-DvRMX74T.js} +0 -0
  300. /package/dist/assets/{range-gMGfxVwZ.js → range-Dk7lXDev.js} +0 -0
  301. /package/dist/assets/{react-icons.esm-BNzu6e7h.js → react-icons.esm--O4lBTlZ.js} +0 -0
  302. /package/dist/assets/{react-resizable-panels.browser.esm-CV8-hvjx.js → react-resizable-panels.browser.esm-BdtIs0E-.js} +0 -0
  303. /package/dist/assets/{refresh-ccw-C-n2VFP5.js → refresh-ccw-DLc784Sj.js} +0 -0
  304. /package/dist/assets/{refresh-cw-DHwG4Mac.js → refresh-cw-a_9k9BK7.js} +0 -0
  305. /package/dist/assets/{rotate-ccw-CNsAb2VZ.js → rotate-ccw-DoQtGgvU.js} +0 -0
  306. /package/dist/assets/{save-Clg5dMoP.js → save-DZ5aail6.js} +0 -0
  307. /package/dist/assets/{semaphore-FlZezxaf.js → semaphore-X3ApuO41.js} +0 -0
  308. /package/dist/assets/{session-0B5NBztP.js → session-DGdfs0bJ.js} +0 -0
  309. /package/dist/assets/{settings-DfFe0dWD.js → settings-SnYErNWQ.js} +0 -0
  310. /package/dist/assets/{sparkles-lWUAsPhp.js → sparkles-CC9Bko6a.js} +0 -0
  311. /package/dist/assets/{spinner-Bhir8k53.js → spinner-UuZAUjoP.js} +0 -0
  312. /package/dist/assets/{square-function-uY_yJr5g.js → square-function-blYaQso8.js} +0 -0
  313. /package/dist/assets/{square-B5xWCt0a.js → square-rACnnz-q.js} +0 -0
  314. /package/dist/assets/{stex-BGrE-q7N.js → stex-lcoTi-7o.js} +0 -0
  315. /package/dist/assets/{table-BGPSHfig.js → table-Bgc-inJs.js} +0 -0
  316. /package/dist/assets/{trash-2-rVklqqFF.js → trash-2-BJLYnZpG.js} +0 -0
  317. /package/dist/assets/{trash-DqeDe-Ua.js → trash-wVOgGgQw.js} +0 -0
  318. /package/dist/assets/{triangle-alert-D2p96PEF.js → triangle-alert-CJ0ZIqcz.js} +0 -0
  319. /package/dist/assets/{types-W8WWuumF.js → types-BYwxXaSY.js} +0 -0
  320. /package/dist/assets/{useAsyncData-bgszE9F0.js → useAsyncData-Dg8E_bPh.js} +0 -0
  321. /package/dist/assets/{useDebounce-7xZwVnnu.js → useDebounce-_BZcy6gx.js} +0 -0
  322. /package/dist/assets/{useDeepCompareMemoize-zUHU--0D.js → useDeepCompareMemoize-CWcgQCbT.js} +0 -0
  323. /package/dist/assets/{useHotkey-TKvjHf1m.js → useHotkey-B5GI97Mf.js} +0 -0
  324. /package/dist/assets/{useIframeCapabilities-CcI1zSdn.js → useIframeCapabilities-DPVTppnD.js} +0 -0
  325. /package/dist/assets/{useTheme-CI2eq4XN.js → useTheme-BrivXfaa.js} +0 -0
  326. /package/dist/assets/{uuid-e9lSomwA.js → uuid-D_zLHhNi.js} +0 -0
  327. /package/dist/assets/{workflow-Bm8qF7yj.js → workflow-CgnDnnoN.js} +0 -0
  328. /package/dist/assets/{youtube-CbdpN8oL.js → youtube-3lRHw8NU.js} +0 -0
@@ -0,0 +1,44 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+ import type { Cell, RowData } from "@tanstack/react-table";
3
+ import type { ReactNode } from "react";
4
+ import { stringifyUnknownValue } from "../utils";
5
+
6
+ export function applyHoverTemplate<TData extends RowData>(
7
+ template: string,
8
+ cells: Cell<TData, unknown>[],
9
+ ): string {
10
+ const variableRegex = /{{(\w+)}}/g;
11
+ const idToValue = new Map<string, string>();
12
+ for (const c of cells) {
13
+ const s = stringifyUnknownValue({
14
+ value: c.getValue(),
15
+ nullAsEmptyString: true,
16
+ });
17
+ idToValue.set(c.column.id, s);
18
+ }
19
+ return template.replaceAll(variableRegex, (_substr, varName: string) => {
20
+ const val = idToValue.get(varName);
21
+ return val === undefined ? `{{${varName}}}` : val;
22
+ });
23
+ }
24
+
25
+ /**
26
+ * Resolve the tooltip content for a hovered cell.
27
+ *
28
+ * Cell-level (callable `hover_template`) takes precedence; otherwise the
29
+ * row-level string template is rendered against the row's visible cells.
30
+ * Returns `undefined` when there is nothing to show.
31
+ */
32
+ export function computeCellTooltipContent<TData extends RowData>(
33
+ cell: Cell<TData, unknown>,
34
+ hoverTemplate: string | null,
35
+ ): ReactNode {
36
+ const cellTitle = cell.getHoverTitle?.();
37
+ if (cellTitle != null && cellTitle !== "") {
38
+ return cellTitle;
39
+ }
40
+ if (hoverTemplate) {
41
+ return applyHoverTemplate(hoverTemplate, cell.row.getVisibleCells());
42
+ }
43
+ return undefined;
44
+ }
@@ -0,0 +1,55 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+ import {
3
+ TooltipContent,
4
+ TooltipPortal,
5
+ TooltipRoot,
6
+ TooltipTrigger,
7
+ } from "@/components/ui/tooltip";
8
+ import type { HoverTooltipState } from "./use-table-hover-tooltip";
9
+
10
+ interface HoverTooltipProps {
11
+ state: HoverTooltipState | null;
12
+ contentId: string;
13
+ onClose: () => void;
14
+ }
15
+
16
+ /**
17
+ * A single radix tooltip whose anchor is repositioned to the hovered cell.
18
+ * Rendering one instance per table (instead of one per cell) keeps the cost
19
+ * constant regardless of how many cells are on screen.
20
+ */
21
+ export const HoverTooltip = ({
22
+ state,
23
+ contentId,
24
+ onClose,
25
+ }: HoverTooltipProps) => {
26
+ return (
27
+ <TooltipRoot
28
+ open={state != null}
29
+ onOpenChange={(open) => {
30
+ if (!open) {
31
+ onClose();
32
+ }
33
+ }}
34
+ delayDuration={0}
35
+ disableHoverableContent={true}
36
+ >
37
+ <TooltipTrigger asChild={true}>
38
+ <div
39
+ aria-hidden={true}
40
+ style={{
41
+ position: "fixed",
42
+ top: state?.rect.top ?? 0,
43
+ left: state?.rect.left ?? 0,
44
+ width: state?.rect.width ?? 0,
45
+ height: state?.rect.height ?? 0,
46
+ pointerEvents: "none",
47
+ }}
48
+ />
49
+ </TooltipTrigger>
50
+ <TooltipPortal>
51
+ <TooltipContent id={contentId}>{state?.content}</TooltipContent>
52
+ </TooltipPortal>
53
+ </TooltipRoot>
54
+ );
55
+ };
@@ -0,0 +1,159 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+ import type { Cell, RowData, Table } from "@tanstack/react-table";
3
+ import {
4
+ type ReactNode,
5
+ useEffect,
6
+ useId,
7
+ useLayoutEffect,
8
+ useRef,
9
+ useState,
10
+ } from "react";
11
+ import useEvent from "react-use-event-hook";
12
+ import { computeCellTooltipContent } from "./content";
13
+
14
+ // Matches the default TooltipProvider delay (MarimoApp.tsx) for visual parity
15
+ // with the rest of the app's tooltips.
16
+ const TOOLTIP_DELAY_MS = 400;
17
+
18
+ export interface HoverTooltipState {
19
+ rect: { top: number; left: number; width: number; height: number };
20
+ content: ReactNode;
21
+ }
22
+
23
+ export function useTableHoverTooltip<TData extends RowData>({
24
+ table,
25
+ }: {
26
+ table: Table<TData>;
27
+ }) {
28
+ const hoverTemplate = table.getState().cellHoverTemplate || null;
29
+ const [tooltipState, setTooltipState] = useState<HoverTooltipState | null>(
30
+ null,
31
+ );
32
+ const timer = useRef<number | null>(null);
33
+
34
+ // Stable id linking the focused/hovered cell to the tooltip content for
35
+ // assistive tech (the radix trigger is an aria-hidden phantom anchor).
36
+ const tooltipContentId = useId();
37
+ const anchorCell = useRef<HTMLElement | null>(null);
38
+ // Focus fires for pointer interactions too; track pointer state so
39
+ // click/drag-select focus doesn't show a tooltip (keyboard focus still does).
40
+ const pointerDown = useRef(false);
41
+
42
+ const clearTimer = () => {
43
+ if (timer.current != null) {
44
+ clearTimeout(timer.current);
45
+ timer.current = null;
46
+ }
47
+ };
48
+
49
+ const hideTooltip = useEvent(() => {
50
+ clearTimer();
51
+ setTooltipState(null);
52
+ });
53
+
54
+ const showFor = (target: HTMLElement, content: ReactNode) => {
55
+ anchorCell.current = target;
56
+ const r = target.getBoundingClientRect();
57
+ setTooltipState({
58
+ rect: { top: r.top, left: r.left, width: r.width, height: r.height },
59
+ content,
60
+ });
61
+ };
62
+
63
+ // Point the real cell at the tooltip content while it is shown. Done in a
64
+ // layout effect (after commit) so React's re-render from `setTooltipState`
65
+ // can't clobber an imperatively set attribute; cleanup unlinks the previous
66
+ // cell.
67
+ useLayoutEffect(() => {
68
+ if (!tooltipState) {
69
+ return;
70
+ }
71
+ const cell = anchorCell.current;
72
+ cell?.setAttribute("aria-describedby", tooltipContentId);
73
+ return () => cell?.removeAttribute("aria-describedby");
74
+ }, [tooltipState, tooltipContentId]);
75
+
76
+ useEffect(() => {
77
+ const onDown = () => {
78
+ pointerDown.current = true;
79
+ };
80
+ const onUp = () => {
81
+ pointerDown.current = false;
82
+ };
83
+ window.addEventListener("mousedown", onDown, { capture: true });
84
+ window.addEventListener("mouseup", onUp, { capture: true });
85
+ return () => {
86
+ window.removeEventListener("mousedown", onDown, { capture: true });
87
+ window.removeEventListener("mouseup", onUp, { capture: true });
88
+ };
89
+ }, []);
90
+
91
+ const handleCellMouseOver = useEvent(
92
+ (e: React.MouseEvent, cell: Cell<TData, unknown>) => {
93
+ // Suppress while a mouse button is held (range-select drag).
94
+ if (e.buttons !== 0) {
95
+ return;
96
+ }
97
+ const target = e.currentTarget as HTMLElement;
98
+ const content = computeCellTooltipContent(cell, hoverTemplate);
99
+ if (content == null || content === "") {
100
+ hideTooltip();
101
+ return;
102
+ }
103
+ clearTimer();
104
+ timer.current = window.setTimeout(
105
+ () => showFor(target, content),
106
+ TOOLTIP_DELAY_MS,
107
+ );
108
+ },
109
+ );
110
+
111
+ const handleCellMouseLeave = useEvent(() => hideTooltip());
112
+
113
+ // Keyboard parity: cells are tabIndex=0, native `title` showed on focus too.
114
+ const handleCellFocus = useEvent(
115
+ (e: React.FocusEvent, cell: Cell<TData, unknown>) => {
116
+ // Cancel any pending hover-show so a stale timer can't overwrite the
117
+ // focus-triggered tooltip after the delay.
118
+ clearTimer();
119
+ // Focus also fires for click/drag-select; only keyboard focus (no pointer
120
+ // held) should show the tooltip, mirroring the hover drag suppression.
121
+ if (pointerDown.current) {
122
+ return;
123
+ }
124
+ const content = computeCellTooltipContent(cell, hoverTemplate);
125
+ if (content == null || content === "") {
126
+ return;
127
+ }
128
+ showFor(e.currentTarget as HTMLElement, content);
129
+ },
130
+ );
131
+
132
+ const handleCellBlur = useEvent(() => hideTooltip());
133
+
134
+ // The anchor rect is captured at hover time, so any scroll or resize leaves
135
+ // it stale; hide instead of tracking. Capture catches scrolls inside the
136
+ // table's own container too (scroll events don't bubble but do fire in
137
+ // capture).
138
+ useEffect(() => {
139
+ const opts = { passive: true, capture: true } as const;
140
+ window.addEventListener("scroll", hideTooltip, opts);
141
+ window.addEventListener("resize", hideTooltip);
142
+ return () => {
143
+ window.removeEventListener("scroll", hideTooltip, { capture: true });
144
+ window.removeEventListener("resize", hideTooltip);
145
+ };
146
+ }, [hideTooltip]);
147
+
148
+ useEffect(() => clearTimer, []);
149
+
150
+ return {
151
+ tooltipState,
152
+ tooltipContentId,
153
+ hideTooltip,
154
+ handleCellMouseOver,
155
+ handleCellMouseLeave,
156
+ handleCellFocus,
157
+ handleCellBlur,
158
+ };
159
+ }
@@ -24,11 +24,12 @@ import { cn } from "@/utils/cn";
24
24
  import { getCellDomProps } from "./cell-utils";
25
25
  import { COLUMN_WRAPPING_STYLES } from "./column-wrapping/feature";
26
26
  import { DataTableContextMenu } from "./context-menu";
27
+ import { HoverTooltip } from "./hover-tooltip/hover-tooltip";
28
+ import { useTableHoverTooltip } from "./hover-tooltip/use-table-hover-tooltip";
27
29
  import { CellRangeSelectionIndicator } from "./range-focus/cell-selection-indicator";
28
30
  import { useCellRangeSelection } from "./range-focus/use-cell-range-selection";
29
31
  import { useScrollIntoViewOnFocus } from "./range-focus/use-scroll-into-view";
30
32
  import { AUTO_WIDTH_MAX_COLUMNS, TABLE_ROW_HEIGHT_PX } from "./types";
31
- import { stringifyUnknownValue } from "./utils";
32
33
 
33
34
  export function renderTableHeader<TData>(
34
35
  table: Table<TData>,
@@ -135,24 +136,7 @@ export const DataTableBody = <TData,>({
135
136
  contextMenuCell.current = cell;
136
137
  });
137
138
 
138
- function applyHoverTemplate(
139
- template: string,
140
- cells: Cell<TData, unknown>[],
141
- ): string {
142
- const variableRegex = /{{(\w+)}}/g;
143
- // Map column id -> stringified value
144
- const idToValue = new Map<string, string>();
145
- for (const c of cells) {
146
- const v = c.getValue();
147
- // Prefer empty string for nulls to keep tooltip clean
148
- const s = stringifyUnknownValue({ value: v, nullAsEmptyString: true });
149
- idToValue.set(c.column.id, s);
150
- }
151
- return template.replaceAll(variableRegex, (_substr, varName: string) => {
152
- const val = idToValue.get(varName);
153
- return val === undefined ? `{{${varName}}}` : val;
154
- });
155
- }
139
+ const hoverTooltip = useTableHoverTooltip({ table });
156
140
 
157
141
  const renderCells = (cells: Cell<TData, unknown>[]) => {
158
142
  return cells.map((cell) => {
@@ -163,7 +147,6 @@ export const DataTableBody = <TData,>({
163
147
  pinningstyle,
164
148
  );
165
149
 
166
- const title = cell.getHoverTitle?.() ?? undefined;
167
150
  return (
168
151
  <TableCell
169
152
  tabIndex={0}
@@ -178,10 +161,18 @@ export const DataTableBody = <TData,>({
178
161
  className,
179
162
  )}
180
163
  style={style}
181
- title={title}
182
- onMouseDown={(e) => handleCellMouseDown(e, cell)}
164
+ onMouseDown={(e) => {
165
+ handleCellMouseDown(e, cell);
166
+ hoverTooltip.hideTooltip();
167
+ }}
183
168
  onMouseUp={handleCellMouseUp}
184
- onMouseOver={(e) => handleCellMouseOver(e, cell)}
169
+ onMouseOver={(e) => {
170
+ handleCellMouseOver(e, cell);
171
+ hoverTooltip.handleCellMouseOver(e, cell);
172
+ }}
173
+ onMouseLeave={hoverTooltip.handleCellMouseLeave}
174
+ onFocus={(e) => hoverTooltip.handleCellFocus(e, cell)}
175
+ onBlur={hoverTooltip.handleCellBlur}
185
176
  onContextMenu={() => handleContextMenu(cell)}
186
177
  >
187
178
  <CellRangeSelectionIndicator cellId={cell.id} />
@@ -200,8 +191,6 @@ export const DataTableBody = <TData,>({
200
191
  }
201
192
  };
202
193
 
203
- const hoverTemplate = table.getState().cellHoverTemplate || null;
204
-
205
194
  const renderRow = (row: Row<TData>) => {
206
195
  // Only find the row index if the row viewer panel is open
207
196
  const rowIndex = rowViewerPanelOpen
@@ -209,22 +198,10 @@ export const DataTableBody = <TData,>({
209
198
  : undefined;
210
199
  const isRowViewedInPanel = rowViewerPanelOpen && viewedRowIdx === rowIndex;
211
200
 
212
- // Compute hover title once per row using all visible cells
213
- let rowTitle: string | undefined;
214
- if (hoverTemplate) {
215
- const visibleCells = row.getVisibleCells?.() ?? [
216
- ...row.getLeftVisibleCells(),
217
- ...row.getCenterVisibleCells(),
218
- ...row.getRightVisibleCells(),
219
- ];
220
- rowTitle = applyHoverTemplate(hoverTemplate, visibleCells);
221
- }
222
-
223
201
  return (
224
202
  <TableRow
225
203
  key={row.id}
226
204
  data-state={row.getIsSelected() && "selected"}
227
- title={rowTitle}
228
205
  // These classes ensure that empty rows (nulls) still render
229
206
  className={cn(
230
207
  "border-t h-6",
@@ -296,12 +273,19 @@ export const DataTableBody = <TData,>({
296
273
  );
297
274
 
298
275
  return (
299
- <DataTableContextMenu
300
- tableBody={tableBody}
301
- contextMenuRef={contextMenuCell}
302
- tableRef={tableRef}
303
- copyAllCells={handleCopyAllCells}
304
- />
276
+ <>
277
+ <DataTableContextMenu
278
+ tableBody={tableBody}
279
+ contextMenuRef={contextMenuCell}
280
+ tableRef={tableRef}
281
+ copyAllCells={handleCopyAllCells}
282
+ />
283
+ <HoverTooltip
284
+ state={hoverTooltip.tooltipState}
285
+ contentId={hoverTooltip.tooltipContentId}
286
+ onClose={hoverTooltip.hideTooltip}
287
+ />
288
+ </>
305
289
  );
306
290
  };
307
291
 
@@ -0,0 +1,183 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { describe, expect, it } from "vitest";
4
+ import type {
5
+ Database,
6
+ DatabaseSchema,
7
+ DataTable,
8
+ } from "@/core/kernel/messages";
9
+ import { filterEmptyDatabases } from "../datasources";
10
+
11
+ function makeTable(name: string): DataTable {
12
+ return {
13
+ name,
14
+ columns: [],
15
+ source: "memory",
16
+ source_type: "local",
17
+ type: "table",
18
+ engine: null,
19
+ indexes: null,
20
+ num_columns: null,
21
+ num_rows: null,
22
+ variable_name: null,
23
+ primary_keys: null,
24
+ };
25
+ }
26
+
27
+ function makeSchema(opts: {
28
+ name: string;
29
+ tables: DataTable[];
30
+ tables_resolved?: boolean;
31
+ }): DatabaseSchema {
32
+ return {
33
+ name: opts.name,
34
+ tables: opts.tables,
35
+ tables_resolved: opts.tables_resolved ?? true,
36
+ };
37
+ }
38
+
39
+ function makeDatabase(
40
+ name: string,
41
+ schemas: DatabaseSchema[],
42
+ schemas_resolved = true,
43
+ ): Database {
44
+ return {
45
+ name,
46
+ dialect: "duckdb",
47
+ schemas,
48
+ schemas_resolved,
49
+ engine: null,
50
+ };
51
+ }
52
+
53
+ describe("filterEmptyDatabases", () => {
54
+ it("hides schemas whose tables are resolved and empty", () => {
55
+ const databases = [
56
+ makeDatabase("memory", [
57
+ makeSchema({ name: "main", tables: [makeTable("t1")] }),
58
+ makeSchema({ name: "empty_schema", tables: [] }),
59
+ ]),
60
+ ];
61
+
62
+ expect(filterEmptyDatabases(databases)).toEqual([
63
+ makeDatabase("memory", [
64
+ makeSchema({ name: "main", tables: [makeTable("t1")] }),
65
+ ]),
66
+ ]);
67
+ });
68
+
69
+ it("preserves databases whose schemas have not been resolved yet (lazy state)", () => {
70
+ const databases = [
71
+ makeDatabase("not_loaded_yet", [], /* schemas_resolved */ false),
72
+ ];
73
+
74
+ expect(filterEmptyDatabases(databases)).toEqual([
75
+ makeDatabase("not_loaded_yet", [], false),
76
+ ]);
77
+ });
78
+
79
+ it("hides databases that have been resolved as empty", () => {
80
+ const databases = [
81
+ makeDatabase("really_empty", [], /* schemas_resolved */ true),
82
+ makeDatabase("has_tables", [
83
+ makeSchema({ name: "main", tables: [makeTable("t1")] }),
84
+ ]),
85
+ ];
86
+
87
+ expect(filterEmptyDatabases(databases)).toEqual([
88
+ makeDatabase("has_tables", [
89
+ makeSchema({ name: "main", tables: [makeTable("t1")] }),
90
+ ]),
91
+ ]);
92
+ });
93
+
94
+ it("hides databases whose schemas all filtered to empty", () => {
95
+ const databases = [
96
+ makeDatabase("only_empty", [
97
+ makeSchema({ name: "a", tables: [] }),
98
+ makeSchema({ name: "b", tables: [] }),
99
+ ]),
100
+ makeDatabase("has_tables", [
101
+ makeSchema({ name: "main", tables: [makeTable("t1")] }),
102
+ ]),
103
+ ];
104
+
105
+ expect(filterEmptyDatabases(databases)).toEqual([
106
+ makeDatabase("has_tables", [
107
+ makeSchema({ name: "main", tables: [makeTable("t1")] }),
108
+ ]),
109
+ ]);
110
+ });
111
+
112
+ it("treats missing schemas_resolved as resolved (backward compatible)", () => {
113
+ const databases = [
114
+ { name: "memory", dialect: "duckdb", schemas: [], engine: null },
115
+ ] as Database[];
116
+
117
+ expect(filterEmptyDatabases(databases)).toEqual([]);
118
+ });
119
+
120
+ it("preserves schemas whose tables have not been resolved yet", () => {
121
+ const databases = [
122
+ makeDatabase("snowflake_db", [
123
+ // include_tables=False was used; the schema is not actually empty,
124
+ // tables will be fetched lazily on expand.
125
+ makeSchema({ name: "public", tables: [], tables_resolved: false }),
126
+ makeSchema({ name: "audit", tables: [], tables_resolved: false }),
127
+ makeSchema({
128
+ name: "really_empty",
129
+ tables: [],
130
+ tables_resolved: true,
131
+ }),
132
+ ]),
133
+ ];
134
+
135
+ expect(filterEmptyDatabases(databases)).toEqual([
136
+ makeDatabase("snowflake_db", [
137
+ makeSchema({ name: "public", tables: [], tables_resolved: false }),
138
+ makeSchema({ name: "audit", tables: [], tables_resolved: false }),
139
+ ]),
140
+ ]);
141
+ });
142
+
143
+ it("treats missing tables_resolved as resolved (backward compatible)", () => {
144
+ // Older payloads predating the new flag may omit it; default semantics
145
+ // treat the schema as resolved/authoritative.
146
+ const databases = [
147
+ makeDatabase("memory", [
148
+ { name: "main", tables: [makeTable("t1")] },
149
+ { name: "empty_schema", tables: [] },
150
+ ] as DatabaseSchema[]),
151
+ ];
152
+
153
+ expect(filterEmptyDatabases(databases)).toEqual([
154
+ makeDatabase("memory", [
155
+ { name: "main", tables: [makeTable("t1")] },
156
+ ] as DatabaseSchema[]),
157
+ ]);
158
+ });
159
+
160
+ it("returns the same reference when nothing was filtered", () => {
161
+ const databases = [
162
+ makeDatabase("memory", [
163
+ makeSchema({ name: "main", tables: [makeTable("t1")] }),
164
+ ]),
165
+ ];
166
+
167
+ expect(filterEmptyDatabases(databases)).toBe(databases);
168
+ });
169
+
170
+ it("does not mutate the input", () => {
171
+ const databases = [
172
+ makeDatabase("memory", [
173
+ makeSchema({ name: "main", tables: [makeTable("t1")] }),
174
+ makeSchema({ name: "empty_schema", tables: [] }),
175
+ ]),
176
+ ];
177
+ const snapshot = JSON.parse(JSON.stringify(databases));
178
+
179
+ filterEmptyDatabases(databases);
180
+
181
+ expect(databases).toEqual(snapshot);
182
+ });
183
+ });