@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.
- package/dist/assets/{CellStatus-wvvZkEPY.js → CellStatus-LoTwk7fR.js} +1 -1
- package/dist/assets/{ConnectedDataExplorerComponent-Dkqk66Mv.js → ConnectedDataExplorerComponent-DW6cJ-Lr.js} +1 -1
- package/dist/assets/{ErrorBoundary-npvnLp9x.js → ErrorBoundary-CDVGFYfr.js} +1 -1
- package/dist/assets/JsonOutput-CzIGPEZQ.js +50 -0
- package/dist/assets/{LazyAnyLanguageCodeMirror-zSwE3dvU.js → LazyAnyLanguageCodeMirror-LboTfNDv.js} +2 -2
- package/dist/assets/{MarimoErrorOutput-CeUo5oka.js → MarimoErrorOutput-DqpoYOvw.js} +1 -1
- package/dist/assets/RenderHTML-DwhMAd9l.js +1 -0
- package/dist/assets/{RunButton-BaV8A-z4.js → RunButton-BlpbweyH.js} +1 -1
- package/dist/assets/{VisuallyHidden-SChAyKIQ.js → VisuallyHidden-CBMDmozt.js} +1 -1
- package/dist/assets/{add-cell-with-ai-DoLkfzYn.js → add-cell-with-ai-R-3DS79K.js} +1 -1
- package/dist/assets/{add-connection-dialog-C1IF1nR0.js → add-connection-dialog-BGfpw5lC.js} +1 -1
- package/dist/assets/{agent-panel-CG154QrU.js → agent-panel-Cgt_K0tD.js} +1 -1
- package/dist/assets/ai-model-dropdown-DCmiuk7x.js +3 -0
- package/dist/assets/alert-dialog-CA-2mBs3.js +11 -0
- package/dist/assets/any-language-editor-DflLjPSQ.js +1 -0
- package/dist/assets/{app-config-button-ktBH1pEB.js → app-config-button-DUJC6b6x.js} +1 -1
- package/dist/assets/badge-BSgtkC2e.js +1 -0
- package/dist/assets/{blob-DVtvZmWH.js → blob-B2d_dHwI.js} +1 -1
- package/dist/assets/button-Dh8EwhsF.js +1 -0
- package/dist/assets/{cache-panel-C7jsiqCa.js → cache-panel-DqgtC98s.js} +1 -1
- package/dist/assets/{capabilities-LWzb3E3M.js → capabilities-D8kPgMKr.js} +1 -1
- package/dist/assets/{cell-editor-DoDqaCZp.js → cell-editor-Dw09J9yD.js} +6 -6
- package/dist/assets/{cell-link-Dqqt6Rgs.js → cell-link-6burT2XF.js} +1 -1
- package/dist/assets/{cells-D4RwofRx.js → cells-Cp-G34Rz.js} +56 -56
- package/dist/assets/{chat-display-COmzHgx5.js → chat-display-Bu405iyc.js} +1 -1
- package/dist/assets/{chat-panel-B2tyEfGW.js → chat-panel-BlUwNY5j.js} +1 -1
- package/dist/assets/{chat-ui-B5bsr-7V.js → chat-ui-DGiVDzNh.js} +1 -1
- package/dist/assets/{circle--ysFeW9z.js → circle-ByJdIf5p.js} +1 -1
- package/dist/assets/{column-preview-CW5DPFvy.js → column-preview-riSXbYe_.js} +1 -1
- package/dist/assets/{command-palette-B9dyvMvJ.js → command-palette-CCVzJcyf.js} +1 -1
- package/dist/assets/{common-CC3tuCpi.js → common-CHR7w7aQ.js} +1 -1
- package/dist/assets/{components-D02ZZZMP.js → components-C7mWrmXr.js} +1 -1
- package/dist/assets/{components-CBlGIIAX.js → components-DUYCXngU.js} +1 -1
- package/dist/assets/config-B-r81uZ6.js +1 -0
- package/dist/assets/{config-D4frVGQ4.js → config-BlNM0caL.js} +1 -1
- package/dist/assets/{copy-Cg05mM3C.js → copy-DlEomWzI.js} +1 -1
- package/dist/assets/{copy-icon-CRxVZTJ9.js → copy-icon-z8fuTE3w.js} +1 -1
- package/dist/assets/{createReducer-Bq-nQJiK.js → createReducer-B_XmHROl.js} +1 -1
- package/dist/assets/{datasource-Do8f2hFf.js → datasource-DrODSIs0.js} +1 -1
- package/dist/assets/{dates-DK5JikJo.js → dates-B58T85Jq.js} +1 -1
- package/dist/assets/dependency-graph-panel-AY5BEVH2.js +5 -0
- package/dist/assets/{dist-7qdHLg-L.js → dist-B0RXffUm.js} +1 -1
- package/dist/assets/dist-B0qYy1Ye.js +1 -0
- package/dist/assets/{dist-B97dKQGP.js → dist-B8pdsUrS.js} +1 -1
- package/dist/assets/{dist-BcfF-rkl.js → dist-BF7Lrekv.js} +1 -1
- package/dist/assets/dist-BL8RjfvI.js +1 -0
- package/dist/assets/dist-BVZxge7j.js +1 -0
- package/dist/assets/{dist-fxnYzq-R.js → dist-BaRNSaca.js} +1 -1
- package/dist/assets/dist-Bk2yQtw4.js +1 -0
- package/dist/assets/dist-BsfiiKoy.js +1 -0
- package/dist/assets/dist-BskmVd3B.js +1 -0
- package/dist/assets/{dist-DL1Thauf.js → dist-Bsu3h9O7.js} +1 -1
- package/dist/assets/{dist-PIe1xcZo.js → dist-BtC9ym6k.js} +7 -7
- package/dist/assets/{dist-DeuWxYVk.js → dist-Bv0S1RlZ.js} +1 -1
- package/dist/assets/{dist-CidBzUqR.js → dist-C--ZICxQ.js} +1 -1
- package/dist/assets/dist-C-2fD3XY.js +1 -0
- package/dist/assets/{dist-DzAWvPVK.js → dist-CKUpxYGQ.js} +1 -1
- package/dist/assets/dist-CMFMfBm1.js +1 -0
- package/dist/assets/dist-CZNULd6Z.js +2 -0
- package/dist/assets/dist-CjFduPiC.js +1 -0
- package/dist/assets/{dist-DoAjHgMj.js → dist-CmImLjus.js} +1 -1
- package/dist/assets/{dist-BKx0MLbb.js → dist-Cq8NOszX.js} +1 -1
- package/dist/assets/{dist-DCc0VfiO.js → dist-Cx4BE7jC.js} +3 -3
- package/dist/assets/dist-D45ScydS.js +1 -0
- package/dist/assets/dist-D6dg87r7.js +1 -0
- package/dist/assets/dist-DEjfQLz0.js +1 -0
- package/dist/assets/{dist-BcYW-EJk.js → dist-DJOWOALB.js} +1 -1
- package/dist/assets/{dist-CxxZaSme.js → dist-DPoYlxSC.js} +1 -1
- package/dist/assets/dist-DR-Y-nl3.js +1 -0
- package/dist/assets/dist-Dahlp9JQ.js +1 -0
- package/dist/assets/{dist-DIkuvChK.js → dist-Db-HC93w.js} +1 -1
- package/dist/assets/{dist-DTM7BAUu.js → dist-DdOlZ6mj.js} +1 -1
- package/dist/assets/{dist-Dz_04liz.js → dist-DiYjQAhI.js} +1 -1
- package/dist/assets/{dist-BO5c5n_b.js → dist-DkPPv0FB.js} +1 -1
- package/dist/assets/{dist-DkCO0b-0.js → dist-DmGtPaVM.js} +1 -1
- package/dist/assets/{dist-CXmhxecl.js → dist-DnxYYyU7.js} +1 -1
- package/dist/assets/{dist-CO1Hfv5H.js → dist-Dw9YRsCU.js} +1 -1
- package/dist/assets/dist-Dwx3KdUP.js +1 -0
- package/dist/assets/{dist-BgL43w5r.js → dist-DzCQtGkt.js} +1 -1
- package/dist/assets/dist-JweaDt6P.js +1 -0
- package/dist/assets/{dist-Dbu5sPgR.js → dist-gELFCfHH.js} +1 -1
- package/dist/assets/dist-kGDJ0AWF.js +1 -0
- package/dist/assets/{dist-Cqm1W-Zm.js → dist-kaLvV_d4.js} +1 -1
- package/dist/assets/{dist-CEeRVpMD.js → dist-qvk3g06H.js} +1 -1
- package/dist/assets/dist-wCw2twuB.js +1 -0
- package/dist/assets/dist-wF42tUcr.js +1 -0
- package/dist/assets/dist-wY2yia-1.js +3 -0
- package/dist/assets/{documentation-panel-DvHjxlli.js → documentation-panel-CaJF7e-j.js} +1 -1
- package/dist/assets/{download-BP8MrprC.js → download-DMxOzI7b.js} +1 -1
- package/dist/assets/dropdown-menu-C40jM9o4.js +1 -0
- package/dist/assets/{edit-page-71cK-Nhe.js → edit-page-Vqd9NedM.js} +6 -6
- package/dist/assets/{error-banner-BYN8YUMe.js → error-banner-Dw5YY9wq.js} +1 -1
- package/dist/assets/{error-entries-DOZ1u97X.js → error-entries-Cv2m1yhc.js} +1 -1
- package/dist/assets/{error-panel-CINcf76Z.js → error-panel-Bhlvt6_H.js} +1 -1
- package/dist/assets/{es-BuMFDpR4.js → es-CrFhA96G.js} +1 -1
- package/dist/assets/{esm-Dw0vvSg2.js → esm-BHMOvJzM.js} +1 -1
- package/dist/assets/{esm-BGpfQNhD.js → esm-DmqQGMel.js} +1 -1
- package/dist/assets/file-explorer-panel-6HvNfCrJ.js +1 -0
- package/dist/assets/{file-icons-DgKj9mrV.js → file-icons-DdGYuADJ.js} +1 -1
- package/dist/assets/file-name-input-K4MKESI_.js +3 -0
- package/dist/assets/{floating-outline-CRXIsrPm.js → floating-outline-DaUHnl_e.js} +1 -1
- package/dist/assets/{formats-DSzGCKeO.js → formats-C6s96nu2.js} +1 -1
- package/dist/assets/{formatting-BJlT1s_3.js → formatting-BWZH2nBg.js} +1 -1
- package/dist/assets/{fullscreen-0EEPR7uc.js → fullscreen-BH2jz-F4.js} +5 -5
- package/dist/assets/{gallery-page-TqnFYECu.js → gallery-page-C3pBpHlY.js} +1 -1
- package/dist/assets/{glide-data-editor-DcY3nXI8.js → glide-data-editor-sICUMEhk.js} +1 -1
- package/dist/assets/{glide-utils-CR1sWLZU.js → glide-utils-UGsrRHYj.js} +1 -1
- package/dist/assets/home-page-BLd6rnbk.js +4 -0
- package/dist/assets/{html-to-image-DMn_7CCA.js → html-to-image-D6GUC5q6.js} +1 -1
- package/dist/assets/index-BGT7have.css +2 -0
- package/dist/assets/index-ipFYAeCF.js +29 -0
- package/dist/assets/{input-Daz6sk8l.js → input-DEbY281O.js} +2 -2
- package/dist/assets/{kiosk-mode-CzGt3mc2.js → kiosk-mode-mxgb5lWf.js} +1 -1
- package/dist/assets/{layout-KJMQADEj.js → layout-DWnrxIo2.js} +5 -5
- package/dist/assets/links-DLvOXyO_.js +1 -0
- package/dist/assets/{logs-panel-DFibPeCT.js → logs-panel-BGS2v3ba.js} +1 -1
- package/dist/assets/lua-1tNpM92n.js +1 -0
- package/dist/assets/{lua-CRIS5afH.js → lua-DXBxhmso.js} +1 -1
- package/dist/assets/{markdown-renderer-CdZX2MvM.js → markdown-renderer-BZKlbm2Q.js} +1 -1
- package/dist/assets/menu-items-BSRA2YI9.js +1 -0
- package/dist/assets/{mermaid-CeseGGnS.js → mermaid-l6lQkCib.js} +1 -1
- package/dist/assets/{name-cell-input-B-d5Jwh4.js → name-cell-input-BMCdK-el.js} +1 -1
- package/dist/assets/{numbers-DKurV_vq.js → numbers-Bc-r3jsC.js} +1 -1
- package/dist/assets/{outline-panel-D2Wk10hY.js → outline-panel-Dr5u73V6.js} +1 -1
- package/dist/assets/{packages-panel-CQsbxIlG.js → packages-panel-BdpRg9tX.js} +1 -1
- package/dist/assets/{pair-with-agent-modal-CNxmIMFM.js → pair-with-agent-modal-CbUFKJHP.js} +1 -1
- package/dist/assets/{panel-accordion-state-D6-FMXE2.js → panel-accordion-state-DjUTN7i5.js} +1 -1
- package/dist/assets/{panels-BgcK_4M7.js → panels-CWn0Yh1D.js} +1 -1
- package/dist/assets/process-output-Ck7-P_xp.js +1 -0
- package/dist/assets/{readonly-code-display-BvqLeUsJ.js → readonly-code-display--S2daDa6.js} +1 -1
- package/dist/assets/{readonly-python-code-GSN4D4sb.js → readonly-python-code-DpIyjHqY.js} +1 -1
- package/dist/assets/{renderShortcut-DK_OucLT.js → renderShortcut-X-CJUSM8.js} +1 -1
- package/dist/assets/{reveal-component-u9ED2diI.js → reveal-component-nZIAapoD.js} +1 -1
- package/dist/assets/{run-page-U5iuhyz8.js → run-page-9pl8qhQR.js} +1 -1
- package/dist/assets/{runs-BQTlv6Ja.js → runs-euHSzlw_.js} +1 -1
- package/dist/assets/{scratchpad-panel--YOR0kUB.js → scratchpad-panel-BleGt2KM.js} +1 -1
- package/dist/assets/{scroll-to-bottom-button-xua0e9rA.js → scroll-to-bottom-button-DufiYUD7.js} +1 -1
- package/dist/assets/{secrets-panel-CZPbFAZH.js → secrets-panel-D3BpDIgg.js} +1 -1
- package/dist/assets/select-nLt2y0Sw.js +1 -0
- package/dist/assets/{session-CEdta5_Q.js → session-Sz5nSFku.js} +1 -1
- package/dist/assets/session-panel-DRGPlL5J.js +1 -0
- package/dist/assets/{share-C2MPy_VX.js → share-CPtBiNgr.js} +1 -1
- package/dist/assets/{skeleton-CsceCAjw.js → skeleton-BKeh8hf3.js} +1 -1
- package/dist/assets/{snippets-panel-BxwHsPmW.js → snippets-panel-D5yy7kj0.js} +1 -1
- package/dist/assets/{spec-CUFquO6R.js → spec-DKsAmLTi.js} +1 -1
- package/dist/assets/{state-BqrF--O0.js → state-B6AklCzD.js} +1 -1
- package/dist/assets/{state-C2c3_VuY.js → state-B8XRqyFg.js} +1 -1
- package/dist/assets/{state-CHplO0zn.js → state-Di9TbuV9.js} +1 -1
- package/dist/assets/{strings-CpYReX2Q.js → strings-CReV1AYG.js} +1 -1
- package/dist/assets/{swiper-component-Bot7ehYH.js → swiper-component-5Kdi1-Gn.js} +1 -1
- package/dist/assets/{switch-CtH5Ct94.js → switch-DjXr9Y9D.js} +1 -1
- package/dist/assets/{tabs-c6mpccpH.js → tabs-Cy1qXLzH.js} +1 -1
- package/dist/assets/{terminal-C73iz1fU.js → terminal-CQAFGBfN.js} +1 -1
- package/dist/assets/{tooltip-Cwltdez8.js → tooltip-BG35uS_N.js} +1 -1
- package/dist/assets/{tooltip-D7dq6bnc.js → tooltip-BUtPzKB4.js} +1 -1
- package/dist/assets/{tracing-Dhq121dR.js → tracing-DNk3gmWv.js} +1 -1
- package/dist/assets/{tracing-panel-IKt1933_.js → tracing-panel-Yqazr6w_.js} +2 -2
- package/dist/assets/{tree-actions-DIfWonCA.js → tree-actions-CG5g7aUQ.js} +1 -1
- package/dist/assets/{useAddCell-BYrVaYfZ.js → useAddCell-DdB_uBif.js} +1 -1
- package/dist/assets/{useBoolean-ZU5StAbr.js → useBoolean-BXC4CItZ.js} +1 -1
- package/dist/assets/{useCellActionButton-DbXDs0MA.js → useCellActionButton-B31neI3m.js} +1 -1
- package/dist/assets/{useDebounce-Dulm61-t.js → useDebounce-DgpSQhrP.js} +1 -1
- package/dist/assets/{useDeleteCell-Wgcl3CRG.js → useDeleteCell-Bz49_cFj.js} +1 -1
- package/dist/assets/{useDependencyPanelTab-C5AU8GKL.js → useDependencyPanelTab-C_ps0r--.js} +1 -1
- package/dist/assets/{useEventListener-D27Dii_l.js → useEventListener-92uWqz-P.js} +1 -1
- package/dist/assets/{useIframeCapabilities-DEpTCh1Q.js → useIframeCapabilities-C7Di8f2y.js} +1 -1
- package/dist/assets/{useInstallPackage-u7d0wo79.js → useInstallPackage-DsD08SLK.js} +1 -1
- package/dist/assets/{useInterval-B1_J4wdT.js → useInterval-B4v7_zd0.js} +1 -1
- package/dist/assets/{useNotebookActions-FuMERtHq.js → useNotebookActions-CQtbk9CB.js} +2 -2
- package/dist/assets/{useRunCells-DTdMRbkE.js → useRunCells-CX7oW5zy.js} +1 -1
- package/dist/assets/{useSplitCell-2z0zEB8X.js → useSplitCell-Dy2_DmJR.js} +1 -1
- package/dist/assets/{useTheme-CF-zG9YH.js → useTheme-C-ri8BtY.js} +1 -1
- package/dist/assets/utils-D5Ywm7-q.js +1 -0
- package/dist/assets/{vega-component-BSXJcTJb.js → vega-component-C7TCzyaU.js} +1 -1
- package/dist/assets/write-secret-modal-D6zoZgLY.js +2 -0
- package/dist/index.html +73 -73
- package/package.json +26 -26
- package/src/components/ai/ai-provider-icon.tsx +2 -2
- package/src/components/ai/ai-utils.ts +1 -1
- package/src/components/app-config/ai-config.tsx +33 -37
- package/src/components/app-config/user-config-form.tsx +1 -1
- package/src/components/data-table/__tests__/column-explorer.test.tsx +64 -4
- package/src/components/data-table/__tests__/column-visibility-dropdown.test.tsx +137 -4
- package/src/components/data-table/__tests__/url-detector.test.tsx +1 -1
- package/src/components/data-table/__tests__/useColumnVisibility.test.ts +153 -1
- package/src/components/data-table/column-explorer-panel/column-explorer.tsx +54 -25
- package/src/components/data-table/column-visibility-dropdown.tsx +62 -14
- package/src/components/data-table/date-popover.tsx +2 -1
- package/src/components/data-table/hooks/use-column-visibility.ts +76 -0
- package/src/components/data-table/renderers.tsx +4 -8
- package/src/components/data-table/show-only-column-button.tsx +58 -0
- package/src/components/dependency-graph/__tests__/elements.test.ts +151 -0
- package/src/components/dependency-graph/custom-edge.tsx +68 -0
- package/src/components/dependency-graph/custom-node.tsx +54 -26
- package/src/components/dependency-graph/dependency-graph-tree.tsx +110 -21
- package/src/components/dependency-graph/elements.ts +225 -31
- package/src/components/dependency-graph/panels.tsx +23 -2
- package/src/components/dependency-graph/utils/__tests__/layout.test.ts +78 -0
- package/src/components/dependency-graph/utils/layout.ts +108 -18
- package/src/components/editor/Output.tsx +40 -0
- package/src/components/editor/__tests__/Output.test.tsx +85 -2
- package/src/components/editor/connections/secret-combobox.tsx +3 -1
- package/src/components/editor/file-tree/__tests__/file-explorer-navigation.test.tsx +832 -0
- package/src/components/editor/file-tree/__tests__/file-viewer.test.tsx +27 -0
- package/src/components/editor/file-tree/__tests__/requesting-tree.test.ts +130 -0
- package/src/components/editor/file-tree/file-browser-row.tsx +82 -0
- package/src/components/editor/file-tree/file-explorer.tsx +377 -159
- package/src/components/editor/file-tree/file-operations.tsx +1 -1
- package/src/components/editor/file-tree/file-search-results.tsx +142 -0
- package/src/components/editor/file-tree/file-tree-row.tsx +40 -0
- package/src/components/editor/file-tree/file-viewer.tsx +2 -1
- package/src/components/editor/file-tree/renderers.tsx +4 -2
- package/src/components/editor/file-tree/requesting-tree.tsx +162 -32
- package/src/components/editor/file-tree/use-file-search.ts +142 -0
- package/src/components/editor/file-tree/use-hover-expand.ts +34 -0
- package/src/components/editor/navigation/__tests__/navigation.test.ts +30 -0
- package/src/components/editor/navigation/navigation.ts +20 -0
- package/src/components/forms/form.tsx +6 -4
- package/src/components/home/components.tsx +1 -0
- package/src/components/static-html/share-modal.tsx +3 -2
- package/src/components/ui/__tests__/badge.test.ts +63 -0
- package/src/components/ui/__tests__/button.test.ts +19 -0
- package/src/components/ui/__tests__/fullscreen.test.tsx +88 -0
- package/src/components/ui/__tests__/menu-items.test.ts +31 -0
- package/src/components/ui/badge.tsx +9 -7
- package/src/components/ui/button.tsx +1 -0
- package/src/components/ui/combobox.tsx +1 -1
- package/src/components/ui/date-input.tsx +1 -1
- package/src/components/ui/dialog.tsx +3 -1
- package/src/components/ui/fullscreen.tsx +43 -1
- package/src/components/ui/input.tsx +5 -1
- package/src/components/ui/links.tsx +2 -1
- package/src/components/ui/menu-items.tsx +9 -9
- package/src/components/ui/native-select.tsx +2 -2
- package/src/components/ui/navigation.tsx +1 -1
- package/src/components/ui/range-slider.tsx +5 -3
- package/src/components/ui/select.tsx +1 -1
- package/src/components/ui/sheet.tsx +1 -1
- package/src/components/ui/toast.tsx +8 -5
- package/src/components/ui/toaster.tsx +2 -2
- package/src/core/ai/ids/__tests__/ids.test.ts +0 -8
- package/src/core/ai/ids/ids.ts +1 -1
- package/src/core/cells/__tests__/add-missing-import.test.ts +20 -0
- package/src/core/codemirror/language/__tests__/utils.test.ts +1 -1
- package/src/core/config/config-schema.ts +1 -0
- package/src/core/hotkeys/hotkeys.ts +5 -0
- package/src/core/network/__tests__/requests-toasting.test.tsx +14 -0
- package/src/core/network/requests-toasting.tsx +1 -1
- package/src/css/globals.css +28 -0
- package/src/hooks/useEventListener.ts +1 -1
- package/src/plugins/core/RenderHTML.tsx +6 -4
- package/src/plugins/impl/MatrixPlugin.tsx +1 -1
- package/src/plugins/impl/code/any-language-editor.tsx +7 -4
- package/src/plugins/impl/data-frames/__tests__/schema.test.ts +34 -1
- package/src/plugins/impl/data-frames/forms/__tests__/__snapshots__/form.test.tsx.snap +25 -25
- package/src/plugins/impl/data-frames/schema.ts +5 -11
- package/src/plugins/impl/panel/__tests__/utils.test.ts +4 -4
- package/src/plugins/impl/vega/__tests__/vega.test.ts +1 -1
- package/src/stories/cell.stories.tsx +15 -11
- package/src/utils/__tests__/events.test.ts +1 -1
- package/dist/assets/JsonOutput-CRLQQVKf.js +0 -50
- package/dist/assets/RenderHTML-YkwVPe9V.js +0 -1
- package/dist/assets/ai-model-dropdown-CrSrb8Zz.js +0 -3
- package/dist/assets/alert-dialog-BVi_GyOB.js +0 -11
- package/dist/assets/any-language-editor-BL8H-DwB.js +0 -1
- package/dist/assets/badge-D_-uqy2N.js +0 -1
- package/dist/assets/button-Btj6GXNy.js +0 -1
- package/dist/assets/config-ycxJQP2b.js +0 -1
- package/dist/assets/dependency-graph-panel-Dg-tUB9T.js +0 -5
- package/dist/assets/dist-2oRmY-BC.js +0 -1
- package/dist/assets/dist-8lhGoJ54.js +0 -3
- package/dist/assets/dist-B4QaGCvF.js +0 -1
- package/dist/assets/dist-B_ABJVTW.js +0 -1
- package/dist/assets/dist-Bke0Ybxy.js +0 -1
- package/dist/assets/dist-Bw8CAY5i.js +0 -1
- package/dist/assets/dist-BwmO3SSk.js +0 -1
- package/dist/assets/dist-C3KxgR0m.js +0 -1
- package/dist/assets/dist-CLE562nZ.js +0 -1
- package/dist/assets/dist-CLi0j3bw.js +0 -1
- package/dist/assets/dist-CMIyXejt.js +0 -1
- package/dist/assets/dist-CbQrIaPV.js +0 -2
- package/dist/assets/dist-CpgfIIzU.js +0 -1
- package/dist/assets/dist-D7aYPqs9.js +0 -1
- package/dist/assets/dist-DKCF5R0T.js +0 -1
- package/dist/assets/dist-DbFa9cik.js +0 -1
- package/dist/assets/dist-DdPuhs_-.js +0 -1
- package/dist/assets/dist-DkfdZExz.js +0 -1
- package/dist/assets/dist-hOh6JBwi.js +0 -1
- package/dist/assets/dist-mY1j_5eA.js +0 -1
- package/dist/assets/dist-tg3aVnpD.js +0 -1
- package/dist/assets/dropdown-menu-CqNabM1f.js +0 -1
- package/dist/assets/file-explorer-panel-cflxW8a7.js +0 -1
- package/dist/assets/file-name-input-CQF4Ovwx.js +0 -3
- package/dist/assets/home-page-DPRHPAQM.js +0 -4
- package/dist/assets/index-BkIXRAbi.css +0 -2
- package/dist/assets/index-p49fbIlR.js +0 -29
- package/dist/assets/links-DPh7yoP-.js +0 -1
- package/dist/assets/lua-_4wuYo1j.js +0 -1
- package/dist/assets/menu-items-BSVrMleU.js +0 -1
- package/dist/assets/process-output-DvTtgoMs.js +0 -1
- package/dist/assets/select-czJLm6jf.js +0 -1
- package/dist/assets/session-panel-C_N3WGWQ.js +0 -1
- package/dist/assets/utils-CWSZQEjr.js +0 -1
- package/dist/assets/write-secret-modal-lNU_7c2x.js +0 -2
|
@@ -16,14 +16,17 @@ import {
|
|
|
16
16
|
PlaySquareIcon,
|
|
17
17
|
UploadIcon,
|
|
18
18
|
ViewIcon,
|
|
19
|
+
XIcon,
|
|
19
20
|
} from "lucide-react";
|
|
20
21
|
import React, { Suspense, use, useRef, useState } from "react";
|
|
21
22
|
import {
|
|
23
|
+
type DragPreviewProps,
|
|
22
24
|
type NodeApi,
|
|
23
25
|
type NodeRendererProps,
|
|
24
26
|
Tree,
|
|
25
27
|
type TreeApi,
|
|
26
28
|
} from "react-arborist";
|
|
29
|
+
import { createPortal } from "react-dom";
|
|
27
30
|
import useEvent from "react-use-event-hook";
|
|
28
31
|
import {
|
|
29
32
|
FILE_ICON,
|
|
@@ -54,6 +57,7 @@ import {
|
|
|
54
57
|
DropdownMenuSeparator,
|
|
55
58
|
} from "@/components/ui/dropdown-menu";
|
|
56
59
|
import { Tooltip } from "@/components/ui/tooltip";
|
|
60
|
+
import { SearchInput } from "@/components/ui/input";
|
|
57
61
|
import { toast } from "@/components/ui/use-toast";
|
|
58
62
|
import { useCellActions } from "@/core/cells/cells";
|
|
59
63
|
import { useLastFocusedCellId } from "@/core/cells/focus";
|
|
@@ -61,6 +65,8 @@ import { disableFileDownloadsAtom } from "@/core/config/config";
|
|
|
61
65
|
import { useRequestClient } from "@/core/network/requests";
|
|
62
66
|
import { isWasm } from "@/core/wasm/utils";
|
|
63
67
|
import { useAsyncData } from "@/hooks/useAsyncData";
|
|
68
|
+
import { useFileSearch } from "./use-file-search";
|
|
69
|
+
import { useHoverExpand } from "./use-hover-expand";
|
|
64
70
|
import { ErrorBanner } from "@/plugins/impl/common/error-banner";
|
|
65
71
|
import { cn } from "@/utils/cn";
|
|
66
72
|
import { copyToClipboard } from "@/utils/copy";
|
|
@@ -71,6 +77,8 @@ import { jotaiJsonStorage } from "@/utils/storage/jotai";
|
|
|
71
77
|
import { useTreeDndManager } from "./dnd-wrapper";
|
|
72
78
|
import { downloadFile } from "./download";
|
|
73
79
|
import { FileViewer } from "./file-viewer";
|
|
80
|
+
import { FileSearchResults } from "./file-search-results";
|
|
81
|
+
import { FileBrowserRow } from "./file-browser-row";
|
|
74
82
|
import type { FileTreeNode, RequestingTree } from "./requesting-tree";
|
|
75
83
|
import { openStateAtom, treeAtom } from "./state";
|
|
76
84
|
import { PYTHON_CODE_FOR_FILE_TYPE } from "./types";
|
|
@@ -102,6 +110,10 @@ export const FileExplorer: React.FC<{
|
|
|
102
110
|
externalDropDestinationPath?: FilePath | null;
|
|
103
111
|
}> = ({ height, externalDropDestinationPath = null }) => {
|
|
104
112
|
const treeRef = useRef<TreeApi<FileTreeNode>>(null);
|
|
113
|
+
const searchTreeRef = useRef<TreeApi<FileTreeNode>>(null);
|
|
114
|
+
const searchRef = useRef<HTMLInputElement>(null);
|
|
115
|
+
const [query, setQuery] = useState("");
|
|
116
|
+
const [revealId, setRevealId] = useState<string | null>(null);
|
|
105
117
|
const dndManager = useTreeDndManager();
|
|
106
118
|
const [tree] = useAtom(treeAtom);
|
|
107
119
|
const [data, setData] = useState<FileTreeNode[]>([]);
|
|
@@ -112,6 +124,7 @@ export const FileExplorer: React.FC<{
|
|
|
112
124
|
} | null>(null);
|
|
113
125
|
const [showHiddenFiles, setShowHiddenFiles] =
|
|
114
126
|
useAtom<boolean>(hiddenFilesState);
|
|
127
|
+
const search = useFileSearch({ query: query.trim(), tree, showHiddenFiles });
|
|
115
128
|
// Keep external state to remember which folders are open when this
|
|
116
129
|
// component is unmounted.
|
|
117
130
|
const [openState, setOpenState] = useAtom(openStateAtom);
|
|
@@ -135,14 +148,12 @@ export const FileExplorer: React.FC<{
|
|
|
135
148
|
openUploadPicker();
|
|
136
149
|
});
|
|
137
150
|
|
|
138
|
-
const { openPrompt } = useImperativeModal();
|
|
151
|
+
const { openPrompt, openConfirm } = useImperativeModal();
|
|
139
152
|
const { isPending, error } = useAsyncData(() => tree.initialize(setData), []);
|
|
140
153
|
|
|
141
|
-
const handleRefresh = useEvent(() => {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
Object.keys(openState).filter((id) => openState[id]),
|
|
145
|
-
);
|
|
154
|
+
const handleRefresh = useEvent(async () => {
|
|
155
|
+
await tree.refreshAll(Object.keys(openState).filter((id) => openState[id]));
|
|
156
|
+
search.refetch();
|
|
146
157
|
});
|
|
147
158
|
|
|
148
159
|
const handleHiddenFilesToggle = useEvent(() => {
|
|
@@ -204,6 +215,77 @@ export const FileExplorer: React.FC<{
|
|
|
204
215
|
[tree, handleUploadFiles, externalDropDestinationPath],
|
|
205
216
|
);
|
|
206
217
|
|
|
218
|
+
React.useEffect(() => {
|
|
219
|
+
if (revealId && !openFile) {
|
|
220
|
+
const activeTree = query.trim() ? searchTreeRef.current : treeRef.current;
|
|
221
|
+
activeTree?.openParents(revealId);
|
|
222
|
+
activeTree?.select(revealId);
|
|
223
|
+
const node = activeTree?.get(revealId);
|
|
224
|
+
if (node?.data.isDirectory) {
|
|
225
|
+
node.open();
|
|
226
|
+
}
|
|
227
|
+
setRevealId(null);
|
|
228
|
+
}
|
|
229
|
+
}, [revealId, query, openFile, data]);
|
|
230
|
+
|
|
231
|
+
const handleReveal = async (node: FileTreeNode) => {
|
|
232
|
+
if (!node.isDirectory) {
|
|
233
|
+
setOpenFile(node);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
if (await tree.reveal(node)) {
|
|
237
|
+
setQuery("");
|
|
238
|
+
setRevealId(node.id);
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
const handleTreeKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
|
|
243
|
+
if ((event.ctrlKey || event.metaKey) && event.key === "f") {
|
|
244
|
+
event.preventDefault();
|
|
245
|
+
event.stopPropagation();
|
|
246
|
+
searchRef.current?.focus();
|
|
247
|
+
searchRef.current?.select();
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
if (event.key === "Escape" && query) {
|
|
251
|
+
event.preventDefault();
|
|
252
|
+
event.stopPropagation();
|
|
253
|
+
setQuery("");
|
|
254
|
+
searchRef.current?.focus();
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
const role =
|
|
258
|
+
event.target instanceof Element
|
|
259
|
+
? event.target.getAttribute("role")
|
|
260
|
+
: null;
|
|
261
|
+
if (query.trim() || (role !== "treeitem" && role !== "tree")) {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
const node = treeRef.current?.focusedNode;
|
|
265
|
+
if (!node || treeRef.current?.isEditing) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
if (event.key === "Enter") {
|
|
269
|
+
event.preventDefault();
|
|
270
|
+
event.stopPropagation();
|
|
271
|
+
if (node.data.isDirectory) {
|
|
272
|
+
node.toggle();
|
|
273
|
+
} else {
|
|
274
|
+
node.activate();
|
|
275
|
+
}
|
|
276
|
+
} else if (event.key === "F2" && !node.data.isRoot) {
|
|
277
|
+
event.preventDefault();
|
|
278
|
+
event.stopPropagation();
|
|
279
|
+
node.edit();
|
|
280
|
+
} else if (event.key === "Delete" || event.key === "Backspace") {
|
|
281
|
+
event.preventDefault();
|
|
282
|
+
event.stopPropagation();
|
|
283
|
+
if (!node.data.isRoot) {
|
|
284
|
+
void node.tree.delete(node.id);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
|
|
207
289
|
if (isPending) {
|
|
208
290
|
return <Spinner size="medium" centered={true} />;
|
|
209
291
|
}
|
|
@@ -212,128 +294,230 @@ export const FileExplorer: React.FC<{
|
|
|
212
294
|
return <ErrorBanner error={error} />;
|
|
213
295
|
}
|
|
214
296
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
<
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
297
|
+
const preview = openFile ? (
|
|
298
|
+
<>
|
|
299
|
+
<div className="flex items-center pl-1 pr-3 shrink-0 border-b justify-between">
|
|
300
|
+
<Button
|
|
301
|
+
autoFocus={true}
|
|
302
|
+
aria-label="Back to files"
|
|
303
|
+
onClick={() => {
|
|
304
|
+
setRevealId(openFile.id);
|
|
305
|
+
setOpenFile(null);
|
|
306
|
+
}}
|
|
307
|
+
data-testid="file-explorer-back-button"
|
|
308
|
+
variant="text"
|
|
309
|
+
size="xs"
|
|
310
|
+
className="mb-0"
|
|
311
|
+
>
|
|
312
|
+
<ArrowLeftIcon size={16} />
|
|
313
|
+
</Button>
|
|
314
|
+
<span className="font-bold">{openFile.name}</span>
|
|
315
|
+
</div>
|
|
316
|
+
<Suspense>
|
|
317
|
+
<FileViewer
|
|
318
|
+
onOpenNotebook={
|
|
319
|
+
tree.isPrimaryNode(openFile)
|
|
320
|
+
? (evt) => {
|
|
321
|
+
const path = tree.getPrimaryRelativePath(
|
|
322
|
+
openFile.path as FilePath,
|
|
323
|
+
);
|
|
324
|
+
if (path !== null) {
|
|
325
|
+
openMarimoNotebook(evt, path);
|
|
241
326
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
327
|
+
}
|
|
328
|
+
: undefined
|
|
329
|
+
}
|
|
330
|
+
file={openFile}
|
|
331
|
+
/>
|
|
332
|
+
</Suspense>
|
|
333
|
+
</>
|
|
334
|
+
) : null;
|
|
250
335
|
|
|
251
336
|
return (
|
|
252
337
|
<>
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
selectedFolder?.path ?? tree.getPrimaryRootPath(),
|
|
271
|
-
)}
|
|
272
|
-
onUpload={handleUploadFiles}
|
|
273
|
-
/>
|
|
274
|
-
<RequestingTreeContext value={contextValue}>
|
|
275
|
-
<Tree<FileTreeNode>
|
|
276
|
-
width="100%"
|
|
277
|
-
ref={treeRef}
|
|
278
|
-
height={height - 33}
|
|
279
|
-
className="h-full"
|
|
280
|
-
data={visibleData}
|
|
281
|
-
initialOpenState={openState}
|
|
282
|
-
openByDefault={false}
|
|
283
|
-
// Use shared DnD manager to prevent "Cannot have two HTML5 backends" error
|
|
284
|
-
dndManager={dndManager}
|
|
285
|
-
// Hide the drop cursor
|
|
286
|
-
renderCursor={() => null}
|
|
287
|
-
// Disable dropping files into files
|
|
288
|
-
disableDrag={(node) => node.isRoot}
|
|
289
|
-
disableDrop={({ parentNode, dragNodes }) =>
|
|
290
|
-
dragNodes.some((node) => node.data.isRoot) ||
|
|
291
|
-
(parentNode ? !parentNode.data.isDirectory : false)
|
|
292
|
-
}
|
|
293
|
-
onDelete={async ({ ids }) => {
|
|
294
|
-
for (const id of ids) {
|
|
295
|
-
await tree.delete(id);
|
|
338
|
+
{preview}
|
|
339
|
+
<div hidden={!!openFile} onKeyDownCapture={handleTreeKeyDown}>
|
|
340
|
+
<input
|
|
341
|
+
data-testid="file-explorer-upload-input"
|
|
342
|
+
{...getUploadInputProps()}
|
|
343
|
+
/>
|
|
344
|
+
<div>
|
|
345
|
+
<Toolbar
|
|
346
|
+
onRefresh={handleRefresh}
|
|
347
|
+
onHidden={handleHiddenFilesToggle}
|
|
348
|
+
showHiddenFiles={showHiddenFiles}
|
|
349
|
+
onCreateFile={handleCreateFile}
|
|
350
|
+
onCreateNotebook={handleCreateNotebook}
|
|
351
|
+
onCreateFolder={handleCreateFolder}
|
|
352
|
+
onCollapseAll={handleCollapseAll}
|
|
353
|
+
uploadDestinationPath={
|
|
354
|
+
selectedFolder?.path ?? tree.getPrimaryRootPath()
|
|
296
355
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
356
|
+
uploadDestinationLabel={getUploadDestinationLabel(
|
|
357
|
+
tree,
|
|
358
|
+
selectedFolder?.path ?? tree.getPrimaryRootPath(),
|
|
359
|
+
)}
|
|
360
|
+
onUpload={handleUploadFiles}
|
|
361
|
+
/>
|
|
362
|
+
<div className="relative -mt-1">
|
|
363
|
+
<SearchInput
|
|
364
|
+
ref={searchRef}
|
|
365
|
+
className="rounded-none pr-6 focus-visible:outline-1 focus-visible:outline-ring/50"
|
|
366
|
+
clearable={false}
|
|
367
|
+
aria-label="Search files and folders"
|
|
368
|
+
placeholder="Search"
|
|
369
|
+
title="Search names in all configured roots, up to 20 folders deep. Generated directories such as node_modules and .git are excluded."
|
|
370
|
+
value={query}
|
|
371
|
+
onChange={(event) => setQuery(event.target.value)}
|
|
372
|
+
onKeyDown={(event) => {
|
|
373
|
+
const activeTree = query.trim()
|
|
374
|
+
? searchTreeRef.current
|
|
375
|
+
: treeRef.current;
|
|
376
|
+
if (event.key === "ArrowDown") {
|
|
377
|
+
event.preventDefault();
|
|
378
|
+
activeTree?.focus(activeTree.firstNode);
|
|
379
|
+
} else if (event.key === "Enter" && query.trim()) {
|
|
380
|
+
event.preventDefault();
|
|
381
|
+
activeTree?.firstNode?.activate();
|
|
382
|
+
}
|
|
383
|
+
}}
|
|
384
|
+
/>
|
|
385
|
+
{query && (
|
|
386
|
+
<Button
|
|
387
|
+
variant="text"
|
|
388
|
+
size="icon"
|
|
389
|
+
className="absolute right-3 top-1/2 h-5 w-5 -translate-y-1/2"
|
|
390
|
+
aria-label="Clear search"
|
|
391
|
+
onClick={() => {
|
|
392
|
+
setQuery("");
|
|
393
|
+
searchRef.current?.focus();
|
|
394
|
+
}}
|
|
395
|
+
>
|
|
396
|
+
<XIcon className="h-3 w-3" />
|
|
397
|
+
</Button>
|
|
398
|
+
)}
|
|
399
|
+
</div>
|
|
400
|
+
</div>
|
|
401
|
+
<div hidden={!query.trim()}>
|
|
402
|
+
<FileSearchResults
|
|
403
|
+
treeRef={searchTreeRef}
|
|
404
|
+
search={search}
|
|
405
|
+
height={Math.max(0, height - 63)}
|
|
406
|
+
onReveal={handleReveal}
|
|
407
|
+
/>
|
|
408
|
+
</div>
|
|
409
|
+
<div hidden={!!query.trim()} className="relative">
|
|
410
|
+
<RequestingTreeContext value={contextValue}>
|
|
411
|
+
<Tree<FileTreeNode>
|
|
412
|
+
width="100%"
|
|
413
|
+
ref={treeRef}
|
|
414
|
+
height={Math.max(0, height - 63)}
|
|
415
|
+
className="h-full"
|
|
416
|
+
data={visibleData}
|
|
417
|
+
childrenAccessor={(node) =>
|
|
418
|
+
node.isDirectory ? node.children : null
|
|
419
|
+
}
|
|
420
|
+
selectionFollowsFocus={true}
|
|
421
|
+
rowClassName="outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring"
|
|
422
|
+
renderRow={FileBrowserRow}
|
|
423
|
+
renderDragPreview={(props) => (
|
|
424
|
+
<FileDragPreview {...props} tree={treeRef.current} />
|
|
425
|
+
)}
|
|
426
|
+
disableEdit={(node) => node.isRoot}
|
|
427
|
+
initialOpenState={openState}
|
|
428
|
+
openByDefault={false}
|
|
429
|
+
// Use shared DnD manager to prevent "Cannot have two HTML5 backends" error
|
|
430
|
+
dndManager={dndManager}
|
|
431
|
+
// Hide the drop cursor
|
|
432
|
+
renderCursor={() => null}
|
|
433
|
+
// Disable dropping files into files
|
|
434
|
+
disableDrag={(node) => node.isRoot}
|
|
435
|
+
disableDrop={({ parentNode, dragNodes }) =>
|
|
436
|
+
dragNodes.some((node) => node.data.isRoot) ||
|
|
437
|
+
(parentNode ? !parentNode.data.isDirectory : false)
|
|
438
|
+
}
|
|
439
|
+
onDelete={async ({ ids }) => {
|
|
440
|
+
openConfirm({
|
|
441
|
+
title: "Delete file",
|
|
442
|
+
description:
|
|
443
|
+
"Are you sure you want to delete the selected file or folder?",
|
|
444
|
+
confirmAction: (
|
|
445
|
+
<AlertDialogDestructiveAction
|
|
446
|
+
onClick={async () => {
|
|
447
|
+
for (const id of ids) {
|
|
448
|
+
await tree.delete(id);
|
|
449
|
+
}
|
|
450
|
+
}}
|
|
451
|
+
>
|
|
452
|
+
Delete
|
|
453
|
+
</AlertDialogDestructiveAction>
|
|
454
|
+
),
|
|
455
|
+
});
|
|
456
|
+
}}
|
|
457
|
+
onRename={async ({ id, name }) => {
|
|
458
|
+
await tree.rename(id, name);
|
|
459
|
+
}}
|
|
460
|
+
onMove={async ({ dragIds, parentId }) => {
|
|
461
|
+
await tree.move(dragIds, parentId);
|
|
462
|
+
}}
|
|
463
|
+
onSelect={(nodes) => {
|
|
464
|
+
const first = nodes[0];
|
|
465
|
+
if (!first) {
|
|
466
|
+
setSelectedFolder(null);
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
if (first.data.isDirectory) {
|
|
470
|
+
setSelectedFolder({
|
|
471
|
+
id: first.id,
|
|
472
|
+
path: first.data.path as FilePath,
|
|
473
|
+
});
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
setSelectedFolder(null);
|
|
477
|
+
}}
|
|
478
|
+
onActivate={(node) => {
|
|
479
|
+
if (!node.data.isDirectory) {
|
|
480
|
+
setOpenFile(node.data);
|
|
481
|
+
}
|
|
482
|
+
}}
|
|
483
|
+
onToggle={async (id) => {
|
|
484
|
+
const isOpen =
|
|
485
|
+
treeRef.current?.isOpen(id) ?? !(openState[id] ?? false);
|
|
486
|
+
if (!isOpen) {
|
|
487
|
+
setOpenState((previous) => ({ ...previous, [id]: false }));
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
const loaded = await tree.expand(id);
|
|
491
|
+
if (!loaded) {
|
|
492
|
+
treeRef.current?.close(id);
|
|
493
|
+
}
|
|
494
|
+
const remainsOpen =
|
|
495
|
+
loaded && (treeRef.current?.isOpen(id) ?? false);
|
|
496
|
+
setOpenState((previous) => ({
|
|
497
|
+
...previous,
|
|
498
|
+
[id]: remainsOpen,
|
|
499
|
+
}));
|
|
500
|
+
}}
|
|
501
|
+
padding={15}
|
|
502
|
+
rowHeight={30}
|
|
503
|
+
indent={INDENT_STEP}
|
|
504
|
+
overscanCount={8}
|
|
505
|
+
// Disable multi-selection
|
|
506
|
+
disableMultiSelection={true}
|
|
507
|
+
>
|
|
508
|
+
{Node}
|
|
509
|
+
</Tree>
|
|
510
|
+
</RequestingTreeContext>
|
|
511
|
+
{!visibleData.length && (
|
|
512
|
+
<p
|
|
513
|
+
role="status"
|
|
514
|
+
className="absolute top-2 left-3 text-sm text-muted-foreground"
|
|
515
|
+
>
|
|
516
|
+
No visible files.
|
|
517
|
+
</p>
|
|
518
|
+
)}
|
|
519
|
+
</div>
|
|
520
|
+
</div>
|
|
337
521
|
</>
|
|
338
522
|
);
|
|
339
523
|
};
|
|
@@ -368,7 +552,7 @@ const Toolbar = ({
|
|
|
368
552
|
const uploadLabel = `Upload files to ${uploadDestinationLabel}`;
|
|
369
553
|
|
|
370
554
|
return (
|
|
371
|
-
<div className="flex items-center justify-end px-2 shrink-0
|
|
555
|
+
<div className="flex items-center justify-end px-2 shrink-0">
|
|
372
556
|
<Tooltip content="Add notebook">
|
|
373
557
|
<Button
|
|
374
558
|
data-testid="file-explorer-add-notebook-button"
|
|
@@ -445,16 +629,7 @@ const Show = ({
|
|
|
445
629
|
) => void;
|
|
446
630
|
}) => {
|
|
447
631
|
const label = (
|
|
448
|
-
<span
|
|
449
|
-
className="flex-1 overflow-hidden text-ellipsis"
|
|
450
|
-
onClick={(e) => {
|
|
451
|
-
if (node.data.isDirectory) {
|
|
452
|
-
return;
|
|
453
|
-
}
|
|
454
|
-
e.stopPropagation();
|
|
455
|
-
node.select();
|
|
456
|
-
}}
|
|
457
|
-
>
|
|
632
|
+
<span className="flex-1 overflow-hidden text-ellipsis">
|
|
458
633
|
{node.data.name}
|
|
459
634
|
{node.data.isMarimoFile && node.data.isPrimaryRoot && !isWasm() && (
|
|
460
635
|
<span
|
|
@@ -484,7 +659,7 @@ const Node = ({ node, style, dragHandle }: NodeRendererProps<FileTreeNode>) => {
|
|
|
484
659
|
: guessFileIconType(node.data.name);
|
|
485
660
|
|
|
486
661
|
const Icon = FILE_ICON[fileType];
|
|
487
|
-
const {
|
|
662
|
+
const { openPrompt } = useImperativeModal();
|
|
488
663
|
const { createNewCell } = useCellActions();
|
|
489
664
|
const lastFocusedCellId = useLastFocusedCellId();
|
|
490
665
|
|
|
@@ -501,6 +676,7 @@ const Node = ({ node, style, dragHandle }: NodeRendererProps<FileTreeNode>) => {
|
|
|
501
676
|
const isExternalDropTarget =
|
|
502
677
|
node.data.isDirectory &&
|
|
503
678
|
fileExplorer?.externalDropDestinationPath === node.data.path;
|
|
679
|
+
useHoverExpand(node, isExternalDropTarget);
|
|
504
680
|
|
|
505
681
|
const handleOpenMarimoFile = async (
|
|
506
682
|
evt: Pick<Event, "stopPropagation" | "preventDefault">,
|
|
@@ -514,20 +690,7 @@ const Node = ({ node, style, dragHandle }: NodeRendererProps<FileTreeNode>) => {
|
|
|
514
690
|
const handleDeleteFile = async (evt: Event) => {
|
|
515
691
|
evt.stopPropagation();
|
|
516
692
|
evt.preventDefault();
|
|
517
|
-
|
|
518
|
-
title: "Delete file",
|
|
519
|
-
description: `Are you sure you want to delete ${node.data.name}?`,
|
|
520
|
-
confirmAction: (
|
|
521
|
-
<AlertDialogDestructiveAction
|
|
522
|
-
onClick={async () => {
|
|
523
|
-
await node.tree.delete(node.id);
|
|
524
|
-
}}
|
|
525
|
-
aria-label="Confirm"
|
|
526
|
-
>
|
|
527
|
-
Delete
|
|
528
|
-
</AlertDialogDestructiveAction>
|
|
529
|
-
),
|
|
530
|
-
});
|
|
693
|
+
await node.tree.delete(node.id);
|
|
531
694
|
};
|
|
532
695
|
|
|
533
696
|
const handleCreateFolder = useEvent(async () => {
|
|
@@ -576,40 +739,53 @@ const Node = ({ node, style, dragHandle }: NodeRendererProps<FileTreeNode>) => {
|
|
|
576
739
|
? { [FILE_EXPLORER_DIRECTORY_PATH_ATTRIBUTE]: node.data.path }
|
|
577
740
|
: {})}
|
|
578
741
|
className={cn(
|
|
579
|
-
"flex items-center cursor-pointer ml-1 text-muted-foreground whitespace-nowrap group",
|
|
742
|
+
"relative flex h-full items-center cursor-pointer ml-1 text-muted-foreground whitespace-nowrap group",
|
|
580
743
|
)}
|
|
581
744
|
draggable={!node.data.isRoot}
|
|
582
|
-
onClick={(
|
|
583
|
-
evt.stopPropagation();
|
|
745
|
+
onClick={() => {
|
|
584
746
|
if (node.data.isDirectory) {
|
|
585
|
-
node.select();
|
|
586
747
|
node.toggle();
|
|
587
748
|
}
|
|
588
749
|
}}
|
|
750
|
+
onDoubleClick={(event) => {
|
|
751
|
+
event.stopPropagation();
|
|
752
|
+
if (!node.data.isDirectory) {
|
|
753
|
+
node.activate();
|
|
754
|
+
}
|
|
755
|
+
}}
|
|
589
756
|
>
|
|
757
|
+
{Array.from({ length: node.level }, (_, level) => (
|
|
758
|
+
<span
|
|
759
|
+
key={level}
|
|
760
|
+
aria-hidden="true"
|
|
761
|
+
className="pointer-events-none absolute inset-y-0 border-l border-border"
|
|
762
|
+
style={{ left: level * INDENT_STEP + 8 }}
|
|
763
|
+
/>
|
|
764
|
+
))}
|
|
590
765
|
<FolderArrow node={node} />
|
|
591
766
|
<span
|
|
592
767
|
className={cn(
|
|
593
768
|
"flex items-center pl-1 py-1 cursor-pointer hover:bg-accent/50 hover:text-accent-foreground rounded-l flex-1 overflow-hidden group",
|
|
594
769
|
node.willReceiveDrop &&
|
|
770
|
+
node.tree.canDrop() &&
|
|
595
771
|
node.data.isDirectory &&
|
|
596
|
-
"bg-
|
|
772
|
+
"bg-primary/15 hover:bg-primary/15 text-accent-foreground",
|
|
597
773
|
node.isSelected &&
|
|
598
774
|
"bg-accent/60 hover:bg-accent/60 text-accent-foreground",
|
|
599
775
|
isExternalDropTarget &&
|
|
600
|
-
"bg-primary/15 hover:bg-primary/15 text-accent-foreground
|
|
776
|
+
"bg-primary/15 hover:bg-primary/15 text-accent-foreground",
|
|
601
777
|
)}
|
|
602
778
|
>
|
|
603
779
|
{node.data.isRoot ? (
|
|
604
780
|
<FolderRootIcon
|
|
605
|
-
className="w-
|
|
781
|
+
className="w-4 h-4 shrink-0 mr-2 text-primary"
|
|
606
782
|
strokeWidth={1.5}
|
|
607
783
|
/>
|
|
608
784
|
) : node.data.isMarimoFile ? (
|
|
609
|
-
<MarimoIcon className="w-
|
|
785
|
+
<MarimoIcon className="w-4 h-4 shrink-0 mr-2" strokeWidth={1.5} />
|
|
610
786
|
) : (
|
|
611
787
|
<Icon
|
|
612
|
-
className={cn("w-
|
|
788
|
+
className={cn("w-4 h-4 shrink-0 mr-2", FILE_ICON_COLOR[fileType])}
|
|
613
789
|
strokeWidth={1.5}
|
|
614
790
|
/>
|
|
615
791
|
)}
|
|
@@ -624,7 +800,7 @@ const Node = ({ node, style, dragHandle }: NodeRendererProps<FileTreeNode>) => {
|
|
|
624
800
|
>
|
|
625
801
|
{!node.data.isDirectory && (
|
|
626
802
|
<DropdownMenuItem
|
|
627
|
-
onSelect={() => node.
|
|
803
|
+
onSelect={() => node.activate()}
|
|
628
804
|
data-testid="file-explorer-open-file-menu-item"
|
|
629
805
|
>
|
|
630
806
|
<ViewIcon className={MENU_ITEM_ICON_CLASS} />
|
|
@@ -794,6 +970,18 @@ const FolderArrow = ({ node }: { node: NodeApi<FileTreeNode> }) => {
|
|
|
794
970
|
return <span className="w-4 h-4 shrink-0" />;
|
|
795
971
|
}
|
|
796
972
|
|
|
973
|
+
if (node.data.loadState === "loading") {
|
|
974
|
+
return (
|
|
975
|
+
<span
|
|
976
|
+
role="status"
|
|
977
|
+
aria-label={`Loading ${node.data.name}`}
|
|
978
|
+
className="w-4 h-4 shrink-0"
|
|
979
|
+
>
|
|
980
|
+
<Spinner size="small" />
|
|
981
|
+
</span>
|
|
982
|
+
);
|
|
983
|
+
}
|
|
984
|
+
|
|
797
985
|
return <TreeChevron isExpanded={node.isOpen} className="w-4 h-4" />;
|
|
798
986
|
};
|
|
799
987
|
|
|
@@ -852,3 +1040,33 @@ function treeContainsId(list: FileTreeNode[], id: string): boolean {
|
|
|
852
1040
|
(item.children ? treeContainsId(item.children, id) : false),
|
|
853
1041
|
);
|
|
854
1042
|
}
|
|
1043
|
+
|
|
1044
|
+
function FileDragPreview({
|
|
1045
|
+
mouse,
|
|
1046
|
+
id,
|
|
1047
|
+
isDragging,
|
|
1048
|
+
tree,
|
|
1049
|
+
}: DragPreviewProps & { tree: TreeApi<FileTreeNode> | null }) {
|
|
1050
|
+
const node = tree?.get(id);
|
|
1051
|
+
if (!isDragging || !mouse || !node) {
|
|
1052
|
+
return null;
|
|
1053
|
+
}
|
|
1054
|
+
const fileType = node.data.isDirectory
|
|
1055
|
+
? "directory"
|
|
1056
|
+
: guessFileIconType(node.data.name);
|
|
1057
|
+
const Icon = FILE_ICON[fileType];
|
|
1058
|
+
return createPortal(
|
|
1059
|
+
<div
|
|
1060
|
+
className="pointer-events-none fixed left-0 top-0 z-[1000] opacity-[0.85]"
|
|
1061
|
+
style={{
|
|
1062
|
+
transform: `translate3d(${mouse.x + 12}px, ${mouse.y + 12}px, 0)`,
|
|
1063
|
+
}}
|
|
1064
|
+
>
|
|
1065
|
+
<div className="flex max-w-72 items-center gap-2 rounded-md border border-border bg-popover px-3 py-2 text-sm text-popover-foreground shadow-md">
|
|
1066
|
+
<Icon className={cn("h-4 w-4 shrink-0", FILE_ICON_COLOR[fileType])} />
|
|
1067
|
+
<span className="truncate">{node.data.name}</span>
|
|
1068
|
+
</div>
|
|
1069
|
+
</div>,
|
|
1070
|
+
document.body,
|
|
1071
|
+
);
|
|
1072
|
+
}
|