@hienlh/ppm 0.21.0 → 0.22.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 (620) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/CLAUDE.md +29 -4
  3. package/THIRD-PARTY-NOTICES.md +1 -1
  4. package/assets/skills/ppm/SKILL.md +1 -1
  5. package/assets/skills/ppm/references/cli-reference.md +4 -4
  6. package/assets/skills/ppm/references/http-api.md +1 -1
  7. package/bun.lock +16 -1
  8. package/dist/web/assets/accounts-settings-section-D3BylchP.js +1 -0
  9. package/dist/web/assets/{ai-resource-editor-F09pf8Hd.js → ai-resource-editor-BLPcnhTM.js} +1 -1
  10. package/dist/web/assets/ai-settings-section-BtR7rF-T.js +1 -0
  11. package/dist/web/assets/ai-settings-section-r9JctHNM.js +1 -0
  12. package/dist/web/assets/{angular-html-DkR4iCth.js → angular-html-G66FNlyn.js} +1 -1
  13. package/dist/web/assets/{angular-ts-DcyL4Chx.js → angular-ts-B-PnUMyt.js} +1 -1
  14. package/dist/web/assets/{api-client-bfB7cXRM.js → api-client-C1ZwRL4Y.js} +1 -1
  15. package/dist/web/assets/api-client-pQL53cDQ.js +1 -0
  16. package/dist/web/assets/{api-settings-BcNjZIvG.js → api-settings-_7Q-ULxp.js} +1 -1
  17. package/dist/web/assets/{api-themes-DBfyVUtX.js → api-themes-DRAERo4Q.js} +1 -1
  18. package/dist/web/assets/{apl-BVlrVtjt.js → apl-BmDqqwE8.js} +1 -1
  19. package/dist/web/assets/{appearance-settings-section-whgnq3ss.js → appearance-settings-section-CiMdk3sb.js} +1 -1
  20. package/dist/web/assets/architecture-PBZL5I3N-Dx6nBEKT.js +1 -0
  21. package/dist/web/assets/{astro-MjJlozLj.js → astro-BfwMNfxr.js} +1 -1
  22. package/dist/web/assets/{audio-preview-Bc54GCAV.js → audio-preview-DsYvgKqj.js} +1 -1
  23. package/dist/web/assets/{blade-DnJWi1fz.js → blade-BsFV4pnN.js} +1 -1
  24. package/dist/web/assets/branch-review-tab-B1pFjlKM.js +1 -0
  25. package/dist/web/assets/{bsl-DCmJxSIz.js → bsl-Cw9Outng.js} +1 -1
  26. package/dist/web/assets/{button-VW4QGKCn.js → button-B9CTrsEf.js} +1 -1
  27. package/dist/web/assets/{cairo-C8dDenub.js → cairo-DOK5Wqnd.js} +1 -1
  28. package/dist/web/assets/chat-tab-DZagX06m.js +15 -0
  29. package/dist/web/assets/{cobol-DPx_nifl.js → cobol-DRaRy4FK.js} +1 -1
  30. package/dist/web/assets/code-editor-BJhOEsnZ.js +7 -0
  31. package/dist/web/assets/{codex-daily-guard-CfybHv_c.js → codex-daily-guard-CaCQJ4r_.js} +1 -1
  32. package/dist/web/assets/{coffee-DvFzqhgN.js → coffee-DvXuBmEJ.js} +1 -1
  33. package/dist/web/assets/{conflict-editor-DenQUOT2.js → conflict-editor-B7u_p49h.js} +3 -3
  34. package/dist/web/assets/context-menu-C6pIsf83.js +1 -0
  35. package/dist/web/assets/{cpp-BLJN0umi.js → cpp-zCmm8rKA.js} +1 -1
  36. package/dist/web/assets/{crystal-BXR-9fGw.js → crystal-CdQx6lB6.js} +1 -1
  37. package/dist/web/assets/{csv-preview-CClrJOuR.js → csv-preview-CttjJU1e.js} +1 -1
  38. package/dist/web/assets/{data-grid-overlay-editor-D4fhcMfT.js → data-grid-overlay-editor-6xUCeLHh.js} +1 -1
  39. package/dist/web/assets/{database-viewer-DItjm8wB.js → database-viewer-Ca4HxIfx.js} +1 -1
  40. package/dist/web/assets/{dialog-DQTn55Mt.js → dialog-B84_DJQ8.js} +1 -1
  41. package/dist/web/assets/diff-viewer-9-XjLIwa.js +4 -0
  42. package/dist/web/assets/{docx-preview-DYBkiUaE.js → docx-preview-CXuk-emm.js} +1 -1
  43. package/dist/web/assets/{edge-rWOlCmXT.js → edge-E8QLt7oi.js} +1 -1
  44. package/dist/web/assets/{edit-diff-preview-COtmo54G.js → edit-diff-preview-BGGGecVw.js} +1 -1
  45. package/dist/web/assets/{editor-language-service-DxkZ51E5.js → editor-language-service-D0DuEOs_.js} +1 -1
  46. package/dist/web/assets/{elixir-E7Y82okW.js → elixir-DPb_-72P.js} +1 -1
  47. package/dist/web/assets/{elm-DTPGbc-w.js → elm-BMzJY5b0.js} +1 -1
  48. package/dist/web/assets/{erb-DHqLHrE6.js → erb-AABQNtMK.js} +1 -1
  49. package/dist/web/assets/{erlang-Byg0nNiV.js → erlang-CEIKOkfP.js} +1 -1
  50. package/dist/web/assets/{esm-D_DmOM7n.js → esm-B9RVMpwV.js} +1 -1
  51. package/dist/web/assets/explorer-body-VdCBqcZu.js +2 -0
  52. package/dist/web/assets/explorer-window-content-BIsbl7Th.js +1 -0
  53. package/dist/web/assets/extension-manager-section-HkY7geS5.js +1 -0
  54. package/dist/web/assets/{extension-webview-DI4VL2tJ.js → extension-webview-DZG8YHX9.js} +1 -1
  55. package/dist/web/assets/file-icons-BXdvggK2.js +1 -0
  56. package/dist/web/assets/file-icons.generated-BtBErEwb.css +1 -0
  57. package/dist/web/assets/file-store-ojgMXgAw.js +1 -0
  58. package/dist/web/assets/files-settings-section-qnRtkX05.js +1 -0
  59. package/dist/web/assets/{format-file-meta-DOpzJJhS.js → format-file-meta-C77h04cZ.js} +1 -1
  60. package/dist/web/assets/{fortran-fixed-form-CY1n0Xf6.js → fortran-fixed-form-CrXvLHyi.js} +1 -1
  61. package/dist/web/assets/{fsharp-_vEDSCJz.js → fsharp-DCI4qAz_.js} +1 -1
  62. package/dist/web/assets/{gdresource-B6XT0wAC.js → gdresource-Vb2kO1MG.js} +1 -1
  63. package/dist/web/assets/{general-settings-section-BcxwiZ0X.js → general-settings-section-BAFSZmCE.js} +1 -1
  64. package/dist/web/assets/{git-commit-Wk9ovq59.js → git-commit-BMI_OWn-.js} +1 -1
  65. package/dist/web/assets/git-log-panel-2sGpfEla.js +1 -0
  66. package/dist/web/assets/{git-rebase-DSarGPvK.js → git-rebase-D-d_2iqZ.js} +1 -1
  67. package/dist/web/assets/{git-repo-store-Dy69YCHN.js → git-repo-store-88Z8v-pF.js} +1 -1
  68. package/dist/web/assets/gitGraph-HDMCJU4V-C6qRruHI.js +1 -0
  69. package/dist/web/assets/{glide-data-grid-BvgAqSUr.js → glide-data-grid-f0hyITiF.js} +7 -7
  70. package/dist/web/assets/{glimmer-js-CkYI35_P.js → glimmer-js-BmNNiR0Z.js} +1 -1
  71. package/dist/web/assets/{glimmer-ts-BqmIZcds.js → glimmer-ts-DQE2Ufha.js} +1 -1
  72. package/dist/web/assets/{glsl-Be-yBCF5.js → glsl-BcIDsxRJ.js} +1 -1
  73. package/dist/web/assets/{graphql-a5DrENHY.js → graphql-DGKSLmGH.js} +1 -1
  74. package/dist/web/assets/group-chat-tab-Ck6sk7yY.js +1 -0
  75. package/dist/web/assets/{hack-BLAg20aD.js → hack-YiC8bEIb.js} +1 -1
  76. package/dist/web/assets/{haml-Dj6bGOQH.js → haml-DjblxAsz.js} +1 -1
  77. package/dist/web/assets/{handlebars-dmEa36Oh.js → handlebars-BQepOXaU.js} +1 -1
  78. package/dist/web/assets/{html-yacZYzSE.js → html-Cayj_2GP.js} +1 -1
  79. package/dist/web/assets/{html-derivative-lLi3-RNT.js → html-derivative-Weq79bL6.js} +1 -1
  80. package/dist/web/assets/{http-1KaxcE4e.js → http-BeLfdGdz.js} +1 -1
  81. package/dist/web/assets/{hurl-ST-dEEZR.js → hurl-D9rypCLK.js} +1 -1
  82. package/dist/web/assets/{hxml-CpDtuq__.js → hxml-clG8mmSH.js} +1 -1
  83. package/dist/web/assets/{image-preview-CJVxL0uH.js → image-preview-CzCc4_Do.js} +1 -1
  84. package/dist/web/assets/image-preview-geometry-9lgeLYB3.js +1 -0
  85. package/dist/web/assets/index-BH99TIE0.css +1 -0
  86. package/dist/web/assets/index-CEZVxM4G.js +52 -0
  87. package/dist/web/assets/info-3K5VOQVL-BOSpdwlz.js +1 -0
  88. package/dist/web/assets/{jinja-Cjw-evAn.js → jinja-BShBWfs5.js} +1 -1
  89. package/dist/web/assets/{jira-watcher-section-DIC1yIV3.js → jira-watcher-section-CeLmhvu1.js} +1 -1
  90. package/dist/web/assets/{jison-DfATMYoY.js → jison-dN2Nf_Dr.js} +1 -1
  91. package/dist/web/assets/{julia-DDCaxiqT.js → julia-5U0vR-uJ.js} +1 -1
  92. package/dist/web/assets/{just-CJL529u6.js → just-BMppVRy6.js} +1 -1
  93. package/dist/web/assets/keybindings-store-C_3ChOEf.js +1 -0
  94. package/dist/web/assets/{keyboard-shortcuts-section-BW03P3AF.js → keyboard-shortcuts-section-PVz6kuh2.js} +1 -1
  95. package/dist/web/assets/{latex-Zc4EpLpW.js → latex-DSSOKjis.js} +1 -1
  96. package/dist/web/assets/{liquid-B_6KMNYS.js → liquid-C24dKoyI.js} +1 -1
  97. package/dist/web/assets/{lua-BtS2idK7.js → lua-DNOsnyhn.js} +1 -1
  98. package/dist/web/assets/{markdown-renderer-CvL1pWeK.js → markdown-renderer-C140Ijk0.js} +3 -3
  99. package/dist/web/assets/markdown-renderer-Dz7-DYUg.js +1 -0
  100. package/dist/web/assets/{marko-Wk6LBnck.js → marko-26MHO_Q9.js} +1 -1
  101. package/dist/web/assets/{mdc-cRaXBrzL.js → mdc-Dhw9mGrC.js} +1 -1
  102. package/dist/web/assets/{media-url-DL8OCGGg.js → media-url-yfqHpPz0.js} +1 -1
  103. package/dist/web/assets/{mobile-bottom-sheet-CoUauvLF.js → mobile-bottom-sheet-C3TniEP-.js} +1 -1
  104. package/dist/web/assets/mobile-explorer-sheet-CUG_owdf.js +1 -0
  105. package/dist/web/assets/{nextflow-Bos2wvDV.js → nextflow-xH4oJTex.js} +1 -1
  106. package/dist/web/assets/{nginx-DMlOwzdr.js → nginx-CMz4w0M8.js} +1 -1
  107. package/dist/web/assets/{nim-D2dZQA88.js → nim-D8FnIYsx.js} +1 -1
  108. package/dist/web/assets/notification-store-6vib1ISy.js +1 -0
  109. package/dist/web/assets/{notifications-settings-section-DPUROMw-.js → notifications-settings-section-DqlPDfNX.js} +1 -1
  110. package/dist/web/assets/{number-overlay-editor-DZ2i_Bnb.js → number-overlay-editor-CRGwULhR.js} +1 -1
  111. package/dist/web/assets/{overview-panel-R146OIom.js → overview-panel-Di-3aV9p.js} +1 -1
  112. package/dist/web/assets/packet-RMMSAZCW-BGIE1QdU.js +1 -0
  113. package/dist/web/assets/panel-store-CboGJAwh.js +1 -0
  114. package/dist/web/assets/{pdf-preview-BWUPGLp6.js → pdf-preview-CmBI6E7d.js} +1 -1
  115. package/dist/web/assets/{perl-C5-Z6fPn.js → perl-D9iNWYwb.js} +1 -1
  116. package/dist/web/assets/{php-BXNNhkMH.js → php-Bw3Qo0Zv.js} +1 -1
  117. package/dist/web/assets/pie-UPGHQEXC-Dg-QTbXX.js +1 -0
  118. package/dist/web/assets/{postgres-viewer-CjOvaruz.js → postgres-viewer-8ZAqEALv.js} +3 -3
  119. package/dist/web/assets/{ppmbot-settings-section-DrvWh8l8.js → ppmbot-settings-section-Cji-Mil-.js} +1 -1
  120. package/dist/web/assets/problems-panel-a6_hrWvl.js +1 -0
  121. package/dist/web/assets/{process-table-BRMS1MrX.js → process-table-CiIqlOIK.js} +1 -1
  122. package/dist/web/assets/project-framework-store-B70Q3dNM.js +1 -0
  123. package/dist/web/assets/project-store-B87hLRSk.js +1 -0
  124. package/dist/web/assets/{proxy-settings-section-Bv0Ny-Yy.js → proxy-settings-section-BjI4IFgp.js} +1 -1
  125. package/dist/web/assets/{pug-C1zf1yWy.js → pug-fT5JC6Vm.js} +1 -1
  126. package/dist/web/assets/{qml-OW58jGpD.js → qml-P_wEDB1w.js} +1 -1
  127. package/dist/web/assets/{query-audit-section-BcHJZcVC.js → query-audit-section-By6qyqdB.js} +1 -1
  128. package/dist/web/assets/radar-KQ55EAFF-DZ6IVu2s.js +1 -0
  129. package/dist/web/assets/{razor-DzjSaobM.js → razor-Drrt2_Ph.js} +1 -1
  130. package/dist/web/assets/remote-desktop-clipboard-notice-eXYvwgx7.js +1 -0
  131. package/dist/web/assets/remote-desktop-mobile-sheet-Doh-fci-.js +2 -0
  132. package/dist/web/assets/remote-desktop-mobile-view-Dt8Akc3p.js +1 -0
  133. package/dist/web/assets/{remote-desktop-readiness-gate-BTzss2wX.js → remote-desktop-readiness-gate-t83fqbID.js} +1 -1
  134. package/dist/web/assets/remote-desktop-window-content-CguhZMmx.js +1 -0
  135. package/dist/web/assets/{rst-RfuihOUW.js → rst-Dr2mW4-h.js} +1 -1
  136. package/dist/web/assets/{ruby-CNO3pli1.js → ruby-C6EpUsTY.js} +1 -1
  137. package/dist/web/assets/{run-in-terminal-Cb_hVU8x.js → run-in-terminal-CdefEe_R.js} +1 -1
  138. package/dist/web/assets/{sas-DZY5BtXx.js → sas-D8ZEeP6v.js} +1 -1
  139. package/dist/web/assets/{schedules-settings-section-DO8xAOVj.js → schedules-settings-section-BVkpW5ms.js} +1 -1
  140. package/dist/web/assets/{scroll-area-LtNV8g5o.js → scroll-area-BzeiOd-B.js} +1 -1
  141. package/dist/web/assets/{scss--4g-REcN.js → scss-ITL7jQ1f.js} +1 -1
  142. package/dist/web/assets/{send-to-chat-Cnpru1M0.js → send-to-chat-F207yH3u.js} +1 -1
  143. package/dist/web/assets/{separator-Cds1yxy8.js → separator-TRMsJUYe.js} +1 -1
  144. package/dist/web/assets/settings-body-CoIxYAh6.js +2 -0
  145. package/dist/web/assets/settings-store-krC6bc79.js +2 -0
  146. package/dist/web/assets/settings-tab-7DKVUTbM.js +1 -0
  147. package/dist/web/assets/settings-window-content-3_39TeX-.js +1 -0
  148. package/dist/web/assets/{shaderlab-Cu4sBiVn.js → shaderlab-mW-IIlhk.js} +1 -1
  149. package/dist/web/assets/{shellsession-DWzJfEv7.js → shellsession-DhNl5STt.js} +1 -1
  150. package/dist/web/assets/{soy-BnqE-5Ff.js → soy-SdY0Qjtr.js} +1 -1
  151. package/dist/web/assets/{sparql-Ctg__NhZ.js → sparql-ftqZo2i-.js} +1 -1
  152. package/dist/web/assets/{sql-query-editor-D9k19T_M.js → sql-query-editor-CQB7Yp7U.js} +1 -1
  153. package/dist/web/assets/{sqlite-viewer-DoV9hnWJ.js → sqlite-viewer-Bd7rjr1D.js} +1 -1
  154. package/dist/web/assets/{stata-Qk2h66XW.js → stata-Bj7Yjb4b.js} +1 -1
  155. package/dist/web/assets/{surrealql-8QEX7EfH.js → surrealql-CLhYGAfB.js} +1 -1
  156. package/dist/web/assets/{svelte-D_mubC5-.js → svelte-DTAlRHhK.js} +1 -1
  157. package/dist/web/assets/system-monitor-body-DsRXeMyU.js +2 -0
  158. package/dist/web/assets/system-monitor-tab-DdSZgux3.js +1 -0
  159. package/dist/web/assets/{system-monitor-window-content-qXLcicFM.js → system-monitor-window-content-ClPo4-El.js} +1 -1
  160. package/dist/web/assets/{tab-host-window-content-7fPZNEni.js → tab-host-window-content-d2TAD4PU.js} +1 -1
  161. package/dist/web/assets/{tab-store-DGJGnbwv.js → tab-store-OJa1GPCq.js} +1 -1
  162. package/dist/web/assets/team-member-sheet-7d2Qp-ie.js +2 -0
  163. package/dist/web/assets/{team-member-window-content-BgvJFypM.js → team-member-window-content-Bctg2gmg.js} +1 -1
  164. package/dist/web/assets/{templ-C1jV0fsO.js → templ-BMfhACxb.js} +1 -1
  165. package/dist/web/assets/{terminal-tab-Cs0sGljx.js → terminal-tab-0HQic25V.js} +3 -3
  166. package/dist/web/assets/{tex-D_StFXy_.js → tex-BcssM-QA.js} +1 -1
  167. package/dist/web/assets/tool-cards-CmLLRACd.js +9 -0
  168. package/dist/web/assets/treemap-KZPCXAKY-CE30wCXa.js +1 -0
  169. package/dist/web/assets/{ts-tags-B5m47rEm.js → ts-tags-CrR5IYI8.js} +1 -1
  170. package/dist/web/assets/{twig-C_br6GIl.js → twig-DchfUmyI.js} +1 -1
  171. package/dist/web/assets/{use-blob-url-C-SGi_Hb.js → use-blob-url-CPtCcY3i.js} +1 -1
  172. package/dist/web/assets/{use-inline-blame-B4trxGcR.js → use-inline-blame-sDMjcz1-.js} +3 -3
  173. package/dist/web/assets/{use-lsp-CV0vzILO.js → use-lsp-CBgID-EU.js} +1 -1
  174. package/dist/web/assets/{use-monaco-theme-DBqbWjWX.js → use-monaco-theme-gvhJ_jZh.js} +1 -1
  175. package/dist/web/assets/{use-open-team-member-DENsjp0s.js → use-open-team-member-C3mrUzIC.js} +1 -1
  176. package/dist/web/assets/{use-websocket-LgXiX1qN.js → use-websocket-C9wlRliB.js} +1 -1
  177. package/dist/web/assets/{vendor-mermaid-Cbig5zA6.js → vendor-mermaid-BitiXJlL.js} +1 -1
  178. package/dist/web/assets/{vendor-ui-BBdkypKW.js → vendor-ui-DmZ-Mb-H.js} +1 -1
  179. package/dist/web/assets/video-preview-CSDK5j6H.js +1 -0
  180. package/dist/web/assets/{vue-DQwCnLdr.js → vue-1EL6Pw1h.js} +1 -1
  181. package/dist/web/assets/{vue-html-T_EoGpPJ.js → vue-html-BPQNwPC6.js} +1 -1
  182. package/dist/web/assets/{vue-vine-DmxLqX50.js → vue-vine-Bb_dsWLt.js} +1 -1
  183. package/dist/web/assets/{window-pip-registry-3l4hoxuD.js → window-pip-registry-yD6esSju.js} +1 -1
  184. package/dist/web/assets/ws-auth-CEYVQ8NH.js +1 -0
  185. package/dist/web/assets/{xml-Bs6Bw8is.js → xml-dxkLNA0K.js} +1 -1
  186. package/dist/web/assets/{xsl-f5ZNhRCc.js → xsl-Bw2adwrl.js} +1 -1
  187. package/dist/web/index.html +27 -26
  188. package/dist/web/sw.js +1 -1
  189. package/docs/architecture/ai-chat-and-providers.md +9 -0
  190. package/docs/architecture/plugins-and-tracing.md +285 -0
  191. package/docs/lessons-learned.md +31 -0
  192. package/docs/system-architecture.md +1 -0
  193. package/package.json +2 -1
  194. package/packages/ext-git-graph/src/compare-diff-base.test.ts +173 -0
  195. package/packages/ext-git-graph/src/compare-view.ts +22 -3
  196. package/packages/ext-git-graph/src/extension.ts +6 -0
  197. package/packages/ext-git-graph/src/types.ts +19 -0
  198. package/packages/ext-git-graph/src/webview-html.test.ts +159 -1
  199. package/packages/ext-git-graph/src/webview-html.ts +344 -26
  200. package/scripts/gen-file-icons.ts +493 -242
  201. package/src/providers/claude-agent-sdk.ts +42 -1
  202. package/src/providers/codex-app-server/codex-history.ts +77 -23
  203. package/src/server/index.ts +42 -7
  204. package/src/server/routes/chat.ts +15 -11
  205. package/src/server/routes/git.ts +83 -3
  206. package/src/server/routes/remote-desktop.ts +7 -2
  207. package/src/server/ws/global.ts +1 -1
  208. package/src/server/ws/remote-desktop.ts +12 -5
  209. package/src/services/binary-content.ts +25 -0
  210. package/src/services/chat-search.service.ts +1 -1
  211. package/src/services/chat.service.ts +9 -0
  212. package/src/services/edge-forwarder.ts +191 -191
  213. package/src/services/event-loop-lag.ts +208 -0
  214. package/src/services/file-lines.ts +114 -0
  215. package/src/services/file-watcher/recreated-dir-poller.ts +108 -6
  216. package/src/services/file-watcher/watch-tree.ts +131 -10
  217. package/src/services/file-watcher.service.ts +73 -23
  218. package/src/services/git-branch-diff/branch-diff-parse.ts +151 -0
  219. package/src/services/git-branch-diff/branch-diff.service.ts +79 -0
  220. package/src/services/git-hunks/git-hunks.service.ts +26 -11
  221. package/src/services/git-hunks/unified-diff.ts +15 -1
  222. package/src/services/git.service.ts +74 -24
  223. package/src/services/jsonl-transcript-parser.ts +138 -6
  224. package/src/services/log-rotate.ts +142 -0
  225. package/src/services/media-transcode/ffmpeg-capabilities.ts +72 -2
  226. package/src/services/remote-desktop/remote-desktop-audio.ts +193 -0
  227. package/src/services/remote-desktop/remote-desktop-capture-input.ts +87 -14
  228. package/src/services/remote-desktop/remote-desktop-capture.ts +40 -7
  229. package/src/services/remote-desktop/remote-desktop-clipboard.ts +166 -0
  230. package/src/services/remote-desktop/remote-desktop-displays.ts +77 -1
  231. package/src/services/remote-desktop/remote-desktop-encoder-args.ts +25 -6
  232. package/src/services/remote-desktop/remote-desktop-evdev-key-map.ts +69 -0
  233. package/src/services/remote-desktop/remote-desktop-input-linux.ts +253 -0
  234. package/src/services/remote-desktop/remote-desktop-input-uinput.ts +242 -0
  235. package/src/services/remote-desktop/remote-desktop-input.ts +40 -8
  236. package/src/services/remote-desktop/remote-desktop-linux-session.ts +146 -0
  237. package/src/services/remote-desktop/remote-desktop-ogg-opus.ts +144 -0
  238. package/src/services/remote-desktop/remote-desktop-privacy.ts +163 -0
  239. package/src/services/remote-desktop/remote-desktop-quality.ts +138 -0
  240. package/src/services/remote-desktop/remote-desktop-requirements.ts +158 -5
  241. package/src/services/remote-desktop/remote-desktop-resolution.ts +302 -0
  242. package/src/services/remote-desktop/remote-desktop-session.ts +523 -21
  243. package/src/services/remote-desktop/remote-desktop-x11.ts +250 -0
  244. package/src/services/supervisor.ts +47 -4
  245. package/src/services/windows-process-tree.ts +351 -351
  246. package/src/shared/codex-daily-guard.ts +12 -5
  247. package/src/shared/remote-desktop-custom-quality.ts +117 -0
  248. package/src/shared/remote-desktop-quality.ts +127 -0
  249. package/src/types/chat.ts +4 -0
  250. package/src/types/git.ts +52 -0
  251. package/src/web/components/branch-review/branch-review-tab.tsx +360 -0
  252. package/src/web/components/branch-review/branch-review-tree-row.tsx +138 -0
  253. package/src/web/components/chat/attachment-chips.tsx +30 -6
  254. package/src/web/components/chat/chat-tab.tsx +12 -1
  255. package/src/web/components/chat/message-events.tsx +273 -0
  256. package/src/web/components/chat/message-list.tsx +107 -861
  257. package/src/web/components/chat/message-markdown.tsx +34 -0
  258. package/src/web/components/chat/message-media.tsx +174 -0
  259. package/src/web/components/chat/message-user-bubble.tsx +399 -0
  260. package/src/web/components/chat/provider-selector.tsx +30 -22
  261. package/src/web/components/chat/session-history-list.tsx +2 -2
  262. package/src/web/components/chat/session-list-panel.tsx +3 -2
  263. package/src/web/components/editor/binary-diff-view.tsx +212 -0
  264. package/src/web/components/editor/diff-viewer.tsx +76 -3
  265. package/src/web/components/explorer/file-tree.tsx +235 -134
  266. package/src/web/components/explorer/flatten-visible-tree.ts +35 -40
  267. package/src/web/components/explorer/tree-node.tsx +69 -69
  268. package/src/web/components/explorer/use-tree-row-dnd.ts +3 -4
  269. package/src/web/components/extensions/extension-tree-view.tsx +4 -11
  270. package/src/web/components/git/git-status-panel.tsx +25 -0
  271. package/src/web/components/layout/command-palette.tsx +2 -18
  272. package/src/web/components/layout/draggable-tab.tsx +4 -2
  273. package/src/web/components/layout/nav-section-rail.tsx +12 -3
  274. package/src/web/components/layout/status-bar.tsx +10 -6
  275. package/src/web/components/layout/tab-content.tsx +5 -0
  276. package/src/web/components/layout/tab-pool.tsx +1 -0
  277. package/src/web/components/layout/upgrade-button.tsx +5 -1
  278. package/src/web/components/remote-desktop/remote-desktop-clipboard-client.ts +67 -0
  279. package/src/web/components/remote-desktop/remote-desktop-clipboard-notice.tsx +127 -0
  280. package/src/web/components/remote-desktop/remote-desktop-codec-labels.ts +28 -0
  281. package/src/web/components/remote-desktop/remote-desktop-custom-quality-dialog.tsx +144 -0
  282. package/src/web/components/remote-desktop/remote-desktop-mobile-key-bar.tsx +13 -2
  283. package/src/web/components/remote-desktop/remote-desktop-mobile-settings.tsx +232 -0
  284. package/src/web/components/remote-desktop/remote-desktop-mobile-toolbar.tsx +21 -8
  285. package/src/web/components/remote-desktop/remote-desktop-mobile-view.tsx +82 -3
  286. package/src/web/components/remote-desktop/remote-desktop-recording.ts +76 -0
  287. package/src/web/components/remote-desktop/remote-desktop-resolution-list.ts +100 -0
  288. package/src/web/components/remote-desktop/remote-desktop-toolbar.tsx +497 -0
  289. package/src/web/components/remote-desktop/remote-desktop-view-style.ts +145 -0
  290. package/src/web/components/remote-desktop/remote-desktop-window-content.tsx +126 -35
  291. package/src/web/components/remote-desktop/use-h264-canvas-decoder.ts +11 -0
  292. package/src/web/components/remote-desktop/use-opus-audio-player.ts +148 -0
  293. package/src/web/components/remote-desktop/use-remote-desktop-connection.ts +284 -1
  294. package/src/web/components/remote-desktop/use-remote-desktop-readiness.ts +75 -1
  295. package/src/web/components/remote-desktop/use-remote-desktop-recorder.ts +103 -0
  296. package/src/web/components/remote-desktop/use-remote-desktop-virtual-keyboard.ts +19 -2
  297. package/src/web/components/remote-desktop/use-remote-input-capture.ts +39 -3
  298. package/src/web/components/settings/accounts/account-card.tsx +2 -2
  299. package/src/web/components/settings/accounts/codex-accounts-section.tsx +4 -4
  300. package/src/web/components/settings/extension-manager-section.tsx +8 -1
  301. package/src/web/components/settings/theme-picker.tsx +2 -1
  302. package/src/web/components/ui/tooltip.tsx +15 -2
  303. package/src/web/lib/branch-review-state.ts +175 -0
  304. package/src/web/lib/extension-icons.ts +70 -0
  305. package/src/web/lib/file-icon-name.ts +62 -0
  306. package/src/web/lib/file-icons.generated.ts +3287 -25
  307. package/src/web/lib/file-icons.tsx +71 -73
  308. package/src/web/lib/flatten-expansions.ts +18 -5
  309. package/src/web/lib/message-ordinals.ts +23 -0
  310. package/src/web/lib/provider-logos.tsx +143 -0
  311. package/src/web/lib/tab-type-icons.ts +25 -0
  312. package/src/web/stores/file-store.ts +73 -35
  313. package/src/web/stores/panel-utils.ts +5 -0
  314. package/src/web/stores/project-framework-store.ts +82 -0
  315. package/src/web/stores/project-store.ts +7 -1
  316. package/src/web/stores/settings-store.ts +128 -0
  317. package/src/web/stores/tab-store.ts +1 -0
  318. package/src/web/stores/window-panel-persistence.ts +1 -0
  319. package/src/web/styles/file-icons.generated.css +3282 -234
  320. package/src/web/styles/globals.css +26 -0
  321. package/dist/web/assets/accounts-settings-section-BuxD9zH3.js +0 -1
  322. package/dist/web/assets/ai-settings-section-Dd9tOUp0.js +0 -1
  323. package/dist/web/assets/ai-settings-section-FrU2X8Fu.js +0 -1
  324. package/dist/web/assets/api-client-BUCo1lms.js +0 -1
  325. package/dist/web/assets/architecture-PBZL5I3N-5K0wEkAn.js +0 -1
  326. package/dist/web/assets/chat-tab-Cr6_KSGO.js +0 -15
  327. package/dist/web/assets/code-editor-DfmlGamv.js +0 -7
  328. package/dist/web/assets/context-menu-DOODKZdF.js +0 -1
  329. package/dist/web/assets/diff-viewer-BpNj7pN9.js +0 -4
  330. package/dist/web/assets/explorer-body-D-5z464e.js +0 -2
  331. package/dist/web/assets/explorer-window-content-7-1wuRul.js +0 -1
  332. package/dist/web/assets/extension-manager-section-BhwB6nqr.js +0 -1
  333. package/dist/web/assets/file-icons-EzPL0q-k.js +0 -1
  334. package/dist/web/assets/file-icons.generated-CfaJuvZu.css +0 -1
  335. package/dist/web/assets/file-store-DMABxOkV.js +0 -1
  336. package/dist/web/assets/files-settings-section-CZzABuKz.js +0 -1
  337. package/dist/web/assets/git-log-panel-CRN4JVOx.js +0 -1
  338. package/dist/web/assets/gitGraph-HDMCJU4V-DkVT2MAM.js +0 -1
  339. package/dist/web/assets/group-chat-tab-DFa5mAx5.js +0 -1
  340. package/dist/web/assets/index-D7DbXD3G.css +0 -1
  341. package/dist/web/assets/index-ZtfxZOyb.js +0 -52
  342. package/dist/web/assets/info-3K5VOQVL-BBsmKGzA.js +0 -1
  343. package/dist/web/assets/keybindings-store-tVfhPANS.js +0 -1
  344. package/dist/web/assets/markdown-renderer-sJNNVhlS.js +0 -1
  345. package/dist/web/assets/mobile-explorer-sheet-CZ0HZ2E_.js +0 -1
  346. package/dist/web/assets/notification-store-BZbsPnW0.js +0 -1
  347. package/dist/web/assets/packet-RMMSAZCW-DwmYPOlx.js +0 -1
  348. package/dist/web/assets/panel-store-BVLBRn6M.js +0 -1
  349. package/dist/web/assets/pie-UPGHQEXC-D0oogIFi.js +0 -1
  350. package/dist/web/assets/problems-panel-B7BWVhdy.js +0 -1
  351. package/dist/web/assets/project-store-BhMbVln3.js +0 -1
  352. package/dist/web/assets/radar-KQ55EAFF-CQIJOQGa.js +0 -1
  353. package/dist/web/assets/remote-desktop-mobile-sheet-ByjaeZKX.js +0 -2
  354. package/dist/web/assets/remote-desktop-mobile-view-D13mtr9F.js +0 -1
  355. package/dist/web/assets/remote-desktop-window-content-CbqTCaC6.js +0 -1
  356. package/dist/web/assets/settings-body-Rnu-FcX-.js +0 -2
  357. package/dist/web/assets/settings-store-DZPgPtEA.js +0 -2
  358. package/dist/web/assets/settings-tab-C6PHxs0Z.js +0 -1
  359. package/dist/web/assets/settings-window-content-CQ4gT2qj.js +0 -1
  360. package/dist/web/assets/system-monitor-body-2vQ1kMxE.js +0 -2
  361. package/dist/web/assets/system-monitor-tab-krAcvwF7.js +0 -1
  362. package/dist/web/assets/team-member-sheet-2FnRmk1a.js +0 -2
  363. package/dist/web/assets/tool-cards-DU_S2o51.js +0 -9
  364. package/dist/web/assets/treemap-KZPCXAKY-BhEtjljr.js +0 -1
  365. package/dist/web/assets/use-remote-desktop-display-choice-BK_f787P.js +0 -1
  366. package/dist/web/assets/video-preview-BDNwumbW.js +0 -1
  367. package/dist/web/assets/ws-auth-DEAJiGCR.js +0 -1
  368. package/packages/vscode-compat/node_modules/.bin/tsc.bunx +0 -0
  369. package/packages/vscode-compat/node_modules/.bin/tsc.exe +0 -0
  370. package/packages/vscode-compat/node_modules/.bin/tsserver.bunx +0 -0
  371. package/packages/vscode-compat/node_modules/.bin/tsserver.exe +0 -0
  372. /package/dist/web/assets/{abap-tkptwpsK.js → abap-IN2yINdN.js} +0 -0
  373. /package/dist/web/assets/{actionscript-3-Di9TMGwg.js → actionscript-3-C_t772eR.js} +0 -0
  374. /package/dist/web/assets/{ada-CB9F079D.js → ada-DPme3emm.js} +0 -0
  375. /package/dist/web/assets/{andromeeda-Dcl8jpb7.js → andromeeda-DSV4i4mZ.js} +0 -0
  376. /package/dist/web/assets/{apache-D0Nk0qsH.js → apache-CWr8s8I8.js} +0 -0
  377. /package/dist/web/assets/{apex-mQIoWncD.js → apex-Z20JnDF5.js} +0 -0
  378. /package/dist/web/assets/{applescript-C6ljbcsI.js → applescript-BKNPu3ga.js} +0 -0
  379. /package/dist/web/assets/{apply-theme-BwIMNXyX.js → apply-theme-CbR31K6I.js} +0 -0
  380. /package/dist/web/assets/{ara-WFqRPGPe.js → ara-hWdY3O96.js} +0 -0
  381. /package/dist/web/assets/{asciidoc-bNd8w4nf.js → asciidoc-eynhq496.js} +0 -0
  382. /package/dist/web/assets/{asm-BQ1_F580.js → asm-Bu02Kq4P.js} +0 -0
  383. /package/dist/web/assets/{aurora-x-Ciqs_Dbu.js → aurora-x-BHBGXbIZ.js} +0 -0
  384. /package/dist/web/assets/{awk-GCR-6KZV.js → awk-B4ojezyv.js} +0 -0
  385. /package/dist/web/assets/{ayu-dark-DxJXuj99.js → ayu-dark-D0GQzFi9.js} +0 -0
  386. /package/dist/web/assets/{ayu-light-jvSyJWDV.js → ayu-light-BVYb5Yn8.js} +0 -0
  387. /package/dist/web/assets/{ayu-mirage-Cg3BPO6H.js → ayu-mirage-BGFW6oNb.js} +0 -0
  388. /package/dist/web/assets/{ballerina-BPz4CrX6.js → ballerina-B6Og3IKT.js} +0 -0
  389. /package/dist/web/assets/{bat-CSTs2hIG.js → bat-D8YFILfx.js} +0 -0
  390. /package/dist/web/assets/{beancount-DMateAZX.js → beancount-BjlbBBuX.js} +0 -0
  391. /package/dist/web/assets/{berry-9lXg0GRM.js → berry-Cz1ygeoj.js} +0 -0
  392. /package/dist/web/assets/{bibtex-DIbqmvFg.js → bibtex-CVkJceJh.js} +0 -0
  393. /package/dist/web/assets/{bicep-02WGsoJ8.js → bicep-DrhYI5GX.js} +0 -0
  394. /package/dist/web/assets/{bird2-BY0tbu3k.js → bird2-BV-KJtmc.js} +0 -0
  395. /package/dist/web/assets/{c-jMBX_9Je.js → c-CuLlvxbG.js} +0 -0
  396. /package/dist/web/assets/{c3-B8JVbNjW.js → c3-CI7-1CJ0.js} +0 -0
  397. /package/dist/web/assets/{cadence-DUIf1tvL.js → cadence-DjxhNP3Q.js} +0 -0
  398. /package/dist/web/assets/{can-open-in-ppm-DGEc0s4P.js → can-open-in-ppm-cv5--yHX.js} +0 -0
  399. /package/dist/web/assets/{catppuccin-frappe-FhUCVxex.js → catppuccin-frappe-CEozpJRh.js} +0 -0
  400. /package/dist/web/assets/{catppuccin-latte-CzfrtmgL.js → catppuccin-latte-C4sXQFMc.js} +0 -0
  401. /package/dist/web/assets/{catppuccin-macchiato-6xL0lOtj.js → catppuccin-macchiato-Ci-fDhXy.js} +0 -0
  402. /package/dist/web/assets/{catppuccin-mocha-D39-xS3P.js → catppuccin-mocha-CH7YXw8d.js} +0 -0
  403. /package/dist/web/assets/{clarity-D8TeGvoT.js → clarity-BWrJ7UmT.js} +0 -0
  404. /package/dist/web/assets/{clojure-Dx_acSX7.js → clojure-C94fh2hb.js} +0 -0
  405. /package/dist/web/assets/{cmake-CyeYPq8o.js → cmake-CtvCkMsG.js} +0 -0
  406. /package/dist/web/assets/{codeowners-DrTI0lmI.js → codeowners-CJnubqzH.js} +0 -0
  407. /package/dist/web/assets/{codeql-CPkFxVW-.js → codeql-BOoP5HY2.js} +0 -0
  408. /package/dist/web/assets/{common-lisp-B0k-3LTj.js → common-lisp-B9nlMFJd.js} +0 -0
  409. /package/dist/web/assets/{coq-uygpo7to.js → coq-CgzIQiSH.js} +0 -0
  410. /package/dist/web/assets/{csharp-DKUYGfSp.js → csharp-4xAIx3jw.js} +0 -0
  411. /package/dist/web/assets/{css-DoOUp7v4.js → css-D4YoLYGn.js} +0 -0
  412. /package/dist/web/assets/{csv-B3YE13gi.js → csv-C2v-ZakW.js} +0 -0
  413. /package/dist/web/assets/{csv-parser-DXePqmXe.js → csv-parser-_oDBUkwQ.js} +0 -0
  414. /package/dist/web/assets/{cue-DViO2wvn.js → cue-Bbznh_an.js} +0 -0
  415. /package/dist/web/assets/{cypher-4BMCIW5N.js → cypher-BKhVEARF.js} +0 -0
  416. /package/dist/web/assets/{d-BT_sTHas.js → d-D6IhWopl.js} +0 -0
  417. /package/dist/web/assets/{dark-plus-DVVXu-1N.js → dark-plus-xAWu790O.js} +0 -0
  418. /package/dist/web/assets/{dart-DHz6f6_6.js → dart-Bj6S9O1j.js} +0 -0
  419. /package/dist/web/assets/{data-grid-types-OgUxMLdQ.js → data-grid-types-C5bthITf.js} +0 -0
  420. /package/dist/web/assets/{dax-ChYCjweK.js → dax-CF2zvaqq.js} +0 -0
  421. /package/dist/web/assets/{desktop-D6Lt4p04.js → desktop-CnmyxMKE.js} +0 -0
  422. /package/dist/web/assets/{diff-D8TDe4hP.js → diff-Qi8WuJsI.js} +0 -0
  423. /package/dist/web/assets/{dist-DOTlFQJA.js → dist-Bjseolz4.js} +0 -0
  424. /package/dist/web/assets/{dist-CrBOCtCW.js → dist-Czq7BUMy.js} +0 -0
  425. /package/dist/web/assets/{docker-Bsp0YbPx.js → docker-vLbbNP5d.js} +0 -0
  426. /package/dist/web/assets/{dotenv-B_7giSY_.js → dotenv-DJMvvNzn.js} +0 -0
  427. /package/dist/web/assets/{dracula-TrQL2iB5.js → dracula-CtAh2rhB.js} +0 -0
  428. /package/dist/web/assets/{dracula-soft-n2XOlgh-.js → dracula-soft-CXtuAAf6.js} +0 -0
  429. /package/dist/web/assets/{dream-maker-CdIEdhpT.js → dream-maker-D5YQyK49.js} +0 -0
  430. /package/dist/web/assets/{editor-font-BDrdiGf1.js → editor-font-ClTC9gaD.js} +0 -0
  431. /package/dist/web/assets/{emacs-lisp-CSbixs6u.js → emacs-lisp-CGTjbiqF.js} +0 -0
  432. /package/dist/web/assets/{everforest-dark-BuZahZYp.js → everforest-dark-D59p7vAQ.js} +0 -0
  433. /package/dist/web/assets/{everforest-light-DPH53z5H.js → everforest-light-B3Qtud3V.js} +0 -0
  434. /package/dist/web/assets/{fennel-B1-K_hlJ.js → fennel-BCyHNUwf.js} +0 -0
  435. /package/dist/web/assets/{fish-Cy12Dbua.js → fish-DDDlLV2G.js} +0 -0
  436. /package/dist/web/assets/{fluent-B_82ys8E.js → fluent-By54R9tl.js} +0 -0
  437. /package/dist/web/assets/{format-bytes-D5sBgIqU.js → format-bytes-D1AQyUCt.js} +0 -0
  438. /package/dist/web/assets/{fortran-free-form-BRQdNzZF.js → fortran-free-form-tP8mRym_.js} +0 -0
  439. /package/dist/web/assets/{gdscript-XBDmLUm9.js → gdscript-nHC2z6Te.js} +0 -0
  440. /package/dist/web/assets/{gdshader-DhVfB3u4.js → gdshader-K-cSmPlW.js} +0 -0
  441. /package/dist/web/assets/{genie-X3K29Y49.js → genie-BAcRhvEe.js} +0 -0
  442. /package/dist/web/assets/{gherkin-DKIx-6cX.js → gherkin-CcifxMLb.js} +0 -0
  443. /package/dist/web/assets/{github-dark-Bf7zdp5s.js → github-dark-67KKlMdB.js} +0 -0
  444. /package/dist/web/assets/{github-dark-default-DqsBBV11.js → github-dark-default-tja8pidR.js} +0 -0
  445. /package/dist/web/assets/{github-dark-dimmed-X2gqRz6w.js → github-dark-dimmed-CtjhYQtx.js} +0 -0
  446. /package/dist/web/assets/{github-dark-high-contrast-Z7tvbGX4.js → github-dark-high-contrast-Bf-LPFgn.js} +0 -0
  447. /package/dist/web/assets/{github-light-BKxt-7zZ.js → github-light-CbJxE7n3.js} +0 -0
  448. /package/dist/web/assets/{github-light-default-B0liF4d4.js → github-light-default-DRQmdHxt.js} +0 -0
  449. /package/dist/web/assets/{github-light-high-contrast-F4encJwr.js → github-light-high-contrast-CJkDgTeM.js} +0 -0
  450. /package/dist/web/assets/{gleam-Pz2bHgBB.js → gleam-D4LCl2ri.js} +0 -0
  451. /package/dist/web/assets/{gn-CgmdNsPU.js → gn-COY8zZmC.js} +0 -0
  452. /package/dist/web/assets/{gnuplot-qLNF5yZ9.js → gnuplot-ROk1VitR.js} +0 -0
  453. /package/dist/web/assets/{go-CSDUNhE7.js → go-H30c3p0C.js} +0 -0
  454. /package/dist/web/assets/{groovy-B3NEnOHF.js → groovy-DBLCaelV.js} +0 -0
  455. /package/dist/web/assets/{gruvbox-dark-hard-DcoftiUX.js → gruvbox-dark-hard-DJy4irps.js} +0 -0
  456. /package/dist/web/assets/{gruvbox-dark-medium-D1wVSuD8.js → gruvbox-dark-medium-Dhfw1QTO.js} +0 -0
  457. /package/dist/web/assets/{gruvbox-dark-soft-DWpunymH.js → gruvbox-dark-soft-BncJU-8w.js} +0 -0
  458. /package/dist/web/assets/{gruvbox-light-hard-rHMvyUdS.js → gruvbox-light-hard-Cq12_YvM.js} +0 -0
  459. /package/dist/web/assets/{gruvbox-light-medium-3O2KiXO7.js → gruvbox-light-medium-DqVja5_v.js} +0 -0
  460. /package/dist/web/assets/{gruvbox-light-soft-lNlXJPwH.js → gruvbox-light-soft-IvwhveGF.js} +0 -0
  461. /package/dist/web/assets/{haskell-aaG3aC0h.js → haskell-DA1JV-MC.js} +0 -0
  462. /package/dist/web/assets/{haxe-CPcHhUDd.js → haxe-V9RTRc5E.js} +0 -0
  463. /package/dist/web/assets/{hcl-CMQNlR_4.js → hcl-hr1lyZ10.js} +0 -0
  464. /package/dist/web/assets/{hjson-DeuklPFj.js → hjson-D3vW3i7X.js} +0 -0
  465. /package/dist/web/assets/{hlsl-BI3hlsaM.js → hlsl-BB85gfJT.js} +0 -0
  466. /package/dist/web/assets/{horizon-C7-gOcjs.js → horizon-DU8OOnj8.js} +0 -0
  467. /package/dist/web/assets/{horizon-bright-Cxt9bRW7.js → horizon-bright-DNMUclW6.js} +0 -0
  468. /package/dist/web/assets/{houston-DbF2G7iD.js → houston-DAiZKf6T.js} +0 -0
  469. /package/dist/web/assets/{hy-DRG6Hs2I.js → hy-C6Zv0LMe.js} +0 -0
  470. /package/dist/web/assets/{image-gallery-CObzTisL.js → image-gallery-CGvlv8zR.js} +0 -0
  471. /package/dist/web/assets/{imba-CxKv_2TW.js → imba-Didnp6Y-.js} +0 -0
  472. /package/dist/web/assets/{ini-BiHsyCdO.js → ini-m0T7q4gQ.js} +0 -0
  473. /package/dist/web/assets/{input-D-dR8RI6.js → input-C7Q8FxeL.js} +0 -0
  474. /package/dist/web/assets/{java-D3RZzgDB.js → java-Buy07jGn.js} +0 -0
  475. /package/dist/web/assets/{javascript-4FmSqG-T.js → javascript-Cjq3CbRL.js} +0 -0
  476. /package/dist/web/assets/{json-CjB236_E.js → json-BSVu8IqD.js} +0 -0
  477. /package/dist/web/assets/{json5-bkfO-Rco.js → json5-NN_Vb7e6.js} +0 -0
  478. /package/dist/web/assets/{jsonc-BwaTeLyv.js → jsonc-BdyZhBUw.js} +0 -0
  479. /package/dist/web/assets/{jsonl-CfxNSwGd.js → jsonl-BsDSNvwi.js} +0 -0
  480. /package/dist/web/assets/{jsonnet-CQETCYrp.js → jsonnet-Gm963F-U.js} +0 -0
  481. /package/dist/web/assets/{jssm-D5o3Zzdz.js → jssm-Ds3bKWgJ.js} +0 -0
  482. /package/dist/web/assets/{jsx-BAMILPLo.js → jsx-BT5mW58X.js} +0 -0
  483. /package/dist/web/assets/{kanagawa-dragon-BhGVxu3R.js → kanagawa-dragon-BvNvf7xd.js} +0 -0
  484. /package/dist/web/assets/{kanagawa-lotus-FH4Y59ro.js → kanagawa-lotus-CuSiliSn.js} +0 -0
  485. /package/dist/web/assets/{kanagawa-wave-eilvP9MB.js → kanagawa-wave-C6g-d6jT.js} +0 -0
  486. /package/dist/web/assets/{kdl-BQ-yvXUK.js → kdl-Bx4kZef-.js} +0 -0
  487. /package/dist/web/assets/{kotlin-BuCx0-VT.js → kotlin-qvaVvCcm.js} +0 -0
  488. /package/dist/web/assets/{kusto-DAN_n_Cy.js → kusto-BPLyNCN6.js} +0 -0
  489. /package/dist/web/assets/{laserwave-Cq2BlxrT.js → laserwave-Sf8ZEhWP.js} +0 -0
  490. /package/dist/web/assets/{lean-Dhq7uXKw.js → lean-DGe6uqgk.js} +0 -0
  491. /package/dist/web/assets/{less-CxaMQb8j.js → less-CqM85x-9.js} +0 -0
  492. /package/dist/web/assets/{lib-BVwmtv2i.js → lib-CD6pOS4u.js} +0 -0
  493. /package/dist/web/assets/{libesm-DdtEjhIi.js → libesm-CPh1Av0r.js} +0 -0
  494. /package/dist/web/assets/{light-plus-B4sMAend.js → light-plus-DBtKwhFA.js} +0 -0
  495. /package/dist/web/assets/{llvm-DQ6-B_6Y.js → llvm-Dpbq3gky.js} +0 -0
  496. /package/dist/web/assets/{log-Bb_vqvu7.js → log-qKMcPm9S.js} +0 -0
  497. /package/dist/web/assets/{logo-BzfYv3A_.js → logo-DFo7i5KH.js} +0 -0
  498. /package/dist/web/assets/{luau-BnAeNR77.js → luau-DGlqWFyL.js} +0 -0
  499. /package/dist/web/assets/{make-B6IFuxpC.js → make-CixdvUQe.js} +0 -0
  500. /package/dist/web/assets/{markdown-Z0xi77Fn.js → markdown-CmRnbngE.js} +0 -0
  501. /package/dist/web/assets/{material-theme-1scy1SYn.js → material-theme-WDUbafB-.js} +0 -0
  502. /package/dist/web/assets/{material-theme-darker-G-JykXQP.js → material-theme-darker-u8y2xpOk.js} +0 -0
  503. /package/dist/web/assets/{material-theme-lighter-BtKkCMVq.js → material-theme-lighter-cyQZK3Tg.js} +0 -0
  504. /package/dist/web/assets/{material-theme-ocean-irrDC89L.js → material-theme-ocean-LBAVvw7k.js} +0 -0
  505. /package/dist/web/assets/{material-theme-palenight-Eq5_K4LM.js → material-theme-palenight-BJqhBxL0.js} +0 -0
  506. /package/dist/web/assets/{matlab-BE3oCXZC.js → matlab-B38sOJee.js} +0 -0
  507. /package/dist/web/assets/{mdx-DYiuFTGw.js → mdx-C8QrE_IW.js} +0 -0
  508. /package/dist/web/assets/{mermaid-DVlaEvtw.js → mermaid-BQ-eeU8A.js} +0 -0
  509. /package/dist/web/assets/{min-dark-DSkeTpQa.js → min-dark-D4gNa5tv.js} +0 -0
  510. /package/dist/web/assets/{min-light-CFv2Q0uF.js → min-light-Dfbhn_PL.js} +0 -0
  511. /package/dist/web/assets/{mipsasm-CiAFuZQ2.js → mipsasm-bLOhJkdm.js} +0 -0
  512. /package/dist/web/assets/{mojo-ARshcaSQ.js → mojo-B0BCOMQW.js} +0 -0
  513. /package/dist/web/assets/{monaco-dotenv-language-BDYnKcnr.js → monaco-dotenv-language-uB-wok6C.js} +0 -0
  514. /package/dist/web/assets/{monokai-CPTV-SOg.js → monokai-Bdw_07OM.js} +0 -0
  515. /package/dist/web/assets/{moonbit-D57B0dZr.js → moonbit-BFdGaeuk.js} +0 -0
  516. /package/dist/web/assets/{move-qgy3G_o9.js → move-CyXQFGPM.js} +0 -0
  517. /package/dist/web/assets/{narrat-CZ3rC-oc.js → narrat-9DvTD3_3.js} +0 -0
  518. /package/dist/web/assets/{nextflow-groovy-BxxIOcI4.js → nextflow-groovy-C4VY3kNY.js} +0 -0
  519. /package/dist/web/assets/{night-owl-B1ryGCda.js → night-owl-feYm66V0.js} +0 -0
  520. /package/dist/web/assets/{night-owl-light-BZvNnvMF.js → night-owl-light-DALqFjvx.js} +0 -0
  521. /package/dist/web/assets/{nix-BVBQi8yv.js → nix-C1kPA9Sw.js} +0 -0
  522. /package/dist/web/assets/{nord-3NXw613h.js → nord-1dvrj1Al.js} +0 -0
  523. /package/dist/web/assets/{nushell-BMOLRalb.js → nushell-DSQ_9Uqj.js} +0 -0
  524. /package/dist/web/assets/{objective-c-AxruEUgh.js → objective-c-Dhcn5ZK8.js} +0 -0
  525. /package/dist/web/assets/{objective-cpp-cFT0qRsD.js → objective-cpp-CGVYCSrp.js} +0 -0
  526. /package/dist/web/assets/{ocaml-_8JH6O8J.js → ocaml-rxXW21An.js} +0 -0
  527. /package/dist/web/assets/{odin-nwYvR0O6.js → odin-ClSJX2DE.js} +0 -0
  528. /package/dist/web/assets/{one-dark-pro-C1LaYJaH.js → one-dark-pro-3gbWDfFW.js} +0 -0
  529. /package/dist/web/assets/{one-light-B7rMc__c.js → one-light-C2q5ZZAw.js} +0 -0
  530. /package/dist/web/assets/{openscad-Cwes8bKO.js → openscad-eHXeafBl.js} +0 -0
  531. /package/dist/web/assets/{pascal-DMt-6tdZ.js → pascal-kKha3Txs.js} +0 -0
  532. /package/dist/web/assets/{pkl-QBTyYQmB.js → pkl-CTiPytg-.js} +0 -0
  533. /package/dist/web/assets/{plastic-BTSUp8Fk.js → plastic-D1Ygezi4.js} +0 -0
  534. /package/dist/web/assets/{plsql-DQv3IFXP.js → plsql-D_gbgZIr.js} +0 -0
  535. /package/dist/web/assets/{po-CNwZx4tW.js → po-BkhbDUnq.js} +0 -0
  536. /package/dist/web/assets/{poimandres-8Os7Qequ.js → poimandres-Do6NywrA.js} +0 -0
  537. /package/dist/web/assets/{polar-b4yK8oD8.js → polar-B3B23wca.js} +0 -0
  538. /package/dist/web/assets/{postcss-sFctwpxx.js → postcss-Dv_YBvWd.js} +0 -0
  539. /package/dist/web/assets/{powerquery-Cg_WrtuN.js → powerquery-fKJWI-Sh.js} +0 -0
  540. /package/dist/web/assets/{powershell-C7Ifz95h.js → powershell-B4Y7KIgx.js} +0 -0
  541. /package/dist/web/assets/{prisma-Z52HUJzQ.js → prisma-J--XdTXX.js} +0 -0
  542. /package/dist/web/assets/{prolog-DLqW-zR1.js → prolog-BBTg4Vb3.js} +0 -0
  543. /package/dist/web/assets/{proto-BOIzxCCJ.js → proto-DWlum3T8.js} +0 -0
  544. /package/dist/web/assets/{puppet-De7TjZIf.js → puppet-ZiUfc9JV.js} +0 -0
  545. /package/dist/web/assets/{purescript-R76ou1WT.js → purescript-CmT8SCHB.js} +0 -0
  546. /package/dist/web/assets/{python-Dprm0tNP.js → python-BURpj-fq.js} +0 -0
  547. /package/dist/web/assets/{qmldir-Clp_jpNw.js → qmldir-CpaiJywQ.js} +0 -0
  548. /package/dist/web/assets/{qss-eQPYYzSo.js → qss-C-zdz0i5.js} +0 -0
  549. /package/dist/web/assets/{r-CCcRHIf5.js → r-K56BeS-s.js} +0 -0
  550. /package/dist/web/assets/{racket-CzE472yp.js → racket-gkn6Z_Jw.js} +0 -0
  551. /package/dist/web/assets/{raku-DQwXrTSi.js → raku-C0ZBuKlx.js} +0 -0
  552. /package/dist/web/assets/{red-KCS6wyop.js → red-BWZ6x-3p.js} +0 -0
  553. /package/dist/web/assets/{reg-CcrAddUh.js → reg-Dt_EsyGR.js} +0 -0
  554. /package/dist/web/assets/{regexp-DDbud-Ri.js → regexp-CT6BLG2C.js} +0 -0
  555. /package/dist/web/assets/{rel-99GDgaHp.js → rel-MJ2LcyzJ.js} +0 -0
  556. /package/dist/web/assets/{resolve-theme-CW2niGV2.js → resolve-theme-B_ukrg_8.js} +0 -0
  557. /package/dist/web/assets/{riscv-Dm2eNv0R.js → riscv-BZYWzMAA.js} +0 -0
  558. /package/dist/web/assets/{ron-CzFt0ygH.js → ron-CLbwHWMn.js} +0 -0
  559. /package/dist/web/assets/{rose-pine-Ck6lCrCP.js → rose-pine-BPiI_daR.js} +0 -0
  560. /package/dist/web/assets/{rose-pine-dawn-vtALRJNj.js → rose-pine-dawn-BjkJc7dW.js} +0 -0
  561. /package/dist/web/assets/{rose-pine-moon-DPto8qXw.js → rose-pine-moon-BQSxkt7g.js} +0 -0
  562. /package/dist/web/assets/{rosmsg-NtS6429H.js → rosmsg-yt4151pJ.js} +0 -0
  563. /package/dist/web/assets/{rust-BjIXvF9h.js → rust-1dAcengL.js} +0 -0
  564. /package/dist/web/assets/{sass-CQ23wf5E.js → sass-DUgtQJsg.js} +0 -0
  565. /package/dist/web/assets/{scala-LfKLM143.js → scala-CxoGYpS0.js} +0 -0
  566. /package/dist/web/assets/{scheme-D3SLBXlF.js → scheme-B2K3u9e3.js} +0 -0
  567. /package/dist/web/assets/{sdbl-ODRd2rGN.js → sdbl-C0Ygk0n6.js} +0 -0
  568. /package/dist/web/assets/{shellscript-3KB1G_oA.js → shellscript-C2CvhqMk.js} +0 -0
  569. /package/dist/web/assets/{slack-dark-DTtyWmIy.js → slack-dark-DYdgzRGj.js} +0 -0
  570. /package/dist/web/assets/{slack-ochin-Bu0PApk3.js → slack-ochin-X-WD0WCg.js} +0 -0
  571. /package/dist/web/assets/{smalltalk-haMT-Sbe.js → smalltalk-Dp7g_zb9.js} +0 -0
  572. /package/dist/web/assets/{snazzy-light-DXJzefE9.js → snazzy-light-DWHWr-a6.js} +0 -0
  573. /package/dist/web/assets/{solarized-dark-B6LyYa18.js → solarized-dark-CxdLsjvb.js} +0 -0
  574. /package/dist/web/assets/{solarized-light-QLADebkv.js → solarized-light-Cz9rAvG5.js} +0 -0
  575. /package/dist/web/assets/{solidity-mzfq1aDc.js → solidity-D33gHHdn.js} +0 -0
  576. /package/dist/web/assets/{splunk-CyH4RWBp.js → splunk-BFbOroYw.js} +0 -0
  577. /package/dist/web/assets/{sql-CWVlT5D3.js → sql-ChpxvmdY.js} +0 -0
  578. /package/dist/web/assets/{ssh-config-DNvQJ1Qx.js → ssh-config-CUuSQ6JG.js} +0 -0
  579. /package/dist/web/assets/{streaming-store-uDu3PoN1.js → streaming-store-BYukdB56.js} +0 -0
  580. /package/dist/web/assets/{stylus-ByQQdtmf.js → stylus-DDihP0_W.js} +0 -0
  581. /package/dist/web/assets/{swift-D1cpY4ow.js → swift-XoalUn4x.js} +0 -0
  582. /package/dist/web/assets/{synthwave-84-BDF_-QiW.js → synthwave-84-DjkG0Bff.js} +0 -0
  583. /package/dist/web/assets/{system-verilog-BYrkMydC.js → system-verilog-f_pLKMYd.js} +0 -0
  584. /package/dist/web/assets/{systemd-CQm3zyK0.js → systemd-DjDcLfxp.js} +0 -0
  585. /package/dist/web/assets/{talonscript-DMW5CzE1.js → talonscript-BpUzYcDl.js} +0 -0
  586. /package/dist/web/assets/{tasl-BhFzMCr2.js → tasl-DP-cUbg8.js} +0 -0
  587. /package/dist/web/assets/{tcl-D7VEAF-9.js → tcl-BHoy-dfE.js} +0 -0
  588. /package/dist/web/assets/{terraform-DdT5A0Vy.js → terraform-vCVxN25B.js} +0 -0
  589. /package/dist/web/assets/{tokyo-night-WGLMM6Q9.js → tokyo-night-D0u4xZba.js} +0 -0
  590. /package/dist/web/assets/{toml-CZ7ipXHZ.js → toml-BS8D0__A.js} +0 -0
  591. /package/dist/web/assets/{tsv-B1zVvoTr.js → tsv-DCb-NQh3.js} +0 -0
  592. /package/dist/web/assets/{tsx-D4HwRUBq.js → tsx-DF8YeRgG.js} +0 -0
  593. /package/dist/web/assets/{turtle-B4r8KEJH.js → turtle-Bhx1GlIy.js} +0 -0
  594. /package/dist/web/assets/{typescript-BnHhxMtp.js → typescript-CAVD2Wb6.js} +0 -0
  595. /package/dist/web/assets/{typespec-DP2q46GT.js → typespec-D7mchdY_.js} +0 -0
  596. /package/dist/web/assets/{typst-DFsD_yaD.js → typst-CeXzq6Yw.js} +0 -0
  597. /package/dist/web/assets/{use-is-mobile-BQ9Bv5Ga.js → use-is-mobile-TkcwP1Gx.js} +0 -0
  598. /package/dist/web/assets/{use-visual-viewport-CbwnktSG.js → use-visual-viewport-Lp878uzs.js} +0 -0
  599. /package/dist/web/assets/{v-Bji8hSVT.js → v-DhzO54je.js} +0 -0
  600. /package/dist/web/assets/{vala-D3TIgGmU.js → vala-Dw_RAk3E.js} +0 -0
  601. /package/dist/web/assets/{vb-DvcxFS2q.js → vb-CiXdNh2Y.js} +0 -0
  602. /package/dist/web/assets/{vendor-xterm-BqPDsE9b.js → vendor-xterm-BHEY6Ctu.js} +0 -0
  603. /package/dist/web/assets/{verilog-Dy7cF35d.js → verilog-fITdxqwV.js} +0 -0
  604. /package/dist/web/assets/{vesper-BzjwUv9J.js → vesper-B1wm_At7.js} +0 -0
  605. /package/dist/web/assets/{vhdl-DuFnFNmW.js → vhdl-ebHjqKp9.js} +0 -0
  606. /package/dist/web/assets/{viml-yAPgKlbz.js → viml-Dgmnoxoo.js} +0 -0
  607. /package/dist/web/assets/{vitesse-black-oSSQdyIZ.js → vitesse-black-BZ5gMb7l.js} +0 -0
  608. /package/dist/web/assets/{vitesse-dark-BZxwPihB.js → vitesse-dark-Lij7kkOt.js} +0 -0
  609. /package/dist/web/assets/{vitesse-light-uIigycvt.js → vitesse-light-DW6Wq7VC.js} +0 -0
  610. /package/dist/web/assets/{vyper-CX3NAxm5.js → vyper-6FxDnVNg.js} +0 -0
  611. /package/dist/web/assets/{wasm-6mEOOvVa.js → wasm-BDxaNPCj.js} +0 -0
  612. /package/dist/web/assets/{wasm-D9td37qO.js → wasm-DKhAtzfe.js} +0 -0
  613. /package/dist/web/assets/{wenyan-lwNIHgk6.js → wenyan-C0k0DYG_.js} +0 -0
  614. /package/dist/web/assets/{wgsl-DQAzCfog.js → wgsl-Y2zagbdn.js} +0 -0
  615. /package/dist/web/assets/{wikitext-DTXmt5NE.js → wikitext-C4QGWzkY.js} +0 -0
  616. /package/dist/web/assets/{wit-Bdw_7WDH.js → wit-CQESzjHy.js} +0 -0
  617. /package/dist/web/assets/{wolfram-DqyHDZ07.js → wolfram-B-ia69Ia.js} +0 -0
  618. /package/dist/web/assets/{yaml-CPXhdByd.js → yaml-CEYYTQPm.js} +0 -0
  619. /package/dist/web/assets/{zenscript-6s_i5zek.js → zenscript-BggWXIeJ.js} +0 -0
  620. /package/dist/web/assets/{zig-B2htezJ_.js → zig-CbaUfBQe.js} +0 -0
