@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.
Files changed (600) hide show
  1. package/.release-run.sh +5 -0
  2. package/CHANGELOG.md +24 -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-CXWQcx3_.js → ai-resource-editor-Bmk4P0YS.js} +1 -1
  8. package/dist/web/assets/{angular-html-D4YJUMLP.js → angular-html-C-Kjr43S.js} +1 -1
  9. package/dist/web/assets/{angular-ts-CUvq2h3X.js → angular-ts-CaDDbZqm.js} +1 -1
  10. package/dist/web/assets/api-client-CaRE4HYh.js +1 -0
  11. package/dist/web/assets/{api-client-COQtP-dB.js → api-client-rTAGdEKo.js} +1 -1
  12. package/dist/web/assets/{api-settings-Do0Z0IRN.js → api-settings-CAhcNpj5.js} +1 -1
  13. package/dist/web/assets/api-settings-DBcXfeJ8.js +1 -0
  14. package/dist/web/assets/{api-themes-DXi8sz-9.js → api-themes-CAjg_Bt9.js} +1 -1
  15. package/dist/web/assets/{apl-DSANFLx0.js → apl-B1WsQFAb.js} +1 -1
  16. package/dist/web/assets/architecture-PBZL5I3N-B4J2-O8Q.js +1 -0
  17. package/dist/web/assets/{astro-CXIotiNS.js → astro-Bdnz7nVp.js} +1 -1
  18. package/dist/web/assets/{audio-preview-D7jNc8mi.js → audio-preview-CikvnAOI.js} +1 -1
  19. package/dist/web/assets/{blade-BvrP2gPd.js → blade-B2euh8sS.js} +1 -1
  20. package/dist/web/assets/{bot-E9mcVZnr.js → bot-nk1k3ELu.js} +1 -1
  21. package/dist/web/assets/{bsl-CGf6Xuyu.js → bsl-GNeMaxd0.js} +1 -1
  22. package/dist/web/assets/{cairo-BboZ9qIf.js → cairo-D7G0W7-s.js} +1 -1
  23. package/dist/web/assets/can-open-in-ppm-BzVXoV-d.js +1 -0
  24. package/dist/web/assets/chat-tab-B3-KcCn2.js +13 -0
  25. package/dist/web/assets/chevron-right-BLOtX8Vy.js +1 -0
  26. package/dist/web/assets/{circle-question-mark-PaJgx-3d.js → circle-question-mark-CENpx4oA.js} +1 -1
  27. package/dist/web/assets/clock-D493FU_D.js +1 -0
  28. package/dist/web/assets/{cobol-D39V6ukp.js → cobol-Q6YgysnI.js} +1 -1
  29. package/dist/web/assets/code-CLF5x7KK.js +1 -0
  30. package/dist/web/assets/code-editor-BpbZw13T.js +10 -0
  31. package/dist/web/assets/{coffee-DTtK7kKi.js → coffee-BjufWxVc.js} +1 -1
  32. package/dist/web/assets/{conflict-editor-CpGcSopK.js → conflict-editor-DC3liBm_.js} +3 -3
  33. package/dist/web/assets/context-menu-q-RHHAWz.js +1 -0
  34. package/dist/web/assets/copy-BApI9v7Z.js +1 -0
  35. package/dist/web/assets/{cpp-CnU9hfBq.js → cpp-CqWU0gCR.js} +1 -1
  36. package/dist/web/assets/{createLucideIcon-ConhwJnb.js → createLucideIcon-D8pyde1A.js} +1 -1
  37. package/dist/web/assets/{crystal-DMjCL--0.js → crystal-ylayVajF.js} +1 -1
  38. package/dist/web/assets/csv-preview-BhCOjgq1.js +2 -0
  39. package/dist/web/assets/{data-grid-overlay-editor-Bdc80TJ1.js → data-grid-overlay-editor-BzPfsCfd.js} +1 -1
  40. package/dist/web/assets/{data-grid-types-BuMHLKXV.js → data-grid-types-CUx8cusN.js} +1 -1
  41. package/dist/web/assets/{database-COYAMZaw.js → database-B9okYLx6.js} +1 -1
  42. package/dist/web/assets/{database-viewer-DTMmic31.js → database-viewer-Z6uLyZVO.js} +1 -1
  43. package/dist/web/assets/dialog-CkI7rwrh.js +1 -0
  44. package/dist/web/assets/diff-viewer-CT1ALWt2.js +4 -0
  45. package/dist/web/assets/{dist-m_oJqIDk.js → dist-DduBIVkD.js} +1 -1
  46. package/dist/web/assets/{docx-preview-CR2ZtvNP.js → docx-preview-C_fSjfy_.js} +1 -1
  47. package/dist/web/assets/{edge-CTom35R_.js → edge-DX4NxQfh.js} +1 -1
  48. package/dist/web/assets/{edit-diff-preview-D1TEK7Jz.js → edit-diff-preview-DfX-9om9.js} +1 -1
  49. package/dist/web/assets/{elixir-BAX9HSOu.js → elixir-CSh3NjbV.js} +1 -1
  50. package/dist/web/assets/{elm-CpD28xsW.js → elm-DBzwFYq9.js} +1 -1
  51. package/dist/web/assets/{erb-DHX2IIEs.js → erb-yxVOb8aB.js} +1 -1
  52. package/dist/web/assets/{erlang-B_etLcPG.js → erlang-5XIDkYrM.js} +1 -1
  53. package/dist/web/assets/{esm-PmlN1RTs.js → esm-DO4el2OJ.js} +1 -1
  54. package/dist/web/assets/explorer-body-CQCyGEet.js +2 -0
  55. package/dist/web/assets/explorer-window-content-EtyTAZSh.js +1 -0
  56. package/dist/web/assets/{extension-webview-BTCFWY2A.js → extension-webview-ePIM2fwM.js} +1 -1
  57. package/dist/web/assets/{eye-off-Rq1n2-oe.js → eye-off-CUdzgmX8.js} +1 -1
  58. package/dist/web/assets/{file-exclamation-point-DBEC-Kst.js → file-exclamation-point-COAwgNGw.js} +1 -1
  59. package/dist/web/assets/{fortran-fixed-form-CpR0cN7O.js → fortran-fixed-form-D4tE5VWY.js} +1 -1
  60. package/dist/web/assets/{fsharp-DxITxZW5.js → fsharp-By8soj6-.js} +1 -1
  61. package/dist/web/assets/{gdresource-DJ7IvROZ.js → gdresource-DoSwM7Hv.js} +1 -1
  62. package/dist/web/assets/{git-commit-sQznNP43.js → git-commit-_ZiciHIx.js} +1 -1
  63. package/dist/web/assets/git-log-panel-BRWmTzX1.js +1 -0
  64. package/dist/web/assets/{git-rebase-DkHvejAR.js → git-rebase-C3Y5N9-s.js} +1 -1
  65. package/dist/web/assets/gitGraph-HDMCJU4V-CtQ3yXN-.js +1 -0
  66. package/dist/web/assets/glide-data-grid-DCJCz4XB.js +138 -0
  67. package/dist/web/assets/{glimmer-js-BwJnDfL-.js → glimmer-js-BPBTXVlC.js} +1 -1
  68. package/dist/web/assets/{glimmer-ts-Coz155XK.js → glimmer-ts-BQSZdy_D.js} +1 -1
  69. package/dist/web/assets/{glsl-BED7ZmUZ.js → glsl-CbQdxpU-.js} +1 -1
  70. package/dist/web/assets/{graphql-B_KyE6oz.js → graphql-GMlKikpY.js} +1 -1
  71. package/dist/web/assets/group-chat-tab-C3UD2bcm.js +1 -0
  72. package/dist/web/assets/{hack-sxywdIAQ.js → hack-DyshcLec.js} +1 -1
  73. package/dist/web/assets/{haml-Dts9JlPU.js → haml-rqCqWzsq.js} +1 -1
  74. package/dist/web/assets/{handlebars-C9HXIMtj.js → handlebars-BCKwSWjR.js} +1 -1
  75. package/dist/web/assets/{html-2p0RvXi4.js → html-Cg29xPR1.js} +1 -1
  76. package/dist/web/assets/{html-derivative-BiTPuOba.js → html-derivative-geltIVg8.js} +1 -1
  77. package/dist/web/assets/{http-CHd2gYPf.js → http-pG7Vp237.js} +1 -1
  78. package/dist/web/assets/{hurl-boMPgDm9.js → hurl-Di3Uu_Mc.js} +1 -1
  79. package/dist/web/assets/{hxml-BQvjiuBt.js → hxml-CRy4lbv8.js} +1 -1
  80. package/dist/web/assets/{image-preview-0SGb7_tH.js → image-preview-CP82lor8.js} +1 -1
  81. package/dist/web/assets/index-BYetJoXQ.js +52 -0
  82. package/dist/web/assets/index-D-xwjNyX.css +2 -0
  83. package/dist/web/assets/info-3K5VOQVL-Cg0mIJn9.js +1 -0
  84. package/dist/web/assets/{jinja-B-zKiwlp.js → jinja-C8GyVnvd.js} +1 -1
  85. package/dist/web/assets/{jison-C2vJYjbJ.js → jison-BywnqPn6.js} +1 -1
  86. package/dist/web/assets/{julia-DJ8MrQPA.js → julia-DTrO3T3B.js} +1 -1
  87. package/dist/web/assets/{just-BvQHq7VE.js → just-CA3KSHPi.js} +1 -1
  88. package/dist/web/assets/keybindings-store-yBWU8ZYT.js +1 -0
  89. package/dist/web/assets/{latex-ChNeshUC.js → latex-BC5xUMpc.js} +1 -1
  90. package/dist/web/assets/{lib-DTaN9riD.js → lib-BFetOULu.js} +1 -1
  91. package/dist/web/assets/{liquid-KNJEBOwW.js → liquid-Chd8tSyQ.js} +1 -1
  92. package/dist/web/assets/{lua-D8NNsLbC.js → lua-DHaEtwLv.js} +1 -1
  93. package/dist/web/assets/markdown-renderer-B2D4gdok.js +1 -0
  94. package/dist/web/assets/markdown-renderer-DDgl5zDc.js +3 -0
  95. package/dist/web/assets/{marko-HGo5Qtz4.js → marko-Desx_bC-.js} +1 -1
  96. package/dist/web/assets/{mdc-omkodQB5.js → mdc-DahWHc1t.js} +1 -1
  97. package/dist/web/assets/mobile-bottom-sheet-BQt45q9T.js +1 -0
  98. package/dist/web/assets/mobile-explorer-sheet-BtxyynR3.js +1 -0
  99. package/dist/web/assets/{nextflow-Adq02pv-.js → nextflow-3A7lutCA.js} +1 -1
  100. package/dist/web/assets/{nginx-ByvWruGD.js → nginx-XQda6Hdy.js} +1 -1
  101. package/dist/web/assets/{nim-BrMojX4O.js → nim-DHqlpwP1.js} +1 -1
  102. package/dist/web/assets/notification-store-DKw_mGED.js +1 -0
  103. package/dist/web/assets/{number-overlay-editor-CunvWJnN.js → number-overlay-editor-DgZtViPO.js} +1 -1
  104. package/dist/web/assets/packet-RMMSAZCW-D79vAFR_.js +1 -0
  105. package/dist/web/assets/{panel-store-CpKvXNnm.js → panel-store-s6Qx49RK.js} +1 -1
  106. package/dist/web/assets/{pdf-preview-DEn8amrO.js → pdf-preview-BXDOe7bG.js} +1 -1
  107. package/dist/web/assets/{perl-Bd_0l-b4.js → perl-M-ncjACb.js} +1 -1
  108. package/dist/web/assets/{php-D-DLTnWJ.js → php-CKIajrZi.js} +1 -1
  109. package/dist/web/assets/pie-UPGHQEXC-DYUOH68U.js +1 -0
  110. package/dist/web/assets/{plus-DcuJuDC3.js → plus-9LatwJMx.js} +1 -1
  111. package/dist/web/assets/{postgres-viewer-CjrM15gz.js → postgres-viewer-DkHYV33h.js} +3 -3
  112. package/dist/web/assets/{project-store-D0ALup9Z.js → project-store-DyaEpHVG.js} +1 -1
  113. package/dist/web/assets/{pug-D2aO-l2T.js → pug-DO0u_gO4.js} +1 -1
  114. package/dist/web/assets/{qml-CyQVzNXS.js → qml-UEje1PAA.js} +1 -1
  115. package/dist/web/assets/radar-KQ55EAFF-B1eznNtd.js +1 -0
  116. package/dist/web/assets/{razor-8tvJXsO3.js → razor-lCs5T1Ib.js} +1 -1
  117. package/dist/web/assets/{react-8hrD3-kv.js → react-w6wINGnd.js} +1 -1
  118. package/dist/web/assets/{rst-C4AZyQQl.js → rst-CZX_O0z4.js} +1 -1
  119. package/dist/web/assets/{ruby-O_z2NTLY.js → ruby-idycVAyE.js} +1 -1
  120. package/dist/web/assets/{sas-BtnF4zaV.js → sas-o5gTfUwp.js} +1 -1
  121. package/dist/web/assets/{save-BM4p9_9n.js → save-C4z1p51e.js} +1 -1
  122. package/dist/web/assets/{scss-GAyp5euH.js → scss-CRTk3iBQ.js} +1 -1
  123. package/dist/web/assets/settings-store-ChqHdT2V.js +2 -0
  124. package/dist/web/assets/settings-tab-ESL-6MAA.js +1 -0
  125. package/dist/web/assets/{shaderlab-nM5YZwa5.js → shaderlab-3e5ln8ci.js} +1 -1
  126. package/dist/web/assets/{shellsession-D2OCI4Gv.js → shellsession-CGQjQ75I.js} +1 -1
  127. package/dist/web/assets/{shield-check-C3qYSU8a.js → shield-check-C5laObJv.js} +1 -1
  128. package/dist/web/assets/{shield-off-D5tU7gQT.js → shield-off-DULFtyLS.js} +1 -1
  129. package/dist/web/assets/{soy-DrLNhqfp.js → soy-CxY0VwX2.js} +1 -1
  130. package/dist/web/assets/sparkles-n1hzh65o.js +1 -0
  131. package/dist/web/assets/{sparql-CsM6F2Yv.js → sparql-CBAGAFuq.js} +1 -1
  132. package/dist/web/assets/{sql-query-editor-CT5M9PKQ.js → sql-query-editor-CZdZuawP.js} +1 -1
  133. package/dist/web/assets/sqlite-viewer-Cpq06-nz.js +1 -0
  134. package/dist/web/assets/{stata-Bk3cgfXJ.js → stata-BoezhOvk.js} +1 -1
  135. package/dist/web/assets/{surrealql-Dkz0DPK8.js → surrealql-CHEzAhu7.js} +1 -1
  136. package/dist/web/assets/{svelte-23si1KV5.js → svelte-y6xtu2zX.js} +1 -1
  137. package/dist/web/assets/{system-monitor-tab-De3oz9zD.js → system-monitor-tab-XR8NXHpv.js} +1 -1
  138. package/dist/web/assets/{tab-store-BQmDVh9c.js → tab-store-C42sktPo.js} +1 -1
  139. package/dist/web/assets/{table-Di8pv2Y_.js → table-CHmxuh6M.js} +1 -1
  140. package/dist/web/assets/{templ-CPxlP4vF.js → templ-DbG2y5yD.js} +1 -1
  141. package/dist/web/assets/{terminal-tab-CciNqtMc.js → terminal-tab-CCCB-_50.js} +3 -3
  142. package/dist/web/assets/{tex-ugJpgYPc.js → tex-CeKQhrQG.js} +1 -1
  143. package/dist/web/assets/tool-cards-CsTs5go2.js +7 -0
  144. package/dist/web/assets/treemap-KZPCXAKY-SeI2TxLs.js +1 -0
  145. package/dist/web/assets/{ts-tags-BovP9TMu.js → ts-tags-iKKOXJjG.js} +1 -1
  146. package/dist/web/assets/{twig-B7VUm2Ca.js → twig-DK8Vrp-G.js} +1 -1
  147. package/dist/web/assets/{use-blob-url-B3YovXF6.js → use-blob-url-DYtOHK7u.js} +1 -1
  148. package/dist/web/assets/{use-is-mobile-BcuCjH19.js → use-is-mobile-DnFSkUG2.js} +1 -1
  149. package/dist/web/assets/use-monaco-theme-CZh7xTJZ.js +1 -0
  150. package/dist/web/assets/vendor-markdown-Ls5AcU91.js +291 -0
  151. package/dist/web/assets/{vendor-mermaid-CYOBtZtD.js → vendor-mermaid-C977xNeE.js} +1 -1
  152. package/dist/web/assets/{vendor-ui-DMRGWRuc.js → vendor-ui-CaeeFO0B.js} +2 -2
  153. package/dist/web/assets/{video-preview-D-S_YnG6.js → video-preview-KbRcyuAA.js} +1 -1
  154. package/dist/web/assets/{vue-m75pWowk.js → vue-JkIEObMV.js} +1 -1
  155. package/dist/web/assets/{vue-html-odHs7IMn.js → vue-html-D8SqnU86.js} +1 -1
  156. package/dist/web/assets/{vue-vine-BbBB3V_G.js → vue-vine-DJj7N4WD.js} +1 -1
  157. package/dist/web/assets/x-B1qHmLdR.js +1 -0
  158. package/dist/web/assets/{xml-BHzIcDWI.js → xml-C-00w9Rv.js} +1 -1
  159. package/dist/web/assets/{xsl-DTLhN7uu.js → xsl-CVdCwPHV.js} +1 -1
  160. package/dist/web/index.html +29 -26
  161. package/dist/web/monacoeditorwork/css.worker.bundle.js +122 -122
  162. package/dist/web/monacoeditorwork/editor.worker.bundle.js +78 -78
  163. package/dist/web/monacoeditorwork/html.worker.bundle.js +110 -110
  164. package/dist/web/monacoeditorwork/json.worker.bundle.js +108 -108
  165. package/dist/web/monacoeditorwork/ts.worker.bundle.js +81 -81
  166. package/dist/web/sw.js +1 -1
  167. package/docs/codebase-summary.md +10 -1
  168. package/docs/design-guidelines.md +31 -0
  169. package/docs/project-roadmap.md +10 -0
  170. package/docs/system-architecture.md +59 -0
  171. package/package.json +2 -1
  172. package/src/server/index.ts +8 -0
  173. package/src/server/routes/files.ts +2 -2
  174. package/src/server/routes/fs-browse.ts +99 -74
  175. package/src/server/routes/fs-ops.ts +118 -0
  176. package/src/server/routes/fs-sqlite.ts +129 -0
  177. package/src/server/routes/host-info.ts +17 -0
  178. package/src/server/routes/settings.ts +2 -0
  179. package/src/services/download-token.service.ts +21 -5
  180. package/src/services/file.service.ts +8 -12
  181. package/src/services/fs-browse.service.ts +104 -161
  182. package/src/services/fs-ops/fs-content-disposition.ts +17 -0
  183. package/src/services/fs-ops/fs-core-ops.ts +149 -0
  184. package/src/services/fs-ops/fs-error-response.ts +24 -0
  185. package/src/services/fs-ops/fs-git-init.service.ts +23 -0
  186. package/src/services/fs-ops/fs-hidden-names.ts +17 -0
  187. package/src/services/fs-ops/fs-list-files.service.ts +68 -0
  188. package/src/services/fs-ops/fs-ops-copy-move.service.ts +60 -0
  189. package/src/services/fs-ops/fs-ops-mutate.service.ts +59 -0
  190. package/src/services/fs-ops/fs-ops-read-write.service.ts +70 -0
  191. package/src/services/fs-ops/fs-ops-stat.service.ts +109 -0
  192. package/src/services/fs-ops/fs-ops-trash.service.ts +111 -0
  193. package/src/services/fs-ops/fs-real-path.ts +44 -0
  194. package/src/services/fs-ops/sql-row-reader.ts +26 -0
  195. package/src/services/fs-ops/sql-statement-guard.ts +63 -0
  196. package/src/services/fs-path-guard.service.ts +196 -0
  197. package/src/services/host-info/apple-bookmark-decoder.ts +97 -0
  198. package/src/services/host-info/apple-plist-xml-parser.ts +107 -0
  199. package/src/services/host-info/concurrency-limit.ts +20 -0
  200. package/src/services/host-info/host-drives.ts +190 -0
  201. package/src/services/host-info/host-info.service.ts +171 -0
  202. package/src/services/host-info/host-known-folders.ts +180 -0
  203. package/src/services/host-info/pinned-linux-bookmarks.ts +89 -0
  204. package/src/services/host-info/pinned-macos-finder-favorites.ts +175 -0
  205. package/src/services/host-info/pinned-windows-quick-access.ts +87 -0
  206. package/src/services/host-info/spawn-runner.ts +41 -0
  207. package/src/services/sqlite-types.ts +26 -0
  208. package/src/services/sqlite.service.ts +38 -29
  209. package/src/services/subagent-transcript-merger.ts +1 -1
  210. package/src/types/system.ts +50 -0
  211. package/src/web/app.tsx +19 -3
  212. package/src/web/components/editor/code-editor.tsx +3 -9
  213. package/src/web/components/explorer/file-tree.tsx +103 -21
  214. package/src/web/components/explorer/tree-node-context-menu.tsx +6 -1
  215. package/src/web/components/explorer/tree-node.tsx +34 -93
  216. package/src/web/components/explorer/use-file-upload-drag.ts +14 -9
  217. package/src/web/components/explorer/use-tree-row-dnd.ts +206 -0
  218. package/src/web/components/floating-window/default-window-chrome.tsx +71 -0
  219. package/src/web/components/floating-window/floating-window.tsx +167 -0
  220. package/src/web/components/floating-window/use-window-drag.ts +28 -0
  221. package/src/web/components/floating-window/use-window-gesture-context.ts +44 -0
  222. package/src/web/components/floating-window/use-window-keyboard.ts +55 -0
  223. package/src/web/components/floating-window/use-window-resize.ts +30 -0
  224. package/src/web/components/floating-window/window-chrome-contract.ts +38 -0
  225. package/src/web/components/floating-window/window-content-registry.ts +51 -0
  226. package/src/web/components/floating-window/window-geometry.ts +121 -0
  227. package/src/web/components/floating-window/window-layer.tsx +92 -0
  228. package/src/web/components/floating-window/window-persistence.ts +104 -0
  229. package/src/web/components/floating-window/window-resize-handles.tsx +81 -0
  230. package/src/web/components/floating-window/window-store-types.ts +20 -0
  231. package/src/web/components/floating-window/window-store.ts +192 -0
  232. package/src/web/components/group-chat/group-list.tsx +2 -3
  233. package/src/web/components/layout/command-palette.tsx +10 -0
  234. package/src/web/components/layout/mobile-drawer-tab-bar.tsx +3 -10
  235. package/src/web/components/layout/mobile-drawer.tsx +13 -2
  236. package/src/web/components/layout/nav-section-rail.tsx +16 -11
  237. package/src/web/components/os-explorer/actions/explorer-actions-clipboard.ts +186 -0
  238. package/src/web/components/os-explorer/actions/explorer-actions-mutate.ts +109 -0
  239. package/src/web/components/os-explorer/actions/explorer-actions-open.ts +67 -0
  240. package/src/web/components/os-explorer/actions/explorer-actions-transfer-helpers.ts +35 -0
  241. package/src/web/components/os-explorer/actions/use-explorer-actions.ts +205 -0
  242. package/src/web/components/os-explorer/can-open-in-ppm.ts +89 -0
  243. package/src/web/components/os-explorer/dnd/drag-depth-counter.ts +36 -0
  244. package/src/web/components/os-explorer/dnd/drag-ghost.ts +44 -0
  245. package/src/web/components/os-explorer/dnd/drop-target-decision.ts +46 -0
  246. package/src/web/components/os-explorer/dnd/drop-target-style.ts +7 -0
  247. package/src/web/components/os-explorer/dnd/entry-drag-payload.ts +115 -0
  248. package/src/web/components/os-explorer/dnd/entry-drag-state.ts +35 -0
  249. package/src/web/components/os-explorer/dnd/entry-drop-executor.ts +43 -0
  250. package/src/web/components/os-explorer/dnd/use-drag-auto-scroll.ts +70 -0
  251. package/src/web/components/os-explorer/dnd/use-drop-transfer.tsx +82 -0
  252. package/src/web/components/os-explorer/dnd/use-entry-drag-source.ts +73 -0
  253. package/src/web/components/os-explorer/dnd/use-entry-drop-target.ts +150 -0
  254. package/src/web/components/os-explorer/dnd/use-entry-row-dnd.ts +66 -0
  255. package/src/web/components/os-explorer/dnd/use-path-drop-target.ts +30 -0
  256. package/src/web/components/os-explorer/explorer-body.tsx +153 -0
  257. package/src/web/components/os-explorer/explorer-breadcrumb.tsx +108 -0
  258. package/src/web/components/os-explorer/explorer-context-menu.tsx +131 -0
  259. package/src/web/components/os-explorer/explorer-dialogs.tsx +103 -0
  260. package/src/web/components/os-explorer/explorer-modal-shell.tsx +60 -0
  261. package/src/web/components/os-explorer/explorer-pins-store.ts +63 -0
  262. package/src/web/components/os-explorer/explorer-sidebar.tsx +198 -0
  263. package/src/web/components/os-explorer/explorer-sort-menu.tsx +68 -0
  264. package/src/web/components/os-explorer/explorer-status-bar.tsx +35 -0
  265. package/src/web/components/os-explorer/explorer-store.ts +179 -0
  266. package/src/web/components/os-explorer/explorer-toolbar.tsx +139 -0
  267. package/src/web/components/os-explorer/explorer-window-content.tsx +36 -0
  268. package/src/web/components/os-explorer/format-file-meta.ts +74 -0
  269. package/src/web/components/os-explorer/icons/file-type-icon-map.ts +106 -0
  270. package/src/web/components/os-explorer/icons/file-type-icon.tsx +56 -0
  271. package/src/web/components/os-explorer/icons/thumbnail-fetch-semaphore.ts +44 -0
  272. package/src/web/components/os-explorer/icons/thumbnail-image.tsx +124 -0
  273. package/src/web/components/os-explorer/mobile/mobile-bottom-toolbar.tsx +102 -0
  274. package/src/web/components/os-explorer/mobile/mobile-explorer-sheet.tsx +61 -0
  275. package/src/web/components/os-explorer/mobile/mobile-explorer-top-bar.tsx +41 -0
  276. package/src/web/components/os-explorer/mobile/mobile-more-sheet.tsx +117 -0
  277. package/src/web/components/os-explorer/mobile/mobile-places-sheet.tsx +120 -0
  278. package/src/web/components/os-explorer/mobile/mobile-places-strip.tsx +109 -0
  279. package/src/web/components/os-explorer/mobile/mobile-tap-action.ts +19 -0
  280. package/src/web/components/os-explorer/mobile/use-mobile-row-tap.ts +50 -0
  281. package/src/web/components/os-explorer/open-explorer.ts +35 -0
  282. package/src/web/components/os-explorer/properties-dialog.tsx +99 -0
  283. package/src/web/components/os-explorer/skins/explorer-skin-chrome.tsx +17 -0
  284. package/src/web/components/os-explorer/skins/folder-icon-macos.tsx +42 -0
  285. package/src/web/components/os-explorer/skins/folder-icon-windows.tsx +25 -0
  286. package/src/web/components/os-explorer/skins/macos-skin.ts +14 -0
  287. package/src/web/components/os-explorer/skins/macos-window-chrome.tsx +69 -0
  288. package/src/web/components/os-explorer/skins/skin-types.ts +40 -0
  289. package/src/web/components/os-explorer/skins/skins.css +25 -0
  290. package/src/web/components/os-explorer/skins/use-explorer-skin.ts +21 -0
  291. package/src/web/components/os-explorer/skins/windows-skin.ts +11 -0
  292. package/src/web/components/os-explorer/skins/windows-window-chrome.tsx +56 -0
  293. package/src/web/components/os-explorer/sort-and-filter-entries.ts +52 -0
  294. package/src/web/components/os-explorer/sort-options.ts +29 -0
  295. package/src/web/components/os-explorer/toolbar-icon-button.ts +18 -0
  296. package/src/web/components/os-explorer/use-coarse-long-press.ts +97 -0
  297. package/src/web/components/os-explorer/use-explorer-keyboard.ts +124 -0
  298. package/src/web/components/os-explorer/use-explorer-navigation.ts +155 -0
  299. package/src/web/components/os-explorer/use-explorer-open-state.ts +67 -0
  300. package/src/web/components/os-explorer/use-host-info.ts +85 -0
  301. package/src/web/components/os-explorer/views/column-row.tsx +126 -0
  302. package/src/web/components/os-explorer/views/column-view-column.tsx +112 -0
  303. package/src/web/components/os-explorer/views/column-view-mobile.tsx +98 -0
  304. package/src/web/components/os-explorer/views/column-view-preview.tsx +70 -0
  305. package/src/web/components/os-explorer/views/column-view.tsx +204 -0
  306. package/src/web/components/os-explorer/views/compose-interactive-row-props.ts +77 -0
  307. package/src/web/components/os-explorer/views/explorer-view-registry.tsx +55 -0
  308. package/src/web/components/os-explorer/views/icons-view-tile.tsx +158 -0
  309. package/src/web/components/os-explorer/views/icons-view.tsx +188 -0
  310. package/src/web/components/os-explorer/views/inline-name-input.tsx +60 -0
  311. package/src/web/components/os-explorer/views/list-row.tsx +162 -0
  312. package/src/web/components/os-explorer/views/list-view.tsx +177 -0
  313. package/src/web/components/os-explorer/views/use-column-view-state.ts +158 -0
  314. package/src/web/components/os-explorer/views/use-entry-selection.ts +178 -0
  315. package/src/web/components/settings/settings-tab.tsx +29 -2
  316. package/src/web/components/sqlite/sqlite-viewer.tsx +5 -2
  317. package/src/web/components/sqlite/use-sqlite.ts +7 -2
  318. package/src/web/components/ui/adaptive-context-menu.tsx +57 -1
  319. package/src/web/components/ui/feature-badge.tsx +40 -0
  320. package/src/web/components/ui/file-browser-picker.tsx +1 -18
  321. package/src/web/lib/feature-badges.ts +22 -0
  322. package/src/web/lib/file-download.ts +6 -4
  323. package/src/web/lib/fs-api.ts +185 -0
  324. package/src/web/lib/sidebar-tabs/tab-registry.ts +4 -3
  325. package/src/web/stores/file-store.ts +38 -1
  326. package/src/web/stores/settings-store.ts +14 -0
  327. package/src/web/styles/globals.css +1 -0
  328. package/dist/web/assets/api-client-RtNXvS0P.js +0 -1
  329. package/dist/web/assets/api-settings-De--HNHp.js +0 -1
  330. package/dist/web/assets/architecture-PBZL5I3N-DVRYSsQn.js +0 -1
  331. package/dist/web/assets/chat-tab-C5U7a_rP.js +0 -13
  332. package/dist/web/assets/chevron-right-DeUue2aK.js +0 -1
  333. package/dist/web/assets/code-TekEnDFM.js +0 -1
  334. package/dist/web/assets/code-editor-CqlzLQ7T.js +0 -10
  335. package/dist/web/assets/context-menu-DIiZdfbE.js +0 -1
  336. package/dist/web/assets/csv-preview-DD94T9ff.js +0 -2
  337. package/dist/web/assets/dialog-HHdNInEH.js +0 -1
  338. package/dist/web/assets/diff-viewer-DyQLYP7J.js +0 -4
  339. package/dist/web/assets/git-log-panel-CrUgsLY5.js +0 -1
  340. package/dist/web/assets/gitGraph-HDMCJU4V-CutkUNY_.js +0 -1
  341. package/dist/web/assets/glide-data-grid-LinTYl0_.js +0 -138
  342. package/dist/web/assets/group-chat-tab-BqNJW7y2.js +0 -1
  343. package/dist/web/assets/index-CVJmlKhp.css +0 -2
  344. package/dist/web/assets/index-I2ik0ZF7.js +0 -50
  345. package/dist/web/assets/info-3K5VOQVL-BpNjgbj9.js +0 -1
  346. package/dist/web/assets/keybindings-store-C46tNxJE.js +0 -1
  347. package/dist/web/assets/markdown-renderer-DSxSupDq.js +0 -3
  348. package/dist/web/assets/markdown-renderer-xzyhJklS.js +0 -1
  349. package/dist/web/assets/notification-store-Dek7ClI2.js +0 -1
  350. package/dist/web/assets/packet-RMMSAZCW-D5IUZ66-.js +0 -1
  351. package/dist/web/assets/pie-UPGHQEXC-CM29eKDR.js +0 -1
  352. package/dist/web/assets/radar-KQ55EAFF-QtJGaSGt.js +0 -1
  353. package/dist/web/assets/settings-store-B3LfpCPa.js +0 -2
  354. package/dist/web/assets/settings-tab-DIuR1VIc.js +0 -1
  355. package/dist/web/assets/sparkles-B3DmG8PJ.js +0 -1
  356. package/dist/web/assets/sqlite-viewer-CEObRQ7n.js +0 -1
  357. package/dist/web/assets/tool-cards-Bdx04yYD.js +0 -7
  358. package/dist/web/assets/treemap-KZPCXAKY-CjUqpVnS.js +0 -1
  359. package/dist/web/assets/use-monaco-theme-k-17kQqf.js +0 -1
  360. package/dist/web/assets/vendor-markdown-CKTlmXbz.js +0 -291
  361. package/dist/web/assets/x-BsFLg-T8.js +0 -1
  362. /package/dist/web/assets/{abap-BM5PMK0-.js → abap-DrMyiWmK.js} +0 -0
  363. /package/dist/web/assets/{actionscript-3-CML-MTNy.js → actionscript-3-BlSOyi0y.js} +0 -0
  364. /package/dist/web/assets/{ada-DzO1tMqV.js → ada-B_m96icB.js} +0 -0
  365. /package/dist/web/assets/{andromeeda-C3nkvRhj.js → andromeeda-Ch3vOiZB.js} +0 -0
  366. /package/dist/web/assets/{apache-Dg7oWQbs.js → apache-CdsaXc1n.js} +0 -0
  367. /package/dist/web/assets/{apex-3O2170Ju.js → apex-0AnsBevk.js} +0 -0
  368. /package/dist/web/assets/{applescript-5AlzBqQO.js → applescript-CSxLolzy.js} +0 -0
  369. /package/dist/web/assets/{apply-theme-BSJDABU3.js → apply-theme-CMQXnUAa.js} +0 -0
  370. /package/dist/web/assets/{ara-BGtOX5Hn.js → ara-DXZS0jA5.js} +0 -0
  371. /package/dist/web/assets/{asciidoc-CIsC4kkq.js → asciidoc-DDy3pH2A.js} +0 -0
  372. /package/dist/web/assets/{asm-p1-PUkh3.js → asm-Bd17hhqg.js} +0 -0
  373. /package/dist/web/assets/{aurora-x-BPgGL50S.js → aurora-x-C62HJRs-.js} +0 -0
  374. /package/dist/web/assets/{awk-Cj93pRL8.js → awk-Cc-nazni.js} +0 -0
  375. /package/dist/web/assets/{ayu-dark-C50MYKhw.js → ayu-dark-BsfmNWCU.js} +0 -0
  376. /package/dist/web/assets/{ayu-light-DyIp0Idy.js → ayu-light-BZ4HLa4X.js} +0 -0
  377. /package/dist/web/assets/{ayu-mirage-BAdm8pKU.js → ayu-mirage-Bhyjyiqr.js} +0 -0
  378. /package/dist/web/assets/{ballerina-DUO9mwyV.js → ballerina-QiWgir3v.js} +0 -0
  379. /package/dist/web/assets/{bat-Cg8hlSct.js → bat-DxtlkwN3.js} +0 -0
  380. /package/dist/web/assets/{beancount-f8f3BBqB.js → beancount-Bt21X7M_.js} +0 -0
  381. /package/dist/web/assets/{berry-BqVQYZZr.js → berry-Cksre7lO.js} +0 -0
  382. /package/dist/web/assets/{bibtex-C_q8ygAM.js → bibtex-DkakgDlR.js} +0 -0
  383. /package/dist/web/assets/{bicep-DWS-2uWR.js → bicep-8crmcyXU.js} +0 -0
  384. /package/dist/web/assets/{bird2-C0pvipi_.js → bird2-CewQ2I_Z.js} +0 -0
  385. /package/dist/web/assets/{c-BQFvJ6Ux.js → c-C_6zE8Ha.js} +0 -0
  386. /package/dist/web/assets/{c3-Da53KeG6.js → c3-DLWB9-dp.js} +0 -0
  387. /package/dist/web/assets/{cadence-B5X2vWm3.js → cadence-D8qYS6pR.js} +0 -0
  388. /package/dist/web/assets/{catppuccin-frappe-Dm3rVY3b.js → catppuccin-frappe-Bh95lNRy.js} +0 -0
  389. /package/dist/web/assets/{catppuccin-latte-ZxS-ga_K.js → catppuccin-latte-C1PlN3G0.js} +0 -0
  390. /package/dist/web/assets/{catppuccin-macchiato-B0bAzXh0.js → catppuccin-macchiato-CldtgdK4.js} +0 -0
  391. /package/dist/web/assets/{catppuccin-mocha-BeMC5hqi.js → catppuccin-mocha-BiwqxX9A.js} +0 -0
  392. /package/dist/web/assets/{clarity-BRdirlsw.js → clarity-CGiU22jY.js} +0 -0
  393. /package/dist/web/assets/{clojure-pSLH-KQp.js → clojure-COIC3doh.js} +0 -0
  394. /package/dist/web/assets/{cmake-DsZu4U4F.js → cmake-DWUEQfHv.js} +0 -0
  395. /package/dist/web/assets/{codeowners-CtkKTnsH.js → codeowners-BNHiAQea.js} +0 -0
  396. /package/dist/web/assets/{codeql-BpP1PDrj.js → codeql-BuKaoS0j.js} +0 -0
  397. /package/dist/web/assets/{common-lisp-BX1XgHTt.js → common-lisp-D5IKbxsd.js} +0 -0
  398. /package/dist/web/assets/{coq-D2bX6-8g.js → coq-BMVS-p94.js} +0 -0
  399. /package/dist/web/assets/{csharp-DcR97P0D.js → csharp-DMlbXbM4.js} +0 -0
  400. /package/dist/web/assets/{css-CIjciIIk.js → css-CjkJC89X.js} +0 -0
  401. /package/dist/web/assets/{csv-DW48Z5d-.js → csv-DoNVBBf0.js} +0 -0
  402. /package/dist/web/assets/{csv-parser-DVHBcvuE.js → csv-parser-DHvoTjMF.js} +0 -0
  403. /package/dist/web/assets/{cue-Byh1xHko.js → cue-DoTRbBg-.js} +0 -0
  404. /package/dist/web/assets/{cypher-TVlMMWma.js → cypher-DVsNdTT3.js} +0 -0
  405. /package/dist/web/assets/{d-CxaScvxf.js → d-EX7BBSrv.js} +0 -0
  406. /package/dist/web/assets/{dark-plus-sn-FFTT1.js → dark-plus-P_YTHwlh.js} +0 -0
  407. /package/dist/web/assets/{dart-BmpakMe-.js → dart-Do-rgU4S.js} +0 -0
  408. /package/dist/web/assets/{dax-B1aRUMXT.js → dax-DxOieacu.js} +0 -0
  409. /package/dist/web/assets/{desktop-C6lD62ar.js → desktop-CvdV7SBf.js} +0 -0
  410. /package/dist/web/assets/{diff-CupGmT1a.js → diff-CNOetGen.js} +0 -0
  411. /package/dist/web/assets/{dist-Bi4gRyhr.js → dist-CMfLpPW_.js} +0 -0
  412. /package/dist/web/assets/{docker-DjDr3TJF.js → docker-DAlG4_q-.js} +0 -0
  413. /package/dist/web/assets/{dotenv-QeT1SLTv.js → dotenv-Dh3qf6yx.js} +0 -0
  414. /package/dist/web/assets/{dracula-yI5kpKp5.js → dracula-baAGmJYG.js} +0 -0
  415. /package/dist/web/assets/{dracula-soft-CYF6cJce.js → dracula-soft-l7ZIyums.js} +0 -0
  416. /package/dist/web/assets/{dream-maker-TBDKiHrV.js → dream-maker-DeRjdBn8.js} +0 -0
  417. /package/dist/web/assets/{emacs-lisp-B7aZTnJ_.js → emacs-lisp-CB05-jQA.js} +0 -0
  418. /package/dist/web/assets/{everforest-dark-CN77B1NJ.js → everforest-dark-CT-llQBT.js} +0 -0
  419. /package/dist/web/assets/{everforest-light-CyFxGMIs.js → everforest-light-ByPJkTT7.js} +0 -0
  420. /package/dist/web/assets/{fennel-C6ktGUzp.js → fennel-CeMarbvn.js} +0 -0
  421. /package/dist/web/assets/{fish-Vr62C2vP.js → fish-BYrt6Xtb.js} +0 -0
  422. /package/dist/web/assets/{fluent-CH3IJUki.js → fluent-Cr5VcAwh.js} +0 -0
  423. /package/dist/web/assets/{fortran-free-form-BN7VmmxZ.js → fortran-free-form-DQmKtrLa.js} +0 -0
  424. /package/dist/web/assets/{gdscript-BYdRNu_P.js → gdscript-QTDKjzhY.js} +0 -0
  425. /package/dist/web/assets/{gdshader-C2XOq90k.js → gdshader-Chr-5UYt.js} +0 -0
  426. /package/dist/web/assets/{genie-B4hkIXNU.js → genie-kWHMWnt0.js} +0 -0
  427. /package/dist/web/assets/{gherkin-CW9NH2DW.js → gherkin-DtEBQFUo.js} +0 -0
  428. /package/dist/web/assets/{github-dark-BWCf99kM.js → github-dark-B5aiUta2.js} +0 -0
  429. /package/dist/web/assets/{github-dark-default-DZ1m_6OP.js → github-dark-default-BkxtsTqR.js} +0 -0
  430. /package/dist/web/assets/{github-dark-dimmed-CUXSQ8bS.js → github-dark-dimmed-B9QjR_0K.js} +0 -0
  431. /package/dist/web/assets/{github-dark-high-contrast-B6BvU8H-.js → github-dark-high-contrast-D6bO-5c0.js} +0 -0
  432. /package/dist/web/assets/{github-light-Ch-E92cy.js → github-light-Cg2ZW_CJ.js} +0 -0
  433. /package/dist/web/assets/{github-light-default-CKFslV7i.js → github-light-default-eD7qUVlQ.js} +0 -0
  434. /package/dist/web/assets/{github-light-high-contrast-DiJVeRlZ.js → github-light-high-contrast-CFnlSVjd.js} +0 -0
  435. /package/dist/web/assets/{gleam-D4dP7V5M.js → gleam-BpYoOqIT.js} +0 -0
  436. /package/dist/web/assets/{gn-DuO2D2eW.js → gn-BxwtZPau.js} +0 -0
  437. /package/dist/web/assets/{gnuplot-Df_8pFbI.js → gnuplot-CRyiZDfl.js} +0 -0
  438. /package/dist/web/assets/{go-BL16cMJO.js → go-Dw1Qx94W.js} +0 -0
  439. /package/dist/web/assets/{groovy-rfHeCRqy.js → groovy-zq_SbWk-.js} +0 -0
  440. /package/dist/web/assets/{gruvbox-dark-hard-CLbDN-E3.js → gruvbox-dark-hard-DbbNy8-Q.js} +0 -0
  441. /package/dist/web/assets/{gruvbox-dark-medium-Df0L0qje.js → gruvbox-dark-medium-BQZ7Kj6C.js} +0 -0
  442. /package/dist/web/assets/{gruvbox-dark-soft-DV6TAcen.js → gruvbox-dark-soft-vNavErXi.js} +0 -0
  443. /package/dist/web/assets/{gruvbox-light-hard-CWXUzp9b.js → gruvbox-light-hard-AOFbCd7i.js} +0 -0
  444. /package/dist/web/assets/{gruvbox-light-medium-BOHMe1-7.js → gruvbox-light-medium-DqtxND6H.js} +0 -0
  445. /package/dist/web/assets/{gruvbox-light-soft-DrrS2sdV.js → gruvbox-light-soft-Cg0basu6.js} +0 -0
  446. /package/dist/web/assets/{haskell-DybKb9uW.js → haskell-CcsmcOez.js} +0 -0
  447. /package/dist/web/assets/{haxe-DLRwZxmt.js → haxe-BimYY9X4.js} +0 -0
  448. /package/dist/web/assets/{hcl-CusCzwOk.js → hcl-CTrXCK40.js} +0 -0
  449. /package/dist/web/assets/{hjson-BygWIJ-3.js → hjson-CmqqU6Qg.js} +0 -0
  450. /package/dist/web/assets/{hlsl-DCKvuEyZ.js → hlsl-CuJtX_uf.js} +0 -0
  451. /package/dist/web/assets/{horizon-DSOVAzyH.js → horizon-BGeSjDYJ.js} +0 -0
  452. /package/dist/web/assets/{horizon-bright-DKfVjGj_.js → horizon-bright-GYMgd3R0.js} +0 -0
  453. /package/dist/web/assets/{houston-Ddm8AgpV.js → houston--JD_5r2q.js} +0 -0
  454. /package/dist/web/assets/{hy-DzZa6alE.js → hy-C9ILOiqk.js} +0 -0
  455. /package/dist/web/assets/{image-gallery-CObzTisL.js → image-gallery-BWFpZAAP.js} +0 -0
  456. /package/dist/web/assets/{imba-aiXalPnK.js → imba-B6F3yus5.js} +0 -0
  457. /package/dist/web/assets/{ini-CAvrVa5R.js → ini-BfkQswMZ.js} +0 -0
  458. /package/dist/web/assets/{java-PRMSQp24.js → java-DEH5avQ1.js} +0 -0
  459. /package/dist/web/assets/{javascript-DQR6GQKs.js → javascript-Dr4yHuV2.js} +0 -0
  460. /package/dist/web/assets/{json-uWUKqOxq.js → json-BuR35ewf.js} +0 -0
  461. /package/dist/web/assets/{json5-R_LXJXL0.js → json5-Ny9ZiJAC.js} +0 -0
  462. /package/dist/web/assets/{jsonc-tC5zvrPO.js → jsonc-zYVuoZkR.js} +0 -0
  463. /package/dist/web/assets/{jsonl-BAwnWh7U.js → jsonl-B9xthb_H.js} +0 -0
  464. /package/dist/web/assets/{jsonnet-CPj4HCRm.js → jsonnet-CCoLJbMP.js} +0 -0
  465. /package/dist/web/assets/{jssm-B7HswxOM.js → jssm-W0GDlQXi.js} +0 -0
  466. /package/dist/web/assets/{jsx-z4TGELE1.js → jsx-BrLJpWC-.js} +0 -0
  467. /package/dist/web/assets/{kanagawa-dragon-BpeLlraV.js → kanagawa-dragon-CuVwt_MO.js} +0 -0
  468. /package/dist/web/assets/{kanagawa-lotus-CLnIPAGs.js → kanagawa-lotus-B3W_617P.js} +0 -0
  469. /package/dist/web/assets/{kanagawa-wave-BglPniBb.js → kanagawa-wave-CEx4MP5W.js} +0 -0
  470. /package/dist/web/assets/{kdl-B6blIqLq.js → kdl-DilRejnB.js} +0 -0
  471. /package/dist/web/assets/{kotlin-DVAEYiwY.js → kotlin-Bq5JtKaa.js} +0 -0
  472. /package/dist/web/assets/{kusto-Ba87977j.js → kusto-Bhp2KYog.js} +0 -0
  473. /package/dist/web/assets/{laserwave-C-NuzoMp.js → laserwave-WA_7c5hu.js} +0 -0
  474. /package/dist/web/assets/{lean-CJ_KLkrJ.js → lean-AEGcXwS-.js} +0 -0
  475. /package/dist/web/assets/{less-Dfn9YC_2.js → less-NPqH-26u.js} +0 -0
  476. /package/dist/web/assets/{libesm-DdtEjhIi.js → libesm-tR4XlhMj.js} +0 -0
  477. /package/dist/web/assets/{light-plus-B3BLlguG.js → light-plus-B4xW7cgY.js} +0 -0
  478. /package/dist/web/assets/{llvm-6UDP3NQb.js → llvm-CPyWNcDE.js} +0 -0
  479. /package/dist/web/assets/{log-QW3dqr1D.js → log-CeQmiQSz.js} +0 -0
  480. /package/dist/web/assets/{logo-DUinJheI.js → logo-BO0VrjBm.js} +0 -0
  481. /package/dist/web/assets/{luau-B1vcBzl8.js → luau-B6GT9kA1.js} +0 -0
  482. /package/dist/web/assets/{make-CqNL10gS.js → make-CwfmN7e_.js} +0 -0
  483. /package/dist/web/assets/{markdown-BQNMNiYS.js → markdown-Bbe4FlLp.js} +0 -0
  484. /package/dist/web/assets/{material-theme-darker-BZrskvoo.js → material-theme-darker-u7M3XGe7.js} +0 -0
  485. /package/dist/web/assets/{material-theme-rbV8EefR.js → material-theme-iD9XbfsB.js} +0 -0
  486. /package/dist/web/assets/{material-theme-lighter-B0CBBjBw.js → material-theme-lighter-BGdI48zn.js} +0 -0
  487. /package/dist/web/assets/{material-theme-ocean-CX_CHnQq.js → material-theme-ocean-_mk62F5q.js} +0 -0
  488. /package/dist/web/assets/{material-theme-palenight-ztiOCPbw.js → material-theme-palenight-CxFbDyAu.js} +0 -0
  489. /package/dist/web/assets/{matlab-tHQDNC9G.js → matlab-Z0RUE3V_.js} +0 -0
  490. /package/dist/web/assets/{mdx-D4kZ6c6G.js → mdx-BZ5wPYzV.js} +0 -0
  491. /package/dist/web/assets/{mermaid-DlR4U4Pe.js → mermaid-CY1_aP-h.js} +0 -0
  492. /package/dist/web/assets/{min-dark-BX-xHAD2.js → min-dark-DKWtxOre.js} +0 -0
  493. /package/dist/web/assets/{min-light-Bxw0_Gqq.js → min-light-e1iwHDfP.js} +0 -0
  494. /package/dist/web/assets/{mipsasm-DYyZAum-.js → mipsasm-BiDfQ4ir.js} +0 -0
  495. /package/dist/web/assets/{mojo-q9wuEYKR.js → mojo-DteFEHsj.js} +0 -0
  496. /package/dist/web/assets/{monokai-D9vrJ8JM.js → monokai-BgPbE9xZ.js} +0 -0
  497. /package/dist/web/assets/{moonbit-C9roWxMb.js → moonbit-mEbRp-mA.js} +0 -0
  498. /package/dist/web/assets/{move-BvhjG2ic.js → move-CogqZw7A.js} +0 -0
  499. /package/dist/web/assets/{narrat-BXGbPwG3.js → narrat-BD2qjmy1.js} +0 -0
  500. /package/dist/web/assets/{nextflow-groovy-CMy5-Z4k.js → nextflow-groovy-BNX8GOD_.js} +0 -0
  501. /package/dist/web/assets/{night-owl-CBQBcbmw.js → night-owl-ed3GbziD.js} +0 -0
  502. /package/dist/web/assets/{night-owl-light-CC-brflp.js → night-owl-light-B0j7O0Wn.js} +0 -0
  503. /package/dist/web/assets/{nix-B9EAdptu.js → nix-DFDbcQQm.js} +0 -0
  504. /package/dist/web/assets/{nord-DxASd4qG.js → nord-D93TxEQi.js} +0 -0
  505. /package/dist/web/assets/{nushell-BsY59i7N.js → nushell-3_H_XKja.js} +0 -0
  506. /package/dist/web/assets/{objective-c-CoeQAcnP.js → objective-c-BM3u6nlm.js} +0 -0
  507. /package/dist/web/assets/{objective-cpp-BysdFvND.js → objective-cpp-DZ73h42g.js} +0 -0
  508. /package/dist/web/assets/{ocaml-CrtQYMMK.js → ocaml-B1PU5gmh.js} +0 -0
  509. /package/dist/web/assets/{odin-B8pz4gIL.js → odin-D14L6aRL.js} +0 -0
  510. /package/dist/web/assets/{one-dark-pro-3sSHqoqQ.js → one-dark-pro-Bj-94Z8v.js} +0 -0
  511. /package/dist/web/assets/{one-light-LUZAvT8g.js → one-light-DTdGWyOZ.js} +0 -0
  512. /package/dist/web/assets/{openscad-DcKia-sV.js → openscad-Ciq554Sj.js} +0 -0
  513. /package/dist/web/assets/{pascal-zM5SlmgF.js → pascal-Bfd4qdGi.js} +0 -0
  514. /package/dist/web/assets/{pkl-C_r_zLLt.js → pkl-CTurDUfj.js} +0 -0
  515. /package/dist/web/assets/{plastic-B3cRtgKD.js → plastic-BLQfZXQp.js} +0 -0
  516. /package/dist/web/assets/{plsql-BzyjheXU.js → plsql-DyYfP9kQ.js} +0 -0
  517. /package/dist/web/assets/{po-Dz4OaDNi.js → po-CgXdT8vQ.js} +0 -0
  518. /package/dist/web/assets/{poimandres-BBbtYwmR.js → poimandres-DOg5fK6s.js} +0 -0
  519. /package/dist/web/assets/{polar-Che1XC2Z.js → polar-Dv4aoL6X.js} +0 -0
  520. /package/dist/web/assets/{postcss-DouzwG4F.js → postcss-DxugHnJD.js} +0 -0
  521. /package/dist/web/assets/{powerquery-YOdcTZmd.js → powerquery-CR3kzRyX.js} +0 -0
  522. /package/dist/web/assets/{powershell-8ojzYH34.js → powershell-BE91xJQB.js} +0 -0
  523. /package/dist/web/assets/{prisma-DCmjceML.js → prisma-B_3yUlRQ.js} +0 -0
  524. /package/dist/web/assets/{prolog-ByNNz2pB.js → prolog-DcT781Ht.js} +0 -0
  525. /package/dist/web/assets/{proto-DWMt6Qu1.js → proto-CSf-w5k2.js} +0 -0
  526. /package/dist/web/assets/{puppet-osfKyZp4.js → puppet-CLGO6QWl.js} +0 -0
  527. /package/dist/web/assets/{purescript-ClIblkvh.js → purescript-Bah2McKd.js} +0 -0
  528. /package/dist/web/assets/{python-Ctyr4faP.js → python-DUH8qy7N.js} +0 -0
  529. /package/dist/web/assets/{qmldir-CbNgyhnI.js → qmldir-YIsHnjKq.js} +0 -0
  530. /package/dist/web/assets/{qss-LbRHzb21.js → qss-CLmoVNA4.js} +0 -0
  531. /package/dist/web/assets/{r-Dvfjp0wc.js → r-Pon-b-vz.js} +0 -0
  532. /package/dist/web/assets/{racket-DI8v8nzD.js → racket-BA09P5Kg.js} +0 -0
  533. /package/dist/web/assets/{raku-CX1q9GW0.js → raku-DlpzU7Fe.js} +0 -0
  534. /package/dist/web/assets/{red-CSz3QEOw.js → red-l7VuCR6d.js} +0 -0
  535. /package/dist/web/assets/{reg-FMjhUQE-.js → reg-g_k7gQEr.js} +0 -0
  536. /package/dist/web/assets/{regexp-CCSe1Fc4.js → regexp-CT17B7I7.js} +0 -0
  537. /package/dist/web/assets/{rel-Ce0UO-OB.js → rel-DkPCS7Bp.js} +0 -0
  538. /package/dist/web/assets/{resolve-theme-B9Mgd5GG.js → resolve-theme-B0eotaPq.js} +0 -0
  539. /package/dist/web/assets/{riscv-BFZrdZtB.js → riscv-CYuFTw30.js} +0 -0
  540. /package/dist/web/assets/{ron-D0t4kvJG.js → ron-cY7u_yEu.js} +0 -0
  541. /package/dist/web/assets/{rose-pine-HzwmOzHM.js → rose-pine-Cr_h_Ubo.js} +0 -0
  542. /package/dist/web/assets/{rose-pine-dawn-CSp9xAF9.js → rose-pine-dawn-rRewaSZg.js} +0 -0
  543. /package/dist/web/assets/{rose-pine-moon-Cnzr2Cw0.js → rose-pine-moon-3VX5Xwca.js} +0 -0
  544. /package/dist/web/assets/{rosmsg-DEJ8Snbv.js → rosmsg-Du4ie5PU.js} +0 -0
  545. /package/dist/web/assets/{rust-2jBzrC0l.js → rust-B7xWL-Eh.js} +0 -0
  546. /package/dist/web/assets/{sass-DD75Izu_.js → sass-oXbOG8de.js} +0 -0
  547. /package/dist/web/assets/{scala-DpBKROIu.js → scala-nlZJhKHf.js} +0 -0
  548. /package/dist/web/assets/{scheme-DM2jzwUK.js → scheme-FZ9_Zy8D.js} +0 -0
  549. /package/dist/web/assets/{sdbl-C34Cg6e7.js → sdbl-D_lyvXiu.js} +0 -0
  550. /package/dist/web/assets/{shellscript-DxfzKSgU.js → shellscript-DRWRboAd.js} +0 -0
  551. /package/dist/web/assets/{slack-dark-DIIV3bpD.js → slack-dark-BGk2uVHu.js} +0 -0
  552. /package/dist/web/assets/{slack-ochin-BTkxWjEP.js → slack-ochin-PAoSX041.js} +0 -0
  553. /package/dist/web/assets/{smalltalk-Bm0qg6SA.js → smalltalk-qP0zvICo.js} +0 -0
  554. /package/dist/web/assets/{snazzy-light-ClAuuhn4.js → snazzy-light-B7uccpH6.js} +0 -0
  555. /package/dist/web/assets/{solarized-dark-93sEWmYT.js → solarized-dark-CbICLZs3.js} +0 -0
  556. /package/dist/web/assets/{solarized-light-C74FvgcO.js → solarized-light-CprWhyjj.js} +0 -0
  557. /package/dist/web/assets/{solidity-H9fSjkQP.js → solidity-B7pcm2K6.js} +0 -0
  558. /package/dist/web/assets/{splunk-CDTN2iAG.js → splunk-Co2LL4J_.js} +0 -0
  559. /package/dist/web/assets/{sql-CoqObhgo.js → sql-cETIdGnT.js} +0 -0
  560. /package/dist/web/assets/{ssh-config-C3Wv6qRp.js → ssh-config-DETG6JCD.js} +0 -0
  561. /package/dist/web/assets/{stylus-D0uTFXCy.js → stylus-tmTre2-O.js} +0 -0
  562. /package/dist/web/assets/{swift-DtMMgvRI.js → swift-BeKQbULV.js} +0 -0
  563. /package/dist/web/assets/{synthwave-84-DAroyZBu.js → synthwave-84-eCL4-Xdn.js} +0 -0
  564. /package/dist/web/assets/{system-verilog-Bj7X_lq4.js → system-verilog-CviwWvEx.js} +0 -0
  565. /package/dist/web/assets/{systemd-CH8ApCUs.js → systemd-WuoFIbBS.js} +0 -0
  566. /package/dist/web/assets/{talonscript-70lVC3cw.js → talonscript-B9VBu7VI.js} +0 -0
  567. /package/dist/web/assets/{tasl-WQR8VslV.js → tasl-CN7r11ky.js} +0 -0
  568. /package/dist/web/assets/{tcl-caTR_frn.js → tcl-e1cdhe3h.js} +0 -0
  569. /package/dist/web/assets/{terraform-CVRZ4SZ4.js → terraform-BEP0mfa_.js} +0 -0
  570. /package/dist/web/assets/{tokyo-night-CDyT1jCI.js → tokyo-night-BfA_dA37.js} +0 -0
  571. /package/dist/web/assets/{toml-B7D2mIDU.js → toml-BrDIfNnt.js} +0 -0
  572. /package/dist/web/assets/{tsv-2kdn5dqL.js → tsv-CyfGyE4i.js} +0 -0
  573. /package/dist/web/assets/{tsx-DJfG3fzg.js → tsx-BrFA8Iwf.js} +0 -0
  574. /package/dist/web/assets/{turtle-DaQac_UT.js → turtle-9GBtR0mP.js} +0 -0
  575. /package/dist/web/assets/{typescript-BLfAIchl.js → typescript-BuTRZ65V.js} +0 -0
  576. /package/dist/web/assets/{typespec-ymBaOi_c.js → typespec-CRiNNRXF.js} +0 -0
  577. /package/dist/web/assets/{typst-DkvUYMT-.js → typst-CmpTnnkl.js} +0 -0
  578. /package/dist/web/assets/{utils-E0yyGxXt.js → utils-B9H0d3Fh.js} +0 -0
  579. /package/dist/web/assets/{v-D4997FGs.js → v-DnIi2aab.js} +0 -0
  580. /package/dist/web/assets/{vala-AFg9DmKE.js → vala-B7VxCNZP.js} +0 -0
  581. /package/dist/web/assets/{vb-DNUJ9fOP.js → vb-DGoQ3pBT.js} +0 -0
  582. /package/dist/web/assets/{vendor-xterm-QDr0jvpJ.js → vendor-xterm-ClRVIJ-k.js} +0 -0
  583. /package/dist/web/assets/{verilog-BrnAQ3tF.js → verilog-zGOC4qwV.js} +0 -0
  584. /package/dist/web/assets/{vesper-DCZnFmY0.js → vesper-DXR9-06q.js} +0 -0
  585. /package/dist/web/assets/{vhdl-DoK4PjhE.js → vhdl-CCO0oz0l.js} +0 -0
  586. /package/dist/web/assets/{viml-D6EKG66q.js → viml-Dxwr-QNN.js} +0 -0
  587. /package/dist/web/assets/{vitesse-black-C6KILWdF.js → vitesse-black-Bhah7RxE.js} +0 -0
  588. /package/dist/web/assets/{vitesse-dark-BuSr6OT5.js → vitesse-dark-CGK6XVwc.js} +0 -0
  589. /package/dist/web/assets/{vitesse-light-M-eYyOoe.js → vitesse-light-ne5kfw8h.js} +0 -0
  590. /package/dist/web/assets/{vyper-CwSN64Jn.js → vyper-ErDFqVoP.js} +0 -0
  591. /package/dist/web/assets/{wasm-CVlf9Jwb.js → wasm-Bd258nS1.js} +0 -0
  592. /package/dist/web/assets/{wasm-BhQEkzil.js → wasm-DtWvUWGr.js} +0 -0
  593. /package/dist/web/assets/{wenyan-BC7irgM3.js → wenyan-EZe1IEYT.js} +0 -0
  594. /package/dist/web/assets/{wgsl-W-3UHMDO.js → wgsl-B1o5_I_9.js} +0 -0
  595. /package/dist/web/assets/{wikitext-T1DoFcm8.js → wikitext-C_rwp8An.js} +0 -0
  596. /package/dist/web/assets/{wit-DIrx_eCI.js → wit-gbYtF3rY.js} +0 -0
  597. /package/dist/web/assets/{wolfram-Cza4b443.js → wolfram-DrcQFe2n.js} +0 -0
  598. /package/dist/web/assets/{yaml-DOZ10aZe.js → yaml-CzDAObLX.js} +0 -0
  599. /package/dist/web/assets/{zenscript-DDAXkseY.js → zenscript-B-J1phtg.js} +0 -0
  600. /package/dist/web/assets/{zig-CrzwMpQE.js → zig-BlzNia0O.js} +0 -0
