@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,150 @@
1
+ /**
2
+ * Makes any element a drop target for an entry drag: a directory row, a view background, a
3
+ * sidebar place, a breadcrumb crumb or a project-tree node.
4
+ *
5
+ * Two timers, both Finder/Explorer behaviour: the highlight only appears after the pointer
6
+ * has stayed 120 ms (a drag that sweeps across ten rows must not flash all ten), and an
7
+ * optional spring-load opens the hovered folder after 800 ms so a deep destination is
8
+ * reachable without dropping first. Spring-load is opt-in because it only makes sense where
9
+ * "open" does not destroy the drag — Column view and the project tree.
10
+ */
11
+
12
+ import { useCallback, useEffect, useRef, useState, type DragEvent } from "react";
13
+ import { decideDrop, type DropDecision } from "./drop-target-decision";
14
+ import { clearInFlightDrag, getInFlightDrag } from "./entry-drag-state";
15
+ import { decodeEntryDrag, ENTRY_DRAG_MIME, type DropOperation, type EntryDragPayload } from "./entry-drag-payload";
16
+
17
+ const HIGHLIGHT_DELAY_MS = 120;
18
+ const SPRING_LOAD_MS = 800;
19
+
20
+ export interface EntryDropTargetOptions {
21
+ /** Directory a drop writes into; null disables the target (a file row, a missing path). */
22
+ targetDir: string | null;
23
+ onDropEntries(payload: EntryDragPayload, op: DropOperation, targetDir: string): void;
24
+ /** Opens the hovered folder after a rest — Column view and the project tree only. */
25
+ springLoad?: () => void;
26
+ disabled?: boolean;
27
+ }
28
+
29
+ export interface EntryDropTargetResult {
30
+ /** True once the pointer has rested long enough for the target to claim the drop. */
31
+ isOver: boolean;
32
+ handlers: {
33
+ onDragEnter(event: DragEvent): void;
34
+ onDragOver(event: DragEvent): void;
35
+ onDragLeave(event: DragEvent): void;
36
+ onDrop(event: DragEvent): void;
37
+ };
38
+ }
39
+
40
+ function decisionFor(event: DragEvent, targetDir: string | null, payload: EntryDragPayload | null): DropDecision {
41
+ return decideDrop({
42
+ types: Array.from(event.dataTransfer.types),
43
+ payload,
44
+ targetDir,
45
+ modifiers: { ctrlKey: event.ctrlKey, altKey: event.altKey },
46
+ });
47
+ }
48
+
49
+ export function useEntryDropTarget(options: EntryDropTargetOptions): EntryDropTargetResult {
50
+ const { targetDir, onDropEntries, springLoad, disabled } = options;
51
+ const [isOver, setIsOver] = useState(false);
52
+ // Nested children fire their own enter/leave pairs; only a balanced count means "left".
53
+ const depth = useRef(0);
54
+ const timers = useRef<ReturnType<typeof setTimeout>[]>([]);
55
+
56
+ const stopTimers = useCallback(() => {
57
+ for (const timer of timers.current) clearTimeout(timer);
58
+ timers.current = [];
59
+ }, []);
60
+
61
+ const reset = useCallback(() => {
62
+ depth.current = 0;
63
+ stopTimers();
64
+ setIsOver(false);
65
+ }, [stopTimers]);
66
+
67
+ useEffect(() => stopTimers, [stopTimers]);
68
+
69
+ const onDragEnter = useCallback(
70
+ (event: DragEvent) => {
71
+ if (disabled) return;
72
+ // Count this row's own entry drags unconditionally, even when the current decision
73
+ // rejects (e.g. same-directory move) — a modifier held later (Ctrl → copy) can flip
74
+ // that same hover to an accept without a matching enter/leave pair, so counting must
75
+ // not depend on the decision or a later dragleave over the same subtree desyncs the
76
+ // balance and clears the highlight while the pointer is still over the target.
77
+ if (!event.dataTransfer.types.includes(ENTRY_DRAG_MIME)) return;
78
+ const decision = decisionFor(event, targetDir, getInFlightDrag());
79
+ if (decision.accept) {
80
+ event.preventDefault();
81
+ event.stopPropagation();
82
+ }
83
+ depth.current++;
84
+ if (depth.current > 1 || !decision.accept) return;
85
+ timers.current.push(setTimeout(() => setIsOver(true), HIGHLIGHT_DELAY_MS));
86
+ if (springLoad) timers.current.push(setTimeout(springLoad, SPRING_LOAD_MS));
87
+ },
88
+ [disabled, targetDir, springLoad],
89
+ );
90
+
91
+ const onDragOver = useCallback(
92
+ (event: DragEvent) => {
93
+ if (disabled) return;
94
+ const decision = decisionFor(event, targetDir, getInFlightDrag());
95
+ if (!decision.accept) return;
96
+ // Claiming the drop here is what keeps a directory row from also handing the drop to
97
+ // the view background behind it.
98
+ event.preventDefault();
99
+ event.stopPropagation();
100
+ event.dataTransfer.dropEffect = decision.op === "copy" ? "copy" : "move";
101
+ },
102
+ [disabled, targetDir],
103
+ );
104
+
105
+ const onDragLeave = useCallback(
106
+ (event: DragEvent) => {
107
+ if (depth.current === 0) return;
108
+ event.stopPropagation();
109
+ depth.current--;
110
+ if (depth.current === 0) {
111
+ stopTimers();
112
+ setIsOver(false);
113
+ }
114
+ },
115
+ [stopTimers],
116
+ );
117
+
118
+ const onDrop = useCallback(
119
+ (event: DragEvent) => {
120
+ if (disabled) return;
121
+ // The real payload is readable now; the module ref is only the hover-time stand-in.
122
+ // Clear it here too — `dragend` is not guaranteed to reach a source that unmounted
123
+ // mid-drag (e.g. a spring-load navigation), and a stale ref must never outlive a drop.
124
+ const payload = decodeEntryDrag(event.dataTransfer.getData(ENTRY_DRAG_MIME)) ?? getInFlightDrag();
125
+ clearInFlightDrag();
126
+ const decision = decisionFor(event, targetDir, payload);
127
+ if (!decision.accept) {
128
+ // A rejected PPM drag is still consumed here, so the browser does not fall back to
129
+ // navigating to the dropped path; a foreign drag is left alone entirely.
130
+ if (decision.reason !== "not-an-entry-drag") {
131
+ event.preventDefault();
132
+ event.stopPropagation();
133
+ }
134
+ reset();
135
+ return;
136
+ }
137
+ if (!payload || targetDir == null) {
138
+ reset();
139
+ return;
140
+ }
141
+ event.preventDefault();
142
+ event.stopPropagation();
143
+ reset();
144
+ onDropEntries(payload, decision.op, targetDir);
145
+ },
146
+ [disabled, targetDir, onDropEntries, reset],
147
+ );
148
+
149
+ return { isOver, handlers: { onDragEnter, onDragOver, onDragLeave, onDrop } };
150
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Drag source + drop target for one entry row/tile, collapsed into a single spreadable prop
3
+ * bag.
4
+ *
5
+ * The row components sit under a Radix `ContextMenuTrigger asChild`, which clones them and
6
+ * merges its own `ref` and `onContextMenu`. Adding seven more handlers to those signatures
7
+ * would make an easy place to drop one of the two by accident, so the wiring stays one
8
+ * object the row spreads verbatim.
9
+ */
10
+
11
+ import { useMemo, type DragEvent } from "react";
12
+ import type { FsEntry } from "@/lib/fs-api";
13
+ import type { DropRunner } from "./entry-drop-executor";
14
+ import type { EntryDragOrigin } from "./entry-drag-payload";
15
+ import { useEntryDragSource } from "./use-entry-drag-source";
16
+ import { usePathDropTarget } from "./use-path-drop-target";
17
+
18
+ export interface EntryRowDndProps {
19
+ draggable?: true;
20
+ onDragStart?(event: DragEvent): void;
21
+ onDragEnd?(event: DragEvent): void;
22
+ onDragEnter(event: DragEvent): void;
23
+ onDragOver(event: DragEvent): void;
24
+ onDragLeave(event: DragEvent): void;
25
+ onDrop(event: DragEvent): void;
26
+ }
27
+
28
+ export interface EntryRowDndOptions {
29
+ entry: FsEntry;
30
+ /** Absolute paths the drag carries — the whole selection when this row is part of it. */
31
+ dragPaths: string[];
32
+ run: DropRunner;
33
+ /** Column view and the tree open the hovered folder after a rest; List/Icons do not. */
34
+ springLoad?: () => void;
35
+ origin?: EntryDragOrigin;
36
+ projectName?: string;
37
+ extraData?: Record<string, string>;
38
+ onDragStarted?(): void;
39
+ }
40
+
41
+ export interface EntryRowDnd {
42
+ props: EntryRowDndProps;
43
+ /** True while this row is the claimed drop destination — draw the ring from it. */
44
+ isDropTarget: boolean;
45
+ }
46
+
47
+ export function useEntryRowDnd(options: EntryRowDndOptions): EntryRowDnd {
48
+ const { entry, dragPaths, run, springLoad, origin = "explorer", projectName, extraData, onDragStarted } = options;
49
+
50
+ const source = useEntryDragSource({ paths: dragPaths, origin, projectName, extraData, onDragStarted });
51
+
52
+ // Only a directory can receive a drop; a file row lets the event fall through to the view
53
+ // background, which is the same folder the file lives in — exactly what the OS does.
54
+ const target = usePathDropTarget({
55
+ targetDir: entry.type === "directory" ? entry.path : null,
56
+ run,
57
+ springLoad: entry.type === "directory" ? springLoad : undefined,
58
+ });
59
+
60
+ const props = useMemo<EntryRowDndProps>(
61
+ () => ({ ...source, ...target.handlers }),
62
+ [source, target.handlers],
63
+ );
64
+
65
+ return { props, isDropTarget: target.isOver };
66
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Drop target for a surface that already knows its destination directory and has no drag
3
+ * source of its own: a view background, a sidebar place, a breadcrumb crumb, the tree root.
4
+ */
5
+
6
+ import { useCallback } from "react";
7
+ import { executeEntryDrop, type DropRunner } from "./entry-drop-executor";
8
+ import type { DropOperation, EntryDragPayload } from "./entry-drag-payload";
9
+ import { useEntryDropTarget, type EntryDropTargetResult } from "./use-entry-drop-target";
10
+
11
+ export interface PathDropTargetOptions {
12
+ targetDir: string | null;
13
+ run: DropRunner;
14
+ /** Opens this destination after a rest — the project tree and Column view use it. */
15
+ springLoad?: () => void;
16
+ disabled?: boolean;
17
+ }
18
+
19
+ export function usePathDropTarget(options: PathDropTargetOptions): EntryDropTargetResult {
20
+ const { targetDir, run, springLoad, disabled } = options;
21
+
22
+ const onDropEntries = useCallback(
23
+ (payload: EntryDragPayload, op: DropOperation, dstDir: string) => {
24
+ void executeEntryDrop(payload, dstDir, op, run);
25
+ },
26
+ [run],
27
+ );
28
+
29
+ return useEntryDropTarget({ targetDir, onDropEntries, springLoad, disabled });
30
+ }
@@ -0,0 +1,153 @@
1
+ /**
2
+ * The explorer itself: sidebar · toolbar · view · status bar.
3
+ *
4
+ * Deliberately free of any window chrome so the mobile full-screen sheet can render the
5
+ * exact same component; `variant` is the only thing that differs between the two hosts.
6
+ */
7
+
8
+ import { useEffect, useMemo } from "react";
9
+ import { AlertTriangle, Home } from "lucide-react";
10
+ import { useFileStore } from "@/stores/file-store";
11
+ import { useExplorerActions } from "./actions/use-explorer-actions";
12
+ import { ExplorerDialogs } from "./explorer-dialogs";
13
+ import { ExplorerSidebar } from "./explorer-sidebar";
14
+ import { ExplorerStatusBar } from "./explorer-status-bar";
15
+ import { useExplorerStore } from "./explorer-store";
16
+ import { ExplorerToolbar } from "./explorer-toolbar";
17
+ import { useExplorerPinsStore } from "./explorer-pins-store";
18
+ import { FolderIconProvider } from "./icons/file-type-icon";
19
+ import { MobileBottomToolbar } from "./mobile/mobile-bottom-toolbar";
20
+ import { MobileExplorerTopBar } from "./mobile/mobile-explorer-top-bar";
21
+ import { MobilePlacesStrip } from "./mobile/mobile-places-strip";
22
+ import { useExplorerSkin } from "./skins/use-explorer-skin";
23
+ import { sortAndFilterEntries } from "./sort-and-filter-entries";
24
+ import { useCoarseLongPress, usePrefersCoarsePointer } from "./use-coarse-long-press";
25
+ import { useExplorerKeyboard } from "./use-explorer-keyboard";
26
+ import { useExplorerNavigation } from "./use-explorer-navigation";
27
+ import { useHostInfo } from "./use-host-info";
28
+ import { useMobileExplorerOpenState } from "./use-explorer-open-state";
29
+ import { useEntrySelection } from "./views/use-entry-selection";
30
+ import { viewComponentFor } from "./views/explorer-view-registry";
31
+
32
+ /** Row heights: comfortable for a mouse, ≥ 36 px for a finger, 48 px for the mobile sheet
33
+ * (design-guidelines' touch-target minimum plus a little breathing room over 44). */
34
+ const ROW_HEIGHT_FINE = 28;
35
+ const ROW_HEIGHT_COARSE = 36;
36
+ const ROW_HEIGHT_SHEET = 48;
37
+
38
+ export interface ExplorerBodyProps {
39
+ windowId: string;
40
+ /** Where the window opens; navigation takes over from there. */
41
+ initialPath: string;
42
+ /** Presentation host. The sheet variant fills its parent and drops the desktop chrome. */
43
+ variant?: "window" | "sheet";
44
+ }
45
+
46
+ export function ExplorerBody({ windowId, initialPath, variant = "window" }: ExplorerBodyProps) {
47
+ const ensure = useExplorerStore((s) => s.ensure);
48
+ // Creating the slice in an effect (not during render) keeps the store write out of
49
+ // React's render phase; the window shows nothing for exactly one frame.
50
+ useEffect(() => ensure(windowId, initialPath), [ensure, windowId, initialPath]);
51
+
52
+ const slice = useExplorerStore((s) => s.slices[windowId]);
53
+ const showHidden = useExplorerStore((s) => s.showHidden);
54
+ const viewMode = useExplorerStore((s) => s.viewMode);
55
+ const sort = useExplorerStore((s) => s.sort);
56
+ const { host } = useHostInfo();
57
+ const hasClipboard = useFileStore((s) => (s.clipboard?.paths.length ?? 0) > 0);
58
+ const isPinned = useExplorerPinsStore((s) => s.isPinned);
59
+ const coarse = usePrefersCoarsePointer();
60
+ const skin = useExplorerSkin();
61
+
62
+ const nav = useExplorerNavigation(windowId, slice, showHidden);
63
+ const { actions, dialogs } = useExplorerActions(windowId, slice, nav, host?.platform);
64
+
65
+ const entries = useMemo(
66
+ () => (slice ? sortAndFilterEntries(slice.entries, slice.filter, sort) : []),
67
+ [slice?.entries, slice?.filter, sort], // eslint-disable-line react-hooks/exhaustive-deps
68
+ );
69
+ const order = useMemo(() => entries.map((e) => e.path), [entries]);
70
+ const selection = useEntrySelection(windowId, order);
71
+ const onKeyDown = useExplorerKeyboard({
72
+ slice,
73
+ entries,
74
+ actions,
75
+ selection,
76
+ nav,
77
+ });
78
+ // The background menu needs a press target on touch just as the rows do.
79
+ const backgroundLongPress = useCoarseLongPress();
80
+ const closeSheet = useMobileExplorerOpenState((s) => s.close);
81
+
82
+ if (!slice) return null;
83
+
84
+ const View = viewComponentFor(viewMode);
85
+ const isSheet = variant === "sheet";
86
+ const rowHeight = isSheet ? ROW_HEIGHT_SHEET : coarse ? ROW_HEIGHT_COARSE : ROW_HEIGHT_FINE;
87
+
88
+ return (
89
+ <div
90
+ data-skin={skin.id}
91
+ style={{ fontFamily: "var(--x-font)" }}
92
+ className="flex h-full min-h-0 w-full flex-col bg-panel text-text"
93
+ >
94
+ {/* Mobile: title + a places chip strip replace the desktop toolbar/sidebar — every
95
+ frequent action moves to the bottom toolbar (thumb zone) below instead. */}
96
+ {isSheet ? (
97
+ <>
98
+ <MobileExplorerTopBar slice={slice} onClose={closeSheet} />
99
+ <MobilePlacesStrip host={host} currentPath={slice.path} onNavigate={nav.go} vocab={skin.vocab} />
100
+ </>
101
+ ) : (
102
+ <ExplorerToolbar windowId={windowId} slice={slice} nav={nav} />
103
+ )}
104
+
105
+ {slice.error && (
106
+ <div className="flex items-start gap-2 border-b border-border bg-error/10 px-2 py-1.5 text-xs text-error">
107
+ <AlertTriangle className="mt-0.5 size-3.5 shrink-0" />
108
+ <div className="min-w-0 flex-1">
109
+ <p className="break-words">{slice.error.message}</p>
110
+ {slice.error.hint && <p className="mt-0.5 text-text-2">{slice.error.hint}</p>}
111
+ </div>
112
+ {host && (
113
+ <button
114
+ type="button"
115
+ onClick={() => nav.go(host.homedir)}
116
+ className="flex shrink-0 items-center gap-1 rounded border border-border px-1.5 py-0.5 text-text-2 can-hover:hover:bg-surface-elevated"
117
+ >
118
+ <Home className="size-3" /> Go home
119
+ </button>
120
+ )}
121
+ </div>
122
+ )}
123
+
124
+ <FolderIconProvider value={{ closed: skin.FolderIcon, open: skin.FolderOpenIcon }}>
125
+ <div className="flex min-h-0 flex-1">
126
+ {!isSheet && <ExplorerSidebar host={host} currentPath={slice.path} onNavigate={nav.go} vocab={skin.vocab} />}
127
+ <div className="min-w-0 flex-1" onKeyDown={onKeyDown}>
128
+ <View
129
+ windowId={windowId}
130
+ slice={slice}
131
+ entries={entries}
132
+ actions={actions}
133
+ selection={selection}
134
+ inlineError={dialogs.inlineError}
135
+ hasClipboard={hasClipboard}
136
+ isPinned={isPinned}
137
+ rowHeight={rowHeight}
138
+ nav={nav}
139
+ backgroundLongPress={backgroundLongPress}
140
+ />
141
+ </div>
142
+ </div>
143
+ </FolderIconProvider>
144
+
145
+ {isSheet ? (
146
+ <MobileBottomToolbar slice={slice} nav={nav} actions={actions} entries={entries} hasClipboard={hasClipboard} />
147
+ ) : (
148
+ <ExplorerStatusBar entries={entries} selection={slice.selection} truncated={slice.truncated} />
149
+ )}
150
+ <ExplorerDialogs dialogs={dialogs} platform={host?.platform} sep={slice.sep} />
151
+ </div>
152
+ );
153
+ }
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Path breadcrumb built from the server's segments — the client never splits paths itself,
3
+ * because a drive root, a UNC prefix and a POSIX root all split differently.
4
+ *
5
+ * Deep paths keep the first crumb and the last few; everything in between collapses into
6
+ * one overflow menu, so the bar never pushes the toolbar controls out of a narrow window.
7
+ */
8
+
9
+ import { ChevronRight, MoreHorizontal } from "lucide-react";
10
+ import {
11
+ DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger,
12
+ } from "@/components/ui/dropdown-menu";
13
+ import type { FsBreadcrumb } from "@/lib/fs-api";
14
+ import { cn } from "@/lib/utils";
15
+ import { DROP_TARGET_CLASS } from "./dnd/drop-target-style";
16
+ import { usePathDropTarget } from "./dnd/use-path-drop-target";
17
+ import { useDropTransfer } from "./dnd/use-drop-transfer";
18
+
19
+ /** Crumbs shown at the tail before the middle is collapsed. */
20
+ const TAIL_CRUMBS = 3;
21
+
22
+ export interface ExplorerBreadcrumbProps {
23
+ crumbs: FsBreadcrumb[];
24
+ onNavigate(path: string): void;
25
+ className?: string;
26
+ /** Host path separator — every crumb is an ancestor directory of the same window. */
27
+ sep: string;
28
+ }
29
+
30
+ export function ExplorerBreadcrumb({ crumbs, onNavigate, className, sep }: ExplorerBreadcrumbProps) {
31
+ // One drop-transfer context for the whole bar — dropping on any ancestor crumb lands
32
+ // through the same collision prompt as a paste.
33
+ const { run, prompts } = useDropTransfer(sep);
34
+
35
+ if (crumbs.length === 0) return <div className={className} />;
36
+
37
+ const collapsed = crumbs.length > TAIL_CRUMBS + 2;
38
+ const head = collapsed ? crumbs.slice(0, 1) : crumbs;
39
+ const hidden = collapsed ? crumbs.slice(1, crumbs.length - TAIL_CRUMBS) : [];
40
+ const tail = collapsed ? crumbs.slice(crumbs.length - TAIL_CRUMBS) : [];
41
+
42
+ return (
43
+ <nav aria-label="Path" className={cn("flex min-w-0 items-center gap-0.5 text-xs", className)}>
44
+ {head.map((crumb) => (
45
+ <span key={crumb.path} className="flex shrink-0 items-center gap-0.5">
46
+ <BreadcrumbCrumb crumb={crumb} last={!collapsed && crumb === crumbs[crumbs.length - 1]} onNavigate={onNavigate} run={run} />
47
+ <ChevronRight className="size-3 shrink-0 text-text-subtle" />
48
+ </span>
49
+ ))}
50
+
51
+ {collapsed && (
52
+ <DropdownMenu>
53
+ <DropdownMenuTrigger asChild>
54
+ <button
55
+ type="button"
56
+ aria-label="Show hidden path segments"
57
+ className="rounded px-1 py-0.5 text-text-2 can-hover:hover:bg-surface-elevated"
58
+ >
59
+ <MoreHorizontal className="size-3.5" />
60
+ </button>
61
+ </DropdownMenuTrigger>
62
+ <DropdownMenuContent align="start" className="max-h-72 overflow-y-auto">
63
+ {hidden.map((crumb) => (
64
+ <DropdownMenuItem key={crumb.path} onClick={() => onNavigate(crumb.path)}>
65
+ {crumb.name}
66
+ </DropdownMenuItem>
67
+ ))}
68
+ </DropdownMenuContent>
69
+ </DropdownMenu>
70
+ )}
71
+ {collapsed && <ChevronRight className="size-3 shrink-0 text-text-subtle" />}
72
+
73
+ {tail.map((crumb, index) => (
74
+ <span key={crumb.path} className="flex min-w-0 items-center gap-0.5">
75
+ <BreadcrumbCrumb crumb={crumb} last={index === tail.length - 1} onNavigate={onNavigate} run={run} />
76
+ {index < tail.length - 1 && <ChevronRight className="size-3 shrink-0 text-text-subtle" />}
77
+ </span>
78
+ ))}
79
+ {prompts}
80
+ </nav>
81
+ );
82
+ }
83
+
84
+ interface BreadcrumbCrumbProps {
85
+ crumb: FsBreadcrumb;
86
+ last: boolean;
87
+ onNavigate(path: string): void;
88
+ run: ReturnType<typeof useDropTransfer>["run"];
89
+ }
90
+
91
+ /** One crumb, as its own component: a drop target needs its own hook call per crumb. */
92
+ function BreadcrumbCrumb({ crumb, last, onNavigate, run }: BreadcrumbCrumbProps) {
93
+ const drop = usePathDropTarget({ targetDir: crumb.path, run });
94
+ return (
95
+ <button
96
+ type="button"
97
+ onClick={() => onNavigate(crumb.path)}
98
+ className={cn(
99
+ "max-w-[12rem] truncate rounded px-1 py-0.5 can-hover:hover:bg-surface-elevated",
100
+ last ? "font-medium text-text" : "text-text-2",
101
+ drop.isOver && DROP_TARGET_CLASS,
102
+ )}
103
+ {...drop.handlers}
104
+ >
105
+ {crumb.name}
106
+ </button>
107
+ );
108
+ }
@@ -0,0 +1,131 @@
1
+ /**
2
+ * The explorer's context menu. One component covers both cases: a right-click on rows
3
+ * (`targets` non-empty) and a right-click on empty space (`targets` empty), because the
4
+ * OS menus differ only in which items appear.
5
+ *
6
+ * Rendered through the adaptive menu, so it is a bottom sheet on phones and a radix menu
7
+ * everywhere else.
8
+ */
9
+
10
+ import {
11
+ ContextMenuContent,
12
+ ContextMenuItem,
13
+ ContextMenuRadioGroup,
14
+ ContextMenuRadioItem,
15
+ ContextMenuSeparator,
16
+ ContextMenuSub,
17
+ ContextMenuSubContent,
18
+ ContextMenuSubTrigger,
19
+ } from "@/components/ui/adaptive-context-menu";
20
+ import { useIsMobile } from "@/hooks/use-is-mobile";
21
+ import type { FsEntry } from "@/lib/fs-api";
22
+ import { canOpenInPpm } from "./can-open-in-ppm";
23
+ import type { ExplorerActions } from "./actions/use-explorer-actions";
24
+ import { useExplorerStore, type SortDir, type SortKey } from "./explorer-store";
25
+ import { SORT_DIR_OPTIONS, SORT_KEY_OPTIONS } from "./sort-options";
26
+
27
+ export interface ExplorerContextMenuProps {
28
+ /** Entries the menu acts on; empty means the folder background. */
29
+ targets: FsEntry[];
30
+ currentDir: string;
31
+ hasClipboard: boolean;
32
+ isPinned(path: string): boolean;
33
+ actions: ExplorerActions;
34
+ }
35
+
36
+ export function ExplorerContextMenu({
37
+ targets, currentDir, hasClipboard, isPinned, actions,
38
+ }: ExplorerContextMenuProps) {
39
+ // The mobile sheet is a single instance — "Open in New Window" would spawn a desktop
40
+ // floating window that `WindowLayer` never renders below `md`, a silent no-op there.
41
+ const isMobile = useIsMobile();
42
+ const single = targets.length === 1 ? targets[0]! : null;
43
+ const onlyDirs = targets.length > 0 && targets.every((e) => e.type === "directory");
44
+ const count = targets.length;
45
+ const suffix = count > 1 ? ` ${count} items` : "";
46
+ const sort = useExplorerStore((s) => s.sort);
47
+ const setPrefs = useExplorerStore((s) => s.setPrefs);
48
+
49
+ if (count === 0) {
50
+ return (
51
+ <ContextMenuContent>
52
+ <ContextMenuItem onClick={() => actions.startCreate("new-file")}>New File</ContextMenuItem>
53
+ <ContextMenuItem onClick={() => actions.startCreate("new-folder")}>New Folder</ContextMenuItem>
54
+ <ContextMenuSeparator />
55
+ <ContextMenuItem disabled={!hasClipboard} onClick={() => actions.paste()}>Paste</ContextMenuItem>
56
+ <ContextMenuSeparator />
57
+ <ContextMenuSub>
58
+ <ContextMenuSubTrigger>Sort by</ContextMenuSubTrigger>
59
+ <ContextMenuSubContent>
60
+ <ContextMenuRadioGroup
61
+ value={sort.key}
62
+ onValueChange={(key) => setPrefs({ sort: { key: key as SortKey, dir: sort.dir } })}
63
+ >
64
+ {SORT_KEY_OPTIONS.map(({ key, label }) => (
65
+ <ContextMenuRadioItem key={key} value={key}>{label}</ContextMenuRadioItem>
66
+ ))}
67
+ </ContextMenuRadioGroup>
68
+ <ContextMenuSeparator />
69
+ <ContextMenuRadioGroup
70
+ value={sort.dir}
71
+ onValueChange={(dir) => setPrefs({ sort: { key: sort.key, dir: dir as SortDir } })}
72
+ >
73
+ {SORT_DIR_OPTIONS.map(({ dir, label }) => (
74
+ <ContextMenuRadioItem key={dir} value={dir}>{label}</ContextMenuRadioItem>
75
+ ))}
76
+ </ContextMenuRadioGroup>
77
+ </ContextMenuSubContent>
78
+ </ContextMenuSub>
79
+ <ContextMenuSeparator />
80
+ <ContextMenuItem onClick={() => actions.copyPath([currentDir])}>Copy Path</ContextMenuItem>
81
+ </ContextMenuContent>
82
+ );
83
+ }
84
+
85
+ return (
86
+ <ContextMenuContent>
87
+ {single && (single.type === "directory" || canOpenInPpm(single.name)) && (
88
+ <ContextMenuItem onClick={() => actions.openEntry(single)}>Open</ContextMenuItem>
89
+ )}
90
+ {onlyDirs && single && !isMobile && (
91
+ <ContextMenuItem onClick={() => actions.openInNewWindow(single)}>
92
+ Open in New Window
93
+ </ContextMenuItem>
94
+ )}
95
+ <ContextMenuSeparator />
96
+
97
+ <ContextMenuItem onClick={() => actions.cut(targets)}>Cut{suffix}</ContextMenuItem>
98
+ <ContextMenuItem onClick={() => actions.copy(targets)}>Copy{suffix}</ContextMenuItem>
99
+ {onlyDirs && single && (
100
+ <ContextMenuItem disabled={!hasClipboard} onClick={() => actions.paste(single.path)}>
101
+ Paste
102
+ </ContextMenuItem>
103
+ )}
104
+ <ContextMenuSeparator />
105
+
106
+ {single && <ContextMenuItem onClick={() => actions.startRename(single)}>Rename</ContextMenuItem>}
107
+ <ContextMenuItem onClick={() => actions.trash(targets)}>Move to Trash{suffix}</ContextMenuItem>
108
+ <ContextMenuItem variant="destructive" onClick={() => actions.confirmPermanentDelete(targets)}>
109
+ Delete permanently
110
+ </ContextMenuItem>
111
+ <ContextMenuSeparator />
112
+
113
+ <ContextMenuItem onClick={() => actions.copyPath(targets.map((e) => e.path))}>Copy Path</ContextMenuItem>
114
+ <ContextMenuItem onClick={() => actions.copyName(targets.map((e) => e.name))}>Copy Name</ContextMenuItem>
115
+ {targets.some((e) => e.type === "file") && (
116
+ <ContextMenuItem onClick={() => actions.download(targets)}>Download</ContextMenuItem>
117
+ )}
118
+ {onlyDirs && single && (
119
+ <ContextMenuItem onClick={() => actions.togglePin(single.path, single.name)}>
120
+ {isPinned(single.path) ? "Unpin from Sidebar" : "Pin to Sidebar"}
121
+ </ContextMenuItem>
122
+ )}
123
+ {single && (
124
+ <>
125
+ <ContextMenuSeparator />
126
+ <ContextMenuItem onClick={() => actions.showProperties(single)}>Properties</ContextMenuItem>
127
+ </>
128
+ )}
129
+ </ContextMenuContent>
130
+ );
131
+ }