@hienlh/ppm 0.17.56 → 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.
Files changed (583) hide show
  1. package/.release-run.sh +5 -0
  2. package/CHANGELOG.md +21 -0
  3. package/CLAUDE.md +4 -0
  4. package/assets/skills/ppm/SKILL.md +1 -1
  5. package/assets/skills/ppm/references/http-api.md +16 -2
  6. package/bun.lock +3 -0
  7. package/dist/web/assets/{ai-resource-editor-DL011EDu.js → ai-resource-editor-BLYgg033.js} +1 -1
  8. package/dist/web/assets/{angular-html-D4YJUMLP.js → angular-html-jfMqUuRd.js} +1 -1
  9. package/dist/web/assets/{angular-ts-CUvq2h3X.js → angular-ts-wytn9PMm.js} +1 -1
  10. package/dist/web/assets/{api-client-COQtP-dB.js → api-client-B9OFv3nY.js} +1 -1
  11. package/dist/web/assets/api-client-CKo60P6G.js +1 -0
  12. package/dist/web/assets/{api-settings-Do0Z0IRN.js → api-settings-DGhshDRg.js} +1 -1
  13. package/dist/web/assets/api-settings-fdkhqLSF.js +1 -0
  14. package/dist/web/assets/{api-themes-DXi8sz-9.js → api-themes-okiyhhp9.js} +1 -1
  15. package/dist/web/assets/{apl-DSANFLx0.js → apl-i-ahKx6O.js} +1 -1
  16. package/dist/web/assets/architecture-PBZL5I3N-B4J2-O8Q.js +1 -0
  17. package/dist/web/assets/{astro-CXIotiNS.js → astro-DVBCJfG6.js} +1 -1
  18. package/dist/web/assets/{audio-preview-H16VO99g.js → audio-preview-y8XpCNrh.js} +1 -1
  19. package/dist/web/assets/{blade-BvrP2gPd.js → blade-BHlL8SqU.js} +1 -1
  20. package/dist/web/assets/{bsl-CGf6Xuyu.js → bsl-D-UOfR1F.js} +1 -1
  21. package/dist/web/assets/{cairo-BboZ9qIf.js → cairo-Bn6rucli.js} +1 -1
  22. package/dist/web/assets/can-open-in-ppm-BzVXoV-d.js +1 -0
  23. package/dist/web/assets/chat-tab-BcR-3_6R.js +13 -0
  24. package/dist/web/assets/clock-B1CPBp2g.js +1 -0
  25. package/dist/web/assets/{cobol-D39V6ukp.js → cobol-Byn9FYKu.js} +1 -1
  26. package/dist/web/assets/code-editor-CJ8g7dqm.js +10 -0
  27. package/dist/web/assets/{coffee-DTtK7kKi.js → coffee-DjBl4gm-.js} +1 -1
  28. package/dist/web/assets/{conflict-editor-B_wAb0Kb.js → conflict-editor-BP7JFZzG.js} +3 -3
  29. package/dist/web/assets/context-menu-BSE5yuMG.js +1 -0
  30. package/dist/web/assets/copy-Su9m2utd.js +1 -0
  31. package/dist/web/assets/{cpp-CnU9hfBq.js → cpp-CKzic-3E.js} +1 -1
  32. package/dist/web/assets/{crystal-DMjCL--0.js → crystal-Bkg-Ly_g.js} +1 -1
  33. package/dist/web/assets/csv-preview-xAwUDNat.js +2 -0
  34. package/dist/web/assets/{data-grid-overlay-editor-Bdc80TJ1.js → data-grid-overlay-editor-DMTCHIG9.js} +1 -1
  35. package/dist/web/assets/{database-viewer-awmnuDCt.js → database-viewer-BVLWYPa4.js} +1 -1
  36. package/dist/web/assets/dialog-D__LRQTn.js +1 -0
  37. package/dist/web/assets/diff-viewer-BheSOHfl.js +4 -0
  38. package/dist/web/assets/{docx-preview-DDw3duw9.js → docx-preview-CGlMqltP.js} +1 -1
  39. package/dist/web/assets/{edge-CTom35R_.js → edge-DVbZnd3Y.js} +1 -1
  40. package/dist/web/assets/{edit-diff-preview-D1TEK7Jz.js → edit-diff-preview-ClJAJwlL.js} +1 -1
  41. package/dist/web/assets/{elixir-BAX9HSOu.js → elixir-Dwi8Wu7W.js} +1 -1
  42. package/dist/web/assets/{elm-CpD28xsW.js → elm-DtIv-HXJ.js} +1 -1
  43. package/dist/web/assets/{erb-DHX2IIEs.js → erb-D3HEDz1x.js} +1 -1
  44. package/dist/web/assets/{erlang-B_etLcPG.js → erlang-2MjTCzGz.js} +1 -1
  45. package/dist/web/assets/{esm-PmlN1RTs.js → esm-BfqDE09P.js} +1 -1
  46. package/dist/web/assets/explorer-body-DE5339g9.js +2 -0
  47. package/dist/web/assets/explorer-window-content-BrTHAoXM.js +1 -0
  48. package/dist/web/assets/{extension-webview-DkTI51P3.js → extension-webview-DRq1tWC1.js} +1 -1
  49. package/dist/web/assets/{fortran-fixed-form-CpR0cN7O.js → fortran-fixed-form-BLyAtyPM.js} +1 -1
  50. package/dist/web/assets/{fsharp-DxITxZW5.js → fsharp-Bxbn_gbr.js} +1 -1
  51. package/dist/web/assets/{gdresource-DJ7IvROZ.js → gdresource-sFMY4D2Q.js} +1 -1
  52. package/dist/web/assets/{git-commit-sQznNP43.js → git-commit-BzB9Lio5.js} +1 -1
  53. package/dist/web/assets/git-log-panel-pjyOjchU.js +1 -0
  54. package/dist/web/assets/{git-rebase-DkHvejAR.js → git-rebase-CKSVd848.js} +1 -1
  55. package/dist/web/assets/gitGraph-HDMCJU4V-CtQ3yXN-.js +1 -0
  56. package/dist/web/assets/{glide-data-grid-Bpgp72Hg.js → glide-data-grid-LX3N4SsJ.js} +9 -9
  57. package/dist/web/assets/{glimmer-js-BwJnDfL-.js → glimmer-js-BY8aExAJ.js} +1 -1
  58. package/dist/web/assets/{glimmer-ts-Coz155XK.js → glimmer-ts-Bak54dBX.js} +1 -1
  59. package/dist/web/assets/{glsl-BED7ZmUZ.js → glsl-nR2eaJQe.js} +1 -1
  60. package/dist/web/assets/{graphql-B_KyE6oz.js → graphql-CGPPFAU6.js} +1 -1
  61. package/dist/web/assets/group-chat-tab-CVQfptkT.js +1 -0
  62. package/dist/web/assets/{hack-sxywdIAQ.js → hack-CXIMX-Ol.js} +1 -1
  63. package/dist/web/assets/{haml-Dts9JlPU.js → haml-tPY-9Pfm.js} +1 -1
  64. package/dist/web/assets/{handlebars-C9HXIMtj.js → handlebars-BuW1LT7T.js} +1 -1
  65. package/dist/web/assets/{html-2p0RvXi4.js → html-bgVcwZ-t.js} +1 -1
  66. package/dist/web/assets/{html-derivative-BiTPuOba.js → html-derivative-D6-IxTgn.js} +1 -1
  67. package/dist/web/assets/{http-CHd2gYPf.js → http-CJ1zyB-h.js} +1 -1
  68. package/dist/web/assets/{hurl-boMPgDm9.js → hurl-CmoT7l3T.js} +1 -1
  69. package/dist/web/assets/{hxml-BQvjiuBt.js → hxml-CztOsL8o.js} +1 -1
  70. package/dist/web/assets/{image-preview-Dwg0EuJ2.js → image-preview-BDySmiim.js} +1 -1
  71. package/dist/web/assets/index-XoDY9bc0.js +52 -0
  72. package/dist/web/assets/index-YCTraslE.css +2 -0
  73. package/dist/web/assets/info-3K5VOQVL-Cg0mIJn9.js +1 -0
  74. package/dist/web/assets/{jinja-B-zKiwlp.js → jinja-B1pq3jJq.js} +1 -1
  75. package/dist/web/assets/{jison-C2vJYjbJ.js → jison-D8B5pCwA.js} +1 -1
  76. package/dist/web/assets/{julia-DJ8MrQPA.js → julia-D0K3bwpU.js} +1 -1
  77. package/dist/web/assets/{just-BvQHq7VE.js → just-B9Jlddu-.js} +1 -1
  78. package/dist/web/assets/keybindings-store-BE1jVUZW.js +1 -0
  79. package/dist/web/assets/{latex-ChNeshUC.js → latex-BPJSSE6j.js} +1 -1
  80. package/dist/web/assets/{liquid-KNJEBOwW.js → liquid-lv10fo4U.js} +1 -1
  81. package/dist/web/assets/{lua-D8NNsLbC.js → lua-PNuaIBEF.js} +1 -1
  82. package/dist/web/assets/{markdown-renderer-C7dlPs2S.js → markdown-renderer-B8Kz5rvP.js} +3 -3
  83. package/dist/web/assets/markdown-renderer-Btync7yg.js +1 -0
  84. package/dist/web/assets/{marko-HGo5Qtz4.js → marko-Du3frD6X.js} +1 -1
  85. package/dist/web/assets/{mdc-omkodQB5.js → mdc-BeDVcFRl.js} +1 -1
  86. package/dist/web/assets/mobile-bottom-sheet-CzRXE6mY.js +1 -0
  87. package/dist/web/assets/mobile-explorer-sheet-CDU8xriw.js +1 -0
  88. package/dist/web/assets/{nextflow-Adq02pv-.js → nextflow-DkcXP71s.js} +1 -1
  89. package/dist/web/assets/{nginx-ByvWruGD.js → nginx-C5n8N_bt.js} +1 -1
  90. package/dist/web/assets/{nim-BrMojX4O.js → nim-CIhyfs-A.js} +1 -1
  91. package/dist/web/assets/notification-store-Bi7VHAy4.js +1 -0
  92. package/dist/web/assets/{number-overlay-editor-CunvWJnN.js → number-overlay-editor-B8P26qGC.js} +1 -1
  93. package/dist/web/assets/packet-RMMSAZCW-D79vAFR_.js +1 -0
  94. package/dist/web/assets/{panel-store-CpKvXNnm.js → panel-store-gbNJvZB_.js} +1 -1
  95. package/dist/web/assets/{pdf-preview-CEWYMxdp.js → pdf-preview-CtZWvujY.js} +1 -1
  96. package/dist/web/assets/{perl-Bd_0l-b4.js → perl-Cf31sZYE.js} +1 -1
  97. package/dist/web/assets/{php-D-DLTnWJ.js → php-Dzl_MDIW.js} +1 -1
  98. package/dist/web/assets/pie-UPGHQEXC-DYUOH68U.js +1 -0
  99. package/dist/web/assets/{postgres-viewer-D7PggslU.js → postgres-viewer-fqMUi0xS.js} +3 -3
  100. package/dist/web/assets/{project-store-D0ALup9Z.js → project-store-D1D1MN03.js} +1 -1
  101. package/dist/web/assets/{pug-D2aO-l2T.js → pug-CxSYHrB3.js} +1 -1
  102. package/dist/web/assets/{qml-CyQVzNXS.js → qml-DlEjGhwN.js} +1 -1
  103. package/dist/web/assets/radar-KQ55EAFF-B1eznNtd.js +1 -0
  104. package/dist/web/assets/{razor-8tvJXsO3.js → razor-KVt5uW14.js} +1 -1
  105. package/dist/web/assets/{rst-C4AZyQQl.js → rst-D5BwNTnw.js} +1 -1
  106. package/dist/web/assets/{ruby-O_z2NTLY.js → ruby-D5fejQwd.js} +1 -1
  107. package/dist/web/assets/{sas-BtnF4zaV.js → sas-Bj7B1CB2.js} +1 -1
  108. package/dist/web/assets/{scss-GAyp5euH.js → scss-BB0CUoCL.js} +1 -1
  109. package/dist/web/assets/settings-store-Bm7uL8VA.js +2 -0
  110. package/dist/web/assets/settings-tab-DxtBG0DW.js +1 -0
  111. package/dist/web/assets/{shaderlab-nM5YZwa5.js → shaderlab-BdjEZUMc.js} +1 -1
  112. package/dist/web/assets/{shellsession-D2OCI4Gv.js → shellsession-Bp1OA1rR.js} +1 -1
  113. package/dist/web/assets/{soy-DrLNhqfp.js → soy-DlYz3wsC.js} +1 -1
  114. package/dist/web/assets/sparkles-BvJ1EGWd.js +1 -0
  115. package/dist/web/assets/{sparql-CsM6F2Yv.js → sparql-CdfII5Jd.js} +1 -1
  116. package/dist/web/assets/{sql-query-editor-ZnKcxCyj.js → sql-query-editor-D2UxRvG7.js} +1 -1
  117. package/dist/web/assets/sqlite-viewer-CTBwzT-r.js +1 -0
  118. package/dist/web/assets/{stata-Bk3cgfXJ.js → stata-BvUA6GUW.js} +1 -1
  119. package/dist/web/assets/{surrealql-Dkz0DPK8.js → surrealql--DLNbTyI.js} +1 -1
  120. package/dist/web/assets/{svelte-23si1KV5.js → svelte-CBfSsG4n.js} +1 -1
  121. package/dist/web/assets/{system-monitor-tab-Ck3REHUg.js → system-monitor-tab-Bscf6tzK.js} +1 -1
  122. package/dist/web/assets/{tab-store-BQmDVh9c.js → tab-store-DnNnaAcE.js} +1 -1
  123. package/dist/web/assets/{templ-CPxlP4vF.js → templ-Dv8zpKZW.js} +1 -1
  124. package/dist/web/assets/{terminal-tab-D6tYDFSF.js → terminal-tab-CTKU5gaF.js} +3 -3
  125. package/dist/web/assets/{tex-ugJpgYPc.js → tex-CsrOiqh1.js} +1 -1
  126. package/dist/web/assets/tool-cards-xstpayaW.js +7 -0
  127. package/dist/web/assets/treemap-KZPCXAKY-SeI2TxLs.js +1 -0
  128. package/dist/web/assets/{ts-tags-BovP9TMu.js → ts-tags-3T98k4EJ.js} +1 -1
  129. package/dist/web/assets/{twig-B7VUm2Ca.js → twig-u6QZ45FH.js} +1 -1
  130. package/dist/web/assets/{use-blob-url-B3YovXF6.js → use-blob-url-BLRmhlZI.js} +1 -1
  131. package/dist/web/assets/{use-monaco-theme-wKbyNwIn.js → use-monaco-theme-DxYstgYa.js} +1 -1
  132. package/dist/web/assets/{vendor-mermaid-CYOBtZtD.js → vendor-mermaid-C977xNeE.js} +1 -1
  133. package/dist/web/assets/{vendor-ui-DMRGWRuc.js → vendor-ui-CXJtBULr.js} +1 -1
  134. package/dist/web/assets/{video-preview-DwrfenNA.js → video-preview-cfaoRxG3.js} +1 -1
  135. package/dist/web/assets/{vue-m75pWowk.js → vue-CxIsxtwn.js} +1 -1
  136. package/dist/web/assets/{vue-html-odHs7IMn.js → vue-html-BGL5qnuH.js} +1 -1
  137. package/dist/web/assets/{vue-vine-BbBB3V_G.js → vue-vine-DHPh_gMr.js} +1 -1
  138. package/dist/web/assets/{xml-BHzIcDWI.js → xml-C-vM2m7J.js} +1 -1
  139. package/dist/web/assets/{xsl-DTLhN7uu.js → xsl-DvTXNwuO.js} +1 -1
  140. package/dist/web/index.html +26 -23
  141. package/dist/web/sw.js +1 -1
  142. package/docs/codebase-summary.md +10 -1
  143. package/docs/design-guidelines.md +31 -0
  144. package/docs/project-roadmap.md +10 -0
  145. package/docs/system-architecture.md +59 -0
  146. package/package.json +2 -1
  147. package/src/providers/claude-agent-sdk.ts +6 -0
  148. package/src/server/index.ts +8 -0
  149. package/src/server/routes/files.ts +2 -2
  150. package/src/server/routes/fs-browse.ts +99 -74
  151. package/src/server/routes/fs-ops.ts +118 -0
  152. package/src/server/routes/fs-sqlite.ts +129 -0
  153. package/src/server/routes/host-info.ts +17 -0
  154. package/src/server/routes/settings.ts +2 -0
  155. package/src/services/download-token.service.ts +21 -5
  156. package/src/services/file.service.ts +8 -12
  157. package/src/services/fs-browse.service.ts +104 -161
  158. package/src/services/fs-ops/fs-content-disposition.ts +17 -0
  159. package/src/services/fs-ops/fs-core-ops.ts +149 -0
  160. package/src/services/fs-ops/fs-error-response.ts +24 -0
  161. package/src/services/fs-ops/fs-git-init.service.ts +23 -0
  162. package/src/services/fs-ops/fs-hidden-names.ts +17 -0
  163. package/src/services/fs-ops/fs-list-files.service.ts +68 -0
  164. package/src/services/fs-ops/fs-ops-copy-move.service.ts +60 -0
  165. package/src/services/fs-ops/fs-ops-mutate.service.ts +59 -0
  166. package/src/services/fs-ops/fs-ops-read-write.service.ts +70 -0
  167. package/src/services/fs-ops/fs-ops-stat.service.ts +109 -0
  168. package/src/services/fs-ops/fs-ops-trash.service.ts +111 -0
  169. package/src/services/fs-ops/fs-real-path.ts +44 -0
  170. package/src/services/fs-ops/sql-row-reader.ts +26 -0
  171. package/src/services/fs-ops/sql-statement-guard.ts +63 -0
  172. package/src/services/fs-path-guard.service.ts +196 -0
  173. package/src/services/host-info/apple-bookmark-decoder.ts +97 -0
  174. package/src/services/host-info/apple-plist-xml-parser.ts +107 -0
  175. package/src/services/host-info/concurrency-limit.ts +20 -0
  176. package/src/services/host-info/host-drives.ts +190 -0
  177. package/src/services/host-info/host-info.service.ts +171 -0
  178. package/src/services/host-info/host-known-folders.ts +180 -0
  179. package/src/services/host-info/pinned-linux-bookmarks.ts +89 -0
  180. package/src/services/host-info/pinned-macos-finder-favorites.ts +175 -0
  181. package/src/services/host-info/pinned-windows-quick-access.ts +87 -0
  182. package/src/services/host-info/spawn-runner.ts +41 -0
  183. package/src/services/jsonl-transcript-parser.ts +6 -0
  184. package/src/services/sqlite-types.ts +26 -0
  185. package/src/services/sqlite.service.ts +38 -29
  186. package/src/services/subagent-transcript-merger.ts +144 -0
  187. package/src/types/system.ts +50 -0
  188. package/src/web/app.tsx +19 -3
  189. package/src/web/components/editor/code-editor.tsx +3 -9
  190. package/src/web/components/explorer/file-tree.tsx +103 -21
  191. package/src/web/components/explorer/tree-node-context-menu.tsx +6 -1
  192. package/src/web/components/explorer/tree-node.tsx +34 -93
  193. package/src/web/components/explorer/use-file-upload-drag.ts +14 -9
  194. package/src/web/components/explorer/use-tree-row-dnd.ts +206 -0
  195. package/src/web/components/floating-window/default-window-chrome.tsx +71 -0
  196. package/src/web/components/floating-window/floating-window.tsx +167 -0
  197. package/src/web/components/floating-window/use-window-drag.ts +28 -0
  198. package/src/web/components/floating-window/use-window-gesture-context.ts +44 -0
  199. package/src/web/components/floating-window/use-window-keyboard.ts +55 -0
  200. package/src/web/components/floating-window/use-window-resize.ts +30 -0
  201. package/src/web/components/floating-window/window-chrome-contract.ts +38 -0
  202. package/src/web/components/floating-window/window-content-registry.ts +51 -0
  203. package/src/web/components/floating-window/window-geometry.ts +121 -0
  204. package/src/web/components/floating-window/window-layer.tsx +92 -0
  205. package/src/web/components/floating-window/window-persistence.ts +104 -0
  206. package/src/web/components/floating-window/window-resize-handles.tsx +81 -0
  207. package/src/web/components/floating-window/window-store-types.ts +20 -0
  208. package/src/web/components/floating-window/window-store.ts +192 -0
  209. package/src/web/components/layout/command-palette.tsx +10 -0
  210. package/src/web/components/layout/mobile-drawer.tsx +11 -2
  211. package/src/web/components/layout/nav-section-rail.tsx +7 -1
  212. package/src/web/components/os-explorer/actions/explorer-actions-clipboard.ts +186 -0
  213. package/src/web/components/os-explorer/actions/explorer-actions-mutate.ts +109 -0
  214. package/src/web/components/os-explorer/actions/explorer-actions-open.ts +67 -0
  215. package/src/web/components/os-explorer/actions/explorer-actions-transfer-helpers.ts +35 -0
  216. package/src/web/components/os-explorer/actions/use-explorer-actions.ts +205 -0
  217. package/src/web/components/os-explorer/can-open-in-ppm.ts +89 -0
  218. package/src/web/components/os-explorer/dnd/drag-depth-counter.ts +36 -0
  219. package/src/web/components/os-explorer/dnd/drag-ghost.ts +44 -0
  220. package/src/web/components/os-explorer/dnd/drop-target-decision.ts +46 -0
  221. package/src/web/components/os-explorer/dnd/drop-target-style.ts +7 -0
  222. package/src/web/components/os-explorer/dnd/entry-drag-payload.ts +115 -0
  223. package/src/web/components/os-explorer/dnd/entry-drag-state.ts +35 -0
  224. package/src/web/components/os-explorer/dnd/entry-drop-executor.ts +43 -0
  225. package/src/web/components/os-explorer/dnd/use-drag-auto-scroll.ts +70 -0
  226. package/src/web/components/os-explorer/dnd/use-drop-transfer.tsx +82 -0
  227. package/src/web/components/os-explorer/dnd/use-entry-drag-source.ts +73 -0
  228. package/src/web/components/os-explorer/dnd/use-entry-drop-target.ts +150 -0
  229. package/src/web/components/os-explorer/dnd/use-entry-row-dnd.ts +66 -0
  230. package/src/web/components/os-explorer/dnd/use-path-drop-target.ts +30 -0
  231. package/src/web/components/os-explorer/explorer-body.tsx +153 -0
  232. package/src/web/components/os-explorer/explorer-breadcrumb.tsx +108 -0
  233. package/src/web/components/os-explorer/explorer-context-menu.tsx +131 -0
  234. package/src/web/components/os-explorer/explorer-dialogs.tsx +103 -0
  235. package/src/web/components/os-explorer/explorer-modal-shell.tsx +60 -0
  236. package/src/web/components/os-explorer/explorer-pins-store.ts +63 -0
  237. package/src/web/components/os-explorer/explorer-sidebar.tsx +198 -0
  238. package/src/web/components/os-explorer/explorer-sort-menu.tsx +68 -0
  239. package/src/web/components/os-explorer/explorer-status-bar.tsx +35 -0
  240. package/src/web/components/os-explorer/explorer-store.ts +179 -0
  241. package/src/web/components/os-explorer/explorer-toolbar.tsx +139 -0
  242. package/src/web/components/os-explorer/explorer-window-content.tsx +36 -0
  243. package/src/web/components/os-explorer/format-file-meta.ts +74 -0
  244. package/src/web/components/os-explorer/icons/file-type-icon-map.ts +106 -0
  245. package/src/web/components/os-explorer/icons/file-type-icon.tsx +56 -0
  246. package/src/web/components/os-explorer/icons/thumbnail-fetch-semaphore.ts +44 -0
  247. package/src/web/components/os-explorer/icons/thumbnail-image.tsx +124 -0
  248. package/src/web/components/os-explorer/mobile/mobile-bottom-toolbar.tsx +102 -0
  249. package/src/web/components/os-explorer/mobile/mobile-explorer-sheet.tsx +61 -0
  250. package/src/web/components/os-explorer/mobile/mobile-explorer-top-bar.tsx +41 -0
  251. package/src/web/components/os-explorer/mobile/mobile-more-sheet.tsx +117 -0
  252. package/src/web/components/os-explorer/mobile/mobile-places-sheet.tsx +120 -0
  253. package/src/web/components/os-explorer/mobile/mobile-places-strip.tsx +109 -0
  254. package/src/web/components/os-explorer/mobile/mobile-tap-action.ts +19 -0
  255. package/src/web/components/os-explorer/mobile/use-mobile-row-tap.ts +50 -0
  256. package/src/web/components/os-explorer/open-explorer.ts +35 -0
  257. package/src/web/components/os-explorer/properties-dialog.tsx +99 -0
  258. package/src/web/components/os-explorer/skins/explorer-skin-chrome.tsx +17 -0
  259. package/src/web/components/os-explorer/skins/folder-icon-macos.tsx +42 -0
  260. package/src/web/components/os-explorer/skins/folder-icon-windows.tsx +25 -0
  261. package/src/web/components/os-explorer/skins/macos-skin.ts +14 -0
  262. package/src/web/components/os-explorer/skins/macos-window-chrome.tsx +69 -0
  263. package/src/web/components/os-explorer/skins/skin-types.ts +40 -0
  264. package/src/web/components/os-explorer/skins/skins.css +25 -0
  265. package/src/web/components/os-explorer/skins/use-explorer-skin.ts +21 -0
  266. package/src/web/components/os-explorer/skins/windows-skin.ts +11 -0
  267. package/src/web/components/os-explorer/skins/windows-window-chrome.tsx +56 -0
  268. package/src/web/components/os-explorer/sort-and-filter-entries.ts +52 -0
  269. package/src/web/components/os-explorer/sort-options.ts +29 -0
  270. package/src/web/components/os-explorer/toolbar-icon-button.ts +18 -0
  271. package/src/web/components/os-explorer/use-coarse-long-press.ts +97 -0
  272. package/src/web/components/os-explorer/use-explorer-keyboard.ts +124 -0
  273. package/src/web/components/os-explorer/use-explorer-navigation.ts +155 -0
  274. package/src/web/components/os-explorer/use-explorer-open-state.ts +67 -0
  275. package/src/web/components/os-explorer/use-host-info.ts +85 -0
  276. package/src/web/components/os-explorer/views/column-row.tsx +126 -0
  277. package/src/web/components/os-explorer/views/column-view-column.tsx +112 -0
  278. package/src/web/components/os-explorer/views/column-view-mobile.tsx +98 -0
  279. package/src/web/components/os-explorer/views/column-view-preview.tsx +70 -0
  280. package/src/web/components/os-explorer/views/column-view.tsx +204 -0
  281. package/src/web/components/os-explorer/views/compose-interactive-row-props.ts +77 -0
  282. package/src/web/components/os-explorer/views/explorer-view-registry.tsx +55 -0
  283. package/src/web/components/os-explorer/views/icons-view-tile.tsx +158 -0
  284. package/src/web/components/os-explorer/views/icons-view.tsx +188 -0
  285. package/src/web/components/os-explorer/views/inline-name-input.tsx +60 -0
  286. package/src/web/components/os-explorer/views/list-row.tsx +162 -0
  287. package/src/web/components/os-explorer/views/list-view.tsx +177 -0
  288. package/src/web/components/os-explorer/views/use-column-view-state.ts +158 -0
  289. package/src/web/components/os-explorer/views/use-entry-selection.ts +178 -0
  290. package/src/web/components/settings/settings-tab.tsx +29 -2
  291. package/src/web/components/sqlite/sqlite-viewer.tsx +5 -2
  292. package/src/web/components/sqlite/use-sqlite.ts +7 -2
  293. package/src/web/components/ui/adaptive-context-menu.tsx +57 -1
  294. package/src/web/components/ui/file-browser-picker.tsx +1 -18
  295. package/src/web/hooks/use-chat.ts +22 -4
  296. package/src/web/lib/file-download.ts +6 -4
  297. package/src/web/lib/fs-api.ts +185 -0
  298. package/src/web/stores/file-store.ts +38 -1
  299. package/src/web/stores/settings-store.ts +14 -0
  300. package/src/web/styles/globals.css +1 -0
  301. package/dist/web/assets/api-client-RtNXvS0P.js +0 -1
  302. package/dist/web/assets/api-settings-De--HNHp.js +0 -1
  303. package/dist/web/assets/architecture-PBZL5I3N-DVRYSsQn.js +0 -1
  304. package/dist/web/assets/chat-tab-D2nZCpAo.js +0 -13
  305. package/dist/web/assets/code-editor-DqQy2Qm8.js +0 -10
  306. package/dist/web/assets/context-menu-DIiZdfbE.js +0 -1
  307. package/dist/web/assets/csv-preview-BL0I3CKz.js +0 -2
  308. package/dist/web/assets/dialog-HHdNInEH.js +0 -1
  309. package/dist/web/assets/diff-viewer-_yQtutHR.js +0 -4
  310. package/dist/web/assets/git-log-panel-NkghB_rO.js +0 -1
  311. package/dist/web/assets/gitGraph-HDMCJU4V-CutkUNY_.js +0 -1
  312. package/dist/web/assets/group-chat-tab-CQOVBmVp.js +0 -1
  313. package/dist/web/assets/index-CJ0Wp_rW.js +0 -50
  314. package/dist/web/assets/index-CVJmlKhp.css +0 -2
  315. package/dist/web/assets/info-3K5VOQVL-BpNjgbj9.js +0 -1
  316. package/dist/web/assets/keybindings-store-ClNYe9vD.js +0 -1
  317. package/dist/web/assets/markdown-renderer-CwpP5Ola.js +0 -1
  318. package/dist/web/assets/notification-store-DwAhNzTy.js +0 -1
  319. package/dist/web/assets/packet-RMMSAZCW-D5IUZ66-.js +0 -1
  320. package/dist/web/assets/pie-UPGHQEXC-CM29eKDR.js +0 -1
  321. package/dist/web/assets/radar-KQ55EAFF-QtJGaSGt.js +0 -1
  322. package/dist/web/assets/settings-store-B3LfpCPa.js +0 -2
  323. package/dist/web/assets/settings-tab-CeSJQIfF.js +0 -1
  324. package/dist/web/assets/sparkles-B3DmG8PJ.js +0 -1
  325. package/dist/web/assets/sqlite-viewer-8fk_wut8.js +0 -1
  326. package/dist/web/assets/tool-cards-C3ka1Y7I.js +0 -7
  327. package/dist/web/assets/treemap-KZPCXAKY-CjUqpVnS.js +0 -1
  328. /package/dist/web/assets/{abap-BM5PMK0-.js → abap-C1K0-wzc.js} +0 -0
  329. /package/dist/web/assets/{actionscript-3-CML-MTNy.js → actionscript-3-CJhQVjLA.js} +0 -0
  330. /package/dist/web/assets/{ada-DzO1tMqV.js → ada-Bt8r9fME.js} +0 -0
  331. /package/dist/web/assets/{andromeeda-C3nkvRhj.js → andromeeda-DF1I8qi3.js} +0 -0
  332. /package/dist/web/assets/{apache-Dg7oWQbs.js → apache-CnlleE6P.js} +0 -0
  333. /package/dist/web/assets/{apex-3O2170Ju.js → apex-j0C6Xmk6.js} +0 -0
  334. /package/dist/web/assets/{applescript-5AlzBqQO.js → applescript-DO7AnO8U.js} +0 -0
  335. /package/dist/web/assets/{apply-theme-BSJDABU3.js → apply-theme-CMQXnUAa.js} +0 -0
  336. /package/dist/web/assets/{ara-BGtOX5Hn.js → ara-i83AydZj.js} +0 -0
  337. /package/dist/web/assets/{asciidoc-CIsC4kkq.js → asciidoc-DbBA6-sF.js} +0 -0
  338. /package/dist/web/assets/{asm-p1-PUkh3.js → asm-B5S47Rgr.js} +0 -0
  339. /package/dist/web/assets/{aurora-x-BPgGL50S.js → aurora-x-De_IaSKw.js} +0 -0
  340. /package/dist/web/assets/{awk-Cj93pRL8.js → awk-CfyLHXqS.js} +0 -0
  341. /package/dist/web/assets/{ayu-dark-C50MYKhw.js → ayu-dark-DTLdZ1Si.js} +0 -0
  342. /package/dist/web/assets/{ayu-light-DyIp0Idy.js → ayu-light-DTOfkzhu.js} +0 -0
  343. /package/dist/web/assets/{ayu-mirage-BAdm8pKU.js → ayu-mirage-DsAECCMv.js} +0 -0
  344. /package/dist/web/assets/{ballerina-DUO9mwyV.js → ballerina-CumGp9A7.js} +0 -0
  345. /package/dist/web/assets/{bat-Cg8hlSct.js → bat-Ca2SBbYN.js} +0 -0
  346. /package/dist/web/assets/{beancount-f8f3BBqB.js → beancount-CqyXBwwF.js} +0 -0
  347. /package/dist/web/assets/{berry-BqVQYZZr.js → berry-yl-kJr_H.js} +0 -0
  348. /package/dist/web/assets/{bibtex-C_q8ygAM.js → bibtex-DoFFszjV.js} +0 -0
  349. /package/dist/web/assets/{bicep-DWS-2uWR.js → bicep-CWPi4nfK.js} +0 -0
  350. /package/dist/web/assets/{bird2-C0pvipi_.js → bird2-DXew-ut0.js} +0 -0
  351. /package/dist/web/assets/{bot-E9mcVZnr.js → bot-BHXjpQ7x.js} +0 -0
  352. /package/dist/web/assets/{c-BQFvJ6Ux.js → c-clbySDvd.js} +0 -0
  353. /package/dist/web/assets/{c3-Da53KeG6.js → c3-B65jvSM1.js} +0 -0
  354. /package/dist/web/assets/{cadence-B5X2vWm3.js → cadence-Cm8tQTHN.js} +0 -0
  355. /package/dist/web/assets/{catppuccin-frappe-Dm3rVY3b.js → catppuccin-frappe-DLeGslh_.js} +0 -0
  356. /package/dist/web/assets/{catppuccin-latte-ZxS-ga_K.js → catppuccin-latte-CR5GeHws.js} +0 -0
  357. /package/dist/web/assets/{catppuccin-macchiato-B0bAzXh0.js → catppuccin-macchiato-p-XgjxhP.js} +0 -0
  358. /package/dist/web/assets/{catppuccin-mocha-BeMC5hqi.js → catppuccin-mocha-B8QOAZLl.js} +0 -0
  359. /package/dist/web/assets/{chevron-right-DeUue2aK.js → chevron-right-CtcHmZg9.js} +0 -0
  360. /package/dist/web/assets/{circle-question-mark-PaJgx-3d.js → circle-question-mark-EVgisosn.js} +0 -0
  361. /package/dist/web/assets/{clarity-BRdirlsw.js → clarity-DPulbvKH.js} +0 -0
  362. /package/dist/web/assets/{clojure-pSLH-KQp.js → clojure-DFuWS20u.js} +0 -0
  363. /package/dist/web/assets/{cmake-DsZu4U4F.js → cmake-DejCqcu4.js} +0 -0
  364. /package/dist/web/assets/{code-TekEnDFM.js → code-Dupqw5Fr.js} +0 -0
  365. /package/dist/web/assets/{codeowners-CtkKTnsH.js → codeowners-BwAyqWIH.js} +0 -0
  366. /package/dist/web/assets/{codeql-BpP1PDrj.js → codeql-DgSwFjqI.js} +0 -0
  367. /package/dist/web/assets/{common-lisp-BX1XgHTt.js → common-lisp-CxIVJCtf.js} +0 -0
  368. /package/dist/web/assets/{coq-D2bX6-8g.js → coq-DLSjSnmf.js} +0 -0
  369. /package/dist/web/assets/{csharp-DcR97P0D.js → csharp-C1HHzXBp.js} +0 -0
  370. /package/dist/web/assets/{css-CIjciIIk.js → css-VXsMY--y.js} +0 -0
  371. /package/dist/web/assets/{csv-DW48Z5d-.js → csv-BEbcJdHz.js} +0 -0
  372. /package/dist/web/assets/{csv-parser-DVHBcvuE.js → csv-parser-DHvoTjMF.js} +0 -0
  373. /package/dist/web/assets/{cue-Byh1xHko.js → cue-BkDnFqUD.js} +0 -0
  374. /package/dist/web/assets/{cypher-TVlMMWma.js → cypher-1cMqojSQ.js} +0 -0
  375. /package/dist/web/assets/{d-CxaScvxf.js → d-BnYjRXtH.js} +0 -0
  376. /package/dist/web/assets/{dark-plus-sn-FFTT1.js → dark-plus-XfbaH7kZ.js} +0 -0
  377. /package/dist/web/assets/{dart-BmpakMe-.js → dart-Ca7s-8hK.js} +0 -0
  378. /package/dist/web/assets/{data-grid-types-BuMHLKXV.js → data-grid-types-DP-ZexEN.js} +0 -0
  379. /package/dist/web/assets/{database-COYAMZaw.js → database-Dj0DxgtX.js} +0 -0
  380. /package/dist/web/assets/{dax-B1aRUMXT.js → dax-CmuOw-mY.js} +0 -0
  381. /package/dist/web/assets/{desktop-C6lD62ar.js → desktop-C2gJ6Ovb.js} +0 -0
  382. /package/dist/web/assets/{diff-CupGmT1a.js → diff-BCEo1Ej7.js} +0 -0
  383. /package/dist/web/assets/{dist-Bi4gRyhr.js → dist-CipvJy9w.js} +0 -0
  384. /package/dist/web/assets/{dist-m_oJqIDk.js → dist-D__U4ZJz.js} +0 -0
  385. /package/dist/web/assets/{docker-DjDr3TJF.js → docker-DN2jlyAh.js} +0 -0
  386. /package/dist/web/assets/{dotenv-QeT1SLTv.js → dotenv-NExMY2kS.js} +0 -0
  387. /package/dist/web/assets/{dracula-yI5kpKp5.js → dracula-B9lbE01y.js} +0 -0
  388. /package/dist/web/assets/{dracula-soft-CYF6cJce.js → dracula-soft-CMBBfKF_.js} +0 -0
  389. /package/dist/web/assets/{dream-maker-TBDKiHrV.js → dream-maker-DEyDwFsd.js} +0 -0
  390. /package/dist/web/assets/{emacs-lisp-B7aZTnJ_.js → emacs-lisp-D_qP2zS6.js} +0 -0
  391. /package/dist/web/assets/{everforest-dark-CN77B1NJ.js → everforest-dark-DbHa2U-p.js} +0 -0
  392. /package/dist/web/assets/{everforest-light-CyFxGMIs.js → everforest-light-BISkJchP.js} +0 -0
  393. /package/dist/web/assets/{fennel-C6ktGUzp.js → fennel-6B22SfJR.js} +0 -0
  394. /package/dist/web/assets/{file-exclamation-point-DBEC-Kst.js → file-exclamation-point-D-ZORzYB.js} +0 -0
  395. /package/dist/web/assets/{fish-Vr62C2vP.js → fish-CubIIvJD.js} +0 -0
  396. /package/dist/web/assets/{fluent-CH3IJUki.js → fluent-DF55zQ09.js} +0 -0
  397. /package/dist/web/assets/{fortran-free-form-BN7VmmxZ.js → fortran-free-form-DvOu8jAt.js} +0 -0
  398. /package/dist/web/assets/{gdscript-BYdRNu_P.js → gdscript-DCPxdLpq.js} +0 -0
  399. /package/dist/web/assets/{gdshader-C2XOq90k.js → gdshader-CTZ381FO.js} +0 -0
  400. /package/dist/web/assets/{genie-B4hkIXNU.js → genie-C_QDtvgU.js} +0 -0
  401. /package/dist/web/assets/{gherkin-CW9NH2DW.js → gherkin-BcvK42tF.js} +0 -0
  402. /package/dist/web/assets/{github-dark-BWCf99kM.js → github-dark-C9afs_4w.js} +0 -0
  403. /package/dist/web/assets/{github-dark-default-DZ1m_6OP.js → github-dark-default-CKGVrl3Z.js} +0 -0
  404. /package/dist/web/assets/{github-dark-dimmed-CUXSQ8bS.js → github-dark-dimmed-C3dQZ_LC.js} +0 -0
  405. /package/dist/web/assets/{github-dark-high-contrast-B6BvU8H-.js → github-dark-high-contrast-DW78QukY.js} +0 -0
  406. /package/dist/web/assets/{github-light-Ch-E92cy.js → github-light-BnP2dznS.js} +0 -0
  407. /package/dist/web/assets/{github-light-default-CKFslV7i.js → github-light-default-C2fPZzPG.js} +0 -0
  408. /package/dist/web/assets/{github-light-high-contrast-DiJVeRlZ.js → github-light-high-contrast-BzIuUFR2.js} +0 -0
  409. /package/dist/web/assets/{gleam-D4dP7V5M.js → gleam-BpeKwEIO.js} +0 -0
  410. /package/dist/web/assets/{gn-DuO2D2eW.js → gn-nUb_Mit0.js} +0 -0
  411. /package/dist/web/assets/{gnuplot-Df_8pFbI.js → gnuplot-D7I0v20p.js} +0 -0
  412. /package/dist/web/assets/{go-BL16cMJO.js → go-D7Qqska7.js} +0 -0
  413. /package/dist/web/assets/{groovy-rfHeCRqy.js → groovy-DS-pne5-.js} +0 -0
  414. /package/dist/web/assets/{gruvbox-dark-hard-CLbDN-E3.js → gruvbox-dark-hard-C0W01fsq.js} +0 -0
  415. /package/dist/web/assets/{gruvbox-dark-medium-Df0L0qje.js → gruvbox-dark-medium-Bqo6fQbg.js} +0 -0
  416. /package/dist/web/assets/{gruvbox-dark-soft-DV6TAcen.js → gruvbox-dark-soft-D-XSM_fu.js} +0 -0
  417. /package/dist/web/assets/{gruvbox-light-hard-CWXUzp9b.js → gruvbox-light-hard-IVAwMJRl.js} +0 -0
  418. /package/dist/web/assets/{gruvbox-light-medium-BOHMe1-7.js → gruvbox-light-medium-38ATiG7i.js} +0 -0
  419. /package/dist/web/assets/{gruvbox-light-soft-DrrS2sdV.js → gruvbox-light-soft-DnZr3c_0.js} +0 -0
  420. /package/dist/web/assets/{haskell-DybKb9uW.js → haskell-uQcywkDH.js} +0 -0
  421. /package/dist/web/assets/{haxe-DLRwZxmt.js → haxe-c6bI4_GT.js} +0 -0
  422. /package/dist/web/assets/{hcl-CusCzwOk.js → hcl-CQXTiEXq.js} +0 -0
  423. /package/dist/web/assets/{hjson-BygWIJ-3.js → hjson-ChETf2bz.js} +0 -0
  424. /package/dist/web/assets/{hlsl-DCKvuEyZ.js → hlsl-BMC4JGoE.js} +0 -0
  425. /package/dist/web/assets/{horizon-DSOVAzyH.js → horizon-BusFlhLW.js} +0 -0
  426. /package/dist/web/assets/{horizon-bright-DKfVjGj_.js → horizon-bright-2oD0Mz7W.js} +0 -0
  427. /package/dist/web/assets/{houston-Ddm8AgpV.js → houston-DdbbjqFg.js} +0 -0
  428. /package/dist/web/assets/{hy-DzZa6alE.js → hy-D_SOuqw9.js} +0 -0
  429. /package/dist/web/assets/{image-gallery-CObzTisL.js → image-gallery-BWFpZAAP.js} +0 -0
  430. /package/dist/web/assets/{imba-aiXalPnK.js → imba-BxLOHO5I.js} +0 -0
  431. /package/dist/web/assets/{ini-CAvrVa5R.js → ini-ox-CFcg0.js} +0 -0
  432. /package/dist/web/assets/{java-PRMSQp24.js → java-Bzo8yIal.js} +0 -0
  433. /package/dist/web/assets/{javascript-DQR6GQKs.js → javascript-DHuz9VXn.js} +0 -0
  434. /package/dist/web/assets/{json-uWUKqOxq.js → json-0_y80FD5.js} +0 -0
  435. /package/dist/web/assets/{json5-R_LXJXL0.js → json5-CEQ50pGM.js} +0 -0
  436. /package/dist/web/assets/{jsonc-tC5zvrPO.js → jsonc-CfrLEAKA.js} +0 -0
  437. /package/dist/web/assets/{jsonl-BAwnWh7U.js → jsonl-fKbrANC2.js} +0 -0
  438. /package/dist/web/assets/{jsonnet-CPj4HCRm.js → jsonnet-DKbkPaQ2.js} +0 -0
  439. /package/dist/web/assets/{jssm-B7HswxOM.js → jssm-Dxxx1Hqu.js} +0 -0
  440. /package/dist/web/assets/{jsx-z4TGELE1.js → jsx-DlJL-Oj3.js} +0 -0
  441. /package/dist/web/assets/{kanagawa-dragon-BpeLlraV.js → kanagawa-dragon-CwN4SV1c.js} +0 -0
  442. /package/dist/web/assets/{kanagawa-lotus-CLnIPAGs.js → kanagawa-lotus-BHltayiE.js} +0 -0
  443. /package/dist/web/assets/{kanagawa-wave-BglPniBb.js → kanagawa-wave-D-KXPjw6.js} +0 -0
  444. /package/dist/web/assets/{kdl-B6blIqLq.js → kdl-DvtoJdFo.js} +0 -0
  445. /package/dist/web/assets/{kotlin-DVAEYiwY.js → kotlin-CTS4MLcx.js} +0 -0
  446. /package/dist/web/assets/{kusto-Ba87977j.js → kusto-HtSE_sKm.js} +0 -0
  447. /package/dist/web/assets/{laserwave-C-NuzoMp.js → laserwave-DaXZG9ge.js} +0 -0
  448. /package/dist/web/assets/{lean-CJ_KLkrJ.js → lean-BYtGUfWx.js} +0 -0
  449. /package/dist/web/assets/{less-Dfn9YC_2.js → less-BoxmU_Mk.js} +0 -0
  450. /package/dist/web/assets/{lib-DTaN9riD.js → lib-Bpbn00xM.js} +0 -0
  451. /package/dist/web/assets/{libesm-DdtEjhIi.js → libesm-tR4XlhMj.js} +0 -0
  452. /package/dist/web/assets/{light-plus-B3BLlguG.js → light-plus-Bt5CyUuo.js} +0 -0
  453. /package/dist/web/assets/{llvm-6UDP3NQb.js → llvm-CSdk9q9V.js} +0 -0
  454. /package/dist/web/assets/{log-QW3dqr1D.js → log-B-kp_3CL.js} +0 -0
  455. /package/dist/web/assets/{logo-DUinJheI.js → logo-DdIsXFDO.js} +0 -0
  456. /package/dist/web/assets/{luau-B1vcBzl8.js → luau-DUBpLlzQ.js} +0 -0
  457. /package/dist/web/assets/{make-CqNL10gS.js → make-B3JVjoW_.js} +0 -0
  458. /package/dist/web/assets/{markdown-BQNMNiYS.js → markdown-Seon0vHs.js} +0 -0
  459. /package/dist/web/assets/{material-theme-rbV8EefR.js → material-theme-Nq-ChX1P.js} +0 -0
  460. /package/dist/web/assets/{material-theme-darker-BZrskvoo.js → material-theme-darker-CPV4ayXJ.js} +0 -0
  461. /package/dist/web/assets/{material-theme-lighter-B0CBBjBw.js → material-theme-lighter-0kyjYqEd.js} +0 -0
  462. /package/dist/web/assets/{material-theme-ocean-CX_CHnQq.js → material-theme-ocean-DGpAp5co.js} +0 -0
  463. /package/dist/web/assets/{material-theme-palenight-ztiOCPbw.js → material-theme-palenight-B0AUn0NG.js} +0 -0
  464. /package/dist/web/assets/{matlab-tHQDNC9G.js → matlab-C2waytxs.js} +0 -0
  465. /package/dist/web/assets/{mdx-D4kZ6c6G.js → mdx-DAis6R6I.js} +0 -0
  466. /package/dist/web/assets/{mermaid-DlR4U4Pe.js → mermaid-_rnT2YGD.js} +0 -0
  467. /package/dist/web/assets/{min-dark-BX-xHAD2.js → min-dark-BSSjchUB.js} +0 -0
  468. /package/dist/web/assets/{min-light-Bxw0_Gqq.js → min-light-DomAFQsa.js} +0 -0
  469. /package/dist/web/assets/{mipsasm-DYyZAum-.js → mipsasm-bBgtvPyG.js} +0 -0
  470. /package/dist/web/assets/{mojo-q9wuEYKR.js → mojo-Cp9BayW0.js} +0 -0
  471. /package/dist/web/assets/{monokai-D9vrJ8JM.js → monokai-CL_9gbmn.js} +0 -0
  472. /package/dist/web/assets/{moonbit-C9roWxMb.js → moonbit-BS_fcr-P.js} +0 -0
  473. /package/dist/web/assets/{move-BvhjG2ic.js → move-C79AH45U.js} +0 -0
  474. /package/dist/web/assets/{narrat-BXGbPwG3.js → narrat-CJ5s3BDf.js} +0 -0
  475. /package/dist/web/assets/{nextflow-groovy-CMy5-Z4k.js → nextflow-groovy-ZHa4JrPJ.js} +0 -0
  476. /package/dist/web/assets/{night-owl-CBQBcbmw.js → night-owl-Jkf9zFQK.js} +0 -0
  477. /package/dist/web/assets/{night-owl-light-CC-brflp.js → night-owl-light-DRPeaG5J.js} +0 -0
  478. /package/dist/web/assets/{nix-B9EAdptu.js → nix-D2Jet9DN.js} +0 -0
  479. /package/dist/web/assets/{nord-DxASd4qG.js → nord-CREVs22j.js} +0 -0
  480. /package/dist/web/assets/{nushell-BsY59i7N.js → nushell-CAiDi_mR.js} +0 -0
  481. /package/dist/web/assets/{objective-c-CoeQAcnP.js → objective-c-CVkmJkC1.js} +0 -0
  482. /package/dist/web/assets/{objective-cpp-BysdFvND.js → objective-cpp-DIKDO1Ly.js} +0 -0
  483. /package/dist/web/assets/{ocaml-CrtQYMMK.js → ocaml-R9qtGQhs.js} +0 -0
  484. /package/dist/web/assets/{odin-B8pz4gIL.js → odin-DTYNQqVr.js} +0 -0
  485. /package/dist/web/assets/{one-dark-pro-3sSHqoqQ.js → one-dark-pro-BSLWUJn5.js} +0 -0
  486. /package/dist/web/assets/{one-light-LUZAvT8g.js → one-light-DbkRXG3t.js} +0 -0
  487. /package/dist/web/assets/{openscad-DcKia-sV.js → openscad-BlPcSnmT.js} +0 -0
  488. /package/dist/web/assets/{pascal-zM5SlmgF.js → pascal-B1Y46ZZB.js} +0 -0
  489. /package/dist/web/assets/{pkl-C_r_zLLt.js → pkl-BhJSJbFv.js} +0 -0
  490. /package/dist/web/assets/{plastic-B3cRtgKD.js → plastic-BzxsCTpi.js} +0 -0
  491. /package/dist/web/assets/{plsql-BzyjheXU.js → plsql-DBba7dyP.js} +0 -0
  492. /package/dist/web/assets/{plus-DcuJuDC3.js → plus-Dm36mgLh.js} +0 -0
  493. /package/dist/web/assets/{po-Dz4OaDNi.js → po-BvgLlnNo.js} +0 -0
  494. /package/dist/web/assets/{poimandres-BBbtYwmR.js → poimandres-DUaI_1JP.js} +0 -0
  495. /package/dist/web/assets/{polar-Che1XC2Z.js → polar-C7uILwJn.js} +0 -0
  496. /package/dist/web/assets/{postcss-DouzwG4F.js → postcss-BgJlpFdw.js} +0 -0
  497. /package/dist/web/assets/{powerquery-YOdcTZmd.js → powerquery-Dn1MIpZu.js} +0 -0
  498. /package/dist/web/assets/{powershell-8ojzYH34.js → powershell-Bo-DYzOS.js} +0 -0
  499. /package/dist/web/assets/{prisma-DCmjceML.js → prisma-CTLs5IdT.js} +0 -0
  500. /package/dist/web/assets/{prolog-ByNNz2pB.js → prolog-QRGun3eG.js} +0 -0
  501. /package/dist/web/assets/{proto-DWMt6Qu1.js → proto-B7PI7apn.js} +0 -0
  502. /package/dist/web/assets/{puppet-osfKyZp4.js → puppet-CBq0tLrP.js} +0 -0
  503. /package/dist/web/assets/{purescript-ClIblkvh.js → purescript-DFewOQb2.js} +0 -0
  504. /package/dist/web/assets/{python-Ctyr4faP.js → python-iWuxKXvT.js} +0 -0
  505. /package/dist/web/assets/{qmldir-CbNgyhnI.js → qmldir-cXiW3AsF.js} +0 -0
  506. /package/dist/web/assets/{qss-LbRHzb21.js → qss-DASwAbBL.js} +0 -0
  507. /package/dist/web/assets/{r-Dvfjp0wc.js → r-TU2eaIPw.js} +0 -0
  508. /package/dist/web/assets/{racket-DI8v8nzD.js → racket-SkRKqrLY.js} +0 -0
  509. /package/dist/web/assets/{raku-CX1q9GW0.js → raku-D31lthkZ.js} +0 -0
  510. /package/dist/web/assets/{react-8hrD3-kv.js → react-DjdYfcEe.js} +0 -0
  511. /package/dist/web/assets/{red-CSz3QEOw.js → red-CTcQZevb.js} +0 -0
  512. /package/dist/web/assets/{reg-FMjhUQE-.js → reg-D8YAsTGf.js} +0 -0
  513. /package/dist/web/assets/{regexp-CCSe1Fc4.js → regexp-CD2uvAWx.js} +0 -0
  514. /package/dist/web/assets/{rel-Ce0UO-OB.js → rel-CAaI-1fg.js} +0 -0
  515. /package/dist/web/assets/{resolve-theme-B9Mgd5GG.js → resolve-theme-B0eotaPq.js} +0 -0
  516. /package/dist/web/assets/{riscv-BFZrdZtB.js → riscv-DR-hJVIj.js} +0 -0
  517. /package/dist/web/assets/{ron-D0t4kvJG.js → ron-CuUCJ0n4.js} +0 -0
  518. /package/dist/web/assets/{rose-pine-HzwmOzHM.js → rose-pine-aFHxW48i.js} +0 -0
  519. /package/dist/web/assets/{rose-pine-dawn-CSp9xAF9.js → rose-pine-dawn-Bqz2sBRY.js} +0 -0
  520. /package/dist/web/assets/{rose-pine-moon-Cnzr2Cw0.js → rose-pine-moon-wqyh0an4.js} +0 -0
  521. /package/dist/web/assets/{rosmsg-DEJ8Snbv.js → rosmsg-CI1ywimO.js} +0 -0
  522. /package/dist/web/assets/{rust-2jBzrC0l.js → rust-p7Imk4G_.js} +0 -0
  523. /package/dist/web/assets/{sass-DD75Izu_.js → sass-C8JfaqCV.js} +0 -0
  524. /package/dist/web/assets/{save-BM4p9_9n.js → save-ignW_jk2.js} +0 -0
  525. /package/dist/web/assets/{scala-DpBKROIu.js → scala-DNxCW1By.js} +0 -0
  526. /package/dist/web/assets/{scheme-DM2jzwUK.js → scheme-BYuUPJoL.js} +0 -0
  527. /package/dist/web/assets/{sdbl-C34Cg6e7.js → sdbl-DbPeJPUr.js} +0 -0
  528. /package/dist/web/assets/{shellscript-DxfzKSgU.js → shellscript-DMiHlR3q.js} +0 -0
  529. /package/dist/web/assets/{shield-check-C3qYSU8a.js → shield-check-BSA9P3nI.js} +0 -0
  530. /package/dist/web/assets/{shield-off-D5tU7gQT.js → shield-off-Cb9J9x_n.js} +0 -0
  531. /package/dist/web/assets/{slack-dark-DIIV3bpD.js → slack-dark-C4xGcQXI.js} +0 -0
  532. /package/dist/web/assets/{slack-ochin-BTkxWjEP.js → slack-ochin-BzgRt_wm.js} +0 -0
  533. /package/dist/web/assets/{smalltalk-Bm0qg6SA.js → smalltalk-LDeL9PYH.js} +0 -0
  534. /package/dist/web/assets/{snazzy-light-ClAuuhn4.js → snazzy-light-CXE7a5j_.js} +0 -0
  535. /package/dist/web/assets/{solarized-dark-93sEWmYT.js → solarized-dark-DgUhY74x.js} +0 -0
  536. /package/dist/web/assets/{solarized-light-C74FvgcO.js → solarized-light-DD9C-H-9.js} +0 -0
  537. /package/dist/web/assets/{solidity-H9fSjkQP.js → solidity-CAbDB4D4.js} +0 -0
  538. /package/dist/web/assets/{splunk-CDTN2iAG.js → splunk-kQREPfIc.js} +0 -0
  539. /package/dist/web/assets/{sql-CoqObhgo.js → sql-CBzeNAD8.js} +0 -0
  540. /package/dist/web/assets/{ssh-config-C3Wv6qRp.js → ssh-config-L1euxrsM.js} +0 -0
  541. /package/dist/web/assets/{stylus-D0uTFXCy.js → stylus-DvBnYniP.js} +0 -0
  542. /package/dist/web/assets/{swift-DtMMgvRI.js → swift-DF5NDQp6.js} +0 -0
  543. /package/dist/web/assets/{synthwave-84-DAroyZBu.js → synthwave-84-DbBhB_Hq.js} +0 -0
  544. /package/dist/web/assets/{system-verilog-Bj7X_lq4.js → system-verilog-D8s2ZW_k.js} +0 -0
  545. /package/dist/web/assets/{systemd-CH8ApCUs.js → systemd-a5-XZJsG.js} +0 -0
  546. /package/dist/web/assets/{table-Di8pv2Y_.js → table-4P8kS7sg.js} +0 -0
  547. /package/dist/web/assets/{talonscript-70lVC3cw.js → talonscript-BLa0URTW.js} +0 -0
  548. /package/dist/web/assets/{tasl-WQR8VslV.js → tasl-DIoV4XcC.js} +0 -0
  549. /package/dist/web/assets/{tcl-caTR_frn.js → tcl-ztYAdXfQ.js} +0 -0
  550. /package/dist/web/assets/{terraform-CVRZ4SZ4.js → terraform-DvpCCiOZ.js} +0 -0
  551. /package/dist/web/assets/{tokyo-night-CDyT1jCI.js → tokyo-night-BkHlYUv0.js} +0 -0
  552. /package/dist/web/assets/{toml-B7D2mIDU.js → toml-acIrxos6.js} +0 -0
  553. /package/dist/web/assets/{tsv-2kdn5dqL.js → tsv-BJB5EVCT.js} +0 -0
  554. /package/dist/web/assets/{tsx-DJfG3fzg.js → tsx-B1MmXGT3.js} +0 -0
  555. /package/dist/web/assets/{turtle-DaQac_UT.js → turtle-DFceTiqd.js} +0 -0
  556. /package/dist/web/assets/{typescript-BLfAIchl.js → typescript-Dz9wU5Y1.js} +0 -0
  557. /package/dist/web/assets/{typespec-ymBaOi_c.js → typespec-CPuYZijj.js} +0 -0
  558. /package/dist/web/assets/{typst-DkvUYMT-.js → typst-CfsCIYv8.js} +0 -0
  559. /package/dist/web/assets/{use-is-mobile-BcuCjH19.js → use-is-mobile-K1a38BwI.js} +0 -0
  560. /package/dist/web/assets/{utils-E0yyGxXt.js → utils-B9H0d3Fh.js} +0 -0
  561. /package/dist/web/assets/{v-D4997FGs.js → v-CGo0jcXA.js} +0 -0
  562. /package/dist/web/assets/{vala-AFg9DmKE.js → vala-AsSR0hrb.js} +0 -0
  563. /package/dist/web/assets/{vb-DNUJ9fOP.js → vb-C7tvbXv1.js} +0 -0
  564. /package/dist/web/assets/{vendor-xterm-QDr0jvpJ.js → vendor-xterm-ClRVIJ-k.js} +0 -0
  565. /package/dist/web/assets/{verilog-BrnAQ3tF.js → verilog--a33ND-p.js} +0 -0
  566. /package/dist/web/assets/{vesper-DCZnFmY0.js → vesper-B_Z0h4pn.js} +0 -0
  567. /package/dist/web/assets/{vhdl-DoK4PjhE.js → vhdl-CWL5dZPN.js} +0 -0
  568. /package/dist/web/assets/{viml-D6EKG66q.js → viml-BglmT14Y.js} +0 -0
  569. /package/dist/web/assets/{vitesse-black-C6KILWdF.js → vitesse-black-RKFsSqQI.js} +0 -0
  570. /package/dist/web/assets/{vitesse-dark-BuSr6OT5.js → vitesse-dark-D7zDWNmc.js} +0 -0
  571. /package/dist/web/assets/{vitesse-light-M-eYyOoe.js → vitesse-light-MAp87W5b.js} +0 -0
  572. /package/dist/web/assets/{vyper-CwSN64Jn.js → vyper-CgCBYg1G.js} +0 -0
  573. /package/dist/web/assets/{wasm-BhQEkzil.js → wasm-BOG7ZeTT.js} +0 -0
  574. /package/dist/web/assets/{wasm-CVlf9Jwb.js → wasm-Bd258nS1.js} +0 -0
  575. /package/dist/web/assets/{wenyan-BC7irgM3.js → wenyan-DpShPKXZ.js} +0 -0
  576. /package/dist/web/assets/{wgsl-W-3UHMDO.js → wgsl-BakcCblK.js} +0 -0
  577. /package/dist/web/assets/{wikitext-T1DoFcm8.js → wikitext-NroRWVAO.js} +0 -0
  578. /package/dist/web/assets/{wit-DIrx_eCI.js → wit-B5CM2pg6.js} +0 -0
  579. /package/dist/web/assets/{wolfram-Cza4b443.js → wolfram-7MTFBJpR.js} +0 -0
  580. /package/dist/web/assets/{x-BsFLg-T8.js → x-BtzqXv1z.js} +0 -0
  581. /package/dist/web/assets/{yaml-DOZ10aZe.js → yaml-Bwr01G2K.js} +0 -0
  582. /package/dist/web/assets/{zenscript-DDAXkseY.js → zenscript-Dj8GATvi.js} +0 -0
  583. /package/dist/web/assets/{zig-CrzwMpQE.js → zig-LAE-fbS-.js} +0 -0
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Maps a window `kind` to the component that fills its body.
3
+ *
4
+ * The registry is the only coupling point between the generic window layer and real
5
+ * features: adding a kind means adding an entry here plus a title resolver, and nothing in
6
+ * the frame, store or gestures changes. Entries are lazy so a window's content (and its
7
+ * icon/virtualisation deps) never lands in the initial bundle.
8
+ */
9
+
10
+ import { lazy, type ComponentType, type LazyExoticComponent } from "react";
11
+ import { ExplorerSkinChrome } from "@/components/os-explorer/skins/explorer-skin-chrome";
12
+ import type { WindowChrome } from "./window-chrome-contract";
13
+ import type { WindowKind } from "./window-store-types";
14
+
15
+ export interface WindowContentProps {
16
+ /** Window id — content may call the store to close/retitle itself. */
17
+ id: string;
18
+ /** Kind-specific state persisted with the window (e.g. the explorer's current path). */
19
+ payload?: Record<string, unknown>;
20
+ }
21
+
22
+ export const WINDOW_CONTENT: Record<WindowKind, LazyExoticComponent<ComponentType<WindowContentProps>>> = {
23
+ explorer: lazy(() => import("@/components/os-explorer/explorer-window-content")),
24
+ };
25
+
26
+ /**
27
+ * Per-kind titlebar chrome. `FloatingWindow` renders `chrome` directly with no
28
+ * `<Suspense>` around it, so — unlike `WINDOW_CONTENT` — entries here must be plain,
29
+ * synchronously-renderable components, not `lazy()`.
30
+ */
31
+ export const WINDOW_CHROME: Partial<Record<WindowKind, WindowChrome>> = {
32
+ explorer: ExplorerSkinChrome,
33
+ };
34
+
35
+ /** Chrome for a window kind, or `undefined` to fall back to `DefaultWindowChrome`. */
36
+ export function chromeFor(kind: WindowKind): WindowChrome | undefined {
37
+ return WINDOW_CHROME[kind];
38
+ }
39
+
40
+ /** Titlebar text for a window. Falls back to the kind's generic name. */
41
+ export function windowTitle(kind: WindowKind, payload?: Record<string, unknown>): string {
42
+ const explicit = payload?.title;
43
+ if (typeof explicit === "string" && explicit.trim()) return explicit;
44
+ const path = payload?.path;
45
+ if (kind === "explorer" && typeof path === "string" && path) {
46
+ const segments = path.split(/[\\/]/).filter(Boolean);
47
+ // A drive or POSIX root has no last segment worth showing; the raw path is the name.
48
+ return segments[segments.length - 1] ?? path;
49
+ }
50
+ return "Explorer";
51
+ }
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Pure geometry for the floating window layer — no DOM, no React, so every rule
3
+ * (min size, on-screen clamping, per-handle resize, spawn cascade) is unit-testable.
4
+ *
5
+ * All coordinates are relative to the layer container (the app content area), not the
6
+ * browser viewport: the nav rail must stay usable while a window is maximised.
7
+ */
8
+
9
+ export interface Rect {
10
+ x: number;
11
+ y: number;
12
+ w: number;
13
+ h: number;
14
+ }
15
+
16
+ /** Size of the layer container the rects live in. */
17
+ export interface Bounds {
18
+ w: number;
19
+ h: number;
20
+ }
21
+
22
+ export type ResizeHandle = "n" | "s" | "e" | "w" | "ne" | "nw" | "se" | "sw";
23
+
24
+ export const RESIZE_HANDLES: ResizeHandle[] = ["n", "s", "e", "w", "ne", "nw", "se", "sw"];
25
+
26
+ export const MIN_SIZE = { w: 360, h: 240 } as const;
27
+
28
+ /**
29
+ * How much of the titlebar must remain reachable after a clamp. A window dragged past an
30
+ * edge keeps this many pixels inside the layer so it can always be grabbed back.
31
+ */
32
+ export const KEEP_VISIBLE = 48;
33
+
34
+ /** Offset between consecutive spawns, matching desktop window managers. */
35
+ export const CASCADE_STEP = 24;
36
+
37
+ /** Hard cap on simultaneous windows so the z-band stays inside 30..38. */
38
+ export const MAX_WINDOWS = 8;
39
+
40
+ /** Base z-index of the layer; strictly below the app's z-40 backdrops and z-50 popovers. */
41
+ export const Z_BASE = 30;
42
+
43
+ const PREFERRED = { w: 880, h: 560 } as const;
44
+ const SPAWN_MARGIN = 48;
45
+
46
+ const clamp = (v: number, lo: number, hi: number) => Math.min(hi, Math.max(lo, v));
47
+
48
+ /** Inline z-index for a window at `rank` (0 = backmost). Never exceeds Z_BASE + 8. */
49
+ export function windowZIndex(rank: number): number {
50
+ return Z_BASE + clamp(Math.trunc(rank), 0, MAX_WINDOWS);
51
+ }
52
+
53
+ /**
54
+ * Fit a rect inside the layer: at least MIN_SIZE, never wider/taller than the layer, and
55
+ * positioned so KEEP_VISIBLE px of the titlebar row stay inside. Values are rounded to
56
+ * whole pixels — sub-pixel transforms blur text on high-DPI screens.
57
+ */
58
+ export function clampRect(rect: Rect, bounds: Bounds): Rect {
59
+ const w = Math.max(MIN_SIZE.w, Math.min(rect.w, Math.max(bounds.w, MIN_SIZE.w)));
60
+ const h = Math.max(MIN_SIZE.h, Math.min(rect.h, Math.max(bounds.h, MIN_SIZE.h)));
61
+ // Left edge may go negative (window hangs off the left) as long as its right edge
62
+ // still shows KEEP_VISIBLE px; the top edge may never go above the layer, or the
63
+ // titlebar becomes unreachable.
64
+ const x = clamp(rect.x, KEEP_VISIBLE - w, Math.max(KEEP_VISIBLE - w, bounds.w - KEEP_VISIBLE));
65
+ const y = clamp(rect.y, 0, Math.max(0, bounds.h - KEEP_VISIBLE));
66
+ return { x: Math.round(x), y: Math.round(y), w: Math.round(w), h: Math.round(h) };
67
+ }
68
+
69
+ /**
70
+ * Apply a drag delta to one of the 8 handles.
71
+ *
72
+ * Edges anchored by the handle stay put; the opposite edge moves. Shrinking past the
73
+ * minimum stops the moving edge instead of pushing the anchored one, which is what makes
74
+ * a resize feel like a native window instead of sliding away under the cursor.
75
+ */
76
+ export function applyResize(
77
+ rect: Rect,
78
+ handle: ResizeHandle,
79
+ dx: number,
80
+ dy: number,
81
+ min: { w: number; h: number } = MIN_SIZE,
82
+ ): Rect {
83
+ let { x, y, w, h } = rect;
84
+
85
+ if (handle.includes("e")) {
86
+ w = Math.max(min.w, rect.w + dx);
87
+ } else if (handle.includes("w")) {
88
+ const right = rect.x + rect.w;
89
+ w = Math.max(min.w, rect.w - dx);
90
+ x = right - w;
91
+ }
92
+
93
+ if (handle.includes("s")) {
94
+ h = Math.max(min.h, rect.h + dy);
95
+ } else if (handle.includes("n")) {
96
+ const bottom = rect.y + rect.h;
97
+ h = Math.max(min.h, rect.h - dy);
98
+ y = bottom - h;
99
+ }
100
+
101
+ return { x, y, w, h };
102
+ }
103
+
104
+ /**
105
+ * Where a newly opened window goes: cascaded from the top-left by CASCADE_STEP per existing
106
+ * window, wrapping before it would walk off the layer.
107
+ */
108
+ export function cascadeSpawnRect(existing: Rect[], bounds: Bounds): Rect {
109
+ const w = Math.min(PREFERRED.w, Math.max(MIN_SIZE.w, bounds.w - SPAWN_MARGIN * 2));
110
+ const h = Math.min(PREFERRED.h, Math.max(MIN_SIZE.h, bounds.h - SPAWN_MARGIN * 2));
111
+ const step = existing.length % MAX_WINDOWS;
112
+ return clampRect(
113
+ { x: SPAWN_MARGIN + step * CASCADE_STEP, y: SPAWN_MARGIN + step * CASCADE_STEP, w, h },
114
+ bounds,
115
+ );
116
+ }
117
+
118
+ /** Nudge step for arrow keys on a focused titlebar (Shift = fine-grained). */
119
+ export function nudgeRect(rect: Rect, dx: number, dy: number, bounds: Bounds): Rect {
120
+ return clampRect({ ...rect, x: rect.x + dx, y: rect.y + dy }, bounds);
121
+ }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Host for all floating windows, sized to the app's content area.
3
+ *
4
+ * It is deliberately a plain absolutely-positioned child of the content wrapper rather than
5
+ * a body-level portal: window coordinates stay relative to the working area, so a maximized
6
+ * window covers the panels but never the nav rail, and the container's own resize is what
7
+ * triggers re-clamping.
8
+ *
9
+ * The layer is desktop-only. Below the `md` breakpoint it renders nothing and never touches
10
+ * the store, leaving the mobile sheet presentation free to own the same content.
11
+ */
12
+
13
+ import { Suspense, useCallback, useLayoutEffect, useRef, useState } from "react";
14
+ import { useIsMobile } from "@/hooks/use-is-mobile";
15
+ import { FloatingWindow } from "./floating-window";
16
+ import { chromeFor, WINDOW_CONTENT, windowTitle } from "./window-content-registry";
17
+ import { Z_BASE, MAX_WINDOWS } from "./window-geometry";
18
+ import { useWindowStore, windowsInRankOrder } from "./window-store";
19
+
20
+ export function WindowLayer() {
21
+ const isMobile = useIsMobile();
22
+ if (isMobile) return null;
23
+ return <DesktopWindowLayer />;
24
+ }
25
+
26
+ function DesktopWindowLayer() {
27
+ const containerRef = useRef<HTMLDivElement>(null);
28
+ const windows = useWindowStore((s) => s.windows);
29
+ const bounds = useWindowStore((s) => s.bounds);
30
+ const [capturing, setCapturing] = useState(false);
31
+
32
+ useLayoutEffect(() => {
33
+ const el = containerRef.current;
34
+ if (!el) return;
35
+ const measure = () => {
36
+ const r = el.getBoundingClientRect();
37
+ const scale = el.offsetWidth > 0 ? r.width / el.offsetWidth : 1;
38
+ // Report layout pixels, not scaled screen pixels, so rects match the CSS coordinates
39
+ // the windows are positioned in.
40
+ return { w: Math.round(r.width / scale), h: Math.round(r.height / scale) };
41
+ };
42
+ const store = useWindowStore.getState();
43
+ store.setBounds(measure());
44
+ store.restoreAll(measure());
45
+ // Panels, the sidebar and the browser all resize this box; window.resize alone misses
46
+ // the first two and would leave windows stranded outside the layer.
47
+ const observer = new ResizeObserver(() => useWindowStore.getState().setBounds(measure()));
48
+ observer.observe(el);
49
+ return () => observer.disconnect();
50
+ }, []);
51
+
52
+ const getScale = useCallback(() => {
53
+ const el = containerRef.current;
54
+ if (!el || el.offsetWidth === 0) return 1;
55
+ return el.getBoundingClientRect().width / el.offsetWidth;
56
+ }, []);
57
+
58
+ const ordered = windowsInRankOrder(windows);
59
+ const frontId = ordered[ordered.length - 1]?.id;
60
+
61
+ return (
62
+ <div ref={containerRef} className="absolute inset-0 overflow-hidden pointer-events-none">
63
+ {ordered.map((win) => {
64
+ const Content = WINDOW_CONTENT[win.kind];
65
+ const title = windowTitle(win.kind, win.payload);
66
+ return (
67
+ <FloatingWindow
68
+ key={win.id}
69
+ win={win}
70
+ bounds={bounds}
71
+ title={title}
72
+ focused={win.id === frontId}
73
+ getScale={getScale}
74
+ onGestureActive={setCapturing}
75
+ chrome={chromeFor(win.kind)}
76
+ >
77
+ <Suspense fallback={<div className="p-3 text-xs text-text-2">Loading…</div>}>
78
+ <Content id={win.id} payload={win.payload} />
79
+ </Suspense>
80
+ </FloatingWindow>
81
+ );
82
+ })}
83
+
84
+ {/* Transparent capture surface: iframes and editors swallow pointer events, which
85
+ would stall a drag the moment the cursor crossed one. Stays inside the window
86
+ z-band so app popovers and dialogs remain above it. */}
87
+ {capturing && (
88
+ <div className="absolute inset-0 pointer-events-auto" style={{ zIndex: Z_BASE + MAX_WINDOWS }} />
89
+ )}
90
+ </div>
91
+ );
92
+ }
@@ -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
+ }
@@ -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)