@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
@@ -5,8 +5,8 @@
5
5
  * `RemoteDesktopWarningGate`, so the connection hook (and its session nonce) only runs once the
6
6
  * user has read the warning.
7
7
  */
8
- import { useCallback, useRef } from "react";
9
- import { RotateCw, MonitorX, Gauge } from "@/lib/icons";
8
+ import { useCallback, useMemo, useRef, useState } from "react";
9
+ import { RotateCw, MonitorX } from "@/lib/icons";
10
10
  import type { WindowContentProps } from "@/components/floating-window/window-content-registry";
11
11
  import { useWindowStore } from "@/components/floating-window/window-store";
12
12
  import { useSettingsStore } from "@/stores/settings-store";
@@ -17,6 +17,13 @@ import { RemoteDesktopStatsOverlay } from "./remote-desktop-stats-overlay";
17
17
  import { RemoteDesktopWarningGate } from "./remote-desktop-warning-gate";
18
18
  import { RemoteDesktopReadinessGate } from "./remote-desktop-readiness-gate";
19
19
  import { useRemoteDesktopDisplayChoice } from "./use-remote-desktop-display-choice";
20
+ import { RemoteDesktopClipboardNotice } from "./remote-desktop-clipboard-notice";
21
+ import { useRemoteDesktopReadiness } from "./use-remote-desktop-readiness";
22
+ import { RemoteDesktopToolbar } from "./remote-desktop-toolbar";
23
+ import { canvasCssSize } from "./remote-desktop-view-style";
24
+ import { useRemoteDesktopRecorder } from "./use-remote-desktop-recorder";
25
+ import { saveCanvasScreenshot } from "./remote-desktop-recording";
26
+ import { RemoteDesktopCustomQualityDialog } from "./remote-desktop-custom-quality-dialog";
20
27
 
