@marimo-team/frontend 0.24.1-dev99 → 0.24.2-dev129

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 (304) hide show
  1. package/dist/assets/{CellStatus-wvvZkEPY.js → CellStatus-LoTwk7fR.js} +1 -1
  2. package/dist/assets/{ConnectedDataExplorerComponent-Dkqk66Mv.js → ConnectedDataExplorerComponent-DW6cJ-Lr.js} +1 -1
  3. package/dist/assets/{ErrorBoundary-npvnLp9x.js → ErrorBoundary-CDVGFYfr.js} +1 -1
  4. package/dist/assets/JsonOutput-CzIGPEZQ.js +50 -0
  5. package/dist/assets/{LazyAnyLanguageCodeMirror-zSwE3dvU.js → LazyAnyLanguageCodeMirror-LboTfNDv.js} +2 -2
  6. package/dist/assets/{MarimoErrorOutput-CeUo5oka.js → MarimoErrorOutput-DqpoYOvw.js} +1 -1
  7. package/dist/assets/RenderHTML-DwhMAd9l.js +1 -0
  8. package/dist/assets/{RunButton-BaV8A-z4.js → RunButton-BlpbweyH.js} +1 -1
  9. package/dist/assets/{VisuallyHidden-SChAyKIQ.js → VisuallyHidden-CBMDmozt.js} +1 -1
  10. package/dist/assets/{add-cell-with-ai-DoLkfzYn.js → add-cell-with-ai-R-3DS79K.js} +1 -1
  11. package/dist/assets/{add-connection-dialog-C1IF1nR0.js → add-connection-dialog-BGfpw5lC.js} +1 -1
  12. package/dist/assets/{agent-panel-CG154QrU.js → agent-panel-Cgt_K0tD.js} +1 -1
  13. package/dist/assets/ai-model-dropdown-DCmiuk7x.js +3 -0
  14. package/dist/assets/alert-dialog-CA-2mBs3.js +11 -0
  15. package/dist/assets/any-language-editor-DflLjPSQ.js +1 -0
  16. package/dist/assets/{app-config-button-ktBH1pEB.js → app-config-button-DUJC6b6x.js} +1 -1
  17. package/dist/assets/badge-BSgtkC2e.js +1 -0
  18. package/dist/assets/{blob-DVtvZmWH.js → blob-B2d_dHwI.js} +1 -1
  19. package/dist/assets/button-Dh8EwhsF.js +1 -0
  20. package/dist/assets/{cache-panel-C7jsiqCa.js → cache-panel-DqgtC98s.js} +1 -1
  21. package/dist/assets/{capabilities-LWzb3E3M.js → capabilities-D8kPgMKr.js} +1 -1
  22. package/dist/assets/{cell-editor-DoDqaCZp.js → cell-editor-Dw09J9yD.js} +6 -6
  23. package/dist/assets/{cell-link-Dqqt6Rgs.js → cell-link-6burT2XF.js} +1 -1
  24. package/dist/assets/{cells-D4RwofRx.js → cells-Cp-G34Rz.js} +56 -56
  25. package/dist/assets/{chat-display-COmzHgx5.js → chat-display-Bu405iyc.js} +1 -1
  26. package/dist/assets/{chat-panel-B2tyEfGW.js → chat-panel-BlUwNY5j.js} +1 -1
  27. package/dist/assets/{chat-ui-B5bsr-7V.js → chat-ui-DGiVDzNh.js} +1 -1
  28. package/dist/assets/{circle--ysFeW9z.js → circle-ByJdIf5p.js} +1 -1
  29. package/dist/assets/{column-preview-CW5DPFvy.js → column-preview-riSXbYe_.js} +1 -1
  30. package/dist/assets/{command-palette-B9dyvMvJ.js → command-palette-CCVzJcyf.js} +1 -1
  31. package/dist/assets/{common-CC3tuCpi.js → common-CHR7w7aQ.js} +1 -1
  32. package/dist/assets/{components-D02ZZZMP.js → components-C7mWrmXr.js} +1 -1
  33. package/dist/assets/{components-CBlGIIAX.js → components-DUYCXngU.js} +1 -1
  34. package/dist/assets/config-B-r81uZ6.js +1 -0
  35. package/dist/assets/{config-D4frVGQ4.js → config-BlNM0caL.js} +1 -1
  36. package/dist/assets/{copy-Cg05mM3C.js → copy-DlEomWzI.js} +1 -1
  37. package/dist/assets/{copy-icon-CRxVZTJ9.js → copy-icon-z8fuTE3w.js} +1 -1
  38. package/dist/assets/{createReducer-Bq-nQJiK.js → createReducer-B_XmHROl.js} +1 -1
  39. package/dist/assets/{datasource-Do8f2hFf.js → datasource-DrODSIs0.js} +1 -1
  40. package/dist/assets/{dates-DK5JikJo.js → dates-B58T85Jq.js} +1 -1
  41. package/dist/assets/dependency-graph-panel-AY5BEVH2.js +5 -0
  42. package/dist/assets/{dist-7qdHLg-L.js → dist-B0RXffUm.js} +1 -1
  43. package/dist/assets/dist-B0qYy1Ye.js +1 -0
  44. package/dist/assets/{dist-B97dKQGP.js → dist-B8pdsUrS.js} +1 -1
  45. package/dist/assets/{dist-BcfF-rkl.js → dist-BF7Lrekv.js} +1 -1
  46. package/dist/assets/dist-BL8RjfvI.js +1 -0
  47. package/dist/assets/dist-BVZxge7j.js +1 -0
  48. package/dist/assets/{dist-fxnYzq-R.js → dist-BaRNSaca.js} +1 -1
  49. package/dist/assets/dist-Bk2yQtw4.js +1 -0
  50. package/dist/assets/dist-BsfiiKoy.js +1 -0
  51. package/dist/assets/dist-BskmVd3B.js +1 -0
  52. package/dist/assets/{dist-DL1Thauf.js → dist-Bsu3h9O7.js} +1 -1
  53. package/dist/assets/{dist-PIe1xcZo.js → dist-BtC9ym6k.js} +7 -7
  54. package/dist/assets/{dist-DeuWxYVk.js → dist-Bv0S1RlZ.js} +1 -1
  55. package/dist/assets/{dist-CidBzUqR.js → dist-C--ZICxQ.js} +1 -1
  56. package/dist/assets/dist-C-2fD3XY.js +1 -0
  57. package/dist/assets/{dist-DzAWvPVK.js → dist-CKUpxYGQ.js} +1 -1
  58. package/dist/assets/dist-CMFMfBm1.js +1 -0
  59. package/dist/assets/dist-CZNULd6Z.js +2 -0
  60. package/dist/assets/dist-CjFduPiC.js +1 -0
  61. package/dist/assets/{dist-DoAjHgMj.js → dist-CmImLjus.js} +1 -1
  62. package/dist/assets/{dist-BKx0MLbb.js → dist-Cq8NOszX.js} +1 -1
  63. package/dist/assets/{dist-DCc0VfiO.js → dist-Cx4BE7jC.js} +3 -3
  64. package/dist/assets/dist-D45ScydS.js +1 -0
  65. package/dist/assets/dist-D6dg87r7.js +1 -0
  66. package/dist/assets/dist-DEjfQLz0.js +1 -0
  67. package/dist/assets/{dist-BcYW-EJk.js → dist-DJOWOALB.js} +1 -1
  68. package/dist/assets/{dist-CxxZaSme.js → dist-DPoYlxSC.js} +1 -1
  69. package/dist/assets/dist-DR-Y-nl3.js +1 -0
  70. package/dist/assets/dist-Dahlp9JQ.js +1 -0
  71. package/dist/assets/{dist-DIkuvChK.js → dist-Db-HC93w.js} +1 -1
  72. package/dist/assets/{dist-DTM7BAUu.js → dist-DdOlZ6mj.js} +1 -1
  73. package/dist/assets/{dist-Dz_04liz.js → dist-DiYjQAhI.js} +1 -1
  74. package/dist/assets/{dist-BO5c5n_b.js → dist-DkPPv0FB.js} +1 -1
  75. package/dist/assets/{dist-DkCO0b-0.js → dist-DmGtPaVM.js} +1 -1
  76. package/dist/assets/{dist-CXmhxecl.js → dist-DnxYYyU7.js} +1 -1
  77. package/dist/assets/{dist-CO1Hfv5H.js → dist-Dw9YRsCU.js} +1 -1
  78. package/dist/assets/dist-Dwx3KdUP.js +1 -0
  79. package/dist/assets/{dist-BgL43w5r.js → dist-DzCQtGkt.js} +1 -1
  80. package/dist/assets/dist-JweaDt6P.js +1 -0
  81. package/dist/assets/{dist-Dbu5sPgR.js → dist-gELFCfHH.js} +1 -1
  82. package/dist/assets/dist-kGDJ0AWF.js +1 -0
  83. package/dist/assets/{dist-Cqm1W-Zm.js → dist-kaLvV_d4.js} +1 -1
  84. package/dist/assets/{dist-CEeRVpMD.js → dist-qvk3g06H.js} +1 -1
  85. package/dist/assets/dist-wCw2twuB.js +1 -0
  86. package/dist/assets/dist-wF42tUcr.js +1 -0
  87. package/dist/assets/dist-wY2yia-1.js +3 -0
  88. package/dist/assets/{documentation-panel-DvHjxlli.js → documentation-panel-CaJF7e-j.js} +1 -1
  89. package/dist/assets/{download-BP8MrprC.js → download-DMxOzI7b.js} +1 -1
  90. package/dist/assets/dropdown-menu-C40jM9o4.js +1 -0
  91. package/dist/assets/{edit-page-71cK-Nhe.js → edit-page-Vqd9NedM.js} +6 -6
  92. package/dist/assets/{error-banner-BYN8YUMe.js → error-banner-Dw5YY9wq.js} +1 -1
  93. package/dist/assets/{error-entries-DOZ1u97X.js → error-entries-Cv2m1yhc.js} +1 -1
  94. package/dist/assets/{error-panel-CINcf76Z.js → error-panel-Bhlvt6_H.js} +1 -1
  95. package/dist/assets/{es-BuMFDpR4.js → es-CrFhA96G.js} +1 -1
  96. package/dist/assets/{esm-Dw0vvSg2.js → esm-BHMOvJzM.js} +1 -1
  97. package/dist/assets/{esm-BGpfQNhD.js → esm-DmqQGMel.js} +1 -1
  98. package/dist/assets/file-explorer-panel-6HvNfCrJ.js +1 -0
  99. package/dist/assets/{file-icons-DgKj9mrV.js → file-icons-DdGYuADJ.js} +1 -1
  100. package/dist/assets/file-name-input-K4MKESI_.js +3 -0
  101. package/dist/assets/{floating-outline-CRXIsrPm.js → floating-outline-DaUHnl_e.js} +1 -1
  102. package/dist/assets/{formats-DSzGCKeO.js → formats-C6s96nu2.js} +1 -1
  103. package/dist/assets/{formatting-BJlT1s_3.js → formatting-BWZH2nBg.js} +1 -1
  104. package/dist/assets/{fullscreen-0EEPR7uc.js → fullscreen-BH2jz-F4.js} +5 -5
  105. package/dist/assets/{gallery-page-TqnFYECu.js → gallery-page-C3pBpHlY.js} +1 -1
  106. package/dist/assets/{glide-data-editor-DcY3nXI8.js → glide-data-editor-sICUMEhk.js} +1 -1
  107. package/dist/assets/{glide-utils-CR1sWLZU.js → glide-utils-UGsrRHYj.js} +1 -1
  108. package/dist/assets/home-page-BLd6rnbk.js +4 -0
  109. package/dist/assets/{html-to-image-DMn_7CCA.js → html-to-image-D6GUC5q6.js} +1 -1
  110. package/dist/assets/index-BGT7have.css +2 -0
  111. package/dist/assets/index-ipFYAeCF.js +29 -0
  112. package/dist/assets/{input-Daz6sk8l.js → input-DEbY281O.js} +2 -2
  113. package/dist/assets/{kiosk-mode-CzGt3mc2.js → kiosk-mode-mxgb5lWf.js} +1 -1
  114. package/dist/assets/{layout-KJMQADEj.js → layout-DWnrxIo2.js} +5 -5
  115. package/dist/assets/links-DLvOXyO_.js +1 -0
  116. package/dist/assets/{logs-panel-DFibPeCT.js → logs-panel-BGS2v3ba.js} +1 -1
  117. package/dist/assets/lua-1tNpM92n.js +1 -0
  118. package/dist/assets/{lua-CRIS5afH.js → lua-DXBxhmso.js} +1 -1
  119. package/dist/assets/{markdown-renderer-CdZX2MvM.js → markdown-renderer-BZKlbm2Q.js} +1 -1
  120. package/dist/assets/menu-items-BSRA2YI9.js +1 -0
  121. package/dist/assets/{mermaid-CeseGGnS.js → mermaid-l6lQkCib.js} +1 -1
  122. package/dist/assets/{name-cell-input-B-d5Jwh4.js → name-cell-input-BMCdK-el.js} +1 -1
  123. package/dist/assets/{numbers-DKurV_vq.js → numbers-Bc-r3jsC.js} +1 -1
  124. package/dist/assets/{outline-panel-D2Wk10hY.js → outline-panel-Dr5u73V6.js} +1 -1
  125. package/dist/assets/{packages-panel-CQsbxIlG.js → packages-panel-BdpRg9tX.js} +1 -1
  126. package/dist/assets/{pair-with-agent-modal-CNxmIMFM.js → pair-with-agent-modal-CbUFKJHP.js} +1 -1
  127. package/dist/assets/{panel-accordion-state-D6-FMXE2.js → panel-accordion-state-DjUTN7i5.js} +1 -1
  128. package/dist/assets/{panels-BgcK_4M7.js → panels-CWn0Yh1D.js} +1 -1
  129. package/dist/assets/process-output-Ck7-P_xp.js +1 -0
  130. package/dist/assets/{readonly-code-display-BvqLeUsJ.js → readonly-code-display--S2daDa6.js} +1 -1
  131. package/dist/assets/{readonly-python-code-GSN4D4sb.js → readonly-python-code-DpIyjHqY.js} +1 -1
  132. package/dist/assets/{renderShortcut-DK_OucLT.js → renderShortcut-X-CJUSM8.js} +1 -1
  133. package/dist/assets/{reveal-component-u9ED2diI.js → reveal-component-nZIAapoD.js} +1 -1
  134. package/dist/assets/{run-page-U5iuhyz8.js → run-page-9pl8qhQR.js} +1 -1
  135. package/dist/assets/{runs-BQTlv6Ja.js → runs-euHSzlw_.js} +1 -1
  136. package/dist/assets/{scratchpad-panel--YOR0kUB.js → scratchpad-panel-BleGt2KM.js} +1 -1
  137. package/dist/assets/{scroll-to-bottom-button-xua0e9rA.js → scroll-to-bottom-button-DufiYUD7.js} +1 -1
  138. package/dist/assets/{secrets-panel-CZPbFAZH.js → secrets-panel-D3BpDIgg.js} +1 -1
  139. package/dist/assets/select-nLt2y0Sw.js +1 -0
  140. package/dist/assets/{session-CEdta5_Q.js → session-Sz5nSFku.js} +1 -1
  141. package/dist/assets/session-panel-DRGPlL5J.js +1 -0
  142. package/dist/assets/{share-C2MPy_VX.js → share-CPtBiNgr.js} +1 -1
  143. package/dist/assets/{skeleton-CsceCAjw.js → skeleton-BKeh8hf3.js} +1 -1
  144. package/dist/assets/{snippets-panel-BxwHsPmW.js → snippets-panel-D5yy7kj0.js} +1 -1
  145. package/dist/assets/{spec-CUFquO6R.js → spec-DKsAmLTi.js} +1 -1
  146. package/dist/assets/{state-BqrF--O0.js → state-B6AklCzD.js} +1 -1
  147. package/dist/assets/{state-C2c3_VuY.js → state-B8XRqyFg.js} +1 -1
  148. package/dist/assets/{state-CHplO0zn.js → state-Di9TbuV9.js} +1 -1
  149. package/dist/assets/{strings-CpYReX2Q.js → strings-CReV1AYG.js} +1 -1
  150. package/dist/assets/{swiper-component-Bot7ehYH.js → swiper-component-5Kdi1-Gn.js} +1 -1
  151. package/dist/assets/{switch-CtH5Ct94.js → switch-DjXr9Y9D.js} +1 -1
  152. package/dist/assets/{tabs-c6mpccpH.js → tabs-Cy1qXLzH.js} +1 -1
  153. package/dist/assets/{terminal-C73iz1fU.js → terminal-CQAFGBfN.js} +1 -1
  154. package/dist/assets/{tooltip-Cwltdez8.js → tooltip-BG35uS_N.js} +1 -1
  155. package/dist/assets/{tooltip-D7dq6bnc.js → tooltip-BUtPzKB4.js} +1 -1
  156. package/dist/assets/{tracing-Dhq121dR.js → tracing-DNk3gmWv.js} +1 -1
  157. package/dist/assets/{tracing-panel-IKt1933_.js → tracing-panel-Yqazr6w_.js} +2 -2
  158. package/dist/assets/{tree-actions-DIfWonCA.js → tree-actions-CG5g7aUQ.js} +1 -1
  159. package/dist/assets/{useAddCell-BYrVaYfZ.js → useAddCell-DdB_uBif.js} +1 -1
  160. package/dist/assets/{useBoolean-ZU5StAbr.js → useBoolean-BXC4CItZ.js} +1 -1
  161. package/dist/assets/{useCellActionButton-DbXDs0MA.js → useCellActionButton-B31neI3m.js} +1 -1
  162. package/dist/assets/{useDebounce-Dulm61-t.js → useDebounce-DgpSQhrP.js} +1 -1
  163. package/dist/assets/{useDeleteCell-Wgcl3CRG.js → useDeleteCell-Bz49_cFj.js} +1 -1
  164. package/dist/assets/{useDependencyPanelTab-C5AU8GKL.js → useDependencyPanelTab-C_ps0r--.js} +1 -1
  165. package/dist/assets/{useEventListener-D27Dii_l.js → useEventListener-92uWqz-P.js} +1 -1
  166. package/dist/assets/{useIframeCapabilities-DEpTCh1Q.js → useIframeCapabilities-C7Di8f2y.js} +1 -1
  167. package/dist/assets/{useInstallPackage-u7d0wo79.js → useInstallPackage-DsD08SLK.js} +1 -1
  168. package/dist/assets/{useInterval-B1_J4wdT.js → useInterval-B4v7_zd0.js} +1 -1
  169. package/dist/assets/{useNotebookActions-FuMERtHq.js → useNotebookActions-CQtbk9CB.js} +2 -2
  170. package/dist/assets/{useRunCells-DTdMRbkE.js → useRunCells-CX7oW5zy.js} +1 -1
  171. package/dist/assets/{useSplitCell-2z0zEB8X.js → useSplitCell-Dy2_DmJR.js} +1 -1
  172. package/dist/assets/{useTheme-CF-zG9YH.js → useTheme-C-ri8BtY.js} +1 -1
  173. package/dist/assets/utils-D5Ywm7-q.js +1 -0
  174. package/dist/assets/{vega-component-BSXJcTJb.js → vega-component-C7TCzyaU.js} +1 -1
  175. package/dist/assets/write-secret-modal-D6zoZgLY.js +2 -0
  176. package/dist/index.html +73 -73
  177. package/package.json +26 -26
  178. package/src/components/ai/ai-provider-icon.tsx +2 -2
  179. package/src/components/ai/ai-utils.ts +1 -1
  180. package/src/components/app-config/ai-config.tsx +33 -37
  181. package/src/components/app-config/user-config-form.tsx +1 -1
  182. package/src/components/data-table/__tests__/column-explorer.test.tsx +64 -4
  183. package/src/components/data-table/__tests__/column-visibility-dropdown.test.tsx +137 -4
  184. package/src/components/data-table/__tests__/url-detector.test.tsx +1 -1
  185. package/src/components/data-table/__tests__/useColumnVisibility.test.ts +153 -1
  186. package/src/components/data-table/column-explorer-panel/column-explorer.tsx +54 -25
  187. package/src/components/data-table/column-visibility-dropdown.tsx +62 -14
  188. package/src/components/data-table/date-popover.tsx +2 -1
  189. package/src/components/data-table/hooks/use-column-visibility.ts +76 -0
  190. package/src/components/data-table/renderers.tsx +4 -8
  191. package/src/components/data-table/show-only-column-button.tsx +58 -0
  192. package/src/components/dependency-graph/__tests__/elements.test.ts +151 -0
  193. package/src/components/dependency-graph/custom-edge.tsx +68 -0
  194. package/src/components/dependency-graph/custom-node.tsx +54 -26
  195. package/src/components/dependency-graph/dependency-graph-tree.tsx +110 -21
  196. package/src/components/dependency-graph/elements.ts +225 -31
  197. package/src/components/dependency-graph/panels.tsx +23 -2
  198. package/src/components/dependency-graph/utils/__tests__/layout.test.ts +78 -0
  199. package/src/components/dependency-graph/utils/layout.ts +108 -18
  200. package/src/components/editor/Output.tsx +40 -0
  201. package/src/components/editor/__tests__/Output.test.tsx +85 -2
  202. package/src/components/editor/connections/secret-combobox.tsx +3 -1
  203. package/src/components/editor/file-tree/__tests__/file-explorer-navigation.test.tsx +832 -0
  204. package/src/components/editor/file-tree/__tests__/file-viewer.test.tsx +27 -0
  205. package/src/components/editor/file-tree/__tests__/requesting-tree.test.ts +130 -0
  206. package/src/components/editor/file-tree/file-browser-row.tsx +82 -0
  207. package/src/components/editor/file-tree/file-explorer.tsx +377 -159
  208. package/src/components/editor/file-tree/file-operations.tsx +1 -1
  209. package/src/components/editor/file-tree/file-search-results.tsx +142 -0
  210. package/src/components/editor/file-tree/file-tree-row.tsx +40 -0
  211. package/src/components/editor/file-tree/file-viewer.tsx +2 -1
  212. package/src/components/editor/file-tree/renderers.tsx +4 -2
  213. package/src/components/editor/file-tree/requesting-tree.tsx +162 -32
  214. package/src/components/editor/file-tree/use-file-search.ts +142 -0
  215. package/src/components/editor/file-tree/use-hover-expand.ts +34 -0
  216. package/src/components/editor/navigation/__tests__/navigation.test.ts +30 -0
  217. package/src/components/editor/navigation/navigation.ts +20 -0
  218. package/src/components/forms/form.tsx +6 -4
  219. package/src/components/home/components.tsx +1 -0
  220. package/src/components/static-html/share-modal.tsx +3 -2
  221. package/src/components/ui/__tests__/badge.test.ts +63 -0
  222. package/src/components/ui/__tests__/button.test.ts +19 -0
  223. package/src/components/ui/__tests__/fullscreen.test.tsx +88 -0
  224. package/src/components/ui/__tests__/menu-items.test.ts +31 -0
  225. package/src/components/ui/badge.tsx +9 -7
  226. package/src/components/ui/button.tsx +1 -0
  227. package/src/components/ui/combobox.tsx +1 -1
  228. package/src/components/ui/date-input.tsx +1 -1
  229. package/src/components/ui/dialog.tsx +3 -1
  230. package/src/components/ui/fullscreen.tsx +43 -1
  231. package/src/components/ui/input.tsx +5 -1
  232. package/src/components/ui/links.tsx +2 -1
  233. package/src/components/ui/menu-items.tsx +9 -9
  234. package/src/components/ui/native-select.tsx +2 -2
  235. package/src/components/ui/navigation.tsx +1 -1
  236. package/src/components/ui/range-slider.tsx +5 -3
  237. package/src/components/ui/select.tsx +1 -1
  238. package/src/components/ui/sheet.tsx +1 -1
  239. package/src/components/ui/toast.tsx +8 -5
  240. package/src/components/ui/toaster.tsx +2 -2
  241. package/src/core/ai/ids/__tests__/ids.test.ts +0 -8
  242. package/src/core/ai/ids/ids.ts +1 -1
  243. package/src/core/cells/__tests__/add-missing-import.test.ts +20 -0
  244. package/src/core/codemirror/language/__tests__/utils.test.ts +1 -1
  245. package/src/core/config/config-schema.ts +1 -0
  246. package/src/core/hotkeys/hotkeys.ts +5 -0
  247. package/src/core/network/__tests__/requests-toasting.test.tsx +14 -0
  248. package/src/core/network/requests-toasting.tsx +1 -1
  249. package/src/css/globals.css +28 -0
  250. package/src/hooks/useEventListener.ts +1 -1
  251. package/src/plugins/core/RenderHTML.tsx +6 -4
  252. package/src/plugins/impl/MatrixPlugin.tsx +1 -1
  253. package/src/plugins/impl/code/any-language-editor.tsx +7 -4
  254. package/src/plugins/impl/data-frames/__tests__/schema.test.ts +34 -1
  255. package/src/plugins/impl/data-frames/forms/__tests__/__snapshots__/form.test.tsx.snap +25 -25
  256. package/src/plugins/impl/data-frames/schema.ts +5 -11
  257. package/src/plugins/impl/panel/__tests__/utils.test.ts +4 -4
  258. package/src/plugins/impl/vega/__tests__/vega.test.ts +1 -1
  259. package/src/stories/cell.stories.tsx +15 -11
  260. package/src/utils/__tests__/events.test.ts +1 -1
  261. package/dist/assets/JsonOutput-CRLQQVKf.js +0 -50
  262. package/dist/assets/RenderHTML-YkwVPe9V.js +0 -1
  263. package/dist/assets/ai-model-dropdown-CrSrb8Zz.js +0 -3
  264. package/dist/assets/alert-dialog-BVi_GyOB.js +0 -11
  265. package/dist/assets/any-language-editor-BL8H-DwB.js +0 -1
  266. package/dist/assets/badge-D_-uqy2N.js +0 -1
  267. package/dist/assets/button-Btj6GXNy.js +0 -1
  268. package/dist/assets/config-ycxJQP2b.js +0 -1
  269. package/dist/assets/dependency-graph-panel-Dg-tUB9T.js +0 -5
  270. package/dist/assets/dist-2oRmY-BC.js +0 -1
  271. package/dist/assets/dist-8lhGoJ54.js +0 -3
  272. package/dist/assets/dist-B4QaGCvF.js +0 -1
  273. package/dist/assets/dist-B_ABJVTW.js +0 -1
  274. package/dist/assets/dist-Bke0Ybxy.js +0 -1
  275. package/dist/assets/dist-Bw8CAY5i.js +0 -1
  276. package/dist/assets/dist-BwmO3SSk.js +0 -1
  277. package/dist/assets/dist-C3KxgR0m.js +0 -1
  278. package/dist/assets/dist-CLE562nZ.js +0 -1
  279. package/dist/assets/dist-CLi0j3bw.js +0 -1
  280. package/dist/assets/dist-CMIyXejt.js +0 -1
  281. package/dist/assets/dist-CbQrIaPV.js +0 -2
  282. package/dist/assets/dist-CpgfIIzU.js +0 -1
  283. package/dist/assets/dist-D7aYPqs9.js +0 -1
  284. package/dist/assets/dist-DKCF5R0T.js +0 -1
  285. package/dist/assets/dist-DbFa9cik.js +0 -1
  286. package/dist/assets/dist-DdPuhs_-.js +0 -1
  287. package/dist/assets/dist-DkfdZExz.js +0 -1
  288. package/dist/assets/dist-hOh6JBwi.js +0 -1
  289. package/dist/assets/dist-mY1j_5eA.js +0 -1
  290. package/dist/assets/dist-tg3aVnpD.js +0 -1
  291. package/dist/assets/dropdown-menu-CqNabM1f.js +0 -1
  292. package/dist/assets/file-explorer-panel-cflxW8a7.js +0 -1
  293. package/dist/assets/file-name-input-CQF4Ovwx.js +0 -3
  294. package/dist/assets/home-page-DPRHPAQM.js +0 -4
  295. package/dist/assets/index-BkIXRAbi.css +0 -2
  296. package/dist/assets/index-p49fbIlR.js +0 -29
  297. package/dist/assets/links-DPh7yoP-.js +0 -1
  298. package/dist/assets/lua-_4wuYo1j.js +0 -1
  299. package/dist/assets/menu-items-BSVrMleU.js +0 -1
  300. package/dist/assets/process-output-DvTtgoMs.js +0 -1
  301. package/dist/assets/select-czJLm6jf.js +0 -1
  302. package/dist/assets/session-panel-C_N3WGWQ.js +0 -1
  303. package/dist/assets/utils-CWSZQEjr.js +0 -1
  304. package/dist/assets/write-secret-modal-lNU_7c2x.js +0 -2