@@ -0,0 +1,19 @@
1
+ /**
2
+ * What a single tap does on the mobile sheet: there is no double-click on touch, so a
3
+ * directory or a PPM-viewable file must open on the first tap. A file with nothing to open
4
+ * it in has no useful "open" action, so a tap on it surfaces the same actions sheet a
5
+ * long-press would — never a silent no-op.
6
+ *
7
+ * Pure so the three-way decision (directory / viewable file / opaque file) is unit-testable
8
+ * without mounting a row.
9
+ */
10
+
11
+ import { canOpenInPpm } from "../can-open-in-ppm";
12
+
13
+ export type MobileTapAction = "open" | "sheet";
14
+
15
+ /** `entry` only needs the two fields the decision actually reads. */
16
+ export function mobileTapAction(entry: { type: "file" | "directory"; name: string }): MobileTapAction {
17
+ if (entry.type === "directory") return "open";
18
+ return canOpenInPpm(entry.name) ? "open" : "sheet";
19
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Shared tap/select-mode decision for every row-level component (List, Icons — Column view
3
+ * keeps its own simpler single-select tap, see `column-view-column.tsx`), so List and Icons
4
+ * branch touch behavior identically instead of two near-copies of the same decision.
5
+ *
6
+ * Must be called from the component actually rendered *inside* the row's own
7
+ * `<ContextMenu>` (i.e. the JSX returned inside `<ContextMenuTrigger>`, not the component
8
+ * that renders `<ContextMenu>` itself) — that is the only position where
9
+ * `useContext(BottomSheetCtx)` resolves to that row's own sheet state instead of the
10
+ * default no-op value.
11
+ */
12
+
13
+ import { useContext } from "react";
14
+ import { BottomSheetCtx } from "@/components/ui/mobile-bottom-sheet";
15
+ import { useIsMobile } from "@/hooks/use-is-mobile";
16
+ import type { FsEntry } from "@/lib/fs-api";
17
+ import type { ExplorerActions } from "../actions/use-explorer-actions";
18
+ import type { EntrySelection } from "../views/use-entry-selection";
19
+ import { useMobileExplorerOpenState } from "../use-explorer-open-state";
20
+ import { mobileTapAction } from "./mobile-tap-action";
21
+
22
+ export interface MobileRowTap {
23
+ /** True only inside the mobile sheet — desktop rows keep their existing click behavior. */
24
+ isMobile: boolean;
25
+ selectMode: boolean;
26
+ /** Call from the row's onClick; returns true once it has handled the tap (mobile branch). */
27
+ handleTap(entry: FsEntry, selected: boolean): boolean;
28
+ }
29
+
30
+ export function useMobileRowTap(actions: ExplorerActions, selection: EntrySelection): MobileRowTap {
31
+ const isMobile = useIsMobile();
32
+ const selectMode = useMobileExplorerOpenState((s) => s.selectMode);
33
+ const { setOpen } = useContext(BottomSheetCtx);
34
+
35
+ const handleTap = (entry: FsEntry, selected: boolean): boolean => {
36
+ if (!isMobile) return false;
37
+ if (selectMode) {
38
+ // Toggle membership like a ctrl-click — a plain tap in Select mode never replaces
39
+ // the whole selection with just this one row.
40
+ selection.onRowClick(entry.path, { shiftKey: false, ctrlKey: true, metaKey: false });
41
+ return true;
42
+ }
43
+ if (!selected) selection.selectOnly(entry.path);
44
+ if (mobileTapAction(entry) === "open") actions.openEntry(entry);
45
+ else setOpen(true); // No viewer for this file — surface the same sheet long-press would.
46
+ return true;
47
+ };
48
+
49
+ return { isMobile, selectMode, handleTap };
50
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * The one way anything outside this folder opens a file-explorer window.
3
+ *
4
+ * Callers (command palette, nav rail, project-tree context menu) should not have to know
5
+ * about window kinds or payload keys, and passing no path should land somewhere sensible
6
+ * rather than at a hardcoded "/" that does not exist on Windows.
7
+ */
8
+
9
+ import { useWindowStore } from "@/components/floating-window/window-store";
10
+ import { isMobileDevice } from "@/hooks/use-is-mobile";
11
+ import { cachedHomedir, getHostInfo } from "./use-host-info";
12
+ import { MOBILE_EXPLORER_WINDOW_ID, openMobileExplorer } from "./use-explorer-open-state";
13
+
14
+ /**
15
+ * Open an explorer at `path`, or at the host's home directory when omitted — a floating
16
+ * window on desktop, the full-screen sheet on mobile. Returns the window id (desktop),
17
+ * `MOBILE_EXPLORER_WINDOW_ID` (mobile), or null when the home lookup failed and no path
18
+ * was given.
19
+ */
20
+ export async function openExplorer(path?: string): Promise<string | null> {
21
+ if (isMobileDevice()) {
22
+ await openMobileExplorer(path);
23
+ return MOBILE_EXPLORER_WINDOW_ID;
24
+ }
25
+
26
+ let target = path ?? cachedHomedir();
27
+ if (!target) {
28
+ try {
29
+ target = (await getHostInfo()).homedir;
30
+ } catch {
31
+ return null;
32
+ }
33
+ }
34
+ return useWindowStore.getState().open("explorer", { path: target });
35
+ }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Properties for one entry, read fresh from `/api/fs/stat` rather than from the listing:
3
+ * the listing has no permissions, no symlink target and no child count, and it may be
4
+ * seconds old.
5
+ */
6
+
7
+ import { useEffect, useState } from "react";
8
+ import { Loader2 } from "lucide-react";
9
+ import { fsApi, type FsEntry, type FsStatResult } from "@/lib/fs-api";
10
+ import { Button } from "@/components/ui/button";
11
+ import { ExplorerModalShell } from "./explorer-modal-shell";
12
+ import { dirnameOf, formatDateTime, formatMode, formatSize } from "./format-file-meta";
13
+
14
+ const KIND_LABEL: Record<FsStatResult["kind"], string> = {
15
+ file: "File",
16
+ directory: "Folder",
17
+ symlink: "Symbolic link",
18
+ unknown: "Unknown",
19
+ };
20
+
21
+ function Row({ label, value }: { label: string; value: string }) {
22
+ if (!value) return null;
23
+ return (
24
+ <div className="grid grid-cols-[7rem_1fr] gap-2 py-1 text-sm">
25
+ <span className="text-text-2">{label}</span>
26
+ <span className="break-all text-text">{value}</span>
27
+ </div>
28
+ );
29
+ }
30
+
31
+ export interface PropertiesDialogProps {
32
+ entry: FsEntry;
33
+ platform: string | undefined;
34
+ sep: string;
35
+ onClose(): void;
36
+ }
37
+
38
+ export function PropertiesDialog({ entry, platform, sep, onClose }: PropertiesDialogProps) {
39
+ const [stat, setStat] = useState<FsStatResult | null>(null);
40
+ const [error, setError] = useState<string | null>(null);
41
+
42
+ useEffect(() => {
43
+ let alive = true;
44
+ const controller = new AbortController();
45
+ setStat(null);
46
+ setError(null);
47
+ fsApi
48
+ .stat(entry.path, controller.signal)
49
+ .then((result) => { if (alive) setStat(result); })
50
+ .catch((e: unknown) => {
51
+ if (alive) setError(e instanceof Error ? e.message : "Could not read properties");
52
+ });
53
+ return () => { alive = false; controller.abort(); };
54
+ }, [entry.path]);
55
+
56
+ const sizeLabel = stat
57
+ ? stat.kind === "directory"
58
+ ? `${stat.childCount ?? 0}${stat.truncated ? "+" : ""} item${stat.childCount === 1 ? "" : "s"}`
59
+ : `${formatSize(stat.size)} (${stat.size.toLocaleString()} bytes)`
60
+ : "";
61
+
62
+ // Windows does not encode write permission in the mode bits, so the read-only flag is
63
+ // the meaningful answer there and the octal is the meaningful one everywhere else.
64
+ const permissions = stat
65
+ ? platform === "win32"
66
+ ? (stat.readonly ? "Read-only" : "Read / write")
67
+ : `${formatMode(stat.mode)}${stat.readonly ? " (read-only)" : ""}`
68
+ : "";
69
+
70
+ return (
71
+ <ExplorerModalShell
72
+ open
73
+ onClose={onClose}
74
+ title={entry.name}
75
+ description="Properties"
76
+ footer={<Button variant="outline" onClick={onClose}>Close</Button>}
77
+ >
78
+ {error && <p className="py-2 text-sm text-error">{error}</p>}
79
+ {!stat && !error && (
80
+ <div className="flex items-center gap-2 py-4 text-sm text-text-2">
81
+ <Loader2 className="size-4 animate-spin" /> Reading…
82
+ </div>
83
+ )}
84
+ {stat && (
85
+ <div className="divide-y divide-border">
86
+ <Row label="Name" value={stat.name} />
87
+ <Row label="Kind" value={KIND_LABEL[stat.kind]} />
88
+ <Row label="Location" value={dirnameOf(stat.path, sep)} />
89
+ <Row label={stat.kind === "directory" ? "Contains" : "Size"} value={sizeLabel} />
90
+ <Row label="Created" value={formatDateTime(stat.birthtime)} />
91
+ <Row label="Modified" value={formatDateTime(stat.mtime)} />
92
+ <Row label="Permissions" value={permissions} />
93
+ <Row label="Hidden" value={stat.isHidden ? "Yes" : ""} />
94
+ <Row label="Links to" value={stat.target ?? ""} />
95
+ </div>
96
+ )}
97
+ </ExplorerModalShell>
98
+ );
99
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * The one thing `window-content-registry.ts` needs to import to give explorer windows a
3
+ * skinned titlebar: a plain (non-lazy) component matching `WindowChromeProps` that picks
4
+ * the current skin and delegates to its real chrome. `FloatingWindow` renders `chrome`
5
+ * directly with no `<Suspense>` around it, so this entry point cannot be `lazy()` the way
6
+ * the window body is — it has to resolve synchronously, which is exactly what a small
7
+ * wrapper component (rather than the whole explorer body) affords.
8
+ */
9
+
10
+ import type { WindowChromeProps } from "@/components/floating-window/window-chrome-contract";
11
+ import { useExplorerSkin } from "./use-explorer-skin";
12
+
13
+ export function ExplorerSkinChrome(props: WindowChromeProps) {
14
+ const skin = useExplorerSkin();
15
+ const Chrome = skin.chrome;
16
+ return <Chrome {...props} />;
17
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Hand-drawn Finder-flavoured folder glyphs (closed/open) — an original blue-gradient
3
+ * rounded folder, not a copy of Apple's proprietary folder artwork.
4
+ *
5
+ * Gradient ids are per-instance (`useId`) since a folder-heavy listing renders dozens of
6
+ * these at once — a hardcoded id would make every icon after the first reuse the first
7
+ * instance's `<linearGradient>` (SVG `url(#id)` resolves to the first match in the DOM).
8
+ */
9
+
10
+ import { useId, type ComponentProps } from "react";
11
+
12
+ export function MacFolderIcon(props: ComponentProps<"svg">) {
13
+ const gradientId = `mac-folder-${useId()}`;
14
+ return (
15
+ <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
16
+ <defs>
17
+ <linearGradient id={gradientId} x1="0" y1="0" x2="0" y2="1">
18
+ <stop offset="0" stopColor="#7FC1FF" />
19
+ <stop offset="1" stopColor="#3E92E5" />
20
+ </linearGradient>
21
+ </defs>
22
+ <path d="M2.5 8a2 2 0 0 1 2-2h5.2l1.7 2H19.5a2 2 0 0 1 2 2v7.5a2 2 0 0 1-2 2h-15a2 2 0 0 1-2-2V8Z" fill={`url(#${gradientId})`} />
23
+ <path d="M2.5 10.5h19V9.8H4.7A2 2 0 0 0 2.5 10.5Z" fill="#FFFFFF" fillOpacity=".3" />
24
+ </svg>
25
+ );
26
+ }
27
+
28
+ export function MacFolderOpenIcon(props: ComponentProps<"svg">) {
29
+ const gradientId = `mac-folder-open-${useId()}`;
30
+ return (
31
+ <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
32
+ <defs>
33
+ <linearGradient id={gradientId} x1="0" y1="0" x2="0" y2="1">
34
+ <stop offset="0" stopColor="#8FCBFF" />
35
+ <stop offset="1" stopColor="#2F82D9" />
36
+ </linearGradient>
37
+ </defs>
38
+ <path d="M2.5 8a2 2 0 0 1 2-2h5.2l1.7 2H19.5a2 2 0 0 1 2 2v.3H5.9a2 2 0 0 0-1.94 1.51L2.5 17.9V8Z" fill="#7FC1FF" />
39
+ <path d="M2.13 18.36 3.98 11.1A2 2 0 0 1 5.92 9.6H21l-2.16 8a2 2 0 0 1-1.93 1.48H4.06a2 2 0 0 1-1.93-2.52Z" fill={`url(#${gradientId})`} />
40
+ </svg>
41
+ );
42
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Hand-drawn Windows-11-flavoured folder glyphs (closed/open) — an original flat-yellow
3
+ * rounded-rect with a tab, not a copy of Microsoft's proprietary folder artwork.
4
+ */
5
+
6
+ import type { ComponentProps } from "react";
7
+
8
+ export function WindowsFolderIcon(props: ComponentProps<"svg">) {
9
+ return (
10
+ <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
11
+ <path d="M3 7.5a1.5 1.5 0 0 1 1.5-1.5H10l1.6 2H19.5A1.5 1.5 0 0 1 21 9.5v8A1.5 1.5 0 0 1 19.5 19h-15A1.5 1.5 0 0 1 3 17.5v-10Z" fill="#FFC93E" />
12
+ <path d="M3 9.5h18v8A1.5 1.5 0 0 1 19.5 19h-15A1.5 1.5 0 0 1 3 17.5v-8Z" fill="#FFCE49" />
13
+ <path d="M4 9.2h16" stroke="#FFFFFF" strokeOpacity=".35" strokeWidth="1" />
14
+ </svg>
15
+ );
16
+ }
17
+
18
+ export function WindowsFolderOpenIcon(props: ComponentProps<"svg">) {
19
+ return (
20
+ <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
21
+ <path d="M3 7.5a1.5 1.5 0 0 1 1.5-1.5H10l1.6 2H19.5A1.5 1.5 0 0 1 21 9.5v.5H5.2a1.5 1.5 0 0 0-1.45 1.13L2 18.2V7.5Z" fill="#FFC93E" />
22
+ <path d="M2.62 18.66 4.3 12.1A1.5 1.5 0 0 1 5.75 11H21l-1.86 6.63A1.5 1.5 0 0 1 17.7 18.7H4.06a1.5 1.5 0 0 1-1.44-1.9Z" fill="#FFB92E" />
23
+ </svg>
24
+ );
25
+ }
@@ -0,0 +1,14 @@
1
+ import { MacFolderIcon, MacFolderOpenIcon } from "./folder-icon-macos";
2
+ import { MacosWindowChrome } from "./macos-window-chrome";
3
+ import type { ExplorerSkin } from "./skin-types";
4
+
5
+ export const macosSkin: ExplorerSkin = {
6
+ id: "macos",
7
+ // Finder's sidebar shows one "Locations" group in principle, but this sidebar renders
8
+ // known folders and drives as two separate sections — reusing the same title would look
9
+ // like a duplicated header, so drives get their own distinct label.
10
+ vocab: { home: "Home", pinned: "Favorites", known: "Locations", drives: "Devices" },
11
+ FolderIcon: MacFolderIcon,
12
+ FolderOpenIcon: MacFolderOpenIcon,
13
+ chrome: MacosWindowChrome,
14
+ };
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Finder-flavoured unified titlebar: traffic lights left (grey when unfocused, hover
3
+ * reveals the glyph), title centred. Height matches the shared `TITLEBAR_HEIGHT` contract
4
+ * constant (not the real Finder 38px) so a minimised window's collapsed height agrees with
5
+ * what `FloatingWindow` reserves for it. The toolbar row (back/forward/view-switch/search)
6
+ * is the existing `ExplorerToolbar` in the window body, tinted by `skins.css` — the chrome
7
+ * slot is only the titlebar itself.
8
+ *
9
+ * `data-skin` is set here (not only on the explorer body) because this titlebar is a
10
+ * sibling of the body in the window tree — the `--x-*` vars it reads only resolve on an
11
+ * element that itself carries the attribute.
12
+ */
13
+
14
+ import { Minus, Plus, X } from "lucide-react";
15
+ import { cn } from "@/lib/utils";
16
+ import { TITLEBAR_HEIGHT, type WindowChromeProps } from "@/components/floating-window/window-chrome-contract";
17
+
18
+ /** The traffic-light colours, plus the unfocused grey — the documented hardcoded hexes. */
19
+ const LIGHTS = [
20
+ { color: "#FF5F57", label: "Close window", Icon: X },
21
+ { color: "#FEBC2E", label: "Minimize window", Icon: Minus },
22
+ { color: "#28C840", label: "Maximize window", Icon: Plus },
23
+ ] as const;
24
+
25
+ export function MacosWindowChrome({
26
+ title, focused, titlebarProps, onMinimize, onToggleMaximize, onClose,
27
+ }: WindowChromeProps) {
28
+ const { className, style, ...rest } = titlebarProps;
29
+ const actions = [onClose, onMinimize, onToggleMaximize];
30
+ return (
31
+ <div
32
+ {...rest}
33
+ data-skin="macos"
34
+ style={{ height: TITLEBAR_HEIGHT, fontFamily: "var(--x-font)", ...style }}
35
+ className={cn(
36
+ "group/titlebar relative flex items-center shrink-0 rounded-t-[var(--x-radius)] overflow-hidden",
37
+ "bg-[var(--x-titlebar-bg)] border-b border-border",
38
+ "outline-none focus-visible:ring-1 focus-visible:ring-primary focus-visible:ring-inset",
39
+ className,
40
+ )}
41
+ >
42
+ <div className="flex items-center gap-2 pl-3">
43
+ {LIGHTS.map(({ color, label, Icon }, i) => (
44
+ <button
45
+ key={label}
46
+ type="button"
47
+ aria-label={label}
48
+ onClick={actions[i]}
49
+ className="grid size-3 place-items-center rounded-full"
50
+ style={{ backgroundColor: focused ? color : "#8E8E93" }}
51
+ >
52
+ <Icon
53
+ className="size-2 text-black/60 can-hover:opacity-0 can-hover:group-hover/titlebar:opacity-100"
54
+ strokeWidth={3}
55
+ />
56
+ </button>
57
+ ))}
58
+ </div>
59
+ <span
60
+ className={cn(
61
+ "pointer-events-none absolute inset-x-0 truncate text-center text-[13px]",
62
+ focused ? "text-text" : "text-text-2",
63
+ )}
64
+ >
65
+ {title}
66
+ </span>
67
+ </div>
68
+ );
69
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * A skin is everything that makes the explorer window look native to a host OS: the
3
+ * titlebar/toolbar chrome, the sidebar section names, and the folder glyph. Views, the
4
+ * toolbar body and the sidebar rows themselves stay skin-agnostic — they read `vocab` and
5
+ * the folder icon slot, nothing else changes.
6
+ *
7
+ * Colours are deliberately not part of this type: skin-specific CSS lives in `skins.css`
8
+ * scoped under `[data-skin]`, reusing PPM's existing semantic tokens (`bg-panel`,
9
+ * `text-text`, …) so both PPM dark and light modes fall out for free instead of needing a
10
+ * second light/dark table here.
11
+ */
12
+
13
+ import type { ComponentProps, FC } from "react";
14
+ import type { WindowChrome } from "@/components/floating-window/window-chrome-contract";
15
+
16
+ export type SkinId = "windows" | "macos";
17
+
18
+ /** Sidebar section labels that differ between a Windows and a Mac/Linux file manager. */
19
+ export interface SkinVocab {
20
+ /** "Home" on both, kept explicit in case a future skin wants to drop it. */
21
+ home: string;
22
+ /** "Quick access" (Windows) vs "Favorites" (macOS). */
23
+ pinned: string;
24
+ /** "Folders" (Windows) vs "Locations" (macOS) — the known-folders section. */
25
+ known: string;
26
+ /** "Drives" (Windows) vs "Devices" (macOS) — rendered as its own section, so it needs a
27
+ * label distinct from `known` even though Finder visually groups both under Locations. */
28
+ drives: string;
29
+ }
30
+
31
+ export type FolderIconComponent = FC<ComponentProps<"svg">>;
32
+
33
+ export interface ExplorerSkin {
34
+ id: SkinId;
35
+ vocab: SkinVocab;
36
+ FolderIcon: FolderIconComponent;
37
+ FolderOpenIcon: FolderIconComponent;
38
+ /** Window titlebar/toolbar chrome for this skin. */
39
+ chrome: WindowChrome;
40
+ }
@@ -0,0 +1,25 @@
1
+ /*
2
+ * OS Explorer skin tokens, scoped to `[data-skin]` set on the explorer root
3
+ * (`ExplorerBody`). Colours stay derived from PPM's existing semantic tokens
4
+ * (`--panel-2`, `--border`, `--accent-2`, …) via `color-mix()` rather than a second
5
+ * light/dark table, so both PPM themes stay legible without duplicating that logic here.
6
+ * Layout metrics (radius, font stack) are the only skin-owned constants.
7
+ */
8
+
9
+ [data-skin="windows"] {
10
+ --x-radius: 8px;
11
+ --x-font: "Segoe UI Variable Text", "Segoe UI", system-ui, -apple-system, sans-serif;
12
+ --x-titlebar-bg: var(--panel-2);
13
+ --x-toolbar-bg: var(--panel-2);
14
+ /* A flat "Mica-ish" wash: a touch lighter than the base panel, same on any theme. */
15
+ --x-sidebar-bg: color-mix(in srgb, var(--panel-2) 90%, white 10%);
16
+ }
17
+
18
+ [data-skin="macos"] {
19
+ --x-radius: 10px;
20
+ --x-font: -apple-system, "SF Pro Text", "SF Pro", system-ui, sans-serif;
21
+ --x-titlebar-bg: var(--panel-2);
22
+ --x-toolbar-bg: var(--panel-2);
23
+ /* Finder's sidebar reads a hair cooler than its content pane. */
24
+ --x-sidebar-bg: color-mix(in srgb, var(--panel-2) 85%, var(--accent-2) 6%);
25
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Resolves which skin an explorer window should wear: the user's Settings override when
3
+ * set, otherwise the host `platform` — Linux gets the macOS look since there is no single
4
+ * dominant Linux file-manager chrome to imitate (same call the plan made for pinned
5
+ * folders and known folders).
6
+ */
7
+
8
+ import { useSettingsStore } from "@/stores/settings-store";
9
+ import { useHostInfo } from "../use-host-info";
10
+ import { macosSkin } from "./macos-skin";
11
+ import type { ExplorerSkin } from "./skin-types";
12
+ import { windowsSkin } from "./windows-skin";
13
+
14
+ export function useExplorerSkin(): ExplorerSkin {
15
+ const override = useSettingsStore((s) => s.explorerSkin);
16
+ const { host } = useHostInfo();
17
+
18
+ if (override === "windows") return windowsSkin;
19
+ if (override === "macos") return macosSkin;
20
+ return host?.platform === "win32" ? windowsSkin : macosSkin;
21
+ }
@@ -0,0 +1,11 @@
1
+ import { WindowsFolderIcon, WindowsFolderOpenIcon } from "./folder-icon-windows";
2
+ import { WindowsWindowChrome } from "./windows-window-chrome";
3
+ import type { ExplorerSkin } from "./skin-types";
4
+
5
+ export const windowsSkin: ExplorerSkin = {
6
+ id: "windows",
7
+ vocab: { home: "Home", pinned: "Quick access", known: "Folders", drives: "Drives" },
8
+ FolderIcon: WindowsFolderIcon,
9
+ FolderOpenIcon: WindowsFolderOpenIcon,
10
+ chrome: WindowsWindowChrome,
11
+ };
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Windows 11 File Explorer-flavoured titlebar: window icon + title left, caption buttons
3
+ * right (46px wide, close hover red). Height matches the shared `TITLEBAR_HEIGHT` contract
4
+ * constant (not the real Win11 32px) so a minimised window's collapsed height agrees with
5
+ * what `FloatingWindow` reserves for it. The toolbar row (back/forward/breadcrumb/search) is
6
+ * the existing `ExplorerToolbar` in the window body, tinted by `skins.css` — the chrome slot
7
+ * is only the titlebar itself.
8
+ *
9
+ * `data-skin` is set here (not only on the explorer body) because this titlebar is a
10
+ * sibling of the body in the window tree — the `--x-*` vars it reads (`--x-titlebar-bg`,
11
+ * `--x-radius`, `--x-font`) only resolve on an element that itself carries the attribute.
12
+ */
13
+
14
+ import { Minus, Square, Copy, X } from "lucide-react";
15
+ import { cn } from "@/lib/utils";
16
+ import { TITLEBAR_HEIGHT, type WindowChromeProps } from "@/components/floating-window/window-chrome-contract";
17
+ import { WindowsFolderIcon } from "./folder-icon-windows";
18
+
19
+ /** Windows metric: 46px wide caption buttons, full titlebar height. */
20
+ const CAPTION_BUTTON =
21
+ "grid place-items-center w-[46px] self-stretch text-text-2 can-hover:hover:bg-surface-elevated can-hover:hover:text-text transition-colors";
22
+ /** The one documented hardcoded hex: Windows' close-button hover red. */
23
+ const CLOSE_BUTTON = cn(CAPTION_BUTTON, "can-hover:hover:!bg-[#c42b1c] can-hover:hover:!text-white");
24
+
25
+ export function WindowsWindowChrome({
26
+ title, state, focused, titlebarProps, onMinimize, onToggleMaximize, onClose,
27
+ }: WindowChromeProps) {
28
+ const { className, style, ...rest } = titlebarProps;
29
+ return (
30
+ <div
31
+ {...rest}
32
+ data-skin="windows"
33
+ style={{ height: TITLEBAR_HEIGHT, fontFamily: "var(--x-font)", ...style }}
34
+ className={cn(
35
+ "flex items-stretch shrink-0 rounded-t-[var(--x-radius)] overflow-hidden",
36
+ "bg-[var(--x-titlebar-bg)] border-b border-border",
37
+ "outline-none focus-visible:ring-1 focus-visible:ring-primary focus-visible:ring-inset",
38
+ className,
39
+ )}
40
+ >
41
+ <div className="flex flex-1 min-w-0 items-center gap-2 px-2.5 cursor-default">
42
+ <WindowsFolderIcon className="size-4 shrink-0" />
43
+ <span className={cn("truncate text-[12px]", focused ? "text-text" : "text-text-2")}>{title}</span>
44
+ </div>
45
+ <button type="button" aria-label="Minimize window" className={CAPTION_BUTTON} onClick={onMinimize}>
46
+ <Minus className="size-3.5" />
47
+ </button>
48
+ <button type="button" aria-label={state === "maximized" ? "Restore window" : "Maximize window"} className={CAPTION_BUTTON} onClick={onToggleMaximize}>
49
+ {state === "maximized" ? <Copy className="size-3" /> : <Square className="size-3" />}
50
+ </button>
51
+ <button type="button" aria-label="Close window" className={CLOSE_BUTTON} onClick={onClose}>
52
+ <X className="size-3.5" />
53
+ </button>
54
+ </div>
55
+ );
56
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * The visible row order: the filter text applied first, then the sort.
3
+ *
4
+ * Directories always come before files regardless of the sort column, which is what every
5
+ * desktop file manager does and what keyboard navigation assumes.
6
+ */
7
+
8
+ import type { FsEntry } from "@/lib/fs-api";
9
+ import { extensionOf } from "./can-open-in-ppm";
10
+ import type { SortDir, SortKey } from "./explorer-store";
11
+
12
+ /** Locale-aware, digit-aware name compare so "file10" sorts after "file9". */
13
+ const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: "base" });
14
+
15
+ function compareBy(key: SortKey, a: FsEntry, b: FsEntry): number {
16
+ switch (key) {
17
+ case "size":
18
+ return (a.size ?? 0) - (b.size ?? 0);
19
+ case "modified":
20
+ return new Date(a.modified).getTime() - new Date(b.modified).getTime();
21
+ case "kind": {
22
+ const byExt = collator.compare(extensionOf(a.name), extensionOf(b.name));
23
+ return byExt !== 0 ? byExt : collator.compare(a.name, b.name);
24
+ }
25
+ case "name":
26
+ return collator.compare(a.name, b.name);
27
+ }
28
+ }
29
+
30
+ export function sortAndFilterEntries(
31
+ entries: FsEntry[],
32
+ filter: string,
33
+ sort: { key: SortKey; dir: SortDir },
34
+ ): FsEntry[] {
35
+ const needle = filter.trim().toLowerCase();
36
+ const visible = needle ? entries.filter((e) => e.name.toLowerCase().includes(needle)) : entries;
37
+ const sign = sort.dir === "asc" ? 1 : -1;
38
+
39
+ return [...visible].sort((a, b) => {
40
+ const aDir = a.type === "directory";
41
+ const bDir = b.type === "directory";
42
+ if (aDir !== bDir) return aDir ? -1 : 1;
43
+ const result = compareBy(sort.key, a, b);
44
+ // Equal keys fall back to the name so the order never depends on readdir sequence.
45
+ return (result !== 0 ? result : collator.compare(a.name, b.name)) * sign;
46
+ });
47
+ }
48
+
49
+ /** Total bytes of the given entries; directories contribute nothing (size unknown). */
50
+ export function totalSize(entries: FsEntry[]): number {
51
+ return entries.reduce((sum, e) => sum + (e.type === "file" ? (e.size ?? 0) : 0), 0);
52
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Shared sort vocabulary: the four sortable keys and the two directions, with the labels
3
+ * shown to the user. The toolbar sort menu, the background context menu's "Sort by"
4
+ * submenu, the List view's column headers and the mobile More sheet all read from here so
5
+ * the four surfaces can never say something different about what "Kind" or "Descending"
6
+ * means.
7
+ */
8
+
9
+ import type { SortDir, SortKey } from "./explorer-store";
10
+
11
+ export const SORT_KEY_OPTIONS: readonly { key: SortKey; label: string }[] = [
12
+ { key: "name", label: "Name" },
13
+ { key: "size", label: "Size" },
14
+ { key: "modified", label: "Modified" },
15
+ { key: "kind", label: "Kind" },
16
+ ];
17
+
18
+ export const SORT_DIR_OPTIONS: readonly { dir: SortDir; label: string }[] = [
19
+ { dir: "asc", label: "Ascending" },
20
+ { dir: "desc", label: "Descending" },
21
+ ];
22
+
23
+ export function sortKeyLabel(key: SortKey): string {
24
+ return SORT_KEY_OPTIONS.find((option) => option.key === key)?.label ?? key;
25
+ }
26
+
27
+ export function sortDirLabel(dir: SortDir): string {
28
+ return SORT_DIR_OPTIONS.find((option) => option.dir === dir)?.label ?? dir;
29
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Shared square icon-button styling for the window toolbar and any trigger that sits
3
+ * alongside it (e.g. the sort menu button) — one class builder keeps their hit-box
4
+ * behaviour identical instead of two toolbars drifting apart.
5
+ *
6
+ * The visible box stays `size-7` (28px) on every pointer type — only the tap-registering
7
+ * area grows to the 44px minimum on a coarse pointer, via an invisible expanded `::before`
8
+ * (a click anywhere in it still hits this element; nothing about the button's look moves).
9
+ */
10
+
11
+ import { cn } from "@/lib/utils";
12
+
13
+ export function toolbarButtonClass(coarse: boolean): string {
14
+ return cn(
15
+ "relative flex size-7 shrink-0 items-center justify-center rounded text-text-subtle can-hover:hover:bg-surface-elevated can-hover:hover:text-text disabled:opacity-30",
16
+ coarse && "before:absolute before:-inset-2 before:content-['']",
17
+ );
18
+ }