@hienlh/ppm 0.17.57 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (580) hide show
  1. package/.release-run.sh +5 -0
  2. package/CHANGELOG.md +16 -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-BLYgg033.js} +1 -1
  8. package/dist/web/assets/{angular-html-D4YJUMLP.js → angular-html-jfMqUuRd.js} +1 -1
  9. package/dist/web/assets/{angular-ts-CUvq2h3X.js → angular-ts-wytn9PMm.js} +1 -1
  10. package/dist/web/assets/{api-client-COQtP-dB.js → api-client-B9OFv3nY.js} +1 -1
  11. package/dist/web/assets/api-client-CKo60P6G.js +1 -0
  12. package/dist/web/assets/{api-settings-Do0Z0IRN.js → api-settings-DGhshDRg.js} +1 -1
  13. package/dist/web/assets/api-settings-fdkhqLSF.js +1 -0
  14. package/dist/web/assets/{api-themes-DXi8sz-9.js → api-themes-okiyhhp9.js} +1 -1
  15. package/dist/web/assets/{apl-DSANFLx0.js → apl-i-ahKx6O.js} +1 -1
  16. package/dist/web/assets/architecture-PBZL5I3N-B4J2-O8Q.js +1 -0
  17. package/dist/web/assets/{astro-CXIotiNS.js → astro-DVBCJfG6.js} +1 -1
  18. package/dist/web/assets/{audio-preview-D7jNc8mi.js → audio-preview-y8XpCNrh.js} +1 -1
  19. package/dist/web/assets/{blade-BvrP2gPd.js → blade-BHlL8SqU.js} +1 -1
  20. package/dist/web/assets/{bsl-CGf6Xuyu.js → bsl-D-UOfR1F.js} +1 -1
  21. package/dist/web/assets/{cairo-BboZ9qIf.js → cairo-Bn6rucli.js} +1 -1
  22. package/dist/web/assets/can-open-in-ppm-BzVXoV-d.js +1 -0
  23. package/dist/web/assets/chat-tab-BcR-3_6R.js +13 -0
  24. package/dist/web/assets/clock-B1CPBp2g.js +1 -0
  25. package/dist/web/assets/{cobol-D39V6ukp.js → cobol-Byn9FYKu.js} +1 -1
  26. package/dist/web/assets/code-editor-CJ8g7dqm.js +10 -0
  27. package/dist/web/assets/{coffee-DTtK7kKi.js → coffee-DjBl4gm-.js} +1 -1
  28. package/dist/web/assets/{conflict-editor-CpGcSopK.js → conflict-editor-BP7JFZzG.js} +3 -3
  29. package/dist/web/assets/context-menu-BSE5yuMG.js +1 -0
  30. package/dist/web/assets/copy-Su9m2utd.js +1 -0
  31. package/dist/web/assets/{cpp-CnU9hfBq.js → cpp-CKzic-3E.js} +1 -1
  32. package/dist/web/assets/{crystal-DMjCL--0.js → crystal-Bkg-Ly_g.js} +1 -1
  33. package/dist/web/assets/csv-preview-xAwUDNat.js +2 -0
  34. package/dist/web/assets/{data-grid-overlay-editor-Bdc80TJ1.js → data-grid-overlay-editor-DMTCHIG9.js} +1 -1
  35. package/dist/web/assets/{database-viewer-DTMmic31.js → database-viewer-BVLWYPa4.js} +1 -1
  36. package/dist/web/assets/dialog-D__LRQTn.js +1 -0
  37. package/dist/web/assets/diff-viewer-BheSOHfl.js +4 -0
  38. package/dist/web/assets/{docx-preview-CR2ZtvNP.js → docx-preview-CGlMqltP.js} +1 -1
  39. package/dist/web/assets/{edge-CTom35R_.js → edge-DVbZnd3Y.js} +1 -1
  40. package/dist/web/assets/{edit-diff-preview-D1TEK7Jz.js → edit-diff-preview-ClJAJwlL.js} +1 -1
  41. package/dist/web/assets/{elixir-BAX9HSOu.js → elixir-Dwi8Wu7W.js} +1 -1
  42. package/dist/web/assets/{elm-CpD28xsW.js → elm-DtIv-HXJ.js} +1 -1
  43. package/dist/web/assets/{erb-DHX2IIEs.js → erb-D3HEDz1x.js} +1 -1
  44. package/dist/web/assets/{erlang-B_etLcPG.js → erlang-2MjTCzGz.js} +1 -1
  45. package/dist/web/assets/{esm-PmlN1RTs.js → esm-BfqDE09P.js} +1 -1
  46. package/dist/web/assets/explorer-body-DE5339g9.js +2 -0
  47. package/dist/web/assets/explorer-window-content-BrTHAoXM.js +1 -0
  48. package/dist/web/assets/{extension-webview-BTCFWY2A.js → extension-webview-DRq1tWC1.js} +1 -1
  49. package/dist/web/assets/{fortran-fixed-form-CpR0cN7O.js → fortran-fixed-form-BLyAtyPM.js} +1 -1
  50. package/dist/web/assets/{fsharp-DxITxZW5.js → fsharp-Bxbn_gbr.js} +1 -1
  51. package/dist/web/assets/{gdresource-DJ7IvROZ.js → gdresource-sFMY4D2Q.js} +1 -1
  52. package/dist/web/assets/{git-commit-sQznNP43.js → git-commit-BzB9Lio5.js} +1 -1
  53. package/dist/web/assets/git-log-panel-pjyOjchU.js +1 -0
  54. package/dist/web/assets/{git-rebase-DkHvejAR.js → git-rebase-CKSVd848.js} +1 -1
  55. package/dist/web/assets/gitGraph-HDMCJU4V-CtQ3yXN-.js +1 -0
  56. package/dist/web/assets/{glide-data-grid-LinTYl0_.js → glide-data-grid-LX3N4SsJ.js} +9 -9
  57. package/dist/web/assets/{glimmer-js-BwJnDfL-.js → glimmer-js-BY8aExAJ.js} +1 -1
  58. package/dist/web/assets/{glimmer-ts-Coz155XK.js → glimmer-ts-Bak54dBX.js} +1 -1
  59. package/dist/web/assets/{glsl-BED7ZmUZ.js → glsl-nR2eaJQe.js} +1 -1
  60. package/dist/web/assets/{graphql-B_KyE6oz.js → graphql-CGPPFAU6.js} +1 -1
  61. package/dist/web/assets/group-chat-tab-CVQfptkT.js +1 -0
  62. package/dist/web/assets/{hack-sxywdIAQ.js → hack-CXIMX-Ol.js} +1 -1
  63. package/dist/web/assets/{haml-Dts9JlPU.js → haml-tPY-9Pfm.js} +1 -1
  64. package/dist/web/assets/{handlebars-C9HXIMtj.js → handlebars-BuW1LT7T.js} +1 -1
  65. package/dist/web/assets/{html-2p0RvXi4.js → html-bgVcwZ-t.js} +1 -1
  66. package/dist/web/assets/{html-derivative-BiTPuOba.js → html-derivative-D6-IxTgn.js} +1 -1
  67. package/dist/web/assets/{http-CHd2gYPf.js → http-CJ1zyB-h.js} +1 -1
  68. package/dist/web/assets/{hurl-boMPgDm9.js → hurl-CmoT7l3T.js} +1 -1
  69. package/dist/web/assets/{hxml-BQvjiuBt.js → hxml-CztOsL8o.js} +1 -1
  70. package/dist/web/assets/{image-preview-0SGb7_tH.js → image-preview-BDySmiim.js} +1 -1
  71. package/dist/web/assets/index-XoDY9bc0.js +52 -0
  72. package/dist/web/assets/index-YCTraslE.css +2 -0
  73. package/dist/web/assets/info-3K5VOQVL-Cg0mIJn9.js +1 -0
  74. package/dist/web/assets/{jinja-B-zKiwlp.js → jinja-B1pq3jJq.js} +1 -1
  75. package/dist/web/assets/{jison-C2vJYjbJ.js → jison-D8B5pCwA.js} +1 -1
  76. package/dist/web/assets/{julia-DJ8MrQPA.js → julia-D0K3bwpU.js} +1 -1
  77. package/dist/web/assets/{just-BvQHq7VE.js → just-B9Jlddu-.js} +1 -1
  78. package/dist/web/assets/keybindings-store-BE1jVUZW.js +1 -0
  79. package/dist/web/assets/{latex-ChNeshUC.js → latex-BPJSSE6j.js} +1 -1
  80. package/dist/web/assets/{liquid-KNJEBOwW.js → liquid-lv10fo4U.js} +1 -1
  81. package/dist/web/assets/{lua-D8NNsLbC.js → lua-PNuaIBEF.js} +1 -1
  82. package/dist/web/assets/{markdown-renderer-DSxSupDq.js → markdown-renderer-B8Kz5rvP.js} +3 -3
  83. package/dist/web/assets/markdown-renderer-Btync7yg.js +1 -0
  84. package/dist/web/assets/{marko-HGo5Qtz4.js → marko-Du3frD6X.js} +1 -1
  85. package/dist/web/assets/{mdc-omkodQB5.js → mdc-BeDVcFRl.js} +1 -1
  86. package/dist/web/assets/mobile-bottom-sheet-CzRXE6mY.js +1 -0
  87. package/dist/web/assets/mobile-explorer-sheet-CDU8xriw.js +1 -0
  88. package/dist/web/assets/{nextflow-Adq02pv-.js → nextflow-DkcXP71s.js} +1 -1
  89. package/dist/web/assets/{nginx-ByvWruGD.js → nginx-C5n8N_bt.js} +1 -1
  90. package/dist/web/assets/{nim-BrMojX4O.js → nim-CIhyfs-A.js} +1 -1
  91. package/dist/web/assets/notification-store-Bi7VHAy4.js +1 -0
  92. package/dist/web/assets/{number-overlay-editor-CunvWJnN.js → number-overlay-editor-B8P26qGC.js} +1 -1
  93. package/dist/web/assets/packet-RMMSAZCW-D79vAFR_.js +1 -0
  94. package/dist/web/assets/{panel-store-CpKvXNnm.js → panel-store-gbNJvZB_.js} +1 -1
  95. package/dist/web/assets/{pdf-preview-DEn8amrO.js → pdf-preview-CtZWvujY.js} +1 -1
  96. package/dist/web/assets/{perl-Bd_0l-b4.js → perl-Cf31sZYE.js} +1 -1
  97. package/dist/web/assets/{php-D-DLTnWJ.js → php-Dzl_MDIW.js} +1 -1
  98. package/dist/web/assets/pie-UPGHQEXC-DYUOH68U.js +1 -0
  99. package/dist/web/assets/{postgres-viewer-CjrM15gz.js → postgres-viewer-fqMUi0xS.js} +3 -3
  100. package/dist/web/assets/{project-store-D0ALup9Z.js → project-store-D1D1MN03.js} +1 -1
  101. package/dist/web/assets/{pug-D2aO-l2T.js → pug-CxSYHrB3.js} +1 -1
  102. package/dist/web/assets/{qml-CyQVzNXS.js → qml-DlEjGhwN.js} +1 -1
  103. package/dist/web/assets/radar-KQ55EAFF-B1eznNtd.js +1 -0
  104. package/dist/web/assets/{razor-8tvJXsO3.js → razor-KVt5uW14.js} +1 -1
  105. package/dist/web/assets/{rst-C4AZyQQl.js → rst-D5BwNTnw.js} +1 -1
  106. package/dist/web/assets/{ruby-O_z2NTLY.js → ruby-D5fejQwd.js} +1 -1
  107. package/dist/web/assets/{sas-BtnF4zaV.js → sas-Bj7B1CB2.js} +1 -1
  108. package/dist/web/assets/{scss-GAyp5euH.js → scss-BB0CUoCL.js} +1 -1
  109. package/dist/web/assets/settings-store-Bm7uL8VA.js +2 -0
  110. package/dist/web/assets/settings-tab-DxtBG0DW.js +1 -0
  111. package/dist/web/assets/{shaderlab-nM5YZwa5.js → shaderlab-BdjEZUMc.js} +1 -1
  112. package/dist/web/assets/{shellsession-D2OCI4Gv.js → shellsession-Bp1OA1rR.js} +1 -1
  113. package/dist/web/assets/{soy-DrLNhqfp.js → soy-DlYz3wsC.js} +1 -1
  114. package/dist/web/assets/sparkles-BvJ1EGWd.js +1 -0
  115. package/dist/web/assets/{sparql-CsM6F2Yv.js → sparql-CdfII5Jd.js} +1 -1
  116. package/dist/web/assets/{sql-query-editor-CT5M9PKQ.js → sql-query-editor-D2UxRvG7.js} +1 -1
  117. package/dist/web/assets/sqlite-viewer-CTBwzT-r.js +1 -0
  118. package/dist/web/assets/{stata-Bk3cgfXJ.js → stata-BvUA6GUW.js} +1 -1
  119. package/dist/web/assets/{surrealql-Dkz0DPK8.js → surrealql--DLNbTyI.js} +1 -1
  120. package/dist/web/assets/{svelte-23si1KV5.js → svelte-CBfSsG4n.js} +1 -1
  121. package/dist/web/assets/{system-monitor-tab-De3oz9zD.js → system-monitor-tab-Bscf6tzK.js} +1 -1
  122. package/dist/web/assets/{tab-store-BQmDVh9c.js → tab-store-DnNnaAcE.js} +1 -1
  123. package/dist/web/assets/{templ-CPxlP4vF.js → templ-Dv8zpKZW.js} +1 -1
  124. package/dist/web/assets/{terminal-tab-CciNqtMc.js → terminal-tab-CTKU5gaF.js} +3 -3
  125. package/dist/web/assets/{tex-ugJpgYPc.js → tex-CsrOiqh1.js} +1 -1
  126. package/dist/web/assets/tool-cards-xstpayaW.js +7 -0
  127. package/dist/web/assets/treemap-KZPCXAKY-SeI2TxLs.js +1 -0
  128. package/dist/web/assets/{ts-tags-BovP9TMu.js → ts-tags-3T98k4EJ.js} +1 -1
  129. package/dist/web/assets/{twig-B7VUm2Ca.js → twig-u6QZ45FH.js} +1 -1
  130. package/dist/web/assets/{use-blob-url-B3YovXF6.js → use-blob-url-BLRmhlZI.js} +1 -1
  131. package/dist/web/assets/{use-monaco-theme-k-17kQqf.js → use-monaco-theme-DxYstgYa.js} +1 -1
  132. package/dist/web/assets/{vendor-mermaid-CYOBtZtD.js → vendor-mermaid-C977xNeE.js} +1 -1
  133. package/dist/web/assets/{vendor-ui-DMRGWRuc.js → vendor-ui-CXJtBULr.js} +1 -1
  134. package/dist/web/assets/{video-preview-D-S_YnG6.js → video-preview-cfaoRxG3.js} +1 -1
  135. package/dist/web/assets/{vue-m75pWowk.js → vue-CxIsxtwn.js} +1 -1
  136. package/dist/web/assets/{vue-html-odHs7IMn.js → vue-html-BGL5qnuH.js} +1 -1
  137. package/dist/web/assets/{vue-vine-BbBB3V_G.js → vue-vine-DHPh_gMr.js} +1 -1
  138. package/dist/web/assets/{xml-BHzIcDWI.js → xml-C-vM2m7J.js} +1 -1
  139. package/dist/web/assets/{xsl-DTLhN7uu.js → xsl-DvTXNwuO.js} +1 -1
  140. package/dist/web/index.html +26 -23
  141. package/dist/web/sw.js +1 -1
  142. package/docs/codebase-summary.md +10 -1
  143. package/docs/design-guidelines.md +31 -0
  144. package/docs/project-roadmap.md +10 -0
  145. package/docs/system-architecture.md +59 -0
  146. package/package.json +2 -1
  147. package/src/server/index.ts +8 -0
  148. package/src/server/routes/files.ts +2 -2
  149. package/src/server/routes/fs-browse.ts +99 -74
  150. package/src/server/routes/fs-ops.ts +118 -0
  151. package/src/server/routes/fs-sqlite.ts +129 -0
  152. package/src/server/routes/host-info.ts +17 -0
  153. package/src/server/routes/settings.ts +2 -0
  154. package/src/services/download-token.service.ts +21 -5
  155. package/src/services/file.service.ts +8 -12
  156. package/src/services/fs-browse.service.ts +104 -161
  157. package/src/services/fs-ops/fs-content-disposition.ts +17 -0
  158. package/src/services/fs-ops/fs-core-ops.ts +149 -0
  159. package/src/services/fs-ops/fs-error-response.ts +24 -0
  160. package/src/services/fs-ops/fs-git-init.service.ts +23 -0
  161. package/src/services/fs-ops/fs-hidden-names.ts +17 -0
  162. package/src/services/fs-ops/fs-list-files.service.ts +68 -0
  163. package/src/services/fs-ops/fs-ops-copy-move.service.ts +60 -0
  164. package/src/services/fs-ops/fs-ops-mutate.service.ts +59 -0
  165. package/src/services/fs-ops/fs-ops-read-write.service.ts +70 -0
  166. package/src/services/fs-ops/fs-ops-stat.service.ts +109 -0
  167. package/src/services/fs-ops/fs-ops-trash.service.ts +111 -0
  168. package/src/services/fs-ops/fs-real-path.ts +44 -0
  169. package/src/services/fs-ops/sql-row-reader.ts +26 -0
  170. package/src/services/fs-ops/sql-statement-guard.ts +63 -0
  171. package/src/services/fs-path-guard.service.ts +196 -0
  172. package/src/services/host-info/apple-bookmark-decoder.ts +97 -0
  173. package/src/services/host-info/apple-plist-xml-parser.ts +107 -0
  174. package/src/services/host-info/concurrency-limit.ts +20 -0
  175. package/src/services/host-info/host-drives.ts +190 -0
  176. package/src/services/host-info/host-info.service.ts +171 -0
  177. package/src/services/host-info/host-known-folders.ts +180 -0
  178. package/src/services/host-info/pinned-linux-bookmarks.ts +89 -0
  179. package/src/services/host-info/pinned-macos-finder-favorites.ts +175 -0
  180. package/src/services/host-info/pinned-windows-quick-access.ts +87 -0
  181. package/src/services/host-info/spawn-runner.ts +41 -0
  182. package/src/services/sqlite-types.ts +26 -0
  183. package/src/services/sqlite.service.ts +38 -29
  184. package/src/services/subagent-transcript-merger.ts +1 -1
  185. package/src/types/system.ts +50 -0
  186. package/src/web/app.tsx +19 -3
  187. package/src/web/components/editor/code-editor.tsx +3 -9
  188. package/src/web/components/explorer/file-tree.tsx +103 -21
  189. package/src/web/components/explorer/tree-node-context-menu.tsx +6 -1
  190. package/src/web/components/explorer/tree-node.tsx +34 -93
  191. package/src/web/components/explorer/use-file-upload-drag.ts +14 -9
  192. package/src/web/components/explorer/use-tree-row-dnd.ts +206 -0
  193. package/src/web/components/floating-window/default-window-chrome.tsx +71 -0
  194. package/src/web/components/floating-window/floating-window.tsx +167 -0
  195. package/src/web/components/floating-window/use-window-drag.ts +28 -0
  196. package/src/web/components/floating-window/use-window-gesture-context.ts +44 -0
  197. package/src/web/components/floating-window/use-window-keyboard.ts +55 -0
  198. package/src/web/components/floating-window/use-window-resize.ts +30 -0
  199. package/src/web/components/floating-window/window-chrome-contract.ts +38 -0
  200. package/src/web/components/floating-window/window-content-registry.ts +51 -0
  201. package/src/web/components/floating-window/window-geometry.ts +121 -0
  202. package/src/web/components/floating-window/window-layer.tsx +92 -0
  203. package/src/web/components/floating-window/window-persistence.ts +104 -0
  204. package/src/web/components/floating-window/window-resize-handles.tsx +81 -0
  205. package/src/web/components/floating-window/window-store-types.ts +20 -0
  206. package/src/web/components/floating-window/window-store.ts +192 -0
  207. package/src/web/components/layout/command-palette.tsx +10 -0
  208. package/src/web/components/layout/mobile-drawer.tsx +11 -2
  209. package/src/web/components/layout/nav-section-rail.tsx +7 -1
  210. package/src/web/components/os-explorer/actions/explorer-actions-clipboard.ts +186 -0
  211. package/src/web/components/os-explorer/actions/explorer-actions-mutate.ts +109 -0
  212. package/src/web/components/os-explorer/actions/explorer-actions-open.ts +67 -0
  213. package/src/web/components/os-explorer/actions/explorer-actions-transfer-helpers.ts +35 -0
  214. package/src/web/components/os-explorer/actions/use-explorer-actions.ts +205 -0
  215. package/src/web/components/os-explorer/can-open-in-ppm.ts +89 -0
  216. package/src/web/components/os-explorer/dnd/drag-depth-counter.ts +36 -0
  217. package/src/web/components/os-explorer/dnd/drag-ghost.ts +44 -0
  218. package/src/web/components/os-explorer/dnd/drop-target-decision.ts +46 -0
  219. package/src/web/components/os-explorer/dnd/drop-target-style.ts +7 -0
  220. package/src/web/components/os-explorer/dnd/entry-drag-payload.ts +115 -0
  221. package/src/web/components/os-explorer/dnd/entry-drag-state.ts +35 -0
  222. package/src/web/components/os-explorer/dnd/entry-drop-executor.ts +43 -0
  223. package/src/web/components/os-explorer/dnd/use-drag-auto-scroll.ts +70 -0
  224. package/src/web/components/os-explorer/dnd/use-drop-transfer.tsx +82 -0
  225. package/src/web/components/os-explorer/dnd/use-entry-drag-source.ts +73 -0
  226. package/src/web/components/os-explorer/dnd/use-entry-drop-target.ts +150 -0
  227. package/src/web/components/os-explorer/dnd/use-entry-row-dnd.ts +66 -0
  228. package/src/web/components/os-explorer/dnd/use-path-drop-target.ts +30 -0
  229. package/src/web/components/os-explorer/explorer-body.tsx +153 -0
  230. package/src/web/components/os-explorer/explorer-breadcrumb.tsx +108 -0
  231. package/src/web/components/os-explorer/explorer-context-menu.tsx +131 -0
  232. package/src/web/components/os-explorer/explorer-dialogs.tsx +103 -0
  233. package/src/web/components/os-explorer/explorer-modal-shell.tsx +60 -0
  234. package/src/web/components/os-explorer/explorer-pins-store.ts +63 -0
  235. package/src/web/components/os-explorer/explorer-sidebar.tsx +198 -0
  236. package/src/web/components/os-explorer/explorer-sort-menu.tsx +68 -0
  237. package/src/web/components/os-explorer/explorer-status-bar.tsx +35 -0
  238. package/src/web/components/os-explorer/explorer-store.ts +179 -0
  239. package/src/web/components/os-explorer/explorer-toolbar.tsx +139 -0
  240. package/src/web/components/os-explorer/explorer-window-content.tsx +36 -0
  241. package/src/web/components/os-explorer/format-file-meta.ts +74 -0
  242. package/src/web/components/os-explorer/icons/file-type-icon-map.ts +106 -0
  243. package/src/web/components/os-explorer/icons/file-type-icon.tsx +56 -0
  244. package/src/web/components/os-explorer/icons/thumbnail-fetch-semaphore.ts +44 -0
  245. package/src/web/components/os-explorer/icons/thumbnail-image.tsx +124 -0
  246. package/src/web/components/os-explorer/mobile/mobile-bottom-toolbar.tsx +102 -0
  247. package/src/web/components/os-explorer/mobile/mobile-explorer-sheet.tsx +61 -0
  248. package/src/web/components/os-explorer/mobile/mobile-explorer-top-bar.tsx +41 -0
  249. package/src/web/components/os-explorer/mobile/mobile-more-sheet.tsx +117 -0
  250. package/src/web/components/os-explorer/mobile/mobile-places-sheet.tsx +120 -0
  251. package/src/web/components/os-explorer/mobile/mobile-places-strip.tsx +109 -0
  252. package/src/web/components/os-explorer/mobile/mobile-tap-action.ts +19 -0
  253. package/src/web/components/os-explorer/mobile/use-mobile-row-tap.ts +50 -0
  254. package/src/web/components/os-explorer/open-explorer.ts +35 -0
  255. package/src/web/components/os-explorer/properties-dialog.tsx +99 -0
  256. package/src/web/components/os-explorer/skins/explorer-skin-chrome.tsx +17 -0
  257. package/src/web/components/os-explorer/skins/folder-icon-macos.tsx +42 -0
  258. package/src/web/components/os-explorer/skins/folder-icon-windows.tsx +25 -0
  259. package/src/web/components/os-explorer/skins/macos-skin.ts +14 -0
  260. package/src/web/components/os-explorer/skins/macos-window-chrome.tsx +69 -0
  261. package/src/web/components/os-explorer/skins/skin-types.ts +40 -0
  262. package/src/web/components/os-explorer/skins/skins.css +25 -0
  263. package/src/web/components/os-explorer/skins/use-explorer-skin.ts +21 -0
  264. package/src/web/components/os-explorer/skins/windows-skin.ts +11 -0
  265. package/src/web/components/os-explorer/skins/windows-window-chrome.tsx +56 -0
  266. package/src/web/components/os-explorer/sort-and-filter-entries.ts +52 -0
  267. package/src/web/components/os-explorer/sort-options.ts +29 -0
  268. package/src/web/components/os-explorer/toolbar-icon-button.ts +18 -0
  269. package/src/web/components/os-explorer/use-coarse-long-press.ts +97 -0
  270. package/src/web/components/os-explorer/use-explorer-keyboard.ts +124 -0
  271. package/src/web/components/os-explorer/use-explorer-navigation.ts +155 -0
  272. package/src/web/components/os-explorer/use-explorer-open-state.ts +67 -0
  273. package/src/web/components/os-explorer/use-host-info.ts +85 -0
  274. package/src/web/components/os-explorer/views/column-row.tsx +126 -0
  275. package/src/web/components/os-explorer/views/column-view-column.tsx +112 -0
  276. package/src/web/components/os-explorer/views/column-view-mobile.tsx +98 -0
  277. package/src/web/components/os-explorer/views/column-view-preview.tsx +70 -0
  278. package/src/web/components/os-explorer/views/column-view.tsx +204 -0
  279. package/src/web/components/os-explorer/views/compose-interactive-row-props.ts +77 -0
  280. package/src/web/components/os-explorer/views/explorer-view-registry.tsx +55 -0
  281. package/src/web/components/os-explorer/views/icons-view-tile.tsx +158 -0
  282. package/src/web/components/os-explorer/views/icons-view.tsx +188 -0
  283. package/src/web/components/os-explorer/views/inline-name-input.tsx +60 -0
  284. package/src/web/components/os-explorer/views/list-row.tsx +162 -0
  285. package/src/web/components/os-explorer/views/list-view.tsx +177 -0
  286. package/src/web/components/os-explorer/views/use-column-view-state.ts +158 -0
  287. package/src/web/components/os-explorer/views/use-entry-selection.ts +178 -0
  288. package/src/web/components/settings/settings-tab.tsx +29 -2
  289. package/src/web/components/sqlite/sqlite-viewer.tsx +5 -2
  290. package/src/web/components/sqlite/use-sqlite.ts +7 -2
  291. package/src/web/components/ui/adaptive-context-menu.tsx +57 -1
  292. package/src/web/components/ui/file-browser-picker.tsx +1 -18
  293. package/src/web/lib/file-download.ts +6 -4
  294. package/src/web/lib/fs-api.ts +185 -0
  295. package/src/web/stores/file-store.ts +38 -1
  296. package/src/web/stores/settings-store.ts +14 -0
  297. package/src/web/styles/globals.css +1 -0
  298. package/dist/web/assets/api-client-RtNXvS0P.js +0 -1
  299. package/dist/web/assets/api-settings-De--HNHp.js +0 -1
  300. package/dist/web/assets/architecture-PBZL5I3N-DVRYSsQn.js +0 -1
  301. package/dist/web/assets/chat-tab-C5U7a_rP.js +0 -13
  302. package/dist/web/assets/code-editor-CqlzLQ7T.js +0 -10
  303. package/dist/web/assets/context-menu-DIiZdfbE.js +0 -1
  304. package/dist/web/assets/csv-preview-DD94T9ff.js +0 -2
  305. package/dist/web/assets/dialog-HHdNInEH.js +0 -1
  306. package/dist/web/assets/diff-viewer-DyQLYP7J.js +0 -4
  307. package/dist/web/assets/git-log-panel-CrUgsLY5.js +0 -1
  308. package/dist/web/assets/gitGraph-HDMCJU4V-CutkUNY_.js +0 -1
  309. package/dist/web/assets/group-chat-tab-BqNJW7y2.js +0 -1
  310. package/dist/web/assets/index-CVJmlKhp.css +0 -2
  311. package/dist/web/assets/index-I2ik0ZF7.js +0 -50
  312. package/dist/web/assets/info-3K5VOQVL-BpNjgbj9.js +0 -1
  313. package/dist/web/assets/keybindings-store-C46tNxJE.js +0 -1
  314. package/dist/web/assets/markdown-renderer-xzyhJklS.js +0 -1
  315. package/dist/web/assets/notification-store-Dek7ClI2.js +0 -1
  316. package/dist/web/assets/packet-RMMSAZCW-D5IUZ66-.js +0 -1
  317. package/dist/web/assets/pie-UPGHQEXC-CM29eKDR.js +0 -1
  318. package/dist/web/assets/radar-KQ55EAFF-QtJGaSGt.js +0 -1
  319. package/dist/web/assets/settings-store-B3LfpCPa.js +0 -2
  320. package/dist/web/assets/settings-tab-DIuR1VIc.js +0 -1
  321. package/dist/web/assets/sparkles-B3DmG8PJ.js +0 -1
  322. package/dist/web/assets/sqlite-viewer-CEObRQ7n.js +0 -1
  323. package/dist/web/assets/tool-cards-Bdx04yYD.js +0 -7
  324. package/dist/web/assets/treemap-KZPCXAKY-CjUqpVnS.js +0 -1
  325. /package/dist/web/assets/{abap-BM5PMK0-.js → abap-C1K0-wzc.js} +0 -0
  326. /package/dist/web/assets/{actionscript-3-CML-MTNy.js → actionscript-3-CJhQVjLA.js} +0 -0
  327. /package/dist/web/assets/{ada-DzO1tMqV.js → ada-Bt8r9fME.js} +0 -0
  328. /package/dist/web/assets/{andromeeda-C3nkvRhj.js → andromeeda-DF1I8qi3.js} +0 -0
  329. /package/dist/web/assets/{apache-Dg7oWQbs.js → apache-CnlleE6P.js} +0 -0
  330. /package/dist/web/assets/{apex-3O2170Ju.js → apex-j0C6Xmk6.js} +0 -0
  331. /package/dist/web/assets/{applescript-5AlzBqQO.js → applescript-DO7AnO8U.js} +0 -0
  332. /package/dist/web/assets/{apply-theme-BSJDABU3.js → apply-theme-CMQXnUAa.js} +0 -0
  333. /package/dist/web/assets/{ara-BGtOX5Hn.js → ara-i83AydZj.js} +0 -0
  334. /package/dist/web/assets/{asciidoc-CIsC4kkq.js → asciidoc-DbBA6-sF.js} +0 -0
  335. /package/dist/web/assets/{asm-p1-PUkh3.js → asm-B5S47Rgr.js} +0 -0
  336. /package/dist/web/assets/{aurora-x-BPgGL50S.js → aurora-x-De_IaSKw.js} +0 -0
  337. /package/dist/web/assets/{awk-Cj93pRL8.js → awk-CfyLHXqS.js} +0 -0
  338. /package/dist/web/assets/{ayu-dark-C50MYKhw.js → ayu-dark-DTLdZ1Si.js} +0 -0
  339. /package/dist/web/assets/{ayu-light-DyIp0Idy.js → ayu-light-DTOfkzhu.js} +0 -0
  340. /package/dist/web/assets/{ayu-mirage-BAdm8pKU.js → ayu-mirage-DsAECCMv.js} +0 -0
  341. /package/dist/web/assets/{ballerina-DUO9mwyV.js → ballerina-CumGp9A7.js} +0 -0
  342. /package/dist/web/assets/{bat-Cg8hlSct.js → bat-Ca2SBbYN.js} +0 -0
  343. /package/dist/web/assets/{beancount-f8f3BBqB.js → beancount-CqyXBwwF.js} +0 -0
  344. /package/dist/web/assets/{berry-BqVQYZZr.js → berry-yl-kJr_H.js} +0 -0
  345. /package/dist/web/assets/{bibtex-C_q8ygAM.js → bibtex-DoFFszjV.js} +0 -0
  346. /package/dist/web/assets/{bicep-DWS-2uWR.js → bicep-CWPi4nfK.js} +0 -0
  347. /package/dist/web/assets/{bird2-C0pvipi_.js → bird2-DXew-ut0.js} +0 -0
  348. /package/dist/web/assets/{bot-E9mcVZnr.js → bot-BHXjpQ7x.js} +0 -0
  349. /package/dist/web/assets/{c-BQFvJ6Ux.js → c-clbySDvd.js} +0 -0
  350. /package/dist/web/assets/{c3-Da53KeG6.js → c3-B65jvSM1.js} +0 -0
  351. /package/dist/web/assets/{cadence-B5X2vWm3.js → cadence-Cm8tQTHN.js} +0 -0
  352. /package/dist/web/assets/{catppuccin-frappe-Dm3rVY3b.js → catppuccin-frappe-DLeGslh_.js} +0 -0
  353. /package/dist/web/assets/{catppuccin-latte-ZxS-ga_K.js → catppuccin-latte-CR5GeHws.js} +0 -0
  354. /package/dist/web/assets/{catppuccin-macchiato-B0bAzXh0.js → catppuccin-macchiato-p-XgjxhP.js} +0 -0
  355. /package/dist/web/assets/{catppuccin-mocha-BeMC5hqi.js → catppuccin-mocha-B8QOAZLl.js} +0 -0
  356. /package/dist/web/assets/{chevron-right-DeUue2aK.js → chevron-right-CtcHmZg9.js} +0 -0
  357. /package/dist/web/assets/{circle-question-mark-PaJgx-3d.js → circle-question-mark-EVgisosn.js} +0 -0
  358. /package/dist/web/assets/{clarity-BRdirlsw.js → clarity-DPulbvKH.js} +0 -0
  359. /package/dist/web/assets/{clojure-pSLH-KQp.js → clojure-DFuWS20u.js} +0 -0
  360. /package/dist/web/assets/{cmake-DsZu4U4F.js → cmake-DejCqcu4.js} +0 -0
  361. /package/dist/web/assets/{code-TekEnDFM.js → code-Dupqw5Fr.js} +0 -0
  362. /package/dist/web/assets/{codeowners-CtkKTnsH.js → codeowners-BwAyqWIH.js} +0 -0
  363. /package/dist/web/assets/{codeql-BpP1PDrj.js → codeql-DgSwFjqI.js} +0 -0
  364. /package/dist/web/assets/{common-lisp-BX1XgHTt.js → common-lisp-CxIVJCtf.js} +0 -0
  365. /package/dist/web/assets/{coq-D2bX6-8g.js → coq-DLSjSnmf.js} +0 -0
  366. /package/dist/web/assets/{csharp-DcR97P0D.js → csharp-C1HHzXBp.js} +0 -0
  367. /package/dist/web/assets/{css-CIjciIIk.js → css-VXsMY--y.js} +0 -0
  368. /package/dist/web/assets/{csv-DW48Z5d-.js → csv-BEbcJdHz.js} +0 -0
  369. /package/dist/web/assets/{csv-parser-DVHBcvuE.js → csv-parser-DHvoTjMF.js} +0 -0
  370. /package/dist/web/assets/{cue-Byh1xHko.js → cue-BkDnFqUD.js} +0 -0
  371. /package/dist/web/assets/{cypher-TVlMMWma.js → cypher-1cMqojSQ.js} +0 -0
  372. /package/dist/web/assets/{d-CxaScvxf.js → d-BnYjRXtH.js} +0 -0
  373. /package/dist/web/assets/{dark-plus-sn-FFTT1.js → dark-plus-XfbaH7kZ.js} +0 -0
  374. /package/dist/web/assets/{dart-BmpakMe-.js → dart-Ca7s-8hK.js} +0 -0
  375. /package/dist/web/assets/{data-grid-types-BuMHLKXV.js → data-grid-types-DP-ZexEN.js} +0 -0
  376. /package/dist/web/assets/{database-COYAMZaw.js → database-Dj0DxgtX.js} +0 -0
  377. /package/dist/web/assets/{dax-B1aRUMXT.js → dax-CmuOw-mY.js} +0 -0
  378. /package/dist/web/assets/{desktop-C6lD62ar.js → desktop-C2gJ6Ovb.js} +0 -0
  379. /package/dist/web/assets/{diff-CupGmT1a.js → diff-BCEo1Ej7.js} +0 -0
  380. /package/dist/web/assets/{dist-Bi4gRyhr.js → dist-CipvJy9w.js} +0 -0
  381. /package/dist/web/assets/{dist-m_oJqIDk.js → dist-D__U4ZJz.js} +0 -0
  382. /package/dist/web/assets/{docker-DjDr3TJF.js → docker-DN2jlyAh.js} +0 -0
  383. /package/dist/web/assets/{dotenv-QeT1SLTv.js → dotenv-NExMY2kS.js} +0 -0
  384. /package/dist/web/assets/{dracula-yI5kpKp5.js → dracula-B9lbE01y.js} +0 -0
  385. /package/dist/web/assets/{dracula-soft-CYF6cJce.js → dracula-soft-CMBBfKF_.js} +0 -0
  386. /package/dist/web/assets/{dream-maker-TBDKiHrV.js → dream-maker-DEyDwFsd.js} +0 -0
  387. /package/dist/web/assets/{emacs-lisp-B7aZTnJ_.js → emacs-lisp-D_qP2zS6.js} +0 -0
  388. /package/dist/web/assets/{everforest-dark-CN77B1NJ.js → everforest-dark-DbHa2U-p.js} +0 -0
  389. /package/dist/web/assets/{everforest-light-CyFxGMIs.js → everforest-light-BISkJchP.js} +0 -0
  390. /package/dist/web/assets/{fennel-C6ktGUzp.js → fennel-6B22SfJR.js} +0 -0
  391. /package/dist/web/assets/{file-exclamation-point-DBEC-Kst.js → file-exclamation-point-D-ZORzYB.js} +0 -0
  392. /package/dist/web/assets/{fish-Vr62C2vP.js → fish-CubIIvJD.js} +0 -0
  393. /package/dist/web/assets/{fluent-CH3IJUki.js → fluent-DF55zQ09.js} +0 -0
  394. /package/dist/web/assets/{fortran-free-form-BN7VmmxZ.js → fortran-free-form-DvOu8jAt.js} +0 -0
  395. /package/dist/web/assets/{gdscript-BYdRNu_P.js → gdscript-DCPxdLpq.js} +0 -0
  396. /package/dist/web/assets/{gdshader-C2XOq90k.js → gdshader-CTZ381FO.js} +0 -0
  397. /package/dist/web/assets/{genie-B4hkIXNU.js → genie-C_QDtvgU.js} +0 -0
  398. /package/dist/web/assets/{gherkin-CW9NH2DW.js → gherkin-BcvK42tF.js} +0 -0
  399. /package/dist/web/assets/{github-dark-BWCf99kM.js → github-dark-C9afs_4w.js} +0 -0
  400. /package/dist/web/assets/{github-dark-default-DZ1m_6OP.js → github-dark-default-CKGVrl3Z.js} +0 -0
  401. /package/dist/web/assets/{github-dark-dimmed-CUXSQ8bS.js → github-dark-dimmed-C3dQZ_LC.js} +0 -0
  402. /package/dist/web/assets/{github-dark-high-contrast-B6BvU8H-.js → github-dark-high-contrast-DW78QukY.js} +0 -0
  403. /package/dist/web/assets/{github-light-Ch-E92cy.js → github-light-BnP2dznS.js} +0 -0
  404. /package/dist/web/assets/{github-light-default-CKFslV7i.js → github-light-default-C2fPZzPG.js} +0 -0
  405. /package/dist/web/assets/{github-light-high-contrast-DiJVeRlZ.js → github-light-high-contrast-BzIuUFR2.js} +0 -0
  406. /package/dist/web/assets/{gleam-D4dP7V5M.js → gleam-BpeKwEIO.js} +0 -0
  407. /package/dist/web/assets/{gn-DuO2D2eW.js → gn-nUb_Mit0.js} +0 -0
  408. /package/dist/web/assets/{gnuplot-Df_8pFbI.js → gnuplot-D7I0v20p.js} +0 -0
  409. /package/dist/web/assets/{go-BL16cMJO.js → go-D7Qqska7.js} +0 -0
  410. /package/dist/web/assets/{groovy-rfHeCRqy.js → groovy-DS-pne5-.js} +0 -0
  411. /package/dist/web/assets/{gruvbox-dark-hard-CLbDN-E3.js → gruvbox-dark-hard-C0W01fsq.js} +0 -0
  412. /package/dist/web/assets/{gruvbox-dark-medium-Df0L0qje.js → gruvbox-dark-medium-Bqo6fQbg.js} +0 -0
  413. /package/dist/web/assets/{gruvbox-dark-soft-DV6TAcen.js → gruvbox-dark-soft-D-XSM_fu.js} +0 -0
  414. /package/dist/web/assets/{gruvbox-light-hard-CWXUzp9b.js → gruvbox-light-hard-IVAwMJRl.js} +0 -0
  415. /package/dist/web/assets/{gruvbox-light-medium-BOHMe1-7.js → gruvbox-light-medium-38ATiG7i.js} +0 -0
  416. /package/dist/web/assets/{gruvbox-light-soft-DrrS2sdV.js → gruvbox-light-soft-DnZr3c_0.js} +0 -0
  417. /package/dist/web/assets/{haskell-DybKb9uW.js → haskell-uQcywkDH.js} +0 -0
  418. /package/dist/web/assets/{haxe-DLRwZxmt.js → haxe-c6bI4_GT.js} +0 -0
  419. /package/dist/web/assets/{hcl-CusCzwOk.js → hcl-CQXTiEXq.js} +0 -0
  420. /package/dist/web/assets/{hjson-BygWIJ-3.js → hjson-ChETf2bz.js} +0 -0
  421. /package/dist/web/assets/{hlsl-DCKvuEyZ.js → hlsl-BMC4JGoE.js} +0 -0
  422. /package/dist/web/assets/{horizon-DSOVAzyH.js → horizon-BusFlhLW.js} +0 -0
  423. /package/dist/web/assets/{horizon-bright-DKfVjGj_.js → horizon-bright-2oD0Mz7W.js} +0 -0
  424. /package/dist/web/assets/{houston-Ddm8AgpV.js → houston-DdbbjqFg.js} +0 -0
  425. /package/dist/web/assets/{hy-DzZa6alE.js → hy-D_SOuqw9.js} +0 -0
  426. /package/dist/web/assets/{image-gallery-CObzTisL.js → image-gallery-BWFpZAAP.js} +0 -0
  427. /package/dist/web/assets/{imba-aiXalPnK.js → imba-BxLOHO5I.js} +0 -0
  428. /package/dist/web/assets/{ini-CAvrVa5R.js → ini-ox-CFcg0.js} +0 -0
  429. /package/dist/web/assets/{java-PRMSQp24.js → java-Bzo8yIal.js} +0 -0
  430. /package/dist/web/assets/{javascript-DQR6GQKs.js → javascript-DHuz9VXn.js} +0 -0
  431. /package/dist/web/assets/{json-uWUKqOxq.js → json-0_y80FD5.js} +0 -0
  432. /package/dist/web/assets/{json5-R_LXJXL0.js → json5-CEQ50pGM.js} +0 -0
  433. /package/dist/web/assets/{jsonc-tC5zvrPO.js → jsonc-CfrLEAKA.js} +0 -0
  434. /package/dist/web/assets/{jsonl-BAwnWh7U.js → jsonl-fKbrANC2.js} +0 -0
  435. /package/dist/web/assets/{jsonnet-CPj4HCRm.js → jsonnet-DKbkPaQ2.js} +0 -0
  436. /package/dist/web/assets/{jssm-B7HswxOM.js → jssm-Dxxx1Hqu.js} +0 -0
  437. /package/dist/web/assets/{jsx-z4TGELE1.js → jsx-DlJL-Oj3.js} +0 -0
  438. /package/dist/web/assets/{kanagawa-dragon-BpeLlraV.js → kanagawa-dragon-CwN4SV1c.js} +0 -0
  439. /package/dist/web/assets/{kanagawa-lotus-CLnIPAGs.js → kanagawa-lotus-BHltayiE.js} +0 -0
  440. /package/dist/web/assets/{kanagawa-wave-BglPniBb.js → kanagawa-wave-D-KXPjw6.js} +0 -0
  441. /package/dist/web/assets/{kdl-B6blIqLq.js → kdl-DvtoJdFo.js} +0 -0
  442. /package/dist/web/assets/{kotlin-DVAEYiwY.js → kotlin-CTS4MLcx.js} +0 -0
  443. /package/dist/web/assets/{kusto-Ba87977j.js → kusto-HtSE_sKm.js} +0 -0
  444. /package/dist/web/assets/{laserwave-C-NuzoMp.js → laserwave-DaXZG9ge.js} +0 -0
  445. /package/dist/web/assets/{lean-CJ_KLkrJ.js → lean-BYtGUfWx.js} +0 -0
  446. /package/dist/web/assets/{less-Dfn9YC_2.js → less-BoxmU_Mk.js} +0 -0
  447. /package/dist/web/assets/{lib-DTaN9riD.js → lib-Bpbn00xM.js} +0 -0
  448. /package/dist/web/assets/{libesm-DdtEjhIi.js → libesm-tR4XlhMj.js} +0 -0
  449. /package/dist/web/assets/{light-plus-B3BLlguG.js → light-plus-Bt5CyUuo.js} +0 -0
  450. /package/dist/web/assets/{llvm-6UDP3NQb.js → llvm-CSdk9q9V.js} +0 -0
  451. /package/dist/web/assets/{log-QW3dqr1D.js → log-B-kp_3CL.js} +0 -0
  452. /package/dist/web/assets/{logo-DUinJheI.js → logo-DdIsXFDO.js} +0 -0
  453. /package/dist/web/assets/{luau-B1vcBzl8.js → luau-DUBpLlzQ.js} +0 -0
  454. /package/dist/web/assets/{make-CqNL10gS.js → make-B3JVjoW_.js} +0 -0
  455. /package/dist/web/assets/{markdown-BQNMNiYS.js → markdown-Seon0vHs.js} +0 -0
  456. /package/dist/web/assets/{material-theme-rbV8EefR.js → material-theme-Nq-ChX1P.js} +0 -0
  457. /package/dist/web/assets/{material-theme-darker-BZrskvoo.js → material-theme-darker-CPV4ayXJ.js} +0 -0
  458. /package/dist/web/assets/{material-theme-lighter-B0CBBjBw.js → material-theme-lighter-0kyjYqEd.js} +0 -0
  459. /package/dist/web/assets/{material-theme-ocean-CX_CHnQq.js → material-theme-ocean-DGpAp5co.js} +0 -0
  460. /package/dist/web/assets/{material-theme-palenight-ztiOCPbw.js → material-theme-palenight-B0AUn0NG.js} +0 -0
  461. /package/dist/web/assets/{matlab-tHQDNC9G.js → matlab-C2waytxs.js} +0 -0
  462. /package/dist/web/assets/{mdx-D4kZ6c6G.js → mdx-DAis6R6I.js} +0 -0
  463. /package/dist/web/assets/{mermaid-DlR4U4Pe.js → mermaid-_rnT2YGD.js} +0 -0
  464. /package/dist/web/assets/{min-dark-BX-xHAD2.js → min-dark-BSSjchUB.js} +0 -0
  465. /package/dist/web/assets/{min-light-Bxw0_Gqq.js → min-light-DomAFQsa.js} +0 -0
  466. /package/dist/web/assets/{mipsasm-DYyZAum-.js → mipsasm-bBgtvPyG.js} +0 -0
  467. /package/dist/web/assets/{mojo-q9wuEYKR.js → mojo-Cp9BayW0.js} +0 -0
  468. /package/dist/web/assets/{monokai-D9vrJ8JM.js → monokai-CL_9gbmn.js} +0 -0
  469. /package/dist/web/assets/{moonbit-C9roWxMb.js → moonbit-BS_fcr-P.js} +0 -0
  470. /package/dist/web/assets/{move-BvhjG2ic.js → move-C79AH45U.js} +0 -0
  471. /package/dist/web/assets/{narrat-BXGbPwG3.js → narrat-CJ5s3BDf.js} +0 -0
  472. /package/dist/web/assets/{nextflow-groovy-CMy5-Z4k.js → nextflow-groovy-ZHa4JrPJ.js} +0 -0
  473. /package/dist/web/assets/{night-owl-CBQBcbmw.js → night-owl-Jkf9zFQK.js} +0 -0
  474. /package/dist/web/assets/{night-owl-light-CC-brflp.js → night-owl-light-DRPeaG5J.js} +0 -0
  475. /package/dist/web/assets/{nix-B9EAdptu.js → nix-D2Jet9DN.js} +0 -0
  476. /package/dist/web/assets/{nord-DxASd4qG.js → nord-CREVs22j.js} +0 -0
  477. /package/dist/web/assets/{nushell-BsY59i7N.js → nushell-CAiDi_mR.js} +0 -0
  478. /package/dist/web/assets/{objective-c-CoeQAcnP.js → objective-c-CVkmJkC1.js} +0 -0
  479. /package/dist/web/assets/{objective-cpp-BysdFvND.js → objective-cpp-DIKDO1Ly.js} +0 -0
  480. /package/dist/web/assets/{ocaml-CrtQYMMK.js → ocaml-R9qtGQhs.js} +0 -0
  481. /package/dist/web/assets/{odin-B8pz4gIL.js → odin-DTYNQqVr.js} +0 -0
  482. /package/dist/web/assets/{one-dark-pro-3sSHqoqQ.js → one-dark-pro-BSLWUJn5.js} +0 -0
  483. /package/dist/web/assets/{one-light-LUZAvT8g.js → one-light-DbkRXG3t.js} +0 -0
  484. /package/dist/web/assets/{openscad-DcKia-sV.js → openscad-BlPcSnmT.js} +0 -0
  485. /package/dist/web/assets/{pascal-zM5SlmgF.js → pascal-B1Y46ZZB.js} +0 -0
  486. /package/dist/web/assets/{pkl-C_r_zLLt.js → pkl-BhJSJbFv.js} +0 -0
  487. /package/dist/web/assets/{plastic-B3cRtgKD.js → plastic-BzxsCTpi.js} +0 -0
  488. /package/dist/web/assets/{plsql-BzyjheXU.js → plsql-DBba7dyP.js} +0 -0
  489. /package/dist/web/assets/{plus-DcuJuDC3.js → plus-Dm36mgLh.js} +0 -0
  490. /package/dist/web/assets/{po-Dz4OaDNi.js → po-BvgLlnNo.js} +0 -0
  491. /package/dist/web/assets/{poimandres-BBbtYwmR.js → poimandres-DUaI_1JP.js} +0 -0
  492. /package/dist/web/assets/{polar-Che1XC2Z.js → polar-C7uILwJn.js} +0 -0
  493. /package/dist/web/assets/{postcss-DouzwG4F.js → postcss-BgJlpFdw.js} +0 -0
  494. /package/dist/web/assets/{powerquery-YOdcTZmd.js → powerquery-Dn1MIpZu.js} +0 -0
  495. /package/dist/web/assets/{powershell-8ojzYH34.js → powershell-Bo-DYzOS.js} +0 -0
  496. /package/dist/web/assets/{prisma-DCmjceML.js → prisma-CTLs5IdT.js} +0 -0
  497. /package/dist/web/assets/{prolog-ByNNz2pB.js → prolog-QRGun3eG.js} +0 -0
  498. /package/dist/web/assets/{proto-DWMt6Qu1.js → proto-B7PI7apn.js} +0 -0
  499. /package/dist/web/assets/{puppet-osfKyZp4.js → puppet-CBq0tLrP.js} +0 -0
  500. /package/dist/web/assets/{purescript-ClIblkvh.js → purescript-DFewOQb2.js} +0 -0
  501. /package/dist/web/assets/{python-Ctyr4faP.js → python-iWuxKXvT.js} +0 -0
  502. /package/dist/web/assets/{qmldir-CbNgyhnI.js → qmldir-cXiW3AsF.js} +0 -0
  503. /package/dist/web/assets/{qss-LbRHzb21.js → qss-DASwAbBL.js} +0 -0
  504. /package/dist/web/assets/{r-Dvfjp0wc.js → r-TU2eaIPw.js} +0 -0
  505. /package/dist/web/assets/{racket-DI8v8nzD.js → racket-SkRKqrLY.js} +0 -0
  506. /package/dist/web/assets/{raku-CX1q9GW0.js → raku-D31lthkZ.js} +0 -0
  507. /package/dist/web/assets/{react-8hrD3-kv.js → react-DjdYfcEe.js} +0 -0
  508. /package/dist/web/assets/{red-CSz3QEOw.js → red-CTcQZevb.js} +0 -0
  509. /package/dist/web/assets/{reg-FMjhUQE-.js → reg-D8YAsTGf.js} +0 -0
  510. /package/dist/web/assets/{regexp-CCSe1Fc4.js → regexp-CD2uvAWx.js} +0 -0
  511. /package/dist/web/assets/{rel-Ce0UO-OB.js → rel-CAaI-1fg.js} +0 -0
  512. /package/dist/web/assets/{resolve-theme-B9Mgd5GG.js → resolve-theme-B0eotaPq.js} +0 -0
  513. /package/dist/web/assets/{riscv-BFZrdZtB.js → riscv-DR-hJVIj.js} +0 -0
  514. /package/dist/web/assets/{ron-D0t4kvJG.js → ron-CuUCJ0n4.js} +0 -0
  515. /package/dist/web/assets/{rose-pine-HzwmOzHM.js → rose-pine-aFHxW48i.js} +0 -0
  516. /package/dist/web/assets/{rose-pine-dawn-CSp9xAF9.js → rose-pine-dawn-Bqz2sBRY.js} +0 -0
  517. /package/dist/web/assets/{rose-pine-moon-Cnzr2Cw0.js → rose-pine-moon-wqyh0an4.js} +0 -0
  518. /package/dist/web/assets/{rosmsg-DEJ8Snbv.js → rosmsg-CI1ywimO.js} +0 -0
  519. /package/dist/web/assets/{rust-2jBzrC0l.js → rust-p7Imk4G_.js} +0 -0
  520. /package/dist/web/assets/{sass-DD75Izu_.js → sass-C8JfaqCV.js} +0 -0
  521. /package/dist/web/assets/{save-BM4p9_9n.js → save-ignW_jk2.js} +0 -0
  522. /package/dist/web/assets/{scala-DpBKROIu.js → scala-DNxCW1By.js} +0 -0
  523. /package/dist/web/assets/{scheme-DM2jzwUK.js → scheme-BYuUPJoL.js} +0 -0
  524. /package/dist/web/assets/{sdbl-C34Cg6e7.js → sdbl-DbPeJPUr.js} +0 -0
  525. /package/dist/web/assets/{shellscript-DxfzKSgU.js → shellscript-DMiHlR3q.js} +0 -0
  526. /package/dist/web/assets/{shield-check-C3qYSU8a.js → shield-check-BSA9P3nI.js} +0 -0
  527. /package/dist/web/assets/{shield-off-D5tU7gQT.js → shield-off-Cb9J9x_n.js} +0 -0
  528. /package/dist/web/assets/{slack-dark-DIIV3bpD.js → slack-dark-C4xGcQXI.js} +0 -0
  529. /package/dist/web/assets/{slack-ochin-BTkxWjEP.js → slack-ochin-BzgRt_wm.js} +0 -0
  530. /package/dist/web/assets/{smalltalk-Bm0qg6SA.js → smalltalk-LDeL9PYH.js} +0 -0
  531. /package/dist/web/assets/{snazzy-light-ClAuuhn4.js → snazzy-light-CXE7a5j_.js} +0 -0
  532. /package/dist/web/assets/{solarized-dark-93sEWmYT.js → solarized-dark-DgUhY74x.js} +0 -0
  533. /package/dist/web/assets/{solarized-light-C74FvgcO.js → solarized-light-DD9C-H-9.js} +0 -0
  534. /package/dist/web/assets/{solidity-H9fSjkQP.js → solidity-CAbDB4D4.js} +0 -0
  535. /package/dist/web/assets/{splunk-CDTN2iAG.js → splunk-kQREPfIc.js} +0 -0
  536. /package/dist/web/assets/{sql-CoqObhgo.js → sql-CBzeNAD8.js} +0 -0
  537. /package/dist/web/assets/{ssh-config-C3Wv6qRp.js → ssh-config-L1euxrsM.js} +0 -0
  538. /package/dist/web/assets/{stylus-D0uTFXCy.js → stylus-DvBnYniP.js} +0 -0
  539. /package/dist/web/assets/{swift-DtMMgvRI.js → swift-DF5NDQp6.js} +0 -0
  540. /package/dist/web/assets/{synthwave-84-DAroyZBu.js → synthwave-84-DbBhB_Hq.js} +0 -0
  541. /package/dist/web/assets/{system-verilog-Bj7X_lq4.js → system-verilog-D8s2ZW_k.js} +0 -0
  542. /package/dist/web/assets/{systemd-CH8ApCUs.js → systemd-a5-XZJsG.js} +0 -0
  543. /package/dist/web/assets/{table-Di8pv2Y_.js → table-4P8kS7sg.js} +0 -0
  544. /package/dist/web/assets/{talonscript-70lVC3cw.js → talonscript-BLa0URTW.js} +0 -0
  545. /package/dist/web/assets/{tasl-WQR8VslV.js → tasl-DIoV4XcC.js} +0 -0
  546. /package/dist/web/assets/{tcl-caTR_frn.js → tcl-ztYAdXfQ.js} +0 -0
  547. /package/dist/web/assets/{terraform-CVRZ4SZ4.js → terraform-DvpCCiOZ.js} +0 -0
  548. /package/dist/web/assets/{tokyo-night-CDyT1jCI.js → tokyo-night-BkHlYUv0.js} +0 -0
  549. /package/dist/web/assets/{toml-B7D2mIDU.js → toml-acIrxos6.js} +0 -0
  550. /package/dist/web/assets/{tsv-2kdn5dqL.js → tsv-BJB5EVCT.js} +0 -0
  551. /package/dist/web/assets/{tsx-DJfG3fzg.js → tsx-B1MmXGT3.js} +0 -0
  552. /package/dist/web/assets/{turtle-DaQac_UT.js → turtle-DFceTiqd.js} +0 -0
  553. /package/dist/web/assets/{typescript-BLfAIchl.js → typescript-Dz9wU5Y1.js} +0 -0
  554. /package/dist/web/assets/{typespec-ymBaOi_c.js → typespec-CPuYZijj.js} +0 -0
  555. /package/dist/web/assets/{typst-DkvUYMT-.js → typst-CfsCIYv8.js} +0 -0
  556. /package/dist/web/assets/{use-is-mobile-BcuCjH19.js → use-is-mobile-K1a38BwI.js} +0 -0
  557. /package/dist/web/assets/{utils-E0yyGxXt.js → utils-B9H0d3Fh.js} +0 -0
  558. /package/dist/web/assets/{v-D4997FGs.js → v-CGo0jcXA.js} +0 -0
  559. /package/dist/web/assets/{vala-AFg9DmKE.js → vala-AsSR0hrb.js} +0 -0
  560. /package/dist/web/assets/{vb-DNUJ9fOP.js → vb-C7tvbXv1.js} +0 -0
  561. /package/dist/web/assets/{vendor-xterm-QDr0jvpJ.js → vendor-xterm-ClRVIJ-k.js} +0 -0
  562. /package/dist/web/assets/{verilog-BrnAQ3tF.js → verilog--a33ND-p.js} +0 -0
  563. /package/dist/web/assets/{vesper-DCZnFmY0.js → vesper-B_Z0h4pn.js} +0 -0
  564. /package/dist/web/assets/{vhdl-DoK4PjhE.js → vhdl-CWL5dZPN.js} +0 -0
  565. /package/dist/web/assets/{viml-D6EKG66q.js → viml-BglmT14Y.js} +0 -0
  566. /package/dist/web/assets/{vitesse-black-C6KILWdF.js → vitesse-black-RKFsSqQI.js} +0 -0
  567. /package/dist/web/assets/{vitesse-dark-BuSr6OT5.js → vitesse-dark-D7zDWNmc.js} +0 -0
  568. /package/dist/web/assets/{vitesse-light-M-eYyOoe.js → vitesse-light-MAp87W5b.js} +0 -0
  569. /package/dist/web/assets/{vyper-CwSN64Jn.js → vyper-CgCBYg1G.js} +0 -0
  570. /package/dist/web/assets/{wasm-BhQEkzil.js → wasm-BOG7ZeTT.js} +0 -0
  571. /package/dist/web/assets/{wasm-CVlf9Jwb.js → wasm-Bd258nS1.js} +0 -0
  572. /package/dist/web/assets/{wenyan-BC7irgM3.js → wenyan-DpShPKXZ.js} +0 -0
  573. /package/dist/web/assets/{wgsl-W-3UHMDO.js → wgsl-BakcCblK.js} +0 -0
  574. /package/dist/web/assets/{wikitext-T1DoFcm8.js → wikitext-NroRWVAO.js} +0 -0
  575. /package/dist/web/assets/{wit-DIrx_eCI.js → wit-B5CM2pg6.js} +0 -0
  576. /package/dist/web/assets/{wolfram-Cza4b443.js → wolfram-7MTFBJpR.js} +0 -0
  577. /package/dist/web/assets/{x-BsFLg-T8.js → x-BtzqXv1z.js} +0 -0
  578. /package/dist/web/assets/{yaml-DOZ10aZe.js → yaml-Bwr01G2K.js} +0 -0
  579. /package/dist/web/assets/{zenscript-DDAXkseY.js → zenscript-Dj8GATvi.js} +0 -0
  580. /package/dist/web/assets/{zig-CrzwMpQE.js → zig-LAE-fbS-.js} +0 -0
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Preview pane for Column view — the file currently highlighted anywhere in the columns
3
+ * (deepest column, or an earlier one the user clicked into without drilling).
4
+ */
5
+
6
+ import type { FsEntry } from "@/lib/fs-api";
7
+ import { canOpenInPpm, extensionOf, viewerKindOf } from "../can-open-in-ppm";
8
+ import { formatDateTime, formatSize } from "../format-file-meta";
9
+ import { FileTypeIcon } from "../icons/file-type-icon";
10
+ import { ThumbnailImage } from "../icons/thumbnail-image";
11
+
12
+ export interface ColumnViewPreviewProps {
13
+ entry: FsEntry | null;
14
+ onOpen(entry: FsEntry): void;
15
+ }
16
+
17
+ export function ColumnViewPreview({ entry, onOpen }: ColumnViewPreviewProps) {
18
+ if (!entry) {
19
+ return (
20
+ <div className="flex h-full items-center justify-center p-4 text-center text-xs text-text-subtle">
21
+ Select a file to preview
22
+ </div>
23
+ );
24
+ }
25
+
26
+ const isImage = entry.type !== "directory" && viewerKindOf(entry.name) === "image";
27
+
28
+ return (
29
+ <div
30
+ key={entry.path}
31
+ className="flex h-full min-w-0 flex-col items-center gap-3 overflow-y-auto p-4 text-center"
32
+ >
33
+ <div className="flex h-24 w-24 shrink-0 items-center justify-center">
34
+ {isImage ? (
35
+ <ThumbnailImage entry={entry} size={96} />
36
+ ) : (
37
+ <FileTypeIcon name={entry.name} kind={entry.kind} className="size-16" />
38
+ )}
39
+ </div>
40
+ <p className="w-full truncate text-sm font-medium text-text" title={entry.name}>
41
+ {entry.name}
42
+ </p>
43
+ <dl className="w-full space-y-1 text-left text-xs text-text-2">
44
+ <div className="flex justify-between gap-2">
45
+ <dt className="text-text-subtle">Kind</dt>
46
+ <dd className="truncate">{entry.type === "directory" ? "Folder" : extensionOf(entry.name) || "File"}</dd>
47
+ </div>
48
+ {entry.type !== "directory" && (
49
+ <div className="flex justify-between gap-2">
50
+ <dt className="text-text-subtle">Size</dt>
51
+ <dd>{formatSize(entry.size)}</dd>
52
+ </div>
53
+ )}
54
+ <div className="flex justify-between gap-2">
55
+ <dt className="text-text-subtle">Modified</dt>
56
+ <dd className="truncate">{formatDateTime(entry.modified)}</dd>
57
+ </div>
58
+ </dl>
59
+ {canOpenInPpm(entry.name) && (
60
+ <button
61
+ type="button"
62
+ onClick={() => onOpen(entry)}
63
+ className="mt-2 w-full rounded border border-border bg-panel-2 py-1.5 text-xs font-medium text-text can-hover:hover:bg-surface-elevated"
64
+ >
65
+ Open
66
+ </button>
67
+ )}
68
+ </div>
69
+ );
70
+ }
@@ -0,0 +1,204 @@
1
+ /**
2
+ * Column (Miller) view: one fixed-width column per breadcrumb ancestor, plus a preview
3
+ * pane for the selected leaf file. Drilling into a directory — by click or arrow key — is
4
+ * just `nav.go`, the same call the toolbar's back/forward/breadcrumb use, so history and
5
+ * the breadcrumb agree with whatever this view is showing and switching to List/Icons
6
+ * never loses the place.
7
+ */
8
+
9
+ import { useEffect, useMemo, useRef, useState, type KeyboardEvent } from "react";
10
+ import { ContextMenu, ContextMenuTrigger } from "@/components/ui/adaptive-context-menu";
11
+ import { useIsMobile } from "@/hooks/use-is-mobile";
12
+ import type { FsEntry } from "@/lib/fs-api";
13
+ import { useDragAutoScroll } from "../dnd/use-drag-auto-scroll";
14
+ import { ExplorerContextMenu } from "../explorer-context-menu";
15
+ import { ColumnViewColumn } from "./column-view-column";
16
+ import { ColumnViewMobile } from "./column-view-mobile";
17
+ import { ColumnViewPreview } from "./column-view-preview";
18
+ import type { ExplorerViewProps } from "./explorer-view-registry";
19
+ import { useColumnViewState } from "./use-column-view-state";
20
+
21
+ /** Below this window width the preview pane would leave no room for the columns themselves. */
22
+ const NARROW_PREVIEW_THRESHOLD = 720;
23
+
24
+ export function ColumnView({
25
+ slice, entries, actions, selection, hasClipboard, isPinned, nav, backgroundLongPress, rowHeight,
26
+ }: ExplorerViewProps) {
27
+ const paths = useMemo(
28
+ () => (slice.breadcrumbs.length > 0 ? slice.breadcrumbs.map((b) => b.path) : [slice.path]),
29
+ [slice.breadcrumbs, slice.path],
30
+ );
31
+ const { columns } = useColumnViewState(paths, entries, slice.loading);
32
+ const isMobile = useIsMobile();
33
+
34
+ const containerRef = useRef<HTMLDivElement>(null);
35
+ // Horizontal auto-scroll across the Miller strip itself; each column also scrolls
36
+ // vertically on its own (see `column-view-column.tsx`). Harmless on mobile — no drag
37
+ // source ever fires a `dragover` there, since `draggable` is withheld on coarse pointers.
38
+ useDragAutoScroll(containerRef);
39
+ const [focusedIndex, setFocusedIndex] = useState(columns.length - 1);
40
+ const [containerWidth, setContainerWidth] = useState(0);
41
+ const prevLength = useRef(columns.length);
42
+
43
+ useEffect(() => {
44
+ const el = containerRef.current;
45
+ if (!el) return;
46
+ const measure = () => setContainerWidth(el.clientWidth);
47
+ measure();
48
+ const observer = new ResizeObserver(measure);
49
+ observer.observe(el);
50
+ return () => observer.disconnect();
51
+ }, []);
52
+
53
+ // A new column (deeper drill, or a fresh window) moves the keyboard cursor onto it and
54
+ // scrolls it into view — every Miller browser keeps the newest column in frame.
55
+ useEffect(() => {
56
+ if (columns.length === prevLength.current) return;
57
+ prevLength.current = columns.length;
58
+ setFocusedIndex(columns.length - 1);
59
+ const el = containerRef.current;
60
+ if (!el) return;
61
+ const reduceMotion = window.matchMedia?.("(prefers-reduced-motion: reduce)").matches === true;
62
+ el.scrollTo({ left: el.scrollWidth, behavior: reduceMotion ? "auto" : "smooth" });
63
+ }, [columns.length]);
64
+
65
+ // The preview pane follows the shared window selection wherever it lands, even in an
66
+ // earlier column — the deepest column is just the common case.
67
+ const previewEntry: FsEntry | null = useMemo(() => {
68
+ if (!slice.anchor) return null;
69
+ for (const column of columns) {
70
+ const found = column.entries.find((e) => e.path === slice.anchor);
71
+ if (found) return found;
72
+ }
73
+ return null;
74
+ }, [columns, slice.anchor]);
75
+
76
+ const selectedPathFor = (index: number): string | null =>
77
+ index === columns.length - 1 ? slice.anchor : (columns[index + 1]?.path ?? null);
78
+
79
+ const entryAt = (index: number): FsEntry | null => {
80
+ const column = columns[index];
81
+ if (!column) return null;
82
+ const path = selectedPathFor(index);
83
+ return column.entries.find((e) => e.path === path) ?? null;
84
+ };
85
+
86
+ const handleSelect = (index: number, entry: FsEntry) => {
87
+ setFocusedIndex(index);
88
+ if (entry.type === "directory") nav.go(entry.path);
89
+ else selection.selectOnly(entry.path);
90
+ };
91
+
92
+ const handleOpen = (entry: FsEntry) => actions.openEntry(entry);
93
+
94
+ const onKeyDown = (event: KeyboardEvent) => {
95
+ if (event.altKey || event.ctrlKey || event.metaKey) return;
96
+
97
+ if (event.key === "ArrowRight" && focusedIndex < columns.length - 1) {
98
+ event.preventDefault();
99
+ event.stopPropagation();
100
+ setFocusedIndex(focusedIndex + 1);
101
+ return;
102
+ }
103
+ if (event.key === "ArrowLeft" && focusedIndex > 0) {
104
+ event.preventDefault();
105
+ event.stopPropagation();
106
+ setFocusedIndex(focusedIndex - 1);
107
+ return;
108
+ }
109
+ if (event.key === "ArrowUp" || event.key === "ArrowDown") {
110
+ const column = columns[focusedIndex];
111
+ if (!column) return;
112
+ event.preventDefault();
113
+ event.stopPropagation();
114
+ const current = entryAt(focusedIndex);
115
+ const currentIndex = current ? column.entries.findIndex((e) => e.path === current.path) : -1;
116
+ const delta = event.key === "ArrowDown" ? 1 : -1;
117
+ const nextIndex = Math.min(column.entries.length - 1, Math.max(0, currentIndex < 0 ? 0 : currentIndex + delta));
118
+ const next = column.entries[nextIndex];
119
+ if (next) handleSelect(focusedIndex, next);
120
+ return;
121
+ }
122
+ if (event.key === "Enter") {
123
+ const current = entryAt(focusedIndex);
124
+ if (current) {
125
+ event.preventDefault();
126
+ event.stopPropagation();
127
+ handleOpen(current);
128
+ }
129
+ }
130
+ };
131
+
132
+ // Defaults hidden (not shown) before the ResizeObserver's first report — showing it then
133
+ // hiding it a frame later on a narrow window is a worse flash than the reverse.
134
+ const showPreview = containerWidth >= NARROW_PREVIEW_THRESHOLD;
135
+
136
+ // Mobile has no room for a side-by-side Miller strip — see `column-view-mobile.tsx`.
137
+ if (isMobile) {
138
+ return (
139
+ <ColumnViewMobile
140
+ containerRef={containerRef}
141
+ onKeyDown={onKeyDown}
142
+ backgroundLongPress={backgroundLongPress}
143
+ columns={columns}
144
+ focusedIndex={focusedIndex}
145
+ setFocusedIndex={setFocusedIndex}
146
+ selectedPathFor={selectedPathFor}
147
+ slice={slice}
148
+ hasClipboard={hasClipboard}
149
+ isPinned={isPinned}
150
+ actions={actions}
151
+ onSelect={handleSelect}
152
+ onOpen={handleOpen}
153
+ />
154
+ );
155
+ }
156
+
157
+ return (
158
+ <ContextMenu>
159
+ <ContextMenuTrigger asChild>
160
+ <div
161
+ ref={containerRef}
162
+ tabIndex={0}
163
+ data-testid="explorer-columns"
164
+ aria-label="Column view"
165
+ onKeyDown={onKeyDown}
166
+ className="flex h-full min-h-0 w-full overflow-x-auto outline-none"
167
+ {...backgroundLongPress}
168
+ >
169
+ {columns.map((column, index) => (
170
+ <ColumnViewColumn
171
+ key={column.path}
172
+ path={column.path}
173
+ entries={column.entries}
174
+ loading={column.loading}
175
+ error={column.error}
176
+ selectedPath={selectedPathFor(index)}
177
+ isFocused={index === focusedIndex}
178
+ currentDir={slice.path}
179
+ hasClipboard={hasClipboard}
180
+ isPinned={isPinned}
181
+ actions={actions}
182
+ rowHeight={rowHeight}
183
+ onSelect={(entry) => handleSelect(index, entry)}
184
+ onOpen={handleOpen}
185
+ onFocus={() => setFocusedIndex(index)}
186
+ />
187
+ ))}
188
+ {showPreview && (
189
+ <div className="min-w-[240px] flex-1 border-l border-border bg-panel-2/30">
190
+ <ColumnViewPreview entry={previewEntry} onOpen={handleOpen} />
191
+ </div>
192
+ )}
193
+ </div>
194
+ </ContextMenuTrigger>
195
+ <ExplorerContextMenu
196
+ targets={[]}
197
+ currentDir={slice.path}
198
+ hasClipboard={hasClipboard}
199
+ isPinned={isPinned}
200
+ actions={actions}
201
+ />
202
+ </ContextMenu>
203
+ );
204
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Merges the props a parent injects into a row with the row's own props.
3
+ *
4
+ * Radix's `ContextMenuTrigger asChild` clones its child and merges `ref`, `onContextMenu`,
5
+ * `onPointerDown`, `onPointerMove`, `onPointerUp`, `onPointerCancel`, `data-state`,
6
+ * `data-disabled` and `style={{ WebkitTouchCallout: "none", … }}` onto it. When the child is
7
+ * a custom component rather than a DOM element, those land in its props object and a fixed
8
+ * destructuring list silently drops every one it does not name — which is how the injected
9
+ * `style` (the only thing suppressing the native long-press callout on iOS) and Radix's own
10
+ * pointer-based long-press fallback disappeared before reaching the real `<div>`.
11
+ *
12
+ * Rules, in the order a row needs them:
13
+ * - two handlers for the same event chain, injected first, so the parent's behaviour (open
14
+ * the menu) happens before the row's own (select, then stop the event travelling further);
15
+ * - `style` and `className` merge, with the row's own values winning per key;
16
+ * - anything else defined by the row wins outright.
17
+ */
18
+
19
+ import { cn } from "@/lib/utils";
20
+ import type { EntryRowDndProps } from "../dnd/use-entry-row-dnd";
21
+
22
+ type RowProps = Record<string, unknown>;
23
+
24
+ /**
25
+ * The two props every row/tile adds on top of whatever Radix injects: the drag-and-drop
26
+ * handler bag from `useEntryRowDnd`, and whether this row is the currently claimed drop
27
+ * target (draws `DROP_TARGET_CLASS`).
28
+ *
29
+ * Deliberately *not* `extends Record<string, unknown>` — React's `PropsWithoutRef<P>` picks
30
+ * over `keyof P`, and an index signature widens that to `string | number`, which collapses
31
+ * every other named property's type to `unknown` for any component using this as its props
32
+ * type. Radix's own injected props (`onContextMenu`, pointer handlers, `data-state`, `style`,
33
+ * …) still land in the `...rest` destructured alongside this — object rest/spread copies
34
+ * every own enumerable property at runtime regardless of what the static type declares, so
35
+ * they are forwarded correctly whether or not the type says so.
36
+ */
37
+ export interface InjectedRowProps {
38
+ dnd: EntryRowDndProps;
39
+ dropActive: boolean;
40
+ }
41
+
42
+ type Handler = (event: unknown) => void;
43
+
44
+ function isHandlerKey(key: string): boolean {
45
+ return key.startsWith("on") && key.length > 2 && key[2] === key[2]?.toUpperCase();
46
+ }
47
+
48
+ function chain(first: unknown, second: unknown): Handler {
49
+ return (event) => {
50
+ (first as Handler | undefined)?.(event);
51
+ (second as Handler | undefined)?.(event);
52
+ };
53
+ }
54
+
55
+ export function composeInteractiveRowProps<P extends RowProps>(injected: P | undefined, own: P): P {
56
+ if (!injected) return own;
57
+ const merged: RowProps = { ...injected };
58
+
59
+ for (const [key, value] of Object.entries(own)) {
60
+ const existing = merged[key];
61
+ if (isHandlerKey(key) && typeof existing === "function" && typeof value === "function") {
62
+ merged[key] = chain(existing, value);
63
+ continue;
64
+ }
65
+ if (key === "style" && existing && value) {
66
+ merged[key] = { ...(existing as object), ...(value as object) };
67
+ continue;
68
+ }
69
+ if (key === "className") {
70
+ merged[key] = cn(existing as string | undefined, value as string | undefined);
71
+ continue;
72
+ }
73
+ if (value !== undefined) merged[key] = value;
74
+ }
75
+
76
+ return merged as P;
77
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * View mode → component. The body renders whatever is registered for the current mode and
3
+ * falls back to List for a mode that is not implemented yet, so a persisted preference
4
+ * can never leave a window blank.
5
+ *
6
+ * Adding Icons or Column view means adding the component file and one entry here; nothing
7
+ * else in the explorer needs to change. The toolbar offers exactly the registered modes.
8
+ */
9
+
10
+ import type { ComponentType } from "react";
11
+ import type { FsEntry } from "@/lib/fs-api";
12
+ import type { ExplorerActions } from "../actions/use-explorer-actions";
13
+ import type { ExplorerSlice, ViewMode } from "../explorer-store";
14
+ import type { LongPressHandlers } from "../use-coarse-long-press";
15
+ import type { ExplorerNavigation } from "../use-explorer-navigation";
16
+ import type { EntrySelection } from "./use-entry-selection";
17
+ import { ColumnView } from "./column-view";
18
+ import { IconsView } from "./icons-view";
19
+ import { ListView } from "./list-view";
20
+
21
+ export interface ExplorerViewProps {
22
+ windowId: string;
23
+ slice: ExplorerSlice;
24
+ /** Already filtered and sorted — every view shares one order. */
25
+ entries: FsEntry[];
26
+ actions: ExplorerActions;
27
+ selection: EntrySelection;
28
+ /** Message from the last rejected inline commit, shown under the input. */
29
+ inlineError: string | null;
30
+ hasClipboard: boolean;
31
+ isPinned(path: string): boolean;
32
+ /** Row height in px; larger on coarse-pointer devices. */
33
+ rowHeight: number;
34
+ /** Column view drills by navigating like every other entry point (history/breadcrumb stay in sync). */
35
+ nav: ExplorerNavigation;
36
+ /**
37
+ * Long-press handlers for the empty-space background menu. Must land on the same element
38
+ * the view's background `ContextMenuTrigger` wraps — a wrapper further up the tree fires
39
+ * the synthetic `contextmenu` event on itself, and it never bubbles down to a descendant
40
+ * trigger.
41
+ */
42
+ backgroundLongPress: LongPressHandlers;
43
+ }
44
+
45
+ export const EXPLORER_VIEWS: Partial<Record<ViewMode, ComponentType<ExplorerViewProps>>> = {
46
+ list: ListView,
47
+ icons: IconsView,
48
+ columns: ColumnView,
49
+ };
50
+
51
+ export const AVAILABLE_VIEW_MODES = Object.keys(EXPLORER_VIEWS) as ViewMode[];
52
+
53
+ export function viewComponentFor(mode: ViewMode): ComponentType<ExplorerViewProps> {
54
+ return EXPLORER_VIEWS[mode] ?? ListView;
55
+ }
@@ -0,0 +1,158 @@
1
+ /**
2
+ * One tile in the Icons view grid: a large glyph (file-type icon, or an image thumbnail
3
+ * once it scrolls into view) above a two-line truncated label. Selection, long-press and
4
+ * context-menu wiring mirror `ListRow` so switching views never changes what a row/tile
5
+ * can do, only how it looks — including the mobile tap/select-mode branch, see
6
+ * `useMobileRowTap`.
7
+ */
8
+
9
+ import { forwardRef, memo, useMemo, type HTMLAttributes } from "react";
10
+ import { Check } from "lucide-react";
11
+ import { ContextMenu, ContextMenuTrigger } from "@/components/ui/adaptive-context-menu";
12
+ import type { FsEntry } from "@/lib/fs-api";
13
+ import { cn } from "@/lib/utils";
14
+ import { viewerKindOf } from "../can-open-in-ppm";
15
+ import { DROP_TARGET_CLASS } from "../dnd/drop-target-style";
16
+ import { useEntryRowDnd } from "../dnd/use-entry-row-dnd";
17
+ import { ExplorerContextMenu } from "../explorer-context-menu";
18
+ import { FileTypeIcon } from "../icons/file-type-icon";
19
+ import { ThumbnailImage } from "../icons/thumbnail-image";
20
+ import { useMobileRowTap } from "../mobile/use-mobile-row-tap";
21
+ import { useCoarseLongPress } from "../use-coarse-long-press";
22
+ import { composeInteractiveRowProps, type InjectedRowProps } from "./compose-interactive-row-props";
23
+ import type { ExplorerViewProps } from "./explorer-view-registry";
24
+ import { InlineNameInput } from "./inline-name-input";
25
+
26
+ export interface IconsViewTileProps
27
+ extends Pick<ExplorerViewProps, "actions" | "selection" | "hasClipboard" | "isPinned" | "inlineError"> {
28
+ entry: FsEntry;
29
+ currentDir: string;
30
+ selected: boolean;
31
+ focused: boolean;
32
+ cut: boolean;
33
+ renaming: boolean;
34
+ /** Fixed pixel width on desktop; `undefined` on mobile, where a CSS grid column sizes it. */
35
+ tileWidth: number | undefined;
36
+ /** Entries the menu should act on when this tile is right-clicked. */
37
+ menuTargets: FsEntry[];
38
+ }
39
+
40
+ export const IconsViewTile = memo(function IconsViewTile(props: IconsViewTileProps) {
41
+ const { entry, currentDir, hasClipboard, isPinned, actions, menuTargets } = props;
42
+ // A drag carries whatever a context menu would act on: the selection when this tile is
43
+ // part of it, otherwise just this tile.
44
+ const dragPaths = useMemo(() => menuTargets.map((target) => target.path), [menuTargets]);
45
+ const dnd = useEntryRowDnd({ entry, dragPaths, run: actions.transferInto });
46
+ return (
47
+ <ContextMenu>
48
+ <ContextMenuTrigger asChild>
49
+ <IconsViewTileInteractive {...props} dnd={dnd.props} dropActive={dnd.isDropTarget} />
50
+ </ContextMenuTrigger>
51
+ <ExplorerContextMenu
52
+ targets={menuTargets}
53
+ currentDir={currentDir}
54
+ hasClipboard={hasClipboard}
55
+ isPinned={isPinned}
56
+ actions={actions}
57
+ />
58
+ </ContextMenu>
59
+ );
60
+ });
61
+
62
+ /**
63
+ * Rendered inside the tile's own `<ContextMenu>` — see `useMobileRowTap`'s doc comment.
64
+ *
65
+ * `forwardRef` plus `composeInteractiveRowProps` over *everything* injected — not just
66
+ * `onContextMenu` — is required here: `asChild` clones this element and merges its own
67
+ * `onContextMenu` (which actually opens the tile's menu), a `ref`, pointer handlers,
68
+ * `data-state` and a `style` that suppresses the native touch callout, but a component that
69
+ * doesn't accept and forward all of them keeps them from ever touching the real DOM node.
70
+ */
71
+ export const IconsViewTileInteractive = forwardRef<HTMLDivElement, IconsViewTileProps & InjectedRowProps>(
72
+ function IconsViewTileInteractive(
73
+ {
74
+ entry, selected, focused, cut, renaming, tileWidth, actions, selection, inlineError,
75
+ currentDir: _currentDir, hasClipboard: _hasClipboard, isPinned: _isPinned, menuTargets: _menuTargets,
76
+ dnd, dropActive, ...injected
77
+ },
78
+ ref,
79
+ ) {
80
+ const longPress = useCoarseLongPress(() => {
81
+ if (!selected) selection.selectOnly(entry.path);
82
+ });
83
+ const { isMobile, selectMode, handleTap } = useMobileRowTap(actions, selection);
84
+ const isImage = entry.type !== "directory" && viewerKindOf(entry.name) === "image";
85
+
86
+ const props = composeInteractiveRowProps<Record<string, unknown>>(
87
+ { ...injected, ...longPress, ...dnd },
88
+ {
89
+ role: "gridcell",
90
+ "aria-selected": selected,
91
+ "data-testid": "explorer-tile",
92
+ "data-path": entry.path,
93
+ title: entry.name,
94
+ style: tileWidth ? { width: tileWidth } : undefined,
95
+ // Radix's own handler is chained in first (it opens this tile's menu); only
96
+ // afterwards does this stop the event from also reaching the background trigger
97
+ // further up the DOM.
98
+ onContextMenu: (e: React.MouseEvent) => {
99
+ if (!selected) selection.selectOnly(entry.path);
100
+ e.stopPropagation();
101
+ },
102
+ onClick: (e: React.MouseEvent) => {
103
+ if (!handleTap(entry, selected)) selection.onRowClick(entry.path, e);
104
+ },
105
+ onDoubleClick: () => actions.openEntry(entry),
106
+ className: cn(
107
+ "relative flex flex-col items-center gap-1 rounded p-2 text-center select-none",
108
+ !tileWidth && "w-full",
109
+ "can-hover:hover:bg-surface-elevated",
110
+ focused && !selected && "bg-surface-elevated",
111
+ selected && "bg-accent-wash",
112
+ cut && "opacity-40",
113
+ dropActive && DROP_TARGET_CLASS,
114
+ ),
115
+ },
116
+ );
117
+
118
+ return (
119
+ <div ref={ref} {...(props as HTMLAttributes<HTMLDivElement>)}>
120
+ {isMobile && selectMode && (
121
+ <span
122
+ aria-hidden
123
+ className={cn(
124
+ "absolute left-1.5 top-1.5 flex size-5 items-center justify-center rounded border",
125
+ selected ? "border-primary bg-primary text-primary-foreground" : "border-border bg-panel",
126
+ )}
127
+ >
128
+ {selected && <Check className="size-3.5" />}
129
+ </span>
130
+ )}
131
+ <div className="flex h-12 w-12 shrink-0 items-center justify-center">
132
+ {isImage ? (
133
+ <ThumbnailImage entry={entry} size={48} />
134
+ ) : (
135
+ <FileTypeIcon name={entry.name} kind={entry.kind} className="size-12" />
136
+ )}
137
+ </div>
138
+ {renaming ? (
139
+ <InlineNameInput
140
+ initial={entry.name}
141
+ error={inlineError}
142
+ onCommit={(value) => void actions.commitInline(value)}
143
+ onCancel={actions.cancelInline}
144
+ />
145
+ ) : (
146
+ <span
147
+ className={cn(
148
+ "line-clamp-2 w-full break-words text-[12px] leading-tight",
149
+ selected ? "text-text" : "text-text-2",
150
+ )}
151
+ >
152
+ {entry.name}
153
+ </span>
154
+ )}
155
+ </div>
156
+ );
157
+ },
158
+ );