@@ -71,11 +71,7 @@ export function renderTableHeader<TData>(
71
71
  {renderHeaderGroup(table.getCenterHeaderGroups())}
72
72
  {renderHeaderGroup(table.getRightHeaderGroups())}
73
73
  {table.getAllColumns().length <= AUTO_WIDTH_MAX_COLUMNS && (
74
- <th
75
- className="w-full border-0"
76
- aria-hidden="true"
77
- role="presentation"
78
- />
74
+ <th className="w-full border-0" aria-hidden="true" />
79
75
  )}
80
76
  </TableRow>
81
77
  </TableHeader>
@@ -217,7 +213,7 @@ export const DataTableBody = <TData,>({
217
213
  {renderCells(row.getCenterVisibleCells())}
218
214
  {renderCells(row.getRightVisibleCells())}
219
215
  {columns.length <= AUTO_WIDTH_MAX_COLUMNS && (
220
- <td className="border-0" aria-hidden="true" role="presentation" />
216
+ <td className="border-0" aria-hidden="true" />
221
217
  )}
222
218
  </TableRow>
223
219
  );
@@ -247,7 +243,7 @@ export const DataTableBody = <TData,>({
247
243
  data-virtual-spacer=""
248
244
  style={{ height: virtualItems[0].start }}
249
245
  >
250
- <td colSpan={totalColSpan} />
246
+ <td aria-hidden="true" colSpan={totalColSpan} />
251
247
  </tr>
252
248
  )}
253
249
  {virtualItems.map((vItem) => renderRow(rows[vItem.index]))}
@@ -258,7 +254,7 @@ export const DataTableBody = <TData,>({
258
254
  height: totalSize - (virtualItems.at(-1)?.end ?? totalSize),
259
255
  }}
260
256
  >
261
- <td colSpan={totalColSpan} />
257
+ <td aria-hidden="true" colSpan={totalColSpan} />
262
258
  </tr>
263
259
  )}
