@hienlh/ppm 0.18.1 → 0.18.3

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 (519) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/CLAUDE.md +1 -0
  3. package/assets/skills/ppm/SKILL.md +1 -1
  4. package/assets/skills/ppm/references/http-api.md +6 -1
  5. package/dist/web/assets/{ai-resource-editor-Bmk4P0YS.js → ai-resource-editor-3Zk3gYSZ.js} +1 -1
  6. package/dist/web/assets/{angular-html-C-Kjr43S.js → angular-html-jfMqUuRd.js} +1 -1
  7. package/dist/web/assets/{angular-ts-CaDDbZqm.js → angular-ts-wytn9PMm.js} +1 -1
  8. package/dist/web/assets/api-client-CzABTreM.js +1 -0
  9. package/dist/web/assets/{api-settings-CAhcNpj5.js → api-settings-DfNRPr1q.js} +1 -1
  10. package/dist/web/assets/api-settings-DlcoR5QT.js +1 -0
  11. package/dist/web/assets/{api-themes-CAjg_Bt9.js → api-themes-CEyYK7AK.js} +1 -1
  12. package/dist/web/assets/{apl-B1WsQFAb.js → apl-i-ahKx6O.js} +1 -1
  13. package/dist/web/assets/architecture-PBZL5I3N-Bs7wzj-W.js +1 -0
  14. package/dist/web/assets/arrow-right-DubGFkN8.js +1 -0
  15. package/dist/web/assets/{astro-Bdnz7nVp.js → astro-DVBCJfG6.js} +1 -1
  16. package/dist/web/assets/audio-preview-DG2Tw0cO.js +1 -0
  17. package/dist/web/assets/{blade-B2euh8sS.js → blade-BHlL8SqU.js} +1 -1
  18. package/dist/web/assets/{bsl-GNeMaxd0.js → bsl-D-UOfR1F.js} +1 -1
  19. package/dist/web/assets/button-B4MWPjWj.js +1 -0
  20. package/dist/web/assets/{cairo-D7G0W7-s.js → cairo-Bn6rucli.js} +1 -1
  21. package/dist/web/assets/chat-tab-DvbbVMBi.js +15 -0
  22. package/dist/web/assets/{cobol-Q6YgysnI.js → cobol-Byn9FYKu.js} +1 -1
  23. package/dist/web/assets/code-editor-BKg7Li-L.js +10 -0
  24. package/dist/web/assets/{coffee-BjufWxVc.js → coffee-DjBl4gm-.js} +1 -1
  25. package/dist/web/assets/{conflict-editor-DC3liBm_.js → conflict-editor-CJNtovCh.js} +3 -3
  26. package/dist/web/assets/{context-menu-q-RHHAWz.js → context-menu-DkQdt6YA.js} +1 -1
  27. package/dist/web/assets/{cpp-CqWU0gCR.js → cpp-CKzic-3E.js} +1 -1
  28. package/dist/web/assets/{crystal-ylayVajF.js → crystal-Bkg-Ly_g.js} +1 -1
  29. package/dist/web/assets/{csv-preview-BhCOjgq1.js → csv-preview-BR6gCSZJ.js} +1 -1
  30. package/dist/web/assets/{data-grid-overlay-editor-BzPfsCfd.js → data-grid-overlay-editor-B1mTl3KS.js} +1 -1
  31. package/dist/web/assets/{database-viewer-Z6uLyZVO.js → database-viewer-DzuwuFSr.js} +1 -1
  32. package/dist/web/assets/diff-viewer-DL3_tISz.js +4 -0
  33. package/dist/web/assets/{docx-preview-C_fSjfy_.js → docx-preview-BzgyssBV.js} +1 -1
  34. package/dist/web/assets/download-D9UYwY0-.js +1 -0
  35. package/dist/web/assets/{edge-DX4NxQfh.js → edge-DVbZnd3Y.js} +1 -1
  36. package/dist/web/assets/{edit-diff-preview-DfX-9om9.js → edit-diff-preview-HTZPC9VP.js} +1 -1
  37. package/dist/web/assets/{elixir-CSh3NjbV.js → elixir-Dwi8Wu7W.js} +1 -1
  38. package/dist/web/assets/{elm-DBzwFYq9.js → elm-DtIv-HXJ.js} +1 -1
  39. package/dist/web/assets/{erb-yxVOb8aB.js → erb-D3HEDz1x.js} +1 -1
  40. package/dist/web/assets/{erlang-5XIDkYrM.js → erlang-2MjTCzGz.js} +1 -1
  41. package/dist/web/assets/{esm-DO4el2OJ.js → esm-CZ8B8Ba2.js} +1 -1
  42. package/dist/web/assets/explorer-body-CrrY1HbH.js +2 -0
  43. package/dist/web/assets/explorer-window-content-CvQPeiwU.js +1 -0
  44. package/dist/web/assets/{extension-webview-ePIM2fwM.js → extension-webview-DH1PfQF2.js} +1 -1
  45. package/dist/web/assets/{fortran-fixed-form-D4tE5VWY.js → fortran-fixed-form-BLyAtyPM.js} +1 -1
  46. package/dist/web/assets/{fsharp-By8soj6-.js → fsharp-Bxbn_gbr.js} +1 -1
  47. package/dist/web/assets/{gdresource-DoSwM7Hv.js → gdresource-sFMY4D2Q.js} +1 -1
  48. package/dist/web/assets/{git-commit-_ZiciHIx.js → git-commit-BzB9Lio5.js} +1 -1
  49. package/dist/web/assets/{git-log-panel-BRWmTzX1.js → git-log-panel-gUm4E3wo.js} +1 -1
  50. package/dist/web/assets/{git-rebase-C3Y5N9-s.js → git-rebase-CKSVd848.js} +1 -1
  51. package/dist/web/assets/gitGraph-HDMCJU4V-D7i54kbM.js +1 -0
  52. package/dist/web/assets/glide-data-grid-D8J5lwUP.js +138 -0
  53. package/dist/web/assets/{glimmer-js-BPBTXVlC.js → glimmer-js-BY8aExAJ.js} +1 -1
  54. package/dist/web/assets/{glimmer-ts-BQSZdy_D.js → glimmer-ts-Bak54dBX.js} +1 -1
  55. package/dist/web/assets/{glsl-CbQdxpU-.js → glsl-nR2eaJQe.js} +1 -1
  56. package/dist/web/assets/{graphql-GMlKikpY.js → graphql-CGPPFAU6.js} +1 -1
  57. package/dist/web/assets/group-chat-tab-DihiQPSi.js +1 -0
  58. package/dist/web/assets/{hack-DyshcLec.js → hack-CXIMX-Ol.js} +1 -1
  59. package/dist/web/assets/{haml-rqCqWzsq.js → haml-tPY-9Pfm.js} +1 -1
  60. package/dist/web/assets/{handlebars-BCKwSWjR.js → handlebars-BuW1LT7T.js} +1 -1
  61. package/dist/web/assets/{html-Cg29xPR1.js → html-bgVcwZ-t.js} +1 -1
  62. package/dist/web/assets/{html-derivative-geltIVg8.js → html-derivative-D6-IxTgn.js} +1 -1
  63. package/dist/web/assets/{http-pG7Vp237.js → http-CJ1zyB-h.js} +1 -1
  64. package/dist/web/assets/{hurl-Di3Uu_Mc.js → hurl-CmoT7l3T.js} +1 -1
  65. package/dist/web/assets/{hxml-CRy4lbv8.js → hxml-CztOsL8o.js} +1 -1
  66. package/dist/web/assets/{image-preview-CP82lor8.js → image-preview-DOQBNgwl.js} +1 -1
  67. package/dist/web/assets/index-BtncTUyM.js +52 -0
  68. package/dist/web/assets/index-Bucbpby-.css +2 -0
  69. package/dist/web/assets/info-3K5VOQVL-BV41AzR9.js +1 -0
  70. package/dist/web/assets/{jinja-C8GyVnvd.js → jinja-B1pq3jJq.js} +1 -1
  71. package/dist/web/assets/{jison-BywnqPn6.js → jison-D8B5pCwA.js} +1 -1
  72. package/dist/web/assets/{julia-DTrO3T3B.js → julia-D0K3bwpU.js} +1 -1
  73. package/dist/web/assets/{just-CA3KSHPi.js → just-B9Jlddu-.js} +1 -1
  74. package/dist/web/assets/keybindings-store-AynYQVnV.js +1 -0
  75. package/dist/web/assets/{latex-BC5xUMpc.js → latex-BPJSSE6j.js} +1 -1
  76. package/dist/web/assets/{liquid-Chd8tSyQ.js → liquid-lv10fo4U.js} +1 -1
  77. package/dist/web/assets/{lua-DHaEtwLv.js → lua-PNuaIBEF.js} +1 -1
  78. package/dist/web/assets/markdown-renderer-BBlGpszb.js +1 -0
  79. package/dist/web/assets/{markdown-renderer-DDgl5zDc.js → markdown-renderer-CVGub2iS.js} +3 -3
  80. package/dist/web/assets/{marko-Desx_bC-.js → marko-Du3frD6X.js} +1 -1
  81. package/dist/web/assets/{mdc-DahWHc1t.js → mdc-BeDVcFRl.js} +1 -1
  82. package/dist/web/assets/media-url-C5vA0ha9.js +1 -0
  83. package/dist/web/assets/{mobile-bottom-sheet-BQt45q9T.js → mobile-bottom-sheet-CS3XMwFw.js} +1 -1
  84. package/dist/web/assets/mobile-explorer-sheet-CSgc5JOk.js +1 -0
  85. package/dist/web/assets/{nextflow-3A7lutCA.js → nextflow-DkcXP71s.js} +1 -1
  86. package/dist/web/assets/{nginx-XQda6Hdy.js → nginx-C5n8N_bt.js} +1 -1
  87. package/dist/web/assets/{nim-DHqlpwP1.js → nim-CIhyfs-A.js} +1 -1
  88. package/dist/web/assets/notification-store-1ksB0YwR.js +1 -0
  89. package/dist/web/assets/{number-overlay-editor-DgZtViPO.js → number-overlay-editor-NgOsWJ7x.js} +1 -1
  90. package/dist/web/assets/packet-RMMSAZCW-CaJoP44m.js +1 -0
  91. package/dist/web/assets/{panel-store-s6Qx49RK.js → panel-store-DCD82zc1.js} +1 -1
  92. package/dist/web/assets/{pdf-preview-BXDOe7bG.js → pdf-preview-DSAkWbxY.js} +1 -1
  93. package/dist/web/assets/{perl-M-ncjACb.js → perl-Cf31sZYE.js} +1 -1
  94. package/dist/web/assets/{php-CKIajrZi.js → php-Dzl_MDIW.js} +1 -1
  95. package/dist/web/assets/pie-UPGHQEXC-DSGZmNw2.js +1 -0
  96. package/dist/web/assets/plus-DmjC2iRB.js +1 -0
  97. package/dist/web/assets/{postgres-viewer-DkHYV33h.js → postgres-viewer-DOiM_5Nv.js} +3 -3
  98. package/dist/web/assets/{project-store-DyaEpHVG.js → project-store-B8BkHXy5.js} +1 -1
  99. package/dist/web/assets/{pug-DO0u_gO4.js → pug-CxSYHrB3.js} +1 -1
  100. package/dist/web/assets/{qml-UEje1PAA.js → qml-DlEjGhwN.js} +1 -1
  101. package/dist/web/assets/radar-KQ55EAFF-Be4VUTs0.js +1 -0
  102. package/dist/web/assets/{razor-lCs5T1Ib.js → razor-KVt5uW14.js} +1 -1
  103. package/dist/web/assets/resolve-theme-CJvkL8hU.js +1 -0
  104. package/dist/web/assets/{rst-CZX_O0z4.js → rst-D5BwNTnw.js} +1 -1
  105. package/dist/web/assets/{ruby-idycVAyE.js → ruby-D5fejQwd.js} +1 -1
  106. package/dist/web/assets/run-in-terminal-B8TDtaM_.js +1 -0
  107. package/dist/web/assets/{sas-o5gTfUwp.js → sas-Bj7B1CB2.js} +1 -1
  108. package/dist/web/assets/{scss-CRTk3iBQ.js → scss-BB0CUoCL.js} +1 -1
  109. package/dist/web/assets/send-to-chat-CuBAVC8l.js +1 -0
  110. package/dist/web/assets/{settings-store-ChqHdT2V.js → settings-store-CY-4_U3H.js} +2 -2
  111. package/dist/web/assets/settings-tab-D_oruJB9.js +1 -0
  112. package/dist/web/assets/{shaderlab-3e5ln8ci.js → shaderlab-BdjEZUMc.js} +1 -1
  113. package/dist/web/assets/{shellsession-CGQjQ75I.js → shellsession-Bp1OA1rR.js} +1 -1
  114. package/dist/web/assets/{soy-CxY0VwX2.js → soy-DlYz3wsC.js} +1 -1
  115. package/dist/web/assets/{sparql-CBAGAFuq.js → sparql-CdfII5Jd.js} +1 -1
  116. package/dist/web/assets/sql-query-editor-PWpuw-O5.js +1 -0
  117. package/dist/web/assets/{sqlite-viewer-Cpq06-nz.js → sqlite-viewer-1S6cwVWa.js} +1 -1
  118. package/dist/web/assets/{stata-BoezhOvk.js → stata-BvUA6GUW.js} +1 -1
  119. package/dist/web/assets/{surrealql-CHEzAhu7.js → surrealql--DLNbTyI.js} +1 -1
  120. package/dist/web/assets/{svelte-y6xtu2zX.js → svelte-CBfSsG4n.js} +1 -1
  121. package/dist/web/assets/system-monitor-tab-U0Jh9-2v.js +1 -0
  122. package/dist/web/assets/{tab-store-C42sktPo.js → tab-store-DAsvpx4t.js} +1 -1
  123. package/dist/web/assets/team-member-window-content-DSZL4GIj.js +1 -0
  124. package/dist/web/assets/{templ-DbG2y5yD.js → templ-Dv8zpKZW.js} +1 -1
  125. package/dist/web/assets/terminal-tab-uVhX8AGG.js +3 -0
  126. package/dist/web/assets/{tex-CeKQhrQG.js → tex-CsrOiqh1.js} +1 -1
  127. package/dist/web/assets/tool-cards-QuTCNfYR.js +8 -0
  128. package/dist/web/assets/treemap-KZPCXAKY-Cu2zgbxU.js +1 -0
  129. package/dist/web/assets/{ts-tags-iKKOXJjG.js → ts-tags-3T98k4EJ.js} +1 -1
  130. package/dist/web/assets/{twig-DK8Vrp-G.js → twig-u6QZ45FH.js} +1 -1
  131. package/dist/web/assets/{use-blob-url-DYtOHK7u.js → use-blob-url-BmlYYZ8L.js} +1 -1
  132. package/dist/web/assets/use-is-mobile-B8FA4kQf.js +1 -0
  133. package/dist/web/assets/use-monaco-theme-DvmINdhI.js +1 -0
  134. package/dist/web/assets/use-websocket-DoZdQS0G.js +1 -0
  135. package/dist/web/assets/{vendor-mermaid-C977xNeE.js → vendor-mermaid-Bg4vE8QA.js} +1 -1
  136. package/dist/web/assets/video-preview-CzU09X0C.js +1 -0
  137. package/dist/web/assets/{vue-JkIEObMV.js → vue-CxIsxtwn.js} +1 -1
  138. package/dist/web/assets/{vue-html-D8SqnU86.js → vue-html-BGL5qnuH.js} +1 -1
  139. package/dist/web/assets/{vue-vine-DJj7N4WD.js → vue-vine-DHPh_gMr.js} +1 -1
  140. package/dist/web/assets/{xml-C-00w9Rv.js → xml-C-vM2m7J.js} +1 -1
  141. package/dist/web/assets/{xsl-CVdCwPHV.js → xsl-DvTXNwuO.js} +1 -1
  142. package/dist/web/index.html +21 -20
  143. package/dist/web/sw.js +1 -1
  144. package/docs/codebase-summary.md +1 -0
  145. package/package.json +1 -1
  146. package/scripts/dev-quick-tunnel-retry.mjs +51 -0
  147. package/src/providers/claude-agent-sdk.ts +6 -0
  148. package/src/server/helpers/media-route-handlers.ts +66 -0
  149. package/src/server/helpers/range-file-response.ts +86 -0
  150. package/src/server/index.ts +24 -17
  151. package/src/server/middleware/auth.ts +3 -2
  152. package/src/server/routes/files.ts +40 -7
  153. package/src/server/routes/fs-browse.ts +43 -5
  154. package/src/server/routes/fs-upload.ts +32 -0
  155. package/src/server/routes/teams.ts +34 -0
  156. package/src/server/ws/chat.ts +64 -16
  157. package/src/server/ws/team-inbox-watcher.ts +61 -22
  158. package/src/server/ws/terminal.ts +24 -6
  159. package/src/services/fs-ops/fs-ops-upload.service.ts +120 -0
  160. package/src/services/media-transcode/ffmpeg-capabilities.ts +89 -0
  161. package/src/services/media-transcode/media-probe.ts +63 -0
  162. package/src/services/media-transcode/transcode-stream.ts +129 -0
  163. package/src/services/subagent-transcript-merger.ts +1 -1
  164. package/src/services/team-member-activity/member-activity.service.ts +117 -0
  165. package/src/services/team-member-activity/outbound-message-scanner.ts +121 -0
  166. package/src/services/team-member-activity/subagent-transcript-index.ts +95 -0
  167. package/src/services/team-member-activity/transcript-tail-summary.ts +125 -0
  168. package/src/shared/background-agent-status.ts +79 -0
  169. package/src/types/api.ts +1 -0
  170. package/src/types/chat.ts +6 -1
  171. package/src/web/components/chat/chat-history-bar.tsx +4 -2
  172. package/src/web/components/chat/chat-tab.tsx +1 -0
  173. package/src/web/components/chat/message-input.tsx +13 -6
  174. package/src/web/components/chat/mini-markdown.tsx +17 -0
  175. package/src/web/components/chat/send-message-card.tsx +134 -0
  176. package/src/web/components/chat/send-message-parse.ts +159 -0
  177. package/src/web/components/chat/team-activity-panel.tsx +107 -121
  178. package/src/web/components/chat/team-member-activity-row.tsx +92 -0
  179. package/src/web/components/chat/team-member-list.tsx +43 -0
  180. package/src/web/components/chat/team-member-window-content.tsx +97 -0
  181. package/src/web/components/chat/team-message-badges.ts +18 -0
  182. package/src/web/components/chat/team-message-list.tsx +88 -0
  183. package/src/web/components/chat/team-message-preview.ts +87 -0
  184. package/src/web/components/chat/tool-cards.tsx +44 -18
  185. package/src/web/components/editor/audio-preview.tsx +6 -7
  186. package/src/web/components/editor/video-player/use-video-keyboard-shortcuts.ts +62 -0
  187. package/src/web/components/editor/video-player/video-player-controls.tsx +103 -0
  188. package/src/web/components/editor/video-player/video-player.tsx +190 -0
  189. package/src/web/components/editor/video-player/video-settings-menu.tsx +92 -0
  190. package/src/web/components/editor/video-player/video-transform.ts +49 -0
  191. package/src/web/components/editor/video-preview.tsx +75 -14
  192. package/src/web/components/explorer/file-tree.tsx +6 -0
  193. package/src/web/components/explorer/tree-node-context-menu.tsx +5 -1
  194. package/src/web/components/floating-window/window-content-registry.ts +5 -0
  195. package/src/web/components/floating-window/window-store-types.ts +1 -1
  196. package/src/web/components/os-explorer/actions/explorer-actions-upload.ts +96 -0
  197. package/src/web/components/os-explorer/actions/use-explorer-actions.ts +23 -2
  198. package/src/web/components/os-explorer/actions/use-explorer-upload-actions.tsx +41 -0
  199. package/src/web/components/os-explorer/dnd/use-drop-transfer.tsx +10 -1
  200. package/src/web/components/os-explorer/dnd/use-entry-row-dnd.ts +8 -1
  201. package/src/web/components/os-explorer/dnd/use-external-file-drop.ts +84 -0
  202. package/src/web/components/os-explorer/dnd/use-path-drop-target.ts +46 -3
  203. package/src/web/components/os-explorer/explorer-body.tsx +1 -1
  204. package/src/web/components/os-explorer/explorer-context-menu.tsx +10 -0
  205. package/src/web/components/os-explorer/explorer-dialogs.tsx +2 -0
  206. package/src/web/components/os-explorer/explorer-sidebar.tsx +10 -5
  207. package/src/web/components/os-explorer/explorer-toolbar.tsx +32 -2
  208. package/src/web/components/os-explorer/mobile/mobile-bottom-toolbar.tsx +5 -1
  209. package/src/web/components/os-explorer/upload/collect-dropped-entries.ts +81 -0
  210. package/src/web/components/os-explorer/upload/upload-queue.ts +79 -0
  211. package/src/web/components/os-explorer/upload/use-upload-picker.tsx +74 -0
  212. package/src/web/components/os-explorer/views/column-row.tsx +3 -1
  213. package/src/web/components/os-explorer/views/column-view-column.tsx +6 -1
  214. package/src/web/components/os-explorer/views/icons-view-tile.tsx +1 -1
  215. package/src/web/components/os-explorer/views/icons-view.tsx +5 -1
  216. package/src/web/components/os-explorer/views/list-row.tsx +1 -1
  217. package/src/web/components/os-explorer/views/list-view.tsx +5 -1
  218. package/src/web/components/shared/markdown-code-block.tsx +3 -7
  219. package/src/web/components/terminal/terminal-tab.tsx +19 -25
  220. package/src/web/hooks/use-chat.ts +77 -15
  221. package/src/web/hooks/use-team-activity-feed.ts +92 -0
  222. package/src/web/hooks/use-terminal-command-queue.ts +108 -0
  223. package/src/web/hooks/use-terminal.ts +45 -3
  224. package/src/web/lib/fs-api.ts +9 -0
  225. package/src/web/lib/fs-upload-xhr.ts +62 -0
  226. package/src/web/lib/media-url.ts +51 -0
  227. package/src/web/lib/open-terminal-at.ts +22 -0
  228. package/src/web/lib/run-in-terminal.ts +55 -0
  229. package/src/web/lib/send-to-chat.ts +118 -0
  230. package/src/web/lib/ws-auth.ts +13 -0
  231. package/src/web/lib/ws-client.ts +10 -5
  232. package/src/web/theme/builtin/aurora.ts +8 -5
  233. package/.release-run.sh +0 -5
  234. package/dist/web/assets/api-client-CaRE4HYh.js +0 -1
  235. package/dist/web/assets/api-settings-DBcXfeJ8.js +0 -1
  236. package/dist/web/assets/architecture-PBZL5I3N-B4J2-O8Q.js +0 -1
  237. package/dist/web/assets/audio-preview-CikvnAOI.js +0 -1
  238. package/dist/web/assets/chat-tab-B3-KcCn2.js +0 -13
  239. package/dist/web/assets/code-editor-BpbZw13T.js +0 -10
  240. package/dist/web/assets/dialog-CkI7rwrh.js +0 -1
  241. package/dist/web/assets/diff-viewer-CT1ALWt2.js +0 -4
  242. package/dist/web/assets/explorer-body-CQCyGEet.js +0 -2
  243. package/dist/web/assets/explorer-window-content-EtyTAZSh.js +0 -1
  244. package/dist/web/assets/gitGraph-HDMCJU4V-CtQ3yXN-.js +0 -1
  245. package/dist/web/assets/glide-data-grid-DCJCz4XB.js +0 -138
  246. package/dist/web/assets/group-chat-tab-C3UD2bcm.js +0 -1
  247. package/dist/web/assets/index-BYetJoXQ.js +0 -52
  248. package/dist/web/assets/index-D-xwjNyX.css +0 -2
  249. package/dist/web/assets/info-3K5VOQVL-Cg0mIJn9.js +0 -1
  250. package/dist/web/assets/keybindings-store-yBWU8ZYT.js +0 -1
  251. package/dist/web/assets/markdown-renderer-B2D4gdok.js +0 -1
  252. package/dist/web/assets/mobile-explorer-sheet-BtxyynR3.js +0 -1
  253. package/dist/web/assets/notification-store-DKw_mGED.js +0 -1
  254. package/dist/web/assets/packet-RMMSAZCW-D79vAFR_.js +0 -1
  255. package/dist/web/assets/pie-UPGHQEXC-DYUOH68U.js +0 -1
  256. package/dist/web/assets/plus-9LatwJMx.js +0 -1
  257. package/dist/web/assets/radar-KQ55EAFF-B1eznNtd.js +0 -1
  258. package/dist/web/assets/resolve-theme-B0eotaPq.js +0 -1
  259. package/dist/web/assets/settings-tab-ESL-6MAA.js +0 -1
  260. package/dist/web/assets/sql-query-editor-CZdZuawP.js +0 -1
  261. package/dist/web/assets/system-monitor-tab-XR8NXHpv.js +0 -1
  262. package/dist/web/assets/terminal-tab-CCCB-_50.js +0 -3
  263. package/dist/web/assets/tool-cards-CsTs5go2.js +0 -7
  264. package/dist/web/assets/treemap-KZPCXAKY-SeI2TxLs.js +0 -1
  265. package/dist/web/assets/use-is-mobile-DnFSkUG2.js +0 -1
  266. package/dist/web/assets/use-monaco-theme-CZh7xTJZ.js +0 -1
  267. package/dist/web/assets/video-preview-KbRcyuAA.js +0 -1
  268. package/src/web/components/chat/team-activity-popover.tsx +0 -202
  269. /package/dist/web/assets/{abap-DrMyiWmK.js → abap-C1K0-wzc.js} +0 -0
  270. /package/dist/web/assets/{actionscript-3-BlSOyi0y.js → actionscript-3-CJhQVjLA.js} +0 -0
  271. /package/dist/web/assets/{ada-B_m96icB.js → ada-Bt8r9fME.js} +0 -0
  272. /package/dist/web/assets/{andromeeda-Ch3vOiZB.js → andromeeda-DF1I8qi3.js} +0 -0
  273. /package/dist/web/assets/{apache-CdsaXc1n.js → apache-CnlleE6P.js} +0 -0
  274. /package/dist/web/assets/{apex-0AnsBevk.js → apex-j0C6Xmk6.js} +0 -0
  275. /package/dist/web/assets/{api-client-rTAGdEKo.js → api-client-BSB14J4w.js} +0 -0
  276. /package/dist/web/assets/{applescript-CSxLolzy.js → applescript-DO7AnO8U.js} +0 -0
  277. /package/dist/web/assets/{apply-theme-CMQXnUAa.js → apply-theme-CNj1iA-v.js} +0 -0
  278. /package/dist/web/assets/{ara-DXZS0jA5.js → ara-i83AydZj.js} +0 -0
  279. /package/dist/web/assets/{asciidoc-DDy3pH2A.js → asciidoc-DbBA6-sF.js} +0 -0
  280. /package/dist/web/assets/{asm-Bd17hhqg.js → asm-B5S47Rgr.js} +0 -0
  281. /package/dist/web/assets/{aurora-x-C62HJRs-.js → aurora-x-De_IaSKw.js} +0 -0
  282. /package/dist/web/assets/{awk-Cc-nazni.js → awk-CfyLHXqS.js} +0 -0
  283. /package/dist/web/assets/{ayu-dark-BsfmNWCU.js → ayu-dark-DTLdZ1Si.js} +0 -0
  284. /package/dist/web/assets/{ayu-light-BZ4HLa4X.js → ayu-light-DTOfkzhu.js} +0 -0
  285. /package/dist/web/assets/{ayu-mirage-Bhyjyiqr.js → ayu-mirage-DsAECCMv.js} +0 -0
  286. /package/dist/web/assets/{ballerina-QiWgir3v.js → ballerina-CumGp9A7.js} +0 -0
  287. /package/dist/web/assets/{bat-DxtlkwN3.js → bat-Ca2SBbYN.js} +0 -0
  288. /package/dist/web/assets/{beancount-Bt21X7M_.js → beancount-CqyXBwwF.js} +0 -0
  289. /package/dist/web/assets/{berry-Cksre7lO.js → berry-yl-kJr_H.js} +0 -0
  290. /package/dist/web/assets/{bibtex-DkakgDlR.js → bibtex-DoFFszjV.js} +0 -0
  291. /package/dist/web/assets/{bicep-8crmcyXU.js → bicep-CWPi4nfK.js} +0 -0
  292. /package/dist/web/assets/{bird2-CewQ2I_Z.js → bird2-DXew-ut0.js} +0 -0
  293. /package/dist/web/assets/{bot-nk1k3ELu.js → bot-CunPuEdW.js} +0 -0
  294. /package/dist/web/assets/{c-C_6zE8Ha.js → c-clbySDvd.js} +0 -0
  295. /package/dist/web/assets/{c3-DLWB9-dp.js → c3-B65jvSM1.js} +0 -0
  296. /package/dist/web/assets/{cadence-D8qYS6pR.js → cadence-Cm8tQTHN.js} +0 -0
  297. /package/dist/web/assets/{can-open-in-ppm-BzVXoV-d.js → can-open-in-ppm-Cu-XYIgu.js} +0 -0
  298. /package/dist/web/assets/{catppuccin-frappe-Bh95lNRy.js → catppuccin-frappe-DLeGslh_.js} +0 -0
  299. /package/dist/web/assets/{catppuccin-latte-C1PlN3G0.js → catppuccin-latte-CR5GeHws.js} +0 -0
  300. /package/dist/web/assets/{catppuccin-macchiato-CldtgdK4.js → catppuccin-macchiato-p-XgjxhP.js} +0 -0
  301. /package/dist/web/assets/{catppuccin-mocha-BiwqxX9A.js → catppuccin-mocha-B8QOAZLl.js} +0 -0
  302. /package/dist/web/assets/{clarity-CGiU22jY.js → clarity-DPulbvKH.js} +0 -0
  303. /package/dist/web/assets/{clojure-COIC3doh.js → clojure-DFuWS20u.js} +0 -0
  304. /package/dist/web/assets/{cmake-DWUEQfHv.js → cmake-DejCqcu4.js} +0 -0
  305. /package/dist/web/assets/{codeowners-BNHiAQea.js → codeowners-BwAyqWIH.js} +0 -0
  306. /package/dist/web/assets/{codeql-BuKaoS0j.js → codeql-DgSwFjqI.js} +0 -0
  307. /package/dist/web/assets/{common-lisp-D5IKbxsd.js → common-lisp-CxIVJCtf.js} +0 -0
  308. /package/dist/web/assets/{coq-BMVS-p94.js → coq-DLSjSnmf.js} +0 -0
  309. /package/dist/web/assets/{csharp-DMlbXbM4.js → csharp-C1HHzXBp.js} +0 -0
  310. /package/dist/web/assets/{css-CjkJC89X.js → css-VXsMY--y.js} +0 -0
  311. /package/dist/web/assets/{csv-DoNVBBf0.js → csv-BEbcJdHz.js} +0 -0
  312. /package/dist/web/assets/{csv-parser-DHvoTjMF.js → csv-parser-r-Nk1oHT.js} +0 -0
  313. /package/dist/web/assets/{cue-DoTRbBg-.js → cue-BkDnFqUD.js} +0 -0
  314. /package/dist/web/assets/{cypher-DVsNdTT3.js → cypher-1cMqojSQ.js} +0 -0
  315. /package/dist/web/assets/{d-EX7BBSrv.js → d-BnYjRXtH.js} +0 -0
  316. /package/dist/web/assets/{dark-plus-P_YTHwlh.js → dark-plus-XfbaH7kZ.js} +0 -0
  317. /package/dist/web/assets/{dart-Do-rgU4S.js → dart-Ca7s-8hK.js} +0 -0
  318. /package/dist/web/assets/{data-grid-types-CUx8cusN.js → data-grid-types-BXwlFMzs.js} +0 -0
  319. /package/dist/web/assets/{dax-DxOieacu.js → dax-CmuOw-mY.js} +0 -0
  320. /package/dist/web/assets/{desktop-CvdV7SBf.js → desktop-C2gJ6Ovb.js} +0 -0
  321. /package/dist/web/assets/{diff-CNOetGen.js → diff-BCEo1Ej7.js} +0 -0
  322. /package/dist/web/assets/{dist-CMfLpPW_.js → dist-CUPbqNnL.js} +0 -0
  323. /package/dist/web/assets/{dist-DduBIVkD.js → dist-DOlBnxFX.js} +0 -0
  324. /package/dist/web/assets/{docker-DAlG4_q-.js → docker-DN2jlyAh.js} +0 -0
  325. /package/dist/web/assets/{dotenv-Dh3qf6yx.js → dotenv-NExMY2kS.js} +0 -0
  326. /package/dist/web/assets/{dracula-baAGmJYG.js → dracula-B9lbE01y.js} +0 -0
  327. /package/dist/web/assets/{dracula-soft-l7ZIyums.js → dracula-soft-CMBBfKF_.js} +0 -0
  328. /package/dist/web/assets/{dream-maker-DeRjdBn8.js → dream-maker-DEyDwFsd.js} +0 -0
  329. /package/dist/web/assets/{emacs-lisp-CB05-jQA.js → emacs-lisp-D_qP2zS6.js} +0 -0
  330. /package/dist/web/assets/{everforest-dark-CT-llQBT.js → everforest-dark-DbHa2U-p.js} +0 -0
  331. /package/dist/web/assets/{everforest-light-ByPJkTT7.js → everforest-light-BISkJchP.js} +0 -0
  332. /package/dist/web/assets/{fennel-CeMarbvn.js → fennel-6B22SfJR.js} +0 -0
  333. /package/dist/web/assets/{file-exclamation-point-COAwgNGw.js → file-exclamation-point-a8f3_w7a.js} +0 -0
  334. /package/dist/web/assets/{fish-BYrt6Xtb.js → fish-CubIIvJD.js} +0 -0
  335. /package/dist/web/assets/{fluent-Cr5VcAwh.js → fluent-DF55zQ09.js} +0 -0
  336. /package/dist/web/assets/{fortran-free-form-DQmKtrLa.js → fortran-free-form-DvOu8jAt.js} +0 -0
  337. /package/dist/web/assets/{gdscript-QTDKjzhY.js → gdscript-DCPxdLpq.js} +0 -0
  338. /package/dist/web/assets/{gdshader-Chr-5UYt.js → gdshader-CTZ381FO.js} +0 -0
  339. /package/dist/web/assets/{genie-kWHMWnt0.js → genie-C_QDtvgU.js} +0 -0
  340. /package/dist/web/assets/{gherkin-DtEBQFUo.js → gherkin-BcvK42tF.js} +0 -0
  341. /package/dist/web/assets/{github-dark-B5aiUta2.js → github-dark-C9afs_4w.js} +0 -0
  342. /package/dist/web/assets/{github-dark-default-BkxtsTqR.js → github-dark-default-CKGVrl3Z.js} +0 -0
  343. /package/dist/web/assets/{github-dark-dimmed-B9QjR_0K.js → github-dark-dimmed-C3dQZ_LC.js} +0 -0
  344. /package/dist/web/assets/{github-dark-high-contrast-D6bO-5c0.js → github-dark-high-contrast-DW78QukY.js} +0 -0
  345. /package/dist/web/assets/{github-light-Cg2ZW_CJ.js → github-light-BnP2dznS.js} +0 -0
  346. /package/dist/web/assets/{github-light-default-eD7qUVlQ.js → github-light-default-C2fPZzPG.js} +0 -0
  347. /package/dist/web/assets/{github-light-high-contrast-CFnlSVjd.js → github-light-high-contrast-BzIuUFR2.js} +0 -0
  348. /package/dist/web/assets/{gleam-BpYoOqIT.js → gleam-BpeKwEIO.js} +0 -0
  349. /package/dist/web/assets/{gn-BxwtZPau.js → gn-nUb_Mit0.js} +0 -0
  350. /package/dist/web/assets/{gnuplot-CRyiZDfl.js → gnuplot-D7I0v20p.js} +0 -0
  351. /package/dist/web/assets/{go-Dw1Qx94W.js → go-D7Qqska7.js} +0 -0
  352. /package/dist/web/assets/{groovy-zq_SbWk-.js → groovy-DS-pne5-.js} +0 -0
  353. /package/dist/web/assets/{gruvbox-dark-hard-DbbNy8-Q.js → gruvbox-dark-hard-C0W01fsq.js} +0 -0
  354. /package/dist/web/assets/{gruvbox-dark-medium-BQZ7Kj6C.js → gruvbox-dark-medium-Bqo6fQbg.js} +0 -0
  355. /package/dist/web/assets/{gruvbox-dark-soft-vNavErXi.js → gruvbox-dark-soft-D-XSM_fu.js} +0 -0
  356. /package/dist/web/assets/{gruvbox-light-hard-AOFbCd7i.js → gruvbox-light-hard-IVAwMJRl.js} +0 -0
  357. /package/dist/web/assets/{gruvbox-light-medium-DqtxND6H.js → gruvbox-light-medium-38ATiG7i.js} +0 -0
  358. /package/dist/web/assets/{gruvbox-light-soft-Cg0basu6.js → gruvbox-light-soft-DnZr3c_0.js} +0 -0
  359. /package/dist/web/assets/{haskell-CcsmcOez.js → haskell-uQcywkDH.js} +0 -0
  360. /package/dist/web/assets/{haxe-BimYY9X4.js → haxe-c6bI4_GT.js} +0 -0
  361. /package/dist/web/assets/{hcl-CTrXCK40.js → hcl-CQXTiEXq.js} +0 -0
  362. /package/dist/web/assets/{hjson-CmqqU6Qg.js → hjson-ChETf2bz.js} +0 -0
  363. /package/dist/web/assets/{hlsl-CuJtX_uf.js → hlsl-BMC4JGoE.js} +0 -0
  364. /package/dist/web/assets/{horizon-BGeSjDYJ.js → horizon-BusFlhLW.js} +0 -0
  365. /package/dist/web/assets/{horizon-bright-GYMgd3R0.js → horizon-bright-2oD0Mz7W.js} +0 -0
  366. /package/dist/web/assets/{houston--JD_5r2q.js → houston-DdbbjqFg.js} +0 -0
  367. /package/dist/web/assets/{hy-C9ILOiqk.js → hy-D_SOuqw9.js} +0 -0
  368. /package/dist/web/assets/{image-gallery-BWFpZAAP.js → image-gallery-CrM5fHKy.js} +0 -0
  369. /package/dist/web/assets/{imba-B6F3yus5.js → imba-BxLOHO5I.js} +0 -0
  370. /package/dist/web/assets/{ini-BfkQswMZ.js → ini-ox-CFcg0.js} +0 -0
  371. /package/dist/web/assets/{java-DEH5avQ1.js → java-Bzo8yIal.js} +0 -0
  372. /package/dist/web/assets/{javascript-Dr4yHuV2.js → javascript-DHuz9VXn.js} +0 -0
  373. /package/dist/web/assets/{json-BuR35ewf.js → json-0_y80FD5.js} +0 -0
  374. /package/dist/web/assets/{json5-Ny9ZiJAC.js → json5-CEQ50pGM.js} +0 -0
  375. /package/dist/web/assets/{jsonc-zYVuoZkR.js → jsonc-CfrLEAKA.js} +0 -0
  376. /package/dist/web/assets/{jsonl-B9xthb_H.js → jsonl-fKbrANC2.js} +0 -0
  377. /package/dist/web/assets/{jsonnet-CCoLJbMP.js → jsonnet-DKbkPaQ2.js} +0 -0
  378. /package/dist/web/assets/{jssm-W0GDlQXi.js → jssm-Dxxx1Hqu.js} +0 -0
  379. /package/dist/web/assets/{jsx-BrLJpWC-.js → jsx-DlJL-Oj3.js} +0 -0
  380. /package/dist/web/assets/{kanagawa-dragon-CuVwt_MO.js → kanagawa-dragon-CwN4SV1c.js} +0 -0
  381. /package/dist/web/assets/{kanagawa-lotus-B3W_617P.js → kanagawa-lotus-BHltayiE.js} +0 -0
  382. /package/dist/web/assets/{kanagawa-wave-CEx4MP5W.js → kanagawa-wave-D-KXPjw6.js} +0 -0
  383. /package/dist/web/assets/{kdl-DilRejnB.js → kdl-DvtoJdFo.js} +0 -0
  384. /package/dist/web/assets/{kotlin-Bq5JtKaa.js → kotlin-CTS4MLcx.js} +0 -0
  385. /package/dist/web/assets/{kusto-Bhp2KYog.js → kusto-HtSE_sKm.js} +0 -0
  386. /package/dist/web/assets/{laserwave-WA_7c5hu.js → laserwave-DaXZG9ge.js} +0 -0
  387. /package/dist/web/assets/{lean-AEGcXwS-.js → lean-BYtGUfWx.js} +0 -0
  388. /package/dist/web/assets/{less-NPqH-26u.js → less-BoxmU_Mk.js} +0 -0
  389. /package/dist/web/assets/{lib-BFetOULu.js → lib-BzxL1ARZ.js} +0 -0
  390. /package/dist/web/assets/{libesm-tR4XlhMj.js → libesm-ah-mr-sh.js} +0 -0
  391. /package/dist/web/assets/{light-plus-B4xW7cgY.js → light-plus-Bt5CyUuo.js} +0 -0
  392. /package/dist/web/assets/{llvm-CPyWNcDE.js → llvm-CSdk9q9V.js} +0 -0
  393. /package/dist/web/assets/{log-CeQmiQSz.js → log-B-kp_3CL.js} +0 -0
  394. /package/dist/web/assets/{logo-BO0VrjBm.js → logo-DdIsXFDO.js} +0 -0
  395. /package/dist/web/assets/{luau-B6GT9kA1.js → luau-DUBpLlzQ.js} +0 -0
  396. /package/dist/web/assets/{make-CwfmN7e_.js → make-B3JVjoW_.js} +0 -0
  397. /package/dist/web/assets/{markdown-Bbe4FlLp.js → markdown-Seon0vHs.js} +0 -0
  398. /package/dist/web/assets/{material-theme-iD9XbfsB.js → material-theme-Nq-ChX1P.js} +0 -0
  399. /package/dist/web/assets/{material-theme-darker-u7M3XGe7.js → material-theme-darker-CPV4ayXJ.js} +0 -0
  400. /package/dist/web/assets/{material-theme-lighter-BGdI48zn.js → material-theme-lighter-0kyjYqEd.js} +0 -0
  401. /package/dist/web/assets/{material-theme-ocean-_mk62F5q.js → material-theme-ocean-DGpAp5co.js} +0 -0
  402. /package/dist/web/assets/{material-theme-palenight-CxFbDyAu.js → material-theme-palenight-B0AUn0NG.js} +0 -0
  403. /package/dist/web/assets/{matlab-Z0RUE3V_.js → matlab-C2waytxs.js} +0 -0
  404. /package/dist/web/assets/{mdx-BZ5wPYzV.js → mdx-DAis6R6I.js} +0 -0
  405. /package/dist/web/assets/{mermaid-CY1_aP-h.js → mermaid-_rnT2YGD.js} +0 -0
  406. /package/dist/web/assets/{min-dark-DKWtxOre.js → min-dark-BSSjchUB.js} +0 -0
  407. /package/dist/web/assets/{min-light-e1iwHDfP.js → min-light-DomAFQsa.js} +0 -0
  408. /package/dist/web/assets/{mipsasm-BiDfQ4ir.js → mipsasm-bBgtvPyG.js} +0 -0
  409. /package/dist/web/assets/{mojo-DteFEHsj.js → mojo-Cp9BayW0.js} +0 -0
  410. /package/dist/web/assets/{monokai-BgPbE9xZ.js → monokai-CL_9gbmn.js} +0 -0
  411. /package/dist/web/assets/{moonbit-mEbRp-mA.js → moonbit-BS_fcr-P.js} +0 -0
  412. /package/dist/web/assets/{move-CogqZw7A.js → move-C79AH45U.js} +0 -0
  413. /package/dist/web/assets/{narrat-BD2qjmy1.js → narrat-CJ5s3BDf.js} +0 -0
  414. /package/dist/web/assets/{nextflow-groovy-BNX8GOD_.js → nextflow-groovy-ZHa4JrPJ.js} +0 -0
  415. /package/dist/web/assets/{night-owl-ed3GbziD.js → night-owl-Jkf9zFQK.js} +0 -0
  416. /package/dist/web/assets/{night-owl-light-B0j7O0Wn.js → night-owl-light-DRPeaG5J.js} +0 -0
  417. /package/dist/web/assets/{nix-DFDbcQQm.js → nix-D2Jet9DN.js} +0 -0
  418. /package/dist/web/assets/{nord-D93TxEQi.js → nord-CREVs22j.js} +0 -0
  419. /package/dist/web/assets/{nushell-3_H_XKja.js → nushell-CAiDi_mR.js} +0 -0
  420. /package/dist/web/assets/{objective-c-BM3u6nlm.js → objective-c-CVkmJkC1.js} +0 -0
  421. /package/dist/web/assets/{objective-cpp-DZ73h42g.js → objective-cpp-DIKDO1Ly.js} +0 -0
  422. /package/dist/web/assets/{ocaml-B1PU5gmh.js → ocaml-R9qtGQhs.js} +0 -0
  423. /package/dist/web/assets/{odin-D14L6aRL.js → odin-DTYNQqVr.js} +0 -0
  424. /package/dist/web/assets/{one-dark-pro-Bj-94Z8v.js → one-dark-pro-BSLWUJn5.js} +0 -0
  425. /package/dist/web/assets/{one-light-DTdGWyOZ.js → one-light-DbkRXG3t.js} +0 -0
  426. /package/dist/web/assets/{openscad-Ciq554Sj.js → openscad-BlPcSnmT.js} +0 -0
  427. /package/dist/web/assets/{pascal-Bfd4qdGi.js → pascal-B1Y46ZZB.js} +0 -0
  428. /package/dist/web/assets/{pkl-CTurDUfj.js → pkl-BhJSJbFv.js} +0 -0
  429. /package/dist/web/assets/{plastic-BLQfZXQp.js → plastic-BzxsCTpi.js} +0 -0
  430. /package/dist/web/assets/{plsql-DyYfP9kQ.js → plsql-DBba7dyP.js} +0 -0
  431. /package/dist/web/assets/{po-CgXdT8vQ.js → po-BvgLlnNo.js} +0 -0
  432. /package/dist/web/assets/{poimandres-DOg5fK6s.js → poimandres-DUaI_1JP.js} +0 -0
  433. /package/dist/web/assets/{polar-Dv4aoL6X.js → polar-C7uILwJn.js} +0 -0
  434. /package/dist/web/assets/{postcss-DxugHnJD.js → postcss-BgJlpFdw.js} +0 -0
  435. /package/dist/web/assets/{powerquery-CR3kzRyX.js → powerquery-Dn1MIpZu.js} +0 -0
  436. /package/dist/web/assets/{powershell-BE91xJQB.js → powershell-Bo-DYzOS.js} +0 -0
  437. /package/dist/web/assets/{prisma-B_3yUlRQ.js → prisma-CTLs5IdT.js} +0 -0
  438. /package/dist/web/assets/{prolog-DcT781Ht.js → prolog-QRGun3eG.js} +0 -0
  439. /package/dist/web/assets/{proto-CSf-w5k2.js → proto-B7PI7apn.js} +0 -0
  440. /package/dist/web/assets/{puppet-CLGO6QWl.js → puppet-CBq0tLrP.js} +0 -0
  441. /package/dist/web/assets/{purescript-Bah2McKd.js → purescript-DFewOQb2.js} +0 -0
  442. /package/dist/web/assets/{python-DUH8qy7N.js → python-iWuxKXvT.js} +0 -0
  443. /package/dist/web/assets/{qmldir-YIsHnjKq.js → qmldir-cXiW3AsF.js} +0 -0
  444. /package/dist/web/assets/{qss-CLmoVNA4.js → qss-DASwAbBL.js} +0 -0
  445. /package/dist/web/assets/{r-Pon-b-vz.js → r-TU2eaIPw.js} +0 -0
  446. /package/dist/web/assets/{racket-BA09P5Kg.js → racket-SkRKqrLY.js} +0 -0
  447. /package/dist/web/assets/{raku-DlpzU7Fe.js → raku-D31lthkZ.js} +0 -0
  448. /package/dist/web/assets/{react-w6wINGnd.js → react-I_EwPIZi.js} +0 -0
  449. /package/dist/web/assets/{red-l7VuCR6d.js → red-CTcQZevb.js} +0 -0
  450. /package/dist/web/assets/{reg-g_k7gQEr.js → reg-D8YAsTGf.js} +0 -0
  451. /package/dist/web/assets/{regexp-CT17B7I7.js → regexp-CD2uvAWx.js} +0 -0
  452. /package/dist/web/assets/{rel-DkPCS7Bp.js → rel-CAaI-1fg.js} +0 -0
  453. /package/dist/web/assets/{riscv-CYuFTw30.js → riscv-DR-hJVIj.js} +0 -0
  454. /package/dist/web/assets/{ron-cY7u_yEu.js → ron-CuUCJ0n4.js} +0 -0
  455. /package/dist/web/assets/{rose-pine-Cr_h_Ubo.js → rose-pine-aFHxW48i.js} +0 -0
  456. /package/dist/web/assets/{rose-pine-dawn-rRewaSZg.js → rose-pine-dawn-Bqz2sBRY.js} +0 -0
  457. /package/dist/web/assets/{rose-pine-moon-3VX5Xwca.js → rose-pine-moon-wqyh0an4.js} +0 -0
  458. /package/dist/web/assets/{rosmsg-Du4ie5PU.js → rosmsg-CI1ywimO.js} +0 -0
  459. /package/dist/web/assets/{rust-B7xWL-Eh.js → rust-p7Imk4G_.js} +0 -0
  460. /package/dist/web/assets/{sass-oXbOG8de.js → sass-C8JfaqCV.js} +0 -0
  461. /package/dist/web/assets/{save-C4z1p51e.js → save-DRPrDfTt.js} +0 -0
  462. /package/dist/web/assets/{scala-nlZJhKHf.js → scala-DNxCW1By.js} +0 -0
  463. /package/dist/web/assets/{scheme-FZ9_Zy8D.js → scheme-BYuUPJoL.js} +0 -0
  464. /package/dist/web/assets/{sdbl-D_lyvXiu.js → sdbl-DbPeJPUr.js} +0 -0
  465. /package/dist/web/assets/{shellscript-DRWRboAd.js → shellscript-DMiHlR3q.js} +0 -0
  466. /package/dist/web/assets/{shield-check-C5laObJv.js → shield-check-8GObuUAn.js} +0 -0
  467. /package/dist/web/assets/{shield-off-DULFtyLS.js → shield-off-egKAZEwR.js} +0 -0
  468. /package/dist/web/assets/{slack-dark-BGk2uVHu.js → slack-dark-C4xGcQXI.js} +0 -0
  469. /package/dist/web/assets/{slack-ochin-PAoSX041.js → slack-ochin-BzgRt_wm.js} +0 -0
  470. /package/dist/web/assets/{smalltalk-qP0zvICo.js → smalltalk-LDeL9PYH.js} +0 -0
  471. /package/dist/web/assets/{snazzy-light-B7uccpH6.js → snazzy-light-CXE7a5j_.js} +0 -0
  472. /package/dist/web/assets/{solarized-dark-CbICLZs3.js → solarized-dark-DgUhY74x.js} +0 -0
  473. /package/dist/web/assets/{solarized-light-CprWhyjj.js → solarized-light-DD9C-H-9.js} +0 -0
  474. /package/dist/web/assets/{solidity-B7pcm2K6.js → solidity-CAbDB4D4.js} +0 -0
  475. /package/dist/web/assets/{splunk-Co2LL4J_.js → splunk-kQREPfIc.js} +0 -0
  476. /package/dist/web/assets/{sql-cETIdGnT.js → sql-CBzeNAD8.js} +0 -0
  477. /package/dist/web/assets/{ssh-config-DETG6JCD.js → ssh-config-L1euxrsM.js} +0 -0
  478. /package/dist/web/assets/{stylus-tmTre2-O.js → stylus-DvBnYniP.js} +0 -0
  479. /package/dist/web/assets/{swift-BeKQbULV.js → swift-DF5NDQp6.js} +0 -0
  480. /package/dist/web/assets/{synthwave-84-eCL4-Xdn.js → synthwave-84-DbBhB_Hq.js} +0 -0
  481. /package/dist/web/assets/{system-verilog-CviwWvEx.js → system-verilog-D8s2ZW_k.js} +0 -0
  482. /package/dist/web/assets/{systemd-WuoFIbBS.js → systemd-a5-XZJsG.js} +0 -0
  483. /package/dist/web/assets/{table-CHmxuh6M.js → table-BX2ZrJfM.js} +0 -0
  484. /package/dist/web/assets/{talonscript-B9VBu7VI.js → talonscript-BLa0URTW.js} +0 -0
  485. /package/dist/web/assets/{tasl-CN7r11ky.js → tasl-DIoV4XcC.js} +0 -0
  486. /package/dist/web/assets/{tcl-e1cdhe3h.js → tcl-ztYAdXfQ.js} +0 -0
  487. /package/dist/web/assets/{terraform-BEP0mfa_.js → terraform-DvpCCiOZ.js} +0 -0
  488. /package/dist/web/assets/{tokyo-night-BfA_dA37.js → tokyo-night-BkHlYUv0.js} +0 -0
  489. /package/dist/web/assets/{toml-BrDIfNnt.js → toml-acIrxos6.js} +0 -0
  490. /package/dist/web/assets/{tsv-CyfGyE4i.js → tsv-BJB5EVCT.js} +0 -0
  491. /package/dist/web/assets/{tsx-BrFA8Iwf.js → tsx-B1MmXGT3.js} +0 -0
  492. /package/dist/web/assets/{turtle-9GBtR0mP.js → turtle-DFceTiqd.js} +0 -0
  493. /package/dist/web/assets/{typescript-BuTRZ65V.js → typescript-Dz9wU5Y1.js} +0 -0
  494. /package/dist/web/assets/{typespec-CRiNNRXF.js → typespec-CPuYZijj.js} +0 -0
  495. /package/dist/web/assets/{typst-CmpTnnkl.js → typst-CfsCIYv8.js} +0 -0
  496. /package/dist/web/assets/{utils-B9H0d3Fh.js → utils-DoY4yTP-.js} +0 -0
  497. /package/dist/web/assets/{v-DnIi2aab.js → v-CGo0jcXA.js} +0 -0
  498. /package/dist/web/assets/{vala-B7VxCNZP.js → vala-AsSR0hrb.js} +0 -0
  499. /package/dist/web/assets/{vb-DGoQ3pBT.js → vb-C7tvbXv1.js} +0 -0
  500. /package/dist/web/assets/{vendor-xterm-ClRVIJ-k.js → vendor-xterm-BeaIjRrv.js} +0 -0
  501. /package/dist/web/assets/{verilog-zGOC4qwV.js → verilog--a33ND-p.js} +0 -0
  502. /package/dist/web/assets/{vesper-DXR9-06q.js → vesper-B_Z0h4pn.js} +0 -0
  503. /package/dist/web/assets/{vhdl-CCO0oz0l.js → vhdl-CWL5dZPN.js} +0 -0
  504. /package/dist/web/assets/{viml-Dxwr-QNN.js → viml-BglmT14Y.js} +0 -0
  505. /package/dist/web/assets/{vitesse-black-Bhah7RxE.js → vitesse-black-RKFsSqQI.js} +0 -0
  506. /package/dist/web/assets/{vitesse-dark-CGK6XVwc.js → vitesse-dark-D7zDWNmc.js} +0 -0
  507. /package/dist/web/assets/{vitesse-light-ne5kfw8h.js → vitesse-light-MAp87W5b.js} +0 -0
  508. /package/dist/web/assets/{vyper-ErDFqVoP.js → vyper-CgCBYg1G.js} +0 -0
  509. /package/dist/web/assets/{wasm-DtWvUWGr.js → wasm-BOG7ZeTT.js} +0 -0
  510. /package/dist/web/assets/{wasm-Bd258nS1.js → wasm-Bwl4txwp.js} +0 -0
  511. /package/dist/web/assets/{wenyan-EZe1IEYT.js → wenyan-DpShPKXZ.js} +0 -0
  512. /package/dist/web/assets/{wgsl-B1o5_I_9.js → wgsl-BakcCblK.js} +0 -0
  513. /package/dist/web/assets/{wikitext-C_rwp8An.js → wikitext-NroRWVAO.js} +0 -0
  514. /package/dist/web/assets/{wit-gbYtF3rY.js → wit-B5CM2pg6.js} +0 -0
  515. /package/dist/web/assets/{wolfram-DrcQFe2n.js → wolfram-7MTFBJpR.js} +0 -0
  516. /package/dist/web/assets/{x-B1qHmLdR.js → x-BcqfTY_7.js} +0 -0
  517. /package/dist/web/assets/{yaml-CzDAObLX.js → yaml-Bwr01G2K.js} +0 -0
  518. /package/dist/web/assets/{zenscript-B-J1phtg.js → zenscript-Dj8GATvi.js} +0 -0
  519. /package/dist/web/assets/{zig-BlzNia0O.js → zig-LAE-fbS-.js} +0 -0
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Turns a dropped/picked file batch into `PUT /api/fs/upload` calls, streamed through
3
+ * `uploadFileXhr` with bounded concurrency. Mirrors `transfer()` in
4
+ * `explorer-actions-clipboard.ts`: the same collision prompt (`ctx.resolve`), the same
5
+ * trash-first "Replace" via the 409 `EEXIST` contract, and a `fsChanged` refresh once the
6
+ * batch settles — drag-to-upload and the picker both funnel through this one function.
7
+ */
8
+
9
+ import { toast } from "sonner";
10
+ import { FsError } from "@/lib/fs-api";
11
+ import { uploadFileXhr } from "@/lib/fs-upload-xhr";
12
+ import { dirnameOf, joinPath } from "../format-file-meta";
13
+ import { fsChanged } from "../explorer-store";
14
+ import type { DroppedEntry } from "../upload/collect-dropped-entries";
15
+ import { runUploadQueue, type UploadJob } from "../upload/upload-queue";
16
+ import type { TransferContext } from "./explorer-actions-clipboard";
17
+ import { freeName } from "./explorer-actions-transfer-helpers";
18
+
19
+ /** Uploads sitting behind a collision prompt or a slow connection should not all queue up
20
+ * behind one giant file — three in flight matches the plan's chosen concurrency. */
21
+ const CONCURRENCY = 3;
22
+
23
+ function nameOf(relativePath: string): string {
24
+ return relativePath.split("/").pop() ?? relativePath;
25
+ }
26
+
27
+ /** Turns a "/"-joined relative path (as `collectDroppedEntries` produces) into a host path
28
+ * under `dstDir`, using the destination's own separator. */
29
+ function destinationFor(dstDir: string, relativePath: string, sep: string): string {
30
+ return joinPath(dstDir, relativePath.split("/").join(sep), sep);
31
+ }
32
+
33
+ async function uploadResolvingCollisions(
34
+ entry: DroppedEntry,
35
+ dstDir: string,
36
+ sep: string,
37
+ ctx: TransferContext,
38
+ onProgress: (bytesLoaded: number) => void,
39
+ ): Promise<void> {
40
+ const destination = destinationFor(dstDir, entry.relativePath, sep);
41
+ try {
42
+ await uploadFileXhr(destination, entry.file, false, (p) => onProgress(p.loaded));
43
+ return;
44
+ } catch (e) {
45
+ if (!(e instanceof FsError) || e.code !== "EEXIST") throw e;
46
+ }
47
+
48
+ const name = nameOf(entry.relativePath);
49
+ const choice = await ctx.resolve({ name, destination });
50
+ if (choice === "skip") return;
51
+ if (choice === "keep-both") {
52
+ const containingDir = dirnameOf(destination, sep);
53
+ const alternative = await freeName(containingDir, name, sep);
54
+ if (!alternative) throw new Error("No free name available");
55
+ await uploadFileXhr(joinPath(containingDir, alternative, sep), entry.file, false, (p) => onProgress(p.loaded));
56
+ return;
57
+ }
58
+ // Replace: the server upload route itself makes the overwrite atomic (tmp file + rename),
59
+ // so unlike the copy/move `transfer()` there is no separate trash-then-write step here.
60
+ await uploadFileXhr(destination, entry.file, true, (p) => onProgress(p.loaded));
61
+ }
62
+
63
+ function uploadLabel(entries: DroppedEntry[], fraction: number): string {
64
+ const label = entries.length === 1 ? entries[0]!.file.name : `${entries.length} files`;
65
+ return `Uploading ${label}… ${Math.round(fraction * 100)}%`;
66
+ }
67
+
68
+ export async function uploadEntries(
69
+ entries: DroppedEntry[],
70
+ dstDir: string,
71
+ sep: string,
72
+ ctx: TransferContext,
73
+ ): Promise<void> {
74
+ if (entries.length === 0) return;
75
+
76
+ const toastId = toast.loading(uploadLabel(entries, 0));
77
+ const jobs: UploadJob<void>[] = entries.map((entry, index) => ({
78
+ id: String(index),
79
+ size: entry.file.size,
80
+ run: (onProgress) => uploadResolvingCollisions(entry, dstDir, sep, ctx, onProgress),
81
+ }));
82
+
83
+ const results = await runUploadQueue(jobs, CONCURRENCY, (progress) => {
84
+ const fraction = progress.bytesTotal > 0 ? progress.bytesLoaded / progress.bytesTotal : 0;
85
+ toast.loading(uploadLabel(entries, fraction), { id: toastId });
86
+ });
87
+
88
+ fsChanged(dstDir);
89
+ const succeeded = results.filter((r) => r.ok).length;
90
+ const failed = results.length - succeeded;
91
+ if (failed === 0) {
92
+ toast.success(`Uploaded ${succeeded} item${succeeded === 1 ? "" : "s"}`, { id: toastId });
93
+ } else {
94
+ toast.error(`Uploaded ${succeeded}, ${failed} failed`, { id: toastId });
95
+ }
96
+ }
@@ -7,9 +7,10 @@
7
7
  * plain async functions with no React inside them.