21
28
  export default function RemoteDesktopWindowContent({ id }: WindowContentProps) {
22
29
  const closeWindow = useWindowStore((s) => s.close);
@@ -33,12 +40,53 @@ export default function RemoteDesktopWindowContent({ id }: WindowContentProps) {
33
40
  function RemoteDesktopViewer() {
34
41
  const canvasRef = useRef<HTMLCanvasElement | null>(null);
35
42
  const display = useRemoteDesktopDisplayChoice();
36
- const { connState, errorMessage, decoderStatus, decoderErrorMessage, sendMessage, reconnect, getFrameCount, getTotalBytes } =
37
- useRemoteDesktopConnection(canvasRef, { displayId: display.displayId });
43
+ const {
44
+ connState, errorMessage, decoderStatus, decoderErrorMessage, sendMessage, reconnect,
45
+ getFrameCount, getTotalBytes, frameSize, quality, setQuality, setCustomQuality,
46
+ showCursor, setShowCursor,
47
+ codec, setCodec, audioOn, setAudioOn, getAudioTracks,
48
+ privacyOn, privacyError, setPrivacyOn,
49
+ hostModeId, hostOriginalModeId, resolutionError, setHostMode,
50
+ pendingHostClipboard, clearHostClipboard, sendClipboard, requestHostClipboard,
51
+ } = useRemoteDesktopConnection(canvasRef, { displayId: display.displayId });
38
52
  const statsVisible = useSettingsStore((s) => s.remoteDesktopStatsVisible);
39
53
  const toggleStats = useSettingsStore((s) => s.toggleRemoteDesktopStatsVisible);
54
+ const viewStyle = useSettingsStore((s) => s.remoteDesktopViewStyle);
55
+ const customScale = useSettingsStore((s) => s.remoteDesktopCustomScale);
56
+ const setViewStyle = useSettingsStore((s) => s.setRemoteDesktopViewStyle);
57
+ const setCustomScale = useSettingsStore((s) => s.setRemoteDesktopCustomScale);
58
+ const clipboardSync = useSettingsStore((s) => s.remoteDesktopClipboardSync);
59
+ const qualityChoice = useSettingsStore((s) => s.remoteDesktopQuality);
60
+ const customPercent = useSettingsStore((s) => s.remoteDesktopCustomQualityPercent);
61
+ const customFps = useSettingsStore((s) => s.remoteDesktopCustomFps);
62
+ const customMore = useSettingsStore((s) => s.remoteDesktopCustomQualityMore);
63
+ const setCustomMore = useSettingsStore((s) => s.setRemoteDesktopCustomQualityMore);
64
+ const [customQualityOpen, setCustomQualityOpen] = useState(false);
65
+ const setClipboardSync = useSettingsStore((s) => s.setRemoteDesktopClipboardSync);
66
+ // One fetch, no polling: this is read only to explain a clipboard that cannot work. The
67
+ // readiness *gate* above already cleared, so nothing here is allowed to block the viewer.
68
+ const { caps } = useRemoteDesktopReadiness(false);
69
+ const [pasteAttempted, setPasteAttempted] = useState(false);
40
70
 
41
- useRemoteInputCapture(canvasRef, sendMessage, connState === "streaming");
71
+ // Stable object: `useRemoteInputCapture` lists it as an effect dependency, so a fresh literal
72
+ // per render would re-bind every canvas listener on every render.
73
+ //
74
+ // Undefined with sync off, which is the whole implementation of the toggle: the hook then
75
+ // stops exempting Ctrl+V from `preventDefault` and forwards it as an ordinary keystroke, so
76
+ // the host pastes its *own* clipboard and no text crosses the connection either way.
77
+ const clipboardHooks = useMemo(() => (clipboardSync ? {
78
+ onPasteText: (text: string, shift: boolean) => { setPasteAttempted(true); sendClipboard(text, true, shift); },
79
+ onCopyCombo: requestHostClipboard,
80
+ } : undefined), [clipboardSync, sendClipboard, requestHostClipboard]);
81
+
82
+ useRemoteInputCapture(canvasRef, sendMessage, connState === "streaming", clipboardHooks);
83
+
84
+ // The recorder muxes the session audio in when it is playing, so one file carries both.
85
+ const recorder = useRemoteDesktopRecorder(canvasRef, getAudioTracks);
86
+
87
+ // null in `adaptive` (and before the first frame): the canvas keeps its `max-*-full` classes
88
+ // and the browser fits it, which is the path this viewer has always been on.
89
+ const cssSize = canvasCssSize(viewStyle, customScale, frameSize);
42
90
 
43
91
  const overlayMessage = decoderStatus === "unsupported"
44
92
  ? decoderErrorMessage
@@ -56,37 +104,80 @@ function RemoteDesktopViewer() {
56
104
  data-testid="remote-desktop-window"
57
105
  data-conn-state={connState}
58
106
  >
59
- <canvas ref={canvasRef} data-testid="remote-desktop-canvas" className="max-h-full max-w-full outline-none" />
107
+ {/* The scroll container is a child rather than this root, because the overlays below are
108
+ absolutely positioned against the root — inside a scroller they would scroll away with
109
+ the picture. `m-auto` on the canvas rather than `justify-center` on the box: centring
110
+ an oversized child with `justify-content` puts half the overflow *before* the scroll
111
+ origin, where no scrollbar can reach it (the top-left of a 1:1 3440×1440 capture in a
112
+ small window). Auto margins collapse to 0 when the child overflows, so both work. */}
113
+ <div className={cn("flex h-full w-full", cssSize ? "overflow-auto" : "overflow-hidden")}>
114
+ <canvas
115
+ ref={canvasRef}
116
+ data-testid="remote-desktop-canvas"
117
+ data-view-style={viewStyle}
118
+ className={cn("m-auto outline-none", !cssSize && "max-h-full max-w-full")}
119
+ style={cssSize ? { width: cssSize.width, height: cssSize.height } : undefined}
120
+ />
121
+ </div>
60
122
  <RemoteDesktopStatsOverlay canvasRef={canvasRef} getFrameCount={getFrameCount} getTotalBytes={getTotalBytes} />
61
- {/* Multi-monitor hosts: pick which display to stream. Left of the stats toggle, same
62
- corner cluster; a single-display host renders nothing here. */}
63
- {display.displays.length > 1 && (
64
- <select
65
- value={display.current?.id ?? ""}
66
- onChange={(e) => display.select(e.target.value)}
67
- aria-label="Display"
68
- className="absolute right-8 top-1 z-40 h-6 max-w-[45%] rounded bg-black/40 px-1.5 text-xs text-white/80 hover:bg-black/60"
69
- data-testid="remote-desktop-display-select"
70
- >
71
- {display.displays.map((d) => (
72
- <option key={d.id} value={d.id}>{d.label}{d.primary ? " (main)" : ""} · {d.width}×{d.height}</option>
73
- ))}
74
- </select>
75
- )}
76
- {/* Small corner toggle for the stats overlay above — top-right so it never collides with
77
- the overlay itself (top-left) or the window's own title bar/controls above this body. */}
78
- <button
79
- type="button"
80
- onClick={toggleStats}
81
- aria-label="Toggle stats overlay"
82
- aria-pressed={statsVisible}
83
- className={cn(
84
- "absolute right-1 top-1 z-40 flex size-6 items-center justify-center rounded bg-black/40 text-white/70 hover:bg-black/60 hover:text-white",
85
- statsVisible && "bg-primary/70 text-primary-foreground hover:bg-primary/80",
86
- )}
87
- >
88
- <Gauge className="size-3.5" />
89
- </button>
123
+ <RemoteDesktopToolbar
124
+ displays={display.displays}
125
+ currentDisplayId={display.current?.id}
126
+ onSelectDisplay={display.select}
127
+ quality={quality}
128
+ qualityChoice={qualityChoice}
129
+ onSetQuality={setQuality}
130
+ onOpenCustomQuality={() => setCustomQualityOpen(true)}
131
+ codecs={caps?.encoders ?? []}
132
+ codec={codec}
133
+ onSetCodec={setCodec}
134
+ hostModes={caps?.resolutions?.modes ?? []}
135
+ hostModeId={hostModeId}
136
+ hostOriginalModeId={hostOriginalModeId}
137
+ onSetHostMode={setHostMode}
138
+ canResizeHost={caps?.inputReady ?? false}
139
+ resolutionError={resolutionError}
140
+ viewStyle={viewStyle}
141
+ customScale={customScale}
142
+ onSetViewStyle={setViewStyle}
143
+ onSetCustomScale={setCustomScale}
144
+ showCursor={showCursor}
145
+ onSetShowCursor={setShowCursor}
146
+ audioOn={audioOn}
147
+ audioReason={caps && !caps.audio.available ? caps.audio.reason : null}
148
+ onSetAudioOn={setAudioOn}
149
+ privacyOn={privacyOn}
150
+ privacyReason={privacyError ?? (caps && !caps.privacy.available ? caps.privacy.reason : null)}
151
+ privacyCanBlank={caps?.privacy?.canBlank ?? false}
152
+ onSetPrivacyOn={setPrivacyOn}
153
+ recording={recorder.recording}
154
+ recordingElapsedSec={recorder.elapsedSec}
155
+ recordingSupported={recorder.supported}
156
+ onToggleRecording={recorder.toggle}
157
+ onScreenshot={() => void saveCanvasScreenshot(canvasRef.current)}
158
+ clipboardEnabled={clipboardSync}
159
+ onSetClipboardEnabled={setClipboardSync}
160
+ statsVisible={statsVisible}
161
+ onToggleStats={toggleStats}
162
+ />
163
+ {/* Only after a real paste attempt: a host with no clipboard tool is not worth a warning
164
+ until the user actually reaches for it. */}
165
+ <RemoteDesktopClipboardNotice
166
+ pendingText={pendingHostClipboard}
167
+ onDismiss={clearHostClipboard}
168
+ missingToolAction={pasteAttempted && caps && !caps.clipboard.available ? caps.clipboard.action : null}
169
+ onDismissMissingTool={() => setPasteAttempted(false)}
170
+ />
171
+ <RemoteDesktopCustomQualityDialog
172
+ open={customQualityOpen}
173
+ onOpenChange={setCustomQualityOpen}
174
+ percent={customPercent}
175
+ fps={customFps}
176
+ more={customMore}
177
+ onSetMore={setCustomMore}
178
+ onCommit={setCustomQuality}
179
+ frameSize={frameSize}
180
+ />
90
181
  {overlayMessage && (
91
182
  <div className="absolute inset-0 flex flex-col items-center justify-center gap-3 bg-black/70 text-sm text-white">
92
183
  <MonitorX className="size-6 opacity-70" />
@@ -46,6 +46,13 @@ export interface UseH264CanvasDecoderResult {
46
46
  * last `reset()`. Ref-backed, not React state — read it from a poll (e.g. the stats overlay),
47
47
  * not a render dependency, so every decoded frame doesn't force a re-render. */
48
48
  getFrameCount: () => number;
49
+ /** Pixel size of the decoded picture — what `canvas.width`/`height` were last set to, 0×0
50
+ * before the first frame. Real state rather than a ref (unlike the counter above) because
51
+ * the `original`/`custom` scale modes size the *element* from it, and it changes once per
52
+ * rung rather than once per frame. Deliberately kept across a `reset()`: a rung switch
53
+ * respawns the host encoder, and blanking this mid-switch would collapse the canvas to
54
+ * nothing for ~400ms before the new resolution arrives. */
55
+ frameSize: { width: number; height: number };
49
56
  }
50
57
 
51
58
  export function useH264CanvasDecoder(
@@ -53,6 +60,7 @@ export function useH264CanvasDecoder(
53
60
  ): UseH264CanvasDecoderResult {
54
61
  const [status, setStatus] = useState<DecoderStatus>("idle");
55
62
  const [errorMessage, setErrorMessage] = useState<string | null>(null);
63
+ const [frameSize, setFrameSize] = useState({ width: 0, height: 0 });
56
64
  const decoderRef = useRef<VideoDecoder | null>(null);
57
65
  const ctxRef = useRef<CanvasRenderingContext2D | null>(null);
58
66
  const decodedAnyKeyRef = useRef(false);
@@ -97,6 +105,8 @@ export function useH264CanvasDecoder(
97
105
  if (canvas.width !== frame.displayWidth || canvas.height !== frame.displayHeight) {
98
106
  canvas.width = frame.displayWidth;
99
107
  canvas.height = frame.displayHeight;
108
+ // Inside the size guard, so this is one setState per resolution change, not per frame.
109
+ setFrameSize({ width: frame.displayWidth, height: frame.displayHeight });
100
110
  }
101
111
  if (!ctxRef.current) ctxRef.current = canvas.getContext("2d");
102
112
  ctxRef.current?.drawImage(frame, 0, 0, canvas.width, canvas.height);
@@ -171,5 +181,6 @@ export function useH264CanvasDecoder(
171
181
  decodeAccessUnit,
172
182
  reset,
173
183
  getFrameCount,
184
+ frameSize,
174
185
  };
175
186
  }
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Plays the host's audio: Opus packets → `AudioDecoder` → scheduled into one `AudioContext`.
3
+ *
4
+ * The sibling of `use-h264-canvas-decoder.ts`, and it needs no new platform support: every
5
+ * browser that has `VideoDecoder` (which the viewer already hard-requires) has `AudioDecoder`.
6
+ *
7
+ * Scheduling is one `AudioBufferSourceNode` per decoded 20 ms frame against a running cursor,
8
+ * rather than an `AudioWorklet` ring buffer. That is 50 nodes a second, which the audio thread
9
+ * does not notice, and it avoids a separate worklet module — the thing a worklet would buy is
10
+ * sample-accurate underrun handling, and this player wants the opposite: on an underrun it
11
+ * should jump back to "now" and carry on, because a remote desktop's audio has to stay in sync
12
+ * with the picture rather than play every sample late.
13
+ *
14
+ * `TARGET_LEAD_MS` is that resync threshold in both directions. Too small and every network
15
+ * hiccup is a gap; too large and the audio lags the video visibly. 120 ms sits above the ~40 ms
16
+ * the host's Ogg pages arrive in with room for one missed page.
17
+ */
18
+ import { useCallback, useEffect, useRef, useState } from "react";
19
+
20
+ /** How far ahead of the clock the next frame is scheduled. Also the resync window: a cursor
21
+ * further behind than this has fallen off and is snapped forward. */
22
+ const TARGET_LEAD_MS = 120;
23
+ /** Beyond this the client is scheduling audio it will not hear for a noticeable time, which
24
+ * means the host is producing faster than realtime (a resumed suspended sink) — drop to the
25
+ * target rather than building a queue that can only be heard late. */
26
+ const MAX_LEAD_MS = 400;
27
+
28
+ export type AudioPlayerStatus = "idle" | "playing" | "unsupported";
29
+
30
+ export interface UseOpusAudioPlayerResult {
31
+ status: AudioPlayerStatus;
32
+ /** Configure for a stream; safe to call again when the host restarts its encoder. */
33
+ configure: (channels: number, sampleRate: number) => Promise<void>;
34
+ /** Feed one Opus packet. Ignored until `configure` has run. */
35
+ decodePacket: (packet: Uint8Array) => void;
36
+ /** Tear the decoder and the context down. */
37
+ reset: () => void;
38
+ /** Tracks for the recorder to mux in, empty while idle. */
39
+ getTracks: () => MediaStreamTrack[];
40
+ }
41
+
42
+ export function useOpusAudioPlayer(): UseOpusAudioPlayerResult {
43
+ const ctxRef = useRef<AudioContext | null>(null);
44
+ const decoderRef = useRef<AudioDecoder | null>(null);
45
+ const destRef = useRef<MediaStreamAudioDestinationNode | null>(null);
46
+ /** When the next frame should start, in the context's own clock. */
47
+ const cursorRef = useRef(0);
48
+ const timestampRef = useRef(0);
49
+ const [status, setStatus] = useState<AudioPlayerStatus>("idle");
50
+
51
+ const reset = useCallback(() => {
52
+ try { decoderRef.current?.close(); } catch { /* already closed */ }
53
+ decoderRef.current = null;
54
+ destRef.current = null;
55
+ // The context is closed too, not just disconnected: a suspended AudioContext still holds
56
+ // an audio device open, which on some hosts keeps the sink awake for the whole session.
57
+ const ctx = ctxRef.current;
58
+ ctxRef.current = null;
59
+ if (ctx) void ctx.close().catch(() => {});
60
+ cursorRef.current = 0;
61
+ timestampRef.current = 0;
62
+ setStatus((s) => (s === "unsupported" ? s : "idle"));
63
+ }, []);
64
+
65
+ const play = useCallback((data: AudioData) => {
66
+ const ctx = ctxRef.current;
67
+ if (!ctx) { data.close(); return; }
68
+ const frames = data.numberOfFrames;
69
+ const channels = data.numberOfChannels;
70
+ const buffer = ctx.createBuffer(channels, frames, data.sampleRate);
71
+ // `copyTo` per plane: the decoder hands back planar f32, which is exactly what an
72
+ // AudioBuffer channel wants, so there is no interleaving to undo.
73
+ for (let ch = 0; ch < channels; ch++) {
74
+ const plane = new Float32Array(frames);
75
+ data.copyTo(plane, { planeIndex: ch, format: "f32-planar" });
76
+ buffer.copyToChannel(plane, ch);
77
+ }
78
+ data.close();
79
+
80
+ const now = ctx.currentTime;
81
+ const lead = (cursorRef.current - now) * 1000;
82
+ // Underrun (the cursor is in the past) or a runaway queue: snap back to the target lead.
83
+ // Without this the first case plays everything late forever and the second drifts further
84
+ // behind the picture with every packet.
85
+ if (lead < 0 || lead > MAX_LEAD_MS) cursorRef.current = now + TARGET_LEAD_MS / 1000;
86
+
87
+ const source = ctx.createBufferSource();
88
+ source.buffer = buffer;
89
+ source.connect(ctx.destination);
90
+ if (destRef.current) source.connect(destRef.current);
91
+ source.start(cursorRef.current);
92
+ cursorRef.current += buffer.duration;
93
+ }, []);
94
+
95
+ const configure = useCallback(async (channels: number, sampleRate: number) => {
96
+ if (typeof AudioDecoder === "undefined" || typeof AudioContext === "undefined") {
97
+ setStatus("unsupported");
98
+ return;
99
+ }
100
+ reset();
101
+ const ctx = new AudioContext({ sampleRate, latencyHint: "interactive" });
102
+ ctxRef.current = ctx;
103
+ // Autoplay policy: a context created without a user gesture starts suspended, and
104
+ // `resume()` only succeeds once the page has one. The toggle that turns audio on *is* a
105
+ // click, so by the time this runs there has been one.
106
+ if (ctx.state === "suspended") await ctx.resume().catch(() => {});
107
+ destRef.current = ctx.createMediaStreamDestination();
108
+
109
+ const decoder = new AudioDecoder({
110
+ output: play,
111
+ // A decode error is one bad packet, not a dead stream: Opus frames are independent, so
112
+ // the next one decodes fine. Resetting instead of closing keeps the stream alive.
113
+ error: () => { try { decoderRef.current?.reset(); } catch { /* gone */ } },
114
+ });
115
+ try {
116
+ decoder.configure({ codec: "opus", sampleRate, numberOfChannels: channels });
117
+ } catch {
118
+ setStatus("unsupported");
119
+ return;
120
+ }
121
+ decoderRef.current = decoder;
122
+ cursorRef.current = 0;
123
+ timestampRef.current = 0;
124
+ setStatus("playing");
125
+ }, [play, reset]);
126
+
127
+ const decodePacket = useCallback((packet: Uint8Array) => {
128
+ const decoder = decoderRef.current;
129
+ if (!decoder || decoder.state !== "configured") return;
130
+ // Timestamps have to advance monotonically or the decoder rejects the chunk, and the host
131
+ // sends no per-packet pts — so they are counted here at the one frame duration the encoder
132
+ // is pinned to (`-frame_duration 20`).
133
+ const timestamp = timestampRef.current;
134
+ timestampRef.current += 20_000;
135
+ try {
136
+ decoder.decode(new EncodedAudioChunk({ type: "key", timestamp, data: packet }));
137
+ } catch { /* a chunk the decoder refused; the next one is independent */ }
138
+ }, []);
139
+
140
+ const getTracks = useCallback(
141
+ () => (destRef.current ? destRef.current.stream.getAudioTracks() : []),
142
+ [],
143
+ );
144
+
145
+ useEffect(() => () => reset(), [reset]);
146
+
147
+ return { status, configure, decodePacket, reset, getTracks };
148
+ }