@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,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finder-flavoured unified titlebar: traffic lights left (grey when unfocused, hover
|
|
3
|
+
* reveals the glyph), title centred. Height matches the shared `TITLEBAR_HEIGHT` contract
|
|
4
|
+
* constant (not the real Finder 38px) so a minimised window's collapsed height agrees with
|
|
5
|
+
* what `FloatingWindow` reserves for it. The toolbar row (back/forward/view-switch/search)
|
|
6
|
+
* is the existing `ExplorerToolbar` in the window body, tinted by `skins.css` — the chrome
|
|
7
|
+
* slot is only the titlebar itself.
|
|
8
|
+
*
|
|
9
|
+
* `data-skin` is set here (not only on the explorer body) because this titlebar is a
|
|
10
|
+
* sibling of the body in the window tree — the `--x-*` vars it reads only resolve on an
|
|
11
|
+
* element that itself carries the attribute.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { Minus, Plus, X } from "lucide-react";
|
|
15
|
+
import { cn } from "@/lib/utils";
|
|
16
|
+
import { TITLEBAR_HEIGHT, type WindowChromeProps } from "@/components/floating-window/window-chrome-contract";
|
|
17
|
+
|
|
18
|
+
/** The traffic-light colours, plus the unfocused grey — the documented hardcoded hexes. */
|
|
19
|
+
const LIGHTS = [
|
|
20
|
+
{ color: "#FF5F57", label: "Close window", Icon: X },
|
|
21
|
+
{ color: "#FEBC2E", label: "Minimize window", Icon: Minus },
|
|
22
|
+
{ color: "#28C840", label: "Maximize window", Icon: Plus },
|
|
23
|
+
] as const;
|
|
24
|
+
|
|
25
|
+
export function MacosWindowChrome({
|
|
26
|
+
title, focused, titlebarProps, onMinimize, onToggleMaximize, onClose,
|
|
27
|
+
}: WindowChromeProps) {
|
|
28
|
+
const { className, style, ...rest } = titlebarProps;
|
|
29
|
+
const actions = [onClose, onMinimize, onToggleMaximize];
|
|
30
|
+
return (
|
|
31
|
+
<div
|
|
32
|
+
{...rest}
|
|
33
|
+
data-skin="macos"
|
|
34
|
+
style={{ height: TITLEBAR_HEIGHT, fontFamily: "var(--x-font)", ...style }}
|
|
35
|
+
className={cn(
|
|
36
|
+
"group/titlebar relative flex items-center shrink-0 rounded-t-[var(--x-radius)] overflow-hidden",
|
|
37
|
+
"bg-[var(--x-titlebar-bg)] border-b border-border",
|
|
38
|
+
"outline-none focus-visible:ring-1 focus-visible:ring-primary focus-visible:ring-inset",
|
|
39
|
+
className,
|
|
40
|
+
)}
|
|
41
|
+
>
|
|
42
|
+
<div className="flex items-center gap-2 pl-3">
|
|
43
|
+
{LIGHTS.map(({ color, label, Icon }, i) => (
|
|
44
|
+
<button
|
|
45
|
+
key={label}
|
|
46
|
+
type="button"
|
|
47
|
+
aria-label={label}
|
|
48
|
+
onClick={actions[i]}
|
|
49
|
+
className="grid size-3 place-items-center rounded-full"
|
|
50
|
+
style={{ backgroundColor: focused ? color : "#8E8E93" }}
|
|
51
|
+
>
|
|
52
|
+
<Icon
|
|
53
|
+
className="size-2 text-black/60 can-hover:opacity-0 can-hover:group-hover/titlebar:opacity-100"
|
|
54
|
+
strokeWidth={3}
|
|
55
|
+
/>
|
|
56
|
+
</button>
|
|
57
|
+
))}
|
|
58
|
+
</div>
|
|
59
|
+
<span
|
|
60
|
+
className={cn(
|
|
61
|
+
"pointer-events-none absolute inset-x-0 truncate text-center text-[13px]",
|
|
62
|
+
focused ? "text-text" : "text-text-2",
|
|
63
|
+
)}
|
|
64
|
+
>
|
|
65
|
+
{title}
|
|
66
|
+
</span>
|
|
67
|
+
</div>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A skin is everything that makes the explorer window look native to a host OS: the
|
|
3
|
+
* titlebar/toolbar chrome, the sidebar section names, and the folder glyph. Views, the
|
|
4
|
+
* toolbar body and the sidebar rows themselves stay skin-agnostic — they read `vocab` and
|
|
5
|
+
* the folder icon slot, nothing else changes.
|
|
6
|
+
*
|
|
7
|
+
* Colours are deliberately not part of this type: skin-specific CSS lives in `skins.css`
|
|
8
|
+
* scoped under `[data-skin]`, reusing PPM's existing semantic tokens (`bg-panel`,
|
|
9
|
+
* `text-text`, …) so both PPM dark and light modes fall out for free instead of needing a
|
|
10
|
+
* second light/dark table here.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { ComponentProps, FC } from "react";
|
|
14
|
+
import type { WindowChrome } from "@/components/floating-window/window-chrome-contract";
|
|
15
|
+
|
|
16
|
+
export type SkinId = "windows" | "macos";
|
|
17
|
+
|
|
18
|
+
/** Sidebar section labels that differ between a Windows and a Mac/Linux file manager. */
|
|
19
|
+
export interface SkinVocab {
|
|
20
|
+
/** "Home" on both, kept explicit in case a future skin wants to drop it. */
|
|
21
|
+
home: string;
|
|
22
|
+
/** "Quick access" (Windows) vs "Favorites" (macOS). */
|
|
23
|
+
pinned: string;
|
|
24
|
+
/** "Folders" (Windows) vs "Locations" (macOS) — the known-folders section. */
|
|
25
|
+
known: string;
|
|
26
|
+
/** "Drives" (Windows) vs "Devices" (macOS) — rendered as its own section, so it needs a
|
|
27
|
+
* label distinct from `known` even though Finder visually groups both under Locations. */
|
|
28
|
+
drives: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type FolderIconComponent = FC<ComponentProps<"svg">>;
|
|
32
|
+
|
|
33
|
+
export interface ExplorerSkin {
|
|
34
|
+
id: SkinId;
|
|
35
|
+
vocab: SkinVocab;
|
|
36
|
+
FolderIcon: FolderIconComponent;
|
|
37
|
+
FolderOpenIcon: FolderIconComponent;
|
|
38
|
+
/** Window titlebar/toolbar chrome for this skin. */
|
|
39
|
+
chrome: WindowChrome;
|
|
40
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* OS Explorer skin tokens, scoped to `[data-skin]` set on the explorer root
|
|
3
|
+
* (`ExplorerBody`). Colours stay derived from PPM's existing semantic tokens
|
|
4
|
+
* (`--panel-2`, `--border`, `--accent-2`, …) via `color-mix()` rather than a second
|
|
5
|
+
* light/dark table, so both PPM themes stay legible without duplicating that logic here.
|
|
6
|
+
* Layout metrics (radius, font stack) are the only skin-owned constants.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
[data-skin="windows"] {
|
|
10
|
+
--x-radius: 8px;
|
|
11
|
+
--x-font: "Segoe UI Variable Text", "Segoe UI", system-ui, -apple-system, sans-serif;
|
|
12
|
+
--x-titlebar-bg: var(--panel-2);
|
|
13
|
+
--x-toolbar-bg: var(--panel-2);
|
|
14
|
+
/* A flat "Mica-ish" wash: a touch lighter than the base panel, same on any theme. */
|
|
15
|
+
--x-sidebar-bg: color-mix(in srgb, var(--panel-2) 90%, white 10%);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
[data-skin="macos"] {
|
|
19
|
+
--x-radius: 10px;
|
|
20
|
+
--x-font: -apple-system, "SF Pro Text", "SF Pro", system-ui, sans-serif;
|
|
21
|
+
--x-titlebar-bg: var(--panel-2);
|
|
22
|
+
--x-toolbar-bg: var(--panel-2);
|
|
23
|
+
/* Finder's sidebar reads a hair cooler than its content pane. */
|
|
24
|
+
--x-sidebar-bg: color-mix(in srgb, var(--panel-2) 85%, var(--accent-2) 6%);
|
|
25
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves which skin an explorer window should wear: the user's Settings override when
|
|
3
|
+
* set, otherwise the host `platform` — Linux gets the macOS look since there is no single
|
|
4
|
+
* dominant Linux file-manager chrome to imitate (same call the plan made for pinned
|
|
5
|
+
* folders and known folders).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { useSettingsStore } from "@/stores/settings-store";
|
|
9
|
+
import { useHostInfo } from "../use-host-info";
|
|
10
|
+
import { macosSkin } from "./macos-skin";
|
|
11
|
+
import type { ExplorerSkin } from "./skin-types";
|
|
12
|
+
import { windowsSkin } from "./windows-skin";
|
|
13
|
+
|
|
14
|
+
export function useExplorerSkin(): ExplorerSkin {
|
|
15
|
+
const override = useSettingsStore((s) => s.explorerSkin);
|
|
16
|
+
const { host } = useHostInfo();
|
|
17
|
+
|
|
18
|
+
if (override === "windows") return windowsSkin;
|
|
19
|
+
if (override === "macos") return macosSkin;
|
|
20
|
+
return host?.platform === "win32" ? windowsSkin : macosSkin;
|
|
21
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { WindowsFolderIcon, WindowsFolderOpenIcon } from "./folder-icon-windows";
|
|
2
|
+
import { WindowsWindowChrome } from "./windows-window-chrome";
|
|
3
|
+
import type { ExplorerSkin } from "./skin-types";
|
|
4
|
+
|
|
5
|
+
export const windowsSkin: ExplorerSkin = {
|
|
6
|
+
id: "windows",
|
|
7
|
+
vocab: { home: "Home", pinned: "Quick access", known: "Folders", drives: "Drives" },
|
|
8
|
+
FolderIcon: WindowsFolderIcon,
|
|
9
|
+
FolderOpenIcon: WindowsFolderOpenIcon,
|
|
10
|
+
chrome: WindowsWindowChrome,
|
|
11
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Windows 11 File Explorer-flavoured titlebar: window icon + title left, caption buttons
|
|
3
|
+
* right (46px wide, close hover red). Height matches the shared `TITLEBAR_HEIGHT` contract
|
|
4
|
+
* constant (not the real Win11 32px) so a minimised window's collapsed height agrees with
|
|
5
|
+
* what `FloatingWindow` reserves for it. The toolbar row (back/forward/breadcrumb/search) is
|
|
6
|
+
* the existing `ExplorerToolbar` in the window body, tinted by `skins.css` — the chrome slot
|
|
7
|
+
* is only the titlebar itself.
|
|
8
|
+
*
|
|
9
|
+
* `data-skin` is set here (not only on the explorer body) because this titlebar is a
|
|
10
|
+
* sibling of the body in the window tree — the `--x-*` vars it reads (`--x-titlebar-bg`,
|
|
11
|
+
* `--x-radius`, `--x-font`) only resolve on an element that itself carries the attribute.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { Minus, Square, Copy, X } from "lucide-react";
|
|
15
|
+
import { cn } from "@/lib/utils";
|
|
16
|
+
import { TITLEBAR_HEIGHT, type WindowChromeProps } from "@/components/floating-window/window-chrome-contract";
|
|
17
|
+
import { WindowsFolderIcon } from "./folder-icon-windows";
|
|
18
|
+
|
|
19
|
+
/** Windows metric: 46px wide caption buttons, full titlebar height. */
|
|
20
|
+
const CAPTION_BUTTON =
|
|
21
|
+
"grid place-items-center w-[46px] self-stretch text-text-2 can-hover:hover:bg-surface-elevated can-hover:hover:text-text transition-colors";
|
|
22
|
+
/** The one documented hardcoded hex: Windows' close-button hover red. */
|
|
23
|
+
const CLOSE_BUTTON = cn(CAPTION_BUTTON, "can-hover:hover:!bg-[#c42b1c] can-hover:hover:!text-white");
|
|
24
|
+
|
|
25
|
+
export function WindowsWindowChrome({
|
|
26
|
+
title, state, focused, titlebarProps, onMinimize, onToggleMaximize, onClose,
|
|
27
|
+
}: WindowChromeProps) {
|
|
28
|
+
const { className, style, ...rest } = titlebarProps;
|
|
29
|
+
return (
|
|
30
|
+
<div
|
|
31
|
+
{...rest}
|
|
32
|
+
data-skin="windows"
|
|
33
|
+
style={{ height: TITLEBAR_HEIGHT, fontFamily: "var(--x-font)", ...style }}
|
|
34
|
+
className={cn(
|
|
35
|
+
"flex items-stretch shrink-0 rounded-t-[var(--x-radius)] overflow-hidden",
|
|
36
|
+
"bg-[var(--x-titlebar-bg)] border-b border-border",
|
|
37
|
+
"outline-none focus-visible:ring-1 focus-visible:ring-primary focus-visible:ring-inset",
|
|
38
|
+
className,
|
|
39
|
+
)}
|
|
40
|
+
>
|
|
41
|
+
<div className="flex flex-1 min-w-0 items-center gap-2 px-2.5 cursor-default">
|
|
42
|
+
<WindowsFolderIcon className="size-4 shrink-0" />
|
|
43
|
+
<span className={cn("truncate text-[12px]", focused ? "text-text" : "text-text-2")}>{title}</span>
|
|
44
|
+
</div>
|
|
45
|
+
<button type="button" aria-label="Minimize window" className={CAPTION_BUTTON} onClick={onMinimize}>
|
|
46
|
+
<Minus className="size-3.5" />
|
|
47
|
+
</button>
|
|
48
|
+
<button type="button" aria-label={state === "maximized" ? "Restore window" : "Maximize window"} className={CAPTION_BUTTON} onClick={onToggleMaximize}>
|
|
49
|
+
{state === "maximized" ? <Copy className="size-3" /> : <Square className="size-3" />}
|
|
50
|
+
</button>
|
|
51
|
+
<button type="button" aria-label="Close window" className={CLOSE_BUTTON} onClick={onClose}>
|
|
52
|
+
<X className="size-3.5" />
|
|
53
|
+
</button>
|
|
54
|
+
</div>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The visible row order: the filter text applied first, then the sort.
|
|
3
|
+
*
|
|
4
|
+
* Directories always come before files regardless of the sort column, which is what every
|
|
5
|
+
* desktop file manager does and what keyboard navigation assumes.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { FsEntry } from "@/lib/fs-api";
|
|
9
|
+
import { extensionOf } from "./can-open-in-ppm";
|
|
10
|
+
import type { SortDir, SortKey } from "./explorer-store";
|
|
11
|
+
|
|
12
|
+
/** Locale-aware, digit-aware name compare so "file10" sorts after "file9". */
|
|
13
|
+
const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: "base" });
|
|
14
|
+
|
|
15
|
+
function compareBy(key: SortKey, a: FsEntry, b: FsEntry): number {
|
|
16
|
+
switch (key) {
|
|
17
|
+
case "size":
|
|
18
|
+
return (a.size ?? 0) - (b.size ?? 0);
|
|
19
|
+
case "modified":
|
|
20
|
+
return new Date(a.modified).getTime() - new Date(b.modified).getTime();
|
|
21
|
+
case "kind": {
|
|
22
|
+
const byExt = collator.compare(extensionOf(a.name), extensionOf(b.name));
|
|
23
|
+
return byExt !== 0 ? byExt : collator.compare(a.name, b.name);
|
|
24
|
+
}
|
|
25
|
+
case "name":
|
|
26
|
+
return collator.compare(a.name, b.name);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function sortAndFilterEntries(
|
|
31
|
+
entries: FsEntry[],
|
|
32
|
+
filter: string,
|
|
33
|
+
sort: { key: SortKey; dir: SortDir },
|
|
34
|
+
): FsEntry[] {
|
|
35
|
+
const needle = filter.trim().toLowerCase();
|
|
36
|
+
const visible = needle ? entries.filter((e) => e.name.toLowerCase().includes(needle)) : entries;
|
|
37
|
+
const sign = sort.dir === "asc" ? 1 : -1;
|
|
38
|
+
|
|
39
|
+
return [...visible].sort((a, b) => {
|
|
40
|
+
const aDir = a.type === "directory";
|
|
41
|
+
const bDir = b.type === "directory";
|
|
42
|
+
if (aDir !== bDir) return aDir ? -1 : 1;
|
|
43
|
+
const result = compareBy(sort.key, a, b);
|
|
44
|
+
// Equal keys fall back to the name so the order never depends on readdir sequence.
|
|
45
|
+
return (result !== 0 ? result : collator.compare(a.name, b.name)) * sign;
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Total bytes of the given entries; directories contribute nothing (size unknown). */
|
|
50
|
+
export function totalSize(entries: FsEntry[]): number {
|
|
51
|
+
return entries.reduce((sum, e) => sum + (e.type === "file" ? (e.size ?? 0) : 0), 0);
|
|
52
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared sort vocabulary: the four sortable keys and the two directions, with the labels
|
|
3
|
+
* shown to the user. The toolbar sort menu, the background context menu's "Sort by"
|
|
4
|
+
* submenu, the List view's column headers and the mobile More sheet all read from here so
|
|
5
|
+
* the four surfaces can never say something different about what "Kind" or "Descending"
|
|
6
|
+
* means.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { SortDir, SortKey } from "./explorer-store";
|
|
10
|
+
|
|
11
|
+
export const SORT_KEY_OPTIONS: readonly { key: SortKey; label: string }[] = [
|
|
12
|
+
{ key: "name", label: "Name" },
|
|
13
|
+
{ key: "size", label: "Size" },
|
|
14
|
+
{ key: "modified", label: "Modified" },
|
|
15
|
+
{ key: "kind", label: "Kind" },
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
export const SORT_DIR_OPTIONS: readonly { dir: SortDir; label: string }[] = [
|
|
19
|
+
{ dir: "asc", label: "Ascending" },
|
|
20
|
+
{ dir: "desc", label: "Descending" },
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
export function sortKeyLabel(key: SortKey): string {
|
|
24
|
+
return SORT_KEY_OPTIONS.find((option) => option.key === key)?.label ?? key;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function sortDirLabel(dir: SortDir): string {
|
|
28
|
+
return SORT_DIR_OPTIONS.find((option) => option.dir === dir)?.label ?? dir;
|
|
29
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared square icon-button styling for the window toolbar and any trigger that sits
|
|
3
|
+
* alongside it (e.g. the sort menu button) — one class builder keeps their hit-box
|
|
4
|
+
* behaviour identical instead of two toolbars drifting apart.
|
|
5
|
+
*
|
|
6
|
+
* The visible box stays `size-7` (28px) on every pointer type — only the tap-registering
|
|
7
|
+
* area grows to the 44px minimum on a coarse pointer, via an invisible expanded `::before`
|
|
8
|
+
* (a click anywhere in it still hits this element; nothing about the button's look moves).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { cn } from "@/lib/utils";
|
|
12
|
+
|
|
13
|
+
export function toolbarButtonClass(coarse: boolean): string {
|
|
14
|
+
return cn(
|
|
15
|
+
"relative flex size-7 shrink-0 items-center justify-center rounded text-text-subtle can-hover:hover:bg-surface-elevated can-hover:hover:text-text disabled:opacity-30",
|
|
16
|
+
coarse && "before:absolute before:-inset-2 before:content-['']",
|
|
17
|
+
);
|
|
18
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Long-press → context menu on touch screens that are wide enough to get the desktop
|
|
3
|
+
* layout (an iPad is ≥ 768 px, so `useIsMobile` is false and the bottom-sheet path never
|
|
4
|
+
* runs). Radix opens its own menu on long-press but only after 700 ms; the design rules
|
|
5
|
+
* ask for 400 ms, so the press is timed here and delivered as a synthetic `contextmenu`
|
|
6
|
+
* event, which is exactly what the trigger already listens for.
|
|
7
|
+
*
|
|
8
|
+
* Attached only when the primary pointer is coarse — a mouse keeps plain right-click.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { useCallback, useEffect, useRef, useState, type MouseEvent, type TouchEvent } from "react";
|
|
12
|
+
|
|
13
|
+
const LONG_PRESS_MS = 400;
|
|
14
|
+
/** Finger travel that turns a press into a scroll instead. */
|
|
15
|
+
const MOVE_TOLERANCE_PX = 10;
|
|
16
|
+
|
|
17
|
+
export function usePrefersCoarsePointer(): boolean {
|
|
18
|
+
const [coarse, setCoarse] = useState(
|
|
19
|
+
() => typeof window !== "undefined" && window.matchMedia?.("(pointer: coarse)").matches === true,
|
|
20
|
+
);
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
const query = window.matchMedia?.("(pointer: coarse)");
|
|
23
|
+
if (!query) return;
|
|
24
|
+
const update = () => setCoarse(query.matches);
|
|
25
|
+
query.addEventListener("change", update);
|
|
26
|
+
return () => query.removeEventListener("change", update);
|
|
27
|
+
}, []);
|
|
28
|
+
return coarse;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface LongPressHandlers {
|
|
32
|
+
onTouchStart?(event: TouchEvent): void;
|
|
33
|
+
onTouchMove?(event: TouchEvent): void;
|
|
34
|
+
onTouchEnd?(event: TouchEvent): void;
|
|
35
|
+
onContextMenu?(event: MouseEvent): void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Handlers to spread on a row. `onBeforeOpen` runs first so the menu acts on the pressed
|
|
40
|
+
* row rather than whatever was selected before.
|
|
41
|
+
*/
|
|
42
|
+
export function useCoarseLongPress(onBeforeOpen?: () => void): LongPressHandlers {
|
|
43
|
+
const coarse = usePrefersCoarsePointer();
|
|
44
|
+
const timer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
45
|
+
const origin = useRef<{ x: number; y: number } | null>(null);
|
|
46
|
+
const fired = useRef(false);
|
|
47
|
+
|
|
48
|
+
const cancel = useCallback(() => {
|
|
49
|
+
if (timer.current) clearTimeout(timer.current);
|
|
50
|
+
timer.current = null;
|
|
51
|
+
}, []);
|
|
52
|
+
|
|
53
|
+
useEffect(() => cancel, [cancel]);
|
|
54
|
+
|
|
55
|
+
const start = useCallback(
|
|
56
|
+
(event: TouchEvent) => {
|
|
57
|
+
const touch = event.touches[0];
|
|
58
|
+
if (!touch) return;
|
|
59
|
+
const target = event.currentTarget as HTMLElement;
|
|
60
|
+
origin.current = { x: touch.clientX, y: touch.clientY };
|
|
61
|
+
fired.current = false;
|
|
62
|
+
cancel();
|
|
63
|
+
timer.current = setTimeout(() => {
|
|
64
|
+
fired.current = true;
|
|
65
|
+
onBeforeOpen?.();
|
|
66
|
+
target.dispatchEvent(
|
|
67
|
+
new MouseEvent("contextmenu", {
|
|
68
|
+
bubbles: true,
|
|
69
|
+
cancelable: true,
|
|
70
|
+
clientX: touch.clientX,
|
|
71
|
+
clientY: touch.clientY,
|
|
72
|
+
}),
|
|
73
|
+
);
|
|
74
|
+
}, LONG_PRESS_MS);
|
|
75
|
+
},
|
|
76
|
+
[cancel, onBeforeOpen],
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const move = useCallback(
|
|
80
|
+
(event: TouchEvent) => {
|
|
81
|
+
const touch = event.touches[0];
|
|
82
|
+
if (!touch || !origin.current) return;
|
|
83
|
+
const moved =
|
|
84
|
+
Math.abs(touch.clientX - origin.current.x) > MOVE_TOLERANCE_PX ||
|
|
85
|
+
Math.abs(touch.clientY - origin.current.y) > MOVE_TOLERANCE_PX;
|
|
86
|
+
if (moved) cancel();
|
|
87
|
+
},
|
|
88
|
+
[cancel],
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
if (!coarse) return {};
|
|
92
|
+
return {
|
|
93
|
+
onTouchStart: start,
|
|
94
|
+
onTouchMove: move,
|
|
95
|
+
onTouchEnd: cancel,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keyboard behaviour for an explorer view container, shared by every view mode.
|
|
3
|
+
*
|
|
4
|
+
* Bound to the scrolling element rather than the window so two open explorers never both
|
|
5
|
+
* react to the same key, and so PPM's global shortcuts keep working when focus is
|
|
6
|
+
* elsewhere.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { useCallback } from "react";
|
|
10
|
+
import type { KeyboardEvent } from "react";
|
|
11
|
+
import type { FsEntry } from "@/lib/fs-api";
|
|
12
|
+
import type { ExplorerActions } from "./actions/use-explorer-actions";
|
|
13
|
+
import type { ExplorerSlice } from "./explorer-store";
|
|
14
|
+
import type { ExplorerNavigation } from "./use-explorer-navigation";
|
|
15
|
+
import type { EntrySelection } from "./views/use-entry-selection";
|
|
16
|
+
|
|
17
|
+
export interface ExplorerKeyboardOptions {
|
|
18
|
+
/** Undefined for the one frame before the window has a slice. */
|
|
19
|
+
slice: ExplorerSlice | undefined;
|
|
20
|
+
/** Rows in visible order. */
|
|
21
|
+
entries: FsEntry[];
|
|
22
|
+
actions: ExplorerActions;
|
|
23
|
+
selection: EntrySelection;
|
|
24
|
+
nav: ExplorerNavigation;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Entries the keyboard acts on: the selection, or the cursor row when nothing is selected. */
|
|
28
|
+
export function activeEntries(slice: ExplorerSlice, entries: FsEntry[]): FsEntry[] {
|
|
29
|
+
if (slice.selection.size > 0) return entries.filter((e) => slice.selection.has(e.path));
|
|
30
|
+
const cursor = entries.find((e) => e.path === slice.anchor);
|
|
31
|
+
return cursor ? [cursor] : [];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function useExplorerKeyboard({
|
|
35
|
+
slice, entries, actions, selection, nav,
|
|
36
|
+
}: ExplorerKeyboardOptions): (event: KeyboardEvent) => void {
|
|
37
|
+
return useCallback(
|
|
38
|
+
(event: KeyboardEvent) => {
|
|
39
|
+
if (!slice) return;
|
|
40
|
+
// The inline rename field and the filter box own their own keys.
|
|
41
|
+
const target = event.target as HTMLElement;
|
|
42
|
+
if (target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable) return;
|
|
43
|
+
|
|
44
|
+
const mod = event.ctrlKey || event.metaKey;
|
|
45
|
+
const targets = activeEntries(slice, entries);
|
|
46
|
+
|
|
47
|
+
if (mod) {
|
|
48
|
+
switch (event.key.toLowerCase()) {
|
|
49
|
+
case "a":
|
|
50
|
+
event.preventDefault();
|
|
51
|
+
selection.selectAll();
|
|
52
|
+
return;
|
|
53
|
+
case "c":
|
|
54
|
+
event.preventDefault();
|
|
55
|
+
actions.copy(targets);
|
|
56
|
+
return;
|
|
57
|
+
case "x":
|
|
58
|
+
event.preventDefault();
|
|
59
|
+
actions.cut(targets);
|
|
60
|
+
return;
|
|
61
|
+
case "v":
|
|
62
|
+
event.preventDefault();
|
|
63
|
+
actions.paste();
|
|
64
|
+
return;
|
|
65
|
+
default:
|
|
66
|
+
return; // leave every other accelerator to the app
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
switch (event.key) {
|
|
71
|
+
case "ArrowDown":
|
|
72
|
+
event.preventDefault();
|
|
73
|
+
selection.moveTo(1, event.shiftKey);
|
|
74
|
+
return;
|
|
75
|
+
case "ArrowUp":
|
|
76
|
+
event.preventDefault();
|
|
77
|
+
selection.moveTo(-1, event.shiftKey);
|
|
78
|
+
return;
|
|
79
|
+
case "Home":
|
|
80
|
+
event.preventDefault();
|
|
81
|
+
if (entries[0]) selection.selectOnly(entries[0].path);
|
|
82
|
+
return;
|
|
83
|
+
case "End":
|
|
84
|
+
event.preventDefault();
|
|
85
|
+
if (entries.length > 0) selection.selectOnly(entries[entries.length - 1]!.path);
|
|
86
|
+
return;
|
|
87
|
+
case "ArrowLeft":
|
|
88
|
+
if (event.altKey) { event.preventDefault(); nav.back(); }
|
|
89
|
+
return;
|
|
90
|
+
case "ArrowRight":
|
|
91
|
+
if (event.altKey) { event.preventDefault(); nav.forward(); }
|
|
92
|
+
return;
|
|
93
|
+
case "Backspace":
|
|
94
|
+
event.preventDefault();
|
|
95
|
+
nav.back();
|
|
96
|
+
return;
|
|
97
|
+
case "Enter":
|
|
98
|
+
event.preventDefault();
|
|
99
|
+
if (targets.length === 1) actions.openEntry(targets[0]!);
|
|
100
|
+
return;
|
|
101
|
+
case "F2":
|
|
102
|
+
event.preventDefault();
|
|
103
|
+
if (targets.length === 1) actions.startRename(targets[0]!);
|
|
104
|
+
return;
|
|
105
|
+
case "Delete":
|
|
106
|
+
event.preventDefault();
|
|
107
|
+
if (targets.length === 0) return;
|
|
108
|
+
if (event.shiftKey) actions.confirmPermanentDelete(targets);
|
|
109
|
+
else actions.trash(targets);
|
|
110
|
+
return;
|
|
111
|
+
case "Escape":
|
|
112
|
+
selection.clear();
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Type-ahead: a single printable character with no modifier jumps to a name.
|
|
117
|
+
if (event.key.length === 1 && !event.altKey) {
|
|
118
|
+
event.preventDefault();
|
|
119
|
+
selection.typeAhead(event.key);
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
[slice, entries, actions, selection, nav],
|
|
123
|
+
);
|
|
124
|
+
}
|