264
260
  </>
@@ -0,0 +1,58 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+ "use no memo";
3
+
4
+ import type { Table } from "@tanstack/react-table";
5
+ import { ScanEyeIcon } from "lucide-react";
6
+ import { Button } from "@/components/ui/button";
7
+ import { Tooltip } from "@/components/ui/tooltip";
8
+ import { cn } from "@/utils/cn";
9
+ import { applyShowOnlyColumns } from "./hooks/use-column-visibility";
10
+
11
+ export const SHOW_ONLY_COLUMN_LABEL = "Show only this column";
12
+
13
+ interface ShowOnlyColumnButtonProps<TData> {
14
+ table: Table<TData>;
15
+ columnIds: string[];
16
+ disabled: boolean;
17
+ className?: string;
18
+ iconClassName?: string;
19
+ }
20
+
21
+ export function ShowOnlyColumnButton<TData>({
22
+ table,
23
+ columnIds,
24
+ disabled,
25
+ className,
26
+ iconClassName = "h-3 w-3",
27
+ }: ShowOnlyColumnButtonProps<TData>) {
28
+ return (
29
+ <Tooltip content={SHOW_ONLY_COLUMN_LABEL} delayDuration={400}>
30
+ <Button
31
+ type="button"
32
+ variant="text"
33
+ size="icon"
34
+ aria-label={SHOW_ONLY_COLUMN_LABEL}
35
+ aria-disabled={disabled}
36
+ tabIndex={disabled ? -1 : 0}
37
+ className={cn(
38
+ "h-6 w-6 hover:bg-muted text-muted-foreground hover:text-primary",
39
+ disabled && "opacity-50",
40
+ className,
41
+ )}
42
+ onPointerDown={(event) => {
43
+ event.stopPropagation();
44
+ }}
45
+ onClick={(event) => {
46
+ event.preventDefault();
47
+ event.stopPropagation();
48
+ if (disabled) {
49
+ return;
50
+ }
51
+ applyShowOnlyColumns(table, columnIds);
52
+ }}
53
+ >
54
+ <ScanEyeIcon className={iconClassName} strokeWidth={2.5} />
55
+ </Button>
56
+ </Tooltip>
57
+ );
58
+ }
@@ -0,0 +1,151 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { describe, expect, it } from "vitest";
4
+ import type { CellId } from "@/core/cells/ids";
5
+ import type { Variable, VariableName, Variables } from "@/core/variables/types";
6
+ import { createCellRuntimeState } from "@/core/cells/types";
7
+ import {
8
+ collapsedNodeWidth,
9
+ computeDefsByCell,
10
+ isStateFlowVariable,
11
+ reusableCellIds,
12
+ wrapDefs,
13
+ } from "../elements";
14
+
15
+ const names = (list: string[]) => list as VariableName[];
16
+
17
+ function variable(
18
+ partial: Omit<Partial<Variable>, "name"> & { name: string },
19
+ ): Variable {
20
+ return {
21
+ declaredBy: [],
22
+ usedBy: [],
23
+ ...partial,
24
+ name: partial.name as VariableName,
25
+ };
26
+ }
27
+
28
+ describe("collapsedNodeWidth", () => {
29
+ it("clamps short labels to the minimum width", () => {
30
+ expect(collapsedNodeWidth("")).toBe(90);
31
+ expect(collapsedNodeWidth("x")).toBe(90);
32
+ });
33
+
34
+ it("clamps long labels to the maximum width", () => {
35
+ expect(collapsedNodeWidth("a".repeat(100))).toBe(240);
36
+ });
37
+
38
+ it("scales with label length between the bounds", () => {
39
+ const width = collapsedNodeWidth("df, summary");
40
+ expect(width).toBeGreaterThan(90);
41
+ expect(width).toBeLessThan(240);
42
+ });
43
+ });
44
+
45
+ describe("isStateFlowVariable", () => {
46
+ it("flags State and SetFunctor (from mo.state)", () => {
47
+ expect(
48
+ isStateFlowVariable(variable({ name: "g", dataType: "State" })),
49
+ ).toBe(true);
50
+ expect(
51
+ isStateFlowVariable(variable({ name: "s", dataType: "SetFunctor" })),
52
+ ).toBe(true);
53
+ });
54
+
55
+ it("does not flag UI elements or plain values", () => {
56
+ // UIElement is frontend-driven / immutable during a run — not a state flow.
57
+ expect(
58
+ isStateFlowVariable(variable({ name: "sl", dataType: "slider" })),
59
+ ).toBe(false);
60
+ expect(isStateFlowVariable(variable({ name: "n", dataType: "int" }))).toBe(
61
+ false,
62
+ );
63
+ expect(isStateFlowVariable(variable({ name: "u", dataType: null }))).toBe(
64
+ false,
65
+ );
66
+ expect(isStateFlowVariable(variable({ name: "u2" }))).toBe(false);
67
+ });
68
+ });
69
+
70
+ describe("wrapDefs", () => {
71
+ it("packs short names to the 12-char minimum budget", () => {
72
+ expect(
73
+ wrapDefs(names(["np", "math", "time", "io", "numpy", "pandas"])),
74
+ ).toEqual(["np, math", "time, io", "numpy", "pandas"]);
75
+ });
76
+
77
+ it("gives a long name its own line, then packs the rest to its width", () => {
78
+ expect(
79
+ wrapDefs(
80
+ names([
81
+ "very_very_very_long_variable_name",
82
+ "short_var",
83
+ "shr_v",
84
+ "io",
85
+ "numpy",
86
+ "wev",
87
+ ]),
88
+ ),
89
+ ).toEqual([
90
+ "very_very_very_long_variable_name",
91
+ "short_var, shr_v, io, numpy, wev",
92
+ ]);
93
+ });
94
+
95
+ it("returns an empty list for no defs", () => {
96
+ expect(wrapDefs(names([]))).toEqual([]);
97
+ });
98
+
99
+ it("caps the budget so packed lines fit the max node width", () => {
100
+ // One name far beyond the width cap must not widen every other line.
101
+ const huge = "x".repeat(60);
102
+ const lines = wrapDefs(
103
+ names([huge, "aaaaaaaaaa", "bbbbbbbbbb", "cccccccccc"]),
104
+ );
105
+ expect(lines[0]).toBe(huge);
106
+ for (const line of lines.slice(1)) {
107
+ expect(line.length).toBeLessThanOrEqual(33);
108
+ }
109
+ });
110
+ });
111
+
112
+ describe("computeDefsByCell", () => {
113
+ it("maps each cell to the variables it declares", () => {
114
+ const c1 = "c1" as CellId;
115
+ const c2 = "c2" as CellId;
116
+ const a = "a" as VariableName;
117
+ const b = "b" as VariableName;
118
+ const c = "c" as VariableName;
119
+ const variables: Variables = {
120
+ [a]: variable({ name: a, declaredBy: [c1], usedBy: [c2] }),
121
+ [b]: variable({ name: b, declaredBy: [c1] }),
122
+ [c]: variable({ name: c, declaredBy: [c2] }),
123
+ };
124
+ const defs = computeDefsByCell(variables);
125
+ expect(defs.get(c1)).toEqual([a, b]);
126
+ expect(defs.get(c2)).toEqual([c]);
127
+ });
128
+
129
+ it("returns an empty map when there are no variables", () => {
130
+ expect(computeDefsByCell({}).size).toBe(0);
131
+ });
132
+ });
133
+
134
+ describe("reusableCellIds", () => {
135
+ const c1 = "c1" as CellId;
136
+ const c2 = "c2" as CellId;
137
+ const c3 = "c3" as CellId;
138
+
139
+ it("collects cells whose serialization hint is valid, case-insensitively", () => {
140
+ const runtime = {
141
+ [c1]: createCellRuntimeState({ serialization: "Valid" }),
142
+ [c2]: createCellRuntimeState({ serialization: "Unparsable" }),
143
+ [c3]: createCellRuntimeState(),
144
+ };
145
+ expect(reusableCellIds([c1, c2, c3], runtime)).toEqual(new Set([c1]));
146
+ });
147
+
148
+ it("ignores cells with no runtime entry", () => {
149
+ expect(reusableCellIds([c1], {})).toEqual(new Set());
150
+ });
151
+ });
@@ -0,0 +1,68 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { memo } from "react";
4
+ import { BaseEdge, type EdgeProps, getBezierPath } from "reactflow";
5
+ import {
6
+ DEFAULT_EDGE_COLOR,
7
+ type DependencyEdgeData,
8
+ STATE_FLOW_EDGE_COLOR,
9
+ } from "./elements";
10
+
11
+ // Gentle curvature keeps edges reading as clean directional splines
12
+ // (TensorBoard-style) rather than hard right-angle elbows.
13
+ const EDGE_CURVATURE = 0.35;
14
+
15
+ /**
16
+ * A dependency edge, drawn as a smooth curved spline that flows out of the
17
+ * source handle and into the target — the TensorBoard graph aesthetic. Mutable
18
+ * `State`/setter flows get a distinct dashed accent style.
19
+ */
20
+ export const DependencyEdge = memo((props: EdgeProps<DependencyEdgeData>) => {
21
+ const {
22
+ sourceX,
23
+ sourceY,
24
+ targetX,
25
+ targetY,
26
+ sourcePosition,
27
+ targetPosition,
28
+ markerEnd,
29
+ data,
30
+ selected,
31
+ } = props;
32
+
33
+ const [path] = getBezierPath({
34
+ sourceX,
35
+ sourceY,
36
+ targetX,
37
+ targetY,
38
+ sourcePosition,
39
+ targetPosition,
40
+ curvature: EDGE_CURVATURE,
41
+ });
42
+
43
+ const isStateFlow = data?.isStateFlow ?? false;
44
+ // The inline stroke replaces React Flow's stylesheet-driven edge colors, so
45
+ // the selected state must be restyled here too or it becomes invisible.
46
+ const stroke = selected
47
+ ? "var(--primary)"
48
+ : isStateFlow
49
+ ? STATE_FLOW_EDGE_COLOR
50
+ : DEFAULT_EDGE_COLOR;
51
+
52
+ return (
53
+ <BaseEdge
54
+ path={path}
55
+ markerEnd={markerEnd}
56
+ style={{
57
+ strokeWidth: selected ? 2.5 : isStateFlow ? 2 : 1.5,
58
+ stroke,
59
+ strokeDasharray: isStateFlow ? "5 3" : undefined,
60
+ }}
61
+ />
62
+ );
63
+ });
64
+ DependencyEdge.displayName = "DependencyEdge";
65
+
66
+ export const edgeTypes = {
67
+ dependency: DependencyEdge,
68
+ };
@@ -2,25 +2,20 @@
2
2
 
