@hienlh/ppm 0.17.57 → 0.18.0
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 +16 -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-BLYgg033.js} +1 -1
- package/dist/web/assets/{angular-html-D4YJUMLP.js → angular-html-jfMqUuRd.js} +1 -1
- package/dist/web/assets/{angular-ts-CUvq2h3X.js → angular-ts-wytn9PMm.js} +1 -1
- package/dist/web/assets/{api-client-COQtP-dB.js → api-client-B9OFv3nY.js} +1 -1
- package/dist/web/assets/api-client-CKo60P6G.js +1 -0
- package/dist/web/assets/{api-settings-Do0Z0IRN.js → api-settings-DGhshDRg.js} +1 -1
- package/dist/web/assets/api-settings-fdkhqLSF.js +1 -0
- package/dist/web/assets/{api-themes-DXi8sz-9.js → api-themes-okiyhhp9.js} +1 -1
- package/dist/web/assets/{apl-DSANFLx0.js → apl-i-ahKx6O.js} +1 -1
- package/dist/web/assets/architecture-PBZL5I3N-B4J2-O8Q.js +1 -0
- package/dist/web/assets/{astro-CXIotiNS.js → astro-DVBCJfG6.js} +1 -1
- package/dist/web/assets/{audio-preview-D7jNc8mi.js → audio-preview-y8XpCNrh.js} +1 -1
- package/dist/web/assets/{blade-BvrP2gPd.js → blade-BHlL8SqU.js} +1 -1
- package/dist/web/assets/{bsl-CGf6Xuyu.js → bsl-D-UOfR1F.js} +1 -1
- package/dist/web/assets/{cairo-BboZ9qIf.js → cairo-Bn6rucli.js} +1 -1
- package/dist/web/assets/can-open-in-ppm-BzVXoV-d.js +1 -0
- package/dist/web/assets/chat-tab-BcR-3_6R.js +13 -0
- package/dist/web/assets/clock-B1CPBp2g.js +1 -0
- package/dist/web/assets/{cobol-D39V6ukp.js → cobol-Byn9FYKu.js} +1 -1
- package/dist/web/assets/code-editor-CJ8g7dqm.js +10 -0
- package/dist/web/assets/{coffee-DTtK7kKi.js → coffee-DjBl4gm-.js} +1 -1
- package/dist/web/assets/{conflict-editor-CpGcSopK.js → conflict-editor-BP7JFZzG.js} +3 -3
- package/dist/web/assets/context-menu-BSE5yuMG.js +1 -0
- package/dist/web/assets/copy-Su9m2utd.js +1 -0
- package/dist/web/assets/{cpp-CnU9hfBq.js → cpp-CKzic-3E.js} +1 -1
- package/dist/web/assets/{crystal-DMjCL--0.js → crystal-Bkg-Ly_g.js} +1 -1
- package/dist/web/assets/csv-preview-xAwUDNat.js +2 -0
- package/dist/web/assets/{data-grid-overlay-editor-Bdc80TJ1.js → data-grid-overlay-editor-DMTCHIG9.js} +1 -1
- package/dist/web/assets/{database-viewer-DTMmic31.js → database-viewer-BVLWYPa4.js} +1 -1
- package/dist/web/assets/dialog-D__LRQTn.js +1 -0
- package/dist/web/assets/diff-viewer-BheSOHfl.js +4 -0
- package/dist/web/assets/{docx-preview-CR2ZtvNP.js → docx-preview-CGlMqltP.js} +1 -1
- package/dist/web/assets/{edge-CTom35R_.js → edge-DVbZnd3Y.js} +1 -1
- package/dist/web/assets/{edit-diff-preview-D1TEK7Jz.js → edit-diff-preview-ClJAJwlL.js} +1 -1
- package/dist/web/assets/{elixir-BAX9HSOu.js → elixir-Dwi8Wu7W.js} +1 -1
- package/dist/web/assets/{elm-CpD28xsW.js → elm-DtIv-HXJ.js} +1 -1
- package/dist/web/assets/{erb-DHX2IIEs.js → erb-D3HEDz1x.js} +1 -1
- package/dist/web/assets/{erlang-B_etLcPG.js → erlang-2MjTCzGz.js} +1 -1
- package/dist/web/assets/{esm-PmlN1RTs.js → esm-BfqDE09P.js} +1 -1
- package/dist/web/assets/explorer-body-DE5339g9.js +2 -0
- package/dist/web/assets/explorer-window-content-BrTHAoXM.js +1 -0
- package/dist/web/assets/{extension-webview-BTCFWY2A.js → extension-webview-DRq1tWC1.js} +1 -1
- package/dist/web/assets/{fortran-fixed-form-CpR0cN7O.js → fortran-fixed-form-BLyAtyPM.js} +1 -1
- package/dist/web/assets/{fsharp-DxITxZW5.js → fsharp-Bxbn_gbr.js} +1 -1
- package/dist/web/assets/{gdresource-DJ7IvROZ.js → gdresource-sFMY4D2Q.js} +1 -1
- package/dist/web/assets/{git-commit-sQznNP43.js → git-commit-BzB9Lio5.js} +1 -1
- package/dist/web/assets/git-log-panel-pjyOjchU.js +1 -0
- package/dist/web/assets/{git-rebase-DkHvejAR.js → git-rebase-CKSVd848.js} +1 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-CtQ3yXN-.js +1 -0
- package/dist/web/assets/{glide-data-grid-LinTYl0_.js → glide-data-grid-LX3N4SsJ.js} +9 -9
- package/dist/web/assets/{glimmer-js-BwJnDfL-.js → glimmer-js-BY8aExAJ.js} +1 -1
- package/dist/web/assets/{glimmer-ts-Coz155XK.js → glimmer-ts-Bak54dBX.js} +1 -1
- package/dist/web/assets/{glsl-BED7ZmUZ.js → glsl-nR2eaJQe.js} +1 -1
- package/dist/web/assets/{graphql-B_KyE6oz.js → graphql-CGPPFAU6.js} +1 -1
- package/dist/web/assets/group-chat-tab-CVQfptkT.js +1 -0
- package/dist/web/assets/{hack-sxywdIAQ.js → hack-CXIMX-Ol.js} +1 -1
- package/dist/web/assets/{haml-Dts9JlPU.js → haml-tPY-9Pfm.js} +1 -1
- package/dist/web/assets/{handlebars-C9HXIMtj.js → handlebars-BuW1LT7T.js} +1 -1
- package/dist/web/assets/{html-2p0RvXi4.js → html-bgVcwZ-t.js} +1 -1
- package/dist/web/assets/{html-derivative-BiTPuOba.js → html-derivative-D6-IxTgn.js} +1 -1
- package/dist/web/assets/{http-CHd2gYPf.js → http-CJ1zyB-h.js} +1 -1
- package/dist/web/assets/{hurl-boMPgDm9.js → hurl-CmoT7l3T.js} +1 -1
- package/dist/web/assets/{hxml-BQvjiuBt.js → hxml-CztOsL8o.js} +1 -1
- package/dist/web/assets/{image-preview-0SGb7_tH.js → image-preview-BDySmiim.js} +1 -1
- package/dist/web/assets/index-XoDY9bc0.js +52 -0
- package/dist/web/assets/index-YCTraslE.css +2 -0
- package/dist/web/assets/info-3K5VOQVL-Cg0mIJn9.js +1 -0
- package/dist/web/assets/{jinja-B-zKiwlp.js → jinja-B1pq3jJq.js} +1 -1
- package/dist/web/assets/{jison-C2vJYjbJ.js → jison-D8B5pCwA.js} +1 -1
- package/dist/web/assets/{julia-DJ8MrQPA.js → julia-D0K3bwpU.js} +1 -1
- package/dist/web/assets/{just-BvQHq7VE.js → just-B9Jlddu-.js} +1 -1
- package/dist/web/assets/keybindings-store-BE1jVUZW.js +1 -0
- package/dist/web/assets/{latex-ChNeshUC.js → latex-BPJSSE6j.js} +1 -1
- package/dist/web/assets/{liquid-KNJEBOwW.js → liquid-lv10fo4U.js} +1 -1
- package/dist/web/assets/{lua-D8NNsLbC.js → lua-PNuaIBEF.js} +1 -1
- package/dist/web/assets/{markdown-renderer-DSxSupDq.js → markdown-renderer-B8Kz5rvP.js} +3 -3
- package/dist/web/assets/markdown-renderer-Btync7yg.js +1 -0
- package/dist/web/assets/{marko-HGo5Qtz4.js → marko-Du3frD6X.js} +1 -1
- package/dist/web/assets/{mdc-omkodQB5.js → mdc-BeDVcFRl.js} +1 -1
- package/dist/web/assets/mobile-bottom-sheet-CzRXE6mY.js +1 -0
- package/dist/web/assets/mobile-explorer-sheet-CDU8xriw.js +1 -0
- package/dist/web/assets/{nextflow-Adq02pv-.js → nextflow-DkcXP71s.js} +1 -1
- package/dist/web/assets/{nginx-ByvWruGD.js → nginx-C5n8N_bt.js} +1 -1
- package/dist/web/assets/{nim-BrMojX4O.js → nim-CIhyfs-A.js} +1 -1
- package/dist/web/assets/notification-store-Bi7VHAy4.js +1 -0
- package/dist/web/assets/{number-overlay-editor-CunvWJnN.js → number-overlay-editor-B8P26qGC.js} +1 -1
- package/dist/web/assets/packet-RMMSAZCW-D79vAFR_.js +1 -0
- package/dist/web/assets/{panel-store-CpKvXNnm.js → panel-store-gbNJvZB_.js} +1 -1
- package/dist/web/assets/{pdf-preview-DEn8amrO.js → pdf-preview-CtZWvujY.js} +1 -1
- package/dist/web/assets/{perl-Bd_0l-b4.js → perl-Cf31sZYE.js} +1 -1
- package/dist/web/assets/{php-D-DLTnWJ.js → php-Dzl_MDIW.js} +1 -1
- package/dist/web/assets/pie-UPGHQEXC-DYUOH68U.js +1 -0
- package/dist/web/assets/{postgres-viewer-CjrM15gz.js → postgres-viewer-fqMUi0xS.js} +3 -3
- package/dist/web/assets/{project-store-D0ALup9Z.js → project-store-D1D1MN03.js} +1 -1
- package/dist/web/assets/{pug-D2aO-l2T.js → pug-CxSYHrB3.js} +1 -1
- package/dist/web/assets/{qml-CyQVzNXS.js → qml-DlEjGhwN.js} +1 -1
- package/dist/web/assets/radar-KQ55EAFF-B1eznNtd.js +1 -0
- package/dist/web/assets/{razor-8tvJXsO3.js → razor-KVt5uW14.js} +1 -1
- package/dist/web/assets/{rst-C4AZyQQl.js → rst-D5BwNTnw.js} +1 -1
- package/dist/web/assets/{ruby-O_z2NTLY.js → ruby-D5fejQwd.js} +1 -1
- package/dist/web/assets/{sas-BtnF4zaV.js → sas-Bj7B1CB2.js} +1 -1
- package/dist/web/assets/{scss-GAyp5euH.js → scss-BB0CUoCL.js} +1 -1
- package/dist/web/assets/settings-store-Bm7uL8VA.js +2 -0
- package/dist/web/assets/settings-tab-DxtBG0DW.js +1 -0
- package/dist/web/assets/{shaderlab-nM5YZwa5.js → shaderlab-BdjEZUMc.js} +1 -1
- package/dist/web/assets/{shellsession-D2OCI4Gv.js → shellsession-Bp1OA1rR.js} +1 -1
- package/dist/web/assets/{soy-DrLNhqfp.js → soy-DlYz3wsC.js} +1 -1
- package/dist/web/assets/sparkles-BvJ1EGWd.js +1 -0
- package/dist/web/assets/{sparql-CsM6F2Yv.js → sparql-CdfII5Jd.js} +1 -1
- package/dist/web/assets/{sql-query-editor-CT5M9PKQ.js → sql-query-editor-D2UxRvG7.js} +1 -1
- package/dist/web/assets/sqlite-viewer-CTBwzT-r.js +1 -0
- package/dist/web/assets/{stata-Bk3cgfXJ.js → stata-BvUA6GUW.js} +1 -1
- package/dist/web/assets/{surrealql-Dkz0DPK8.js → surrealql--DLNbTyI.js} +1 -1
- package/dist/web/assets/{svelte-23si1KV5.js → svelte-CBfSsG4n.js} +1 -1
- package/dist/web/assets/{system-monitor-tab-De3oz9zD.js → system-monitor-tab-Bscf6tzK.js} +1 -1
- package/dist/web/assets/{tab-store-BQmDVh9c.js → tab-store-DnNnaAcE.js} +1 -1
- package/dist/web/assets/{templ-CPxlP4vF.js → templ-Dv8zpKZW.js} +1 -1
- package/dist/web/assets/{terminal-tab-CciNqtMc.js → terminal-tab-CTKU5gaF.js} +3 -3
- package/dist/web/assets/{tex-ugJpgYPc.js → tex-CsrOiqh1.js} +1 -1
- package/dist/web/assets/tool-cards-xstpayaW.js +7 -0
- package/dist/web/assets/treemap-KZPCXAKY-SeI2TxLs.js +1 -0
- package/dist/web/assets/{ts-tags-BovP9TMu.js → ts-tags-3T98k4EJ.js} +1 -1
- package/dist/web/assets/{twig-B7VUm2Ca.js → twig-u6QZ45FH.js} +1 -1
- package/dist/web/assets/{use-blob-url-B3YovXF6.js → use-blob-url-BLRmhlZI.js} +1 -1
- package/dist/web/assets/{use-monaco-theme-k-17kQqf.js → use-monaco-theme-DxYstgYa.js} +1 -1
- package/dist/web/assets/{vendor-mermaid-CYOBtZtD.js → vendor-mermaid-C977xNeE.js} +1 -1
- package/dist/web/assets/{vendor-ui-DMRGWRuc.js → vendor-ui-CXJtBULr.js} +1 -1
- package/dist/web/assets/{video-preview-D-S_YnG6.js → video-preview-cfaoRxG3.js} +1 -1
- package/dist/web/assets/{vue-m75pWowk.js → vue-CxIsxtwn.js} +1 -1
- package/dist/web/assets/{vue-html-odHs7IMn.js → vue-html-BGL5qnuH.js} +1 -1
- package/dist/web/assets/{vue-vine-BbBB3V_G.js → vue-vine-DHPh_gMr.js} +1 -1
- package/dist/web/assets/{xml-BHzIcDWI.js → xml-C-vM2m7J.js} +1 -1
- package/dist/web/assets/{xsl-DTLhN7uu.js → xsl-DvTXNwuO.js} +1 -1
- package/dist/web/index.html +26 -23
- 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/layout/command-palette.tsx +10 -0
- package/src/web/components/layout/mobile-drawer.tsx +11 -2
- package/src/web/components/layout/nav-section-rail.tsx +7 -1
- 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/file-browser-picker.tsx +1 -18
- package/src/web/lib/file-download.ts +6 -4
- package/src/web/lib/fs-api.ts +185 -0
- 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/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/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-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/{abap-BM5PMK0-.js → abap-C1K0-wzc.js} +0 -0
- /package/dist/web/assets/{actionscript-3-CML-MTNy.js → actionscript-3-CJhQVjLA.js} +0 -0
- /package/dist/web/assets/{ada-DzO1tMqV.js → ada-Bt8r9fME.js} +0 -0
- /package/dist/web/assets/{andromeeda-C3nkvRhj.js → andromeeda-DF1I8qi3.js} +0 -0
- /package/dist/web/assets/{apache-Dg7oWQbs.js → apache-CnlleE6P.js} +0 -0
- /package/dist/web/assets/{apex-3O2170Ju.js → apex-j0C6Xmk6.js} +0 -0
- /package/dist/web/assets/{applescript-5AlzBqQO.js → applescript-DO7AnO8U.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-i83AydZj.js} +0 -0
- /package/dist/web/assets/{asciidoc-CIsC4kkq.js → asciidoc-DbBA6-sF.js} +0 -0
- /package/dist/web/assets/{asm-p1-PUkh3.js → asm-B5S47Rgr.js} +0 -0
- /package/dist/web/assets/{aurora-x-BPgGL50S.js → aurora-x-De_IaSKw.js} +0 -0
- /package/dist/web/assets/{awk-Cj93pRL8.js → awk-CfyLHXqS.js} +0 -0
- /package/dist/web/assets/{ayu-dark-C50MYKhw.js → ayu-dark-DTLdZ1Si.js} +0 -0
- /package/dist/web/assets/{ayu-light-DyIp0Idy.js → ayu-light-DTOfkzhu.js} +0 -0
- /package/dist/web/assets/{ayu-mirage-BAdm8pKU.js → ayu-mirage-DsAECCMv.js} +0 -0
- /package/dist/web/assets/{ballerina-DUO9mwyV.js → ballerina-CumGp9A7.js} +0 -0
- /package/dist/web/assets/{bat-Cg8hlSct.js → bat-Ca2SBbYN.js} +0 -0
- /package/dist/web/assets/{beancount-f8f3BBqB.js → beancount-CqyXBwwF.js} +0 -0
- /package/dist/web/assets/{berry-BqVQYZZr.js → berry-yl-kJr_H.js} +0 -0
- /package/dist/web/assets/{bibtex-C_q8ygAM.js → bibtex-DoFFszjV.js} +0 -0
- /package/dist/web/assets/{bicep-DWS-2uWR.js → bicep-CWPi4nfK.js} +0 -0
- /package/dist/web/assets/{bird2-C0pvipi_.js → bird2-DXew-ut0.js} +0 -0
- /package/dist/web/assets/{bot-E9mcVZnr.js → bot-BHXjpQ7x.js} +0 -0
- /package/dist/web/assets/{c-BQFvJ6Ux.js → c-clbySDvd.js} +0 -0
- /package/dist/web/assets/{c3-Da53KeG6.js → c3-B65jvSM1.js} +0 -0
- /package/dist/web/assets/{cadence-B5X2vWm3.js → cadence-Cm8tQTHN.js} +0 -0
- /package/dist/web/assets/{catppuccin-frappe-Dm3rVY3b.js → catppuccin-frappe-DLeGslh_.js} +0 -0
- /package/dist/web/assets/{catppuccin-latte-ZxS-ga_K.js → catppuccin-latte-CR5GeHws.js} +0 -0
- /package/dist/web/assets/{catppuccin-macchiato-B0bAzXh0.js → catppuccin-macchiato-p-XgjxhP.js} +0 -0
- /package/dist/web/assets/{catppuccin-mocha-BeMC5hqi.js → catppuccin-mocha-B8QOAZLl.js} +0 -0
- /package/dist/web/assets/{chevron-right-DeUue2aK.js → chevron-right-CtcHmZg9.js} +0 -0
- /package/dist/web/assets/{circle-question-mark-PaJgx-3d.js → circle-question-mark-EVgisosn.js} +0 -0
- /package/dist/web/assets/{clarity-BRdirlsw.js → clarity-DPulbvKH.js} +0 -0
- /package/dist/web/assets/{clojure-pSLH-KQp.js → clojure-DFuWS20u.js} +0 -0
- /package/dist/web/assets/{cmake-DsZu4U4F.js → cmake-DejCqcu4.js} +0 -0
- /package/dist/web/assets/{code-TekEnDFM.js → code-Dupqw5Fr.js} +0 -0
- /package/dist/web/assets/{codeowners-CtkKTnsH.js → codeowners-BwAyqWIH.js} +0 -0
- /package/dist/web/assets/{codeql-BpP1PDrj.js → codeql-DgSwFjqI.js} +0 -0
- /package/dist/web/assets/{common-lisp-BX1XgHTt.js → common-lisp-CxIVJCtf.js} +0 -0
- /package/dist/web/assets/{coq-D2bX6-8g.js → coq-DLSjSnmf.js} +0 -0
- /package/dist/web/assets/{csharp-DcR97P0D.js → csharp-C1HHzXBp.js} +0 -0
- /package/dist/web/assets/{css-CIjciIIk.js → css-VXsMY--y.js} +0 -0
- /package/dist/web/assets/{csv-DW48Z5d-.js → csv-BEbcJdHz.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-BkDnFqUD.js} +0 -0
- /package/dist/web/assets/{cypher-TVlMMWma.js → cypher-1cMqojSQ.js} +0 -0
- /package/dist/web/assets/{d-CxaScvxf.js → d-BnYjRXtH.js} +0 -0
- /package/dist/web/assets/{dark-plus-sn-FFTT1.js → dark-plus-XfbaH7kZ.js} +0 -0
- /package/dist/web/assets/{dart-BmpakMe-.js → dart-Ca7s-8hK.js} +0 -0
- /package/dist/web/assets/{data-grid-types-BuMHLKXV.js → data-grid-types-DP-ZexEN.js} +0 -0
- /package/dist/web/assets/{database-COYAMZaw.js → database-Dj0DxgtX.js} +0 -0
- /package/dist/web/assets/{dax-B1aRUMXT.js → dax-CmuOw-mY.js} +0 -0
- /package/dist/web/assets/{desktop-C6lD62ar.js → desktop-C2gJ6Ovb.js} +0 -0
- /package/dist/web/assets/{diff-CupGmT1a.js → diff-BCEo1Ej7.js} +0 -0
- /package/dist/web/assets/{dist-Bi4gRyhr.js → dist-CipvJy9w.js} +0 -0
- /package/dist/web/assets/{dist-m_oJqIDk.js → dist-D__U4ZJz.js} +0 -0
- /package/dist/web/assets/{docker-DjDr3TJF.js → docker-DN2jlyAh.js} +0 -0
- /package/dist/web/assets/{dotenv-QeT1SLTv.js → dotenv-NExMY2kS.js} +0 -0
- /package/dist/web/assets/{dracula-yI5kpKp5.js → dracula-B9lbE01y.js} +0 -0
- /package/dist/web/assets/{dracula-soft-CYF6cJce.js → dracula-soft-CMBBfKF_.js} +0 -0
- /package/dist/web/assets/{dream-maker-TBDKiHrV.js → dream-maker-DEyDwFsd.js} +0 -0
- /package/dist/web/assets/{emacs-lisp-B7aZTnJ_.js → emacs-lisp-D_qP2zS6.js} +0 -0
- /package/dist/web/assets/{everforest-dark-CN77B1NJ.js → everforest-dark-DbHa2U-p.js} +0 -0
- /package/dist/web/assets/{everforest-light-CyFxGMIs.js → everforest-light-BISkJchP.js} +0 -0
- /package/dist/web/assets/{fennel-C6ktGUzp.js → fennel-6B22SfJR.js} +0 -0
- /package/dist/web/assets/{file-exclamation-point-DBEC-Kst.js → file-exclamation-point-D-ZORzYB.js} +0 -0
- /package/dist/web/assets/{fish-Vr62C2vP.js → fish-CubIIvJD.js} +0 -0
- /package/dist/web/assets/{fluent-CH3IJUki.js → fluent-DF55zQ09.js} +0 -0
- /package/dist/web/assets/{fortran-free-form-BN7VmmxZ.js → fortran-free-form-DvOu8jAt.js} +0 -0
- /package/dist/web/assets/{gdscript-BYdRNu_P.js → gdscript-DCPxdLpq.js} +0 -0
- /package/dist/web/assets/{gdshader-C2XOq90k.js → gdshader-CTZ381FO.js} +0 -0
- /package/dist/web/assets/{genie-B4hkIXNU.js → genie-C_QDtvgU.js} +0 -0
- /package/dist/web/assets/{gherkin-CW9NH2DW.js → gherkin-BcvK42tF.js} +0 -0
- /package/dist/web/assets/{github-dark-BWCf99kM.js → github-dark-C9afs_4w.js} +0 -0
- /package/dist/web/assets/{github-dark-default-DZ1m_6OP.js → github-dark-default-CKGVrl3Z.js} +0 -0
- /package/dist/web/assets/{github-dark-dimmed-CUXSQ8bS.js → github-dark-dimmed-C3dQZ_LC.js} +0 -0
- /package/dist/web/assets/{github-dark-high-contrast-B6BvU8H-.js → github-dark-high-contrast-DW78QukY.js} +0 -0
- /package/dist/web/assets/{github-light-Ch-E92cy.js → github-light-BnP2dznS.js} +0 -0
- /package/dist/web/assets/{github-light-default-CKFslV7i.js → github-light-default-C2fPZzPG.js} +0 -0
- /package/dist/web/assets/{github-light-high-contrast-DiJVeRlZ.js → github-light-high-contrast-BzIuUFR2.js} +0 -0
- /package/dist/web/assets/{gleam-D4dP7V5M.js → gleam-BpeKwEIO.js} +0 -0
- /package/dist/web/assets/{gn-DuO2D2eW.js → gn-nUb_Mit0.js} +0 -0
- /package/dist/web/assets/{gnuplot-Df_8pFbI.js → gnuplot-D7I0v20p.js} +0 -0
- /package/dist/web/assets/{go-BL16cMJO.js → go-D7Qqska7.js} +0 -0
- /package/dist/web/assets/{groovy-rfHeCRqy.js → groovy-DS-pne5-.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-hard-CLbDN-E3.js → gruvbox-dark-hard-C0W01fsq.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-medium-Df0L0qje.js → gruvbox-dark-medium-Bqo6fQbg.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-soft-DV6TAcen.js → gruvbox-dark-soft-D-XSM_fu.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-hard-CWXUzp9b.js → gruvbox-light-hard-IVAwMJRl.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-medium-BOHMe1-7.js → gruvbox-light-medium-38ATiG7i.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-soft-DrrS2sdV.js → gruvbox-light-soft-DnZr3c_0.js} +0 -0
- /package/dist/web/assets/{haskell-DybKb9uW.js → haskell-uQcywkDH.js} +0 -0
- /package/dist/web/assets/{haxe-DLRwZxmt.js → haxe-c6bI4_GT.js} +0 -0
- /package/dist/web/assets/{hcl-CusCzwOk.js → hcl-CQXTiEXq.js} +0 -0
- /package/dist/web/assets/{hjson-BygWIJ-3.js → hjson-ChETf2bz.js} +0 -0
- /package/dist/web/assets/{hlsl-DCKvuEyZ.js → hlsl-BMC4JGoE.js} +0 -0
- /package/dist/web/assets/{horizon-DSOVAzyH.js → horizon-BusFlhLW.js} +0 -0
- /package/dist/web/assets/{horizon-bright-DKfVjGj_.js → horizon-bright-2oD0Mz7W.js} +0 -0
- /package/dist/web/assets/{houston-Ddm8AgpV.js → houston-DdbbjqFg.js} +0 -0
- /package/dist/web/assets/{hy-DzZa6alE.js → hy-D_SOuqw9.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-BxLOHO5I.js} +0 -0
- /package/dist/web/assets/{ini-CAvrVa5R.js → ini-ox-CFcg0.js} +0 -0
- /package/dist/web/assets/{java-PRMSQp24.js → java-Bzo8yIal.js} +0 -0
- /package/dist/web/assets/{javascript-DQR6GQKs.js → javascript-DHuz9VXn.js} +0 -0
- /package/dist/web/assets/{json-uWUKqOxq.js → json-0_y80FD5.js} +0 -0
- /package/dist/web/assets/{json5-R_LXJXL0.js → json5-CEQ50pGM.js} +0 -0
- /package/dist/web/assets/{jsonc-tC5zvrPO.js → jsonc-CfrLEAKA.js} +0 -0
- /package/dist/web/assets/{jsonl-BAwnWh7U.js → jsonl-fKbrANC2.js} +0 -0
- /package/dist/web/assets/{jsonnet-CPj4HCRm.js → jsonnet-DKbkPaQ2.js} +0 -0
- /package/dist/web/assets/{jssm-B7HswxOM.js → jssm-Dxxx1Hqu.js} +0 -0
- /package/dist/web/assets/{jsx-z4TGELE1.js → jsx-DlJL-Oj3.js} +0 -0
- /package/dist/web/assets/{kanagawa-dragon-BpeLlraV.js → kanagawa-dragon-CwN4SV1c.js} +0 -0
- /package/dist/web/assets/{kanagawa-lotus-CLnIPAGs.js → kanagawa-lotus-BHltayiE.js} +0 -0
- /package/dist/web/assets/{kanagawa-wave-BglPniBb.js → kanagawa-wave-D-KXPjw6.js} +0 -0
- /package/dist/web/assets/{kdl-B6blIqLq.js → kdl-DvtoJdFo.js} +0 -0
- /package/dist/web/assets/{kotlin-DVAEYiwY.js → kotlin-CTS4MLcx.js} +0 -0
- /package/dist/web/assets/{kusto-Ba87977j.js → kusto-HtSE_sKm.js} +0 -0
- /package/dist/web/assets/{laserwave-C-NuzoMp.js → laserwave-DaXZG9ge.js} +0 -0
- /package/dist/web/assets/{lean-CJ_KLkrJ.js → lean-BYtGUfWx.js} +0 -0
- /package/dist/web/assets/{less-Dfn9YC_2.js → less-BoxmU_Mk.js} +0 -0
- /package/dist/web/assets/{lib-DTaN9riD.js → lib-Bpbn00xM.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-Bt5CyUuo.js} +0 -0
- /package/dist/web/assets/{llvm-6UDP3NQb.js → llvm-CSdk9q9V.js} +0 -0
- /package/dist/web/assets/{log-QW3dqr1D.js → log-B-kp_3CL.js} +0 -0
- /package/dist/web/assets/{logo-DUinJheI.js → logo-DdIsXFDO.js} +0 -0
- /package/dist/web/assets/{luau-B1vcBzl8.js → luau-DUBpLlzQ.js} +0 -0
- /package/dist/web/assets/{make-CqNL10gS.js → make-B3JVjoW_.js} +0 -0
- /package/dist/web/assets/{markdown-BQNMNiYS.js → markdown-Seon0vHs.js} +0 -0
- /package/dist/web/assets/{material-theme-rbV8EefR.js → material-theme-Nq-ChX1P.js} +0 -0
- /package/dist/web/assets/{material-theme-darker-BZrskvoo.js → material-theme-darker-CPV4ayXJ.js} +0 -0
- /package/dist/web/assets/{material-theme-lighter-B0CBBjBw.js → material-theme-lighter-0kyjYqEd.js} +0 -0
- /package/dist/web/assets/{material-theme-ocean-CX_CHnQq.js → material-theme-ocean-DGpAp5co.js} +0 -0
- /package/dist/web/assets/{material-theme-palenight-ztiOCPbw.js → material-theme-palenight-B0AUn0NG.js} +0 -0
- /package/dist/web/assets/{matlab-tHQDNC9G.js → matlab-C2waytxs.js} +0 -0
- /package/dist/web/assets/{mdx-D4kZ6c6G.js → mdx-DAis6R6I.js} +0 -0
- /package/dist/web/assets/{mermaid-DlR4U4Pe.js → mermaid-_rnT2YGD.js} +0 -0
- /package/dist/web/assets/{min-dark-BX-xHAD2.js → min-dark-BSSjchUB.js} +0 -0
- /package/dist/web/assets/{min-light-Bxw0_Gqq.js → min-light-DomAFQsa.js} +0 -0
- /package/dist/web/assets/{mipsasm-DYyZAum-.js → mipsasm-bBgtvPyG.js} +0 -0
- /package/dist/web/assets/{mojo-q9wuEYKR.js → mojo-Cp9BayW0.js} +0 -0
- /package/dist/web/assets/{monokai-D9vrJ8JM.js → monokai-CL_9gbmn.js} +0 -0
- /package/dist/web/assets/{moonbit-C9roWxMb.js → moonbit-BS_fcr-P.js} +0 -0
- /package/dist/web/assets/{move-BvhjG2ic.js → move-C79AH45U.js} +0 -0
- /package/dist/web/assets/{narrat-BXGbPwG3.js → narrat-CJ5s3BDf.js} +0 -0
- /package/dist/web/assets/{nextflow-groovy-CMy5-Z4k.js → nextflow-groovy-ZHa4JrPJ.js} +0 -0
- /package/dist/web/assets/{night-owl-CBQBcbmw.js → night-owl-Jkf9zFQK.js} +0 -0
- /package/dist/web/assets/{night-owl-light-CC-brflp.js → night-owl-light-DRPeaG5J.js} +0 -0
- /package/dist/web/assets/{nix-B9EAdptu.js → nix-D2Jet9DN.js} +0 -0
- /package/dist/web/assets/{nord-DxASd4qG.js → nord-CREVs22j.js} +0 -0
- /package/dist/web/assets/{nushell-BsY59i7N.js → nushell-CAiDi_mR.js} +0 -0
- /package/dist/web/assets/{objective-c-CoeQAcnP.js → objective-c-CVkmJkC1.js} +0 -0
- /package/dist/web/assets/{objective-cpp-BysdFvND.js → objective-cpp-DIKDO1Ly.js} +0 -0
- /package/dist/web/assets/{ocaml-CrtQYMMK.js → ocaml-R9qtGQhs.js} +0 -0
- /package/dist/web/assets/{odin-B8pz4gIL.js → odin-DTYNQqVr.js} +0 -0
- /package/dist/web/assets/{one-dark-pro-3sSHqoqQ.js → one-dark-pro-BSLWUJn5.js} +0 -0
- /package/dist/web/assets/{one-light-LUZAvT8g.js → one-light-DbkRXG3t.js} +0 -0
- /package/dist/web/assets/{openscad-DcKia-sV.js → openscad-BlPcSnmT.js} +0 -0
- /package/dist/web/assets/{pascal-zM5SlmgF.js → pascal-B1Y46ZZB.js} +0 -0
- /package/dist/web/assets/{pkl-C_r_zLLt.js → pkl-BhJSJbFv.js} +0 -0
- /package/dist/web/assets/{plastic-B3cRtgKD.js → plastic-BzxsCTpi.js} +0 -0
- /package/dist/web/assets/{plsql-BzyjheXU.js → plsql-DBba7dyP.js} +0 -0
- /package/dist/web/assets/{plus-DcuJuDC3.js → plus-Dm36mgLh.js} +0 -0
- /package/dist/web/assets/{po-Dz4OaDNi.js → po-BvgLlnNo.js} +0 -0
- /package/dist/web/assets/{poimandres-BBbtYwmR.js → poimandres-DUaI_1JP.js} +0 -0
- /package/dist/web/assets/{polar-Che1XC2Z.js → polar-C7uILwJn.js} +0 -0
- /package/dist/web/assets/{postcss-DouzwG4F.js → postcss-BgJlpFdw.js} +0 -0
- /package/dist/web/assets/{powerquery-YOdcTZmd.js → powerquery-Dn1MIpZu.js} +0 -0
- /package/dist/web/assets/{powershell-8ojzYH34.js → powershell-Bo-DYzOS.js} +0 -0
- /package/dist/web/assets/{prisma-DCmjceML.js → prisma-CTLs5IdT.js} +0 -0
- /package/dist/web/assets/{prolog-ByNNz2pB.js → prolog-QRGun3eG.js} +0 -0
- /package/dist/web/assets/{proto-DWMt6Qu1.js → proto-B7PI7apn.js} +0 -0
- /package/dist/web/assets/{puppet-osfKyZp4.js → puppet-CBq0tLrP.js} +0 -0
- /package/dist/web/assets/{purescript-ClIblkvh.js → purescript-DFewOQb2.js} +0 -0
- /package/dist/web/assets/{python-Ctyr4faP.js → python-iWuxKXvT.js} +0 -0
- /package/dist/web/assets/{qmldir-CbNgyhnI.js → qmldir-cXiW3AsF.js} +0 -0
- /package/dist/web/assets/{qss-LbRHzb21.js → qss-DASwAbBL.js} +0 -0
- /package/dist/web/assets/{r-Dvfjp0wc.js → r-TU2eaIPw.js} +0 -0
- /package/dist/web/assets/{racket-DI8v8nzD.js → racket-SkRKqrLY.js} +0 -0
- /package/dist/web/assets/{raku-CX1q9GW0.js → raku-D31lthkZ.js} +0 -0
- /package/dist/web/assets/{react-8hrD3-kv.js → react-DjdYfcEe.js} +0 -0
- /package/dist/web/assets/{red-CSz3QEOw.js → red-CTcQZevb.js} +0 -0
- /package/dist/web/assets/{reg-FMjhUQE-.js → reg-D8YAsTGf.js} +0 -0
- /package/dist/web/assets/{regexp-CCSe1Fc4.js → regexp-CD2uvAWx.js} +0 -0
- /package/dist/web/assets/{rel-Ce0UO-OB.js → rel-CAaI-1fg.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-DR-hJVIj.js} +0 -0
- /package/dist/web/assets/{ron-D0t4kvJG.js → ron-CuUCJ0n4.js} +0 -0
- /package/dist/web/assets/{rose-pine-HzwmOzHM.js → rose-pine-aFHxW48i.js} +0 -0
- /package/dist/web/assets/{rose-pine-dawn-CSp9xAF9.js → rose-pine-dawn-Bqz2sBRY.js} +0 -0
- /package/dist/web/assets/{rose-pine-moon-Cnzr2Cw0.js → rose-pine-moon-wqyh0an4.js} +0 -0
- /package/dist/web/assets/{rosmsg-DEJ8Snbv.js → rosmsg-CI1ywimO.js} +0 -0
- /package/dist/web/assets/{rust-2jBzrC0l.js → rust-p7Imk4G_.js} +0 -0
- /package/dist/web/assets/{sass-DD75Izu_.js → sass-C8JfaqCV.js} +0 -0
- /package/dist/web/assets/{save-BM4p9_9n.js → save-ignW_jk2.js} +0 -0
- /package/dist/web/assets/{scala-DpBKROIu.js → scala-DNxCW1By.js} +0 -0
- /package/dist/web/assets/{scheme-DM2jzwUK.js → scheme-BYuUPJoL.js} +0 -0
- /package/dist/web/assets/{sdbl-C34Cg6e7.js → sdbl-DbPeJPUr.js} +0 -0
- /package/dist/web/assets/{shellscript-DxfzKSgU.js → shellscript-DMiHlR3q.js} +0 -0
- /package/dist/web/assets/{shield-check-C3qYSU8a.js → shield-check-BSA9P3nI.js} +0 -0
- /package/dist/web/assets/{shield-off-D5tU7gQT.js → shield-off-Cb9J9x_n.js} +0 -0
- /package/dist/web/assets/{slack-dark-DIIV3bpD.js → slack-dark-C4xGcQXI.js} +0 -0
- /package/dist/web/assets/{slack-ochin-BTkxWjEP.js → slack-ochin-BzgRt_wm.js} +0 -0
- /package/dist/web/assets/{smalltalk-Bm0qg6SA.js → smalltalk-LDeL9PYH.js} +0 -0
- /package/dist/web/assets/{snazzy-light-ClAuuhn4.js → snazzy-light-CXE7a5j_.js} +0 -0
- /package/dist/web/assets/{solarized-dark-93sEWmYT.js → solarized-dark-DgUhY74x.js} +0 -0
- /package/dist/web/assets/{solarized-light-C74FvgcO.js → solarized-light-DD9C-H-9.js} +0 -0
- /package/dist/web/assets/{solidity-H9fSjkQP.js → solidity-CAbDB4D4.js} +0 -0
- /package/dist/web/assets/{splunk-CDTN2iAG.js → splunk-kQREPfIc.js} +0 -0
- /package/dist/web/assets/{sql-CoqObhgo.js → sql-CBzeNAD8.js} +0 -0
- /package/dist/web/assets/{ssh-config-C3Wv6qRp.js → ssh-config-L1euxrsM.js} +0 -0
- /package/dist/web/assets/{stylus-D0uTFXCy.js → stylus-DvBnYniP.js} +0 -0
- /package/dist/web/assets/{swift-DtMMgvRI.js → swift-DF5NDQp6.js} +0 -0
- /package/dist/web/assets/{synthwave-84-DAroyZBu.js → synthwave-84-DbBhB_Hq.js} +0 -0
- /package/dist/web/assets/{system-verilog-Bj7X_lq4.js → system-verilog-D8s2ZW_k.js} +0 -0
- /package/dist/web/assets/{systemd-CH8ApCUs.js → systemd-a5-XZJsG.js} +0 -0
- /package/dist/web/assets/{table-Di8pv2Y_.js → table-4P8kS7sg.js} +0 -0
- /package/dist/web/assets/{talonscript-70lVC3cw.js → talonscript-BLa0URTW.js} +0 -0
- /package/dist/web/assets/{tasl-WQR8VslV.js → tasl-DIoV4XcC.js} +0 -0
- /package/dist/web/assets/{tcl-caTR_frn.js → tcl-ztYAdXfQ.js} +0 -0
- /package/dist/web/assets/{terraform-CVRZ4SZ4.js → terraform-DvpCCiOZ.js} +0 -0
- /package/dist/web/assets/{tokyo-night-CDyT1jCI.js → tokyo-night-BkHlYUv0.js} +0 -0
- /package/dist/web/assets/{toml-B7D2mIDU.js → toml-acIrxos6.js} +0 -0
- /package/dist/web/assets/{tsv-2kdn5dqL.js → tsv-BJB5EVCT.js} +0 -0
- /package/dist/web/assets/{tsx-DJfG3fzg.js → tsx-B1MmXGT3.js} +0 -0
- /package/dist/web/assets/{turtle-DaQac_UT.js → turtle-DFceTiqd.js} +0 -0
- /package/dist/web/assets/{typescript-BLfAIchl.js → typescript-Dz9wU5Y1.js} +0 -0
- /package/dist/web/assets/{typespec-ymBaOi_c.js → typespec-CPuYZijj.js} +0 -0
- /package/dist/web/assets/{typst-DkvUYMT-.js → typst-CfsCIYv8.js} +0 -0
- /package/dist/web/assets/{use-is-mobile-BcuCjH19.js → use-is-mobile-K1a38BwI.js} +0 -0
- /package/dist/web/assets/{utils-E0yyGxXt.js → utils-B9H0d3Fh.js} +0 -0
- /package/dist/web/assets/{v-D4997FGs.js → v-CGo0jcXA.js} +0 -0
- /package/dist/web/assets/{vala-AFg9DmKE.js → vala-AsSR0hrb.js} +0 -0
- /package/dist/web/assets/{vb-DNUJ9fOP.js → vb-C7tvbXv1.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--a33ND-p.js} +0 -0
- /package/dist/web/assets/{vesper-DCZnFmY0.js → vesper-B_Z0h4pn.js} +0 -0
- /package/dist/web/assets/{vhdl-DoK4PjhE.js → vhdl-CWL5dZPN.js} +0 -0
- /package/dist/web/assets/{viml-D6EKG66q.js → viml-BglmT14Y.js} +0 -0
- /package/dist/web/assets/{vitesse-black-C6KILWdF.js → vitesse-black-RKFsSqQI.js} +0 -0
- /package/dist/web/assets/{vitesse-dark-BuSr6OT5.js → vitesse-dark-D7zDWNmc.js} +0 -0
- /package/dist/web/assets/{vitesse-light-M-eYyOoe.js → vitesse-light-MAp87W5b.js} +0 -0
- /package/dist/web/assets/{vyper-CwSN64Jn.js → vyper-CgCBYg1G.js} +0 -0
- /package/dist/web/assets/{wasm-BhQEkzil.js → wasm-BOG7ZeTT.js} +0 -0
- /package/dist/web/assets/{wasm-CVlf9Jwb.js → wasm-Bd258nS1.js} +0 -0
- /package/dist/web/assets/{wenyan-BC7irgM3.js → wenyan-DpShPKXZ.js} +0 -0
- /package/dist/web/assets/{wgsl-W-3UHMDO.js → wgsl-BakcCblK.js} +0 -0
- /package/dist/web/assets/{wikitext-T1DoFcm8.js → wikitext-NroRWVAO.js} +0 -0
- /package/dist/web/assets/{wit-DIrx_eCI.js → wit-B5CM2pg6.js} +0 -0
- /package/dist/web/assets/{wolfram-Cza4b443.js → wolfram-7MTFBJpR.js} +0 -0
- /package/dist/web/assets/{x-BsFLg-T8.js → x-BtzqXv1z.js} +0 -0
- /package/dist/web/assets/{yaml-DOZ10aZe.js → yaml-Bwr01G2K.js} +0 -0
- /package/dist/web/assets/{zenscript-DDAXkseY.js → zenscript-Dj8GATvi.js} +0 -0
- /package/dist/web/assets/{zig-CrzwMpQE.js → zig-LAE-fbS-.js} +0 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The mobile toolbar's overflow sheet: view mode, sort, hidden files, permanent delete and
|
|
3
|
+
* properties of the *current folder* (not the selection — that's what the row/tile menu and
|
|
4
|
+
* the selection-mode toolbar are for).
|
|
5
|
+
*
|
|
6
|
+
* "Delete permanently" lives here rather than on the one-tap selection toolbar so the
|
|
7
|
+
* irreversible action never sits as close at hand as Trash.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
Columns3, Eye, EyeOff, Info, LayoutGrid, List, Trash2,
|
|
12
|
+
} from "lucide-react";
|
|
13
|
+
import type { FsEntry } from "@/lib/fs-api";
|
|
14
|
+
import { BottomSheet, BottomSheetItem, BottomSheetSeparator, BottomSheetSubLabel } from "@/components/ui/mobile-bottom-sheet";
|
|
15
|
+
import type { ExplorerActions } from "../actions/use-explorer-actions";
|
|
16
|
+
import { useExplorerStore, type ExplorerSlice, type ViewMode } from "../explorer-store";
|
|
17
|
+
import { SORT_KEY_OPTIONS } from "../sort-options";
|
|
18
|
+
import { activeEntries } from "../use-explorer-keyboard";
|
|
19
|
+
import { AVAILABLE_VIEW_MODES } from "../views/explorer-view-registry";
|
|
20
|
+
import { cn } from "@/lib/utils";
|
|
21
|
+
|
|
22
|
+
const VIEW_ICON: Record<ViewMode, typeof List> = { list: List, icons: LayoutGrid, columns: Columns3 };
|
|
23
|
+
const VIEW_LABEL: Record<ViewMode, string> = { list: "List", icons: "Icons", columns: "Columns" };
|
|
24
|
+
|
|
25
|
+
/** The current folder, in the shape `showProperties`/`PropertiesDialog` need — the dialog
|
|
26
|
+
* fetches everything else itself from `/api/fs/stat`. */
|
|
27
|
+
function currentFolderEntry(slice: ExplorerSlice): FsEntry {
|
|
28
|
+
const name = slice.breadcrumbs[slice.breadcrumbs.length - 1]?.name ?? slice.path;
|
|
29
|
+
return { name, path: slice.path, type: "directory", kind: "directory", modified: "" };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface MobileMoreSheetProps {
|
|
33
|
+
open: boolean;
|
|
34
|
+
onClose(): void;
|
|
35
|
+
slice: ExplorerSlice;
|
|
36
|
+
actions: ExplorerActions;
|
|
37
|
+
entries: FsEntry[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function MobileMoreSheet({ open, onClose, slice, actions, entries }: MobileMoreSheetProps) {
|
|
41
|
+
const viewMode = useExplorerStore((s) => s.viewMode);
|
|
42
|
+
const sort = useExplorerStore((s) => s.sort);
|
|
43
|
+
const showHidden = useExplorerStore((s) => s.showHidden);
|
|
44
|
+
const setPrefs = useExplorerStore((s) => s.setPrefs);
|
|
45
|
+
const targets = activeEntries(slice, entries);
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<BottomSheet open={open} onClose={onClose} className="p-2">
|
|
49
|
+
<BottomSheetSubLabel>View</BottomSheetSubLabel>
|
|
50
|
+
<div className="flex gap-1 px-2 pb-2">
|
|
51
|
+
{AVAILABLE_VIEW_MODES.map((mode) => {
|
|
52
|
+
const Icon = VIEW_ICON[mode];
|
|
53
|
+
return (
|
|
54
|
+
<button
|
|
55
|
+
key={mode}
|
|
56
|
+
type="button"
|
|
57
|
+
aria-pressed={viewMode === mode}
|
|
58
|
+
onClick={() => { setPrefs({ viewMode: mode }); onClose(); }}
|
|
59
|
+
className={cn(
|
|
60
|
+
"flex flex-1 flex-col items-center gap-1 rounded-lg py-2 text-xs active:bg-surface-elevated",
|
|
61
|
+
viewMode === mode ? "bg-accent-wash text-primary" : "text-text-2",
|
|
62
|
+
)}
|
|
63
|
+
>
|
|
64
|
+
<Icon className="size-4" />
|
|
65
|
+
{VIEW_LABEL[mode]}
|
|
66
|
+
</button>
|
|
67
|
+
);
|
|
68
|
+
})}
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<BottomSheetSeparator />
|
|
72
|
+
<BottomSheetSubLabel>Sort by</BottomSheetSubLabel>
|
|
73
|
+
<div className="flex flex-wrap gap-1 px-2 pb-2">
|
|
74
|
+
{SORT_KEY_OPTIONS.map(({ key, label }) => (
|
|
75
|
+
<button
|
|
76
|
+
key={key}
|
|
77
|
+
type="button"
|
|
78
|
+
aria-pressed={sort.key === key}
|
|
79
|
+
onClick={() => {
|
|
80
|
+
setPrefs({ sort: { key, dir: sort.key === key && sort.dir === "asc" ? "desc" : "asc" } });
|
|
81
|
+
onClose();
|
|
82
|
+
}}
|
|
83
|
+
className={cn(
|
|
84
|
+
// min-h-11 (44px) keeps the tap target compliant without inflating the pill's
|
|
85
|
+
// padding to match — inline-flex + items-center re-centers the small text
|
|
86
|
+
// inside the taller box.
|
|
87
|
+
"inline-flex min-h-11 items-center justify-center rounded-full border px-3 py-1.5 text-xs active:bg-surface-elevated",
|
|
88
|
+
sort.key === key ? "border-primary bg-accent-wash text-text" : "border-border text-text-2",
|
|
89
|
+
)}
|
|
90
|
+
>
|
|
91
|
+
{label}{sort.key === key ? (sort.dir === "asc" ? " ↑" : " ↓") : ""}
|
|
92
|
+
</button>
|
|
93
|
+
))}
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<BottomSheetSeparator />
|
|
97
|
+
<BottomSheetItem onClick={() => setPrefs({ showHidden: !showHidden })}>
|
|
98
|
+
{showHidden ? <Eye className="size-4" /> : <EyeOff className="size-4" />}
|
|
99
|
+
{showHidden ? "Hide hidden files" : "Show hidden files"}
|
|
100
|
+
</BottomSheetItem>
|
|
101
|
+
|
|
102
|
+
<BottomSheetSeparator />
|
|
103
|
+
<BottomSheetItem
|
|
104
|
+
variant="destructive"
|
|
105
|
+
disabled={targets.length === 0}
|
|
106
|
+
onClick={() => actions.confirmPermanentDelete(targets)}
|
|
107
|
+
>
|
|
108
|
+
<Trash2 className="size-4" />
|
|
109
|
+
Delete permanently{targets.length > 1 ? ` (${targets.length})` : ""}
|
|
110
|
+
</BottomSheetItem>
|
|
111
|
+
<BottomSheetItem onClick={() => actions.showProperties(currentFolderEntry(slice))}>
|
|
112
|
+
<Info className="size-4" />
|
|
113
|
+
Properties of this folder
|
|
114
|
+
</BottomSheetItem>
|
|
115
|
+
</BottomSheet>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Full places list behind the chip strip's "All places" chip: PPM pins, OS pinned folders,
|
|
3
|
+
* known folders and drives, each sectioned exactly like the desktop sidebar — the strip only
|
|
4
|
+
* condenses the *common case*, this sheet is where everything still lives.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { ReactNode } from "react";
|
|
8
|
+
import { HardDrive, Network, Pin, Usb } from "lucide-react";
|
|
9
|
+
import type { Drive, HostInfo } from "../../../../types/system";
|
|
10
|
+
import { BottomSheet } from "@/components/ui/mobile-bottom-sheet";
|
|
11
|
+
import { useExplorerPinsStore } from "../explorer-pins-store";
|
|
12
|
+
import { FileTypeIcon } from "../icons/file-type-icon";
|
|
13
|
+
import type { SkinVocab } from "../skins/skin-types";
|
|
14
|
+
import { cn } from "@/lib/utils";
|
|
15
|
+
|
|
16
|
+
const ICON_CLASS = "size-4 shrink-0 text-text-subtle";
|
|
17
|
+
const DRIVE_ICON: Record<Drive["kind"], typeof HardDrive> = {
|
|
18
|
+
fixed: HardDrive,
|
|
19
|
+
removable: Usb,
|
|
20
|
+
network: Network,
|
|
21
|
+
unknown: HardDrive,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/** Folder-type place rows draw the current skin's folder glyph, same as the desktop sidebar. */
|
|
25
|
+
function folderIcon(): ReactNode {
|
|
26
|
+
return <FileTypeIcon name="" kind="directory" className={ICON_CLASS} />;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function Row({
|
|
30
|
+
label, path, active, icon, onNavigate,
|
|
31
|
+
}: {
|
|
32
|
+
label: string; path: string; active: boolean; icon: ReactNode; onNavigate(path: string): void;
|
|
33
|
+
}) {
|
|
34
|
+
return (
|
|
35
|
+
<button
|
|
36
|
+
type="button"
|
|
37
|
+
title={path}
|
|
38
|
+
onClick={() => onNavigate(path)}
|
|
39
|
+
className={cn(
|
|
40
|
+
"flex min-h-[44px] w-full items-center gap-2.5 rounded-lg px-3 text-left text-sm active:bg-surface-elevated",
|
|
41
|
+
active ? "bg-accent-wash text-text" : "text-text-2",
|
|
42
|
+
)}
|
|
43
|
+
>
|
|
44
|
+
{icon}
|
|
45
|
+
<span className="truncate">{label}</span>
|
|
46
|
+
</button>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function Section({ title, children }: { title: string; children: ReactNode }) {
|
|
51
|
+
return (
|
|
52
|
+
<div className="pb-2">
|
|
53
|
+
<p className="px-3 pb-1 pt-3 text-[11px] font-semibold uppercase tracking-wide text-text-subtle">{title}</p>
|
|
54
|
+
{children}
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface MobilePlacesSheetProps {
|
|
60
|
+
open: boolean;
|
|
61
|
+
onClose(): void;
|
|
62
|
+
host: HostInfo | null;
|
|
63
|
+
currentPath: string;
|
|
64
|
+
vocab: SkinVocab;
|
|
65
|
+
onNavigate(path: string): void;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function MobilePlacesSheet({ open, onClose, host, currentPath, vocab, onNavigate }: MobilePlacesSheetProps) {
|
|
69
|
+
const pins = useExplorerPinsStore((s) => s.pins);
|
|
70
|
+
const isActive = (path: string) => path === currentPath;
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<BottomSheet open={open} onClose={onClose} zIndex={65} className="p-1">
|
|
74
|
+
<div className="max-h-[70vh] overflow-y-auto px-1 pb-2">
|
|
75
|
+
{pins.length > 0 && (
|
|
76
|
+
<Section title="Pinned">
|
|
77
|
+
{pins.map((pin) => (
|
|
78
|
+
<Row key={pin.path} label={pin.name} path={pin.path} icon={<Pin className={ICON_CLASS} />} active={isActive(pin.path)} onNavigate={onNavigate} />
|
|
79
|
+
))}
|
|
80
|
+
</Section>
|
|
81
|
+
)}
|
|
82
|
+
|
|
83
|
+
{host && host.pinned.length > 0 && (
|
|
84
|
+
<Section title={vocab.pinned}>
|
|
85
|
+
{host.pinned.map((entry) => (
|
|
86
|
+
<Row key={entry.path} label={entry.name} path={entry.path} icon={folderIcon()} active={isActive(entry.path)} onNavigate={onNavigate} />
|
|
87
|
+
))}
|
|
88
|
+
</Section>
|
|
89
|
+
)}
|
|
90
|
+
|
|
91
|
+
{host && (
|
|
92
|
+
<Section title={vocab.known}>
|
|
93
|
+
<Row label={vocab.home} path={host.homedir} icon={folderIcon()} active={isActive(host.homedir)} onNavigate={onNavigate} />
|
|
94
|
+
{host.knownFolders.map((folder) => (
|
|
95
|
+
<Row key={folder.path} label={folder.name} path={folder.path} icon={folderIcon()} active={isActive(folder.path)} onNavigate={onNavigate} />
|
|
96
|
+
))}
|
|
97
|
+
</Section>
|
|
98
|
+
)}
|
|
99
|
+
|
|
100
|
+
{host && host.drives.length > 0 && (
|
|
101
|
+
<Section title={vocab.drives}>
|
|
102
|
+
{host.drives.map((drive) => {
|
|
103
|
+
const DriveIcon = DRIVE_ICON[drive.kind];
|
|
104
|
+
return (
|
|
105
|
+
<Row
|
|
106
|
+
key={drive.path}
|
|
107
|
+
label={drive.label ? `${drive.label} (${drive.name})` : drive.name}
|
|
108
|
+
path={drive.path}
|
|
109
|
+
icon={<DriveIcon className={ICON_CLASS} />}
|
|
110
|
+
active={isActive(drive.path)}
|
|
111
|
+
onNavigate={onNavigate}
|
|
112
|
+
/>
|
|
113
|
+
);
|
|
114
|
+
})}
|
|
115
|
+
</Section>
|
|
116
|
+
)}
|
|
117
|
+
</div>
|
|
118
|
+
</BottomSheet>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sidebar → horizontally scrollable "places" chip strip on mobile: PPM pins, the host's own
|
|
3
|
+
* pinned folders, Home, and drives — condensed to fit under the title instead of a side
|
|
4
|
+
* column. The full list (known folders included) lives in a "Places" sheet behind the last
|
|
5
|
+
* chip.
|
|
6
|
+
*
|
|
7
|
+
* `touch-pan-x` keeps the browser's native scroll-chaining to the horizontal axis only, so a
|
|
8
|
+
* drag that starts on a chip does not also read as a vertical page scroll.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { useState, type ComponentType } from "react";
|
|
12
|
+
import { HardDrive, MoreHorizontal, Network, Pin, Usb } from "lucide-react";
|
|
13
|
+
import type { Drive, HostInfo } from "../../../../types/system";
|
|
14
|
+
import { cn } from "@/lib/utils";
|
|
15
|
+
import { useExplorerPinsStore } from "../explorer-pins-store";
|
|
16
|
+
import { FileTypeIcon } from "../icons/file-type-icon";
|
|
17
|
+
import type { SkinVocab } from "../skins/skin-types";
|
|
18
|
+
import { MobilePlacesSheet } from "./mobile-places-sheet";
|
|
19
|
+
|
|
20
|
+
function FolderChipIcon({ className }: { className?: string }) {
|
|
21
|
+
return <FileTypeIcon name="" kind="directory" className={className} />;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const DRIVE_ICON: Record<Drive["kind"], ComponentType<{ className?: string }>> = {
|
|
25
|
+
fixed: HardDrive,
|
|
26
|
+
removable: Usb,
|
|
27
|
+
network: Network,
|
|
28
|
+
unknown: HardDrive,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
interface Chip {
|
|
32
|
+
key: string;
|
|
33
|
+
label: string;
|
|
34
|
+
path: string;
|
|
35
|
+
icon: ComponentType<{ className?: string }>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function Chip({ chip, active, onNavigate }: { chip: Chip; active: boolean; onNavigate(path: string): void }) {
|
|
39
|
+
const Icon = chip.icon;
|
|
40
|
+
return (
|
|
41
|
+
<button
|
|
42
|
+
type="button"
|
|
43
|
+
onClick={() => onNavigate(chip.path)}
|
|
44
|
+
title={chip.path}
|
|
45
|
+
className={cn(
|
|
46
|
+
"flex h-11 shrink-0 items-center gap-1.5 rounded-full border px-3 text-[13px] active:bg-surface-elevated",
|
|
47
|
+
active ? "border-primary bg-accent-wash text-text" : "border-border text-text-2",
|
|
48
|
+
)}
|
|
49
|
+
>
|
|
50
|
+
<Icon className="size-4 shrink-0 text-text-subtle" />
|
|
51
|
+
<span className="max-w-[9rem] truncate">{chip.label}</span>
|
|
52
|
+
</button>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface MobilePlacesStripProps {
|
|
57
|
+
host: HostInfo | null;
|
|
58
|
+
currentPath: string;
|
|
59
|
+
onNavigate(path: string): void;
|
|
60
|
+
vocab: SkinVocab;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function MobilePlacesStrip({ host, currentPath, onNavigate, vocab }: MobilePlacesStripProps) {
|
|
64
|
+
const pins = useExplorerPinsStore((s) => s.pins);
|
|
65
|
+
const [placesOpen, setPlacesOpen] = useState(false);
|
|
66
|
+
|
|
67
|
+
const chips: Chip[] = [
|
|
68
|
+
...pins.map((pin): Chip => ({ key: `pin:${pin.path}`, label: pin.name, path: pin.path, icon: Pin })),
|
|
69
|
+
...(host ? [{ key: "home", label: vocab.home, path: host.homedir, icon: FolderChipIcon }] : []),
|
|
70
|
+
...(host?.pinned.map((p): Chip => ({ key: `os:${p.path}`, label: p.name, path: p.path, icon: FolderChipIcon })) ?? []),
|
|
71
|
+
...(host?.drives.map((d): Chip => ({
|
|
72
|
+
key: `drive:${d.path}`,
|
|
73
|
+
label: d.label ? `${d.label} (${d.name})` : d.name,
|
|
74
|
+
path: d.path,
|
|
75
|
+
icon: DRIVE_ICON[d.kind],
|
|
76
|
+
})) ?? []),
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<>
|
|
81
|
+
<div className="flex shrink-0 items-center gap-1.5 overflow-x-auto touch-pan-x border-b border-border px-2 py-1.5">
|
|
82
|
+
{chips.map((chip) => (
|
|
83
|
+
<Chip key={chip.key} chip={chip} active={chip.path === currentPath} onNavigate={onNavigate} />
|
|
84
|
+
))}
|
|
85
|
+
<button
|
|
86
|
+
type="button"
|
|
87
|
+
aria-label="All places"
|
|
88
|
+
title="All places"
|
|
89
|
+
onClick={() => setPlacesOpen(true)}
|
|
90
|
+
className="flex h-11 shrink-0 items-center gap-1 rounded-full border border-border px-3 text-[13px] text-text-2 active:bg-surface-elevated"
|
|
91
|
+
>
|
|
92
|
+
<MoreHorizontal className="size-4" />
|
|
93
|
+
</button>
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<MobilePlacesSheet
|
|
97
|
+
open={placesOpen}
|
|
98
|
+
onClose={() => setPlacesOpen(false)}
|
|
99
|
+
host={host}
|
|
100
|
+
currentPath={currentPath}
|
|
101
|
+
vocab={vocab}
|
|
102
|
+
onNavigate={(path) => {
|
|
103
|
+
setPlacesOpen(false);
|
|
104
|
+
onNavigate(path);
|
|
105
|
+
}}
|
|
106
|
+
/>
|
|
107
|
+
</>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a single tap does on the mobile sheet: there is no double-click on touch, so a
|
|
3
|
+
* directory or a PPM-viewable file must open on the first tap. A file with nothing to open
|
|
4
|
+
* it in has no useful "open" action, so a tap on it surfaces the same actions sheet a
|
|
5
|
+
* long-press would — never a silent no-op.
|
|
6
|
+
*
|
|
7
|
+
* Pure so the three-way decision (directory / viewable file / opaque file) is unit-testable
|
|
8
|
+
* without mounting a row.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { canOpenInPpm } from "../can-open-in-ppm";
|
|
12
|
+
|
|
13
|
+
export type MobileTapAction = "open" | "sheet";
|
|
14
|
+
|
|
15
|
+
/** `entry` only needs the two fields the decision actually reads. */
|
|
16
|
+
export function mobileTapAction(entry: { type: "file" | "directory"; name: string }): MobileTapAction {
|
|
17
|
+
if (entry.type === "directory") return "open";
|
|
18
|
+
return canOpenInPpm(entry.name) ? "open" : "sheet";
|
|
19
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared tap/select-mode decision for every row-level component (List, Icons — Column view
|
|
3
|
+
* keeps its own simpler single-select tap, see `column-view-column.tsx`), so List and Icons
|
|
4
|
+
* branch touch behavior identically instead of two near-copies of the same decision.
|
|
5
|
+
*
|
|
6
|
+
* Must be called from the component actually rendered *inside* the row's own
|
|
7
|
+
* `<ContextMenu>` (i.e. the JSX returned inside `<ContextMenuTrigger>`, not the component
|
|
8
|
+
* that renders `<ContextMenu>` itself) — that is the only position where
|
|
9
|
+
* `useContext(BottomSheetCtx)` resolves to that row's own sheet state instead of the
|
|
10
|
+
* default no-op value.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { useContext } from "react";
|
|
14
|
+
import { BottomSheetCtx } from "@/components/ui/mobile-bottom-sheet";
|
|
15
|
+
import { useIsMobile } from "@/hooks/use-is-mobile";
|
|
16
|
+
import type { FsEntry } from "@/lib/fs-api";
|
|
17
|
+
import type { ExplorerActions } from "../actions/use-explorer-actions";
|
|
18
|
+
import type { EntrySelection } from "../views/use-entry-selection";
|
|
19
|
+
import { useMobileExplorerOpenState } from "../use-explorer-open-state";
|
|
20
|
+
import { mobileTapAction } from "./mobile-tap-action";
|
|
21
|
+
|
|
22
|
+
export interface MobileRowTap {
|
|
23
|
+
/** True only inside the mobile sheet — desktop rows keep their existing click behavior. */
|
|
24
|
+
isMobile: boolean;
|
|
25
|
+
selectMode: boolean;
|
|
26
|
+
/** Call from the row's onClick; returns true once it has handled the tap (mobile branch). */
|
|
27
|
+
handleTap(entry: FsEntry, selected: boolean): boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function useMobileRowTap(actions: ExplorerActions, selection: EntrySelection): MobileRowTap {
|
|
31
|
+
const isMobile = useIsMobile();
|
|
32
|
+
const selectMode = useMobileExplorerOpenState((s) => s.selectMode);
|
|
33
|
+
const { setOpen } = useContext(BottomSheetCtx);
|
|
34
|
+
|
|
35
|
+
const handleTap = (entry: FsEntry, selected: boolean): boolean => {
|
|
36
|
+
if (!isMobile) return false;
|
|
37
|
+
if (selectMode) {
|
|
38
|
+
// Toggle membership like a ctrl-click — a plain tap in Select mode never replaces
|
|
39
|
+
// the whole selection with just this one row.
|
|
40
|
+
selection.onRowClick(entry.path, { shiftKey: false, ctrlKey: true, metaKey: false });
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
if (!selected) selection.selectOnly(entry.path);
|
|
44
|
+
if (mobileTapAction(entry) === "open") actions.openEntry(entry);
|
|
45
|
+
else setOpen(true); // No viewer for this file — surface the same sheet long-press would.
|
|
46
|
+
return true;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
return { isMobile, selectMode, handleTap };
|
|
50
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one way anything outside this folder opens a file-explorer window.
|
|
3
|
+
*
|
|
4
|
+
* Callers (command palette, nav rail, project-tree context menu) should not have to know
|
|
5
|
+
* about window kinds or payload keys, and passing no path should land somewhere sensible
|
|
6
|
+
* rather than at a hardcoded "/" that does not exist on Windows.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { useWindowStore } from "@/components/floating-window/window-store";
|
|
10
|
+
import { isMobileDevice } from "@/hooks/use-is-mobile";
|
|
11
|
+
import { cachedHomedir, getHostInfo } from "./use-host-info";
|
|
12
|
+
import { MOBILE_EXPLORER_WINDOW_ID, openMobileExplorer } from "./use-explorer-open-state";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Open an explorer at `path`, or at the host's home directory when omitted — a floating
|
|
16
|
+
* window on desktop, the full-screen sheet on mobile. Returns the window id (desktop),
|
|
17
|
+
* `MOBILE_EXPLORER_WINDOW_ID` (mobile), or null when the home lookup failed and no path
|
|
18
|
+
* was given.
|
|
19
|
+
*/
|
|
20
|
+
export async function openExplorer(path?: string): Promise<string | null> {
|
|
21
|
+
if (isMobileDevice()) {
|
|
22
|
+
await openMobileExplorer(path);
|
|
23
|
+
return MOBILE_EXPLORER_WINDOW_ID;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let target = path ?? cachedHomedir();
|
|
27
|
+
if (!target) {
|
|
28
|
+
try {
|
|
29
|
+
target = (await getHostInfo()).homedir;
|
|
30
|
+
} catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return useWindowStore.getState().open("explorer", { path: target });
|
|
35
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Properties for one entry, read fresh from `/api/fs/stat` rather than from the listing:
|
|
3
|
+
* the listing has no permissions, no symlink target and no child count, and it may be
|
|
4
|
+
* seconds old.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { useEffect, useState } from "react";
|
|
8
|
+
import { Loader2 } from "lucide-react";
|
|
9
|
+
import { fsApi, type FsEntry, type FsStatResult } from "@/lib/fs-api";
|
|
10
|
+
import { Button } from "@/components/ui/button";
|
|
11
|
+
import { ExplorerModalShell } from "./explorer-modal-shell";
|
|
12
|
+
import { dirnameOf, formatDateTime, formatMode, formatSize } from "./format-file-meta";
|
|
13
|
+
|
|
14
|
+
const KIND_LABEL: Record<FsStatResult["kind"], string> = {
|
|
15
|
+
file: "File",
|
|
16
|
+
directory: "Folder",
|
|
17
|
+
symlink: "Symbolic link",
|
|
18
|
+
unknown: "Unknown",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
function Row({ label, value }: { label: string; value: string }) {
|
|
22
|
+
if (!value) return null;
|
|
23
|
+
return (
|
|
24
|
+
<div className="grid grid-cols-[7rem_1fr] gap-2 py-1 text-sm">
|
|
25
|
+
<span className="text-text-2">{label}</span>
|
|
26
|
+
<span className="break-all text-text">{value}</span>
|
|
27
|
+
</div>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface PropertiesDialogProps {
|
|
32
|
+
entry: FsEntry;
|
|
33
|
+
platform: string | undefined;
|
|
34
|
+
sep: string;
|
|
35
|
+
onClose(): void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function PropertiesDialog({ entry, platform, sep, onClose }: PropertiesDialogProps) {
|
|
39
|
+
const [stat, setStat] = useState<FsStatResult | null>(null);
|
|
40
|
+
const [error, setError] = useState<string | null>(null);
|
|
41
|
+
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
let alive = true;
|
|
44
|
+
const controller = new AbortController();
|
|
45
|
+
setStat(null);
|
|
46
|
+
setError(null);
|
|
47
|
+
fsApi
|
|
48
|
+
.stat(entry.path, controller.signal)
|
|
49
|
+
.then((result) => { if (alive) setStat(result); })
|
|
50
|
+
.catch((e: unknown) => {
|
|
51
|
+
if (alive) setError(e instanceof Error ? e.message : "Could not read properties");
|
|
52
|
+
});
|
|
53
|
+
return () => { alive = false; controller.abort(); };
|
|
54
|
+
}, [entry.path]);
|
|
55
|
+
|
|
56
|
+
const sizeLabel = stat
|
|
57
|
+
? stat.kind === "directory"
|
|
58
|
+
? `${stat.childCount ?? 0}${stat.truncated ? "+" : ""} item${stat.childCount === 1 ? "" : "s"}`
|
|
59
|
+
: `${formatSize(stat.size)} (${stat.size.toLocaleString()} bytes)`
|
|
60
|
+
: "";
|
|
61
|
+
|
|
62
|
+
// Windows does not encode write permission in the mode bits, so the read-only flag is
|
|
63
|
+
// the meaningful answer there and the octal is the meaningful one everywhere else.
|
|
64
|
+
const permissions = stat
|
|
65
|
+
? platform === "win32"
|
|
66
|
+
? (stat.readonly ? "Read-only" : "Read / write")
|
|
67
|
+
: `${formatMode(stat.mode)}${stat.readonly ? " (read-only)" : ""}`
|
|
68
|
+
: "";
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<ExplorerModalShell
|
|
72
|
+
open
|
|
73
|
+
onClose={onClose}
|
|
74
|
+
title={entry.name}
|
|
75
|
+
description="Properties"
|
|
76
|
+
footer={<Button variant="outline" onClick={onClose}>Close</Button>}
|
|
77
|
+
>
|
|
78
|
+
{error && <p className="py-2 text-sm text-error">{error}</p>}
|
|
79
|
+
{!stat && !error && (
|
|
80
|
+
<div className="flex items-center gap-2 py-4 text-sm text-text-2">
|
|
81
|
+
<Loader2 className="size-4 animate-spin" /> Reading…
|
|
82
|
+
</div>
|
|
83
|
+
)}
|
|
84
|
+
{stat && (
|
|
85
|
+
<div className="divide-y divide-border">
|
|
86
|
+
<Row label="Name" value={stat.name} />
|
|
87
|
+
<Row label="Kind" value={KIND_LABEL[stat.kind]} />
|
|
88
|
+
<Row label="Location" value={dirnameOf(stat.path, sep)} />
|
|
89
|
+
<Row label={stat.kind === "directory" ? "Contains" : "Size"} value={sizeLabel} />
|
|
90
|
+
<Row label="Created" value={formatDateTime(stat.birthtime)} />
|
|
91
|
+
<Row label="Modified" value={formatDateTime(stat.mtime)} />
|
|
92
|
+
<Row label="Permissions" value={permissions} />
|
|
93
|
+
<Row label="Hidden" value={stat.isHidden ? "Yes" : ""} />
|
|
94
|
+
<Row label="Links to" value={stat.target ?? ""} />
|
|
95
|
+
</div>
|
|
96
|
+
)}
|
|
97
|
+
</ExplorerModalShell>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one thing `window-content-registry.ts` needs to import to give explorer windows a
|
|
3
|
+
* skinned titlebar: a plain (non-lazy) component matching `WindowChromeProps` that picks
|
|
4
|
+
* the current skin and delegates to its real chrome. `FloatingWindow` renders `chrome`
|
|
5
|
+
* directly with no `<Suspense>` around it, so this entry point cannot be `lazy()` the way
|
|
6
|
+
* the window body is — it has to resolve synchronously, which is exactly what a small
|
|
7
|
+
* wrapper component (rather than the whole explorer body) affords.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { WindowChromeProps } from "@/components/floating-window/window-chrome-contract";
|
|
11
|
+
import { useExplorerSkin } from "./use-explorer-skin";
|
|
12
|
+
|
|
13
|
+
export function ExplorerSkinChrome(props: WindowChromeProps) {
|
|
14
|
+
const skin = useExplorerSkin();
|
|
15
|
+
const Chrome = skin.chrome;
|
|
16
|
+
return <Chrome {...props} />;
|
|
17
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hand-drawn Finder-flavoured folder glyphs (closed/open) — an original blue-gradient
|
|
3
|
+
* rounded folder, not a copy of Apple's proprietary folder artwork.
|
|
4
|
+
*
|
|
5
|
+
* Gradient ids are per-instance (`useId`) since a folder-heavy listing renders dozens of
|
|
6
|
+
* these at once — a hardcoded id would make every icon after the first reuse the first
|
|
7
|
+
* instance's `<linearGradient>` (SVG `url(#id)` resolves to the first match in the DOM).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { useId, type ComponentProps } from "react";
|
|
11
|
+
|
|
12
|
+
export function MacFolderIcon(props: ComponentProps<"svg">) {
|
|
13
|
+
const gradientId = `mac-folder-${useId()}`;
|
|
14
|
+
return (
|
|
15
|
+
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
|
16
|
+
<defs>
|
|
17
|
+
<linearGradient id={gradientId} x1="0" y1="0" x2="0" y2="1">
|
|
18
|
+
<stop offset="0" stopColor="#7FC1FF" />
|
|
19
|
+
<stop offset="1" stopColor="#3E92E5" />
|
|
20
|
+
</linearGradient>
|
|
21
|
+
</defs>
|
|
22
|
+
<path d="M2.5 8a2 2 0 0 1 2-2h5.2l1.7 2H19.5a2 2 0 0 1 2 2v7.5a2 2 0 0 1-2 2h-15a2 2 0 0 1-2-2V8Z" fill={`url(#${gradientId})`} />
|
|
23
|
+
<path d="M2.5 10.5h19V9.8H4.7A2 2 0 0 0 2.5 10.5Z" fill="#FFFFFF" fillOpacity=".3" />
|
|
24
|
+
</svg>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function MacFolderOpenIcon(props: ComponentProps<"svg">) {
|
|
29
|
+
const gradientId = `mac-folder-open-${useId()}`;
|
|
30
|
+
return (
|
|
31
|
+
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
|
32
|
+
<defs>
|
|
33
|
+
<linearGradient id={gradientId} x1="0" y1="0" x2="0" y2="1">
|
|
34
|
+
<stop offset="0" stopColor="#8FCBFF" />
|
|
35
|
+
<stop offset="1" stopColor="#2F82D9" />
|
|
36
|
+
</linearGradient>
|
|
37
|
+
</defs>
|
|
38
|
+
<path d="M2.5 8a2 2 0 0 1 2-2h5.2l1.7 2H19.5a2 2 0 0 1 2 2v.3H5.9a2 2 0 0 0-1.94 1.51L2.5 17.9V8Z" fill="#7FC1FF" />
|
|
39
|
+
<path d="M2.13 18.36 3.98 11.1A2 2 0 0 1 5.92 9.6H21l-2.16 8a2 2 0 0 1-1.93 1.48H4.06a2 2 0 0 1-1.93-2.52Z" fill={`url(#${gradientId})`} />
|
|
40
|
+
</svg>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hand-drawn Windows-11-flavoured folder glyphs (closed/open) — an original flat-yellow
|
|
3
|
+
* rounded-rect with a tab, not a copy of Microsoft's proprietary folder artwork.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ComponentProps } from "react";
|
|
7
|
+
|
|
8
|
+
export function WindowsFolderIcon(props: ComponentProps<"svg">) {
|
|
9
|
+
return (
|
|
10
|
+
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
|
11
|
+
<path d="M3 7.5a1.5 1.5 0 0 1 1.5-1.5H10l1.6 2H19.5A1.5 1.5 0 0 1 21 9.5v8A1.5 1.5 0 0 1 19.5 19h-15A1.5 1.5 0 0 1 3 17.5v-10Z" fill="#FFC93E" />
|
|
12
|
+
<path d="M3 9.5h18v8A1.5 1.5 0 0 1 19.5 19h-15A1.5 1.5 0 0 1 3 17.5v-8Z" fill="#FFCE49" />
|
|
13
|
+
<path d="M4 9.2h16" stroke="#FFFFFF" strokeOpacity=".35" strokeWidth="1" />
|
|
14
|
+
</svg>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function WindowsFolderOpenIcon(props: ComponentProps<"svg">) {
|
|
19
|
+
return (
|
|
20
|
+
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
|
21
|
+
<path d="M3 7.5a1.5 1.5 0 0 1 1.5-1.5H10l1.6 2H19.5A1.5 1.5 0 0 1 21 9.5v.5H5.2a1.5 1.5 0 0 0-1.45 1.13L2 18.2V7.5Z" fill="#FFC93E" />
|
|
22
|
+
<path d="M2.62 18.66 4.3 12.1A1.5 1.5 0 0 1 5.75 11H21l-1.86 6.63A1.5 1.5 0 0 1 17.7 18.7H4.06a1.5 1.5 0 0 1-1.44-1.9Z" fill="#FFB92E" />
|
|
23
|
+
</svg>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { MacFolderIcon, MacFolderOpenIcon } from "./folder-icon-macos";
|
|
2
|
+
import { MacosWindowChrome } from "./macos-window-chrome";
|
|
3
|
+
import type { ExplorerSkin } from "./skin-types";
|
|
4
|
+
|
|
5
|
+
export const macosSkin: ExplorerSkin = {
|
|
6
|
+
id: "macos",
|
|
7
|
+
// Finder's sidebar shows one "Locations" group in principle, but this sidebar renders
|
|
8
|
+
// known folders and drives as two separate sections — reusing the same title would look
|
|
9
|
+
// like a duplicated header, so drives get their own distinct label.
|
|
10
|
+
vocab: { home: "Home", pinned: "Favorites", known: "Locations", drives: "Devices" },
|
|
11
|
+
FolderIcon: MacFolderIcon,
|
|
12
|
+
FolderOpenIcon: MacFolderOpenIcon,
|
|
13
|
+
chrome: MacosWindowChrome,
|
|
14
|
+
};
|