@hienlh/ppm 0.17.57 → 0.18.1
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/.release-run.sh +5 -0
- package/CHANGELOG.md +24 -0
- package/CLAUDE.md +4 -0
- package/assets/skills/ppm/SKILL.md +1 -1
- package/assets/skills/ppm/references/http-api.md +16 -2
- package/bun.lock +3 -0
- package/dist/web/assets/{ai-resource-editor-CXWQcx3_.js → ai-resource-editor-Bmk4P0YS.js} +1 -1
- package/dist/web/assets/{angular-html-D4YJUMLP.js → angular-html-C-Kjr43S.js} +1 -1
- package/dist/web/assets/{angular-ts-CUvq2h3X.js → angular-ts-CaDDbZqm.js} +1 -1
- package/dist/web/assets/api-client-CaRE4HYh.js +1 -0
- package/dist/web/assets/{api-client-COQtP-dB.js → api-client-rTAGdEKo.js} +1 -1
- package/dist/web/assets/{api-settings-Do0Z0IRN.js → api-settings-CAhcNpj5.js} +1 -1
- package/dist/web/assets/api-settings-DBcXfeJ8.js +1 -0
- package/dist/web/assets/{api-themes-DXi8sz-9.js → api-themes-CAjg_Bt9.js} +1 -1
- package/dist/web/assets/{apl-DSANFLx0.js → apl-B1WsQFAb.js} +1 -1
- package/dist/web/assets/architecture-PBZL5I3N-B4J2-O8Q.js +1 -0
- package/dist/web/assets/{astro-CXIotiNS.js → astro-Bdnz7nVp.js} +1 -1
- package/dist/web/assets/{audio-preview-D7jNc8mi.js → audio-preview-CikvnAOI.js} +1 -1
- package/dist/web/assets/{blade-BvrP2gPd.js → blade-B2euh8sS.js} +1 -1
- package/dist/web/assets/{bot-E9mcVZnr.js → bot-nk1k3ELu.js} +1 -1
- package/dist/web/assets/{bsl-CGf6Xuyu.js → bsl-GNeMaxd0.js} +1 -1
- package/dist/web/assets/{cairo-BboZ9qIf.js → cairo-D7G0W7-s.js} +1 -1
- package/dist/web/assets/can-open-in-ppm-BzVXoV-d.js +1 -0
- package/dist/web/assets/chat-tab-B3-KcCn2.js +13 -0
- package/dist/web/assets/chevron-right-BLOtX8Vy.js +1 -0
- package/dist/web/assets/{circle-question-mark-PaJgx-3d.js → circle-question-mark-CENpx4oA.js} +1 -1
- package/dist/web/assets/clock-D493FU_D.js +1 -0
- package/dist/web/assets/{cobol-D39V6ukp.js → cobol-Q6YgysnI.js} +1 -1
- package/dist/web/assets/code-CLF5x7KK.js +1 -0
- package/dist/web/assets/code-editor-BpbZw13T.js +10 -0
- package/dist/web/assets/{coffee-DTtK7kKi.js → coffee-BjufWxVc.js} +1 -1
- package/dist/web/assets/{conflict-editor-CpGcSopK.js → conflict-editor-DC3liBm_.js} +3 -3
- package/dist/web/assets/context-menu-q-RHHAWz.js +1 -0
- package/dist/web/assets/copy-BApI9v7Z.js +1 -0
- package/dist/web/assets/{cpp-CnU9hfBq.js → cpp-CqWU0gCR.js} +1 -1
- package/dist/web/assets/{createLucideIcon-ConhwJnb.js → createLucideIcon-D8pyde1A.js} +1 -1
- package/dist/web/assets/{crystal-DMjCL--0.js → crystal-ylayVajF.js} +1 -1
- package/dist/web/assets/csv-preview-BhCOjgq1.js +2 -0
- package/dist/web/assets/{data-grid-overlay-editor-Bdc80TJ1.js → data-grid-overlay-editor-BzPfsCfd.js} +1 -1
- package/dist/web/assets/{data-grid-types-BuMHLKXV.js → data-grid-types-CUx8cusN.js} +1 -1
- package/dist/web/assets/{database-COYAMZaw.js → database-B9okYLx6.js} +1 -1
- package/dist/web/assets/{database-viewer-DTMmic31.js → database-viewer-Z6uLyZVO.js} +1 -1
- package/dist/web/assets/dialog-CkI7rwrh.js +1 -0
- package/dist/web/assets/diff-viewer-CT1ALWt2.js +4 -0
- package/dist/web/assets/{dist-m_oJqIDk.js → dist-DduBIVkD.js} +1 -1
- package/dist/web/assets/{docx-preview-CR2ZtvNP.js → docx-preview-C_fSjfy_.js} +1 -1
- package/dist/web/assets/{edge-CTom35R_.js → edge-DX4NxQfh.js} +1 -1
- package/dist/web/assets/{edit-diff-preview-D1TEK7Jz.js → edit-diff-preview-DfX-9om9.js} +1 -1
- package/dist/web/assets/{elixir-BAX9HSOu.js → elixir-CSh3NjbV.js} +1 -1
- package/dist/web/assets/{elm-CpD28xsW.js → elm-DBzwFYq9.js} +1 -1
- package/dist/web/assets/{erb-DHX2IIEs.js → erb-yxVOb8aB.js} +1 -1
- package/dist/web/assets/{erlang-B_etLcPG.js → erlang-5XIDkYrM.js} +1 -1
- package/dist/web/assets/{esm-PmlN1RTs.js → esm-DO4el2OJ.js} +1 -1
- package/dist/web/assets/explorer-body-CQCyGEet.js +2 -0
- package/dist/web/assets/explorer-window-content-EtyTAZSh.js +1 -0
- package/dist/web/assets/{extension-webview-BTCFWY2A.js → extension-webview-ePIM2fwM.js} +1 -1
- package/dist/web/assets/{eye-off-Rq1n2-oe.js → eye-off-CUdzgmX8.js} +1 -1
- package/dist/web/assets/{file-exclamation-point-DBEC-Kst.js → file-exclamation-point-COAwgNGw.js} +1 -1
- package/dist/web/assets/{fortran-fixed-form-CpR0cN7O.js → fortran-fixed-form-D4tE5VWY.js} +1 -1
- package/dist/web/assets/{fsharp-DxITxZW5.js → fsharp-By8soj6-.js} +1 -1
- package/dist/web/assets/{gdresource-DJ7IvROZ.js → gdresource-DoSwM7Hv.js} +1 -1
- package/dist/web/assets/{git-commit-sQznNP43.js → git-commit-_ZiciHIx.js} +1 -1
- package/dist/web/assets/git-log-panel-BRWmTzX1.js +1 -0
- package/dist/web/assets/{git-rebase-DkHvejAR.js → git-rebase-C3Y5N9-s.js} +1 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-CtQ3yXN-.js +1 -0
- package/dist/web/assets/glide-data-grid-DCJCz4XB.js +138 -0
- package/dist/web/assets/{glimmer-js-BwJnDfL-.js → glimmer-js-BPBTXVlC.js} +1 -1
- package/dist/web/assets/{glimmer-ts-Coz155XK.js → glimmer-ts-BQSZdy_D.js} +1 -1
- package/dist/web/assets/{glsl-BED7ZmUZ.js → glsl-CbQdxpU-.js} +1 -1
- package/dist/web/assets/{graphql-B_KyE6oz.js → graphql-GMlKikpY.js} +1 -1
- package/dist/web/assets/group-chat-tab-C3UD2bcm.js +1 -0
- package/dist/web/assets/{hack-sxywdIAQ.js → hack-DyshcLec.js} +1 -1
- package/dist/web/assets/{haml-Dts9JlPU.js → haml-rqCqWzsq.js} +1 -1
- package/dist/web/assets/{handlebars-C9HXIMtj.js → handlebars-BCKwSWjR.js} +1 -1
- package/dist/web/assets/{html-2p0RvXi4.js → html-Cg29xPR1.js} +1 -1
- package/dist/web/assets/{html-derivative-BiTPuOba.js → html-derivative-geltIVg8.js} +1 -1
- package/dist/web/assets/{http-CHd2gYPf.js → http-pG7Vp237.js} +1 -1
- package/dist/web/assets/{hurl-boMPgDm9.js → hurl-Di3Uu_Mc.js} +1 -1
- package/dist/web/assets/{hxml-BQvjiuBt.js → hxml-CRy4lbv8.js} +1 -1
- package/dist/web/assets/{image-preview-0SGb7_tH.js → image-preview-CP82lor8.js} +1 -1
- package/dist/web/assets/index-BYetJoXQ.js +52 -0
- package/dist/web/assets/index-D-xwjNyX.css +2 -0
- package/dist/web/assets/info-3K5VOQVL-Cg0mIJn9.js +1 -0
- package/dist/web/assets/{jinja-B-zKiwlp.js → jinja-C8GyVnvd.js} +1 -1
- package/dist/web/assets/{jison-C2vJYjbJ.js → jison-BywnqPn6.js} +1 -1
- package/dist/web/assets/{julia-DJ8MrQPA.js → julia-DTrO3T3B.js} +1 -1
- package/dist/web/assets/{just-BvQHq7VE.js → just-CA3KSHPi.js} +1 -1
- package/dist/web/assets/keybindings-store-yBWU8ZYT.js +1 -0
- package/dist/web/assets/{latex-ChNeshUC.js → latex-BC5xUMpc.js} +1 -1
- package/dist/web/assets/{lib-DTaN9riD.js → lib-BFetOULu.js} +1 -1
- package/dist/web/assets/{liquid-KNJEBOwW.js → liquid-Chd8tSyQ.js} +1 -1
- package/dist/web/assets/{lua-D8NNsLbC.js → lua-DHaEtwLv.js} +1 -1
- package/dist/web/assets/markdown-renderer-B2D4gdok.js +1 -0
- package/dist/web/assets/markdown-renderer-DDgl5zDc.js +3 -0
- package/dist/web/assets/{marko-HGo5Qtz4.js → marko-Desx_bC-.js} +1 -1
- package/dist/web/assets/{mdc-omkodQB5.js → mdc-DahWHc1t.js} +1 -1
- package/dist/web/assets/mobile-bottom-sheet-BQt45q9T.js +1 -0
- package/dist/web/assets/mobile-explorer-sheet-BtxyynR3.js +1 -0
- package/dist/web/assets/{nextflow-Adq02pv-.js → nextflow-3A7lutCA.js} +1 -1
- package/dist/web/assets/{nginx-ByvWruGD.js → nginx-XQda6Hdy.js} +1 -1
- package/dist/web/assets/{nim-BrMojX4O.js → nim-DHqlpwP1.js} +1 -1
- package/dist/web/assets/notification-store-DKw_mGED.js +1 -0
- package/dist/web/assets/{number-overlay-editor-CunvWJnN.js → number-overlay-editor-DgZtViPO.js} +1 -1
- package/dist/web/assets/packet-RMMSAZCW-D79vAFR_.js +1 -0
- package/dist/web/assets/{panel-store-CpKvXNnm.js → panel-store-s6Qx49RK.js} +1 -1
- package/dist/web/assets/{pdf-preview-DEn8amrO.js → pdf-preview-BXDOe7bG.js} +1 -1
- package/dist/web/assets/{perl-Bd_0l-b4.js → perl-M-ncjACb.js} +1 -1
- package/dist/web/assets/{php-D-DLTnWJ.js → php-CKIajrZi.js} +1 -1
- package/dist/web/assets/pie-UPGHQEXC-DYUOH68U.js +1 -0
- package/dist/web/assets/{plus-DcuJuDC3.js → plus-9LatwJMx.js} +1 -1
- package/dist/web/assets/{postgres-viewer-CjrM15gz.js → postgres-viewer-DkHYV33h.js} +3 -3
- package/dist/web/assets/{project-store-D0ALup9Z.js → project-store-DyaEpHVG.js} +1 -1
- package/dist/web/assets/{pug-D2aO-l2T.js → pug-DO0u_gO4.js} +1 -1
- package/dist/web/assets/{qml-CyQVzNXS.js → qml-UEje1PAA.js} +1 -1
- package/dist/web/assets/radar-KQ55EAFF-B1eznNtd.js +1 -0
- package/dist/web/assets/{razor-8tvJXsO3.js → razor-lCs5T1Ib.js} +1 -1
- package/dist/web/assets/{react-8hrD3-kv.js → react-w6wINGnd.js} +1 -1
- package/dist/web/assets/{rst-C4AZyQQl.js → rst-CZX_O0z4.js} +1 -1
- package/dist/web/assets/{ruby-O_z2NTLY.js → ruby-idycVAyE.js} +1 -1
- package/dist/web/assets/{sas-BtnF4zaV.js → sas-o5gTfUwp.js} +1 -1
- package/dist/web/assets/{save-BM4p9_9n.js → save-C4z1p51e.js} +1 -1
- package/dist/web/assets/{scss-GAyp5euH.js → scss-CRTk3iBQ.js} +1 -1
- package/dist/web/assets/settings-store-ChqHdT2V.js +2 -0
- package/dist/web/assets/settings-tab-ESL-6MAA.js +1 -0
- package/dist/web/assets/{shaderlab-nM5YZwa5.js → shaderlab-3e5ln8ci.js} +1 -1
- package/dist/web/assets/{shellsession-D2OCI4Gv.js → shellsession-CGQjQ75I.js} +1 -1
- package/dist/web/assets/{shield-check-C3qYSU8a.js → shield-check-C5laObJv.js} +1 -1
- package/dist/web/assets/{shield-off-D5tU7gQT.js → shield-off-DULFtyLS.js} +1 -1
- package/dist/web/assets/{soy-DrLNhqfp.js → soy-CxY0VwX2.js} +1 -1
- package/dist/web/assets/sparkles-n1hzh65o.js +1 -0
- package/dist/web/assets/{sparql-CsM6F2Yv.js → sparql-CBAGAFuq.js} +1 -1
- package/dist/web/assets/{sql-query-editor-CT5M9PKQ.js → sql-query-editor-CZdZuawP.js} +1 -1
- package/dist/web/assets/sqlite-viewer-Cpq06-nz.js +1 -0
- package/dist/web/assets/{stata-Bk3cgfXJ.js → stata-BoezhOvk.js} +1 -1
- package/dist/web/assets/{surrealql-Dkz0DPK8.js → surrealql-CHEzAhu7.js} +1 -1
- package/dist/web/assets/{svelte-23si1KV5.js → svelte-y6xtu2zX.js} +1 -1
- package/dist/web/assets/{system-monitor-tab-De3oz9zD.js → system-monitor-tab-XR8NXHpv.js} +1 -1
- package/dist/web/assets/{tab-store-BQmDVh9c.js → tab-store-C42sktPo.js} +1 -1
- package/dist/web/assets/{table-Di8pv2Y_.js → table-CHmxuh6M.js} +1 -1
- package/dist/web/assets/{templ-CPxlP4vF.js → templ-DbG2y5yD.js} +1 -1
- package/dist/web/assets/{terminal-tab-CciNqtMc.js → terminal-tab-CCCB-_50.js} +3 -3
- package/dist/web/assets/{tex-ugJpgYPc.js → tex-CeKQhrQG.js} +1 -1
- package/dist/web/assets/tool-cards-CsTs5go2.js +7 -0
- package/dist/web/assets/treemap-KZPCXAKY-SeI2TxLs.js +1 -0
- package/dist/web/assets/{ts-tags-BovP9TMu.js → ts-tags-iKKOXJjG.js} +1 -1
- package/dist/web/assets/{twig-B7VUm2Ca.js → twig-DK8Vrp-G.js} +1 -1
- package/dist/web/assets/{use-blob-url-B3YovXF6.js → use-blob-url-DYtOHK7u.js} +1 -1
- package/dist/web/assets/{use-is-mobile-BcuCjH19.js → use-is-mobile-DnFSkUG2.js} +1 -1
- package/dist/web/assets/use-monaco-theme-CZh7xTJZ.js +1 -0
- package/dist/web/assets/vendor-markdown-Ls5AcU91.js +291 -0
- package/dist/web/assets/{vendor-mermaid-CYOBtZtD.js → vendor-mermaid-C977xNeE.js} +1 -1
- package/dist/web/assets/{vendor-ui-DMRGWRuc.js → vendor-ui-CaeeFO0B.js} +2 -2
- package/dist/web/assets/{video-preview-D-S_YnG6.js → video-preview-KbRcyuAA.js} +1 -1
- package/dist/web/assets/{vue-m75pWowk.js → vue-JkIEObMV.js} +1 -1
- package/dist/web/assets/{vue-html-odHs7IMn.js → vue-html-D8SqnU86.js} +1 -1
- package/dist/web/assets/{vue-vine-BbBB3V_G.js → vue-vine-DJj7N4WD.js} +1 -1
- package/dist/web/assets/x-B1qHmLdR.js +1 -0
- package/dist/web/assets/{xml-BHzIcDWI.js → xml-C-00w9Rv.js} +1 -1
- package/dist/web/assets/{xsl-DTLhN7uu.js → xsl-CVdCwPHV.js} +1 -1
- package/dist/web/index.html +29 -26
- package/dist/web/monacoeditorwork/css.worker.bundle.js +122 -122
- package/dist/web/monacoeditorwork/editor.worker.bundle.js +78 -78
- package/dist/web/monacoeditorwork/html.worker.bundle.js +110 -110
- package/dist/web/monacoeditorwork/json.worker.bundle.js +108 -108
- package/dist/web/monacoeditorwork/ts.worker.bundle.js +81 -81
- package/dist/web/sw.js +1 -1
- package/docs/codebase-summary.md +10 -1
- package/docs/design-guidelines.md +31 -0
- package/docs/project-roadmap.md +10 -0
- package/docs/system-architecture.md +59 -0
- package/package.json +2 -1
- package/src/server/index.ts +8 -0
- package/src/server/routes/files.ts +2 -2
- package/src/server/routes/fs-browse.ts +99 -74
- package/src/server/routes/fs-ops.ts +118 -0
- package/src/server/routes/fs-sqlite.ts +129 -0
- package/src/server/routes/host-info.ts +17 -0
- package/src/server/routes/settings.ts +2 -0
- package/src/services/download-token.service.ts +21 -5
- package/src/services/file.service.ts +8 -12
- package/src/services/fs-browse.service.ts +104 -161
- package/src/services/fs-ops/fs-content-disposition.ts +17 -0
- package/src/services/fs-ops/fs-core-ops.ts +149 -0
- package/src/services/fs-ops/fs-error-response.ts +24 -0
- package/src/services/fs-ops/fs-git-init.service.ts +23 -0
- package/src/services/fs-ops/fs-hidden-names.ts +17 -0
- package/src/services/fs-ops/fs-list-files.service.ts +68 -0
- package/src/services/fs-ops/fs-ops-copy-move.service.ts +60 -0
- package/src/services/fs-ops/fs-ops-mutate.service.ts +59 -0
- package/src/services/fs-ops/fs-ops-read-write.service.ts +70 -0
- package/src/services/fs-ops/fs-ops-stat.service.ts +109 -0
- package/src/services/fs-ops/fs-ops-trash.service.ts +111 -0
- package/src/services/fs-ops/fs-real-path.ts +44 -0
- package/src/services/fs-ops/sql-row-reader.ts +26 -0
- package/src/services/fs-ops/sql-statement-guard.ts +63 -0
- package/src/services/fs-path-guard.service.ts +196 -0
- package/src/services/host-info/apple-bookmark-decoder.ts +97 -0
- package/src/services/host-info/apple-plist-xml-parser.ts +107 -0
- package/src/services/host-info/concurrency-limit.ts +20 -0
- package/src/services/host-info/host-drives.ts +190 -0
- package/src/services/host-info/host-info.service.ts +171 -0
- package/src/services/host-info/host-known-folders.ts +180 -0
- package/src/services/host-info/pinned-linux-bookmarks.ts +89 -0
- package/src/services/host-info/pinned-macos-finder-favorites.ts +175 -0
- package/src/services/host-info/pinned-windows-quick-access.ts +87 -0
- package/src/services/host-info/spawn-runner.ts +41 -0
- package/src/services/sqlite-types.ts +26 -0
- package/src/services/sqlite.service.ts +38 -29
- package/src/services/subagent-transcript-merger.ts +1 -1
- package/src/types/system.ts +50 -0
- package/src/web/app.tsx +19 -3
- package/src/web/components/editor/code-editor.tsx +3 -9
- package/src/web/components/explorer/file-tree.tsx +103 -21
- package/src/web/components/explorer/tree-node-context-menu.tsx +6 -1
- package/src/web/components/explorer/tree-node.tsx +34 -93
- package/src/web/components/explorer/use-file-upload-drag.ts +14 -9
- package/src/web/components/explorer/use-tree-row-dnd.ts +206 -0
- package/src/web/components/floating-window/default-window-chrome.tsx +71 -0
- package/src/web/components/floating-window/floating-window.tsx +167 -0
- package/src/web/components/floating-window/use-window-drag.ts +28 -0
- package/src/web/components/floating-window/use-window-gesture-context.ts +44 -0
- package/src/web/components/floating-window/use-window-keyboard.ts +55 -0
- package/src/web/components/floating-window/use-window-resize.ts +30 -0
- package/src/web/components/floating-window/window-chrome-contract.ts +38 -0
- package/src/web/components/floating-window/window-content-registry.ts +51 -0
- package/src/web/components/floating-window/window-geometry.ts +121 -0
- package/src/web/components/floating-window/window-layer.tsx +92 -0
- package/src/web/components/floating-window/window-persistence.ts +104 -0
- package/src/web/components/floating-window/window-resize-handles.tsx +81 -0
- package/src/web/components/floating-window/window-store-types.ts +20 -0
- package/src/web/components/floating-window/window-store.ts +192 -0
- package/src/web/components/group-chat/group-list.tsx +2 -3
- package/src/web/components/layout/command-palette.tsx +10 -0
- package/src/web/components/layout/mobile-drawer-tab-bar.tsx +3 -10
- package/src/web/components/layout/mobile-drawer.tsx +13 -2
- package/src/web/components/layout/nav-section-rail.tsx +16 -11
- package/src/web/components/os-explorer/actions/explorer-actions-clipboard.ts +186 -0
- package/src/web/components/os-explorer/actions/explorer-actions-mutate.ts +109 -0
- package/src/web/components/os-explorer/actions/explorer-actions-open.ts +67 -0
- package/src/web/components/os-explorer/actions/explorer-actions-transfer-helpers.ts +35 -0
- package/src/web/components/os-explorer/actions/use-explorer-actions.ts +205 -0
- package/src/web/components/os-explorer/can-open-in-ppm.ts +89 -0
- package/src/web/components/os-explorer/dnd/drag-depth-counter.ts +36 -0
- package/src/web/components/os-explorer/dnd/drag-ghost.ts +44 -0
- package/src/web/components/os-explorer/dnd/drop-target-decision.ts +46 -0
- package/src/web/components/os-explorer/dnd/drop-target-style.ts +7 -0
- package/src/web/components/os-explorer/dnd/entry-drag-payload.ts +115 -0
- package/src/web/components/os-explorer/dnd/entry-drag-state.ts +35 -0
- package/src/web/components/os-explorer/dnd/entry-drop-executor.ts +43 -0
- package/src/web/components/os-explorer/dnd/use-drag-auto-scroll.ts +70 -0
- package/src/web/components/os-explorer/dnd/use-drop-transfer.tsx +82 -0
- package/src/web/components/os-explorer/dnd/use-entry-drag-source.ts +73 -0
- package/src/web/components/os-explorer/dnd/use-entry-drop-target.ts +150 -0
- package/src/web/components/os-explorer/dnd/use-entry-row-dnd.ts +66 -0
- package/src/web/components/os-explorer/dnd/use-path-drop-target.ts +30 -0
- package/src/web/components/os-explorer/explorer-body.tsx +153 -0
- package/src/web/components/os-explorer/explorer-breadcrumb.tsx +108 -0
- package/src/web/components/os-explorer/explorer-context-menu.tsx +131 -0
- package/src/web/components/os-explorer/explorer-dialogs.tsx +103 -0
- package/src/web/components/os-explorer/explorer-modal-shell.tsx +60 -0
- package/src/web/components/os-explorer/explorer-pins-store.ts +63 -0
- package/src/web/components/os-explorer/explorer-sidebar.tsx +198 -0
- package/src/web/components/os-explorer/explorer-sort-menu.tsx +68 -0
- package/src/web/components/os-explorer/explorer-status-bar.tsx +35 -0
- package/src/web/components/os-explorer/explorer-store.ts +179 -0
- package/src/web/components/os-explorer/explorer-toolbar.tsx +139 -0
- package/src/web/components/os-explorer/explorer-window-content.tsx +36 -0
- package/src/web/components/os-explorer/format-file-meta.ts +74 -0
- package/src/web/components/os-explorer/icons/file-type-icon-map.ts +106 -0
- package/src/web/components/os-explorer/icons/file-type-icon.tsx +56 -0
- package/src/web/components/os-explorer/icons/thumbnail-fetch-semaphore.ts +44 -0
- package/src/web/components/os-explorer/icons/thumbnail-image.tsx +124 -0
- package/src/web/components/os-explorer/mobile/mobile-bottom-toolbar.tsx +102 -0
- package/src/web/components/os-explorer/mobile/mobile-explorer-sheet.tsx +61 -0
- package/src/web/components/os-explorer/mobile/mobile-explorer-top-bar.tsx +41 -0
- package/src/web/components/os-explorer/mobile/mobile-more-sheet.tsx +117 -0
- package/src/web/components/os-explorer/mobile/mobile-places-sheet.tsx +120 -0
- package/src/web/components/os-explorer/mobile/mobile-places-strip.tsx +109 -0
- package/src/web/components/os-explorer/mobile/mobile-tap-action.ts +19 -0
- package/src/web/components/os-explorer/mobile/use-mobile-row-tap.ts +50 -0
- package/src/web/components/os-explorer/open-explorer.ts +35 -0
- package/src/web/components/os-explorer/properties-dialog.tsx +99 -0
- package/src/web/components/os-explorer/skins/explorer-skin-chrome.tsx +17 -0
- package/src/web/components/os-explorer/skins/folder-icon-macos.tsx +42 -0
- package/src/web/components/os-explorer/skins/folder-icon-windows.tsx +25 -0
- package/src/web/components/os-explorer/skins/macos-skin.ts +14 -0
- package/src/web/components/os-explorer/skins/macos-window-chrome.tsx +69 -0
- package/src/web/components/os-explorer/skins/skin-types.ts +40 -0
- package/src/web/components/os-explorer/skins/skins.css +25 -0
- package/src/web/components/os-explorer/skins/use-explorer-skin.ts +21 -0
- package/src/web/components/os-explorer/skins/windows-skin.ts +11 -0
- package/src/web/components/os-explorer/skins/windows-window-chrome.tsx +56 -0
- package/src/web/components/os-explorer/sort-and-filter-entries.ts +52 -0
- package/src/web/components/os-explorer/sort-options.ts +29 -0
- package/src/web/components/os-explorer/toolbar-icon-button.ts +18 -0
- package/src/web/components/os-explorer/use-coarse-long-press.ts +97 -0
- package/src/web/components/os-explorer/use-explorer-keyboard.ts +124 -0
- package/src/web/components/os-explorer/use-explorer-navigation.ts +155 -0
- package/src/web/components/os-explorer/use-explorer-open-state.ts +67 -0
- package/src/web/components/os-explorer/use-host-info.ts +85 -0
- package/src/web/components/os-explorer/views/column-row.tsx +126 -0
- package/src/web/components/os-explorer/views/column-view-column.tsx +112 -0
- package/src/web/components/os-explorer/views/column-view-mobile.tsx +98 -0
- package/src/web/components/os-explorer/views/column-view-preview.tsx +70 -0
- package/src/web/components/os-explorer/views/column-view.tsx +204 -0
- package/src/web/components/os-explorer/views/compose-interactive-row-props.ts +77 -0
- package/src/web/components/os-explorer/views/explorer-view-registry.tsx +55 -0
- package/src/web/components/os-explorer/views/icons-view-tile.tsx +158 -0
- package/src/web/components/os-explorer/views/icons-view.tsx +188 -0
- package/src/web/components/os-explorer/views/inline-name-input.tsx +60 -0
- package/src/web/components/os-explorer/views/list-row.tsx +162 -0
- package/src/web/components/os-explorer/views/list-view.tsx +177 -0
- package/src/web/components/os-explorer/views/use-column-view-state.ts +158 -0
- package/src/web/components/os-explorer/views/use-entry-selection.ts +178 -0
- package/src/web/components/settings/settings-tab.tsx +29 -2
- package/src/web/components/sqlite/sqlite-viewer.tsx +5 -2
- package/src/web/components/sqlite/use-sqlite.ts +7 -2
- package/src/web/components/ui/adaptive-context-menu.tsx +57 -1
- package/src/web/components/ui/feature-badge.tsx +40 -0
- package/src/web/components/ui/file-browser-picker.tsx +1 -18
- package/src/web/lib/feature-badges.ts +22 -0
- package/src/web/lib/file-download.ts +6 -4
- package/src/web/lib/fs-api.ts +185 -0
- package/src/web/lib/sidebar-tabs/tab-registry.ts +4 -3
- package/src/web/stores/file-store.ts +38 -1
- package/src/web/stores/settings-store.ts +14 -0
- package/src/web/styles/globals.css +1 -0
- package/dist/web/assets/api-client-RtNXvS0P.js +0 -1
- package/dist/web/assets/api-settings-De--HNHp.js +0 -1
- package/dist/web/assets/architecture-PBZL5I3N-DVRYSsQn.js +0 -1
- package/dist/web/assets/chat-tab-C5U7a_rP.js +0 -13
- package/dist/web/assets/chevron-right-DeUue2aK.js +0 -1
- package/dist/web/assets/code-TekEnDFM.js +0 -1
- package/dist/web/assets/code-editor-CqlzLQ7T.js +0 -10
- package/dist/web/assets/context-menu-DIiZdfbE.js +0 -1
- package/dist/web/assets/csv-preview-DD94T9ff.js +0 -2
- package/dist/web/assets/dialog-HHdNInEH.js +0 -1
- package/dist/web/assets/diff-viewer-DyQLYP7J.js +0 -4
- package/dist/web/assets/git-log-panel-CrUgsLY5.js +0 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-CutkUNY_.js +0 -1
- package/dist/web/assets/glide-data-grid-LinTYl0_.js +0 -138
- package/dist/web/assets/group-chat-tab-BqNJW7y2.js +0 -1
- package/dist/web/assets/index-CVJmlKhp.css +0 -2
- package/dist/web/assets/index-I2ik0ZF7.js +0 -50
- package/dist/web/assets/info-3K5VOQVL-BpNjgbj9.js +0 -1
- package/dist/web/assets/keybindings-store-C46tNxJE.js +0 -1
- package/dist/web/assets/markdown-renderer-DSxSupDq.js +0 -3
- package/dist/web/assets/markdown-renderer-xzyhJklS.js +0 -1
- package/dist/web/assets/notification-store-Dek7ClI2.js +0 -1
- package/dist/web/assets/packet-RMMSAZCW-D5IUZ66-.js +0 -1
- package/dist/web/assets/pie-UPGHQEXC-CM29eKDR.js +0 -1
- package/dist/web/assets/radar-KQ55EAFF-QtJGaSGt.js +0 -1
- package/dist/web/assets/settings-store-B3LfpCPa.js +0 -2
- package/dist/web/assets/settings-tab-DIuR1VIc.js +0 -1
- package/dist/web/assets/sparkles-B3DmG8PJ.js +0 -1
- package/dist/web/assets/sqlite-viewer-CEObRQ7n.js +0 -1
- package/dist/web/assets/tool-cards-Bdx04yYD.js +0 -7
- package/dist/web/assets/treemap-KZPCXAKY-CjUqpVnS.js +0 -1
- package/dist/web/assets/use-monaco-theme-k-17kQqf.js +0 -1
- package/dist/web/assets/vendor-markdown-CKTlmXbz.js +0 -291
- package/dist/web/assets/x-BsFLg-T8.js +0 -1
- /package/dist/web/assets/{abap-BM5PMK0-.js → abap-DrMyiWmK.js} +0 -0
- /package/dist/web/assets/{actionscript-3-CML-MTNy.js → actionscript-3-BlSOyi0y.js} +0 -0
- /package/dist/web/assets/{ada-DzO1tMqV.js → ada-B_m96icB.js} +0 -0
- /package/dist/web/assets/{andromeeda-C3nkvRhj.js → andromeeda-Ch3vOiZB.js} +0 -0
- /package/dist/web/assets/{apache-Dg7oWQbs.js → apache-CdsaXc1n.js} +0 -0
- /package/dist/web/assets/{apex-3O2170Ju.js → apex-0AnsBevk.js} +0 -0
- /package/dist/web/assets/{applescript-5AlzBqQO.js → applescript-CSxLolzy.js} +0 -0
- /package/dist/web/assets/{apply-theme-BSJDABU3.js → apply-theme-CMQXnUAa.js} +0 -0
- /package/dist/web/assets/{ara-BGtOX5Hn.js → ara-DXZS0jA5.js} +0 -0
- /package/dist/web/assets/{asciidoc-CIsC4kkq.js → asciidoc-DDy3pH2A.js} +0 -0
- /package/dist/web/assets/{asm-p1-PUkh3.js → asm-Bd17hhqg.js} +0 -0
- /package/dist/web/assets/{aurora-x-BPgGL50S.js → aurora-x-C62HJRs-.js} +0 -0
- /package/dist/web/assets/{awk-Cj93pRL8.js → awk-Cc-nazni.js} +0 -0
- /package/dist/web/assets/{ayu-dark-C50MYKhw.js → ayu-dark-BsfmNWCU.js} +0 -0
- /package/dist/web/assets/{ayu-light-DyIp0Idy.js → ayu-light-BZ4HLa4X.js} +0 -0
- /package/dist/web/assets/{ayu-mirage-BAdm8pKU.js → ayu-mirage-Bhyjyiqr.js} +0 -0
- /package/dist/web/assets/{ballerina-DUO9mwyV.js → ballerina-QiWgir3v.js} +0 -0
- /package/dist/web/assets/{bat-Cg8hlSct.js → bat-DxtlkwN3.js} +0 -0
- /package/dist/web/assets/{beancount-f8f3BBqB.js → beancount-Bt21X7M_.js} +0 -0
- /package/dist/web/assets/{berry-BqVQYZZr.js → berry-Cksre7lO.js} +0 -0
- /package/dist/web/assets/{bibtex-C_q8ygAM.js → bibtex-DkakgDlR.js} +0 -0
- /package/dist/web/assets/{bicep-DWS-2uWR.js → bicep-8crmcyXU.js} +0 -0
- /package/dist/web/assets/{bird2-C0pvipi_.js → bird2-CewQ2I_Z.js} +0 -0
- /package/dist/web/assets/{c-BQFvJ6Ux.js → c-C_6zE8Ha.js} +0 -0
- /package/dist/web/assets/{c3-Da53KeG6.js → c3-DLWB9-dp.js} +0 -0
- /package/dist/web/assets/{cadence-B5X2vWm3.js → cadence-D8qYS6pR.js} +0 -0
- /package/dist/web/assets/{catppuccin-frappe-Dm3rVY3b.js → catppuccin-frappe-Bh95lNRy.js} +0 -0
- /package/dist/web/assets/{catppuccin-latte-ZxS-ga_K.js → catppuccin-latte-C1PlN3G0.js} +0 -0
- /package/dist/web/assets/{catppuccin-macchiato-B0bAzXh0.js → catppuccin-macchiato-CldtgdK4.js} +0 -0
- /package/dist/web/assets/{catppuccin-mocha-BeMC5hqi.js → catppuccin-mocha-BiwqxX9A.js} +0 -0
- /package/dist/web/assets/{clarity-BRdirlsw.js → clarity-CGiU22jY.js} +0 -0
- /package/dist/web/assets/{clojure-pSLH-KQp.js → clojure-COIC3doh.js} +0 -0
- /package/dist/web/assets/{cmake-DsZu4U4F.js → cmake-DWUEQfHv.js} +0 -0
- /package/dist/web/assets/{codeowners-CtkKTnsH.js → codeowners-BNHiAQea.js} +0 -0
- /package/dist/web/assets/{codeql-BpP1PDrj.js → codeql-BuKaoS0j.js} +0 -0
- /package/dist/web/assets/{common-lisp-BX1XgHTt.js → common-lisp-D5IKbxsd.js} +0 -0
- /package/dist/web/assets/{coq-D2bX6-8g.js → coq-BMVS-p94.js} +0 -0
- /package/dist/web/assets/{csharp-DcR97P0D.js → csharp-DMlbXbM4.js} +0 -0
- /package/dist/web/assets/{css-CIjciIIk.js → css-CjkJC89X.js} +0 -0
- /package/dist/web/assets/{csv-DW48Z5d-.js → csv-DoNVBBf0.js} +0 -0
- /package/dist/web/assets/{csv-parser-DVHBcvuE.js → csv-parser-DHvoTjMF.js} +0 -0
- /package/dist/web/assets/{cue-Byh1xHko.js → cue-DoTRbBg-.js} +0 -0
- /package/dist/web/assets/{cypher-TVlMMWma.js → cypher-DVsNdTT3.js} +0 -0
- /package/dist/web/assets/{d-CxaScvxf.js → d-EX7BBSrv.js} +0 -0
- /package/dist/web/assets/{dark-plus-sn-FFTT1.js → dark-plus-P_YTHwlh.js} +0 -0
- /package/dist/web/assets/{dart-BmpakMe-.js → dart-Do-rgU4S.js} +0 -0
- /package/dist/web/assets/{dax-B1aRUMXT.js → dax-DxOieacu.js} +0 -0
- /package/dist/web/assets/{desktop-C6lD62ar.js → desktop-CvdV7SBf.js} +0 -0
- /package/dist/web/assets/{diff-CupGmT1a.js → diff-CNOetGen.js} +0 -0
- /package/dist/web/assets/{dist-Bi4gRyhr.js → dist-CMfLpPW_.js} +0 -0
- /package/dist/web/assets/{docker-DjDr3TJF.js → docker-DAlG4_q-.js} +0 -0
- /package/dist/web/assets/{dotenv-QeT1SLTv.js → dotenv-Dh3qf6yx.js} +0 -0
- /package/dist/web/assets/{dracula-yI5kpKp5.js → dracula-baAGmJYG.js} +0 -0
- /package/dist/web/assets/{dracula-soft-CYF6cJce.js → dracula-soft-l7ZIyums.js} +0 -0
- /package/dist/web/assets/{dream-maker-TBDKiHrV.js → dream-maker-DeRjdBn8.js} +0 -0
- /package/dist/web/assets/{emacs-lisp-B7aZTnJ_.js → emacs-lisp-CB05-jQA.js} +0 -0
- /package/dist/web/assets/{everforest-dark-CN77B1NJ.js → everforest-dark-CT-llQBT.js} +0 -0
- /package/dist/web/assets/{everforest-light-CyFxGMIs.js → everforest-light-ByPJkTT7.js} +0 -0
- /package/dist/web/assets/{fennel-C6ktGUzp.js → fennel-CeMarbvn.js} +0 -0
- /package/dist/web/assets/{fish-Vr62C2vP.js → fish-BYrt6Xtb.js} +0 -0
- /package/dist/web/assets/{fluent-CH3IJUki.js → fluent-Cr5VcAwh.js} +0 -0
- /package/dist/web/assets/{fortran-free-form-BN7VmmxZ.js → fortran-free-form-DQmKtrLa.js} +0 -0
- /package/dist/web/assets/{gdscript-BYdRNu_P.js → gdscript-QTDKjzhY.js} +0 -0
- /package/dist/web/assets/{gdshader-C2XOq90k.js → gdshader-Chr-5UYt.js} +0 -0
- /package/dist/web/assets/{genie-B4hkIXNU.js → genie-kWHMWnt0.js} +0 -0
- /package/dist/web/assets/{gherkin-CW9NH2DW.js → gherkin-DtEBQFUo.js} +0 -0
- /package/dist/web/assets/{github-dark-BWCf99kM.js → github-dark-B5aiUta2.js} +0 -0
- /package/dist/web/assets/{github-dark-default-DZ1m_6OP.js → github-dark-default-BkxtsTqR.js} +0 -0
- /package/dist/web/assets/{github-dark-dimmed-CUXSQ8bS.js → github-dark-dimmed-B9QjR_0K.js} +0 -0
- /package/dist/web/assets/{github-dark-high-contrast-B6BvU8H-.js → github-dark-high-contrast-D6bO-5c0.js} +0 -0
- /package/dist/web/assets/{github-light-Ch-E92cy.js → github-light-Cg2ZW_CJ.js} +0 -0
- /package/dist/web/assets/{github-light-default-CKFslV7i.js → github-light-default-eD7qUVlQ.js} +0 -0
- /package/dist/web/assets/{github-light-high-contrast-DiJVeRlZ.js → github-light-high-contrast-CFnlSVjd.js} +0 -0
- /package/dist/web/assets/{gleam-D4dP7V5M.js → gleam-BpYoOqIT.js} +0 -0
- /package/dist/web/assets/{gn-DuO2D2eW.js → gn-BxwtZPau.js} +0 -0
- /package/dist/web/assets/{gnuplot-Df_8pFbI.js → gnuplot-CRyiZDfl.js} +0 -0
- /package/dist/web/assets/{go-BL16cMJO.js → go-Dw1Qx94W.js} +0 -0
- /package/dist/web/assets/{groovy-rfHeCRqy.js → groovy-zq_SbWk-.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-hard-CLbDN-E3.js → gruvbox-dark-hard-DbbNy8-Q.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-medium-Df0L0qje.js → gruvbox-dark-medium-BQZ7Kj6C.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-soft-DV6TAcen.js → gruvbox-dark-soft-vNavErXi.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-hard-CWXUzp9b.js → gruvbox-light-hard-AOFbCd7i.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-medium-BOHMe1-7.js → gruvbox-light-medium-DqtxND6H.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-soft-DrrS2sdV.js → gruvbox-light-soft-Cg0basu6.js} +0 -0
- /package/dist/web/assets/{haskell-DybKb9uW.js → haskell-CcsmcOez.js} +0 -0
- /package/dist/web/assets/{haxe-DLRwZxmt.js → haxe-BimYY9X4.js} +0 -0
- /package/dist/web/assets/{hcl-CusCzwOk.js → hcl-CTrXCK40.js} +0 -0
- /package/dist/web/assets/{hjson-BygWIJ-3.js → hjson-CmqqU6Qg.js} +0 -0
- /package/dist/web/assets/{hlsl-DCKvuEyZ.js → hlsl-CuJtX_uf.js} +0 -0
- /package/dist/web/assets/{horizon-DSOVAzyH.js → horizon-BGeSjDYJ.js} +0 -0
- /package/dist/web/assets/{horizon-bright-DKfVjGj_.js → horizon-bright-GYMgd3R0.js} +0 -0
- /package/dist/web/assets/{houston-Ddm8AgpV.js → houston--JD_5r2q.js} +0 -0
- /package/dist/web/assets/{hy-DzZa6alE.js → hy-C9ILOiqk.js} +0 -0
- /package/dist/web/assets/{image-gallery-CObzTisL.js → image-gallery-BWFpZAAP.js} +0 -0
- /package/dist/web/assets/{imba-aiXalPnK.js → imba-B6F3yus5.js} +0 -0
- /package/dist/web/assets/{ini-CAvrVa5R.js → ini-BfkQswMZ.js} +0 -0
- /package/dist/web/assets/{java-PRMSQp24.js → java-DEH5avQ1.js} +0 -0
- /package/dist/web/assets/{javascript-DQR6GQKs.js → javascript-Dr4yHuV2.js} +0 -0
- /package/dist/web/assets/{json-uWUKqOxq.js → json-BuR35ewf.js} +0 -0
- /package/dist/web/assets/{json5-R_LXJXL0.js → json5-Ny9ZiJAC.js} +0 -0
- /package/dist/web/assets/{jsonc-tC5zvrPO.js → jsonc-zYVuoZkR.js} +0 -0
- /package/dist/web/assets/{jsonl-BAwnWh7U.js → jsonl-B9xthb_H.js} +0 -0
- /package/dist/web/assets/{jsonnet-CPj4HCRm.js → jsonnet-CCoLJbMP.js} +0 -0
- /package/dist/web/assets/{jssm-B7HswxOM.js → jssm-W0GDlQXi.js} +0 -0
- /package/dist/web/assets/{jsx-z4TGELE1.js → jsx-BrLJpWC-.js} +0 -0
- /package/dist/web/assets/{kanagawa-dragon-BpeLlraV.js → kanagawa-dragon-CuVwt_MO.js} +0 -0
- /package/dist/web/assets/{kanagawa-lotus-CLnIPAGs.js → kanagawa-lotus-B3W_617P.js} +0 -0
- /package/dist/web/assets/{kanagawa-wave-BglPniBb.js → kanagawa-wave-CEx4MP5W.js} +0 -0
- /package/dist/web/assets/{kdl-B6blIqLq.js → kdl-DilRejnB.js} +0 -0
- /package/dist/web/assets/{kotlin-DVAEYiwY.js → kotlin-Bq5JtKaa.js} +0 -0
- /package/dist/web/assets/{kusto-Ba87977j.js → kusto-Bhp2KYog.js} +0 -0
- /package/dist/web/assets/{laserwave-C-NuzoMp.js → laserwave-WA_7c5hu.js} +0 -0
- /package/dist/web/assets/{lean-CJ_KLkrJ.js → lean-AEGcXwS-.js} +0 -0
- /package/dist/web/assets/{less-Dfn9YC_2.js → less-NPqH-26u.js} +0 -0
- /package/dist/web/assets/{libesm-DdtEjhIi.js → libesm-tR4XlhMj.js} +0 -0
- /package/dist/web/assets/{light-plus-B3BLlguG.js → light-plus-B4xW7cgY.js} +0 -0
- /package/dist/web/assets/{llvm-6UDP3NQb.js → llvm-CPyWNcDE.js} +0 -0
- /package/dist/web/assets/{log-QW3dqr1D.js → log-CeQmiQSz.js} +0 -0
- /package/dist/web/assets/{logo-DUinJheI.js → logo-BO0VrjBm.js} +0 -0
- /package/dist/web/assets/{luau-B1vcBzl8.js → luau-B6GT9kA1.js} +0 -0
- /package/dist/web/assets/{make-CqNL10gS.js → make-CwfmN7e_.js} +0 -0
- /package/dist/web/assets/{markdown-BQNMNiYS.js → markdown-Bbe4FlLp.js} +0 -0
- /package/dist/web/assets/{material-theme-darker-BZrskvoo.js → material-theme-darker-u7M3XGe7.js} +0 -0
- /package/dist/web/assets/{material-theme-rbV8EefR.js → material-theme-iD9XbfsB.js} +0 -0
- /package/dist/web/assets/{material-theme-lighter-B0CBBjBw.js → material-theme-lighter-BGdI48zn.js} +0 -0
- /package/dist/web/assets/{material-theme-ocean-CX_CHnQq.js → material-theme-ocean-_mk62F5q.js} +0 -0
- /package/dist/web/assets/{material-theme-palenight-ztiOCPbw.js → material-theme-palenight-CxFbDyAu.js} +0 -0
- /package/dist/web/assets/{matlab-tHQDNC9G.js → matlab-Z0RUE3V_.js} +0 -0
- /package/dist/web/assets/{mdx-D4kZ6c6G.js → mdx-BZ5wPYzV.js} +0 -0
- /package/dist/web/assets/{mermaid-DlR4U4Pe.js → mermaid-CY1_aP-h.js} +0 -0
- /package/dist/web/assets/{min-dark-BX-xHAD2.js → min-dark-DKWtxOre.js} +0 -0
- /package/dist/web/assets/{min-light-Bxw0_Gqq.js → min-light-e1iwHDfP.js} +0 -0
- /package/dist/web/assets/{mipsasm-DYyZAum-.js → mipsasm-BiDfQ4ir.js} +0 -0
- /package/dist/web/assets/{mojo-q9wuEYKR.js → mojo-DteFEHsj.js} +0 -0
- /package/dist/web/assets/{monokai-D9vrJ8JM.js → monokai-BgPbE9xZ.js} +0 -0
- /package/dist/web/assets/{moonbit-C9roWxMb.js → moonbit-mEbRp-mA.js} +0 -0
- /package/dist/web/assets/{move-BvhjG2ic.js → move-CogqZw7A.js} +0 -0
- /package/dist/web/assets/{narrat-BXGbPwG3.js → narrat-BD2qjmy1.js} +0 -0
- /package/dist/web/assets/{nextflow-groovy-CMy5-Z4k.js → nextflow-groovy-BNX8GOD_.js} +0 -0
- /package/dist/web/assets/{night-owl-CBQBcbmw.js → night-owl-ed3GbziD.js} +0 -0
- /package/dist/web/assets/{night-owl-light-CC-brflp.js → night-owl-light-B0j7O0Wn.js} +0 -0
- /package/dist/web/assets/{nix-B9EAdptu.js → nix-DFDbcQQm.js} +0 -0
- /package/dist/web/assets/{nord-DxASd4qG.js → nord-D93TxEQi.js} +0 -0
- /package/dist/web/assets/{nushell-BsY59i7N.js → nushell-3_H_XKja.js} +0 -0
- /package/dist/web/assets/{objective-c-CoeQAcnP.js → objective-c-BM3u6nlm.js} +0 -0
- /package/dist/web/assets/{objective-cpp-BysdFvND.js → objective-cpp-DZ73h42g.js} +0 -0
- /package/dist/web/assets/{ocaml-CrtQYMMK.js → ocaml-B1PU5gmh.js} +0 -0
- /package/dist/web/assets/{odin-B8pz4gIL.js → odin-D14L6aRL.js} +0 -0
- /package/dist/web/assets/{one-dark-pro-3sSHqoqQ.js → one-dark-pro-Bj-94Z8v.js} +0 -0
- /package/dist/web/assets/{one-light-LUZAvT8g.js → one-light-DTdGWyOZ.js} +0 -0
- /package/dist/web/assets/{openscad-DcKia-sV.js → openscad-Ciq554Sj.js} +0 -0
- /package/dist/web/assets/{pascal-zM5SlmgF.js → pascal-Bfd4qdGi.js} +0 -0
- /package/dist/web/assets/{pkl-C_r_zLLt.js → pkl-CTurDUfj.js} +0 -0
- /package/dist/web/assets/{plastic-B3cRtgKD.js → plastic-BLQfZXQp.js} +0 -0
- /package/dist/web/assets/{plsql-BzyjheXU.js → plsql-DyYfP9kQ.js} +0 -0
- /package/dist/web/assets/{po-Dz4OaDNi.js → po-CgXdT8vQ.js} +0 -0
- /package/dist/web/assets/{poimandres-BBbtYwmR.js → poimandres-DOg5fK6s.js} +0 -0
- /package/dist/web/assets/{polar-Che1XC2Z.js → polar-Dv4aoL6X.js} +0 -0
- /package/dist/web/assets/{postcss-DouzwG4F.js → postcss-DxugHnJD.js} +0 -0
- /package/dist/web/assets/{powerquery-YOdcTZmd.js → powerquery-CR3kzRyX.js} +0 -0
- /package/dist/web/assets/{powershell-8ojzYH34.js → powershell-BE91xJQB.js} +0 -0
- /package/dist/web/assets/{prisma-DCmjceML.js → prisma-B_3yUlRQ.js} +0 -0
- /package/dist/web/assets/{prolog-ByNNz2pB.js → prolog-DcT781Ht.js} +0 -0
- /package/dist/web/assets/{proto-DWMt6Qu1.js → proto-CSf-w5k2.js} +0 -0
- /package/dist/web/assets/{puppet-osfKyZp4.js → puppet-CLGO6QWl.js} +0 -0
- /package/dist/web/assets/{purescript-ClIblkvh.js → purescript-Bah2McKd.js} +0 -0
- /package/dist/web/assets/{python-Ctyr4faP.js → python-DUH8qy7N.js} +0 -0
- /package/dist/web/assets/{qmldir-CbNgyhnI.js → qmldir-YIsHnjKq.js} +0 -0
- /package/dist/web/assets/{qss-LbRHzb21.js → qss-CLmoVNA4.js} +0 -0
- /package/dist/web/assets/{r-Dvfjp0wc.js → r-Pon-b-vz.js} +0 -0
- /package/dist/web/assets/{racket-DI8v8nzD.js → racket-BA09P5Kg.js} +0 -0
- /package/dist/web/assets/{raku-CX1q9GW0.js → raku-DlpzU7Fe.js} +0 -0
- /package/dist/web/assets/{red-CSz3QEOw.js → red-l7VuCR6d.js} +0 -0
- /package/dist/web/assets/{reg-FMjhUQE-.js → reg-g_k7gQEr.js} +0 -0
- /package/dist/web/assets/{regexp-CCSe1Fc4.js → regexp-CT17B7I7.js} +0 -0
- /package/dist/web/assets/{rel-Ce0UO-OB.js → rel-DkPCS7Bp.js} +0 -0
- /package/dist/web/assets/{resolve-theme-B9Mgd5GG.js → resolve-theme-B0eotaPq.js} +0 -0
- /package/dist/web/assets/{riscv-BFZrdZtB.js → riscv-CYuFTw30.js} +0 -0
- /package/dist/web/assets/{ron-D0t4kvJG.js → ron-cY7u_yEu.js} +0 -0
- /package/dist/web/assets/{rose-pine-HzwmOzHM.js → rose-pine-Cr_h_Ubo.js} +0 -0
- /package/dist/web/assets/{rose-pine-dawn-CSp9xAF9.js → rose-pine-dawn-rRewaSZg.js} +0 -0
- /package/dist/web/assets/{rose-pine-moon-Cnzr2Cw0.js → rose-pine-moon-3VX5Xwca.js} +0 -0
- /package/dist/web/assets/{rosmsg-DEJ8Snbv.js → rosmsg-Du4ie5PU.js} +0 -0
- /package/dist/web/assets/{rust-2jBzrC0l.js → rust-B7xWL-Eh.js} +0 -0
- /package/dist/web/assets/{sass-DD75Izu_.js → sass-oXbOG8de.js} +0 -0
- /package/dist/web/assets/{scala-DpBKROIu.js → scala-nlZJhKHf.js} +0 -0
- /package/dist/web/assets/{scheme-DM2jzwUK.js → scheme-FZ9_Zy8D.js} +0 -0
- /package/dist/web/assets/{sdbl-C34Cg6e7.js → sdbl-D_lyvXiu.js} +0 -0
- /package/dist/web/assets/{shellscript-DxfzKSgU.js → shellscript-DRWRboAd.js} +0 -0
- /package/dist/web/assets/{slack-dark-DIIV3bpD.js → slack-dark-BGk2uVHu.js} +0 -0
- /package/dist/web/assets/{slack-ochin-BTkxWjEP.js → slack-ochin-PAoSX041.js} +0 -0
- /package/dist/web/assets/{smalltalk-Bm0qg6SA.js → smalltalk-qP0zvICo.js} +0 -0
- /package/dist/web/assets/{snazzy-light-ClAuuhn4.js → snazzy-light-B7uccpH6.js} +0 -0
- /package/dist/web/assets/{solarized-dark-93sEWmYT.js → solarized-dark-CbICLZs3.js} +0 -0
- /package/dist/web/assets/{solarized-light-C74FvgcO.js → solarized-light-CprWhyjj.js} +0 -0
- /package/dist/web/assets/{solidity-H9fSjkQP.js → solidity-B7pcm2K6.js} +0 -0
- /package/dist/web/assets/{splunk-CDTN2iAG.js → splunk-Co2LL4J_.js} +0 -0
- /package/dist/web/assets/{sql-CoqObhgo.js → sql-cETIdGnT.js} +0 -0
- /package/dist/web/assets/{ssh-config-C3Wv6qRp.js → ssh-config-DETG6JCD.js} +0 -0
- /package/dist/web/assets/{stylus-D0uTFXCy.js → stylus-tmTre2-O.js} +0 -0
- /package/dist/web/assets/{swift-DtMMgvRI.js → swift-BeKQbULV.js} +0 -0
- /package/dist/web/assets/{synthwave-84-DAroyZBu.js → synthwave-84-eCL4-Xdn.js} +0 -0
- /package/dist/web/assets/{system-verilog-Bj7X_lq4.js → system-verilog-CviwWvEx.js} +0 -0
- /package/dist/web/assets/{systemd-CH8ApCUs.js → systemd-WuoFIbBS.js} +0 -0
- /package/dist/web/assets/{talonscript-70lVC3cw.js → talonscript-B9VBu7VI.js} +0 -0
- /package/dist/web/assets/{tasl-WQR8VslV.js → tasl-CN7r11ky.js} +0 -0
- /package/dist/web/assets/{tcl-caTR_frn.js → tcl-e1cdhe3h.js} +0 -0
- /package/dist/web/assets/{terraform-CVRZ4SZ4.js → terraform-BEP0mfa_.js} +0 -0
- /package/dist/web/assets/{tokyo-night-CDyT1jCI.js → tokyo-night-BfA_dA37.js} +0 -0
- /package/dist/web/assets/{toml-B7D2mIDU.js → toml-BrDIfNnt.js} +0 -0
- /package/dist/web/assets/{tsv-2kdn5dqL.js → tsv-CyfGyE4i.js} +0 -0
- /package/dist/web/assets/{tsx-DJfG3fzg.js → tsx-BrFA8Iwf.js} +0 -0
- /package/dist/web/assets/{turtle-DaQac_UT.js → turtle-9GBtR0mP.js} +0 -0
- /package/dist/web/assets/{typescript-BLfAIchl.js → typescript-BuTRZ65V.js} +0 -0
- /package/dist/web/assets/{typespec-ymBaOi_c.js → typespec-CRiNNRXF.js} +0 -0
- /package/dist/web/assets/{typst-DkvUYMT-.js → typst-CmpTnnkl.js} +0 -0
- /package/dist/web/assets/{utils-E0yyGxXt.js → utils-B9H0d3Fh.js} +0 -0
- /package/dist/web/assets/{v-D4997FGs.js → v-DnIi2aab.js} +0 -0
- /package/dist/web/assets/{vala-AFg9DmKE.js → vala-B7VxCNZP.js} +0 -0
- /package/dist/web/assets/{vb-DNUJ9fOP.js → vb-DGoQ3pBT.js} +0 -0
- /package/dist/web/assets/{vendor-xterm-QDr0jvpJ.js → vendor-xterm-ClRVIJ-k.js} +0 -0
- /package/dist/web/assets/{verilog-BrnAQ3tF.js → verilog-zGOC4qwV.js} +0 -0
- /package/dist/web/assets/{vesper-DCZnFmY0.js → vesper-DXR9-06q.js} +0 -0
- /package/dist/web/assets/{vhdl-DoK4PjhE.js → vhdl-CCO0oz0l.js} +0 -0
- /package/dist/web/assets/{viml-D6EKG66q.js → viml-Dxwr-QNN.js} +0 -0
- /package/dist/web/assets/{vitesse-black-C6KILWdF.js → vitesse-black-Bhah7RxE.js} +0 -0
- /package/dist/web/assets/{vitesse-dark-BuSr6OT5.js → vitesse-dark-CGK6XVwc.js} +0 -0
- /package/dist/web/assets/{vitesse-light-M-eYyOoe.js → vitesse-light-ne5kfw8h.js} +0 -0
- /package/dist/web/assets/{vyper-CwSN64Jn.js → vyper-ErDFqVoP.js} +0 -0
- /package/dist/web/assets/{wasm-CVlf9Jwb.js → wasm-Bd258nS1.js} +0 -0
- /package/dist/web/assets/{wasm-BhQEkzil.js → wasm-DtWvUWGr.js} +0 -0
- /package/dist/web/assets/{wenyan-BC7irgM3.js → wenyan-EZe1IEYT.js} +0 -0
- /package/dist/web/assets/{wgsl-W-3UHMDO.js → wgsl-B1o5_I_9.js} +0 -0
- /package/dist/web/assets/{wikitext-T1DoFcm8.js → wikitext-C_rwp8An.js} +0 -0
- /package/dist/web/assets/{wit-DIrx_eCI.js → wit-gbYtF3rY.js} +0 -0
- /package/dist/web/assets/{wolfram-Cza4b443.js → wolfram-DrcQFe2n.js} +0 -0
- /package/dist/web/assets/{yaml-DOZ10aZe.js → yaml-CzDAObLX.js} +0 -0
- /package/dist/web/assets/{zenscript-DDAXkseY.js → zenscript-B-J1phtg.js} +0 -0
- /package/dist/web/assets/{zig-CrzwMpQE.js → zig-BlzNia0O.js} +0 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/** Windows Quick Access pinned folders via the Shell COM `Namespace`
|
|
2
|
+
* (`shell:::{679f85cb-...}`), verified live: ~150ms enumeration, and
|
|
3
|
+
* `System.Home.IsPinned` reliably separates pinned entries from "frequent"
|
|
4
|
+
* ones. Shell COM reports `.zip`/`.rar` archives as `IsFolder=true`, so
|
|
5
|
+
* every candidate is re-checked with a real directory stat before it ships. */
|
|
6
|
+
import * as fsp from "node:fs/promises";
|
|
7
|
+
import type { PinnedFolder } from "../../types/system.ts";
|
|
8
|
+
import { defaultRunner, type Runner } from "./spawn-runner.ts";
|
|
9
|
+
|
|
10
|
+
export interface QuickAccessDeps {
|
|
11
|
+
run: Runner;
|
|
12
|
+
isDirectory: (path: string) => Promise<boolean>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const defaultDeps: QuickAccessDeps = {
|
|
16
|
+
run: defaultRunner,
|
|
17
|
+
isDirectory: async (p) => {
|
|
18
|
+
try {
|
|
19
|
+
return (await fsp.stat(p)).isDirectory();
|
|
20
|
+
} catch {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// `@($result)` forces an array even for 0/1 items — ConvertTo-Json unwraps a
|
|
27
|
+
// single-element pipeline input into a bare object otherwise.
|
|
28
|
+
const QUICK_ACCESS_SCRIPT = `
|
|
29
|
+
$items = (New-Object -ComObject Shell.Application).Namespace('shell:::{679f85cb-0220-4080-b29b-5540cc05aab6}').Items()
|
|
30
|
+
$result = foreach ($i in $items) {
|
|
31
|
+
if ($i.IsFolder) {
|
|
32
|
+
[PSCustomObject]@{ Name = $i.Name; Path = $i.Path; Pinned = $i.ExtendedProperty('System.Home.IsPinned') }
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
$arr = @($result)
|
|
36
|
+
$json = $arr | ConvertTo-Json -Compress
|
|
37
|
+
if ($arr.Count -le 1) { $json = '[' + $json + ']' }
|
|
38
|
+
Write-Output $json
|
|
39
|
+
`.trim();
|
|
40
|
+
|
|
41
|
+
interface RawItem {
|
|
42
|
+
Name?: string;
|
|
43
|
+
Path?: string;
|
|
44
|
+
Pinned?: boolean | string | null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function isPinned(v: RawItem["Pinned"]): boolean {
|
|
48
|
+
return v === true || v === "True" || v === "true";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Read Quick Access pinned folders, appending any failure to the shared `warnings` array (never throws). */
|
|
52
|
+
export async function getWindowsQuickAccessPinned(
|
|
53
|
+
warnings: string[],
|
|
54
|
+
overrides: Partial<QuickAccessDeps> = {},
|
|
55
|
+
): Promise<PinnedFolder[]> {
|
|
56
|
+
const deps: QuickAccessDeps = { ...defaultDeps, ...overrides };
|
|
57
|
+
try {
|
|
58
|
+
const res = await deps.run(
|
|
59
|
+
["powershell.exe", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", QUICK_ACCESS_SCRIPT],
|
|
60
|
+
5000,
|
|
61
|
+
);
|
|
62
|
+
if (res.timedOut || res.code !== 0) {
|
|
63
|
+
warnings.push(`pinned: Quick Access ${res.timedOut ? "timed out" : `exited ${res.code}`}`);
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
let raw: RawItem[];
|
|
68
|
+
try {
|
|
69
|
+
const parsed = JSON.parse(res.stdout.trim() || "[]");
|
|
70
|
+
raw = (Array.isArray(parsed) ? parsed : [parsed]).filter((x): x is RawItem => x != null);
|
|
71
|
+
} catch (e) {
|
|
72
|
+
warnings.push(`pinned: Quick Access JSON parse failed (${(e as Error)?.message ?? e})`);
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const pinned: PinnedFolder[] = [];
|
|
77
|
+
for (const item of raw) {
|
|
78
|
+
if (!item.Name || !item.Path || !isPinned(item.Pinned)) continue;
|
|
79
|
+
if (!(await deps.isDirectory(item.Path))) continue; // drops archive-as-folder (.zip/.rar) and stale entries
|
|
80
|
+
pinned.push({ name: item.Name, path: item.Path, source: "quick-access" });
|
|
81
|
+
}
|
|
82
|
+
return pinned;
|
|
83
|
+
} catch (e) {
|
|
84
|
+
warnings.push(`pinned: Quick Access failed (${(e as Error)?.message ?? e})`);
|
|
85
|
+
return [];
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** Shared argv-array shell-out for host-info providers. Every provider that
|
|
2
|
+
* needs PowerShell/plutil/findmnt/xdg-user-dir injects a `Runner` so unit
|
|
3
|
+
* tests never spawn a real process — only `defaultRunner` touches `Bun.spawn`. */
|
|
4
|
+
export interface RunResult {
|
|
5
|
+
stdout: string;
|
|
6
|
+
stderr: string;
|
|
7
|
+
/** Process exit code, or null when killed by the timeout. */
|
|
8
|
+
code: number | null;
|
|
9
|
+
timedOut: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type Runner = (argv: string[], timeoutMs?: number) => Promise<RunResult>;
|
|
13
|
+
|
|
14
|
+
const DEFAULT_TIMEOUT_MS = 5000;
|
|
15
|
+
|
|
16
|
+
/** Real implementation: argv array only (never string-interpolated into a shell), bounded by timeoutMs. */
|
|
17
|
+
export const defaultRunner: Runner = async (argv, timeoutMs = DEFAULT_TIMEOUT_MS) => {
|
|
18
|
+
const proc = Bun.spawn(argv, { stdout: "pipe", stderr: "pipe", stdin: "ignore", windowsHide: true });
|
|
19
|
+
let timedOut = false;
|
|
20
|
+
const killTimer = setTimeout(() => {
|
|
21
|
+
timedOut = true;
|
|
22
|
+
try {
|
|
23
|
+
proc.kill();
|
|
24
|
+
} catch {
|
|
25
|
+
// Process already exited between the timer firing and the kill call.
|
|
26
|
+
}
|
|
27
|
+
}, timeoutMs);
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
const [stdout, stderr, code] = await Promise.all([
|
|
31
|
+
new Response(proc.stdout).text(),
|
|
32
|
+
new Response(proc.stderr).text(),
|
|
33
|
+
proc.exited,
|
|
34
|
+
]);
|
|
35
|
+
return { stdout, stderr, code, timedOut };
|
|
36
|
+
} catch (e: any) {
|
|
37
|
+
return { stdout: "", stderr: e?.message ?? String(e), code: null, timedOut };
|
|
38
|
+
} finally {
|
|
39
|
+
clearTimeout(killTimer);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** Shapes returned by the SQLite viewer service, shared by both of its doors. */
|
|
2
|
+
|
|
3
|
+
export interface TableInfo {
|
|
4
|
+
name: string;
|
|
5
|
+
rowCount: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface ColumnInfo {
|
|
9
|
+
cid: number;
|
|
10
|
+
name: string;
|
|
11
|
+
type: string;
|
|
12
|
+
notnull: boolean;
|
|
13
|
+
pk: boolean;
|
|
14
|
+
dflt_value: string | null;
|
|
15
|
+
fk: { table: string; column: string } | null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface QueryResult {
|
|
19
|
+
columns: string[];
|
|
20
|
+
rows: Record<string, unknown>[];
|
|
21
|
+
rowsAffected: number;
|
|
22
|
+
changeType: "select" | "modify";
|
|
23
|
+
executionTimeMs: number;
|
|
24
|
+
/** True when a row cap cut the result short. */
|
|
25
|
+
truncated?: boolean;
|
|
26
|
+
}
|
|
@@ -1,29 +1,14 @@
|
|
|
1
1
|
import { Database } from "bun:sqlite";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { existsSync } from "node:fs";
|
|
4
|
+
import { isPpmDirPath } from "./fs-path-guard.service.ts";
|
|
5
|
+
import { assertNoAttachStatement } from "./fs-ops/sql-statement-guard.ts";
|
|
6
|
+
import { readRows } from "./fs-ops/sql-row-reader.ts";
|
|
4
7
|
|
|
5
|
-
|
|
6
|
-
name: string;
|
|
7
|
-
rowCount: number;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface ColumnInfo {
|
|
11
|
-
cid: number;
|
|
12
|
-
name: string;
|
|
13
|
-
type: string;
|
|
14
|
-
notnull: boolean;
|
|
15
|
-
pk: boolean;
|
|
16
|
-
dflt_value: string | null;
|
|
17
|
-
fk: { table: string; column: string } | null;
|
|
18
|
-
}
|
|
8
|
+
import type { ColumnInfo, QueryResult, TableInfo } from "./sqlite-types.ts";
|
|
19
9
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
rows: Record<string, unknown>[];
|
|
23
|
-
rowsAffected: number;
|
|
24
|
-
changeType: "select" | "modify";
|
|
25
|
-
executionTimeMs: number;
|
|
26
|
-
}
|
|
10
|
+
// Re-exported so existing importers keep using the service as one entry point.
|
|
11
|
+
export type { ColumnInfo, QueryResult, TableInfo };
|
|
27
12
|
|
|
28
13
|
/** Auto-close idle databases after 5 minutes */
|
|
29
14
|
const IDLE_TIMEOUT_MS = 5 * 60 * 1000;
|
|
@@ -36,12 +21,24 @@ interface CachedDb {
|
|
|
36
21
|
class SqliteService {
|
|
37
22
|
private cache = new Map<string, CachedDb>();
|
|
38
23
|
|
|
39
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* Resolve db path — supports both project-relative and absolute paths.
|
|
26
|
+
*
|
|
27
|
+
* Absolute paths are not probed with `existsSync`: they come from the
|
|
28
|
+
* filesystem door, which can point at a dead network mount and would block
|
|
29
|
+
* the event loop here. That door stats the file asynchronously before
|
|
30
|
+
* calling in, and `open()` refuses to create a missing database, so nothing
|
|
31
|
+
* is silently brought into existence either way.
|
|
32
|
+
*/
|
|
40
33
|
private resolvePath(projectPath: string, dbRelPath: string): string {
|
|
41
34
|
const isAbsolute = /^(\/|[A-Za-z]:[/\\])/.test(dbRelPath);
|
|
42
35
|
const abs = isAbsolute ? dbRelPath : resolve(projectPath, dbRelPath);
|
|
43
36
|
if (!isAbsolute && !abs.startsWith(projectPath)) throw new Error("Access denied: path outside project");
|
|
44
|
-
|
|
37
|
+
// The PPM directory holds the config database with provider credentials and
|
|
38
|
+
// the auth token. Absolute paths are accepted here, so this door has to
|
|
39
|
+
// refuse it explicitly or a viewer could read the whole secret store.
|
|
40
|
+
if (isPpmDirPath(resolve(abs))) throw new Error("Access denied: PPM directory is not browsable");
|
|
41
|
+
if (!isAbsolute && !existsSync(abs)) throw new Error(`Database not found: ${dbRelPath}`);
|
|
45
42
|
return abs;
|
|
46
43
|
}
|
|
47
44
|
|
|
@@ -53,7 +50,11 @@ class SqliteService {
|
|
|
53
50
|
cached.timer = setTimeout(() => this.close(absPath), IDLE_TIMEOUT_MS);
|
|
54
51
|
return cached.db;
|
|
55
52
|
}
|
|
56
|
-
|
|
53
|
+
// `create: false` — a viewer must never bring a database file into
|
|
54
|
+
// existence, least of all at a path the caller chose. `readwrite` has to
|
|
55
|
+
// be spelled out alongside it: the two flags are passed straight to
|
|
56
|
+
// sqlite3_open_v2, which rejects the combination that omits it.
|
|
57
|
+
const db = new Database(absPath, { readwrite: true, create: false });
|
|
57
58
|
db.exec("PRAGMA journal_mode = WAL");
|
|
58
59
|
// SQLite defaults FK enforcement off per connection, which would let the
|
|
59
60
|
// viewer delete rows other clients reject and leave orphaned children.
|
|
@@ -122,8 +123,13 @@ class SqliteService {
|
|
|
122
123
|
return { columns, rows, total, page, limit };
|
|
123
124
|
}
|
|
124
125
|
|
|
125
|
-
/**
|
|
126
|
-
|
|
126
|
+
/**
|
|
127
|
+
* Execute arbitrary SQL. `maxRows` caps a SELECT result — a foreign database
|
|
128
|
+
* opened through the filesystem door can be arbitrarily large, and reading
|
|
129
|
+
* every row into memory to answer one request is a denial of service.
|
|
130
|
+
*/
|
|
131
|
+
executeQuery(projectPath: string, dbPath: string, sql: string, maxRows?: number): QueryResult {
|
|
132
|
+
assertNoAttachStatement(sql);
|
|
127
133
|
const abs = this.resolvePath(projectPath, dbPath);
|
|
128
134
|
const db = this.open(abs);
|
|
129
135
|
const trimmed = sql.trim().toUpperCase();
|
|
@@ -132,11 +138,13 @@ class SqliteService {
|
|
|
132
138
|
|
|
133
139
|
const start = performance.now();
|
|
134
140
|
if (isSelect) {
|
|
135
|
-
const
|
|
136
|
-
const rows = stmt.all() as Record<string, unknown>[];
|
|
141
|
+
const { rows, truncated } = readRows(db.query(sql), maxRows);
|
|
137
142
|
const executionTimeMs = Math.round(performance.now() - start);
|
|
138
143
|
const columns = rows.length > 0 ? Object.keys(rows[0]!) : [];
|
|
139
|
-
return {
|
|
144
|
+
return {
|
|
145
|
+
columns, rows, rowsAffected: 0, changeType: "select", executionTimeMs,
|
|
146
|
+
...(truncated ? { truncated: true } : {}),
|
|
147
|
+
};
|
|
140
148
|
}
|
|
141
149
|
|
|
142
150
|
const result = db.run(sql);
|
|
@@ -146,6 +154,7 @@ class SqliteService {
|
|
|
146
154
|
|
|
147
155
|
/** Execute multi-statement SQL script (no result rows returned) */
|
|
148
156
|
executeScript(projectPath: string, dbPath: string, sql: string): { executionTimeMs: number } {
|
|
157
|
+
assertNoAttachStatement(sql);
|
|
149
158
|
const abs = this.resolvePath(projectPath, dbPath);
|
|
150
159
|
const db = this.open(abs);
|
|
151
160
|
const start = performance.now();
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
16
16
|
import { homedir } from "node:os";
|
|
17
17
|
import { join } from "node:path";
|
|
18
|
-
import type { ChatEvent } from "../types/
|
|
18
|
+
import type { ChatEvent } from "../types/chat.ts";
|
|
19
19
|
import { parseSessionMessage } from "./jsonl-transcript-parser.ts";
|
|
20
20
|
|
|
21
21
|
/**
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/** Server-side host facts the frontend cannot learn on its own: OS, path
|
|
2
|
+
* separator, home dir, drives/volumes, resolved known folders, and the
|
|
3
|
+
* user's OS-pinned folders (Windows Quick Access, Finder Favorites,
|
|
4
|
+
* GTK/KDE bookmarks). Backs `GET /api/system/host`. */
|
|
5
|
+
export interface HostInfo {
|
|
6
|
+
platform: "win32" | "darwin" | "linux";
|
|
7
|
+
sep: "\\" | "/";
|
|
8
|
+
homedir: string;
|
|
9
|
+
hostname: string;
|
|
10
|
+
drives: Drive[];
|
|
11
|
+
knownFolders: KnownFolder[];
|
|
12
|
+
pinned: PinnedFolder[];
|
|
13
|
+
/** Non-fatal provider failures — a failure never becomes a 500, it becomes text here. */
|
|
14
|
+
warnings: string[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type DriveKind = "fixed" | "removable" | "network" | "unknown";
|
|
18
|
+
|
|
19
|
+
export interface Drive {
|
|
20
|
+
/** Display label for the mount, e.g. "C:" or "Macintosh HD". */
|
|
21
|
+
name: string;
|
|
22
|
+
/** Filesystem path to browse into, e.g. "C:\\" or "/Volumes/External". */
|
|
23
|
+
path: string;
|
|
24
|
+
kind: DriveKind;
|
|
25
|
+
/** Volume label when the OS reports one (Windows CIM `VolumeName`). */
|
|
26
|
+
label?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type KnownFolderKey =
|
|
30
|
+
| "desktop"
|
|
31
|
+
| "documents"
|
|
32
|
+
| "downloads"
|
|
33
|
+
| "pictures"
|
|
34
|
+
| "music"
|
|
35
|
+
| "videos"
|
|
36
|
+
| "icloud";
|
|
37
|
+
|
|
38
|
+
export interface KnownFolder {
|
|
39
|
+
key: KnownFolderKey;
|
|
40
|
+
name: string;
|
|
41
|
+
path: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type PinnedSource = "quick-access" | "finder-favorites" | "gtk" | "kde";
|
|
45
|
+
|
|
46
|
+
export interface PinnedFolder {
|
|
47
|
+
name: string;
|
|
48
|
+
path: string;
|
|
49
|
+
source: PinnedSource;
|
|
50
|
+
}
|
package/src/web/app.tsx
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { useEffect, useState, useCallback, useRef } from "react";
|
|
1
|
+
import { useEffect, useState, useCallback, useRef, lazy, Suspense } from "react";
|
|
2
2
|
import { Toaster } from "@/components/ui/sonner";
|
|
3
3
|
import { TooltipProvider } from "@/components/ui/tooltip";
|
|
4
4
|
import { PanelLayout } from "@/components/layout/panel-layout";
|
|
5
5
|
import { TabPool } from "@/components/layout/tab-pool";
|
|
6
6
|
import { Sidebar } from "@/components/layout/sidebar";
|
|
7
7
|
import { StatusBar } from "@/components/layout/status-bar";
|
|
8
|
+
import { WindowLayer } from "@/components/floating-window/window-layer";
|
|
8
9
|
import { MobileNav } from "@/components/layout/mobile-nav";
|
|
9
10
|
import { MobileDrawer } from "@/components/layout/mobile-drawer";
|
|
10
11
|
import { ProjectBottomSheet } from "@/components/layout/project-bottom-sheet";
|
|
@@ -36,6 +37,13 @@ import { useExtensionWs } from "@/hooks/use-extension-ws";
|
|
|
36
37
|
import { useIsMobile } from "@/hooks/use-is-mobile";
|
|
37
38
|
import { cn } from "@/lib/utils";
|
|
38
39
|
|
|
40
|
+
// Lazy: the explorer feature (views, actions, skins, icon map) is the same heavy bundle the
|
|
41
|
+
// desktop floating window already keeps out of the initial chunk (see `WINDOW_CONTENT`) —
|
|
42
|
+
// a static import here would pull all of it into every user's boot, mobile or not.
|
|
43
|
+
const MobileExplorerSheet = lazy(() =>
|
|
44
|
+
import("@/components/os-explorer/mobile/mobile-explorer-sheet").then((m) => ({ default: m.MobileExplorerSheet })),
|
|
45
|
+
);
|
|
46
|
+
|
|
39
47
|
type AuthState = "checking" | "authenticated" | "unauthenticated";
|
|
40
48
|
|
|
41
49
|
export function App() {
|
|
@@ -284,8 +292,9 @@ export function App() {
|
|
|
284
292
|
{/* Desktop unified nav rail (wordmark + project switcher + section rail + panel) */}
|
|
285
293
|
<Sidebar />
|
|
286
294
|
|
|
287
|
-
{/* Content area — keep-alive per project
|
|
288
|
-
|
|
295
|
+
{/* Content area — keep-alive per project. `relative` is the positioning context
|
|
296
|
+
the floating window layer measures and clamps its windows against. */}
|
|
297
|
+
<div className="relative flex-1 flex flex-col overflow-hidden">
|
|
289
298
|
{[...mountedProjects].map((projectName) => (
|
|
290
299
|
<div
|
|
291
300
|
key={projectName}
|
|
@@ -303,6 +312,8 @@ export function App() {
|
|
|
303
312
|
{/* Desktop status bar — spans the working area only (not under the sidebar).
|
|
304
313
|
Self-gated to desktop via `hidden md:flex`. */}
|
|
305
314
|
<StatusBar />
|
|
315
|
+
{/* Floating windows — desktop only, self-gated; covers the working area. */}
|
|
316
|
+
<WindowLayer />
|
|
306
317
|
</div>
|
|
307
318
|
</div>
|
|
308
319
|
|
|
@@ -331,6 +342,11 @@ export function App() {
|
|
|
331
342
|
{/* Compare Files picker (Mod+Alt+D, palette, context menus) — singleton */}
|
|
332
343
|
<ComparePicker />
|
|
333
344
|
|
|
345
|
+
{/* Mobile full-screen file explorer sheet — lazy + self-gated, loads nothing until opened */}
|
|
346
|
+
<Suspense fallback={null}>
|
|
347
|
+
<MobileExplorerSheet />
|
|
348
|
+
</Suspense>
|
|
349
|
+
|
|
334
350
|
{/* Global bug report popup */}
|
|
335
351
|
<BugReportPopup />
|
|
336
352
|
|
|
@@ -19,6 +19,9 @@ import { useConnections, type Connection } from "../database/use-connections";
|
|
|
19
19
|
import { GlideDataGrid } from "../database/glide-data-grid";
|
|
20
20
|
import type { GridColumnSchema } from "../database/glide-grid-types";
|
|
21
21
|
import type { DbQueryResult } from "../database/use-database";
|
|
22
|
+
// Single source of truth: the explorer decides whether a double-click can open a file at
|
|
23
|
+
// all from these very sets, so they must not be redeclared here.
|
|
24
|
+
import { AUDIO_EXTS, IMAGE_EXTS, SQLITE_EXTS, VIDEO_EXTS } from "@/components/os-explorer/can-open-in-ppm";
|
|
22
25
|
|
|
23
26
|
const MarkdownRenderer = lazy(() =>
|
|
24
27
|
import("@/components/shared/markdown-renderer").then((m) => ({ default: m.MarkdownRenderer }))
|
|
@@ -30,15 +33,6 @@ const VideoPreview = lazy(() => import("./video-preview").then((m) => ({ default
|
|
|
30
33
|
const AudioPreview = lazy(() => import("./audio-preview").then((m) => ({ default: m.AudioPreview })));
|
|
31
34
|
const DocxPreview = lazy(() => import("./docx-preview").then((m) => ({ default: m.DocxPreview })));
|
|
32
35
|
|
|
33
|
-
/** Image extensions renderable inline */
|
|
34
|
-
const IMAGE_EXTS = new Set(["png", "jpg", "jpeg", "gif", "webp", "svg", "ico"]);
|
|
35
|
-
/** Video extensions playable inline */
|
|
36
|
-
const VIDEO_EXTS = new Set(["mp4", "webm", "mov", "ogg", "avi", "mkv"]);
|
|
37
|
-
/** Audio extensions playable inline */
|
|
38
|
-
const AUDIO_EXTS = new Set(["mp3", "wav", "flac", "aac", "m4a", "wma"]);
|
|
39
|
-
/** SQLite extensions — redirect to sqlite viewer */
|
|
40
|
-
const SQLITE_EXTS = new Set(["db", "sqlite", "sqlite3"]);
|
|
41
|
-
|
|
42
36
|
function getFileExt(filename: string): string {
|
|
43
37
|
return filename.split(".").pop()?.toLowerCase() ?? "";
|
|
44
38
|
}
|
|
@@ -16,7 +16,10 @@ import {
|
|
|
16
16
|
import { SidebarHeader } from "@/components/ui/sidebar-header";
|
|
17
17
|
import { copyToClipboard } from "@/lib/clipboard";
|
|
18
18
|
import { useShallow } from "zustand/react/shallow";
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
useFileStore, loadPersistedExpanded, savePersistedExpanded,
|
|
21
|
+
absoluteProjectPath, relativeProjectPath, type FileNode,
|
|
22
|
+
} from "@/stores/file-store";
|
|
20
23
|
import { useProjectStore } from "@/stores/project-store";
|
|
21
24
|
import { useTabStore } from "@/stores/tab-store";
|
|
22
25
|
import { useCompareStore } from "@/stores/compare-store";
|
|
@@ -37,12 +40,23 @@ import { InlineTreeInput } from "./inline-tree-input";
|
|
|
37
40
|
import { flattenVisibleTree, type InputRow } from "./flatten-visible-tree";
|
|
38
41
|
import { downloadFile, downloadFolder } from "@/lib/file-download";
|
|
39
42
|
import { api, projectUrl } from "@/lib/api-client";
|
|
43
|
+
import { openExplorer } from "@/components/os-explorer/open-explorer";
|
|
44
|
+
import { fsChanged, onFsChanged } from "@/components/os-explorer/explorer-store";
|
|
45
|
+
import { dirnameOf } from "@/components/os-explorer/format-file-meta";
|
|
46
|
+
import { useDragAutoScroll } from "@/components/os-explorer/dnd/use-drag-auto-scroll";
|
|
47
|
+
import { useDropTransfer } from "@/components/os-explorer/dnd/use-drop-transfer";
|
|
48
|
+
import { usePathDropTarget } from "@/components/os-explorer/dnd/use-path-drop-target";
|
|
40
49
|
import { useFileUploadDrag } from "./use-file-upload-drag";
|
|
41
50
|
import { useTreeKeyboardNav } from "./use-tree-keyboard-nav";
|
|
42
51
|
|
|
43
52
|
/** Synthetic root node for creating files/folders at project root */
|
|
44
53
|
const ROOT_NODE: FileNode = { name: "", path: "", type: "directory" };
|
|
45
54
|
|
|
55
|
+
/** Parent directory of a project-relative path; "" is the project root. */
|
|
56
|
+
function parentDirOfRelative(path: string): string {
|
|
57
|
+
return path.includes("/") ? path.slice(0, path.lastIndexOf("/")) : "";
|
|
58
|
+
}
|
|
59
|
+
|
|
46
60
|
interface FileTreeProps {
|
|
47
61
|
onFileOpen?: () => void;
|
|
48
62
|
}
|
|
@@ -116,26 +130,58 @@ export function FileTree({ onFileOpen }: FileTreeProps = {}) {
|
|
|
116
130
|
setFocusedPath(filePath);
|
|
117
131
|
}, [activeProject, setExpanded, loadChildren, setFocusedPath]);
|
|
118
132
|
|
|
119
|
-
/**
|
|
133
|
+
/**
|
|
134
|
+
* Paste clipboard entries into a target directory.
|
|
135
|
+
*
|
|
136
|
+
* The clipboard holds absolute paths and may have been filled by an explorer window, so
|
|
137
|
+
* the source decides the route: entries that live inside this project keep the
|
|
138
|
+
* project-scoped endpoints (relative paths, gitignore-aware), anything else goes through
|
|
139
|
+
* the host filesystem API with a fully-composed destination.
|
|
140
|
+
*/
|
|
120
141
|
const pasteFiles = useCallback(async (targetDir: string) => {
|
|
121
142
|
if (!activeProject || !clipboard) return;
|
|
122
143
|
const projectName = activeProject.name;
|
|
144
|
+
const root = activeProject.path;
|
|
145
|
+
// Every source path is an absolute host path (the clipboard is shared with explorer
|
|
146
|
+
// windows), so the host separator implied by the project root also splits them correctly.
|
|
147
|
+
const sep = root.includes("\\") ? "\\" : "/";
|
|
123
148
|
const endpoint = clipboard.operation === "cut" ? "move" : "copy";
|
|
149
|
+
const touched = new Set<string>([absoluteProjectPath(root, targetDir)]);
|
|
124
150
|
for (const source of clipboard.paths) {
|
|
125
|
-
const name = source.
|
|
126
|
-
const
|
|
151
|
+
const name = source.split(/[/\\]/).filter(Boolean).pop() ?? source;
|
|
152
|
+
const relativeSource = relativeProjectPath(root, source);
|
|
153
|
+
touched.add(dirnameOf(source, sep));
|
|
127
154
|
try {
|
|
128
|
-
|
|
155
|
+
if (relativeSource != null) {
|
|
156
|
+
const destination = targetDir ? `${targetDir}/${name}` : name;
|
|
157
|
+
await api.post(`${projectUrl(projectName)}/files/${endpoint}`, { source: relativeSource, destination });
|
|
158
|
+
} else {
|
|
159
|
+
const destination = absoluteProjectPath(root, targetDir ? `${targetDir}/${name}` : name);
|
|
160
|
+
await api.post(`/api/fs/${endpoint}`, { source, destination });
|
|
161
|
+
}
|
|
129
162
|
} catch (err) {
|
|
130
163
|
toast.error(err instanceof Error ? err.message : `Failed to ${endpoint}`);
|
|
131
164
|
}
|
|
132
165
|
}
|
|
133
166
|
if (clipboard.operation === "cut") setClipboard(null);
|
|
167
|
+
// Any explorer window showing the source or destination directory must refresh too.
|
|
168
|
+
fsChanged(...touched);
|
|
134
169
|
reloadTree();
|
|
135
170
|
}, [activeProject, clipboard, setClipboard, reloadTree]);
|
|
136
171
|
|
|
137
172
|
const treeContainerRef = useRef<HTMLDivElement>(null);
|
|
138
173
|
|
|
174
|
+
/** Absolutise before publishing: the clipboard is shared with the explorer windows. */
|
|
175
|
+
const copyToTreeClipboard = useCallback((relativePaths: string[], operation: "cut" | "copy") => {
|
|
176
|
+
if (!activeProject) return;
|
|
177
|
+
const root = activeProject.path;
|
|
178
|
+
setClipboard({
|
|
179
|
+
paths: relativePaths.map((p) => absoluteProjectPath(root, p)),
|
|
180
|
+
operation,
|
|
181
|
+
origin: { projectName: activeProject.name, root },
|
|
182
|
+
});
|
|
183
|
+
}, [activeProject, setClipboard]);
|
|
184
|
+
|
|
139
185
|
/** Ctrl+X / Ctrl+C / Ctrl+V — scoped to file tree container focus */
|
|
140
186
|
const handleClipboardKeyDown = useCallback((e: React.KeyboardEvent) => {
|
|
141
187
|
if (!activeProject) return;
|
|
@@ -145,12 +191,9 @@ export function FileTree({ onFileOpen }: FileTreeProps = {}) {
|
|
|
145
191
|
const mod = e.metaKey || e.ctrlKey;
|
|
146
192
|
if (!mod) return;
|
|
147
193
|
|
|
148
|
-
if (e.key === "x" && selectedFiles.length > 0) {
|
|
149
|
-
e.preventDefault();
|
|
150
|
-
setClipboard({ paths: [...selectedFiles], operation: "cut" });
|
|
151
|
-
} else if (e.key === "c" && selectedFiles.length > 0) {
|
|
194
|
+
if ((e.key === "x" || e.key === "c") && selectedFiles.length > 0) {
|
|
152
195
|
e.preventDefault();
|
|
153
|
-
|
|
196
|
+
copyToTreeClipboard(selectedFiles, e.key === "x" ? "cut" : "copy");
|
|
154
197
|
} else if (e.key === "v" && clipboard) {
|
|
155
198
|
e.preventDefault();
|
|
156
199
|
pasteFiles("");
|
|
@@ -221,17 +264,46 @@ export function FileTree({ onFileOpen }: FileTreeProps = {}) {
|
|
|
221
264
|
};
|
|
222
265
|
}, [activeProject]);
|
|
223
266
|
|
|
267
|
+
// Symmetric with the fsChanged(...) calls this file emits after its own mutations: an
|
|
268
|
+
// explorer window mutating a directory inside this project must invalidate the matching
|
|
269
|
+
// tree node too.
|
|
270
|
+
useEffect(() => {
|
|
271
|
+
if (!activeProject) return;
|
|
272
|
+
const projectName = activeProject.name;
|
|
273
|
+
const root = activeProject.path;
|
|
274
|
+
return onFsChanged((absoluteDir) => {
|
|
275
|
+
const relative = relativeProjectPath(root, absoluteDir);
|
|
276
|
+
if (relative == null) return; // outside this project — not the tree's concern
|
|
277
|
+
const store = useFileStore.getState();
|
|
278
|
+
store.invalidateIndex();
|
|
279
|
+
store.loadIndex(projectName);
|
|
280
|
+
store.invalidateFolder(projectName, relative);
|
|
281
|
+
});
|
|
282
|
+
}, [activeProject]);
|
|
283
|
+
|
|
224
284
|
const {
|
|
225
285
|
uploadFiles, isRootDragOver,
|
|
226
286
|
handleRootDragEnter, handleRootDragLeave, handleRootDragOver, handleRootDrop,
|
|
227
287
|
} = useFileUploadDrag({ projectName: activeProject?.name, setExpanded });
|
|
228
288
|
|
|
289
|
+
// Cross-surface entry drops (from an explorer window, or another project's tree) onto the
|
|
290
|
+
// tree's own empty background — the project root. Uses the same collision-prompt transfer
|
|
291
|
+
// as a paste; separate from `useFileUploadDrag` above, which only ever reacts to OS files.
|
|
292
|
+
const treeRootSep = activeProject?.path.includes("\\") ? "\\" : "/";
|
|
293
|
+
const { run: transferRun, prompts: transferPrompts } = useDropTransfer(treeRootSep);
|
|
294
|
+
const backgroundDrop = usePathDropTarget({
|
|
295
|
+
targetDir: activeProject?.path ?? null,
|
|
296
|
+
run: transferRun,
|
|
297
|
+
disabled: !activeProject,
|
|
298
|
+
});
|
|
299
|
+
|
|
229
300
|
// Virtualized flat rows: only visible rows are mounted (large dirs stay cheap)
|
|
230
301
|
const rows = useMemo(
|
|
231
302
|
() => flattenVisibleTree(tree, expandedPaths, inlineAction),
|
|
232
303
|
[tree, expandedPaths, inlineAction],
|
|
233
304
|
);
|
|
234
305
|
const scrollRef = useRef<HTMLDivElement>(null);
|
|
306
|
+
useDragAutoScroll(scrollRef);
|
|
235
307
|
const isMobile = useIsMobile();
|
|
236
308
|
const rowVirtualizer = useVirtualizer({
|
|
237
309
|
count: rows.length,
|
|
@@ -265,6 +337,7 @@ export function FileTree({ onFileOpen }: FileTreeProps = {}) {
|
|
|
265
337
|
store.invalidateIndex();
|
|
266
338
|
store.loadIndex(projectName);
|
|
267
339
|
store.invalidateFolder(projectName, parentPath);
|
|
340
|
+
fsChanged(absoluteProjectPath(activeProject!.path, parentPath));
|
|
268
341
|
} else {
|
|
269
342
|
const type = row.inline.type === "new-file" ? "file" : "directory";
|
|
270
343
|
const fullPath = row.targetPath ? `${row.targetPath}/${value}` : value;
|
|
@@ -273,6 +346,7 @@ export function FileTree({ onFileOpen }: FileTreeProps = {}) {
|
|
|
273
346
|
store.invalidateIndex();
|
|
274
347
|
store.loadIndex(projectName);
|
|
275
348
|
store.invalidateFolder(projectName, row.targetPath);
|
|
349
|
+
fsChanged(absoluteProjectPath(activeProject!.path, row.targetPath));
|
|
276
350
|
}
|
|
277
351
|
}, [activeProject, clearInlineAction]);
|
|
278
352
|
|
|
@@ -294,14 +368,14 @@ export function FileTree({ onFileOpen }: FileTreeProps = {}) {
|
|
|
294
368
|
onFileOpen?.();
|
|
295
369
|
return;
|
|
296
370
|
}
|
|
297
|
-
if (action === "cut") {
|
|
371
|
+
if (action === "cut" || action === "copy-file") {
|
|
298
372
|
const paths = selectedFiles.length > 0 && selectedFiles.includes(node.path) ? [...selectedFiles] : [node.path];
|
|
299
|
-
|
|
373
|
+
copyToTreeClipboard(paths, action === "cut" ? "cut" : "copy");
|
|
300
374
|
return;
|
|
301
375
|
}
|
|
302
|
-
if (action === "
|
|
303
|
-
const
|
|
304
|
-
|
|
376
|
+
if (action === "open-in-file-explorer") {
|
|
377
|
+
const target = node.type === "directory" ? node.path : parentDirOfRelative(node.path);
|
|
378
|
+
void openExplorer(absoluteProjectPath(activeProject!.path, target));
|
|
305
379
|
return;
|
|
306
380
|
}
|
|
307
381
|
if (action === "paste" && node.type === "directory") {
|
|
@@ -417,13 +491,16 @@ export function FileTree({ onFileOpen }: FileTreeProps = {}) {
|
|
|
417
491
|
return (
|
|
418
492
|
<div
|
|
419
493
|
ref={treeContainerRef}
|
|
420
|
-
className={cn("flex flex-col h-full outline-none", isRootDragOver && "bg-primary/5")}
|
|
494
|
+
className={cn("flex flex-col h-full outline-none", (isRootDragOver || backgroundDrop.isOver) && "bg-primary/5")}
|
|
421
495
|
tabIndex={0}
|
|
422
496
|
onKeyDown={(e) => { handleClipboardKeyDown(e); handleTreeKeyDown(e); }}
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
497
|
+
// Two independent drags share this background: OS files (handleRoot*, unchanged) and
|
|
498
|
+
// a cross-surface entry drag (backgroundDrop, gated on its own MIME) — each ignores
|
|
499
|
+
// the other's kind, so calling both in sequence is safe.
|
|
500
|
+
onDragEnter={(e) => { handleRootDragEnter(e); backgroundDrop.handlers.onDragEnter(e); }}
|
|
501
|
+
onDragLeave={(e) => { handleRootDragLeave(e); backgroundDrop.handlers.onDragLeave(e); }}
|
|
502
|
+
onDragOver={(e) => { handleRootDragOver(e); backgroundDrop.handlers.onDragOver(e); }}
|
|
503
|
+
onDrop={(e) => { handleRootDrop(e); backgroundDrop.handlers.onDrop(e); }}
|
|
427
504
|
>
|
|
428
505
|
<SidebarHeader icon={FolderOpen} title="Explorer">
|
|
429
506
|
<button onClick={() => handleAction("new-file", ROOT_NODE)} title="New File" className={toolbarBtnClass}>
|
|
@@ -481,6 +558,7 @@ export function FileTree({ onFileOpen }: FileTreeProps = {}) {
|
|
|
481
558
|
onAction={handleAction}
|
|
482
559
|
onFileDrop={uploadFiles}
|
|
483
560
|
onFileOpen={onFileOpen}
|
|
561
|
+
transferRun={transferRun}
|
|
484
562
|
/>
|
|
485
563
|
)}
|
|
486
564
|
</div>
|
|
@@ -514,9 +592,13 @@ export function FileTree({ onFileOpen }: FileTreeProps = {}) {
|
|
|
514
592
|
node={actionState.node}
|
|
515
593
|
projectName={activeProject.name}
|
|
516
594
|
onClose={() => setActionState(null)}
|
|
517
|
-
onRefresh={
|
|
595
|
+
onRefresh={() => {
|
|
596
|
+
fsChanged(absoluteProjectPath(activeProject.path, parentDirOfRelative(actionState.node.path)));
|
|
597
|
+
reloadTree();
|
|
598
|
+
}}
|
|
518
599
|
/>
|
|
519
600
|
)}
|
|
601
|
+
{transferPrompts}
|
|
520
602
|
</div>
|
|
521
603
|
);
|
|
522
604
|
}
|