@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,87 @@
1
+ /** Windows Quick Access pinned folders via the Shell COM `Namespace`
2
+ * (`shell:::{679f85cb-...}`), verified live: ~150ms enumeration, and
3
+ * `System.Home.IsPinned` reliably separates pinned entries from "frequent"
4
+ * ones. Shell COM reports `.zip`/`.rar` archives as `IsFolder=true`, so
5
+ * every candidate is re-checked with a real directory stat before it ships. */
6
+ import * as fsp from "node:fs/promises";
7
+ import type { PinnedFolder } from "../../types/system.ts";
8
+ import { defaultRunner, type Runner } from "./spawn-runner.ts";
9
+
10
+ export interface QuickAccessDeps {
11
+ run: Runner;
12
+ isDirectory: (path: string) => Promise<boolean>;
13
+ }
14
+
15
+ const defaultDeps: QuickAccessDeps = {
16
+ run: defaultRunner,
17
+ isDirectory: async (p) => {
18
+ try {
19
+ return (await fsp.stat(p)).isDirectory();
20
+ } catch {
21
+ return false;
22
+ }
23
+ },
24
+ };
25
+
26
+ // `@($result)` forces an array even for 0/1 items — ConvertTo-Json unwraps a
27
+ // single-element pipeline input into a bare object otherwise.
28
+ const QUICK_ACCESS_SCRIPT = `
29
+ $items = (New-Object -ComObject Shell.Application).Namespace('shell:::{679f85cb-0220-4080-b29b-5540cc05aab6}').Items()
30
+ $result = foreach ($i in $items) {
31
+ if ($i.IsFolder) {
32
+ [PSCustomObject]@{ Name = $i.Name; Path = $i.Path; Pinned = $i.ExtendedProperty('System.Home.IsPinned') }
33
+ }
34
+ }
35
+ $arr = @($result)
36
+ $json = $arr | ConvertTo-Json -Compress
37
+ if ($arr.Count -le 1) { $json = '[' + $json + ']' }
38
+ Write-Output $json
39
+ `.trim();
40
+
41
+ interface RawItem {
42
+ Name?: string;
43
+ Path?: string;
44
+ Pinned?: boolean | string | null;
45
+ }
46
+
47
+ function isPinned(v: RawItem["Pinned"]): boolean {
48
+ return v === true || v === "True" || v === "true";
49
+ }
50
+
51
+ /** Read Quick Access pinned folders, appending any failure to the shared `warnings` array (never throws). */
52
+ export async function getWindowsQuickAccessPinned(
53
+ warnings: string[],
54
+ overrides: Partial<QuickAccessDeps> = {},
55
+ ): Promise<PinnedFolder[]> {
56
+ const deps: QuickAccessDeps = { ...defaultDeps, ...overrides };
57
+ try {
58
+ const res = await deps.run(
59
+ ["powershell.exe", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", QUICK_ACCESS_SCRIPT],
60
+ 5000,
61
+ );
62
+ if (res.timedOut || res.code !== 0) {
63
+ warnings.push(`pinned: Quick Access ${res.timedOut ? "timed out" : `exited ${res.code}`}`);
64
+ return [];
65
+ }
66
+
67
+ let raw: RawItem[];
68
+ try {
69
+ const parsed = JSON.parse(res.stdout.trim() || "[]");
70
+ raw = (Array.isArray(parsed) ? parsed : [parsed]).filter((x): x is RawItem => x != null);
71
+ } catch (e) {
72
+ warnings.push(`pinned: Quick Access JSON parse failed (${(e as Error)?.message ?? e})`);
73
+ return [];
74
+ }
75
+
76
+ const pinned: PinnedFolder[] = [];
77
+ for (const item of raw) {
78
+ if (!item.Name || !item.Path || !isPinned(item.Pinned)) continue;
79
+ if (!(await deps.isDirectory(item.Path))) continue; // drops archive-as-folder (.zip/.rar) and stale entries
80
+ pinned.push({ name: item.Name, path: item.Path, source: "quick-access" });
81
+ }
82
+ return pinned;
83
+ } catch (e) {
84
+ warnings.push(`pinned: Quick Access failed (${(e as Error)?.message ?? e})`);
85
+ return [];
86
+ }
87
+ }
@@ -0,0 +1,41 @@
1
+ /** Shared argv-array shell-out for host-info providers. Every provider that
2
+ * needs PowerShell/plutil/findmnt/xdg-user-dir injects a `Runner` so unit
3
+ * tests never spawn a real process — only `defaultRunner` touches `Bun.spawn`. */
4
+ export interface RunResult {
5
+ stdout: string;
6
+ stderr: string;
7
+ /** Process exit code, or null when killed by the timeout. */
8
+ code: number | null;
9
+ timedOut: boolean;
10
+ }
11
+
12
+ export type Runner = (argv: string[], timeoutMs?: number) => Promise<RunResult>;
13
+
14
+ const DEFAULT_TIMEOUT_MS = 5000;
15
+
16
+ /** Real implementation: argv array only (never string-interpolated into a shell), bounded by timeoutMs. */
17
+ export const defaultRunner: Runner = async (argv, timeoutMs = DEFAULT_TIMEOUT_MS) => {
18
+ const proc = Bun.spawn(argv, { stdout: "pipe", stderr: "pipe", stdin: "ignore", windowsHide: true });
19
+ let timedOut = false;
20
+ const killTimer = setTimeout(() => {
21
+ timedOut = true;
22
+ try {
23
+ proc.kill();
24
+ } catch {
25
+ // Process already exited between the timer firing and the kill call.
26
+ }
27
+ }, timeoutMs);
28
+
29
+ try {
30
+ const [stdout, stderr, code] = await Promise.all([
31
+ new Response(proc.stdout).text(),
32
+ new Response(proc.stderr).text(),
33
+ proc.exited,
34
+ ]);
35
+ return { stdout, stderr, code, timedOut };
36
+ } catch (e: any) {
37
+ return { stdout: "", stderr: e?.message ?? String(e), code: null, timedOut };
38
+ } finally {
39
+ clearTimeout(killTimer);
40
+ }
41
+ };
@@ -0,0 +1,26 @@
1
+ /** Shapes returned by the SQLite viewer service, shared by both of its doors. */
2
+
3
+ export interface TableInfo {
4
+ name: string;
5
+ rowCount: number;
6
+ }
7
+
8
+ export interface ColumnInfo {
9
+ cid: number;
10
+ name: string;
11
+ type: string;
12
+ notnull: boolean;
13
+ pk: boolean;
14
+ dflt_value: string | null;
15
+ fk: { table: string; column: string } | null;
16
+ }
17
+
18
+ export interface QueryResult {
19
+ columns: string[];
20
+ rows: Record<string, unknown>[];
21
+ rowsAffected: number;
22
+ changeType: "select" | "modify";
23
+ executionTimeMs: number;
24
+ /** True when a row cap cut the result short. */
25
+ truncated?: boolean;
26
+ }
@@ -1,29 +1,14 @@
1
1
  import { Database } from "bun:sqlite";
2
2
  import { resolve } from "node:path";
3
3
  import { existsSync } from "node:fs";
4
+ import { isPpmDirPath } from "./fs-path-guard.service.ts";
5
+ import { assertNoAttachStatement } from "./fs-ops/sql-statement-guard.ts";
6
+ import { readRows } from "./fs-ops/sql-row-reader.ts";
4
7
 
5
- export interface TableInfo {
6
- name: string;
7
- rowCount: number;
8
- }
9
-
10
- export interface ColumnInfo {
11
- cid: number;
12
- name: string;
13
- type: string;
14
- notnull: boolean;
15
- pk: boolean;
16
- dflt_value: string | null;
17
- fk: { table: string; column: string } | null;
18
- }
8
+ import type { ColumnInfo, QueryResult, TableInfo } from "./sqlite-types.ts";
19
9
 
20
- export interface QueryResult {
21
- columns: string[];
22
- rows: Record<string, unknown>[];
23
- rowsAffected: number;
24
- changeType: "select" | "modify";
25
- executionTimeMs: number;
26
- }
10
+ // Re-exported so existing importers keep using the service as one entry point.
11
+ export type { ColumnInfo, QueryResult, TableInfo };
27
12
 
28
13
  /** Auto-close idle databases after 5 minutes */
29
14
  const IDLE_TIMEOUT_MS = 5 * 60 * 1000;
@@ -36,12 +21,24 @@ interface CachedDb {
36
21
  class SqliteService {
37
22
  private cache = new Map<string, CachedDb>();
38
23
 
39
- /** Resolve db path — supports both project-relative and absolute paths */
24
+ /**
25
+ * Resolve db path — supports both project-relative and absolute paths.
26
+ *
27
+ * Absolute paths are not probed with `existsSync`: they come from the
28
+ * filesystem door, which can point at a dead network mount and would block
29
+ * the event loop here. That door stats the file asynchronously before
30
+ * calling in, and `open()` refuses to create a missing database, so nothing
31
+ * is silently brought into existence either way.
32
+ */
40
33
  private resolvePath(projectPath: string, dbRelPath: string): string {
41
34
  const isAbsolute = /^(\/|[A-Za-z]:[/\\])/.test(dbRelPath);
42
35
  const abs = isAbsolute ? dbRelPath : resolve(projectPath, dbRelPath);
43
36
  if (!isAbsolute && !abs.startsWith(projectPath)) throw new Error("Access denied: path outside project");
44
- if (!existsSync(abs)) throw new Error(`Database not found: ${dbRelPath}`);
37
+ // The PPM directory holds the config database with provider credentials and
38
+ // the auth token. Absolute paths are accepted here, so this door has to
39
+ // refuse it explicitly or a viewer could read the whole secret store.
40
+ if (isPpmDirPath(resolve(abs))) throw new Error("Access denied: PPM directory is not browsable");
41
+ if (!isAbsolute && !existsSync(abs)) throw new Error(`Database not found: ${dbRelPath}`);
45
42
  return abs;
46
43
  }
47
44
 
@@ -53,7 +50,11 @@ class SqliteService {
53
50
  cached.timer = setTimeout(() => this.close(absPath), IDLE_TIMEOUT_MS);
54
51
  return cached.db;
55
52
  }
56
- const db = new Database(absPath);
53
+ // `create: false` a viewer must never bring a database file into
54
+ // existence, least of all at a path the caller chose. `readwrite` has to
55
+ // be spelled out alongside it: the two flags are passed straight to
56
+ // sqlite3_open_v2, which rejects the combination that omits it.
57
+ const db = new Database(absPath, { readwrite: true, create: false });
57
58
  db.exec("PRAGMA journal_mode = WAL");
58
59
  // SQLite defaults FK enforcement off per connection, which would let the
59
60
  // viewer delete rows other clients reject and leave orphaned children.
@@ -122,8 +123,13 @@ class SqliteService {
122
123
  return { columns, rows, total, page, limit };
123
124
  }
124
125
 
125
- /** Execute arbitrary SQL */
126
- executeQuery(projectPath: string, dbPath: string, sql: string): QueryResult {
126
+ /**
127
+ * Execute arbitrary SQL. `maxRows` caps a SELECT result — a foreign database
128
+ * opened through the filesystem door can be arbitrarily large, and reading
129
+ * every row into memory to answer one request is a denial of service.
130
+ */
131
+ executeQuery(projectPath: string, dbPath: string, sql: string, maxRows?: number): QueryResult {
132
+ assertNoAttachStatement(sql);
127
133
  const abs = this.resolvePath(projectPath, dbPath);
128
134
  const db = this.open(abs);
129
135
  const trimmed = sql.trim().toUpperCase();
@@ -132,11 +138,13 @@ class SqliteService {
132
138
 
133
139
  const start = performance.now();
134
140
  if (isSelect) {
135
- const stmt = db.query(sql);
136
- const rows = stmt.all() as Record<string, unknown>[];
141
+ const { rows, truncated } = readRows(db.query(sql), maxRows);
137
142
  const executionTimeMs = Math.round(performance.now() - start);
138
143
  const columns = rows.length > 0 ? Object.keys(rows[0]!) : [];
139
- return { columns, rows, rowsAffected: 0, changeType: "select", executionTimeMs };
144
+ return {
145
+ columns, rows, rowsAffected: 0, changeType: "select", executionTimeMs,
146
+ ...(truncated ? { truncated: true } : {}),
147
+ };
140
148
  }
141
149
 
142
150
  const result = db.run(sql);
@@ -146,6 +154,7 @@ class SqliteService {
146
154
 
147
155
  /** Execute multi-statement SQL script (no result rows returned) */
148
156
  executeScript(projectPath: string, dbPath: string, sql: string): { executionTimeMs: number } {
157
+ assertNoAttachStatement(sql);
149
158
  const abs = this.resolvePath(projectPath, dbPath);
150
159
  const db = this.open(abs);
151
160
  const start = performance.now();
@@ -15,7 +15,7 @@
15
15
  import { existsSync, readdirSync, readFileSync } from "node:fs";
16
16
  import { homedir } from "node:os";
17
17
  import { join } from "node:path";
18
- import type { ChatEvent } from "../types/api.ts";
18
+ import type { ChatEvent } from "../types/chat.ts";
19
19
  import { parseSessionMessage } from "./jsonl-transcript-parser.ts";
20
20
 
21
21
  /**
@@ -0,0 +1,50 @@
1
+ /** Server-side host facts the frontend cannot learn on its own: OS, path
2
+ * separator, home dir, drives/volumes, resolved known folders, and the
3
+ * user's OS-pinned folders (Windows Quick Access, Finder Favorites,
4
+ * GTK/KDE bookmarks). Backs `GET /api/system/host`. */
5
+ export interface HostInfo {
6
+ platform: "win32" | "darwin" | "linux";
7
+ sep: "\\" | "/";
8
+ homedir: string;
9
+ hostname: string;
10
+ drives: Drive[];
11
+ knownFolders: KnownFolder[];
12
+ pinned: PinnedFolder[];
13
+ /** Non-fatal provider failures — a failure never becomes a 500, it becomes text here. */
14
+ warnings: string[];
15
+ }
16
+
17
+ export type DriveKind = "fixed" | "removable" | "network" | "unknown";
18
+
19
+ export interface Drive {
20
+ /** Display label for the mount, e.g. "C:" or "Macintosh HD". */
21
+ name: string;
22
+ /** Filesystem path to browse into, e.g. "C:\\" or "/Volumes/External". */
23
+ path: string;
24
+ kind: DriveKind;
25
+ /** Volume label when the OS reports one (Windows CIM `VolumeName`). */
26
+ label?: string;
27
+ }
28
+
29
+ export type KnownFolderKey =
30
+ | "desktop"
31
+ | "documents"
32
+ | "downloads"
33
+ | "pictures"
34
+ | "music"
35
+ | "videos"
36
+ | "icloud";
37
+
38
+ export interface KnownFolder {
39
+ key: KnownFolderKey;
40
+ name: string;
41
+ path: string;
42
+ }
43
+
44
+ export type PinnedSource = "quick-access" | "finder-favorites" | "gtk" | "kde";
45
+
46
+ export interface PinnedFolder {
47
+ name: string;
48
+ path: string;
49
+ source: PinnedSource;
50
+ }
package/src/web/app.tsx CHANGED
@@ -1,10 +1,11 @@
1
- import { useEffect, useState, useCallback, useRef } from "react";
1
+ import { useEffect, useState, useCallback, useRef, lazy, Suspense } from "react";
2
2
  import { Toaster } from "@/components/ui/sonner";
3
3
  import { TooltipProvider } from "@/components/ui/tooltip";
4
4
  import { PanelLayout } from "@/components/layout/panel-layout";
5
5
  import { TabPool } from "@/components/layout/tab-pool";
6
6
  import { Sidebar } from "@/components/layout/sidebar";
7
7
  import { StatusBar } from "@/components/layout/status-bar";
8
+ import { WindowLayer } from "@/components/floating-window/window-layer";
8
9
  import { MobileNav } from "@/components/layout/mobile-nav";
9
10
  import { MobileDrawer } from "@/components/layout/mobile-drawer";
10
11
  import { ProjectBottomSheet } from "@/components/layout/project-bottom-sheet";
@@ -36,6 +37,13 @@ import { useExtensionWs } from "@/hooks/use-extension-ws";
36
37
  import { useIsMobile } from "@/hooks/use-is-mobile";
37
38
  import { cn } from "@/lib/utils";
38
39
 
40
+ // Lazy: the explorer feature (views, actions, skins, icon map) is the same heavy bundle the
41
+ // desktop floating window already keeps out of the initial chunk (see `WINDOW_CONTENT`) —
42
+ // a static import here would pull all of it into every user's boot, mobile or not.
43
+ const MobileExplorerSheet = lazy(() =>
44
+ import("@/components/os-explorer/mobile/mobile-explorer-sheet").then((m) => ({ default: m.MobileExplorerSheet })),
45
+ );
46
+
39
47
  type AuthState = "checking" | "authenticated" | "unauthenticated";
40
48
 
41
49
  export function App() {
@@ -284,8 +292,9 @@ export function App() {
284
292
  {/* Desktop unified nav rail (wordmark + project switcher + section rail + panel) */}
285
293
  <Sidebar />
286
294
 
287
- {/* Content area — keep-alive per project */}
288
- <div className="flex-1 flex flex-col overflow-hidden">
295
+ {/* Content area — keep-alive per project. `relative` is the positioning context
296
+ the floating window layer measures and clamps its windows against. */}
297
+ <div className="relative flex-1 flex flex-col overflow-hidden">
289
298
  {[...mountedProjects].map((projectName) => (
290
299
  <div
291
300
  key={projectName}
@@ -303,6 +312,8 @@ export function App() {
303
312
  {/* Desktop status bar — spans the working area only (not under the sidebar).
304
313
  Self-gated to desktop via `hidden md:flex`. */}
305
314
  <StatusBar />
315
+ {/* Floating windows — desktop only, self-gated; covers the working area. */}
316
+ <WindowLayer />
306
317
  </div>
307
318
  </div>
308
319
 
@@ -331,6 +342,11 @@ export function App() {
331
342
  {/* Compare Files picker (Mod+Alt+D, palette, context menus) — singleton */}
332
343
  <ComparePicker />
333
344
 
345
+ {/* Mobile full-screen file explorer sheet — lazy + self-gated, loads nothing until opened */}
346
+ <Suspense fallback={null}>
347
+ <MobileExplorerSheet />
348
+ </Suspense>
349
+
334
350
  {/* Global bug report popup */}
335
351
  <BugReportPopup />
336
352
 
@@ -19,6 +19,9 @@ import { useConnections, type Connection } from "../database/use-connections";
19
19
  import { GlideDataGrid } from "../database/glide-data-grid";
20
20
  import type { GridColumnSchema } from "../database/glide-grid-types";
21
21
  import type { DbQueryResult } from "../database/use-database";
22
+ // Single source of truth: the explorer decides whether a double-click can open a file at
23
+ // all from these very sets, so they must not be redeclared here.
24
+ import { AUDIO_EXTS, IMAGE_EXTS, SQLITE_EXTS, VIDEO_EXTS } from "@/components/os-explorer/can-open-in-ppm";
22
25
 
23
26
  const MarkdownRenderer = lazy(() =>
24
27
  import("@/components/shared/markdown-renderer").then((m) => ({ default: m.MarkdownRenderer }))
@@ -30,15 +33,6 @@ const VideoPreview = lazy(() => import("./video-preview").then((m) => ({ default
30
33
  const AudioPreview = lazy(() => import("./audio-preview").then((m) => ({ default: m.AudioPreview })));
31
34
  const DocxPreview = lazy(() => import("./docx-preview").then((m) => ({ default: m.DocxPreview })));
32
35
 
33
- /** Image extensions renderable inline */
34
- const IMAGE_EXTS = new Set(["png", "jpg", "jpeg", "gif", "webp", "svg", "ico"]);
35
- /** Video extensions playable inline */
36
- const VIDEO_EXTS = new Set(["mp4", "webm", "mov", "ogg", "avi", "mkv"]);
37
- /** Audio extensions playable inline */
38
- const AUDIO_EXTS = new Set(["mp3", "wav", "flac", "aac", "m4a", "wma"]);
39
- /** SQLite extensions — redirect to sqlite viewer */
40
- const SQLITE_EXTS = new Set(["db", "sqlite", "sqlite3"]);
41
-
42
36
  function getFileExt(filename: string): string {
43
37
  return filename.split(".").pop()?.toLowerCase() ?? "";
44
38
  }
@@ -16,7 +16,10 @@ import {
16
16
  import { SidebarHeader } from "@/components/ui/sidebar-header";
17
17
  import { copyToClipboard } from "@/lib/clipboard";
18
18
  import { useShallow } from "zustand/react/shallow";
19
- import { useFileStore, loadPersistedExpanded, savePersistedExpanded, type FileNode } from "@/stores/file-store";
19
+ import {
20
+ useFileStore, loadPersistedExpanded, savePersistedExpanded,
21
+ absoluteProjectPath, relativeProjectPath, type FileNode,
22
+ } from "@/stores/file-store";
20
23
  import { useProjectStore } from "@/stores/project-store";
21
24
  import { useTabStore } from "@/stores/tab-store";
22
25
  import { useCompareStore } from "@/stores/compare-store";
@@ -37,12 +40,23 @@ import { InlineTreeInput } from "./inline-tree-input";
37
40
  import { flattenVisibleTree, type InputRow } from "./flatten-visible-tree";
38
41
  import { downloadFile, downloadFolder } from "@/lib/file-download";
39
42
  import { api, projectUrl } from "@/lib/api-client";
43
+ import { openExplorer } from "@/components/os-explorer/open-explorer";
44
+ import { fsChanged, onFsChanged } from "@/components/os-explorer/explorer-store";
45
+ import { dirnameOf } from "@/components/os-explorer/format-file-meta";
46
+ import { useDragAutoScroll } from "@/components/os-explorer/dnd/use-drag-auto-scroll";
47
+ import { useDropTransfer } from "@/components/os-explorer/dnd/use-drop-transfer";
48
+ import { usePathDropTarget } from "@/components/os-explorer/dnd/use-path-drop-target";
40
49
  import { useFileUploadDrag } from "./use-file-upload-drag";
41
50
  import { useTreeKeyboardNav } from "./use-tree-keyboard-nav";
42
51
 
43
52
  /** Synthetic root node for creating files/folders at project root */
44
53
  const ROOT_NODE: FileNode = { name: "", path: "", type: "directory" };
45
54
 
55
+ /** Parent directory of a project-relative path; "" is the project root. */
56
+ function parentDirOfRelative(path: string): string {
57
+ return path.includes("/") ? path.slice(0, path.lastIndexOf("/")) : "";
58
+ }
59
+
46
60
  interface FileTreeProps {
47
61
  onFileOpen?: () => void;
48
62
  }
@@ -116,26 +130,58 @@ export function FileTree({ onFileOpen }: FileTreeProps = {}) {
116
130
  setFocusedPath(filePath);
117
131
  }, [activeProject, setExpanded, loadChildren, setFocusedPath]);
118
132
 
119
- /** Paste clipboard files into a target directory */
133
+ /**
134
+ * Paste clipboard entries into a target directory.
135
+ *
136
+ * The clipboard holds absolute paths and may have been filled by an explorer window, so
137
+ * the source decides the route: entries that live inside this project keep the
138
+ * project-scoped endpoints (relative paths, gitignore-aware), anything else goes through
139
+ * the host filesystem API with a fully-composed destination.
140
+ */
120
141
  const pasteFiles = useCallback(async (targetDir: string) => {
121
142
  if (!activeProject || !clipboard) return;
122
143
  const projectName = activeProject.name;
144
+ const root = activeProject.path;
145
+ // Every source path is an absolute host path (the clipboard is shared with explorer
146
+ // windows), so the host separator implied by the project root also splits them correctly.
147
+ const sep = root.includes("\\") ? "\\" : "/";
123
148
  const endpoint = clipboard.operation === "cut" ? "move" : "copy";
149
+ const touched = new Set<string>([absoluteProjectPath(root, targetDir)]);
124
150
  for (const source of clipboard.paths) {
125
- const name = source.includes("/") ? source.slice(source.lastIndexOf("/") + 1) : source;
126
- const destination = targetDir ? `${targetDir}/${name}` : name;
151
+ const name = source.split(/[/\\]/).filter(Boolean).pop() ?? source;
152
+ const relativeSource = relativeProjectPath(root, source);
153
+ touched.add(dirnameOf(source, sep));
127
154
  try {
128
- await api.post(`${projectUrl(projectName)}/files/${endpoint}`, { source, destination });
155
+ if (relativeSource != null) {
156
+ const destination = targetDir ? `${targetDir}/${name}` : name;
157
+ await api.post(`${projectUrl(projectName)}/files/${endpoint}`, { source: relativeSource, destination });
158
+ } else {
159
+ const destination = absoluteProjectPath(root, targetDir ? `${targetDir}/${name}` : name);
160
+ await api.post(`/api/fs/${endpoint}`, { source, destination });
161
+ }
129
162
  } catch (err) {
130
163
  toast.error(err instanceof Error ? err.message : `Failed to ${endpoint}`);
131
164
  }
132
165
  }
133
166
  if (clipboard.operation === "cut") setClipboard(null);
167
+ // Any explorer window showing the source or destination directory must refresh too.
168
+ fsChanged(...touched);
134
169
  reloadTree();
135
170
  }, [activeProject, clipboard, setClipboard, reloadTree]);
136
171
 
137
172
  const treeContainerRef = useRef<HTMLDivElement>(null);
138
173
 
174
+ /** Absolutise before publishing: the clipboard is shared with the explorer windows. */
175
+ const copyToTreeClipboard = useCallback((relativePaths: string[], operation: "cut" | "copy") => {
176
+ if (!activeProject) return;
177
+ const root = activeProject.path;
178
+ setClipboard({
179
+ paths: relativePaths.map((p) => absoluteProjectPath(root, p)),
180
+ operation,
181
+ origin: { projectName: activeProject.name, root },
182
+ });
183
+ }, [activeProject, setClipboard]);
184
+
139
185
  /** Ctrl+X / Ctrl+C / Ctrl+V — scoped to file tree container focus */
140
186
  const handleClipboardKeyDown = useCallback((e: React.KeyboardEvent) => {
141
187
  if (!activeProject) return;
@@ -145,12 +191,9 @@ export function FileTree({ onFileOpen }: FileTreeProps = {}) {
145
191
  const mod = e.metaKey || e.ctrlKey;
146
192
  if (!mod) return;
147
193
 
148
- if (e.key === "x" && selectedFiles.length > 0) {
149
- e.preventDefault();
150
- setClipboard({ paths: [...selectedFiles], operation: "cut" });
151
- } else if (e.key === "c" && selectedFiles.length > 0) {
194
+ if ((e.key === "x" || e.key === "c") && selectedFiles.length > 0) {
152
195
  e.preventDefault();
153
- setClipboard({ paths: [...selectedFiles], operation: "copy" });
196
+ copyToTreeClipboard(selectedFiles, e.key === "x" ? "cut" : "copy");
154
197
  } else if (e.key === "v" && clipboard) {
155
198
  e.preventDefault();
156
199
  pasteFiles("");
@@ -221,17 +264,46 @@ export function FileTree({ onFileOpen }: FileTreeProps = {}) {
221
264
  };
222
265
  }, [activeProject]);
223
266
 
267
+ // Symmetric with the fsChanged(...) calls this file emits after its own mutations: an
268
+ // explorer window mutating a directory inside this project must invalidate the matching
269
+ // tree node too.
270
+ useEffect(() => {
271
+ if (!activeProject) return;
272
+ const projectName = activeProject.name;
273
+ const root = activeProject.path;
274
+ return onFsChanged((absoluteDir) => {
275
+ const relative = relativeProjectPath(root, absoluteDir);
276
+ if (relative == null) return; // outside this project — not the tree's concern
277
+ const store = useFileStore.getState();
278
+ store.invalidateIndex();
279
+ store.loadIndex(projectName);
280
+ store.invalidateFolder(projectName, relative);
281
+ });
282
+ }, [activeProject]);
283
+
224
284
  const {
225
285
  uploadFiles, isRootDragOver,
226
286
  handleRootDragEnter, handleRootDragLeave, handleRootDragOver, handleRootDrop,
227
287
  } = useFileUploadDrag({ projectName: activeProject?.name, setExpanded });
228
288
 
289
+ // Cross-surface entry drops (from an explorer window, or another project's tree) onto the
290
+ // tree's own empty background — the project root. Uses the same collision-prompt transfer
291
+ // as a paste; separate from `useFileUploadDrag` above, which only ever reacts to OS files.
292
+ const treeRootSep = activeProject?.path.includes("\\") ? "\\" : "/";
293
+ const { run: transferRun, prompts: transferPrompts } = useDropTransfer(treeRootSep);
294
+ const backgroundDrop = usePathDropTarget({
295
+ targetDir: activeProject?.path ?? null,
296
+ run: transferRun,
297
+ disabled: !activeProject,
298
+ });
299
+
229
300
  // Virtualized flat rows: only visible rows are mounted (large dirs stay cheap)
230
301
  const rows = useMemo(
231
302
  () => flattenVisibleTree(tree, expandedPaths, inlineAction),
232
303
  [tree, expandedPaths, inlineAction],
233
304
  );
234
305
  const scrollRef = useRef<HTMLDivElement>(null);
306
+ useDragAutoScroll(scrollRef);
235
307
  const isMobile = useIsMobile();
236
308
  const rowVirtualizer = useVirtualizer({
237
309
  count: rows.length,
@@ -265,6 +337,7 @@ export function FileTree({ onFileOpen }: FileTreeProps = {}) {
265
337
  store.invalidateIndex();
266
338
  store.loadIndex(projectName);
267
339
  store.invalidateFolder(projectName, parentPath);
340
+ fsChanged(absoluteProjectPath(activeProject!.path, parentPath));
268
341
  } else {
269
342
  const type = row.inline.type === "new-file" ? "file" : "directory";
270
343
  const fullPath = row.targetPath ? `${row.targetPath}/${value}` : value;
@@ -273,6 +346,7 @@ export function FileTree({ onFileOpen }: FileTreeProps = {}) {
273
346
  store.invalidateIndex();
274
347
  store.loadIndex(projectName);
275
348
  store.invalidateFolder(projectName, row.targetPath);
349
+ fsChanged(absoluteProjectPath(activeProject!.path, row.targetPath));
276
350
  }
277
351
  }, [activeProject, clearInlineAction]);
278
352
 
@@ -294,14 +368,14 @@ export function FileTree({ onFileOpen }: FileTreeProps = {}) {
294
368
  onFileOpen?.();
295
369
  return;
296
370
  }
297
- if (action === "cut") {
371
+ if (action === "cut" || action === "copy-file") {
298
372
  const paths = selectedFiles.length > 0 && selectedFiles.includes(node.path) ? [...selectedFiles] : [node.path];
299
- setClipboard({ paths, operation: "cut" });
373
+ copyToTreeClipboard(paths, action === "cut" ? "cut" : "copy");
300
374
  return;
301
375
  }
302
- if (action === "copy-file") {
303
- const paths = selectedFiles.length > 0 && selectedFiles.includes(node.path) ? [...selectedFiles] : [node.path];
304
- setClipboard({ paths, operation: "copy" });
376
+ if (action === "open-in-file-explorer") {
377
+ const target = node.type === "directory" ? node.path : parentDirOfRelative(node.path);
378
+ void openExplorer(absoluteProjectPath(activeProject!.path, target));
305
379
  return;
306
380
  }
307
381
  if (action === "paste" && node.type === "directory") {
@@ -417,13 +491,16 @@ export function FileTree({ onFileOpen }: FileTreeProps = {}) {
417
491
  return (
418
492
  <div
419
493
  ref={treeContainerRef}
420
- className={cn("flex flex-col h-full outline-none", isRootDragOver && "bg-primary/5")}
494
+ className={cn("flex flex-col h-full outline-none", (isRootDragOver || backgroundDrop.isOver) && "bg-primary/5")}
421
495
  tabIndex={0}
422
496
  onKeyDown={(e) => { handleClipboardKeyDown(e); handleTreeKeyDown(e); }}
423
- onDragEnter={handleRootDragEnter}
424
- onDragLeave={handleRootDragLeave}
425
- onDragOver={handleRootDragOver}
426
- onDrop={handleRootDrop}
497
+ // Two independent drags share this background: OS files (handleRoot*, unchanged) and
498
+ // a cross-surface entry drag (backgroundDrop, gated on its own MIME) — each ignores
499
+ // the other's kind, so calling both in sequence is safe.
500
+ onDragEnter={(e) => { handleRootDragEnter(e); backgroundDrop.handlers.onDragEnter(e); }}
501
+ onDragLeave={(e) => { handleRootDragLeave(e); backgroundDrop.handlers.onDragLeave(e); }}
502
+ onDragOver={(e) => { handleRootDragOver(e); backgroundDrop.handlers.onDragOver(e); }}
503
+ onDrop={(e) => { handleRootDrop(e); backgroundDrop.handlers.onDrop(e); }}
427
504
  >
428
505
  <SidebarHeader icon={FolderOpen} title="Explorer">
429
506
  <button onClick={() => handleAction("new-file", ROOT_NODE)} title="New File" className={toolbarBtnClass}>
@@ -481,6 +558,7 @@ export function FileTree({ onFileOpen }: FileTreeProps = {}) {
481
558
  onAction={handleAction}
482
559
  onFileDrop={uploadFiles}
483
560
  onFileOpen={onFileOpen}
561
+ transferRun={transferRun}
484
562
  />
485
563
  )}
486
564
  </div>
@@ -514,9 +592,13 @@ export function FileTree({ onFileOpen }: FileTreeProps = {}) {
514
592
  node={actionState.node}
515
593
  projectName={activeProject.name}
516
594
  onClose={() => setActionState(null)}
517
- onRefresh={reloadTree}
595
+ onRefresh={() => {
596
+ fsChanged(absoluteProjectPath(activeProject.path, parentDirOfRelative(actionState.node.path)));
597
+ reloadTree();
598
+ }}
518
599
  />
519
600
  )}
601
+ {transferPrompts}
520
602
  </div>
521
603
  );
522
604
  }