@hienlh/ppm 0.17.57 → 0.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.release-run.sh +5 -0
- package/CHANGELOG.md +24 -0
- package/CLAUDE.md +4 -0
- package/assets/skills/ppm/SKILL.md +1 -1
- package/assets/skills/ppm/references/http-api.md +16 -2
- package/bun.lock +3 -0
- package/dist/web/assets/{ai-resource-editor-CXWQcx3_.js → ai-resource-editor-Bmk4P0YS.js} +1 -1
- package/dist/web/assets/{angular-html-D4YJUMLP.js → angular-html-C-Kjr43S.js} +1 -1
- package/dist/web/assets/{angular-ts-CUvq2h3X.js → angular-ts-CaDDbZqm.js} +1 -1
- package/dist/web/assets/api-client-CaRE4HYh.js +1 -0
- package/dist/web/assets/{api-client-COQtP-dB.js → api-client-rTAGdEKo.js} +1 -1
- package/dist/web/assets/{api-settings-Do0Z0IRN.js → api-settings-CAhcNpj5.js} +1 -1
- package/dist/web/assets/api-settings-DBcXfeJ8.js +1 -0
- package/dist/web/assets/{api-themes-DXi8sz-9.js → api-themes-CAjg_Bt9.js} +1 -1
- package/dist/web/assets/{apl-DSANFLx0.js → apl-B1WsQFAb.js} +1 -1
- package/dist/web/assets/architecture-PBZL5I3N-B4J2-O8Q.js +1 -0
- package/dist/web/assets/{astro-CXIotiNS.js → astro-Bdnz7nVp.js} +1 -1
- package/dist/web/assets/{audio-preview-D7jNc8mi.js → audio-preview-CikvnAOI.js} +1 -1
- package/dist/web/assets/{blade-BvrP2gPd.js → blade-B2euh8sS.js} +1 -1
- package/dist/web/assets/{bot-E9mcVZnr.js → bot-nk1k3ELu.js} +1 -1
- package/dist/web/assets/{bsl-CGf6Xuyu.js → bsl-GNeMaxd0.js} +1 -1
- package/dist/web/assets/{cairo-BboZ9qIf.js → cairo-D7G0W7-s.js} +1 -1
- package/dist/web/assets/can-open-in-ppm-BzVXoV-d.js +1 -0
- package/dist/web/assets/chat-tab-B3-KcCn2.js +13 -0
- package/dist/web/assets/chevron-right-BLOtX8Vy.js +1 -0
- package/dist/web/assets/{circle-question-mark-PaJgx-3d.js → circle-question-mark-CENpx4oA.js} +1 -1
- package/dist/web/assets/clock-D493FU_D.js +1 -0
- package/dist/web/assets/{cobol-D39V6ukp.js → cobol-Q6YgysnI.js} +1 -1
- package/dist/web/assets/code-CLF5x7KK.js +1 -0
- package/dist/web/assets/code-editor-BpbZw13T.js +10 -0
- package/dist/web/assets/{coffee-DTtK7kKi.js → coffee-BjufWxVc.js} +1 -1
- package/dist/web/assets/{conflict-editor-CpGcSopK.js → conflict-editor-DC3liBm_.js} +3 -3
- package/dist/web/assets/context-menu-q-RHHAWz.js +1 -0
- package/dist/web/assets/copy-BApI9v7Z.js +1 -0
- package/dist/web/assets/{cpp-CnU9hfBq.js → cpp-CqWU0gCR.js} +1 -1
- package/dist/web/assets/{createLucideIcon-ConhwJnb.js → createLucideIcon-D8pyde1A.js} +1 -1
- package/dist/web/assets/{crystal-DMjCL--0.js → crystal-ylayVajF.js} +1 -1
- package/dist/web/assets/csv-preview-BhCOjgq1.js +2 -0
- package/dist/web/assets/{data-grid-overlay-editor-Bdc80TJ1.js → data-grid-overlay-editor-BzPfsCfd.js} +1 -1
- package/dist/web/assets/{data-grid-types-BuMHLKXV.js → data-grid-types-CUx8cusN.js} +1 -1
- package/dist/web/assets/{database-COYAMZaw.js → database-B9okYLx6.js} +1 -1
- package/dist/web/assets/{database-viewer-DTMmic31.js → database-viewer-Z6uLyZVO.js} +1 -1
- package/dist/web/assets/dialog-CkI7rwrh.js +1 -0
- package/dist/web/assets/diff-viewer-CT1ALWt2.js +4 -0
- package/dist/web/assets/{dist-m_oJqIDk.js → dist-DduBIVkD.js} +1 -1
- package/dist/web/assets/{docx-preview-CR2ZtvNP.js → docx-preview-C_fSjfy_.js} +1 -1
- package/dist/web/assets/{edge-CTom35R_.js → edge-DX4NxQfh.js} +1 -1
- package/dist/web/assets/{edit-diff-preview-D1TEK7Jz.js → edit-diff-preview-DfX-9om9.js} +1 -1
- package/dist/web/assets/{elixir-BAX9HSOu.js → elixir-CSh3NjbV.js} +1 -1
- package/dist/web/assets/{elm-CpD28xsW.js → elm-DBzwFYq9.js} +1 -1
- package/dist/web/assets/{erb-DHX2IIEs.js → erb-yxVOb8aB.js} +1 -1
- package/dist/web/assets/{erlang-B_etLcPG.js → erlang-5XIDkYrM.js} +1 -1
- package/dist/web/assets/{esm-PmlN1RTs.js → esm-DO4el2OJ.js} +1 -1
- package/dist/web/assets/explorer-body-CQCyGEet.js +2 -0
- package/dist/web/assets/explorer-window-content-EtyTAZSh.js +1 -0
- package/dist/web/assets/{extension-webview-BTCFWY2A.js → extension-webview-ePIM2fwM.js} +1 -1
- package/dist/web/assets/{eye-off-Rq1n2-oe.js → eye-off-CUdzgmX8.js} +1 -1
- package/dist/web/assets/{file-exclamation-point-DBEC-Kst.js → file-exclamation-point-COAwgNGw.js} +1 -1
- package/dist/web/assets/{fortran-fixed-form-CpR0cN7O.js → fortran-fixed-form-D4tE5VWY.js} +1 -1
- package/dist/web/assets/{fsharp-DxITxZW5.js → fsharp-By8soj6-.js} +1 -1
- package/dist/web/assets/{gdresource-DJ7IvROZ.js → gdresource-DoSwM7Hv.js} +1 -1
- package/dist/web/assets/{git-commit-sQznNP43.js → git-commit-_ZiciHIx.js} +1 -1
- package/dist/web/assets/git-log-panel-BRWmTzX1.js +1 -0
- package/dist/web/assets/{git-rebase-DkHvejAR.js → git-rebase-C3Y5N9-s.js} +1 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-CtQ3yXN-.js +1 -0
- package/dist/web/assets/glide-data-grid-DCJCz4XB.js +138 -0
- package/dist/web/assets/{glimmer-js-BwJnDfL-.js → glimmer-js-BPBTXVlC.js} +1 -1
- package/dist/web/assets/{glimmer-ts-Coz155XK.js → glimmer-ts-BQSZdy_D.js} +1 -1
- package/dist/web/assets/{glsl-BED7ZmUZ.js → glsl-CbQdxpU-.js} +1 -1
- package/dist/web/assets/{graphql-B_KyE6oz.js → graphql-GMlKikpY.js} +1 -1
- package/dist/web/assets/group-chat-tab-C3UD2bcm.js +1 -0
- package/dist/web/assets/{hack-sxywdIAQ.js → hack-DyshcLec.js} +1 -1
- package/dist/web/assets/{haml-Dts9JlPU.js → haml-rqCqWzsq.js} +1 -1
- package/dist/web/assets/{handlebars-C9HXIMtj.js → handlebars-BCKwSWjR.js} +1 -1
- package/dist/web/assets/{html-2p0RvXi4.js → html-Cg29xPR1.js} +1 -1
- package/dist/web/assets/{html-derivative-BiTPuOba.js → html-derivative-geltIVg8.js} +1 -1
- package/dist/web/assets/{http-CHd2gYPf.js → http-pG7Vp237.js} +1 -1
- package/dist/web/assets/{hurl-boMPgDm9.js → hurl-Di3Uu_Mc.js} +1 -1
- package/dist/web/assets/{hxml-BQvjiuBt.js → hxml-CRy4lbv8.js} +1 -1
- package/dist/web/assets/{image-preview-0SGb7_tH.js → image-preview-CP82lor8.js} +1 -1
- package/dist/web/assets/index-BYetJoXQ.js +52 -0
- package/dist/web/assets/index-D-xwjNyX.css +2 -0
- package/dist/web/assets/info-3K5VOQVL-Cg0mIJn9.js +1 -0
- package/dist/web/assets/{jinja-B-zKiwlp.js → jinja-C8GyVnvd.js} +1 -1
- package/dist/web/assets/{jison-C2vJYjbJ.js → jison-BywnqPn6.js} +1 -1
- package/dist/web/assets/{julia-DJ8MrQPA.js → julia-DTrO3T3B.js} +1 -1
- package/dist/web/assets/{just-BvQHq7VE.js → just-CA3KSHPi.js} +1 -1
- package/dist/web/assets/keybindings-store-yBWU8ZYT.js +1 -0
- package/dist/web/assets/{latex-ChNeshUC.js → latex-BC5xUMpc.js} +1 -1
- package/dist/web/assets/{lib-DTaN9riD.js → lib-BFetOULu.js} +1 -1
- package/dist/web/assets/{liquid-KNJEBOwW.js → liquid-Chd8tSyQ.js} +1 -1
- package/dist/web/assets/{lua-D8NNsLbC.js → lua-DHaEtwLv.js} +1 -1
- package/dist/web/assets/markdown-renderer-B2D4gdok.js +1 -0
- package/dist/web/assets/markdown-renderer-DDgl5zDc.js +3 -0
- package/dist/web/assets/{marko-HGo5Qtz4.js → marko-Desx_bC-.js} +1 -1
- package/dist/web/assets/{mdc-omkodQB5.js → mdc-DahWHc1t.js} +1 -1
- package/dist/web/assets/mobile-bottom-sheet-BQt45q9T.js +1 -0
- package/dist/web/assets/mobile-explorer-sheet-BtxyynR3.js +1 -0
- package/dist/web/assets/{nextflow-Adq02pv-.js → nextflow-3A7lutCA.js} +1 -1
- package/dist/web/assets/{nginx-ByvWruGD.js → nginx-XQda6Hdy.js} +1 -1
- package/dist/web/assets/{nim-BrMojX4O.js → nim-DHqlpwP1.js} +1 -1
- package/dist/web/assets/notification-store-DKw_mGED.js +1 -0
- package/dist/web/assets/{number-overlay-editor-CunvWJnN.js → number-overlay-editor-DgZtViPO.js} +1 -1
- package/dist/web/assets/packet-RMMSAZCW-D79vAFR_.js +1 -0
- package/dist/web/assets/{panel-store-CpKvXNnm.js → panel-store-s6Qx49RK.js} +1 -1
- package/dist/web/assets/{pdf-preview-DEn8amrO.js → pdf-preview-BXDOe7bG.js} +1 -1
- package/dist/web/assets/{perl-Bd_0l-b4.js → perl-M-ncjACb.js} +1 -1
- package/dist/web/assets/{php-D-DLTnWJ.js → php-CKIajrZi.js} +1 -1
- package/dist/web/assets/pie-UPGHQEXC-DYUOH68U.js +1 -0
- package/dist/web/assets/{plus-DcuJuDC3.js → plus-9LatwJMx.js} +1 -1
- package/dist/web/assets/{postgres-viewer-CjrM15gz.js → postgres-viewer-DkHYV33h.js} +3 -3
- package/dist/web/assets/{project-store-D0ALup9Z.js → project-store-DyaEpHVG.js} +1 -1
- package/dist/web/assets/{pug-D2aO-l2T.js → pug-DO0u_gO4.js} +1 -1
- package/dist/web/assets/{qml-CyQVzNXS.js → qml-UEje1PAA.js} +1 -1
- package/dist/web/assets/radar-KQ55EAFF-B1eznNtd.js +1 -0
- package/dist/web/assets/{razor-8tvJXsO3.js → razor-lCs5T1Ib.js} +1 -1
- package/dist/web/assets/{react-8hrD3-kv.js → react-w6wINGnd.js} +1 -1
- package/dist/web/assets/{rst-C4AZyQQl.js → rst-CZX_O0z4.js} +1 -1
- package/dist/web/assets/{ruby-O_z2NTLY.js → ruby-idycVAyE.js} +1 -1
- package/dist/web/assets/{sas-BtnF4zaV.js → sas-o5gTfUwp.js} +1 -1
- package/dist/web/assets/{save-BM4p9_9n.js → save-C4z1p51e.js} +1 -1
- package/dist/web/assets/{scss-GAyp5euH.js → scss-CRTk3iBQ.js} +1 -1
- package/dist/web/assets/settings-store-ChqHdT2V.js +2 -0
- package/dist/web/assets/settings-tab-ESL-6MAA.js +1 -0
- package/dist/web/assets/{shaderlab-nM5YZwa5.js → shaderlab-3e5ln8ci.js} +1 -1
- package/dist/web/assets/{shellsession-D2OCI4Gv.js → shellsession-CGQjQ75I.js} +1 -1
- package/dist/web/assets/{shield-check-C3qYSU8a.js → shield-check-C5laObJv.js} +1 -1
- package/dist/web/assets/{shield-off-D5tU7gQT.js → shield-off-DULFtyLS.js} +1 -1
- package/dist/web/assets/{soy-DrLNhqfp.js → soy-CxY0VwX2.js} +1 -1
- package/dist/web/assets/sparkles-n1hzh65o.js +1 -0
- package/dist/web/assets/{sparql-CsM6F2Yv.js → sparql-CBAGAFuq.js} +1 -1
- package/dist/web/assets/{sql-query-editor-CT5M9PKQ.js → sql-query-editor-CZdZuawP.js} +1 -1
- package/dist/web/assets/sqlite-viewer-Cpq06-nz.js +1 -0
- package/dist/web/assets/{stata-Bk3cgfXJ.js → stata-BoezhOvk.js} +1 -1
- package/dist/web/assets/{surrealql-Dkz0DPK8.js → surrealql-CHEzAhu7.js} +1 -1
- package/dist/web/assets/{svelte-23si1KV5.js → svelte-y6xtu2zX.js} +1 -1
- package/dist/web/assets/{system-monitor-tab-De3oz9zD.js → system-monitor-tab-XR8NXHpv.js} +1 -1
- package/dist/web/assets/{tab-store-BQmDVh9c.js → tab-store-C42sktPo.js} +1 -1
- package/dist/web/assets/{table-Di8pv2Y_.js → table-CHmxuh6M.js} +1 -1
- package/dist/web/assets/{templ-CPxlP4vF.js → templ-DbG2y5yD.js} +1 -1
- package/dist/web/assets/{terminal-tab-CciNqtMc.js → terminal-tab-CCCB-_50.js} +3 -3
- package/dist/web/assets/{tex-ugJpgYPc.js → tex-CeKQhrQG.js} +1 -1
- package/dist/web/assets/tool-cards-CsTs5go2.js +7 -0
- package/dist/web/assets/treemap-KZPCXAKY-SeI2TxLs.js +1 -0
- package/dist/web/assets/{ts-tags-BovP9TMu.js → ts-tags-iKKOXJjG.js} +1 -1
- package/dist/web/assets/{twig-B7VUm2Ca.js → twig-DK8Vrp-G.js} +1 -1
- package/dist/web/assets/{use-blob-url-B3YovXF6.js → use-blob-url-DYtOHK7u.js} +1 -1
- package/dist/web/assets/{use-is-mobile-BcuCjH19.js → use-is-mobile-DnFSkUG2.js} +1 -1
- package/dist/web/assets/use-monaco-theme-CZh7xTJZ.js +1 -0
- package/dist/web/assets/vendor-markdown-Ls5AcU91.js +291 -0
- package/dist/web/assets/{vendor-mermaid-CYOBtZtD.js → vendor-mermaid-C977xNeE.js} +1 -1
- package/dist/web/assets/{vendor-ui-DMRGWRuc.js → vendor-ui-CaeeFO0B.js} +2 -2
- package/dist/web/assets/{video-preview-D-S_YnG6.js → video-preview-KbRcyuAA.js} +1 -1
- package/dist/web/assets/{vue-m75pWowk.js → vue-JkIEObMV.js} +1 -1
- package/dist/web/assets/{vue-html-odHs7IMn.js → vue-html-D8SqnU86.js} +1 -1
- package/dist/web/assets/{vue-vine-BbBB3V_G.js → vue-vine-DJj7N4WD.js} +1 -1
- package/dist/web/assets/x-B1qHmLdR.js +1 -0
- package/dist/web/assets/{xml-BHzIcDWI.js → xml-C-00w9Rv.js} +1 -1
- package/dist/web/assets/{xsl-DTLhN7uu.js → xsl-CVdCwPHV.js} +1 -1
- package/dist/web/index.html +29 -26
- package/dist/web/monacoeditorwork/css.worker.bundle.js +122 -122
- package/dist/web/monacoeditorwork/editor.worker.bundle.js +78 -78
- package/dist/web/monacoeditorwork/html.worker.bundle.js +110 -110
- package/dist/web/monacoeditorwork/json.worker.bundle.js +108 -108
- package/dist/web/monacoeditorwork/ts.worker.bundle.js +81 -81
- package/dist/web/sw.js +1 -1
- package/docs/codebase-summary.md +10 -1
- package/docs/design-guidelines.md +31 -0
- package/docs/project-roadmap.md +10 -0
- package/docs/system-architecture.md +59 -0
- package/package.json +2 -1
- package/src/server/index.ts +8 -0
- package/src/server/routes/files.ts +2 -2
- package/src/server/routes/fs-browse.ts +99 -74
- package/src/server/routes/fs-ops.ts +118 -0
- package/src/server/routes/fs-sqlite.ts +129 -0
- package/src/server/routes/host-info.ts +17 -0
- package/src/server/routes/settings.ts +2 -0
- package/src/services/download-token.service.ts +21 -5
- package/src/services/file.service.ts +8 -12
- package/src/services/fs-browse.service.ts +104 -161
- package/src/services/fs-ops/fs-content-disposition.ts +17 -0
- package/src/services/fs-ops/fs-core-ops.ts +149 -0
- package/src/services/fs-ops/fs-error-response.ts +24 -0
- package/src/services/fs-ops/fs-git-init.service.ts +23 -0
- package/src/services/fs-ops/fs-hidden-names.ts +17 -0
- package/src/services/fs-ops/fs-list-files.service.ts +68 -0
- package/src/services/fs-ops/fs-ops-copy-move.service.ts +60 -0
- package/src/services/fs-ops/fs-ops-mutate.service.ts +59 -0
- package/src/services/fs-ops/fs-ops-read-write.service.ts +70 -0
- package/src/services/fs-ops/fs-ops-stat.service.ts +109 -0
- package/src/services/fs-ops/fs-ops-trash.service.ts +111 -0
- package/src/services/fs-ops/fs-real-path.ts +44 -0
- package/src/services/fs-ops/sql-row-reader.ts +26 -0
- package/src/services/fs-ops/sql-statement-guard.ts +63 -0
- package/src/services/fs-path-guard.service.ts +196 -0
- package/src/services/host-info/apple-bookmark-decoder.ts +97 -0
- package/src/services/host-info/apple-plist-xml-parser.ts +107 -0
- package/src/services/host-info/concurrency-limit.ts +20 -0
- package/src/services/host-info/host-drives.ts +190 -0
- package/src/services/host-info/host-info.service.ts +171 -0
- package/src/services/host-info/host-known-folders.ts +180 -0
- package/src/services/host-info/pinned-linux-bookmarks.ts +89 -0
- package/src/services/host-info/pinned-macos-finder-favorites.ts +175 -0
- package/src/services/host-info/pinned-windows-quick-access.ts +87 -0
- package/src/services/host-info/spawn-runner.ts +41 -0
- package/src/services/sqlite-types.ts +26 -0
- package/src/services/sqlite.service.ts +38 -29
- package/src/services/subagent-transcript-merger.ts +1 -1
- package/src/types/system.ts +50 -0
- package/src/web/app.tsx +19 -3
- package/src/web/components/editor/code-editor.tsx +3 -9
- package/src/web/components/explorer/file-tree.tsx +103 -21
- package/src/web/components/explorer/tree-node-context-menu.tsx +6 -1
- package/src/web/components/explorer/tree-node.tsx +34 -93
- package/src/web/components/explorer/use-file-upload-drag.ts +14 -9
- package/src/web/components/explorer/use-tree-row-dnd.ts +206 -0
- package/src/web/components/floating-window/default-window-chrome.tsx +71 -0
- package/src/web/components/floating-window/floating-window.tsx +167 -0
- package/src/web/components/floating-window/use-window-drag.ts +28 -0
- package/src/web/components/floating-window/use-window-gesture-context.ts +44 -0
- package/src/web/components/floating-window/use-window-keyboard.ts +55 -0
- package/src/web/components/floating-window/use-window-resize.ts +30 -0
- package/src/web/components/floating-window/window-chrome-contract.ts +38 -0
- package/src/web/components/floating-window/window-content-registry.ts +51 -0
- package/src/web/components/floating-window/window-geometry.ts +121 -0
- package/src/web/components/floating-window/window-layer.tsx +92 -0
- package/src/web/components/floating-window/window-persistence.ts +104 -0
- package/src/web/components/floating-window/window-resize-handles.tsx +81 -0
- package/src/web/components/floating-window/window-store-types.ts +20 -0
- package/src/web/components/floating-window/window-store.ts +192 -0
- package/src/web/components/group-chat/group-list.tsx +2 -3
- package/src/web/components/layout/command-palette.tsx +10 -0
- package/src/web/components/layout/mobile-drawer-tab-bar.tsx +3 -10
- package/src/web/components/layout/mobile-drawer.tsx +13 -2
- package/src/web/components/layout/nav-section-rail.tsx +16 -11
- package/src/web/components/os-explorer/actions/explorer-actions-clipboard.ts +186 -0
- package/src/web/components/os-explorer/actions/explorer-actions-mutate.ts +109 -0
- package/src/web/components/os-explorer/actions/explorer-actions-open.ts +67 -0
- package/src/web/components/os-explorer/actions/explorer-actions-transfer-helpers.ts +35 -0
- package/src/web/components/os-explorer/actions/use-explorer-actions.ts +205 -0
- package/src/web/components/os-explorer/can-open-in-ppm.ts +89 -0
- package/src/web/components/os-explorer/dnd/drag-depth-counter.ts +36 -0
- package/src/web/components/os-explorer/dnd/drag-ghost.ts +44 -0
- package/src/web/components/os-explorer/dnd/drop-target-decision.ts +46 -0
- package/src/web/components/os-explorer/dnd/drop-target-style.ts +7 -0
- package/src/web/components/os-explorer/dnd/entry-drag-payload.ts +115 -0
- package/src/web/components/os-explorer/dnd/entry-drag-state.ts +35 -0
- package/src/web/components/os-explorer/dnd/entry-drop-executor.ts +43 -0
- package/src/web/components/os-explorer/dnd/use-drag-auto-scroll.ts +70 -0
- package/src/web/components/os-explorer/dnd/use-drop-transfer.tsx +82 -0
- package/src/web/components/os-explorer/dnd/use-entry-drag-source.ts +73 -0
- package/src/web/components/os-explorer/dnd/use-entry-drop-target.ts +150 -0
- package/src/web/components/os-explorer/dnd/use-entry-row-dnd.ts +66 -0
- package/src/web/components/os-explorer/dnd/use-path-drop-target.ts +30 -0
- package/src/web/components/os-explorer/explorer-body.tsx +153 -0
- package/src/web/components/os-explorer/explorer-breadcrumb.tsx +108 -0
- package/src/web/components/os-explorer/explorer-context-menu.tsx +131 -0
- package/src/web/components/os-explorer/explorer-dialogs.tsx +103 -0
- package/src/web/components/os-explorer/explorer-modal-shell.tsx +60 -0
- package/src/web/components/os-explorer/explorer-pins-store.ts +63 -0
- package/src/web/components/os-explorer/explorer-sidebar.tsx +198 -0
- package/src/web/components/os-explorer/explorer-sort-menu.tsx +68 -0
- package/src/web/components/os-explorer/explorer-status-bar.tsx +35 -0
- package/src/web/components/os-explorer/explorer-store.ts +179 -0
- package/src/web/components/os-explorer/explorer-toolbar.tsx +139 -0
- package/src/web/components/os-explorer/explorer-window-content.tsx +36 -0
- package/src/web/components/os-explorer/format-file-meta.ts +74 -0
- package/src/web/components/os-explorer/icons/file-type-icon-map.ts +106 -0
- package/src/web/components/os-explorer/icons/file-type-icon.tsx +56 -0
- package/src/web/components/os-explorer/icons/thumbnail-fetch-semaphore.ts +44 -0
- package/src/web/components/os-explorer/icons/thumbnail-image.tsx +124 -0
- package/src/web/components/os-explorer/mobile/mobile-bottom-toolbar.tsx +102 -0
- package/src/web/components/os-explorer/mobile/mobile-explorer-sheet.tsx +61 -0
- package/src/web/components/os-explorer/mobile/mobile-explorer-top-bar.tsx +41 -0
- package/src/web/components/os-explorer/mobile/mobile-more-sheet.tsx +117 -0
- package/src/web/components/os-explorer/mobile/mobile-places-sheet.tsx +120 -0
- package/src/web/components/os-explorer/mobile/mobile-places-strip.tsx +109 -0
- package/src/web/components/os-explorer/mobile/mobile-tap-action.ts +19 -0
- package/src/web/components/os-explorer/mobile/use-mobile-row-tap.ts +50 -0
- package/src/web/components/os-explorer/open-explorer.ts +35 -0
- package/src/web/components/os-explorer/properties-dialog.tsx +99 -0
- package/src/web/components/os-explorer/skins/explorer-skin-chrome.tsx +17 -0
- package/src/web/components/os-explorer/skins/folder-icon-macos.tsx +42 -0
- package/src/web/components/os-explorer/skins/folder-icon-windows.tsx +25 -0
- package/src/web/components/os-explorer/skins/macos-skin.ts +14 -0
- package/src/web/components/os-explorer/skins/macos-window-chrome.tsx +69 -0
- package/src/web/components/os-explorer/skins/skin-types.ts +40 -0
- package/src/web/components/os-explorer/skins/skins.css +25 -0
- package/src/web/components/os-explorer/skins/use-explorer-skin.ts +21 -0
- package/src/web/components/os-explorer/skins/windows-skin.ts +11 -0
- package/src/web/components/os-explorer/skins/windows-window-chrome.tsx +56 -0
- package/src/web/components/os-explorer/sort-and-filter-entries.ts +52 -0
- package/src/web/components/os-explorer/sort-options.ts +29 -0
- package/src/web/components/os-explorer/toolbar-icon-button.ts +18 -0
- package/src/web/components/os-explorer/use-coarse-long-press.ts +97 -0
- package/src/web/components/os-explorer/use-explorer-keyboard.ts +124 -0
- package/src/web/components/os-explorer/use-explorer-navigation.ts +155 -0
- package/src/web/components/os-explorer/use-explorer-open-state.ts +67 -0
- package/src/web/components/os-explorer/use-host-info.ts +85 -0
- package/src/web/components/os-explorer/views/column-row.tsx +126 -0
- package/src/web/components/os-explorer/views/column-view-column.tsx +112 -0
- package/src/web/components/os-explorer/views/column-view-mobile.tsx +98 -0
- package/src/web/components/os-explorer/views/column-view-preview.tsx +70 -0
- package/src/web/components/os-explorer/views/column-view.tsx +204 -0
- package/src/web/components/os-explorer/views/compose-interactive-row-props.ts +77 -0
- package/src/web/components/os-explorer/views/explorer-view-registry.tsx +55 -0
- package/src/web/components/os-explorer/views/icons-view-tile.tsx +158 -0
- package/src/web/components/os-explorer/views/icons-view.tsx +188 -0
- package/src/web/components/os-explorer/views/inline-name-input.tsx +60 -0
- package/src/web/components/os-explorer/views/list-row.tsx +162 -0
- package/src/web/components/os-explorer/views/list-view.tsx +177 -0
- package/src/web/components/os-explorer/views/use-column-view-state.ts +158 -0
- package/src/web/components/os-explorer/views/use-entry-selection.ts +178 -0
- package/src/web/components/settings/settings-tab.tsx +29 -2
- package/src/web/components/sqlite/sqlite-viewer.tsx +5 -2
- package/src/web/components/sqlite/use-sqlite.ts +7 -2
- package/src/web/components/ui/adaptive-context-menu.tsx +57 -1
- package/src/web/components/ui/feature-badge.tsx +40 -0
- package/src/web/components/ui/file-browser-picker.tsx +1 -18
- package/src/web/lib/feature-badges.ts +22 -0
- package/src/web/lib/file-download.ts +6 -4
- package/src/web/lib/fs-api.ts +185 -0
- package/src/web/lib/sidebar-tabs/tab-registry.ts +4 -3
- package/src/web/stores/file-store.ts +38 -1
- package/src/web/stores/settings-store.ts +14 -0
- package/src/web/styles/globals.css +1 -0
- package/dist/web/assets/api-client-RtNXvS0P.js +0 -1
- package/dist/web/assets/api-settings-De--HNHp.js +0 -1
- package/dist/web/assets/architecture-PBZL5I3N-DVRYSsQn.js +0 -1
- package/dist/web/assets/chat-tab-C5U7a_rP.js +0 -13
- package/dist/web/assets/chevron-right-DeUue2aK.js +0 -1
- package/dist/web/assets/code-TekEnDFM.js +0 -1
- package/dist/web/assets/code-editor-CqlzLQ7T.js +0 -10
- package/dist/web/assets/context-menu-DIiZdfbE.js +0 -1
- package/dist/web/assets/csv-preview-DD94T9ff.js +0 -2
- package/dist/web/assets/dialog-HHdNInEH.js +0 -1
- package/dist/web/assets/diff-viewer-DyQLYP7J.js +0 -4
- package/dist/web/assets/git-log-panel-CrUgsLY5.js +0 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-CutkUNY_.js +0 -1
- package/dist/web/assets/glide-data-grid-LinTYl0_.js +0 -138
- package/dist/web/assets/group-chat-tab-BqNJW7y2.js +0 -1
- package/dist/web/assets/index-CVJmlKhp.css +0 -2
- package/dist/web/assets/index-I2ik0ZF7.js +0 -50
- package/dist/web/assets/info-3K5VOQVL-BpNjgbj9.js +0 -1
- package/dist/web/assets/keybindings-store-C46tNxJE.js +0 -1
- package/dist/web/assets/markdown-renderer-DSxSupDq.js +0 -3
- package/dist/web/assets/markdown-renderer-xzyhJklS.js +0 -1
- package/dist/web/assets/notification-store-Dek7ClI2.js +0 -1
- package/dist/web/assets/packet-RMMSAZCW-D5IUZ66-.js +0 -1
- package/dist/web/assets/pie-UPGHQEXC-CM29eKDR.js +0 -1
- package/dist/web/assets/radar-KQ55EAFF-QtJGaSGt.js +0 -1
- package/dist/web/assets/settings-store-B3LfpCPa.js +0 -2
- package/dist/web/assets/settings-tab-DIuR1VIc.js +0 -1
- package/dist/web/assets/sparkles-B3DmG8PJ.js +0 -1
- package/dist/web/assets/sqlite-viewer-CEObRQ7n.js +0 -1
- package/dist/web/assets/tool-cards-Bdx04yYD.js +0 -7
- package/dist/web/assets/treemap-KZPCXAKY-CjUqpVnS.js +0 -1
- package/dist/web/assets/use-monaco-theme-k-17kQqf.js +0 -1
- package/dist/web/assets/vendor-markdown-CKTlmXbz.js +0 -291
- package/dist/web/assets/x-BsFLg-T8.js +0 -1
- /package/dist/web/assets/{abap-BM5PMK0-.js → abap-DrMyiWmK.js} +0 -0
- /package/dist/web/assets/{actionscript-3-CML-MTNy.js → actionscript-3-BlSOyi0y.js} +0 -0
- /package/dist/web/assets/{ada-DzO1tMqV.js → ada-B_m96icB.js} +0 -0
- /package/dist/web/assets/{andromeeda-C3nkvRhj.js → andromeeda-Ch3vOiZB.js} +0 -0
- /package/dist/web/assets/{apache-Dg7oWQbs.js → apache-CdsaXc1n.js} +0 -0
- /package/dist/web/assets/{apex-3O2170Ju.js → apex-0AnsBevk.js} +0 -0
- /package/dist/web/assets/{applescript-5AlzBqQO.js → applescript-CSxLolzy.js} +0 -0
- /package/dist/web/assets/{apply-theme-BSJDABU3.js → apply-theme-CMQXnUAa.js} +0 -0
- /package/dist/web/assets/{ara-BGtOX5Hn.js → ara-DXZS0jA5.js} +0 -0
- /package/dist/web/assets/{asciidoc-CIsC4kkq.js → asciidoc-DDy3pH2A.js} +0 -0
- /package/dist/web/assets/{asm-p1-PUkh3.js → asm-Bd17hhqg.js} +0 -0
- /package/dist/web/assets/{aurora-x-BPgGL50S.js → aurora-x-C62HJRs-.js} +0 -0
- /package/dist/web/assets/{awk-Cj93pRL8.js → awk-Cc-nazni.js} +0 -0
- /package/dist/web/assets/{ayu-dark-C50MYKhw.js → ayu-dark-BsfmNWCU.js} +0 -0
- /package/dist/web/assets/{ayu-light-DyIp0Idy.js → ayu-light-BZ4HLa4X.js} +0 -0
- /package/dist/web/assets/{ayu-mirage-BAdm8pKU.js → ayu-mirage-Bhyjyiqr.js} +0 -0
- /package/dist/web/assets/{ballerina-DUO9mwyV.js → ballerina-QiWgir3v.js} +0 -0
- /package/dist/web/assets/{bat-Cg8hlSct.js → bat-DxtlkwN3.js} +0 -0
- /package/dist/web/assets/{beancount-f8f3BBqB.js → beancount-Bt21X7M_.js} +0 -0
- /package/dist/web/assets/{berry-BqVQYZZr.js → berry-Cksre7lO.js} +0 -0
- /package/dist/web/assets/{bibtex-C_q8ygAM.js → bibtex-DkakgDlR.js} +0 -0
- /package/dist/web/assets/{bicep-DWS-2uWR.js → bicep-8crmcyXU.js} +0 -0
- /package/dist/web/assets/{bird2-C0pvipi_.js → bird2-CewQ2I_Z.js} +0 -0
- /package/dist/web/assets/{c-BQFvJ6Ux.js → c-C_6zE8Ha.js} +0 -0
- /package/dist/web/assets/{c3-Da53KeG6.js → c3-DLWB9-dp.js} +0 -0
- /package/dist/web/assets/{cadence-B5X2vWm3.js → cadence-D8qYS6pR.js} +0 -0
- /package/dist/web/assets/{catppuccin-frappe-Dm3rVY3b.js → catppuccin-frappe-Bh95lNRy.js} +0 -0
- /package/dist/web/assets/{catppuccin-latte-ZxS-ga_K.js → catppuccin-latte-C1PlN3G0.js} +0 -0
- /package/dist/web/assets/{catppuccin-macchiato-B0bAzXh0.js → catppuccin-macchiato-CldtgdK4.js} +0 -0
- /package/dist/web/assets/{catppuccin-mocha-BeMC5hqi.js → catppuccin-mocha-BiwqxX9A.js} +0 -0
- /package/dist/web/assets/{clarity-BRdirlsw.js → clarity-CGiU22jY.js} +0 -0
- /package/dist/web/assets/{clojure-pSLH-KQp.js → clojure-COIC3doh.js} +0 -0
- /package/dist/web/assets/{cmake-DsZu4U4F.js → cmake-DWUEQfHv.js} +0 -0
- /package/dist/web/assets/{codeowners-CtkKTnsH.js → codeowners-BNHiAQea.js} +0 -0
- /package/dist/web/assets/{codeql-BpP1PDrj.js → codeql-BuKaoS0j.js} +0 -0
- /package/dist/web/assets/{common-lisp-BX1XgHTt.js → common-lisp-D5IKbxsd.js} +0 -0
- /package/dist/web/assets/{coq-D2bX6-8g.js → coq-BMVS-p94.js} +0 -0
- /package/dist/web/assets/{csharp-DcR97P0D.js → csharp-DMlbXbM4.js} +0 -0
- /package/dist/web/assets/{css-CIjciIIk.js → css-CjkJC89X.js} +0 -0
- /package/dist/web/assets/{csv-DW48Z5d-.js → csv-DoNVBBf0.js} +0 -0
- /package/dist/web/assets/{csv-parser-DVHBcvuE.js → csv-parser-DHvoTjMF.js} +0 -0
- /package/dist/web/assets/{cue-Byh1xHko.js → cue-DoTRbBg-.js} +0 -0
- /package/dist/web/assets/{cypher-TVlMMWma.js → cypher-DVsNdTT3.js} +0 -0
- /package/dist/web/assets/{d-CxaScvxf.js → d-EX7BBSrv.js} +0 -0
- /package/dist/web/assets/{dark-plus-sn-FFTT1.js → dark-plus-P_YTHwlh.js} +0 -0
- /package/dist/web/assets/{dart-BmpakMe-.js → dart-Do-rgU4S.js} +0 -0
- /package/dist/web/assets/{dax-B1aRUMXT.js → dax-DxOieacu.js} +0 -0
- /package/dist/web/assets/{desktop-C6lD62ar.js → desktop-CvdV7SBf.js} +0 -0
- /package/dist/web/assets/{diff-CupGmT1a.js → diff-CNOetGen.js} +0 -0
- /package/dist/web/assets/{dist-Bi4gRyhr.js → dist-CMfLpPW_.js} +0 -0
- /package/dist/web/assets/{docker-DjDr3TJF.js → docker-DAlG4_q-.js} +0 -0
- /package/dist/web/assets/{dotenv-QeT1SLTv.js → dotenv-Dh3qf6yx.js} +0 -0
- /package/dist/web/assets/{dracula-yI5kpKp5.js → dracula-baAGmJYG.js} +0 -0
- /package/dist/web/assets/{dracula-soft-CYF6cJce.js → dracula-soft-l7ZIyums.js} +0 -0
- /package/dist/web/assets/{dream-maker-TBDKiHrV.js → dream-maker-DeRjdBn8.js} +0 -0
- /package/dist/web/assets/{emacs-lisp-B7aZTnJ_.js → emacs-lisp-CB05-jQA.js} +0 -0
- /package/dist/web/assets/{everforest-dark-CN77B1NJ.js → everforest-dark-CT-llQBT.js} +0 -0
- /package/dist/web/assets/{everforest-light-CyFxGMIs.js → everforest-light-ByPJkTT7.js} +0 -0
- /package/dist/web/assets/{fennel-C6ktGUzp.js → fennel-CeMarbvn.js} +0 -0
- /package/dist/web/assets/{fish-Vr62C2vP.js → fish-BYrt6Xtb.js} +0 -0
- /package/dist/web/assets/{fluent-CH3IJUki.js → fluent-Cr5VcAwh.js} +0 -0
- /package/dist/web/assets/{fortran-free-form-BN7VmmxZ.js → fortran-free-form-DQmKtrLa.js} +0 -0
- /package/dist/web/assets/{gdscript-BYdRNu_P.js → gdscript-QTDKjzhY.js} +0 -0
- /package/dist/web/assets/{gdshader-C2XOq90k.js → gdshader-Chr-5UYt.js} +0 -0
- /package/dist/web/assets/{genie-B4hkIXNU.js → genie-kWHMWnt0.js} +0 -0
- /package/dist/web/assets/{gherkin-CW9NH2DW.js → gherkin-DtEBQFUo.js} +0 -0
- /package/dist/web/assets/{github-dark-BWCf99kM.js → github-dark-B5aiUta2.js} +0 -0
- /package/dist/web/assets/{github-dark-default-DZ1m_6OP.js → github-dark-default-BkxtsTqR.js} +0 -0
- /package/dist/web/assets/{github-dark-dimmed-CUXSQ8bS.js → github-dark-dimmed-B9QjR_0K.js} +0 -0
- /package/dist/web/assets/{github-dark-high-contrast-B6BvU8H-.js → github-dark-high-contrast-D6bO-5c0.js} +0 -0
- /package/dist/web/assets/{github-light-Ch-E92cy.js → github-light-Cg2ZW_CJ.js} +0 -0
- /package/dist/web/assets/{github-light-default-CKFslV7i.js → github-light-default-eD7qUVlQ.js} +0 -0
- /package/dist/web/assets/{github-light-high-contrast-DiJVeRlZ.js → github-light-high-contrast-CFnlSVjd.js} +0 -0
- /package/dist/web/assets/{gleam-D4dP7V5M.js → gleam-BpYoOqIT.js} +0 -0
- /package/dist/web/assets/{gn-DuO2D2eW.js → gn-BxwtZPau.js} +0 -0
- /package/dist/web/assets/{gnuplot-Df_8pFbI.js → gnuplot-CRyiZDfl.js} +0 -0
- /package/dist/web/assets/{go-BL16cMJO.js → go-Dw1Qx94W.js} +0 -0
- /package/dist/web/assets/{groovy-rfHeCRqy.js → groovy-zq_SbWk-.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-hard-CLbDN-E3.js → gruvbox-dark-hard-DbbNy8-Q.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-medium-Df0L0qje.js → gruvbox-dark-medium-BQZ7Kj6C.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-soft-DV6TAcen.js → gruvbox-dark-soft-vNavErXi.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-hard-CWXUzp9b.js → gruvbox-light-hard-AOFbCd7i.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-medium-BOHMe1-7.js → gruvbox-light-medium-DqtxND6H.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-soft-DrrS2sdV.js → gruvbox-light-soft-Cg0basu6.js} +0 -0
- /package/dist/web/assets/{haskell-DybKb9uW.js → haskell-CcsmcOez.js} +0 -0
- /package/dist/web/assets/{haxe-DLRwZxmt.js → haxe-BimYY9X4.js} +0 -0
- /package/dist/web/assets/{hcl-CusCzwOk.js → hcl-CTrXCK40.js} +0 -0
- /package/dist/web/assets/{hjson-BygWIJ-3.js → hjson-CmqqU6Qg.js} +0 -0
- /package/dist/web/assets/{hlsl-DCKvuEyZ.js → hlsl-CuJtX_uf.js} +0 -0
- /package/dist/web/assets/{horizon-DSOVAzyH.js → horizon-BGeSjDYJ.js} +0 -0
- /package/dist/web/assets/{horizon-bright-DKfVjGj_.js → horizon-bright-GYMgd3R0.js} +0 -0
- /package/dist/web/assets/{houston-Ddm8AgpV.js → houston--JD_5r2q.js} +0 -0
- /package/dist/web/assets/{hy-DzZa6alE.js → hy-C9ILOiqk.js} +0 -0
- /package/dist/web/assets/{image-gallery-CObzTisL.js → image-gallery-BWFpZAAP.js} +0 -0
- /package/dist/web/assets/{imba-aiXalPnK.js → imba-B6F3yus5.js} +0 -0
- /package/dist/web/assets/{ini-CAvrVa5R.js → ini-BfkQswMZ.js} +0 -0
- /package/dist/web/assets/{java-PRMSQp24.js → java-DEH5avQ1.js} +0 -0
- /package/dist/web/assets/{javascript-DQR6GQKs.js → javascript-Dr4yHuV2.js} +0 -0
- /package/dist/web/assets/{json-uWUKqOxq.js → json-BuR35ewf.js} +0 -0
- /package/dist/web/assets/{json5-R_LXJXL0.js → json5-Ny9ZiJAC.js} +0 -0
- /package/dist/web/assets/{jsonc-tC5zvrPO.js → jsonc-zYVuoZkR.js} +0 -0
- /package/dist/web/assets/{jsonl-BAwnWh7U.js → jsonl-B9xthb_H.js} +0 -0
- /package/dist/web/assets/{jsonnet-CPj4HCRm.js → jsonnet-CCoLJbMP.js} +0 -0
- /package/dist/web/assets/{jssm-B7HswxOM.js → jssm-W0GDlQXi.js} +0 -0
- /package/dist/web/assets/{jsx-z4TGELE1.js → jsx-BrLJpWC-.js} +0 -0
- /package/dist/web/assets/{kanagawa-dragon-BpeLlraV.js → kanagawa-dragon-CuVwt_MO.js} +0 -0
- /package/dist/web/assets/{kanagawa-lotus-CLnIPAGs.js → kanagawa-lotus-B3W_617P.js} +0 -0
- /package/dist/web/assets/{kanagawa-wave-BglPniBb.js → kanagawa-wave-CEx4MP5W.js} +0 -0
- /package/dist/web/assets/{kdl-B6blIqLq.js → kdl-DilRejnB.js} +0 -0
- /package/dist/web/assets/{kotlin-DVAEYiwY.js → kotlin-Bq5JtKaa.js} +0 -0
- /package/dist/web/assets/{kusto-Ba87977j.js → kusto-Bhp2KYog.js} +0 -0
- /package/dist/web/assets/{laserwave-C-NuzoMp.js → laserwave-WA_7c5hu.js} +0 -0
- /package/dist/web/assets/{lean-CJ_KLkrJ.js → lean-AEGcXwS-.js} +0 -0
- /package/dist/web/assets/{less-Dfn9YC_2.js → less-NPqH-26u.js} +0 -0
- /package/dist/web/assets/{libesm-DdtEjhIi.js → libesm-tR4XlhMj.js} +0 -0
- /package/dist/web/assets/{light-plus-B3BLlguG.js → light-plus-B4xW7cgY.js} +0 -0
- /package/dist/web/assets/{llvm-6UDP3NQb.js → llvm-CPyWNcDE.js} +0 -0
- /package/dist/web/assets/{log-QW3dqr1D.js → log-CeQmiQSz.js} +0 -0
- /package/dist/web/assets/{logo-DUinJheI.js → logo-BO0VrjBm.js} +0 -0
- /package/dist/web/assets/{luau-B1vcBzl8.js → luau-B6GT9kA1.js} +0 -0
- /package/dist/web/assets/{make-CqNL10gS.js → make-CwfmN7e_.js} +0 -0
- /package/dist/web/assets/{markdown-BQNMNiYS.js → markdown-Bbe4FlLp.js} +0 -0
- /package/dist/web/assets/{material-theme-darker-BZrskvoo.js → material-theme-darker-u7M3XGe7.js} +0 -0
- /package/dist/web/assets/{material-theme-rbV8EefR.js → material-theme-iD9XbfsB.js} +0 -0
- /package/dist/web/assets/{material-theme-lighter-B0CBBjBw.js → material-theme-lighter-BGdI48zn.js} +0 -0
- /package/dist/web/assets/{material-theme-ocean-CX_CHnQq.js → material-theme-ocean-_mk62F5q.js} +0 -0
- /package/dist/web/assets/{material-theme-palenight-ztiOCPbw.js → material-theme-palenight-CxFbDyAu.js} +0 -0
- /package/dist/web/assets/{matlab-tHQDNC9G.js → matlab-Z0RUE3V_.js} +0 -0
- /package/dist/web/assets/{mdx-D4kZ6c6G.js → mdx-BZ5wPYzV.js} +0 -0
- /package/dist/web/assets/{mermaid-DlR4U4Pe.js → mermaid-CY1_aP-h.js} +0 -0
- /package/dist/web/assets/{min-dark-BX-xHAD2.js → min-dark-DKWtxOre.js} +0 -0
- /package/dist/web/assets/{min-light-Bxw0_Gqq.js → min-light-e1iwHDfP.js} +0 -0
- /package/dist/web/assets/{mipsasm-DYyZAum-.js → mipsasm-BiDfQ4ir.js} +0 -0
- /package/dist/web/assets/{mojo-q9wuEYKR.js → mojo-DteFEHsj.js} +0 -0
- /package/dist/web/assets/{monokai-D9vrJ8JM.js → monokai-BgPbE9xZ.js} +0 -0
- /package/dist/web/assets/{moonbit-C9roWxMb.js → moonbit-mEbRp-mA.js} +0 -0
- /package/dist/web/assets/{move-BvhjG2ic.js → move-CogqZw7A.js} +0 -0
- /package/dist/web/assets/{narrat-BXGbPwG3.js → narrat-BD2qjmy1.js} +0 -0
- /package/dist/web/assets/{nextflow-groovy-CMy5-Z4k.js → nextflow-groovy-BNX8GOD_.js} +0 -0
- /package/dist/web/assets/{night-owl-CBQBcbmw.js → night-owl-ed3GbziD.js} +0 -0
- /package/dist/web/assets/{night-owl-light-CC-brflp.js → night-owl-light-B0j7O0Wn.js} +0 -0
- /package/dist/web/assets/{nix-B9EAdptu.js → nix-DFDbcQQm.js} +0 -0
- /package/dist/web/assets/{nord-DxASd4qG.js → nord-D93TxEQi.js} +0 -0
- /package/dist/web/assets/{nushell-BsY59i7N.js → nushell-3_H_XKja.js} +0 -0
- /package/dist/web/assets/{objective-c-CoeQAcnP.js → objective-c-BM3u6nlm.js} +0 -0
- /package/dist/web/assets/{objective-cpp-BysdFvND.js → objective-cpp-DZ73h42g.js} +0 -0
- /package/dist/web/assets/{ocaml-CrtQYMMK.js → ocaml-B1PU5gmh.js} +0 -0
- /package/dist/web/assets/{odin-B8pz4gIL.js → odin-D14L6aRL.js} +0 -0
- /package/dist/web/assets/{one-dark-pro-3sSHqoqQ.js → one-dark-pro-Bj-94Z8v.js} +0 -0
- /package/dist/web/assets/{one-light-LUZAvT8g.js → one-light-DTdGWyOZ.js} +0 -0
- /package/dist/web/assets/{openscad-DcKia-sV.js → openscad-Ciq554Sj.js} +0 -0
- /package/dist/web/assets/{pascal-zM5SlmgF.js → pascal-Bfd4qdGi.js} +0 -0
- /package/dist/web/assets/{pkl-C_r_zLLt.js → pkl-CTurDUfj.js} +0 -0
- /package/dist/web/assets/{plastic-B3cRtgKD.js → plastic-BLQfZXQp.js} +0 -0
- /package/dist/web/assets/{plsql-BzyjheXU.js → plsql-DyYfP9kQ.js} +0 -0
- /package/dist/web/assets/{po-Dz4OaDNi.js → po-CgXdT8vQ.js} +0 -0
- /package/dist/web/assets/{poimandres-BBbtYwmR.js → poimandres-DOg5fK6s.js} +0 -0
- /package/dist/web/assets/{polar-Che1XC2Z.js → polar-Dv4aoL6X.js} +0 -0
- /package/dist/web/assets/{postcss-DouzwG4F.js → postcss-DxugHnJD.js} +0 -0
- /package/dist/web/assets/{powerquery-YOdcTZmd.js → powerquery-CR3kzRyX.js} +0 -0
- /package/dist/web/assets/{powershell-8ojzYH34.js → powershell-BE91xJQB.js} +0 -0
- /package/dist/web/assets/{prisma-DCmjceML.js → prisma-B_3yUlRQ.js} +0 -0
- /package/dist/web/assets/{prolog-ByNNz2pB.js → prolog-DcT781Ht.js} +0 -0
- /package/dist/web/assets/{proto-DWMt6Qu1.js → proto-CSf-w5k2.js} +0 -0
- /package/dist/web/assets/{puppet-osfKyZp4.js → puppet-CLGO6QWl.js} +0 -0
- /package/dist/web/assets/{purescript-ClIblkvh.js → purescript-Bah2McKd.js} +0 -0
- /package/dist/web/assets/{python-Ctyr4faP.js → python-DUH8qy7N.js} +0 -0
- /package/dist/web/assets/{qmldir-CbNgyhnI.js → qmldir-YIsHnjKq.js} +0 -0
- /package/dist/web/assets/{qss-LbRHzb21.js → qss-CLmoVNA4.js} +0 -0
- /package/dist/web/assets/{r-Dvfjp0wc.js → r-Pon-b-vz.js} +0 -0
- /package/dist/web/assets/{racket-DI8v8nzD.js → racket-BA09P5Kg.js} +0 -0
- /package/dist/web/assets/{raku-CX1q9GW0.js → raku-DlpzU7Fe.js} +0 -0
- /package/dist/web/assets/{red-CSz3QEOw.js → red-l7VuCR6d.js} +0 -0
- /package/dist/web/assets/{reg-FMjhUQE-.js → reg-g_k7gQEr.js} +0 -0
- /package/dist/web/assets/{regexp-CCSe1Fc4.js → regexp-CT17B7I7.js} +0 -0
- /package/dist/web/assets/{rel-Ce0UO-OB.js → rel-DkPCS7Bp.js} +0 -0
- /package/dist/web/assets/{resolve-theme-B9Mgd5GG.js → resolve-theme-B0eotaPq.js} +0 -0
- /package/dist/web/assets/{riscv-BFZrdZtB.js → riscv-CYuFTw30.js} +0 -0
- /package/dist/web/assets/{ron-D0t4kvJG.js → ron-cY7u_yEu.js} +0 -0
- /package/dist/web/assets/{rose-pine-HzwmOzHM.js → rose-pine-Cr_h_Ubo.js} +0 -0
- /package/dist/web/assets/{rose-pine-dawn-CSp9xAF9.js → rose-pine-dawn-rRewaSZg.js} +0 -0
- /package/dist/web/assets/{rose-pine-moon-Cnzr2Cw0.js → rose-pine-moon-3VX5Xwca.js} +0 -0
- /package/dist/web/assets/{rosmsg-DEJ8Snbv.js → rosmsg-Du4ie5PU.js} +0 -0
- /package/dist/web/assets/{rust-2jBzrC0l.js → rust-B7xWL-Eh.js} +0 -0
- /package/dist/web/assets/{sass-DD75Izu_.js → sass-oXbOG8de.js} +0 -0
- /package/dist/web/assets/{scala-DpBKROIu.js → scala-nlZJhKHf.js} +0 -0
- /package/dist/web/assets/{scheme-DM2jzwUK.js → scheme-FZ9_Zy8D.js} +0 -0
- /package/dist/web/assets/{sdbl-C34Cg6e7.js → sdbl-D_lyvXiu.js} +0 -0
- /package/dist/web/assets/{shellscript-DxfzKSgU.js → shellscript-DRWRboAd.js} +0 -0
- /package/dist/web/assets/{slack-dark-DIIV3bpD.js → slack-dark-BGk2uVHu.js} +0 -0
- /package/dist/web/assets/{slack-ochin-BTkxWjEP.js → slack-ochin-PAoSX041.js} +0 -0
- /package/dist/web/assets/{smalltalk-Bm0qg6SA.js → smalltalk-qP0zvICo.js} +0 -0
- /package/dist/web/assets/{snazzy-light-ClAuuhn4.js → snazzy-light-B7uccpH6.js} +0 -0
- /package/dist/web/assets/{solarized-dark-93sEWmYT.js → solarized-dark-CbICLZs3.js} +0 -0
- /package/dist/web/assets/{solarized-light-C74FvgcO.js → solarized-light-CprWhyjj.js} +0 -0
- /package/dist/web/assets/{solidity-H9fSjkQP.js → solidity-B7pcm2K6.js} +0 -0
- /package/dist/web/assets/{splunk-CDTN2iAG.js → splunk-Co2LL4J_.js} +0 -0
- /package/dist/web/assets/{sql-CoqObhgo.js → sql-cETIdGnT.js} +0 -0
- /package/dist/web/assets/{ssh-config-C3Wv6qRp.js → ssh-config-DETG6JCD.js} +0 -0
- /package/dist/web/assets/{stylus-D0uTFXCy.js → stylus-tmTre2-O.js} +0 -0
- /package/dist/web/assets/{swift-DtMMgvRI.js → swift-BeKQbULV.js} +0 -0
- /package/dist/web/assets/{synthwave-84-DAroyZBu.js → synthwave-84-eCL4-Xdn.js} +0 -0
- /package/dist/web/assets/{system-verilog-Bj7X_lq4.js → system-verilog-CviwWvEx.js} +0 -0
- /package/dist/web/assets/{systemd-CH8ApCUs.js → systemd-WuoFIbBS.js} +0 -0
- /package/dist/web/assets/{talonscript-70lVC3cw.js → talonscript-B9VBu7VI.js} +0 -0
- /package/dist/web/assets/{tasl-WQR8VslV.js → tasl-CN7r11ky.js} +0 -0
- /package/dist/web/assets/{tcl-caTR_frn.js → tcl-e1cdhe3h.js} +0 -0
- /package/dist/web/assets/{terraform-CVRZ4SZ4.js → terraform-BEP0mfa_.js} +0 -0
- /package/dist/web/assets/{tokyo-night-CDyT1jCI.js → tokyo-night-BfA_dA37.js} +0 -0
- /package/dist/web/assets/{toml-B7D2mIDU.js → toml-BrDIfNnt.js} +0 -0
- /package/dist/web/assets/{tsv-2kdn5dqL.js → tsv-CyfGyE4i.js} +0 -0
- /package/dist/web/assets/{tsx-DJfG3fzg.js → tsx-BrFA8Iwf.js} +0 -0
- /package/dist/web/assets/{turtle-DaQac_UT.js → turtle-9GBtR0mP.js} +0 -0
- /package/dist/web/assets/{typescript-BLfAIchl.js → typescript-BuTRZ65V.js} +0 -0
- /package/dist/web/assets/{typespec-ymBaOi_c.js → typespec-CRiNNRXF.js} +0 -0
- /package/dist/web/assets/{typst-DkvUYMT-.js → typst-CmpTnnkl.js} +0 -0
- /package/dist/web/assets/{utils-E0yyGxXt.js → utils-B9H0d3Fh.js} +0 -0
- /package/dist/web/assets/{v-D4997FGs.js → v-DnIi2aab.js} +0 -0
- /package/dist/web/assets/{vala-AFg9DmKE.js → vala-B7VxCNZP.js} +0 -0
- /package/dist/web/assets/{vb-DNUJ9fOP.js → vb-DGoQ3pBT.js} +0 -0
- /package/dist/web/assets/{vendor-xterm-QDr0jvpJ.js → vendor-xterm-ClRVIJ-k.js} +0 -0
- /package/dist/web/assets/{verilog-BrnAQ3tF.js → verilog-zGOC4qwV.js} +0 -0
- /package/dist/web/assets/{vesper-DCZnFmY0.js → vesper-DXR9-06q.js} +0 -0
- /package/dist/web/assets/{vhdl-DoK4PjhE.js → vhdl-CCO0oz0l.js} +0 -0
- /package/dist/web/assets/{viml-D6EKG66q.js → viml-Dxwr-QNN.js} +0 -0
- /package/dist/web/assets/{vitesse-black-C6KILWdF.js → vitesse-black-Bhah7RxE.js} +0 -0
- /package/dist/web/assets/{vitesse-dark-BuSr6OT5.js → vitesse-dark-CGK6XVwc.js} +0 -0
- /package/dist/web/assets/{vitesse-light-M-eYyOoe.js → vitesse-light-ne5kfw8h.js} +0 -0
- /package/dist/web/assets/{vyper-CwSN64Jn.js → vyper-ErDFqVoP.js} +0 -0
- /package/dist/web/assets/{wasm-CVlf9Jwb.js → wasm-Bd258nS1.js} +0 -0
- /package/dist/web/assets/{wasm-BhQEkzil.js → wasm-DtWvUWGr.js} +0 -0
- /package/dist/web/assets/{wenyan-BC7irgM3.js → wenyan-EZe1IEYT.js} +0 -0
- /package/dist/web/assets/{wgsl-W-3UHMDO.js → wgsl-B1o5_I_9.js} +0 -0
- /package/dist/web/assets/{wikitext-T1DoFcm8.js → wikitext-C_rwp8An.js} +0 -0
- /package/dist/web/assets/{wit-DIrx_eCI.js → wit-gbYtF3rY.js} +0 -0
- /package/dist/web/assets/{wolfram-Cza4b443.js → wolfram-DrcQFe2n.js} +0 -0
- /package/dist/web/assets/{yaml-DOZ10aZe.js → yaml-CzDAObLX.js} +0 -0
- /package/dist/web/assets/{zenscript-DDAXkseY.js → zenscript-B-J1phtg.js} +0 -0
- /package/dist/web/assets/{zig-CrzwMpQE.js → zig-BlzNia0O.js} +0 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manual localStorage persistence for floating windows.
|
|
3
|
+
*
|
|
4
|
+
* Hand-rolled instead of zustand's `persist` because the saved rects must be re-clamped
|
|
5
|
+
* against the *current* layer size on load (a rect saved on a 4K screen is off-screen on a
|
|
6
|
+
* laptop), and because only gesture-end commits should hit storage, not every drag frame.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { clampRect, type Bounds, type Rect } from "./window-geometry";
|
|
10
|
+
import type { WindowKind, WindowRuntimeState, WindowVisualState } from "./window-store-types";
|
|
11
|
+
|
|
12
|
+
const STORAGE_KEY = "ppm-windows";
|
|
13
|
+
|
|
14
|
+
/** Only JSON-safe payloads survive a reload — anything else is dropped with the window. */
|
|
15
|
+
export interface PersistedWindow {
|
|
16
|
+
id: string;
|
|
17
|
+
kind: WindowKind;
|
|
18
|
+
rect: Rect;
|
|
19
|
+
state: WindowVisualState;
|
|
20
|
+
payload?: Record<string, unknown>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const KINDS: WindowKind[] = ["explorer"];
|
|
24
|
+
const STATES: WindowVisualState[] = ["normal", "maximized", "minimized"];
|
|
25
|
+
|
|
26
|
+
function isRect(v: unknown): v is Rect {
|
|
27
|
+
if (!v || typeof v !== "object") return false;
|
|
28
|
+
const r = v as Record<string, unknown>;
|
|
29
|
+
return (["x", "y", "w", "h"] as const).every((k) => typeof r[k] === "number" && Number.isFinite(r[k]));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function isSerialisable(payload: unknown): payload is Record<string, unknown> | undefined {
|
|
33
|
+
if (payload === undefined) return true;
|
|
34
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) return false;
|
|
35
|
+
try {
|
|
36
|
+
JSON.stringify(payload);
|
|
37
|
+
return true;
|
|
38
|
+
} catch {
|
|
39
|
+
return false; // cyclic or non-serialisable payload (e.g. a live handle)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function toPersisted(win: WindowRuntimeState): PersistedWindow | null {
|
|
44
|
+
if (!isSerialisable(win.payload)) return null;
|
|
45
|
+
return { id: win.id, kind: win.kind, rect: win.rect, state: win.state, payload: win.payload };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function saveWindowRects(windows: WindowRuntimeState[]): void {
|
|
49
|
+
try {
|
|
50
|
+
// Persisted back-to-front so the restored rank order matches what the user last saw.
|
|
51
|
+
const ordered = [...windows].sort((a, b) => a.rank - b.rank);
|
|
52
|
+
const payload = ordered.map(toPersisted).filter((w): w is PersistedWindow => w !== null);
|
|
53
|
+
if (payload.length === 0) localStorage.removeItem(STORAGE_KEY);
|
|
54
|
+
else localStorage.setItem(STORAGE_KEY, JSON.stringify(payload));
|
|
55
|
+
} catch {
|
|
56
|
+
/* quota exceeded or storage disabled — geometry is cosmetic, never block the UI */
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Read persisted windows, dropping anything malformed, and re-clamp to the current layer. */
|
|
61
|
+
export function loadWindowRects(bounds: Bounds): PersistedWindow[] {
|
|
62
|
+
let raw: string | null = null;
|
|
63
|
+
try {
|
|
64
|
+
raw = localStorage.getItem(STORAGE_KEY);
|
|
65
|
+
} catch {
|
|
66
|
+
return [];
|
|
67
|
+
}
|
|
68
|
+
if (!raw) return [];
|
|
69
|
+
|
|
70
|
+
let parsed: unknown;
|
|
71
|
+
try {
|
|
72
|
+
parsed = JSON.parse(raw);
|
|
73
|
+
} catch {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
if (!Array.isArray(parsed)) return [];
|
|
77
|
+
|
|
78
|
+
const out: PersistedWindow[] = [];
|
|
79
|
+
for (const item of parsed) {
|
|
80
|
+
if (!item || typeof item !== "object") continue;
|
|
81
|
+
const w = item as Record<string, unknown>;
|
|
82
|
+
if (typeof w.id !== "string" || !KINDS.includes(w.kind as WindowKind)) continue;
|
|
83
|
+
if (!isRect(w.rect) || !isSerialisable(w.payload)) continue;
|
|
84
|
+
const state = STATES.includes(w.state as WindowVisualState)
|
|
85
|
+
? (w.state as WindowVisualState)
|
|
86
|
+
: "normal";
|
|
87
|
+
out.push({
|
|
88
|
+
id: w.id,
|
|
89
|
+
kind: w.kind as WindowKind,
|
|
90
|
+
rect: clampRect(w.rect, bounds),
|
|
91
|
+
state,
|
|
92
|
+
payload: w.payload as Record<string, unknown> | undefined,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
return out;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function clearWindowRects(): void {
|
|
99
|
+
try {
|
|
100
|
+
localStorage.removeItem(STORAGE_KEY);
|
|
101
|
+
} catch {
|
|
102
|
+
/* storage disabled */
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The eight invisible grab strips around a window. Corners sit on top of the edges, matching
|
|
3
|
+
* the hit areas desktop window managers use.
|
|
4
|
+
*
|
|
5
|
+
* A window at this size can be reached with a finger — a tablet past the `md` breakpoint gets
|
|
6
|
+
* the desktop layer — and a 6 px strip is far below any touch target, so the grab areas grow
|
|
7
|
+
* on coarse pointers. They are transparent, so nothing about the window's look changes.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { useMediaQuery } from "@/hooks/use-media-query";
|
|
11
|
+
import { RESIZE_HANDLES, type ResizeHandle } from "./window-geometry";
|
|
12
|
+
|
|
13
|
+
type HandleBinder = (handle: ResizeHandle) => Record<string, unknown>;
|
|
14
|
+
|
|
15
|
+
interface HandleSizes {
|
|
16
|
+
edge: number;
|
|
17
|
+
corner: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const MOUSE_SIZES: HandleSizes = { edge: 6, corner: 12 };
|
|
21
|
+
const TOUCH_SIZES: HandleSizes = { edge: 12, corner: 20 };
|
|
22
|
+
|
|
23
|
+
const CURSORS: Record<ResizeHandle, string> = {
|
|
24
|
+
n: "cursor-n-resize",
|
|
25
|
+
s: "cursor-s-resize",
|
|
26
|
+
e: "cursor-e-resize",
|
|
27
|
+
w: "cursor-w-resize",
|
|
28
|
+
ne: "cursor-ne-resize",
|
|
29
|
+
nw: "cursor-nw-resize",
|
|
30
|
+
se: "cursor-se-resize",
|
|
31
|
+
sw: "cursor-sw-resize",
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Absolute placement per handle, expressed in the same units the frame uses. Each strip is
|
|
36
|
+
* centred on its border, so half of the extra width grows outwards and half inwards — the
|
|
37
|
+
* window's own border never moves.
|
|
38
|
+
*/
|
|
39
|
+
function styleFor(handle: ResizeHandle, sizes: HandleSizes): React.CSSProperties {
|
|
40
|
+
const corner = handle.length === 2;
|
|
41
|
+
const s: React.CSSProperties = { position: "absolute", touchAction: "none", zIndex: 1 };
|
|
42
|
+
if (handle.includes("n")) s.top = -sizes.edge / 2;
|
|
43
|
+
if (handle.includes("s")) s.bottom = -sizes.edge / 2;
|
|
44
|
+
if (handle.includes("w")) s.left = -sizes.edge / 2;
|
|
45
|
+
if (handle.includes("e")) s.right = -sizes.edge / 2;
|
|
46
|
+
if (corner) {
|
|
47
|
+
s.width = sizes.corner;
|
|
48
|
+
s.height = sizes.corner;
|
|
49
|
+
return s;
|
|
50
|
+
}
|
|
51
|
+
// Edge strips stop short of the corners so the corner handles keep their full hit area.
|
|
52
|
+
if (handle === "n" || handle === "s") {
|
|
53
|
+
s.left = sizes.corner;
|
|
54
|
+
s.right = sizes.corner;
|
|
55
|
+
s.height = sizes.edge;
|
|
56
|
+
} else {
|
|
57
|
+
s.top = sizes.corner;
|
|
58
|
+
s.bottom = sizes.corner;
|
|
59
|
+
s.width = sizes.edge;
|
|
60
|
+
}
|
|
61
|
+
return s;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function WindowResizeHandles({ bind }: { bind: HandleBinder }) {
|
|
65
|
+
const coarsePointer = useMediaQuery("(pointer: coarse)");
|
|
66
|
+
const sizes = coarsePointer ? TOUCH_SIZES : MOUSE_SIZES;
|
|
67
|
+
return (
|
|
68
|
+
<>
|
|
69
|
+
{RESIZE_HANDLES.map((handle) => (
|
|
70
|
+
<div
|
|
71
|
+
key={handle}
|
|
72
|
+
{...bind(handle)}
|
|
73
|
+
data-resize-handle={handle}
|
|
74
|
+
aria-hidden
|
|
75
|
+
className={CURSORS[handle]}
|
|
76
|
+
style={styleFor(handle, sizes)}
|
|
77
|
+
/>
|
|
78
|
+
))}
|
|
79
|
+
</>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** Shared window types — split out so the store and the persistence layer do not import each other. */
|
|
2
|
+
|
|
3
|
+
import type { Rect } from "./window-geometry";
|
|
4
|
+
|
|
5
|
+
/** Content families the layer can host. The registry maps each kind to its body component. */
|
|
6
|
+
export type WindowKind = "explorer";
|
|
7
|
+
|
|
8
|
+
export type WindowVisualState = "normal" | "maximized" | "minimized";
|
|
9
|
+
|
|
10
|
+
export interface WindowRuntimeState {
|
|
11
|
+
id: string;
|
|
12
|
+
kind: WindowKind;
|
|
13
|
+
/** Geometry in layer coordinates. While maximized this holds the restore rect. */
|
|
14
|
+
rect: Rect;
|
|
15
|
+
/** Dense stacking order, 0 = backmost, renormalised on every focus. */
|
|
16
|
+
rank: number;
|
|
17
|
+
state: WindowVisualState;
|
|
18
|
+
/** Kind-specific, must stay JSON-serialisable to survive a reload. */
|
|
19
|
+
payload?: Record<string, unknown>;
|
|
20
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Floating window state: geometry, stacking order and lifecycle for the desktop window layer.
|
|
3
|
+
*
|
|
4
|
+
* Content-agnostic on purpose — the store knows a `kind` and an opaque payload, the registry
|
|
5
|
+
* decides what renders inside. Ranks are kept dense (0..n-1) so the inline z-index never
|
|
6
|
+
* escapes the band reserved below the app's click-away backdrops.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { create } from "zustand";
|
|
10
|
+
import {
|
|
11
|
+
cascadeSpawnRect,
|
|
12
|
+
clampRect,
|
|
13
|
+
MAX_WINDOWS,
|
|
14
|
+
type Bounds,
|
|
15
|
+
type Rect,
|
|
16
|
+
} from "./window-geometry";
|
|
17
|
+
import { loadWindowRects, saveWindowRects } from "./window-persistence";
|
|
18
|
+
import type { WindowKind, WindowRuntimeState, WindowVisualState } from "./window-store-types";
|
|
19
|
+
|
|
20
|
+
export type { WindowKind, WindowRuntimeState, WindowVisualState };
|
|
21
|
+
|
|
22
|
+
/** Fallback layer size used before the container has been measured. */
|
|
23
|
+
const DEFAULT_BOUNDS: Bounds = { w: 1280, h: 800 };
|
|
24
|
+
|
|
25
|
+
interface WindowStore {
|
|
26
|
+
windows: Record<string, WindowRuntimeState>;
|
|
27
|
+
/** Measured size of the layer container; all rects are clamped against it. */
|
|
28
|
+
bounds: Bounds;
|
|
29
|
+
/** True once persisted windows have been read, so restore runs exactly once. */
|
|
30
|
+
restored: boolean;
|
|
31
|
+
|
|
32
|
+
open(kind: WindowKind, payload?: Record<string, unknown>, rect?: Rect): string;
|
|
33
|
+
close(id: string): void;
|
|
34
|
+
focus(id: string): void;
|
|
35
|
+
move(id: string, position: { x: number; y: number }): void;
|
|
36
|
+
resize(id: string, rect: Rect): void;
|
|
37
|
+
setState(id: string, state: WindowVisualState): void;
|
|
38
|
+
/** Merge into a window's payload; persisted so a reload restores the latest state. */
|
|
39
|
+
setPayload(id: string, payload: Record<string, unknown>): void;
|
|
40
|
+
setBounds(bounds: Bounds): void;
|
|
41
|
+
/** Re-hydrate persisted windows into the layer (no-op after the first call). */
|
|
42
|
+
restoreAll(bounds: Bounds): void;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const sortedByRank = (windows: Record<string, WindowRuntimeState>): WindowRuntimeState[] =>
|
|
46
|
+
Object.values(windows).sort((a, b) => a.rank - b.rank);
|
|
47
|
+
|
|
48
|
+
/** Rewrite ranks to 0..n-1 in the given order (last = frontmost). */
|
|
49
|
+
function densify(ordered: WindowRuntimeState[]): Record<string, WindowRuntimeState> {
|
|
50
|
+
const out: Record<string, WindowRuntimeState> = {};
|
|
51
|
+
ordered.forEach((win, rank) => {
|
|
52
|
+
out[win.id] = win.rank === rank ? win : { ...win, rank };
|
|
53
|
+
});
|
|
54
|
+
return out;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function persist(windows: Record<string, WindowRuntimeState>): void {
|
|
58
|
+
saveWindowRects(Object.values(windows));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function newId(): string {
|
|
62
|
+
return `win-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const useWindowStore = create<WindowStore>((set, get) => ({
|
|
66
|
+
windows: {},
|
|
67
|
+
bounds: DEFAULT_BOUNDS,
|
|
68
|
+
restored: false,
|
|
69
|
+
|
|
70
|
+
open: (kind, payload, rect) => {
|
|
71
|
+
const { windows, bounds } = get();
|
|
72
|
+
const ordered = sortedByRank(windows);
|
|
73
|
+
|
|
74
|
+
// At the cap the layer would leave its z band, so the oldest window is raised instead
|
|
75
|
+
// of silently dropping the request.
|
|
76
|
+
if (ordered.length >= MAX_WINDOWS) {
|
|
77
|
+
const oldest = ordered[0]!;
|
|
78
|
+
get().focus(oldest.id);
|
|
79
|
+
return oldest.id;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const id = newId();
|
|
83
|
+
const spawn = rect ? clampRect(rect, bounds) : cascadeSpawnRect(ordered.map((w) => w.rect), bounds);
|
|
84
|
+
const next = densify([
|
|
85
|
+
...ordered,
|
|
86
|
+
{ id, kind, rect: spawn, rank: ordered.length, state: "normal" as const, payload },
|
|
87
|
+
]);
|
|
88
|
+
set({ windows: next });
|
|
89
|
+
persist(next);
|
|
90
|
+
return id;
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
close: (id) => {
|
|
94
|
+
const { windows } = get();
|
|
95
|
+
if (!windows[id]) return;
|
|
96
|
+
const next = densify(sortedByRank(windows).filter((w) => w.id !== id));
|
|
97
|
+
set({ windows: next });
|
|
98
|
+
persist(next);
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
focus: (id) => {
|
|
102
|
+
const { windows } = get();
|
|
103
|
+
const target = windows[id];
|
|
104
|
+
if (!target) return;
|
|
105
|
+
const ordered = sortedByRank(windows);
|
|
106
|
+
if (ordered[ordered.length - 1]?.id === id) return; // already frontmost
|
|
107
|
+
const next = densify([...ordered.filter((w) => w.id !== id), target]);
|
|
108
|
+
set({ windows: next });
|
|
109
|
+
persist(next); // stacking order is part of the restored layout
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
move: (id, position) => {
|
|
113
|
+
const { windows, bounds } = get();
|
|
114
|
+
const win = windows[id];
|
|
115
|
+
if (!win) return;
|
|
116
|
+
const rect = clampRect({ ...win.rect, ...position }, bounds);
|
|
117
|
+
const next = { ...windows, [id]: { ...win, rect } };
|
|
118
|
+
set({ windows: next });
|
|
119
|
+
persist(next);
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
resize: (id, rect) => {
|
|
123
|
+
const { windows, bounds } = get();
|
|
124
|
+
const win = windows[id];
|
|
125
|
+
if (!win) return;
|
|
126
|
+
const next = { ...windows, [id]: { ...win, rect: clampRect(rect, bounds) } };
|
|
127
|
+
set({ windows: next });
|
|
128
|
+
persist(next);
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
setState: (id, state) => {
|
|
132
|
+
const { windows } = get();
|
|
133
|
+
const win = windows[id];
|
|
134
|
+
if (!win || win.state === state) return;
|
|
135
|
+
const next = { ...windows, [id]: { ...win, state } };
|
|
136
|
+
set({ windows: next });
|
|
137
|
+
persist(next);
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
setPayload: (id, payload) => {
|
|
141
|
+
const { windows } = get();
|
|
142
|
+
const win = windows[id];
|
|
143
|
+
if (!win) return;
|
|
144
|
+
const merged = { ...win.payload, ...payload };
|
|
145
|
+
// Content re-renders on every keystroke in some bodies; skip the write when the
|
|
146
|
+
// payload is unchanged so persistence is not hit for nothing.
|
|
147
|
+
const unchanged = Object.entries(merged).every(
|
|
148
|
+
([key, value]) => win.payload?.[key] === value,
|
|
149
|
+
);
|
|
150
|
+
if (unchanged && Object.keys(merged).length === Object.keys(win.payload ?? {}).length) return;
|
|
151
|
+
const next = { ...windows, [id]: { ...win, payload: merged } };
|
|
152
|
+
set({ windows: next });
|
|
153
|
+
persist(next);
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
setBounds: (bounds) => {
|
|
157
|
+
const current = get();
|
|
158
|
+
if (current.bounds.w === bounds.w && current.bounds.h === bounds.h) return;
|
|
159
|
+
// A shrinking content area (sidebar opened, browser resized) would otherwise strand
|
|
160
|
+
// windows outside the layer with no way to drag them back.
|
|
161
|
+
const windows: Record<string, WindowRuntimeState> = {};
|
|
162
|
+
for (const win of Object.values(current.windows)) {
|
|
163
|
+
const rect = clampRect(win.rect, bounds);
|
|
164
|
+
windows[win.id] =
|
|
165
|
+
rect.x === win.rect.x && rect.y === win.rect.y && rect.w === win.rect.w && rect.h === win.rect.h
|
|
166
|
+
? win
|
|
167
|
+
: { ...win, rect };
|
|
168
|
+
}
|
|
169
|
+
set({ bounds, windows });
|
|
170
|
+
},
|
|
171
|
+
|
|
172
|
+
restoreAll: (bounds) => {
|
|
173
|
+
if (get().restored) return;
|
|
174
|
+
const saved = loadWindowRects(bounds).slice(0, MAX_WINDOWS);
|
|
175
|
+
const windows = densify(
|
|
176
|
+
saved.map((w, rank) => ({
|
|
177
|
+
id: w.id,
|
|
178
|
+
kind: w.kind,
|
|
179
|
+
rect: w.rect,
|
|
180
|
+
rank,
|
|
181
|
+
state: w.state,
|
|
182
|
+
payload: w.payload,
|
|
183
|
+
})),
|
|
184
|
+
);
|
|
185
|
+
set({ bounds, windows, restored: true });
|
|
186
|
+
},
|
|
187
|
+
}));
|
|
188
|
+
|
|
189
|
+
/** Windows in paint order (backmost first) — stable identity per store update. */
|
|
190
|
+
export function windowsInRankOrder(windows: Record<string, WindowRuntimeState>): WindowRuntimeState[] {
|
|
191
|
+
return sortedByRank(windows);
|
|
192
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useState, useEffect, useCallback } from "react";
|
|
2
2
|
import { Plus, Users, Loader2, Trash2 } from "lucide-react";
|
|
3
3
|
import { useShallow } from "zustand/react/shallow";
|
|
4
|
+
import { FeatureBadge } from "@/components/ui/feature-badge";
|
|
4
5
|
import { useProjectStore } from "@/stores/project-store";
|
|
5
6
|
import { usePanelStore } from "@/stores/panel-store";
|
|
6
7
|
import {
|
|
@@ -68,9 +69,7 @@ export function GroupList() {
|
|
|
68
69
|
<div className="flex h-9 shrink-0 items-center justify-between border-b border-border px-2">
|
|
69
70
|
<div className="flex items-center gap-1.5">
|
|
70
71
|
<span className="text-xs font-semibold uppercase tracking-wide text-text-secondary">Teams</span>
|
|
71
|
-
<
|
|
72
|
-
beta
|
|
73
|
-
</span>
|
|
72
|
+
<FeatureBadge id="teams" />
|
|
74
73
|
</div>
|
|
75
74
|
<button
|
|
76
75
|
type="button"
|
|
@@ -18,7 +18,9 @@ import {
|
|
|
18
18
|
Plus,
|
|
19
19
|
Columns2,
|
|
20
20
|
Cloud,
|
|
21
|
+
AppWindow,
|
|
21
22
|
} from "lucide-react";
|
|
23
|
+
import { openExplorer } from "@/components/os-explorer/open-explorer";
|
|
22
24
|
import { useTabStore, type TabType } from "@/stores/tab-store";
|
|
23
25
|
import { useProjectStore } from "@/stores/project-store";
|
|
24
26
|
import { useSettingsStore } from "@/stores/settings-store";
|
|
@@ -232,6 +234,14 @@ export function CommandPalette({ open, onClose, initialQuery = "" }: { open: boo
|
|
|
232
234
|
group: "action",
|
|
233
235
|
shortcut: formatShortcut(getBinding("open-settings")),
|
|
234
236
|
},
|
|
237
|
+
{
|
|
238
|
+
id: "open-file-explorer", label: "Open File Explorer", icon: AppWindow, group: "action",
|
|
239
|
+
keywords: "open file explorer finder browse files folders disk drive window",
|
|
240
|
+
action: () => {
|
|
241
|
+
void openExplorer();
|
|
242
|
+
onClose();
|
|
243
|
+
},
|
|
244
|
+
},
|
|
235
245
|
];
|
|
236
246
|
|
|
237
247
|
// Append extension-contributed commands (with keybinding shortcuts, respecting user overrides)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useRef, useState, useEffect } from "react";
|
|
2
2
|
import { MoreHorizontal } from "lucide-react";
|
|
3
3
|
import { BottomSheet } from "@/components/ui/mobile-bottom-sheet";
|
|
4
|
+
import { FeatureBadge } from "@/components/ui/feature-badge";
|
|
4
5
|
import { cn } from "@/lib/utils";
|
|
5
6
|
import type { SidebarActiveTab } from "@/stores/settings-store";
|
|
6
7
|
import type { SidebarTabDef } from "@/lib/sidebar-tabs/tab-registry";
|
|
@@ -134,11 +135,7 @@ export function MobileDrawerTabBar({ tabs, activeId, onSelect, onReorder }: Prop
|
|
|
134
135
|
>
|
|
135
136
|
<Icon className="size-4" />
|
|
136
137
|
<span>{tab.shortLabel ?? tab.label}</span>
|
|
137
|
-
{tab.
|
|
138
|
-
<span className="absolute top-1 right-2 rounded-full bg-accent-wash px-1 text-[7px] font-bold uppercase leading-[1.4] tracking-wide text-primary">
|
|
139
|
-
beta
|
|
140
|
-
</span>
|
|
141
|
-
)}
|
|
138
|
+
<FeatureBadge id={tab.badge} variant="corner" className="top-1 right-2 shadow-none" />
|
|
142
139
|
</button>
|
|
143
140
|
);
|
|
144
141
|
})}
|
|
@@ -173,11 +170,7 @@ export function MobileDrawerTabBar({ tabs, activeId, onSelect, onReorder }: Prop
|
|
|
173
170
|
>
|
|
174
171
|
<Icon className="size-4 shrink-0" />
|
|
175
172
|
<span>{tab.label}</span>
|
|
176
|
-
{tab.
|
|
177
|
-
<span className="rounded-full bg-accent-wash px-1.5 py-px text-[9px] font-semibold uppercase tracking-wide text-primary">
|
|
178
|
-
beta
|
|
179
|
-
</span>
|
|
180
|
-
)}
|
|
173
|
+
<FeatureBadge id={tab.badge} />
|
|
181
174
|
</button>
|
|
182
175
|
);
|
|
183
176
|
})}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useState, useCallback, useEffect, useMemo } from "react";
|
|
2
|
-
import { X, Bug as BugIcon, Cloud } from "lucide-react";
|
|
2
|
+
import { X, Bug as BugIcon, Cloud, FolderTree } from "lucide-react";
|
|
3
3
|
import { useShallow } from "zustand/react/shallow";
|
|
4
4
|
import { useProjectStore } from "@/stores/project-store";
|
|
5
5
|
import { useSettingsStore, type SidebarActiveTab } from "@/stores/settings-store";
|
|
@@ -22,6 +22,8 @@ import { UpgradeButton } from "@/components/layout/upgrade-button";
|
|
|
22
22
|
import { CloudSharePopover } from "@/components/layout/cloud-share-popover";
|
|
23
23
|
import { BottomSheet } from "@/components/ui/mobile-bottom-sheet";
|
|
24
24
|
import { isMobileDevice } from "@/hooks/use-is-mobile";
|
|
25
|
+
import { openExplorer } from "@/components/os-explorer/open-explorer";
|
|
26
|
+
import { FeatureBadge } from "@/components/ui/feature-badge";
|
|
25
27
|
import { cn } from "@/lib/utils";
|
|
26
28
|
|
|
27
29
|
// Tab ids the mobile drawer can render content for. `search` is desktop-only for now;
|
|
@@ -136,10 +138,19 @@ export function MobileDrawer({ isOpen, onClose, initialTab }: MobileDrawerProps)
|
|
|
136
138
|
onReorder={setSidebarTabOrder}
|
|
137
139
|
/>
|
|
138
140
|
|
|
139
|
-
{/* Report Bug + Cloud & Share + Version / Upgrade */}
|
|
141
|
+
{/* Files (OS explorer) + Report Bug + Cloud & Share + Version / Upgrade */}
|
|
140
142
|
<div className="flex items-center justify-between px-4 py-2 border-t border-border text-[11px]">
|
|
141
143
|
<UpgradeButton align="left" />
|
|
142
144
|
<div className="flex items-center gap-3">
|
|
145
|
+
{/* Not a sidebar tab — the explorer opens as its own full-screen sheet. */}
|
|
146
|
+
<button
|
|
147
|
+
onClick={() => { onClose(); void openExplorer(); }}
|
|
148
|
+
className="flex items-center gap-1 text-[10px] text-text-subtle hover:text-text-secondary transition-colors"
|
|
149
|
+
>
|
|
150
|
+
<FolderTree className="size-3" />
|
|
151
|
+
<span>Files</span>
|
|
152
|
+
<FeatureBadge id="os-explorer" className="text-[7px] px-1" />
|
|
153
|
+
</button>
|
|
143
154
|
<button
|
|
144
155
|
onClick={() => setCloudOpen(true)}
|
|
145
156
|
className="flex items-center gap-1 text-[10px] text-text-subtle hover:text-text-secondary transition-colors"
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { useState, useRef, useEffect, useMemo, memo } from "react";
|
|
2
2
|
import { createPortal } from "react-dom";
|
|
3
|
-
import { Settings, Bug, Cloud } from "lucide-react";
|
|
3
|
+
import { Settings, Bug, Cloud, FolderTree } from "lucide-react";
|
|
4
|
+
import { openExplorer } from "@/components/os-explorer/open-explorer";
|
|
5
|
+
import { FeatureBadge } from "@/components/ui/feature-badge";
|
|
6
|
+
import type { FeatureBadgeId } from "@/lib/feature-badges";
|
|
4
7
|
import { useSettingsStore, type SidebarActiveTab } from "@/stores/settings-store";
|
|
5
8
|
import { getAvailableTabs } from "@/lib/sidebar-tabs/tab-registry";
|
|
6
9
|
import { resolveTabOrder } from "@/lib/sidebar-tabs/resolve-tab-order";
|
|
@@ -25,8 +28,8 @@ function Badge({ count }: { count: number }) {
|
|
|
25
28
|
}
|
|
26
29
|
|
|
27
30
|
// Section nav item: 38×38, active = tinted bg + inset accent bar + bolder icon.
|
|
28
|
-
function NavItem({ icon: Icon, label, active, badge,
|
|
29
|
-
icon: React.ElementType; label: string; active: boolean; badge?: number;
|
|
31
|
+
function NavItem({ icon: Icon, label, active, badge, featureBadge, onClick, dragging, dropBefore, onDragStart, onDragOver, onDrop, onDragEnd }: {
|
|
32
|
+
icon: React.ElementType; label: string; active: boolean; badge?: number; featureBadge?: FeatureBadgeId; onClick: () => void;
|
|
30
33
|
dragging?: boolean; dropBefore?: boolean;
|
|
31
34
|
onDragStart?: (e: React.DragEvent) => void;
|
|
32
35
|
onDragOver?: (e: React.DragEvent) => void;
|
|
@@ -52,11 +55,7 @@ function NavItem({ icon: Icon, label, active, badge, beta, onClick, dragging, dr
|
|
|
52
55
|
>
|
|
53
56
|
<Icon className="size-[18px]" strokeWidth={active ? 2.4 : 2} />
|
|
54
57
|
{badge != null && badge > 0 && <Badge count={badge} />}
|
|
55
|
-
{
|
|
56
|
-
<span className="absolute -top-0.5 -right-0.5 rounded-full bg-accent-wash px-1 text-[7px] font-bold uppercase leading-[1.4] tracking-wide text-primary shadow-sm">
|
|
57
|
-
beta
|
|
58
|
-
</span>
|
|
59
|
-
)}
|
|
58
|
+
<FeatureBadge id={featureBadge} variant="corner" />
|
|
60
59
|
{/* hover tooltip (pointer devices only) */}
|
|
61
60
|
<span className="pointer-events-none absolute left-[calc(100%+8px)] z-50 hidden can-hover:group-hover:block whitespace-nowrap rounded-md border border-border bg-surface-elevated px-2 py-1 text-xs font-medium text-foreground shadow-[0_4px_12px_rgba(0,0,0,.4)]">
|
|
62
61
|
{label}
|
|
@@ -66,11 +65,14 @@ function NavItem({ icon: Icon, label, active, badge, beta, onClick, dragging, dr
|
|
|
66
65
|
}
|
|
67
66
|
|
|
68
67
|
// Footer utility item: 32×32, 16px icon.
|
|
69
|
-
function FooterUtil({ icon: Icon, label, onClick, active }: {
|
|
70
|
-
icon: React.ElementType; label: string; onClick?: () => void; active?: boolean;
|
|
68
|
+
function FooterUtil({ icon: Icon, label, onClick, active, featureBadge }: {
|
|
69
|
+
icon: React.ElementType; label: string; onClick?: () => void; active?: boolean; featureBadge?: FeatureBadgeId;
|
|
71
70
|
}) {
|
|
72
71
|
return (
|
|
73
72
|
<button
|
|
73
|
+
type="button"
|
|
74
|
+
aria-label={label}
|
|
75
|
+
title={label}
|
|
74
76
|
onClick={onClick}
|
|
75
77
|
className={cn(
|
|
76
78
|
"group relative flex items-center justify-center size-8 rounded-[7px] transition-colors shrink-0",
|
|
@@ -78,6 +80,7 @@ function FooterUtil({ icon: Icon, label, onClick, active }: {
|
|
|
78
80
|
)}
|
|
79
81
|
>
|
|
80
82
|
<Icon className="size-4" />
|
|
83
|
+
<FeatureBadge id={featureBadge} variant="corner" />
|
|
81
84
|
<span className="pointer-events-none absolute left-[calc(100%+8px)] z-50 hidden can-hover:group-hover:block whitespace-nowrap rounded-md border border-border bg-surface-elevated px-2 py-1 text-xs font-medium text-foreground shadow-[0_4px_12px_rgba(0,0,0,.4)]">
|
|
82
85
|
{label}
|
|
83
86
|
</span>
|
|
@@ -169,7 +172,7 @@ export const NavSectionRail = memo(function NavSectionRail({ className }: { clas
|
|
|
169
172
|
label={tab.label}
|
|
170
173
|
active={sidebarActiveTab === tab.id}
|
|
171
174
|
badge={tab.id === "git" ? gitChangesCount : tab.id === "jira" ? jiraUnreadCount : tab.id === "history" ? historyUnreadCount : undefined}
|
|
172
|
-
|
|
175
|
+
featureBadge={tab.badge}
|
|
173
176
|
onClick={() => handleTabClick(tab.id)}
|
|
174
177
|
dragging={dragId === tab.id}
|
|
175
178
|
dropBefore={dropId === tab.id && dragId !== tab.id}
|
|
@@ -206,6 +209,8 @@ export const NavSectionRail = memo(function NavSectionRail({ className }: { clas
|
|
|
206
209
|
</>,
|
|
207
210
|
document.body,
|
|
208
211
|
)}
|
|
212
|
+
{/* Not a sidebar tab — the explorer lives in its own floating window. */}
|
|
213
|
+
<FooterUtil icon={FolderTree} label="File Explorer" featureBadge="os-explorer" onClick={() => void openExplorer()} />
|
|
209
214
|
<FooterUtil icon={Bug} label="Report Bug" onClick={handleReportBug} />
|
|
210
215
|
<FooterUtil icon={Settings} label="Settings" active={sidebarActiveTab === "settings"} onClick={() => handleTabClick("settings")} />
|
|
211
216
|
</div>
|