@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,124 @@
1
+ /**
2
+ * Lazy, viewport-gated thumbnail for image files.
3
+ *
4
+ * A bare `<img src="/api/fs/raw?...">` cannot carry the Authorization header the API
5
+ * requires, so the bytes are fetched manually and turned into an object URL — the same
6
+ * trick `useBlobUrl` uses — but gated by an `IntersectionObserver` (nothing fetches until
7
+ * the tile scrolls near the viewport) and a module-wide concurrency cap, so opening a
8
+ * folder of a thousand photos does not fire a thousand requests at once.
9
+ */
10
+
11
+ import { useEffect, useRef, useState } from "react";
12
+ import { getAuthToken } from "@/lib/api-client";
13
+ import type { FsEntry } from "@/lib/fs-api";
14
+ import { cn } from "@/lib/utils";
15
+ import { FileTypeIcon } from "./file-type-icon";
16
+ import { acquireSlot } from "./thumbnail-fetch-semaphore";
17
+
18
+ export interface ThumbnailImageProps {
19
+ entry: Pick<FsEntry, "name" | "path" | "kind">;
20
+ /** Square box the thumbnail (or its icon fallback) renders into, in px. */
21
+ size: number;
22
+ className?: string;
23
+ }
24
+
25
+ export function ThumbnailImage({ entry, size, className }: ThumbnailImageProps) {
26
+ const [src, setSrc] = useState<string | null>(null);
27
+ const [failed, setFailed] = useState(false);
28
+ const rootRef = useRef<HTMLDivElement>(null);
29
+ const urlRef = useRef<string | null>(null);
30
+
31
+ // A different entry (row recycled by the virtualizer, or the user navigated) starts over.
32
+ useEffect(() => {
33
+ setSrc(null);
34
+ setFailed(false);
35
+ }, [entry.path]);
36
+
37
+ useEffect(() => {
38
+ const el = rootRef.current;
39
+ if (!el || src || failed) return;
40
+ // A scrolled-past tile must stop downloading (the server has no thumbnail endpoint —
41
+ // `/api/fs/raw` streams the whole original file) and give its semaphore slot back
42
+ // immediately rather than waiting for a fetch nobody will render.
43
+ const controller = new AbortController();
44
+ let release: (() => void) | null = null;
45
+
46
+ const observer = new IntersectionObserver(
47
+ (observed) => {
48
+ if (!observed[0]?.isIntersecting) return;
49
+ observer.disconnect();
50
+ void (async () => {
51
+ release = await acquireSlot();
52
+ if (controller.signal.aborted) {
53
+ release();
54
+ return;
55
+ }
56
+ try {
57
+ const token = getAuthToken();
58
+ const res = await fetch(`/api/fs/raw?path=${encodeURIComponent(entry.path)}`, {
59
+ headers: token ? { Authorization: `Bearer ${token}` } : {},
60
+ signal: controller.signal,
61
+ });
62
+ if (!res.ok) throw new Error(String(res.status));
63
+ const blob = await res.blob();
64
+ if (controller.signal.aborted) return;
65
+ const url = URL.createObjectURL(blob);
66
+ urlRef.current = url;
67
+ setSrc(url);
68
+ } catch {
69
+ if (!controller.signal.aborted) setFailed(true);
70
+ } finally {
71
+ // Idempotent: harmless if this fires again from the cleanup below for the same
72
+ // completed acquisition.
73
+ release?.();
74
+ }
75
+ })();
76
+ },
77
+ // Start the fetch a little before the tile is actually visible so it is usually
78
+ // ready by the time it scrolls fully into view.
79
+ { rootMargin: "200px" },
80
+ );
81
+ observer.observe(el);
82
+ return () => {
83
+ controller.abort();
84
+ observer.disconnect();
85
+ release?.();
86
+ };
87
+ }, [entry.path, src, failed]);
88
+
89
+ useEffect(
90
+ () => () => {
91
+ if (urlRef.current) URL.revokeObjectURL(urlRef.current);
92
+ },
93
+ [],
94
+ );
95
+
96
+ if (failed || !src) {
97
+ return (
98
+ <div
99
+ ref={rootRef}
100
+ className={cn("flex items-center justify-center", className)}
101
+ style={{ width: size, height: size }}
102
+ >
103
+ <FileTypeIcon name={entry.name} kind={entry.kind} className="size-1/2" />
104
+ </div>
105
+ );
106
+ }
107
+
108
+ return (
109
+ <div
110
+ ref={rootRef}
111
+ className={cn("flex items-center justify-center overflow-hidden", className)}
112
+ style={{ width: size, height: size }}
113
+ >
114
+ {/* eslint-disable-next-line @next/next/no-img-element -- plain img, not next/image */}
115
+ <img
116
+ src={src}
117
+ alt=""
118
+ loading="lazy"
119
+ className="max-h-full max-w-full rounded object-contain"
120
+ onError={() => setFailed(true)}
121
+ />
122
+ </div>
123
+ );
124
+ }
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Bottom toolbar: every frequent action lives here, in the thumb zone. Two rows share the
3
+ * one bar — the normal row (navigate / create / paste / enter Select) and, once "Select" is
4
+ * tapped, a row of bulk actions over the current selection. "Delete permanently" is
5
+ * deliberately not here (it lives in the More sheet) — Trash is the default and the one-tap
6
+ * row should not make the irreversible action as easy to reach as the reversible one.
7
+ */
8
+
9
+ import { useState } from "react";
10
+ import {
11
+ ArrowLeft, Check, ClipboardPaste, Copy, Download, FilePlus, FolderPlus, Info, MoreHorizontal, Plus, Scissors, Trash2, X,
12
+ } from "lucide-react";
13
+ import type { FsEntry } from "@/lib/fs-api";
14
+ import { BottomSheet, BottomSheetItem } from "@/components/ui/mobile-bottom-sheet";
15
+ import type { ExplorerActions } from "../actions/use-explorer-actions";
16
+ import type { ExplorerSlice } from "../explorer-store";
17
+ import type { ExplorerNavigation } from "../use-explorer-navigation";
18
+ import { useMobileExplorerOpenState } from "../use-explorer-open-state";
19
+ import { MobileMoreSheet } from "./mobile-more-sheet";
20
+
21
+ function ToolbarButton({
22
+ icon: Icon, label, onClick, disabled, destructive,
23
+ }: {
24
+ icon: typeof Plus; label: string; onClick(): void; disabled?: boolean; destructive?: boolean;
25
+ }) {
26
+ return (
27
+ <button
28
+ type="button"
29
+ aria-label={label}
30
+ title={label}
31
+ disabled={disabled}
32
+ onClick={onClick}
33
+ className={`flex min-w-[44px] flex-1 flex-col items-center gap-0.5 rounded-lg py-1.5 text-[10px] active:bg-surface-elevated disabled:opacity-30 ${destructive ? "text-error" : "text-text-2"}`}
34
+ >
35
+ <Icon className="size-5" />
36
+ <span className="truncate">{label}</span>
37
+ </button>
38
+ );
39
+ }
40
+
41
+ export interface MobileBottomToolbarProps {
42
+ slice: ExplorerSlice;
43
+ nav: ExplorerNavigation;
44
+ actions: ExplorerActions;
45
+ entries: FsEntry[];
46
+ hasClipboard: boolean;
47
+ }
48
+
49
+ export function MobileBottomToolbar({ slice, nav, actions, entries, hasClipboard }: MobileBottomToolbarProps) {
50
+ const selectMode = useMobileExplorerOpenState((s) => s.selectMode);
51
+ const setSelectMode = useMobileExplorerOpenState((s) => s.setSelectMode);
52
+ const [newOpen, setNewOpen] = useState(false);
53
+ const [moreOpen, setMoreOpen] = useState(false);
54
+
55
+ const selected = entries.filter((e) => slice.selection.has(e.path));
56
+
57
+ if (selectMode) {
58
+ const exitSelect = () => { setSelectMode(false); };
59
+ return (
60
+ <div className="flex shrink-0 items-center gap-1 border-t border-border px-1 py-1">
61
+ <ToolbarButton icon={X} label="Cancel" onClick={exitSelect} />
62
+ <span className="px-1 text-xs text-text-subtle">{selected.length} selected</span>
63
+ <div className="flex-1" />
64
+ <ToolbarButton icon={Scissors} label="Cut" disabled={selected.length === 0} onClick={() => actions.cut(selected)} />
65
+ <ToolbarButton icon={Copy} label="Copy" disabled={selected.length === 0} onClick={() => actions.copy(selected)} />
66
+ <ToolbarButton icon={Trash2} label="Trash" disabled={selected.length === 0} destructive onClick={() => actions.trash(selected)} />
67
+ <ToolbarButton
68
+ icon={Download}
69
+ label="Download"
70
+ disabled={!selected.some((e) => e.type === "file")}
71
+ onClick={() => actions.download(selected)}
72
+ />
73
+ {selected.length === 1 && (
74
+ <ToolbarButton icon={Info} label="Info" onClick={() => actions.showProperties(selected[0]!)} />
75
+ )}
76
+ </div>
77
+ );
78
+ }
79
+
80
+ return (
81
+ <>
82
+ <div className="flex shrink-0 items-center gap-1 border-t border-border px-1 py-1">
83
+ <ToolbarButton icon={ArrowLeft} label="Back" disabled={!nav.canGoBack} onClick={nav.back} />
84
+ <ToolbarButton icon={Plus} label="New" onClick={() => setNewOpen(true)} />
85
+ <ToolbarButton icon={ClipboardPaste} label="Paste" disabled={!hasClipboard} onClick={() => actions.paste()} />
86
+ <ToolbarButton icon={Check} label="Select" onClick={() => setSelectMode(true)} />
87
+ <ToolbarButton icon={MoreHorizontal} label="More" onClick={() => setMoreOpen(true)} />
88
+ </div>
89
+
90
+ <BottomSheet open={newOpen} onClose={() => setNewOpen(false)} className="p-2">
91
+ <BottomSheetItem onClick={() => actions.startCreate("new-file")}>
92
+ <FilePlus className="size-4" /> New File
93
+ </BottomSheetItem>
94
+ <BottomSheetItem onClick={() => actions.startCreate("new-folder")}>
95
+ <FolderPlus className="size-4" /> New Folder
96
+ </BottomSheetItem>
97
+ </BottomSheet>
98
+
99
+ <MobileMoreSheet open={moreOpen} onClose={() => setMoreOpen(false)} slice={slice} actions={actions} entries={entries} />
100
+ </>
101
+ );
102
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Full-screen mobile presentation of the explorer: a `BottomSheet` sized to the whole
3
+ * visible viewport, hosting the exact same `ExplorerBody` every desktop window uses
4
+ * (`variant="sheet"`), so file-type icons, the active OS skin's vocabulary and every mutation
5
+ * action are shared code, not a parallel mobile implementation.
6
+ *
7
+ * Self-gated singleton, mounted once at the app root next to the other overlay singletons
8
+ * (`ComparePicker`, `ImageOverlay`, …) — renders nothing until `openMobileExplorer` is called.
9
+ */
10
+
11
+ import { useEffect, type TouchEvent } from "react";
12
+ import { BottomSheet } from "@/components/ui/mobile-bottom-sheet";
13
+ import { ExplorerBody } from "../explorer-body";
14
+ import { useExplorerStore } from "../explorer-store";
15
+ import { MOBILE_EXPLORER_WINDOW_ID, useMobileExplorerOpenState } from "../use-explorer-open-state";
16
+
17
+ export function MobileExplorerSheet() {
18
+ const isOpen = useMobileExplorerOpenState((s) => s.isOpen);
19
+ const close = useMobileExplorerOpenState((s) => s.close);
20
+ const setSelectMode = useMobileExplorerOpenState((s) => s.setSelectMode);
21
+ const path = useExplorerStore((s) => s.slices[MOBILE_EXPLORER_WINDOW_ID]?.path);
22
+ const renaming = useExplorerStore(
23
+ (s) => s.slices[MOBILE_EXPLORER_WINDOW_ID]?.inlineEdit?.kind === "rename",
24
+ );
25
+
26
+ // Navigating (forward, back, or via a place/breadcrumb) already clears the shared
27
+ // selection — Select mode's own checkbox/bottom-bar state must follow it, or the toolbar
28
+ // is left on Cut/Copy/Trash with 0 items and no way back short of the explicit Cancel.
29
+ useEffect(() => {
30
+ setSelectMode(false);
31
+ }, [path, setSelectMode]);
32
+
33
+ // Swallowed only while an inline rename is active: a drag meant to clear the soft keyboard
34
+ // (or just reach a field below it) would otherwise also read as a swipe-to-dismiss, since
35
+ // `BottomSheet` wires its drag handlers on the panel — an ancestor of everything here.
36
+ const suppressSwipe = (e: TouchEvent) => {
37
+ if (renaming) e.stopPropagation();
38
+ };
39
+
40
+ if (!isOpen) return null;
41
+
42
+ return (
43
+ // Below the default sheet z-index (50): the explorer opens further sheets on top of
44
+ // itself (row actions, "New", "More", "Places") that all rely on that default, and a
45
+ // higher value here would silently sit above every one of them instead. 45 still clears
46
+ // the mobile bottom nav (z-40) so the sheet reads as full-screen underneath it.
47
+ <BottomSheet open={isOpen} onClose={close} zIndex={45} className="flex h-[var(--sheet-vh)] flex-col p-0">
48
+ <div
49
+ className="flex min-h-0 flex-1 flex-col"
50
+ onTouchStart={suppressSwipe}
51
+ onTouchMove={suppressSwipe}
52
+ >
53
+ {path ? (
54
+ <ExplorerBody windowId={MOBILE_EXPLORER_WINDOW_ID} initialPath={path} variant="sheet" />
55
+ ) : (
56
+ <div className="flex flex-1 items-center justify-center text-sm text-text-2">Loading…</div>
57
+ )}
58
+ </div>
59
+ </BottomSheet>
60
+ );
61
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Mobile sheet header: current folder name, nothing else. Every frequent action lives in
3
+ * the bottom toolbar (the thumb zone) — putting anything interactive up here would be the
4
+ * one part of a one-handed grip that is hardest to reach.
5
+ *
6
+ * The close button is the one addition beyond "title only": a sheet that fills the whole
7
+ * screen leaves no backdrop to tap, so swipe-to-dismiss would otherwise be the only way out.
8
+ */
9
+
10
+ import { X } from "lucide-react";
11
+ import type { ExplorerSlice } from "../explorer-store";
12
+
13
+ export interface MobileExplorerTopBarProps {
14
+ slice: ExplorerSlice;
15
+ onClose(): void;
16
+ }
17
+
18
+ /** The folder name to show for the current path — the last breadcrumb is the folder itself. */
19
+ function currentFolderName(slice: ExplorerSlice): string {
20
+ const last = slice.breadcrumbs[slice.breadcrumbs.length - 1];
21
+ return last?.name ?? slice.path;
22
+ }
23
+
24
+ export function MobileExplorerTopBar({ slice, onClose }: MobileExplorerTopBarProps) {
25
+ return (
26
+ <div className="flex shrink-0 items-center gap-2 border-b border-border px-3 py-2.5">
27
+ <span className="min-w-0 flex-1 truncate text-[15px] font-semibold text-text">
28
+ {currentFolderName(slice)}
29
+ </span>
30
+ <button
31
+ type="button"
32
+ aria-label="Close file explorer"
33
+ title="Close"
34
+ onClick={onClose}
35
+ className="flex size-11 shrink-0 items-center justify-center rounded-full text-text-2 active:bg-surface-elevated"
36
+ >
37
+ <X className="size-5" />
38
+ </button>
39
+ </div>
40
+ );
41
+ }
@@ -0,0 +1,117 @@
1
+ /**
2
+ * The mobile toolbar's overflow sheet: view mode, sort, hidden files, permanent delete and
3
+ * properties of the *current folder* (not the selection — that's what the row/tile menu and
4
+ * the selection-mode toolbar are for).
5
+ *
6
+ * "Delete permanently" lives here rather than on the one-tap selection toolbar so the
7
+ * irreversible action never sits as close at hand as Trash.
8
+ */
9
+
10
+ import {
11
+ Columns3, Eye, EyeOff, Info, LayoutGrid, List, Trash2,
12
+ } from "lucide-react";
13
+ import type { FsEntry } from "@/lib/fs-api";
14
+ import { BottomSheet, BottomSheetItem, BottomSheetSeparator, BottomSheetSubLabel } from "@/components/ui/mobile-bottom-sheet";
15
+ import type { ExplorerActions } from "../actions/use-explorer-actions";
16
+ import { useExplorerStore, type ExplorerSlice, type ViewMode } from "../explorer-store";
17
+ import { SORT_KEY_OPTIONS } from "../sort-options";
18
+ import { activeEntries } from "../use-explorer-keyboard";
19
+ import { AVAILABLE_VIEW_MODES } from "../views/explorer-view-registry";
20
+ import { cn } from "@/lib/utils";
21
+
22
+ const VIEW_ICON: Record<ViewMode, typeof List> = { list: List, icons: LayoutGrid, columns: Columns3 };
23
+ const VIEW_LABEL: Record<ViewMode, string> = { list: "List", icons: "Icons", columns: "Columns" };
24
+
25
+ /** The current folder, in the shape `showProperties`/`PropertiesDialog` need — the dialog
26
+ * fetches everything else itself from `/api/fs/stat`. */
27
+ function currentFolderEntry(slice: ExplorerSlice): FsEntry {
28
+ const name = slice.breadcrumbs[slice.breadcrumbs.length - 1]?.name ?? slice.path;
29
+ return { name, path: slice.path, type: "directory", kind: "directory", modified: "" };
30
+ }
31
+
32
+ export interface MobileMoreSheetProps {
33
+ open: boolean;
34
+ onClose(): void;
35
+ slice: ExplorerSlice;
36
+ actions: ExplorerActions;
37
+ entries: FsEntry[];
38
+ }
39
+
40
+ export function MobileMoreSheet({ open, onClose, slice, actions, entries }: MobileMoreSheetProps) {
41
+ const viewMode = useExplorerStore((s) => s.viewMode);
42
+ const sort = useExplorerStore((s) => s.sort);
43
+ const showHidden = useExplorerStore((s) => s.showHidden);
44
+ const setPrefs = useExplorerStore((s) => s.setPrefs);
45
+ const targets = activeEntries(slice, entries);
46
+
47
+ return (
48
+ <BottomSheet open={open} onClose={onClose} className="p-2">
49
+ <BottomSheetSubLabel>View</BottomSheetSubLabel>
50
+ <div className="flex gap-1 px-2 pb-2">
51
+ {AVAILABLE_VIEW_MODES.map((mode) => {
52
+ const Icon = VIEW_ICON[mode];
53
+ return (
54
+ <button
55
+ key={mode}
56
+ type="button"
57
+ aria-pressed={viewMode === mode}
58
+ onClick={() => { setPrefs({ viewMode: mode }); onClose(); }}
59
+ className={cn(
60
+ "flex flex-1 flex-col items-center gap-1 rounded-lg py-2 text-xs active:bg-surface-elevated",
61
+ viewMode === mode ? "bg-accent-wash text-primary" : "text-text-2",
62
+ )}
63
+ >
64
+ <Icon className="size-4" />
65
+ {VIEW_LABEL[mode]}
66
+ </button>
67
+ );
68
+ })}
69
+ </div>
70
+
71
+ <BottomSheetSeparator />
72
+ <BottomSheetSubLabel>Sort by</BottomSheetSubLabel>
73
+ <div className="flex flex-wrap gap-1 px-2 pb-2">
74
+ {SORT_KEY_OPTIONS.map(({ key, label }) => (
75
+ <button
76
+ key={key}
77
+ type="button"
78
+ aria-pressed={sort.key === key}
79
+ onClick={() => {
80
+ setPrefs({ sort: { key, dir: sort.key === key && sort.dir === "asc" ? "desc" : "asc" } });
81
+ onClose();
82
+ }}
83
+ className={cn(
84
+ // min-h-11 (44px) keeps the tap target compliant without inflating the pill's
85
+ // padding to match — inline-flex + items-center re-centers the small text
86
+ // inside the taller box.
87
+ "inline-flex min-h-11 items-center justify-center rounded-full border px-3 py-1.5 text-xs active:bg-surface-elevated",
88
+ sort.key === key ? "border-primary bg-accent-wash text-text" : "border-border text-text-2",
89
+ )}
90
+ >
91
+ {label}{sort.key === key ? (sort.dir === "asc" ? " ↑" : " ↓") : ""}
92
+ </button>
93
+ ))}
94
+ </div>
95
+
96
+ <BottomSheetSeparator />
97
+ <BottomSheetItem onClick={() => setPrefs({ showHidden: !showHidden })}>
98
+ {showHidden ? <Eye className="size-4" /> : <EyeOff className="size-4" />}
99
+ {showHidden ? "Hide hidden files" : "Show hidden files"}
100
+ </BottomSheetItem>
101
+
102
+ <BottomSheetSeparator />
103
+ <BottomSheetItem
104
+ variant="destructive"
105
+ disabled={targets.length === 0}
106
+ onClick={() => actions.confirmPermanentDelete(targets)}
107
+ >
108
+ <Trash2 className="size-4" />
109
+ Delete permanently{targets.length > 1 ? ` (${targets.length})` : ""}
110
+ </BottomSheetItem>
111
+ <BottomSheetItem onClick={() => actions.showProperties(currentFolderEntry(slice))}>
112
+ <Info className="size-4" />
113
+ Properties of this folder
114
+ </BottomSheetItem>
115
+ </BottomSheet>
116
+ );
117
+ }
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Full places list behind the chip strip's "All places" chip: PPM pins, OS pinned folders,
3
+ * known folders and drives, each sectioned exactly like the desktop sidebar — the strip only
4
+ * condenses the *common case*, this sheet is where everything still lives.
5
+ */
6
+
7
+ import type { ReactNode } from "react";
8
+ import { HardDrive, Network, Pin, Usb } from "lucide-react";
9
+ import type { Drive, HostInfo } from "../../../../types/system";
10
+ import { BottomSheet } from "@/components/ui/mobile-bottom-sheet";
11
+ import { useExplorerPinsStore } from "../explorer-pins-store";
12
+ import { FileTypeIcon } from "../icons/file-type-icon";
13
+ import type { SkinVocab } from "../skins/skin-types";
14
+ import { cn } from "@/lib/utils";
15
+
16
+ const ICON_CLASS = "size-4 shrink-0 text-text-subtle";
17
+ const DRIVE_ICON: Record<Drive["kind"], typeof HardDrive> = {
18
+ fixed: HardDrive,
19
+ removable: Usb,
20
+ network: Network,
21
+ unknown: HardDrive,
22
+ };
23
+
24
+ /** Folder-type place rows draw the current skin's folder glyph, same as the desktop sidebar. */
25
+ function folderIcon(): ReactNode {
26
+ return <FileTypeIcon name="" kind="directory" className={ICON_CLASS} />;
27
+ }
28
+
29
+ function Row({
30
+ label, path, active, icon, onNavigate,
31
+ }: {
32
+ label: string; path: string; active: boolean; icon: ReactNode; onNavigate(path: string): void;
33
+ }) {
34
+ return (
35
+ <button
36
+ type="button"
37
+ title={path}
38
+ onClick={() => onNavigate(path)}
39
+ className={cn(
40
+ "flex min-h-[44px] w-full items-center gap-2.5 rounded-lg px-3 text-left text-sm active:bg-surface-elevated",
41
+ active ? "bg-accent-wash text-text" : "text-text-2",
42
+ )}
43
+ >
44
+ {icon}
45
+ <span className="truncate">{label}</span>
46
+ </button>
47
+ );
48
+ }
49
+
50
+ function Section({ title, children }: { title: string; children: ReactNode }) {
51
+ return (
52
+ <div className="pb-2">
53
+ <p className="px-3 pb-1 pt-3 text-[11px] font-semibold uppercase tracking-wide text-text-subtle">{title}</p>
54
+ {children}
55
+ </div>
56
+ );
57
+ }
58
+
59
+ export interface MobilePlacesSheetProps {
60
+ open: boolean;
61
+ onClose(): void;
62
+ host: HostInfo | null;
63
+ currentPath: string;
64
+ vocab: SkinVocab;
65
+ onNavigate(path: string): void;
66
+ }
67
+
68
+ export function MobilePlacesSheet({ open, onClose, host, currentPath, vocab, onNavigate }: MobilePlacesSheetProps) {
69
+ const pins = useExplorerPinsStore((s) => s.pins);
70
+ const isActive = (path: string) => path === currentPath;
71
+
72
+ return (
73
+ <BottomSheet open={open} onClose={onClose} zIndex={65} className="p-1">
74
+ <div className="max-h-[70vh] overflow-y-auto px-1 pb-2">
75
+ {pins.length > 0 && (
76
+ <Section title="Pinned">
77
+ {pins.map((pin) => (
78
+ <Row key={pin.path} label={pin.name} path={pin.path} icon={<Pin className={ICON_CLASS} />} active={isActive(pin.path)} onNavigate={onNavigate} />
79
+ ))}
80
+ </Section>
81
+ )}
82
+
83
+ {host && host.pinned.length > 0 && (
84
+ <Section title={vocab.pinned}>
85
+ {host.pinned.map((entry) => (
86
+ <Row key={entry.path} label={entry.name} path={entry.path} icon={folderIcon()} active={isActive(entry.path)} onNavigate={onNavigate} />
87
+ ))}
88
+ </Section>
89
+ )}
90
+
91
+ {host && (
92
+ <Section title={vocab.known}>
93
+ <Row label={vocab.home} path={host.homedir} icon={folderIcon()} active={isActive(host.homedir)} onNavigate={onNavigate} />
94
+ {host.knownFolders.map((folder) => (
95
+ <Row key={folder.path} label={folder.name} path={folder.path} icon={folderIcon()} active={isActive(folder.path)} onNavigate={onNavigate} />
96
+ ))}
97
+ </Section>
98
+ )}
99
+
100
+ {host && host.drives.length > 0 && (
101
+ <Section title={vocab.drives}>
102
+ {host.drives.map((drive) => {
103
+ const DriveIcon = DRIVE_ICON[drive.kind];
104
+ return (
105
+ <Row
106
+ key={drive.path}
107
+ label={drive.label ? `${drive.label} (${drive.name})` : drive.name}
108
+ path={drive.path}
109
+ icon={<DriveIcon className={ICON_CLASS} />}
110
+ active={isActive(drive.path)}
111
+ onNavigate={onNavigate}
112
+ />
113
+ );
114
+ })}
115
+ </Section>
116
+ )}
117
+ </div>
118
+ </BottomSheet>
119
+ );
120
+ }
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Sidebar → horizontally scrollable "places" chip strip on mobile: PPM pins, the host's own
3
+ * pinned folders, Home, and drives — condensed to fit under the title instead of a side
4
+ * column. The full list (known folders included) lives in a "Places" sheet behind the last
5
+ * chip.
6
+ *
7
+ * `touch-pan-x` keeps the browser's native scroll-chaining to the horizontal axis only, so a
8
+ * drag that starts on a chip does not also read as a vertical page scroll.
9
+ */
10
+
11
+ import { useState, type ComponentType } from "react";
12
+ import { HardDrive, MoreHorizontal, Network, Pin, Usb } from "lucide-react";
13
+ import type { Drive, HostInfo } from "../../../../types/system";
14
+ import { cn } from "@/lib/utils";
15
+ import { useExplorerPinsStore } from "../explorer-pins-store";
16
+ import { FileTypeIcon } from "../icons/file-type-icon";
17
+ import type { SkinVocab } from "../skins/skin-types";
18
+ import { MobilePlacesSheet } from "./mobile-places-sheet";
19
+
20
+ function FolderChipIcon({ className }: { className?: string }) {
21
+ return <FileTypeIcon name="" kind="directory" className={className} />;
22
+ }
23
+
24
+ const DRIVE_ICON: Record<Drive["kind"], ComponentType<{ className?: string }>> = {
25
+ fixed: HardDrive,
26
+ removable: Usb,
27
+ network: Network,
28
+ unknown: HardDrive,
29
+ };
30
+
31
+ interface Chip {
32
+ key: string;
33
+ label: string;
34
+ path: string;
35
+ icon: ComponentType<{ className?: string }>;
36
+ }
37
+
38
+ function Chip({ chip, active, onNavigate }: { chip: Chip; active: boolean; onNavigate(path: string): void }) {
39
+ const Icon = chip.icon;
40
+ return (
41
+ <button
42
+ type="button"
43
+ onClick={() => onNavigate(chip.path)}
44
+ title={chip.path}
45
+ className={cn(
46
+ "flex h-11 shrink-0 items-center gap-1.5 rounded-full border px-3 text-[13px] active:bg-surface-elevated",
47
+ active ? "border-primary bg-accent-wash text-text" : "border-border text-text-2",
48
+ )}
49
+ >
50
+ <Icon className="size-4 shrink-0 text-text-subtle" />
51
+ <span className="max-w-[9rem] truncate">{chip.label}</span>
52
+ </button>
53
+ );
54
+ }
55
+
56
+ export interface MobilePlacesStripProps {
57
+ host: HostInfo | null;
58
+ currentPath: string;
59
+ onNavigate(path: string): void;
60
+ vocab: SkinVocab;
61
+ }
62
+
63
+ export function MobilePlacesStrip({ host, currentPath, onNavigate, vocab }: MobilePlacesStripProps) {
64
+ const pins = useExplorerPinsStore((s) => s.pins);
65
+ const [placesOpen, setPlacesOpen] = useState(false);
66
+
67
+ const chips: Chip[] = [
68
+ ...pins.map((pin): Chip => ({ key: `pin:${pin.path}`, label: pin.name, path: pin.path, icon: Pin })),
69
+ ...(host ? [{ key: "home", label: vocab.home, path: host.homedir, icon: FolderChipIcon }] : []),
70
+ ...(host?.pinned.map((p): Chip => ({ key: `os:${p.path}`, label: p.name, path: p.path, icon: FolderChipIcon })) ?? []),
71
+ ...(host?.drives.map((d): Chip => ({
72
+ key: `drive:${d.path}`,
73
+ label: d.label ? `${d.label} (${d.name})` : d.name,
74
+ path: d.path,
75
+ icon: DRIVE_ICON[d.kind],
76
+ })) ?? []),
77
+ ];
78
+
79
+ return (
80
+ <>
81
+ <div className="flex shrink-0 items-center gap-1.5 overflow-x-auto touch-pan-x border-b border-border px-2 py-1.5">
82
+ {chips.map((chip) => (
83
+ <Chip key={chip.key} chip={chip} active={chip.path === currentPath} onNavigate={onNavigate} />
84
+ ))}
85
+ <button
86
+ type="button"
87
+ aria-label="All places"
88
+ title="All places"
89
+ onClick={() => setPlacesOpen(true)}
90
+ className="flex h-11 shrink-0 items-center gap-1 rounded-full border border-border px-3 text-[13px] text-text-2 active:bg-surface-elevated"
91
+ >
92
+ <MoreHorizontal className="size-4" />
93
+ </button>
94
+ </div>
95
+
96
+ <MobilePlacesSheet
97
+ open={placesOpen}
98
+ onClose={() => setPlacesOpen(false)}
99
+ host={host}
100
+ currentPath={currentPath}
101
+ vocab={vocab}
102
+ onNavigate={(path) => {
103
+ setPlacesOpen(false);
104
+ onNavigate(path);
105
+ }}
106
+ />
107
+ </>
108
+ );
109
+ }