3
3
  import { useAtomValue } from "jotai";
4
4
  import React, { memo, use } from "react";
5
- import { Handle, Position, useStore } from "reactflow";
5
+ import { Handle, Position } from "reactflow";
6
6
  import { TinyCode } from "@/components/editor/cell/TinyCode";
7
7
  import { useCellIds } from "@/core/cells/cells";
8
- import { displayCellName } from "@/core/cells/names";
8
+ import { displayCellName, isInternalCellName } from "@/core/cells/names";
9
9
  import { cn } from "@/utils/cn";
10
10
  import {
11
11
  type CustomNodeProps,
12
- getNodeHeight,
13
12
  INPUTS_HANDLE_ID,
13
+ nodeBodyLines,
14
+ nodeDimensions,
14
15
  OUTPUTS_HANDLE_ID,
15
16
  } from "./elements";
16
17
  import type { LayoutDirection } from "./types";
17
-
18
- function getWidth(canvasWidth: number) {
19
- const minWidth = 100;
20
- const maxWidth = 400;
21
- const padding = 50;
22
- return Math.min(Math.max(canvasWidth - padding * 2, minWidth), maxWidth);
23
- }
18
+ import { extractCellPreview } from "./utils/cell-preview";
24
19
 
25
20
  export const EdgeMarkerContext = React.createContext<LayoutDirection>("LR");