8
8
  */
9
9
 
10
- import { useCallback, useMemo, useRef, useState } from "react";
10
+ import { useCallback, useMemo, useRef, useState, type ReactNode } from "react";
11
11
  import { toast } from "sonner";
12
12
  import type { FsEntry } from "@/lib/fs-api";
13
+ import { openTerminalAt } from "@/lib/open-terminal-at";
13
14
  import { useExplorerPinsStore } from "../explorer-pins-store";
14
15
  import { useExplorerStore, type ExplorerSlice } from "../explorer-store";
15
16
  import type { ExplorerNavigation } from "../use-explorer-navigation";
@@ -19,10 +20,14 @@ import {
19
20
  type CollisionChoice, type CollisionRequest,
20
21
  } from "./explorer-actions-clipboard";
21
22
  import { createEntry, deleteEntries, renameEntry, validateEntryName } from "./explorer-actions-mutate";
23
+ import { useExplorerUploadActions } from "./use-explorer-upload-actions";
24
+ import type { DroppedEntry } from "../upload/collect-dropped-entries";
22
25
 
23
26
  export interface ExplorerActions {
24
27
  openEntry(entry: FsEntry): void;
25
28
  openInNewWindow(entry: FsEntry): void;
29
+ /** Shell in the dock starting in the given directory (or the file's parent). */
30
+ openInTerminal(entry?: FsEntry): void;
26
31
  cut(entries: FsEntry[]): void;
27
32
  copy(entries: FsEntry[]): void;
28
33
  paste(targetDir?: string): void;
@@ -39,6 +44,11 @@ export interface ExplorerActions {
39
44
  showProperties(entry: FsEntry): void;
40
45
  /** Move or copy arbitrary paths into a directory — also the drag-and-drop entry point. */
41
46
  transferInto(paths: string[], dstDir: string, op: "copy" | "move"): Promise<void>;
47
+ /** Upload dropped/picked OS files into a directory — the upload counterpart of `transferInto`. */
48
+ uploadInto(entries: DroppedEntry[], dstDir: string): Promise<void>;
49
+ openUploadPicker(): void;
50
+ openUploadFolderPicker(): void;
51
+ supportsFolderUpload: boolean;
42
52
  }
43
53
 
44
54
  export interface ExplorerDialogState {
@@ -51,6 +61,8 @@ export interface ExplorerDialogState {
51
61
  closeDelete(): void;
52
62
  runPermanentDelete(): void;
53
63
  closeProperties(): void;
64
+ /** Hidden upload `<input>`s — render once per window. */
65
+ uploadInputs: ReactNode;
54
66
  }
55
67
 
56
68
  export function useExplorerActions(
@@ -99,6 +111,7 @@ export function useExplorerActions(
99
111
  );
100
112
 
101
113
  const currentDir = () => sliceRef.current?.path ?? "";
114
+ const upload = useExplorerUploadActions(context, currentDir);
102
115
 
103
116
  const actions = useMemo<ExplorerActions>(() => ({
104
117
  openEntry: (entry) => {
@@ -108,6 +121,9 @@ export function useExplorerActions(
108
121
  openInNewWindow: (entry) => {
109
122
  openPathInNewWindow(entry.type === "directory" ? entry.path : (sliceRef.current?.path ?? entry.path));
110
123
  },
124
+ openInTerminal: (entry) => {
125
+ openTerminalAt(entry?.type === "directory" ? entry.path : currentDir());
126
+ },
111
127
  cut: (entries) => setClipboardPaths(entries.map((e) => e.path), "cut"),
112
128
  copy: (entries) => setClipboardPaths(entries.map((e) => e.path), "copy"),
113
129
  paste: (targetDir) => void pasteInto(targetDir ?? currentDir(), context),
@@ -120,6 +136,10 @@ export function useExplorerActions(
120
136
  toast.success(`${verb} ${result.succeeded} item${result.succeeded === 1 ? "" : "s"}`);
121
137
  }
122
138
  },
139
+ uploadInto: upload.uploadInto,
140
+ openUploadPicker: upload.openUploadPicker,
141
+ openUploadFolderPicker: upload.openUploadFolderPicker,
142
+ supportsFolderUpload: upload.supportsFolderUpload,
123
143
 
124
144
  startRename: (entry) => {
125
145
  setInlineError(null);
@@ -180,7 +200,7 @@ export function useExplorerActions(
180
200
  else pins.pin({ path, name });
181
201
  },
182
202
  showProperties: (entry) => setProperties(entry),
183
- }), [windowId, patch, nav, context, platform]);
203
+ }), [windowId, patch, nav, context, platform, upload]);
184
204
 
185
205
  const runPermanentDelete = useCallback(() => {
186
206
  const target = pendingDelete;
@@ -200,6 +220,7 @@ export function useExplorerActions(
200
220
  closeDelete: () => setPendingDelete(null),
201
221
  runPermanentDelete,
202
222
  closeProperties: () => setProperties(null),
223
+ uploadInputs: upload.uploadInputs,
203
224
  },
204
225
  };
205
226
  }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Upload wiring for one explorer window: the hidden file/folder pickers and the action that
3
+ * turns a drop or a pick into `uploadEntries` calls. Split out of `use-explorer-actions.ts` to
4
+ * keep that file under the line cap — uploads are one more interruption-owning concern
5
+ * alongside collision/delete/properties, not a different architecture.
6
+ */
7
+
8
+ import { useCallback, type ReactNode } from "react";
9
+ import type { DroppedEntry } from "../upload/collect-dropped-entries";
10
+ import { useUploadPicker } from "../upload/use-upload-picker";
11
+ import type { TransferContext } from "./explorer-actions-clipboard";
12
+ import { uploadEntries } from "./explorer-actions-upload";
13
+
14
+ export interface ExplorerUploadActions {
15
+ uploadInto(entries: DroppedEntry[], dstDir: string): Promise<void>;
16
+ openUploadPicker(): void;
17
+ openUploadFolderPicker(): void;
18
+ supportsFolderUpload: boolean;
19
+ /** Render once per window — the hidden `<input>` elements the pickers click. */
20
+ uploadInputs: ReactNode;
21
+ }
22
+
23
+ export function useExplorerUploadActions(
24
+ context: TransferContext,
25
+ currentDir: () => string,
26
+ ): ExplorerUploadActions {
27
+ const uploadInto = useCallback(
28
+ (entries: DroppedEntry[], dstDir: string) => uploadEntries(entries, dstDir, context.sep, context),
29
+ [context],
30
+ );
31
+
32
+ const picker = useUploadPicker((entries) => void uploadInto(entries, currentDir()));
33
+
34
+ return {
35
+ uploadInto,
36
+ openUploadPicker: picker.openFilePicker,
37
+ openUploadFolderPicker: picker.openFolderPicker,
38
+ supportsFolderUpload: picker.supportsFolderPicker,
39
+ uploadInputs: picker.inputs,
40
+ };
41
+ }
@@ -10,12 +10,16 @@
10
10
 
11
11
  import { useEffect, useMemo, useRef, useState, type ReactNode } from "react";
12
12
  import type { CollisionChoice, CollisionRequest, TransferContext } from "../actions/explorer-actions-clipboard";
13
+ import { uploadEntries } from "../actions/explorer-actions-upload";
13
14
  import type { ExplorerDialogState } from "../actions/use-explorer-actions";
14
15
  import { ExplorerDialogs } from "../explorer-dialogs";
16
+ import type { DroppedEntry } from "../upload/collect-dropped-entries";
15
17
  import { transferRunner, type DropRunner } from "./entry-drop-executor";
16
18
 
17
19
  export interface DropTransfer {
18
20
  run: DropRunner;
21
+ /** Upload dropped OS files into a directory, through the same collision prompt as `run`. */
22
+ uploadRun: (entries: DroppedEntry[], dstDir: string) => Promise<void>;
19
23
  /** Render this somewhere inside the surface — it is empty until a prompt is raised. */
20
24
  prompts: ReactNode;
21
25
  }
@@ -60,6 +64,10 @@ export function useDropTransfer(sep: string, platform?: string): DropTransfer {
60
64
  );
61
65
 
62
66
  const run = useMemo<DropRunner>(() => transferRunner(context), [context]);
67
+ const uploadRun = useMemo(
68
+ () => (entries: DroppedEntry[], dstDir: string) => uploadEntries(entries, dstDir, sepRef.current, context),
69
+ [context],
70
+ );
63
71
 
64
72
  const prompts = (
65
73
  <ExplorerDialogs
@@ -72,11 +80,12 @@ export function useDropTransfer(sep: string, platform?: string): DropTransfer {
72
80
  closeDelete: () => {},
73
81
  runPermanentDelete: () => {},
74
82
  closeProperties: () => {},
83
+ uploadInputs: null,
75
84
  }}
76
85
  platform={platform}
77
86
  sep={sep}
78
87
  />
79
88
  );
80
89
 
81
- return { run, prompts };
90
+ return { run, uploadRun, prompts };
82
91
  }