@@ -0,0 +1,34 @@
1
+ /**
2
+ * The transcript's markdown, and the one reason it is fetched the way it is.
3
+ *
4
+ * The chunk fetch is kicked off at **module load**, not at first render: a
5
+ * Suspense skeleton that resolves after the list has mounted grows each message
6
+ * and shoves the bottom of the transcript out of view on a fresh load. Keeping
7
+ * this module on `message-list.tsx`'s static import path is what preserves that
8
+ * — reach it through `lazy()` and the fetch moves back to first render.
9
+ */
10
+ import { lazy, Suspense } from "react";
11
+ import { RenderErrorBoundary } from "@/components/shared/markdown-error-boundary";
12
+
13
+ export const markdownRendererImport = import("@/components/shared/markdown-renderer");
14
+ const MarkdownRenderer = lazy(() =>
15
+ markdownRendererImport.then((m) => ({ default: m.MarkdownRenderer }))
16
+ );
17
+ /** Strip SDK teammate-message XML tags from text — team popover shows these */
18
+ const TEAMMATE_MSG_RE = /<teammate-message[^>]*>[\s\S]*?<\/teammate-message>/g;
19
+ function stripTeammateMessages(text: string): string {
20
+ return text.replace(TEAMMATE_MSG_RE, "").replace(/\n{3,}/g, "\n\n").trim();
21
+ }
22
+
23
+ /** Wrapper: delegates to shared MarkdownRenderer with code actions enabled */
24
+ export function MarkdownContent({ content, projectName, isStreaming }: { content: string; projectName?: string; isStreaming?: boolean }) {
25
+ const cleaned = stripTeammateMessages(content);
26
+ if (!cleaned) return null;
27
+ return (
28
+ <RenderErrorBoundary fallbackContent={cleaned}>
29
+ <Suspense fallback={<div className="animate-pulse h-4 bg-muted rounded" />}>
30
+ <MarkdownRenderer content={cleaned} projectName={projectName} codeActions isStreaming={isStreaming} />
31
+ </Suspense>
32
+ </RenderErrorBoundary>
33
+ );
34
+ }
@@ -0,0 +1,174 @@
1
+ /**
2
+ * Images and files inside a transcript, which cannot be `<img src>`.
3
+ *
4
+ * Every upload is behind PPM's auth, and an `<img>` sends no `Authorization`
5
+ * header — so the bytes are fetched with one and handed to the element as a blob
6
+ * URL. That is also why the cache below exists rather than being an optimisation:
7
+ * the transcript rows unmount and remount constantly while scrolling.
8
+ */
9
+ import { useCallback, useEffect, useState } from "react";
10
+ import { FileText, Image as ImageIcon } from "@/lib/icons";
11
+ import { getAuthToken } from "@/lib/api-client";
12
+ import { basename } from "@/lib/utils";
13
+ import { useImageOverlay } from "@/stores/image-overlay-store";
14
+ import { collectGallery, GALLERY_ITEM_ATTR } from "@/lib/image-gallery";
15
+
16
+ /** Image extensions that can be previewed inline */
17
+ const IMAGE_EXTS = new Set([".png", ".jpg", ".jpeg", ".gif", ".webp"]);
18
+
19
+ /** Build a preview URL for an uploaded file (served from /chat/uploads/:filename) */
20
+ export function uploadPreviewUrl(filePath: string, projectName?: string): string {
21
+ const filename = basename(filePath);
22
+ // Use a generic project name — the upload route is project-scoped but files are global
23
+ return `/api/project/${encodeURIComponent(projectName ?? "_")}/chat/uploads/${encodeURIComponent(filename)}`;
24
+ }
25
+
26
+ /** Check if a file path is an image based on extension */
27
+ export function isImagePath(path: string): boolean {
28
+ const dot = path.lastIndexOf(".");
29
+ if (dot === -1) return false;
30
+ return IMAGE_EXTS.has(path.slice(dot).toLowerCase());
31
+ }
32
+
33
+ function isPdfPath(path: string): boolean {
34
+ return path.toLowerCase().endsWith(".pdf");
35
+ }
36
+ /**
37
+ * Session-scoped image cache: blob URL + rendered box per src. Virtualized rows
38
+ * unmount/remount constantly while scrolling; refetching the image and re-growing
39
+ * from the placeholder on EVERY remount changed the row height after paint — the
40
+ * repeatable downward jerk when scrolling up through image-bearing messages.
41
+ * URLs are intentionally never revoked (bounded by unique images per session).
42
+ */
43
+ const imageBlobCache = new Map<string, { url: string; w?: number; h?: number }>();
44
+
45
+ /** Hook: fetch an image via auth header, return blob URL (cached across mounts) */
46
+ function useAuthBlob(src: string): { blobUrl: string | null; error: boolean } {
47
+ const [blobUrl, setBlobUrl] = useState<string | null>(() => imageBlobCache.get(src)?.url ?? null);
48
+ const [error, setError] = useState(false);
49
+
50
+ useEffect(() => {
51
+ const cached = imageBlobCache.get(src);
52
+ if (cached) { setBlobUrl(cached.url); return; }
53
+ let stale = false;
54
+ const token = getAuthToken();
55
+ fetch(src, { headers: token ? { Authorization: `Bearer ${token}` } : {} })
56
+ .then((r) => { if (!r.ok) throw new Error("Failed"); return r.blob(); })
57
+ .then((blob) => {
58
+ if (stale) return;
59
+ const url = URL.createObjectURL(blob);
60
+ imageBlobCache.set(src, { url });
61
+ setBlobUrl(url);
62
+ })
63
+ .catch(() => { if (!stale) setError(true); });
64
+ return () => { stale = true; }; // cache owns the URL — no revoke
65
+ }, [src]);
66
+
67
+ return { blobUrl, error };
68
+ }
69
+
70
+ /** Fetches image with auth header, renders as blob URL — click opens lightbox */
71
+ function AuthImage({ src, alt }: { src: string; alt: string }) {
72
+ const { blobUrl, error } = useAuthBlob(src);
73
+ const openOverlay = useImageOverlay((s) => s.open);
74
+ // Rendered box captured after the first load — pins the layout on remounts so
75
+ // the row never grows after paint (see imageBlobCache).
76
+ const cached = imageBlobCache.get(src);
77
+ const box = cached?.w && cached?.h ? { width: cached.w, height: cached.h } : undefined;
78
+
79
+ if (error) {
80
+ return (
81
+ <div className="flex items-center gap-1.5 rounded-md border border-border bg-background/50 px-2 py-1 text-xs text-text-secondary">
82
+ <ImageIcon className="size-3.5 shrink-0" />
83
+ <span className="truncate max-w-40">{alt}</span>
84
+ </div>
85
+ );
86
+ }
87
+
88
+ if (!blobUrl) {
89
+ return <div className="rounded-md bg-surface border border-border h-24 w-32 animate-pulse" style={box} />;
90
+ }
91
+
92
+ return (
93
+ <button
94
+ type="button"
95
+ onClick={(e) => openOverlay(blobUrl, alt, collectGallery(e.currentTarget))}
96
+ className="block text-left"
97
+ >
98
+ <img
99
+ src={blobUrl}
100
+ alt={alt}
101
+ style={box}
102
+ {...{ [GALLERY_ITEM_ATTR]: "" }}
103
+ onLoad={(e) => {
104
+ const el = e.currentTarget;
105
+ const c = imageBlobCache.get(src);
106
+ if (c && (!c.w || !c.h)) { c.w = el.offsetWidth; c.h = el.offsetHeight; }
107
+ }}
108
+ className="rounded-md max-h-48 max-w-full object-contain border border-border cursor-pointer hover:opacity-90 transition-opacity"
109
+ />
110
+ </button>
111
+ );
112
+ }
113
+
114
+ /** Chip for attached images in user bubble — tiny preview replaces icon, click opens lightbox */
115
+ export function AuthImageThumbnail({ filePath, projectName }: { filePath: string; projectName?: string }) {
116
+ const src = uploadPreviewUrl(filePath, projectName);
117
+ const { blobUrl, error } = useAuthBlob(src);
118
+ const openOverlay = useImageOverlay((s) => s.open);
119
+ const name = basename(filePath);
120
+
121
+ return (
122
+ <button
123
+ type="button"
124
+ onClick={(e) => blobUrl && openOverlay(blobUrl, name, collectGallery(e.currentTarget))}
125
+ className="flex items-center gap-1 rounded-md border border-border/60 bg-background/40 px-1.5 py-0.5 text-[11px] text-text-secondary hover:bg-surface transition-colors cursor-pointer"
126
+ >
127
+ {blobUrl ? (
128
+ <img src={blobUrl} alt={name} {...{ [GALLERY_ITEM_ATTR]: "" }} className="size-4 rounded-sm object-cover shrink-0" />
129
+ ) : error ? (
130
+ <ImageIcon className="size-3 shrink-0" />
131
+ ) : (
132
+ <div className="size-4 rounded-sm bg-surface animate-pulse shrink-0" />
133
+ )}
134
+ <span className="truncate max-w-32">{name}</span>
135
+ </button>
136
+ );
137
+ }
138
+
139
+ /** Fetches file with auth, opens in new browser tab (for PDFs, etc.) */
140
+ function AuthFileLink({ src, filename, mimeType }: { src: string; filename: string; mimeType: string }) {
141
+ const [loading, setLoading] = useState(false);
142
+
143
+ const handleClick = useCallback(async () => {
144
+ setLoading(true);
145
+ try {
146
+ const token = getAuthToken();
147
+ const res = await fetch(src, { headers: token ? { Authorization: `Bearer ${token}` } : {} });
148
+ if (!res.ok) throw new Error("Failed to load");
149
+ const blob = await res.blob();
150
+ const url = URL.createObjectURL(new Blob([blob], { type: mimeType }));
151
+ window.open(url, "_blank");
152
+ // Revoke after a delay to let the new tab load
153
+ setTimeout(() => URL.revokeObjectURL(url), 60_000);
154
+ } catch {
155
+ // Fallback: try direct link
156
+ window.open(src, "_blank");
157
+ } finally {
158
+ setLoading(false);
159
+ }
160
+ }, [src, mimeType]);
161
+
162
+ return (
163
+ <button
164
+ type="button"
165
+ onClick={handleClick}
166
+ disabled={loading}
167
+ className="flex items-center gap-1.5 rounded-md border border-border bg-background/50 px-2 py-1 text-xs text-text-secondary hover:bg-surface hover:text-text-primary transition-colors cursor-pointer disabled:opacity-50"
168
+ >
169
+ <FileText className="size-3.5 shrink-0 text-error" />
170
+ <span className="truncate max-w-40">{filename}</span>
171
+ {loading && <span className="animate-spin text-[10px]">...</span>}
172
+ </button>
173
+ );
174
+ }
@@ -0,0 +1,399 @@
1
+ /**
2
+ * The user's half of the transcript.
3
+ *
4
+ * A user message is not just text: it carries the attachments the composer sent,
5
+ * the system tags the SDK injected (`<task-notification>`, `<environment_details>`)
6
+ * which are *not* things the user typed and must not read as if they were, and
7
+ * absolute file paths worth turning into something clickable.
8
+ */
9
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
10
+ import {
11
+ Bot, CheckCircle2, ChevronDown, ChevronRight, ChevronUp, ExternalLink, FileText,
12
+ Pencil, RotateCcw, Slash, Tag, TerminalSquare, XCircle,
13
+ } from "@/lib/icons";
14
+ import { cn, basename } from "@/lib/utils";
15
+ import { MessageActionBar, ActionButton } from "./message-action-bar";
16
+ import { VersionSwitcher } from "./version-switcher";
17
+ import { parseUserMessage, type SystemTag } from "./user-message-parse";
18
+ import type { VersionGroup } from "../../../types/api";
19
+ import { useTabStore } from "@/stores/tab-store";
20
+ import { useProjectStore } from "@/stores/project-store";
21
+ import { api } from "@/lib/api-client";
22
+ import { MarkdownContent } from "./message-markdown";
23
+ import { AuthImageThumbnail, isImagePath } from "./message-media";
24
+
25
+ /** Detect if tags contain system-injected content (not real user input) */
26
+ const SYSTEM_TAG_NAMES = new Set(["task-notification", "environment_details", "local-command-caveat"]);
27
+
28
+ /** User message bubble — full width, collapsible, with system tag badges */
29
+ export function UserBubble({ content, messageId, timestamp, projectName, onFork, onEdit, isEditing, sessionId, providerId, versionGroup, onNavigateVersion, versionNavDisabled }: {
30
+ content: string;
31
+ messageId?: string;
32
+ timestamp: string;
33
+ projectName?: string;
34
+ onFork?: () => void;
35
+ onEdit?: () => void;
36
+ isEditing?: boolean;
37
+ sessionId?: string;
38
+ providerId?: string;
39
+ versionGroup?: VersionGroup;
40
+ onNavigateVersion?: (sessionId: string) => void;
41
+ versionNavDisabled?: boolean;
42
+ }) {
43
+ const { files, text, tags, command, terminalBlocks, idePath, agent } = useMemo(
44
+ () => parseUserMessage(content),
45
+ [content],
46
+ );
47
+
48
+ const isSystemContext = tags.some((t) => SYSTEM_TAG_NAMES.has(t.name));
49
+
50
+ const [expanded, setExpanded] = useState(false);
51
+ const [isOverflowing, setIsOverflowing] = useState(false);
52
+ const contentRef = useRef<HTMLDivElement>(null);
53
+
54
+ useEffect(() => {
55
+ const el = contentRef.current;
56
+ if (!el) return;
57
+ const check = () => setIsOverflowing(el.scrollHeight > el.clientHeight + 2);
58
+ check();
59
+ const ro = new ResizeObserver(check);
60
+ ro.observe(el);
61
+ return () => ro.disconnect();
62
+ }, [text]);
63
+
64
+ return (
65
+ <div className={cn("flex flex-col gap-1", !isSystemContext && "items-end")}>
66
+ {/* Own-content bubble: accent-wash fill + custom radius (sharp bottom-right
67
+ corner anchors it to the sender on the right). */}
68
+ <div
69
+ data-user-message={!isSystemContext ? "true" : undefined}
70
+ style={!isSystemContext ? { borderRadius: "var(--rad) var(--rad) 4px var(--rad)" } : undefined}
71
+ className={cn(
72
+ "group/user relative px-3 py-2 text-sm border shadow-sm transition-all",
73
+ isSystemContext
74
+ ? "rounded-lg bg-surface/40 border-border/40 text-text-secondary"
75
+ : "max-w-[80%] bg-accent-wash border-accent-wash-border text-text",
76
+ isEditing && "ring-2 ring-primary/60 border-primary/40",
77
+ )}>
78
+ {/* System tags as badges */}
79
+ {tags.length > 0 && <SystemTagBadges tags={tags} />}
80
+
81
+ {/* Agent delegation chip — parsed back from the "Use the X agent to" prefix */}
82
+ {agent && (
83
+ <div className="flex items-center gap-1.5 mb-0.5">
84
+ <span className="inline-flex items-center gap-1 rounded-md bg-sky-500/15 border border-sky-500/25 px-2 py-0.5 text-xs font-medium text-sky-600 dark:text-sky-400">
85
+ <Bot className="size-3 shrink-0" />
86
+ {agent}
87
+ </span>
88
+ </div>
89
+ )}
90
+
91
+ {/* Slash command chip — args rendered in body for expand/collapse support */}
92
+ {command && (
93
+ <div className="flex items-center gap-1.5 mb-0.5">
94
+ <span className="inline-flex items-center gap-1 rounded-md bg-primary/15 border border-primary/20 px-2 py-0.5 text-xs font-medium text-primary">
95
+ <Slash className="size-3 shrink-0" />
96
+ {command.name}
97
+ </span>
98
+ </div>
99
+ )}
100
+
101
+ {/* IDE context — the file the user had open, as a clickable chip */}
102
+ {idePath && (
103
+ <div className="flex items-center gap-1.5 mb-1 text-[11px] text-text-subtle">
104
+ <span className="shrink-0">Opened in IDE:</span>
105
+ <FilePathChip path={idePath} projectName={projectName} />
106
+ </div>
107
+ )}
108
+
109
+ {/* Attached files — image thumbnails + file chips */}
110
+ {files.length > 0 && (
111
+ <div className="flex flex-wrap gap-1.5">
112
+ {files.map((filePath, i) =>
113
+ isImagePath(filePath) ? (
114
+ <AuthImageThumbnail key={i} filePath={filePath} projectName={projectName} />
115
+ ) : (
116
+ <div
117
+ key={i}
118
+ className="flex items-center gap-1 rounded-md border border-border/60 bg-background/40 px-1.5 py-0.5 text-[11px] text-text-secondary"
119
+ >
120
+ <FileText className="size-3 shrink-0" />
121
+ <span className="truncate max-w-32">{basename(filePath)}</span>
122
+ </div>
123
+ ),
124
+ )}
125
+ </div>
126
+ )}
127
+
128
+ {/* Terminal output previews */}
129
+ {terminalBlocks.length > 0 && (
130
+ <div className="space-y-1.5">
131
+ {terminalBlocks.map((block, i) => (
132
+ <TerminalBlockPreview key={i} content={block} />
133
+ ))}
134
+ </div>
135
+ )}
136
+
137
+ {/* Text content — 2-line clamp by default, expandable */}
138
+ {text && (
139
+ <div
140
+ ref={contentRef}
141
+ className={cn(
142
+ "whitespace-pre-wrap break-words transition-all duration-200 select-text",
143
+ !expanded && "line-clamp-2",
144
+ expanded && "max-h-[50vh] overflow-y-auto",
145
+ )}
146
+ >
147
+ {isSystemContext ? <TextWithFilePaths text={text} projectName={projectName} /> : <TextWithLinks text={text} />}
148
+ </div>
149
+ )}
150
+ {(isOverflowing || expanded) && (
151
+ <button
152
+ onClick={() => setExpanded(!expanded)}
153
+ className={cn(
154
+ "flex items-center gap-1 text-xs mt-1 transition-colors",
155
+ isSystemContext ? "text-text-subtle hover:text-text-secondary" : "text-primary/70 hover:text-primary",
156
+ )}
157
+ >
158
+ {expanded ? <><ChevronUp className="size-3" />Show less</> : <><ChevronDown className="size-3" />Show more</>}
159
+ </button>
160
+ )}
161
+ {/* Version switcher — only when this message has edited siblings */}
162
+ {!isSystemContext && onNavigateVersion && (
163
+ <VersionSwitcher
164
+ group={versionGroup}
165
+ onNavigate={onNavigateVersion}
166
+ disabled={versionNavDisabled}
167
+ />
168
+ )}
169
+ </div>
170
+ {/* Action bar below the bubble — timestamp, copy, edit/fork (real user messages only) */}
171
+ {!isSystemContext && (
172
+ <MessageActionBar timestamp={timestamp} content={content}>
173
+ {onEdit && (
174
+ <ActionButton
175
+ icon={<Pencil className="size-3.5" />}
176
+ label="Edit"
177
+ title="Edit this message (continue in the same tab)"
178
+ onClick={onEdit}
179
+ />
180
+ )}
181
+ {onFork && (
182
+ <ActionButton
183
+ icon={<RotateCcw className="size-3.5" />}
184
+ label="Fork"
185
+ title="Retry from this message (fork into a new tab)"
186
+ onClick={onFork}
187
+ />
188
+ )}
189
+ </MessageActionBar>
190
+ )}
191
+ </div>
192
+ );
193
+ }
194
+
195
+ /** Collapsible terminal output preview in user messages */
196
+ function TerminalBlockPreview({ content }: { content: string }) {
197
+ const [expanded, setExpanded] = useState(false);
198
+ return (
199
+ <div>
200
+ <button
201
+ type="button"
202
+ onClick={() => setExpanded(!expanded)}
203
+ className={cn(
204
+ "flex items-center gap-1.5 rounded-md border px-2 py-1 text-xs transition-colors",
205
+ expanded
206
+ ? "border-primary/50 bg-surface-elevated text-text-primary"
207
+ : "border-border/60 bg-background/40 text-text-secondary hover:bg-surface",
208
+ )}
209
+ >
210
+ <TerminalSquare className="size-3.5 shrink-0" />
211
+ <span>Terminal output</span>
212
+ <ChevronDown className={cn("size-3 shrink-0 transition-transform", expanded && "rotate-180")} />
213
+ </button>
214
+ {expanded && (
215
+ <pre className="mt-1 max-h-40 overflow-auto rounded-md border border-border bg-background p-2 text-xs text-text-primary font-mono whitespace-pre-wrap break-words">
216
+ {content}
217
+ </pre>
218
+ )}
219
+ </div>
220
+ );
221
+ }
222
+
223
+ /** Render system tags as collapsible badges */
224
+ function SystemTagBadges({ tags }: { tags: SystemTag[] }) {
225
+ return (
226
+ <div className="flex flex-wrap gap-1.5">
227
+ {tags.map((tag, i) => (
228
+ <SystemTagBadge key={i} tag={tag} />
229
+ ))}
230
+ </div>
231
+ );
232
+ }
233
+
234
+ function SystemTagBadge({ tag }: { tag: SystemTag }) {
235
+ const [open, setOpen] = useState(false);
236
+
237
+ // Task notification: render formatted instead of raw XML
238
+ if (tag.name === "task-notification") {
239
+ return <TaskNotificationBadge content={tag.content} />;
240
+ }
241
+
242
+ return (
243
+ <div className="text-xs">
244
+ <button
245
+ onClick={() => setOpen(!open)}
246
+ className="flex items-center gap-1 rounded-full border border-border/60 bg-surface/50 px-2 py-0.5 text-text-subtle hover:text-text-secondary hover:bg-surface transition-colors"
247
+ >
248
+ <Tag className="size-2.5" />
249
+ <span>{tag.label}</span>
250
+ <ChevronRight className={cn("size-2.5 transition-transform", open && "rotate-90")} />
251
+ </button>
252
+ {open && (
253
+ <div className="mt-1 rounded border border-border/40 bg-surface/30 px-2 py-1.5 text-[11px] text-text-subtle/80 whitespace-pre-wrap max-h-40 overflow-y-auto leading-relaxed">
254
+ {tag.content}
255
+ </div>
256
+ )}
257
+ </div>
258
+ );
259
+ }
260
+
261
+ /** Extract a sub-tag value from XML-like content */
262
+ function xmlTag(content: string, tag: string): string | undefined {
263
+ const m = content.match(new RegExp(`<${tag}>([\\s\\S]*?)</${tag}>`));
264
+ return m?.[1]?.trim() || undefined;
265
+ }
266
+
267
+ /** Formatted badge for <task-notification> — shows status, summary, output file, result */
268
+ function TaskNotificationBadge({ content }: { content: string }) {
269
+ const [open, setOpen] = useState(false);
270
+ const status = xmlTag(content, "status");
271
+ const summary = xmlTag(content, "summary");
272
+ const outputFile = xmlTag(content, "output-file");
273
+ const result = xmlTag(content, "result");
274
+ const isOk = status === "completed";
275
+
276
+ return (
277
+ <div className="text-xs">
278
+ <button
279
+ onClick={() => setOpen(!open)}
280
+ className="flex items-center gap-1.5 rounded-full border border-border/60 bg-surface/50 px-2 py-0.5 text-text-subtle hover:text-text-secondary hover:bg-surface transition-colors"
281
+ >
282
+ {isOk ? <CheckCircle2 className="size-2.5 text-success" /> : <XCircle className="size-2.5 text-warning" />}
283
+ <span className="truncate max-w-80">{summary ?? "Task notification"}</span>
284
+ <ChevronRight className={cn("size-2.5 transition-transform shrink-0", open && "rotate-90")} />
285
+ </button>
286
+ {open && (
287
+ <div className="mt-1 rounded border border-border/40 bg-surface/30 px-2 py-1.5 space-y-1.5">
288
+ {/* Full summary (button truncates it) */}
289
+ {summary && <p className="text-[11px] text-text-secondary">{summary}</p>}
290
+ {outputFile && <FilePathChip path={outputFile} />}
291
+ {result && (
292
+ <div className="text-[11px] text-text-subtle/80 max-h-60 overflow-y-auto leading-relaxed">
293
+ <MarkdownContent content={result} />
294
+ </div>
295
+ )}
296
+ </div>
297
+ )}
298
+ </div>
299
+ );
300
+ }
301
+
302
+ /** Clickable file path chip — opens file in editor tab */
303
+ function FilePathChip({ path, projectName }: { path: string; projectName?: string }) {
304
+ const handleClick = useCallback(() => {
305
+ const openTab = useTabStore.getState().openTab;
306
+ const pName = projectName ?? useProjectStore.getState().activeProject?.name;
307
+ const fileName = basename(path);
308
+ const meta: Record<string, unknown> = { filePath: path };
309
+ if (pName) meta.projectName = pName;
310
+ // Try to verify file exists, then open; fallback: open directly
311
+ api.get(`/api/fs/read?path=${encodeURIComponent(path)}`).then(() => {
312
+ openTab({ type: "editor", title: fileName, metadata: meta, projectId: null, closable: true });
313
+ }).catch(() => {
314
+ openTab({ type: "editor", title: fileName, metadata: meta, projectId: null, closable: true });
315
+ });
316
+ }, [path, projectName]);
317
+
318
+ return (
319
+ <button
320
+ type="button"
321
+ onClick={handleClick}
322
+ className="inline-flex items-center gap-1 rounded border border-border/50 bg-surface/50 px-1.5 py-0.5 font-mono text-[10px] text-text-secondary hover:text-text-primary hover:bg-surface transition-colors cursor-pointer"
323
+ >
324
+ <FileText className="size-2.5 shrink-0" />
325
+ <span className="truncate max-w-60">{basename(path)}</span>
326
+ <ExternalLink className="size-2 shrink-0 opacity-50" />
327
+ </button>
328
+ );
329
+ }
330
+
331
+ /** Render plain text with http(s) URLs turned into clickable links. Preserves
332
+ * whitespace/newlines via the surrounding `whitespace-pre-wrap` container. */
333
+ function TextWithLinks({ text }: { text: string }) {
334
+ const parts = useMemo(() => {
335
+ const re = /(https?:\/\/[^\s<>()]+[^\s<>().,;:!?'"])/g;
336
+ const result: { kind: "text" | "url"; value: string }[] = [];
337
+ let last = 0;
338
+ let m;
339
+ while ((m = re.exec(text)) !== null) {
340
+ if (m.index > last) result.push({ kind: "text", value: text.slice(last, m.index) });
341
+ result.push({ kind: "url", value: m[1]! });
342
+ last = m.index + m[0].length;
343
+ }
344
+ if (last < text.length) result.push({ kind: "text", value: text.slice(last) });
345
+ return result;
346
+ }, [text]);
347
+
348
+ // No URLs — return the raw string so nothing about the layout changes.
349
+ if (parts.every((p) => p.kind === "text")) return <>{text}</>;
350
+
351
+ return (
352
+ <>
353
+ {parts.map((p, i) =>
354
+ p.kind === "url" ? (
355
+ <a
356
+ key={i}
357
+ href={p.value}
358
+ target="_blank"
359
+ rel="noopener noreferrer"
360
+ className="text-primary underline underline-offset-2 hover:text-primary/80 break-all"
361
+ onClick={(e) => e.stopPropagation()}
362
+ >
363
+ {p.value}
364
+ </a>
365
+ ) : (
366
+ <span key={i}>{p.value}</span>
367
+ ),
368
+ )}
369
+ </>
370
+ );
371
+ }
372
+
373
+ /** Render text with absolute file paths detected and turned into clickable chips */
374
+ function TextWithFilePaths({ text, projectName }: { text: string; projectName?: string }) {
375
+ const parts = useMemo(() => {
376
+ // Match absolute file paths (at least 2 segments)
377
+ const re = /(\/(?:[\w.\-]+\/)+[\w.\-]+)/g;
378
+ const result: { kind: "text" | "path"; value: string }[] = [];
379
+ let last = 0;
380
+ let m;
381
+ while ((m = re.exec(text)) !== null) {
382
+ if (m.index > last) result.push({ kind: "text", value: text.slice(last, m.index) });
383
+ result.push({ kind: "path", value: m[1]! });
384
+ last = m.index + m[0].length;
385
+ }
386
+ if (last < text.length) result.push({ kind: "text", value: text.slice(last) });
387
+ return result;
388
+ }, [text]);
389
+
390
+ return (
391
+ <>
392
+ {parts.map((p, i) =>
393
+ p.kind === "path"
394
+ ? <FilePathChip key={i} path={p.value} projectName={projectName} />
395
+ : <span key={i}>{p.value}</span>,
396
+ )}
397
+ </>
398
+ );
399
+ }