26
21
 
@@ -36,13 +31,19 @@ export const CustomNode = memo((props: CustomNodeProps) => {
36
31
  const { data, selected } = props; // must match the equality check
37
32
  const cell = useAtomValue(data.atom);
38
33
  const cellIndex = useCellIds().inOrderIds.indexOf(cell.id);
39
- const nonSelectedColor = "var(--gray-3)";
40
- const selectedColor = "var(--gray-9)";
41
- const color = selected ? selectedColor : nonSelectedColor;
42
- const reactFlowWidth = useStore(({ width }) => width);
34
+ const handleColor = selected ? "var(--gray-9)" : "var(--gray-3)";
43
35
  const edgeMarkers = use(EdgeMarkerContext);
44
36
 
45
- const linesOfCode = cell.code.split("\n").length;
37
+ const { width, height } = nodeDimensions(data);
38
+ const isNamed = !isInternalCellName(cell.name);
39
+ const name = displayCellName(cell.name, cellIndex);
40
+ // Only collapsed nodes without defs render the preview; skip the parse
41
+ // otherwise, since it scales with notebook size.
42
+ const preview =
43
+ !data.expanded && data.defs.length === 0
44
+ ? extractCellPreview(cell.code).text
45
+ : undefined;
46
+
46
47
  return (
47
48
  <div>
48
49
  <Handle
@@ -50,43 +51,70 @@ export const CustomNode = memo((props: CustomNodeProps) => {
50
51
  id={INPUTS_HANDLE_ID}
51
52
  data-testid="input-one"
52
53
  position={edgeMarkers === "LR" ? Position.Left : Position.Top}
53
- style={{ background: color }}
54
+ style={{ background: handleColor }}
54
55
  />
55
56
  <Handle
56
57
  type="source"
57
58
  id={INPUTS_HANDLE_ID}
58
59
  data-testid="input-two"
59
60
  position={edgeMarkers === "LR" ? Position.Left : Position.Top}
60
- style={{ background: color }}
61
+ style={{ background: handleColor }}
61
62
  />
62
63
  <div
63
64
  className={cn(
64
65
  "flex flex-col bg-card border border-input/50 rounded-md mx-[2px] overflow-hidden",
65
66
  selected && "border-primary",
66
67
  )}
67
- style={{
68
- height: getNodeHeight(linesOfCode),
69
- width: data.forceWidth || getWidth(reactFlowWidth),
70
- }}
68
+ style={{ height, width }}
71
69
  >
72
- <div className="text-muted-foreground font-semibold text-xs py-1 px-2 bg-muted border-b">
73
- {displayCellName(cell.name, cellIndex)}
70
+ <div className="text-xs py-0.5 px-2 bg-muted border-b flex items-center justify-between gap-2 shrink-0">
71
+ <span
72
+ className={cn(
73
+ "truncate",
74
+ isNamed
75
+ ? "font-semibold text-foreground"
76
+ : "text-muted-foreground/70",
77
+ )}
78
+ >
79
+ {name}
80
+ </span>
74
81
  </div>
75
- <TinyCode code={cell.code} />
82
+ {data.expanded ? (
83
+ <TinyCode code={cell.code} />
84
+ ) : (
85
+ <div className="flex-1 min-h-0 flex flex-col justify-center px-2 py-1 font-mono text-xs leading-4">
86
+ {data.defs.length > 0 ? (
87
+ // Defs wrapped into comma-separated lines — taller, not wider.
88
+ nodeBodyLines(data).map((line) => (
89
+ <span
90
+ key={line}
91
+ className="text-foreground truncate"
92
+ title={line}
93
+ >
94
+ {line}
95
+ </span>
96
+ ))
97
+ ) : (
98
+ <span className="truncate italic text-muted-foreground/60">
99
+ {preview ?? "empty"}
100
+ </span>
101
+ )}
102
+ </div>
103
+ )}
76
104
  </div>
77
105
  <Handle
78
106
  type="source"
79
107
  id={OUTPUTS_HANDLE_ID}
80
108
  data-testid="output-one"
81
109
  position={edgeMarkers === "LR" ? Position.Right : Position.Bottom}
82
- style={{ background: color }}
110
+ style={{ background: handleColor }}
83
111
  />
84
112
  <Handle
85
113
  type="target"
86
114
  id={OUTPUTS_HANDLE_ID}
87
115
  data-testid="output-two"
88
116
  position={edgeMarkers === "LR" ? Position.Right : Position.Bottom}
89
- style={{ background: color }}
117
+ style={{ background: handleColor }}
90
118
  />
91
119
  </div>
92
120
  );
@@ -1,8 +1,13 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
- import type { Atom } from "jotai";
3
+ import { type Atom, atom, useAtomValue } from "jotai";
4
4
  import { MapPinIcon } from "lucide-react";
5
- import React, { type PropsWithChildren, useEffect, useState } from "react";
5
+ import React, {
6
+ type PropsWithChildren,
7
+ useEffect,
8
+ useMemo,
9
+ useState,
10
+ } from "react";
6
11
  import useEvent from "react-use-event-hook";
7
12
  import ReactFlow, {
8
13
  Background,
@@ -15,6 +20,7 @@ import ReactFlow, {
15
20
  useNodesState,
16
21
  useReactFlow,
17
22
  } from "reactflow";
23
+ import { edgeTypes } from "@/components/dependency-graph/custom-edge";
18
24
  import {
19
25
  EdgeMarkerContext,
20
26
  nodeTypes,
@@ -25,11 +31,16 @@ import type { CellData } from "@/core/cells/types";
25
31
  import { store } from "@/core/state/jotai";
26
32
  import type { Variables } from "@/core/variables/types";
27
33
  import { Events } from "@/utils/events";
28
- import { scrollAndHighlightCell } from "../editor/links/cell-link";
29
34
  import { Tooltip } from "../ui/tooltip";
30
- import { type NodeData, TreeElementsBuilder } from "./elements";
35
+ import {
36
+ computeDefsByCell,
37
+ type NodeData,
38
+ nodeDimensions,
39
+ TreeElementsBuilder,
40
+ } from "./elements";
31
41
  import { GraphSelectionPanel } from "./panels";
32
42
  import type { GraphSelection, GraphSettings, LayoutDirection } from "./types";
43
+ import { extractCellPreview } from "./utils/cell-preview";
33
44
  import { layoutElements } from "./utils/layout";
34
45
  import { useFitToViewOnDimensionChange } from "./utils/useFitToViewOnDimensionChange";
35
46
 
@@ -43,6 +54,24 @@ interface Props {
43
54
 
44
55
  const elementsBuilder = new TreeElementsBuilder();
45
56
 
57
+ /**
58
+ * Apply the current expand/collapse selection to freshly-built nodes, resizing
59
+ * each so the layout engine and the DOM agree on node dimensions.
60
+ */
61
+ function withExpansion(
62
+ nodes: Node<NodeData>[],
63
+ expandedIds: Set<CellId>,
64
+ ): Node<NodeData>[] {
65
+ return nodes.map((node) => {
66
+ const data: NodeData = {
67
+ ...node.data,
68
+ expanded: expandedIds.has(node.id as CellId),
69
+ };
70
+ const { width, height } = nodeDimensions(data);
71
+ return { ...node, data, width, height };
72
+ });
73
+ }
74
+
46
75
  export const DependencyGraphTree: React.FC<PropsWithChildren<Props>> = ({
47
76
  cellIds,
48
77
  variables,
@@ -51,22 +80,23 @@ export const DependencyGraphTree: React.FC<PropsWithChildren<Props>> = ({
51
80
  layoutDirection,
52
81
  settings,
53
82
  }) => {
83
+ // Cells whose node is expanded to show its full code (toggled by double-click).
84
+ const [expandedIds, setExpandedIds] = useState<Set<CellId>>(() => new Set());
85
+
54
86
  // oxlint-disable-next-line react/hook-use-state
55
87
  const [initial] = useState(() => {
56
- let elements = elementsBuilder.createElements(
88
+ const elements = elementsBuilder.createElements(
57
89
  cellIds,
58
90
  cellAtoms,
59
91
  variables,
60
92
  settings.hidePureMarkdown,
61
93
  settings.hideReusableFunctions,
62
94
  );
63
- elements = layoutElements({
64
- nodes: elements.nodes,
95
+ return layoutElements({
96
+ nodes: withExpansion(elements.nodes, expandedIds),
65
97
  edges: elements.edges,
66
98
  direction: layoutDirection,
67
99
  });
68
-
69
- return elements;
70
100
  // Only run once
71
101
  });
72
102
 
@@ -82,22 +112,68 @@ export const DependencyGraphTree: React.FC<PropsWithChildren<Props>> = ({
82
112
  edges: elements.edges,
83
113
  direction: layoutDirection,
84
114
  });
85
- setNodes(result.nodes);
86
- setEdges(result.edges);
115
+ // Rebuilt elements are fresh objects; carry selection over so a
116
+ // re-layout (e.g. toggling expansion) doesn't silently clear it while
117
+ // the selection panel still targets the element.
118
+ setNodes((prev) => {
119
+ const selected = new Set(
120
+ prev.filter((node) => node.selected).map((node) => node.id),
121
+ );
122
+ return result.nodes.map((node) =>
123
+ selected.has(node.id) ? { ...node, selected: true } : node,
124
+ );
125
+ });
126
+ setEdges((prev) => {
127
+ const selected = new Set(
128
+ prev.filter((edge) => edge.selected).map((edge) => edge.id),
129
+ );
130
+ return result.edges.map((edge) =>
131
+ selected.has(edge.id) ? { ...edge, selected: true } : edge,
132
+ );
133
+ });
87
134
  },
88
135
  );
89
136
 
90
- // If the cellIds change, update the nodes.
137
+ // Node sizes depend on live cell code: the line count when expanded, or the
138
+ // preview when a cell defines nothing. The cell atoms keep their identity
139
+ // across edits, so the rebuild effect below can't see code changes through
140
+ // its deps — subscribe to the size-relevant inputs explicitly.
141
+ const defsByCell = useMemo(() => computeDefsByCell(variables), [variables]);
142
+ const sizeSignature = useAtomValue(
143
+ useMemo(
144
+ () =>
145
+ atom((get) =>
146
+ cellIds
147
+ .map((cellId, index) => {
148
+ const code = get(cellAtoms[index]).code;
149
+ if (expandedIds.has(cellId)) {
150
+ return `e${code.trim().split("\n").length}`;
151
+ }
152
+ if ((defsByCell.get(cellId) ?? []).length > 0) {
153
+ // Collapsed nodes with defs size off the defs, not the code.
154
+ return "d";
155
+ }
156
+ return `p${extractCellPreview(code).text?.length ?? 0}`;
157
+ })
158
+ .join(),
159
+ ),
160
+ [cellIds, cellAtoms, expandedIds, defsByCell],
161
+ ),
162
+ );
163
+
164
+ // Rebuild + re-layout when the graph inputs or the expand/collapse set change.
91
165
  useEffect(() => {
92
- syncChanges(
93
- elementsBuilder.createElements(
94
- cellIds,
95
- cellAtoms,
96
- variables,
97
- settings.hidePureMarkdown,
98
- settings.hideReusableFunctions,
99
- ),
166
+ const elements = elementsBuilder.createElements(
167
+ cellIds,
168
+ cellAtoms,
169
+ variables,
170
+ settings.hidePureMarkdown,
171
+ settings.hideReusableFunctions,
100
172
  );
173
+ syncChanges({
174
+ nodes: withExpansion(elements.nodes, expandedIds),
175
+ edges: elements.edges,
176
+ });
101
177
  }, [
102
178
  cellIds,
103
179
  variables,
@@ -105,6 +181,8 @@ export const DependencyGraphTree: React.FC<PropsWithChildren<Props>> = ({
105
181
  syncChanges,
106
182
  settings.hidePureMarkdown,
107
183
  settings.hideReusableFunctions,
184
+ expandedIds,
185
+ sizeSignature,
108
186
  ]);
109
187
 
110
188
  const [selection, setSelection] = useState<GraphSelection>();
@@ -120,6 +198,7 @@ export const DependencyGraphTree: React.FC<PropsWithChildren<Props>> = ({
120
198
  nodes={nodes}
121
199
  edges={edges}
122
200
  nodeTypes={nodeTypes}
201
+ edgeTypes={edgeTypes}
123
202
  minZoom={0.2}
124
203
  fitViewOptions={{
125
204
  minZoom: 0.5,
@@ -137,7 +216,17 @@ export const DependencyGraphTree: React.FC<PropsWithChildren<Props>> = ({
137
216
  });
138
217
  }}
139
218
  onNodeDoubleClick={(_event, node) => {
140
- scrollAndHighlightCell(node.id as CellId, "focus");
219
+ // Expand/collapse the node to reveal its full code in place.
220
+ const id = node.id as CellId;
221
+ setExpandedIds((prev) => {
222
+ const next = new Set(prev);
223
+ if (next.has(id)) {
224
+ next.delete(id);
225
+ } else {
226
+ next.add(id);
227
+ }
228
+ return next;
229
+ });
141
230
  }}
142
231
  fitView={true}
143
232
  onNodesChange={onNodesChange}