@@ -10,6 +10,7 @@
10
10
 
11
11
  import { useMemo, type DragEvent } from "react";
12
12
  import type { FsEntry } from "@/lib/fs-api";
13
+ import type { DroppedEntry } from "../upload/collect-dropped-entries";
13
14
  import type { DropRunner } from "./entry-drop-executor";
14
15
  import type { EntryDragOrigin } from "./entry-drag-payload";
15
16
  import { useEntryDragSource } from "./use-entry-drag-source";
@@ -36,6 +37,9 @@ export interface EntryRowDndOptions {
36
37
  projectName?: string;
37
38
  extraData?: Record<string, string>;
38
39
  onDragStarted?(): void;
40
+ /** Also accept a native OS file drag onto this row when it is a directory. Omitted where
41
+ * the caller has no upload destination (e.g. a surface with no window action set). */
42
+ onFiles?: (entries: DroppedEntry[], targetDir: string) => void;
39
43
  }
40
44
 
41
45
  export interface EntryRowDnd {
@@ -45,7 +49,9 @@ export interface EntryRowDnd {
45
49
  }
46
50
 
47
51
  export function useEntryRowDnd(options: EntryRowDndOptions): EntryRowDnd {
48
- const { entry, dragPaths, run, springLoad, origin = "explorer", projectName, extraData, onDragStarted } = options;
52
+ const {
53
+ entry, dragPaths, run, springLoad, origin = "explorer", projectName, extraData, onDragStarted, onFiles,
54
+ } = options;
49
55
 
50
56
  const source = useEntryDragSource({ paths: dragPaths, origin, projectName, extraData, onDragStarted });
51
57
 
@@ -55,6 +61,7 @@ export function useEntryRowDnd(options: EntryRowDndOptions): EntryRowDnd {
55
61
  targetDir: entry.type === "directory" ? entry.path : null,
56
62
  run,
57
63
  springLoad: entry.type === "directory" ? springLoad : undefined,
64
+ onFiles,
58
65
  });
59
66
 
60
67
  const props = useMemo<EntryRowDndProps>(
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Drop target for a native OS `Files` drag — the upload counterpart to `useEntryDropTarget`
3
+ * (PPM's own entry drags). The two never overlap: an entry drag carries `ENTRY_DRAG_MIME` but
4
+ * never `Files` (see `use-entry-drag-source.ts`), so a caller wires both hooks' handlers onto
5
+ * the same element — see `usePathDropTarget` — and whichever drag kind is actually happening
6
+ * is the only one that ever calls `preventDefault`.
7
+ *
8
+ * `isExternalFileDrag` is the project tree's own check (`use-tree-row-dnd.ts`), reused rather
9
+ * than redeclared: it excludes the tree's legacy single-path MIME, which an entry drag never
10
+ * carries either, so the same test is exactly as correct here.
11
+ */
12
+
13
+ import { useCallback, useRef, useState, type DragEvent } from "react";
14
+ import { isExternalFileDrag } from "@/components/explorer/use-tree-row-dnd";
15
+ import { collectDroppedEntries, type DroppedEntry } from "../upload/collect-dropped-entries";
16
+ import type { EntryDropTargetResult } from "./use-entry-drop-target";
17
+
18
+ export interface ExternalFileDropOptions {
19
+ /** Directory a drop uploads into; null disables the target. */
20
+ targetDir: string | null;
21
+ onFiles(entries: DroppedEntry[], targetDir: string): void;
22
+ disabled?: boolean;
23
+ }
24
+
25
+ export function useExternalFileDrop(options: ExternalFileDropOptions): EntryDropTargetResult {
26
+ const { targetDir, onFiles, disabled } = options;
27
+ const [isOver, setIsOver] = useState(false);
28
+ // Nested children fire their own enter/leave pairs; only a balanced count means "left".
29
+ const depth = useRef(0);
30
+
31
+ const reset = useCallback(() => {
32
+ depth.current = 0;
33
+ setIsOver(false);
34
+ }, []);
35
+
36
+ const applies = useCallback(
37
+ (event: DragEvent) => !disabled && targetDir != null && isExternalFileDrag(event),
38
+ [disabled, targetDir],
39
+ );
40
+
41
+ const onDragEnter = useCallback(
42
+ (event: DragEvent) => {
43
+ if (!applies(event)) return;
44
+ event.preventDefault();
45
+ event.stopPropagation();
46
+ depth.current++;
47
+ setIsOver(true);
48
+ },
49
+ [applies],
50
+ );
51
+
52
+ const onDragOver = useCallback(
53
+ (event: DragEvent) => {
54
+ if (!applies(event)) return;
55
+ event.preventDefault();
56
+ event.stopPropagation();
57
+ event.dataTransfer.dropEffect = "copy";
58
+ },
59
+ [applies],
60
+ );
61
+
62
+ const onDragLeave = useCallback((event: DragEvent) => {
63
+ if (depth.current === 0) return;
64
+ event.stopPropagation();
65
+ depth.current--;
66
+ if (depth.current === 0) setIsOver(false);
67
+ }, []);
68
+
69
+ const onDrop = useCallback(
70
+ (event: DragEvent) => {
71
+ if (!applies(event) || targetDir == null) return;
72
+ event.preventDefault();
73
+ event.stopPropagation();
74
+ const dataTransfer = event.dataTransfer;
75
+ reset();
76
+ void collectDroppedEntries(dataTransfer).then((entries) => {
77
+ if (entries.length > 0) onFiles(entries, targetDir);
78
+ });
79
+ },
80
+ [applies, targetDir, onFiles, reset],
81
+ );
82
+
83
+ return { isOver, handlers: { onDragEnter, onDragOver, onDragLeave, onDrop } };
84
+ }
@@ -1,12 +1,19 @@
1
1
  /**
2
2
  * Drop target for a surface that already knows its destination directory and has no drag
3
3
  * source of its own: a view background, a sidebar place, a breadcrumb crumb, the tree root.
4
+ *
5
+ * When `onFiles` is supplied this also accepts a native OS `Files` drag on the same element —
6
+ * the drag-to-upload entry point. The two hooks underneath are mutually exclusive by MIME
7
+ * type (see `use-external-file-drop.ts`), so merging their handlers is safe: whichever drag
8
+ * kind is actually happening is the only one that ever calls `preventDefault`.
4
9
  */
5
10
 
6
- import { useCallback } from "react";
11
+ import { useCallback, type DragEvent } from "react";
7
12
  import { executeEntryDrop, type DropRunner } from "./entry-drop-executor";
8
13
  import type { DropOperation, EntryDragPayload } from "./entry-drag-payload";
9
14
  import { useEntryDropTarget, type EntryDropTargetResult } from "./use-entry-drop-target";
15
+ import { useExternalFileDrop } from "./use-external-file-drop";
16
+ import type { DroppedEntry } from "../upload/collect-dropped-entries";
10
17
 
11
18
  export interface PathDropTargetOptions {
12
19
  targetDir: string | null;
@@ -14,10 +21,15 @@ export interface PathDropTargetOptions {
14
21
  /** Opens this destination after a rest — the project tree and Column view use it. */
15
22
  springLoad?: () => void;
16
23
  disabled?: boolean;
24
+ /** Also accept a native OS file drag on this same target. Omitted where the caller has no
25
+ * upload destination context to hand the dropped files to. */
26
+ onFiles?: (entries: DroppedEntry[], targetDir: string) => void;
17
27
  }
18
28
 
29
+ function noopFiles(): void {}
30
+
19
31
  export function usePathDropTarget(options: PathDropTargetOptions): EntryDropTargetResult {
20
- const { targetDir, run, springLoad, disabled } = options;
32
+ const { targetDir, run, springLoad, disabled, onFiles } = options;
21
33
 
22
34
  const onDropEntries = useCallback(
23
35
  (payload: EntryDragPayload, op: DropOperation, dstDir: string) => {
@@ -26,5 +38,36 @@ export function usePathDropTarget(options: PathDropTargetOptions): EntryDropTarg
26
38
  [run],
27
39
  );
28
40
 
29
- return useEntryDropTarget({ targetDir, onDropEntries, springLoad, disabled });
41
+ const entryDrop = useEntryDropTarget({ targetDir, onDropEntries, springLoad, disabled });
42
+ // Always called (Rules of Hooks) — `disabled` is what actually turns it off when the
43
+ // caller has no upload destination for this target.
44
+ const fileDrop = useExternalFileDrop({
45
+ targetDir,
46
+ onFiles: onFiles ?? noopFiles,
47
+ disabled: disabled || !onFiles,
48
+ });
49
+
50
+ if (!onFiles) return entryDrop;
51
+
52
+ return {
53
+ isOver: entryDrop.isOver || fileDrop.isOver,
54
+ handlers: {
55
+ onDragEnter: (event: DragEvent) => {
56
+ entryDrop.handlers.onDragEnter(event);
57
+ fileDrop.handlers.onDragEnter(event);
58
+ },
59
+ onDragOver: (event: DragEvent) => {
60
+ entryDrop.handlers.onDragOver(event);
61
+ fileDrop.handlers.onDragOver(event);
62
+ },
63
+ onDragLeave: (event: DragEvent) => {
64
+ entryDrop.handlers.onDragLeave(event);
65
+ fileDrop.handlers.onDragLeave(event);
66
+ },
67
+ onDrop: (event: DragEvent) => {
68
+ entryDrop.handlers.onDrop(event);
69
+ fileDrop.handlers.onDrop(event);
70
+ },
71
+ },
72
+ };
30
73
  }
@@ -99,7 +99,7 @@ export function ExplorerBody({ windowId, initialPath, variant = "window" }: Expl
99
99
  <MobilePlacesStrip host={host} currentPath={slice.path} onNavigate={nav.go} vocab={skin.vocab} />
100
100
  </>
101
101
  ) : (
102
- <ExplorerToolbar windowId={windowId} slice={slice} nav={nav} />
102
+ <ExplorerToolbar windowId={windowId} slice={slice} nav={nav} actions={actions} />
103
103
  )}
104
104
 
105
105
  {slice.error && (
@@ -54,6 +54,13 @@ export function ExplorerContextMenu({
54
54
  <ContextMenuSeparator />
55
55
  <ContextMenuItem disabled={!hasClipboard} onClick={() => actions.paste()}>Paste</ContextMenuItem>
56
56
  <ContextMenuSeparator />
57
+ <ContextMenuItem onClick={() => actions.openUploadPicker()}>Upload Files…</ContextMenuItem>
58
+ {actions.supportsFolderUpload && (
59
+ <ContextMenuItem onClick={() => actions.openUploadFolderPicker()}>Upload Folder…</ContextMenuItem>
60
+ )}
61
+ <ContextMenuSeparator />
62
+ <ContextMenuItem onClick={() => actions.openInTerminal()}>Open in Terminal</ContextMenuItem>
63
+ <ContextMenuSeparator />
57
64
  <ContextMenuSub>
58
65
  <ContextMenuSubTrigger>Sort by</ContextMenuSubTrigger>
59
66
  <ContextMenuSubContent>
@@ -92,6 +99,9 @@ export function ExplorerContextMenu({
92
99
  Open in New Window
93
100
  </ContextMenuItem>
94
101
  )}
102
+ {onlyDirs && single && (
103
+ <ContextMenuItem onClick={() => actions.openInTerminal(single)}>Open in Terminal</ContextMenuItem>
104
+ )}
95
105
  <ContextMenuSeparator />
96
106
 
97
107
  <ContextMenuItem onClick={() => actions.cut(targets)}>Cut{suffix}</ContextMenuItem>
@@ -23,6 +23,8 @@ export function ExplorerDialogs({ dialogs, platform, sep }: ExplorerDialogsProps
23
23
 
24
24
  return (
25
25
  <>
26
+ {dialogs.uploadInputs}
27
+
26
28
  {collision && (
27
29
  <ExplorerModalShell
28
30
  open
@@ -14,7 +14,7 @@ import { cn } from "@/lib/utils";
14
14
  import { DROP_TARGET_CLASS } from "./dnd/drop-target-style";
15
15
  import type { DropRunner } from "./dnd/entry-drop-executor";
16
16
  import { usePathDropTarget } from "./dnd/use-path-drop-target";
17
- import { useDropTransfer } from "./dnd/use-drop-transfer";
17
+ import { useDropTransfer, type DropTransfer } from "./dnd/use-drop-transfer";
18
18
  import { useExplorerPinsStore } from "./explorer-pins-store";
19
19
  import { FileTypeIcon } from "./icons/file-type-icon";
20
20
  import type { SkinVocab } from "./skins/skin-types";
@@ -43,13 +43,14 @@ interface PlaceProps {
43
43
  onUnpin?(): void;
44
44
  /** Every place is a directory, so a drop always has somewhere valid to land. */
45
45
  run: DropRunner;
46
+ uploadRun: DropTransfer["uploadRun"];
46
47
  }
47
48
 
48
- function Place({ label, path, active, icon: Icon, onNavigate, onUnpin, run }: PlaceProps) {
49
+ function Place({ label, path, active, icon: Icon, onNavigate, onUnpin, run, uploadRun }: PlaceProps) {
49
50
  // Gated on real pointer coarseness, not a `md:` breakpoint — an iPad is ≥768px but has no
50
51
  // mouse, so a breakpoint-only rule was silently dropping the 44px minimum for it.
51
52
  const coarse = usePrefersCoarsePointer();
52
- const drop = usePathDropTarget({ targetDir: path, run });
53
+ const drop = usePathDropTarget({ targetDir: path, run, onFiles: uploadRun });
53
54
  return (
54
55
  <div className="group/place relative flex items-center">
55
56
  <button
@@ -112,7 +113,7 @@ export function ExplorerSidebar({ host, currentPath, onNavigate, vocab = DEFAULT
112
113
  const isActive = (path: string) => path === currentPath;
113
114
  // One drop-transfer context for the whole sidebar — every place lands through the same
114
115
  // collision prompt rather than each row owning its own dialog state.
115
- const { run, prompts } = useDropTransfer(host?.sep ?? "/", host?.platform);
116
+ const { run, uploadRun, prompts } = useDropTransfer(host?.sep ?? "/", host?.platform);
116
117
 
117
118
  return (
118
119
  <aside
@@ -132,6 +133,7 @@ export function ExplorerSidebar({ host, currentPath, onNavigate, vocab = DEFAULT
132
133
  onNavigate={onNavigate}
133
134
  onUnpin={() => unpin(pin.path)}
134
135
  run={run}
136
+ uploadRun={uploadRun}
135
137
  />
136
138
  ))}
137
139
  </Section>
@@ -148,6 +150,7 @@ export function ExplorerSidebar({ host, currentPath, onNavigate, vocab = DEFAULT
148
150
  active={isActive(entry.path)}
149
151
  onNavigate={onNavigate}
150
152
  run={run}
153
+ uploadRun={uploadRun}
151
154
  />
152
155
  ))}
153
156
  </Section>
@@ -155,7 +158,7 @@ export function ExplorerSidebar({ host, currentPath, onNavigate, vocab = DEFAULT
155
158
 
156
159
  {host && (
157
160
  <Section title={vocab.known}>
158
- <Place label={vocab.home} path={host.homedir} icon={FolderPlaceIcon} active={isActive(host.homedir)} onNavigate={onNavigate} run={run} />
161
+ <Place label={vocab.home} path={host.homedir} icon={FolderPlaceIcon} active={isActive(host.homedir)} onNavigate={onNavigate} run={run} uploadRun={uploadRun} />
159
162
  {host.knownFolders.map((folder) => (
160
163
  <Place
161
164
  key={folder.path}
@@ -165,6 +168,7 @@ export function ExplorerSidebar({ host, currentPath, onNavigate, vocab = DEFAULT
165
168
  active={isActive(folder.path)}
166
169
  onNavigate={onNavigate}
167
170
  run={run}
171
+ uploadRun={uploadRun}
168
172
  />
169
173
  ))}
170
174
  </Section>
@@ -181,6 +185,7 @@ export function ExplorerSidebar({ host, currentPath, onNavigate, vocab = DEFAULT
181
185
  active={isActive(drive.path)}
182
186
  onNavigate={onNavigate}
183
187
  run={run}
188
+ uploadRun={uploadRun}
184
189
  />
185
190
  ))}
186
191
  </Section>
@@ -9,9 +9,14 @@
9
9
 
10
10
  import { useEffect, useState } from "react";
11
11
  import {
12
- ArrowLeft, ArrowRight, ArrowUp, Columns3, Eye, EyeOff, LayoutGrid, List, PencilLine, RefreshCw, Search, X,
12
+ ArrowLeft, ArrowRight, ArrowUp, Columns3, Eye, EyeOff, LayoutGrid, List, PencilLine, RefreshCw, Search,
13
+ Upload, X,
13
14
  } from "lucide-react";
15
+ import {
16
+ DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger,
17
+ } from "@/components/ui/dropdown-menu";
14
18
  import { cn } from "@/lib/utils";
19
+ import type { ExplorerActions } from "./actions/use-explorer-actions";
15
20
  import { ExplorerBreadcrumb } from "./explorer-breadcrumb";
16
21
  import { ExplorerSortMenu } from "./explorer-sort-menu";
17
22
  import { useExplorerStore, type ExplorerSlice, type ViewMode } from "./explorer-store";
@@ -27,9 +32,10 @@ export interface ExplorerToolbarProps {
27
32
  windowId: string;
28
33
  slice: ExplorerSlice;
29
34
  nav: ExplorerNavigation;
35
+ actions: ExplorerActions;
30
36
  }
31
37
 
32
- export function ExplorerToolbar({ windowId, slice, nav }: ExplorerToolbarProps) {
38
+ export function ExplorerToolbar({ windowId, slice, nav, actions }: ExplorerToolbarProps) {
33
39
  const patch = useExplorerStore((s) => s.patch);
34
40
  const showHidden = useExplorerStore((s) => s.showHidden);
35
41
  const viewMode = useExplorerStore((s) => s.viewMode);
@@ -124,6 +130,30 @@ export function ExplorerToolbar({ windowId, slice, nav }: ExplorerToolbarProps)
124
130
 
125
131
  <ExplorerSortMenu coarse={coarse} />
126
132
 
133
+ {actions.supportsFolderUpload ? (
134
+ <DropdownMenu>
135
+ <DropdownMenuTrigger asChild>
136
+ <button type="button" aria-label="Upload" title="Upload" className={buttonClass(coarse)}>
137
+ <Upload className="size-4" />
138
+ </button>
139
+ </DropdownMenuTrigger>
140
+ <DropdownMenuContent align="end">
141
+ <DropdownMenuItem onClick={() => actions.openUploadPicker()}>Upload Files…</DropdownMenuItem>
142
+ <DropdownMenuItem onClick={() => actions.openUploadFolderPicker()}>Upload Folder…</DropdownMenuItem>
143
+ </DropdownMenuContent>
144
+ </DropdownMenu>
145
+ ) : (
146
+ <button
147
+ type="button"
148
+ aria-label="Upload"
149
+ title="Upload"
150
+ onClick={() => actions.openUploadPicker()}
151
+ className={buttonClass(coarse)}
152
+ >
153
+ <Upload className="size-4" />
154
+ </button>
155
+ )}
156
+
127
157
  <button
128
158
  type="button"
129
159
  aria-label={showHidden ? "Hide hidden files" : "Show hidden files"}
@@ -8,7 +8,8 @@
8
8
 
9
9
  import { useState } from "react";
10
10
  import {
11
- ArrowLeft, Check, ClipboardPaste, Copy, Download, FilePlus, FolderPlus, Info, MoreHorizontal, Plus, Scissors, Trash2, X,
11
+ ArrowLeft, Check, ClipboardPaste, Copy, Download, FilePlus, FolderPlus, Info, MoreHorizontal, Plus, Scissors,
12
+ Trash2, Upload, X,
12
13
  } from "lucide-react";
13
14
  import type { FsEntry } from "@/lib/fs-api";
14
15
  import { BottomSheet, BottomSheetItem } from "@/components/ui/mobile-bottom-sheet";
@@ -94,6 +95,9 @@ export function MobileBottomToolbar({ slice, nav, actions, entries, hasClipboard
94
95
  <BottomSheetItem onClick={() => actions.startCreate("new-folder")}>
95
96
  <FolderPlus className="size-4" /> New Folder
96
97
  </BottomSheetItem>
98
+ <BottomSheetItem onClick={() => { setNewOpen(false); actions.openUploadPicker(); }}>
99
+ <Upload className="size-4" /> Upload Files…
100
+ </BottomSheetItem>
97
101
  </BottomSheet>
98
102
 
99
103
  <MobileMoreSheet open={moreOpen} onClose={() => setMoreOpen(false)} slice={slice} actions={actions} entries={entries} />