@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,97 @@
1
+ /**
2
+ * Long-press → context menu on touch screens that are wide enough to get the desktop
3
+ * layout (an iPad is ≥ 768 px, so `useIsMobile` is false and the bottom-sheet path never
4
+ * runs). Radix opens its own menu on long-press but only after 700 ms; the design rules
5
+ * ask for 400 ms, so the press is timed here and delivered as a synthetic `contextmenu`
6
+ * event, which is exactly what the trigger already listens for.
7
+ *
8
+ * Attached only when the primary pointer is coarse — a mouse keeps plain right-click.
9
+ */
10
+
11
+ import { useCallback, useEffect, useRef, useState, type MouseEvent, type TouchEvent } from "react";
12
+
13
+ const LONG_PRESS_MS = 400;
14
+ /** Finger travel that turns a press into a scroll instead. */
15
+ const MOVE_TOLERANCE_PX = 10;
16
+
17
+ export function usePrefersCoarsePointer(): boolean {
18
+ const [coarse, setCoarse] = useState(
19
+ () => typeof window !== "undefined" && window.matchMedia?.("(pointer: coarse)").matches === true,
20
+ );
21
+ useEffect(() => {
22
+ const query = window.matchMedia?.("(pointer: coarse)");
23
+ if (!query) return;
24
+ const update = () => setCoarse(query.matches);
25
+ query.addEventListener("change", update);
26
+ return () => query.removeEventListener("change", update);
27
+ }, []);
28
+ return coarse;
29
+ }
30
+
31
+ export interface LongPressHandlers {
32
+ onTouchStart?(event: TouchEvent): void;
33
+ onTouchMove?(event: TouchEvent): void;
34
+ onTouchEnd?(event: TouchEvent): void;
35
+ onContextMenu?(event: MouseEvent): void;
36
+ }
37
+
38
+ /**
39
+ * Handlers to spread on a row. `onBeforeOpen` runs first so the menu acts on the pressed
40
+ * row rather than whatever was selected before.
41
+ */
42
+ export function useCoarseLongPress(onBeforeOpen?: () => void): LongPressHandlers {
43
+ const coarse = usePrefersCoarsePointer();
44
+ const timer = useRef<ReturnType<typeof setTimeout> | null>(null);
45
+ const origin = useRef<{ x: number; y: number } | null>(null);
46
+ const fired = useRef(false);
47
+
48
+ const cancel = useCallback(() => {
49
+ if (timer.current) clearTimeout(timer.current);
50
+ timer.current = null;
51
+ }, []);
52
+
53
+ useEffect(() => cancel, [cancel]);
54
+
55
+ const start = useCallback(
56
+ (event: TouchEvent) => {
57
+ const touch = event.touches[0];
58
+ if (!touch) return;
59
+ const target = event.currentTarget as HTMLElement;
60
+ origin.current = { x: touch.clientX, y: touch.clientY };
61
+ fired.current = false;
62
+ cancel();
63
+ timer.current = setTimeout(() => {
64
+ fired.current = true;
65
+ onBeforeOpen?.();
66
+ target.dispatchEvent(
67
+ new MouseEvent("contextmenu", {
68
+ bubbles: true,
69
+ cancelable: true,
70
+ clientX: touch.clientX,
71
+ clientY: touch.clientY,
72
+ }),
73
+ );
74
+ }, LONG_PRESS_MS);
75
+ },
76
+ [cancel, onBeforeOpen],
77
+ );
78
+
79
+ const move = useCallback(
80
+ (event: TouchEvent) => {
81
+ const touch = event.touches[0];
82
+ if (!touch || !origin.current) return;
83
+ const moved =
84
+ Math.abs(touch.clientX - origin.current.x) > MOVE_TOLERANCE_PX ||
85
+ Math.abs(touch.clientY - origin.current.y) > MOVE_TOLERANCE_PX;
86
+ if (moved) cancel();
87
+ },
88
+ [cancel],
89
+ );
90
+
91
+ if (!coarse) return {};
92
+ return {
93
+ onTouchStart: start,
94
+ onTouchMove: move,
95
+ onTouchEnd: cancel,
96
+ };
97
+ }
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Keyboard behaviour for an explorer view container, shared by every view mode.
3
+ *
4
+ * Bound to the scrolling element rather than the window so two open explorers never both
5
+ * react to the same key, and so PPM's global shortcuts keep working when focus is
6
+ * elsewhere.
7
+ */
8
+
9
+ import { useCallback } from "react";
10
+ import type { KeyboardEvent } from "react";
11
+ import type { FsEntry } from "@/lib/fs-api";
12
+ import type { ExplorerActions } from "./actions/use-explorer-actions";
13
+ import type { ExplorerSlice } from "./explorer-store";
14
+ import type { ExplorerNavigation } from "./use-explorer-navigation";
15
+ import type { EntrySelection } from "./views/use-entry-selection";
16
+
17
+ export interface ExplorerKeyboardOptions {
18
+ /** Undefined for the one frame before the window has a slice. */
19
+ slice: ExplorerSlice | undefined;
20
+ /** Rows in visible order. */
21
+ entries: FsEntry[];
22
+ actions: ExplorerActions;
23
+ selection: EntrySelection;
24
+ nav: ExplorerNavigation;
25
+ }
26
+
27
+ /** Entries the keyboard acts on: the selection, or the cursor row when nothing is selected. */
28
+ export function activeEntries(slice: ExplorerSlice, entries: FsEntry[]): FsEntry[] {
29
+ if (slice.selection.size > 0) return entries.filter((e) => slice.selection.has(e.path));
30
+ const cursor = entries.find((e) => e.path === slice.anchor);
31
+ return cursor ? [cursor] : [];
32
+ }
33
+
34
+ export function useExplorerKeyboard({
35
+ slice, entries, actions, selection, nav,
36
+ }: ExplorerKeyboardOptions): (event: KeyboardEvent) => void {
37
+ return useCallback(
38
+ (event: KeyboardEvent) => {
39
+ if (!slice) return;
40
+ // The inline rename field and the filter box own their own keys.
41
+ const target = event.target as HTMLElement;
42
+ if (target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable) return;
43
+
44
+ const mod = event.ctrlKey || event.metaKey;
45
+ const targets = activeEntries(slice, entries);
46
+
47
+ if (mod) {
48
+ switch (event.key.toLowerCase()) {
49
+ case "a":
50
+ event.preventDefault();
51
+ selection.selectAll();
52
+ return;
53
+ case "c":
54
+ event.preventDefault();
55
+ actions.copy(targets);
56
+ return;
57
+ case "x":
58
+ event.preventDefault();
59
+ actions.cut(targets);
60
+ return;
61
+ case "v":
62
+ event.preventDefault();
63
+ actions.paste();
64
+ return;
65
+ default:
66
+ return; // leave every other accelerator to the app
67
+ }
68
+ }
69
+
70
+ switch (event.key) {
71
+ case "ArrowDown":
72
+ event.preventDefault();
73
+ selection.moveTo(1, event.shiftKey);
74
+ return;
75
+ case "ArrowUp":
76
+ event.preventDefault();
77
+ selection.moveTo(-1, event.shiftKey);
78
+ return;
79
+ case "Home":
80
+ event.preventDefault();
81
+ if (entries[0]) selection.selectOnly(entries[0].path);
82
+ return;
83
+ case "End":
84
+ event.preventDefault();
85
+ if (entries.length > 0) selection.selectOnly(entries[entries.length - 1]!.path);
86
+ return;
87
+ case "ArrowLeft":
88
+ if (event.altKey) { event.preventDefault(); nav.back(); }
89
+ return;
90
+ case "ArrowRight":
91
+ if (event.altKey) { event.preventDefault(); nav.forward(); }
92
+ return;
93
+ case "Backspace":
94
+ event.preventDefault();
95
+ nav.back();
96
+ return;
97
+ case "Enter":
98
+ event.preventDefault();
99
+ if (targets.length === 1) actions.openEntry(targets[0]!);
100
+ return;
101
+ case "F2":
102
+ event.preventDefault();
103
+ if (targets.length === 1) actions.startRename(targets[0]!);
104
+ return;
105
+ case "Delete":
106
+ event.preventDefault();
107
+ if (targets.length === 0) return;
108
+ if (event.shiftKey) actions.confirmPermanentDelete(targets);
109
+ else actions.trash(targets);
110
+ return;
111
+ case "Escape":
112
+ selection.clear();
113
+ return;
114
+ }
115
+
116
+ // Type-ahead: a single printable character with no modifier jumps to a name.
117
+ if (event.key.length === 1 && !event.altKey) {
118
+ event.preventDefault();
119
+ selection.typeAhead(event.key);
120
+ }
121
+ },
122
+ [slice, entries, actions, selection, nav],
123
+ );
124
+ }
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Directory navigation for one explorer window: fetching, history, and cross-window
3
+ * refresh.
4
+ *
5
+ * Two requests for the same window must never race: a slow `C:\Windows\System32` listing
6
+ * arriving after the user already stepped into another folder would repaint stale rows.
7
+ * Each fetch therefore aborts the previous one and re-checks its own identity before
8
+ * writing to the store.
9
+ */
10
+
11
+ import { useCallback, useEffect, useMemo, useRef } from "react";
12
+ import { fsApi, FsError } from "@/lib/fs-api";
13
+ import { useWindowStore } from "@/components/floating-window/window-store";
14
+ import { onFsChanged, useExplorerStore, type ExplorerSlice } from "./explorer-store";
15
+
16
+ export interface ExplorerNavigation {
17
+ go(path: string, opts?: { replace?: boolean }): void;
18
+ back(): void;
19
+ forward(): void;
20
+ up(): void;
21
+ refresh(): void;
22
+ canGoBack: boolean;
23
+ canGoForward: boolean;
24
+ }
25
+
26
+ export function useExplorerNavigation(
27
+ windowId: string,
28
+ slice: ExplorerSlice | undefined,
29
+ showHidden: boolean,
30
+ ): ExplorerNavigation {
31
+ const abortRef = useRef<AbortController | null>(null);
32
+ const patch = useExplorerStore((s) => s.patch);
33
+
34
+ // The load target is read from a ref so `load` keeps a stable identity: it is a
35
+ // dependency of the fsChanged subscription, which must not resubscribe per keystroke.
36
+ const pathRef = useRef(slice?.path ?? "");
37
+ pathRef.current = slice?.path ?? "";
38
+ const hiddenRef = useRef(showHidden);
39
+ hiddenRef.current = showHidden;
40
+
41
+ const load = useCallback(
42
+ async (path: string) => {
43
+ abortRef.current?.abort();
44
+ const controller = new AbortController();
45
+ abortRef.current = controller;
46
+ patch(windowId, { loading: true, error: null });
47
+ try {
48
+ const result = await fsApi.browse(path, {
49
+ showHidden: hiddenRef.current,
50
+ signal: controller.signal,
51
+ });
52
+ if (controller.signal.aborted) return;
53
+ patch(windowId, {
54
+ entries: result.entries,
55
+ breadcrumbs: result.breadcrumbs,
56
+ parent: result.parent,
57
+ sep: result.sep,
58
+ truncated: result.truncated === true,
59
+ loading: false,
60
+ error: null,
61
+ });
62
+ } catch (e) {
63
+ if (controller.signal.aborted || (e instanceof DOMException && e.name === "AbortError")) return;
64
+ patch(windowId, {
65
+ loading: false,
66
+ entries: [],
67
+ error:
68
+ e instanceof FsError
69
+ ? { message: e.message, code: e.code, hint: e.hint }
70
+ : { message: e instanceof Error ? e.message : "Failed to read directory", code: "EUNKNOWN" },
71
+ });
72
+ }
73
+ },
74
+ [windowId, patch],
75
+ );
76
+
77
+ // Fetch whenever the target directory or the hidden-files preference changes.
78
+ const path = slice?.path;
79
+ useEffect(() => {
80
+ if (!path) return;
81
+ void load(path);
82
+ return () => abortRef.current?.abort();
83
+ }, [path, showHidden, load]);
84
+
85
+ // Another window (or this one) mutated a directory we are displaying.
86
+ useEffect(() => {
87
+ return onFsChanged((dir) => {
88
+ if (dir === pathRef.current) void load(dir);
89
+ });
90
+ }, [load]);
91
+
92
+ const go = useCallback(
93
+ (target: string, opts?: { replace?: boolean }) => {
94
+ const current = useExplorerStore.getState().slices[windowId];
95
+ if (!current || target === current.path) return;
96
+ const history = opts?.replace
97
+ ? [...current.history.slice(0, current.historyIndex), target]
98
+ : [...current.history.slice(0, current.historyIndex + 1), target];
99
+ patch(windowId, {
100
+ path: target,
101
+ history: history.slice(-100),
102
+ historyIndex: Math.min(history.length, 100) - 1,
103
+ selection: new Set(),
104
+ anchor: null,
105
+ filter: "",
106
+ inlineEdit: null,
107
+ });
108
+ // Keeps the titlebar in step and makes the window reopen here after a reload.
109
+ useWindowStore.getState().setPayload(windowId, { path: target });
110
+ },
111
+ [windowId, patch],
112
+ );
113
+
114
+ const step = useCallback(
115
+ (delta: number) => {
116
+ const current = useExplorerStore.getState().slices[windowId];
117
+ if (!current) return;
118
+ const index = current.historyIndex + delta;
119
+ const target = current.history[index];
120
+ if (target == null) return;
121
+ patch(windowId, {
122
+ path: target,
123
+ historyIndex: index,
124
+ selection: new Set(),
125
+ anchor: null,
126
+ filter: "",
127
+ inlineEdit: null,
128
+ });
129
+ useWindowStore.getState().setPayload(windowId, { path: target });
130
+ },
131
+ [windowId, patch],
132
+ );
133
+
134
+ const back = useCallback(() => step(-1), [step]);
135
+ const forward = useCallback(() => step(1), [step]);
136
+
137
+ const up = useCallback(() => {
138
+ const current = useExplorerStore.getState().slices[windowId];
139
+ if (current?.parent) go(current.parent);
140
+ }, [windowId, go]);
141
+
142
+ const refresh = useCallback(() => {
143
+ if (pathRef.current) void load(pathRef.current);
144
+ }, [load]);
145
+
146
+ const canGoBack = (slice?.historyIndex ?? 0) > 0;
147
+ const canGoForward = slice ? slice.historyIndex < slice.history.length - 1 : false;
148
+
149
+ // A fresh object every render would defeat memo(ListRow) (it receives this as a prop
150
+ // several levels down) even though the callbacks themselves are already stable.
151
+ return useMemo(
152
+ () => ({ go, back, forward, up, refresh, canGoBack, canGoForward }),
153
+ [go, back, forward, up, refresh, canGoBack, canGoForward],
154
+ );
155
+ }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Global open/close state for the single mobile explorer sheet, plus its one UI-only mode
3
+ * flag ("Select").
4
+ *
5
+ * Desktop windows are many and multiplex through `useWindowStore`; the mobile sheet is
6
+ * always exactly one instance (`windowId = MOBILE_EXPLORER_WINDOW_ID`), so a tiny dedicated
7
+ * store is simpler than threading a "mobile" window kind through the desktop window model.
8
+ */
9
+
10
+ import { create } from "zustand";
11
+ import { useExplorerStore } from "./explorer-store";
12
+ import { cachedHomedir, getHostInfo } from "./use-host-info";
13
+
14
+ /** The one explorer-store slice id the mobile sheet ever uses. */
15
+ export const MOBILE_EXPLORER_WINDOW_ID = "mobile";
16
+
17
+ interface MobileExplorerOpenState {
18
+ isOpen: boolean;
19
+ /** "Select" toolbar mode: rows show checkboxes and a tap toggles selection instead of opening. */
20
+ selectMode: boolean;
21
+ close(): void;
22
+ setSelectMode(value: boolean): void;
23
+ }
24
+
25
+ export const useMobileExplorerOpenState = create<MobileExplorerOpenState>((set) => ({
26
+ isOpen: false,
27
+ selectMode: false,
28
+ close: () => set({ isOpen: false, selectMode: false }),
29
+ setSelectMode: (value) => set({ selectMode: value }),
30
+ }));
31
+
32
+ /**
33
+ * Open the mobile sheet, landing on `path`. With no path: an already-open-before instance
34
+ * resumes wherever it was (the slice survives a close, since only `isOpen` is reset), and a
35
+ * first-ever open falls back to the host home directory — same resolution order as the
36
+ * desktop facade.
37
+ */
38
+ export async function openMobileExplorer(path?: string): Promise<void> {
39
+ const store = useExplorerStore.getState();
40
+ const existing = store.slices[MOBILE_EXPLORER_WINDOW_ID];
41
+ let target = path ?? existing?.path ?? cachedHomedir();
42
+ if (!target) {
43
+ try {
44
+ target = (await getHostInfo()).homedir;
45
+ } catch {
46
+ return; // No usable starting directory — nothing sensible to open.
47
+ }
48
+ }
49
+
50
+ if (!existing) {
51
+ store.ensure(MOBILE_EXPLORER_WINDOW_ID, target);
52
+ } else if (path && existing.path !== path) {
53
+ // Jump an already-open instance to a new path, mirroring what `nav.go` does for a
54
+ // window: fresh history, cleared selection — this is a new place, not a back/forward step.
55
+ store.patch(MOBILE_EXPLORER_WINDOW_ID, {
56
+ path: target,
57
+ history: [target],
58
+ historyIndex: 0,
59
+ selection: new Set(),
60
+ anchor: null,
61
+ filter: "",
62
+ inlineEdit: null,
63
+ });
64
+ }
65
+
66
+ useMobileExplorerOpenState.setState({ isOpen: true, selectMode: false });
67
+ }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Host facts (platform, separator, home, drives, known folders, OS pins) for the sidebar.
3
+ *
4
+ * Every explorer window needs the same answer, and the server call spawns PowerShell /
5
+ * plutil / findmnt, so the result is cached module-wide and the in-flight promise is
6
+ * shared: N windows opening at once produce exactly one request.
7
+ */
8
+
9
+ import { useEffect, useState } from "react";
10
+ import { api } from "@/lib/api-client";
11
+ import type { HostInfo } from "../../../types/system";
12
+
13
+ const CACHE_TTL_MS = 60_000;
14
+
15
+ let cached: { at: number; value: HostInfo } | null = null;
16
+ let inFlight: Promise<HostInfo> | null = null;
17
+ const subscribers = new Set<(value: HostInfo) => void>();
18
+
19
+ function fetchHostInfo(): Promise<HostInfo> {
20
+ if (cached && Date.now() - cached.at < CACHE_TTL_MS) return Promise.resolve(cached.value);
21
+ if (inFlight) return inFlight;
22
+ inFlight = api
23
+ .get<HostInfo>("/api/system/host")
24
+ .then((value) => {
25
+ cached = { at: Date.now(), value };
26
+ for (const notify of subscribers) notify(value);
27
+ return value;
28
+ })
29
+ .finally(() => {
30
+ inFlight = null;
31
+ });
32
+ return inFlight;
33
+ }
34
+
35
+ export interface HostInfoState {
36
+ host: HostInfo | null;
37
+ loading: boolean;
38
+ error: string | null;
39
+ }
40
+
41
+ export function useHostInfo(): HostInfoState {
42
+ const [state, setState] = useState<HostInfoState>(() => ({
43
+ host: cached?.value ?? null,
44
+ loading: cached == null,
45
+ error: null,
46
+ }));
47
+
48
+ useEffect(() => {
49
+ let alive = true;
50
+ const notify = (value: HostInfo) => {
51
+ if (alive) setState({ host: value, loading: false, error: null });
52
+ };
53
+ subscribers.add(notify);
54
+ fetchHostInfo()
55
+ .then(notify)
56
+ .catch((e: unknown) => {
57
+ if (alive) {
58
+ setState({
59
+ host: null,
60
+ loading: false,
61
+ error: e instanceof Error ? e.message : "Failed to read host info",
62
+ });
63
+ }
64
+ });
65
+ return () => {
66
+ alive = false;
67
+ subscribers.delete(notify);
68
+ };
69
+ }, []);
70
+
71
+ return state;
72
+ }
73
+
74
+ /**
75
+ * Host info for callers outside React (command palette, nav rail): resolves from cache
76
+ * when warm, otherwise fetches. Used to pick a sensible starting directory.
77
+ */
78
+ export function getHostInfo(): Promise<HostInfo> {
79
+ return fetchHostInfo();
80
+ }
81
+
82
+ /** Best-effort home directory without waiting for the server. */
83
+ export function cachedHomedir(): string | null {
84
+ return cached?.value.homedir ?? null;
85
+ }
@@ -0,0 +1,126 @@
1
+ /**
2
+ * One row inside a Miller column: icon, name, a drill-in chevron for directories. Split out
3
+ * of `column-view-column.tsx` to keep that file under the size budget — the column container
4
+ * and one of its rows are different concerns (virtualised layout vs. a single interactive
5
+ * element).
6
+ */
7
+
8
+ import { forwardRef, useContext, type HTMLAttributes } from "react";
9
+ import { BottomSheetCtx } from "@/components/ui/mobile-bottom-sheet";
10
+ import { ContextMenu, ContextMenuTrigger } from "@/components/ui/adaptive-context-menu";
11
+ import { useIsMobile } from "@/hooks/use-is-mobile";
12
+ import type { FsEntry } from "@/lib/fs-api";
13
+ import { cn } from "@/lib/utils";
14
+ import type { ExplorerActions } from "../actions/use-explorer-actions";
15
+ import { DROP_TARGET_CLASS } from "../dnd/drop-target-style";
16
+ import { useEntryRowDnd } from "../dnd/use-entry-row-dnd";
17
+ import { ExplorerContextMenu } from "../explorer-context-menu";
18
+ import { FileTypeIcon } from "../icons/file-type-icon";
19
+ import { mobileTapAction } from "../mobile/mobile-tap-action";
20
+ import { useCoarseLongPress } from "../use-coarse-long-press";
21
+ import { composeInteractiveRowProps, type InjectedRowProps } from "./compose-interactive-row-props";
22
+
23
+ export interface ColumnRowProps {
24
+ entry: FsEntry;
25
+ selected: boolean;
26
+ currentDir: string;
27
+ hasClipboard: boolean;
28
+ isPinned(path: string): boolean;
29
+ actions: ExplorerActions;
30
+ rowHeight: number;
31
+ onSelect(): void;
32
+ onOpen(): void;
33
+ }
34
+
35
+ export function ColumnRow({ entry, selected, currentDir, hasClipboard, isPinned, actions, rowHeight, onSelect, onOpen }: ColumnRowProps) {
36
+ // Column view has no multi-select — a drag always carries just this one entry.
37
+ // Spring-load is Column view's own drill-in gesture (`onSelect` already navigates for a
38
+ // directory), so a rested hover reaches a deep folder without dropping first.
39
+ const dnd = useEntryRowDnd({ entry, dragPaths: [entry.path], run: actions.transferInto, springLoad: onSelect });
40
+ return (
41
+ <ContextMenu>
42
+ <ContextMenuTrigger asChild>
43
+ <ColumnRowInteractive
44
+ entry={entry}
45
+ selected={selected}
46
+ rowHeight={rowHeight}
47
+ onSelect={onSelect}
48
+ onOpen={onOpen}
49
+ dnd={dnd.props}
50
+ dropActive={dnd.isDropTarget}
51
+ />
52
+ </ContextMenuTrigger>
53
+ <ExplorerContextMenu
54
+ targets={[entry]}
55
+ currentDir={currentDir}
56
+ hasClipboard={hasClipboard}
57
+ isPinned={isPinned}
58
+ actions={actions}
59
+ />
60
+ </ContextMenu>
61
+ );
62
+ }
63
+
64
+ /**
65
+ * Rendered inside the row's own `<ContextMenu>` — the position `useContext(BottomSheetCtx)`
66
+ * needs to reach this row's own sheet state (see `use-mobile-row-tap.ts` for the same
67
+ * pattern in List/Icons). Column view keeps its existing single-select semantics on mobile —
68
+ * a directory already navigates on tap via `onSelect`; the only mobile addition is opening a
69
+ * viewable file immediately (no double-click on touch) and surfacing the actions sheet for
70
+ * one that has no viewer, instead of a tap that silently does nothing.
71
+ *
72
+ * `forwardRef` plus `composeInteractiveRowProps` over *everything* injected — not just
73
+ * `onContextMenu` — is required here: `asChild` clones this element and merges its own
74
+ * `onContextMenu` (which actually opens the row's menu), a `ref`, pointer handlers,
75
+ * `data-state` and a `style` that suppresses the native touch callout, but a component that
76
+ * doesn't accept and forward all of them keeps them from ever touching the real DOM node.
77
+ */
78
+ export const ColumnRowInteractive = forwardRef<
79
+ HTMLDivElement,
80
+ Pick<ColumnRowProps, "entry" | "selected" | "rowHeight" | "onSelect" | "onOpen"> & InjectedRowProps
81
+ >(function ColumnRowInteractive({ entry, selected, rowHeight, onSelect, onOpen, dnd, dropActive, ...injected }, ref) {
82
+ const longPress = useCoarseLongPress(onSelect);
83
+ const isMobile = useIsMobile();
84
+ const { setOpen } = useContext(BottomSheetCtx);
85
+
86
+ const handleClick = () => {
87
+ onSelect();
88
+ if (!isMobile || entry.type === "directory") return;
89
+ if (mobileTapAction(entry) === "open") onOpen();
90
+ else setOpen(true);
91
+ };
92
+
93
+ const props = composeInteractiveRowProps<Record<string, unknown>>(
94
+ { ...injected, ...longPress, ...dnd },
95
+ {
96
+ role: "option",
97
+ "aria-selected": selected,
98
+ "data-testid": "explorer-column-row",
99
+ "data-path": entry.path,
100
+ title: entry.name,
101
+ style: { height: rowHeight },
102
+ // Radix's own handler is chained in first (it opens this row's menu); only afterwards
103
+ // does this stop the event from also reaching the background trigger further up the DOM.
104
+ onContextMenu: (e: React.MouseEvent) => {
105
+ onSelect();
106
+ e.stopPropagation();
107
+ },
108
+ onClick: handleClick,
109
+ onDoubleClick: onOpen,
110
+ className: cn(
111
+ "flex w-full items-center gap-1.5 px-2 text-[13px] select-none",
112
+ "can-hover:hover:bg-surface-elevated",
113
+ selected && "bg-accent-wash text-text",
114
+ dropActive && DROP_TARGET_CLASS,
115
+ ),
116
+ },
117
+ );
118
+
119
+ return (
120
+ <div ref={ref} {...(props as HTMLAttributes<HTMLDivElement>)}>
121
+ <FileTypeIcon name={entry.name} kind={entry.kind} className="size-4 shrink-0" />
122
+ <span className={cn("flex-1 truncate", selected ? "text-text" : "text-text-2")}>{entry.name}</span>
123
+ {entry.type === "directory" && <span className="text-text-subtle">›</span>}
124
+ </div>
125
+ );
126
+ });