@hienlh/ppm 0.23.11 → 0.23.12

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 (641) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/CLAUDE.md +13 -2
  3. package/assets/skills/ppm/SKILL.md +1 -1
  4. package/assets/skills/ppm/references/cli-reference.md +4 -4
  5. package/assets/skills/ppm/references/http-api.md +37 -1
  6. package/bun.lock +34 -0
  7. package/dist/web/assets/accounts-settings-section-CGe5ikvs.js +1 -0
  8. package/dist/web/assets/accounts-settings-section-CGe5ikvs.js.br +0 -0
  9. package/dist/web/assets/accounts-settings-section-CGe5ikvs.js.gz +0 -0
  10. package/dist/web/assets/{agent-session-sheet-CBMsnl_p.js → agent-session-sheet-vBhfHYrC.js} +2 -2
  11. package/dist/web/assets/agent-session-sheet-vBhfHYrC.js.br +0 -0
  12. package/dist/web/assets/agent-session-sheet-vBhfHYrC.js.gz +0 -0
  13. package/dist/web/assets/{agent-session-window-content-D9C9FA7E.js → agent-session-window-content-BxIxDN5i.js} +1 -1
  14. package/dist/web/assets/agent-session-window-content-BxIxDN5i.js.br +0 -0
  15. package/dist/web/assets/agent-session-window-content-BxIxDN5i.js.gz +0 -0
  16. package/dist/web/assets/{ai-resource-editor-DhUVgDEG.js → ai-resource-editor-Y8clBPRv.js} +1 -1
  17. package/dist/web/assets/ai-resource-editor-Y8clBPRv.js.br +0 -0
  18. package/dist/web/assets/ai-resource-editor-Y8clBPRv.js.gz +0 -0
  19. package/dist/web/assets/{ai-settings-section-DOckWvp4.js → ai-settings-section-BB8vscte.js} +1 -1
  20. package/dist/web/assets/{ai-settings-section-CETpSpYd.js → ai-settings-section-Ck8uN_Bw.js} +1 -1
  21. package/dist/web/assets/ai-settings-section-Ck8uN_Bw.js.br +0 -0
  22. package/dist/web/assets/ai-settings-section-Ck8uN_Bw.js.gz +0 -0
  23. package/dist/web/assets/android-tab-BIOUVVoW.js +2 -0
  24. package/dist/web/assets/android-tab-BIOUVVoW.js.br +0 -0
  25. package/dist/web/assets/android-tab-BIOUVVoW.js.gz +0 -0
  26. package/dist/web/assets/{api-settings-JIgKfcCx.js → api-settings-D1axn9vm.js} +1 -1
  27. package/dist/web/assets/{appearance-settings-section-BJUiLUb7.js → appearance-settings-section-Bv5_n9lH.js} +1 -1
  28. package/dist/web/assets/appearance-settings-section-Bv5_n9lH.js.br +0 -0
  29. package/dist/web/assets/appearance-settings-section-Bv5_n9lH.js.gz +0 -0
  30. package/dist/web/assets/{apps-panel-1VElRgV3.js → apps-panel-DXHF0QEv.js} +1 -1
  31. package/dist/web/assets/apps-panel-DXHF0QEv.js.br +0 -0
  32. package/dist/web/assets/apps-panel-DXHF0QEv.js.gz +0 -0
  33. package/dist/web/assets/{audio-preview-DYkyHu5X.js → audio-preview-DpjOKEm4.js} +1 -1
  34. package/dist/web/assets/audio-preview-DpjOKEm4.js.br +0 -0
  35. package/dist/web/assets/audio-preview-DpjOKEm4.js.gz +0 -0
  36. package/dist/web/assets/branch-review-tab-4dgMNadQ.js +1 -0
  37. package/dist/web/assets/branch-review-tab-4dgMNadQ.js.br +0 -0
  38. package/dist/web/assets/branch-review-tab-4dgMNadQ.js.gz +0 -0
  39. package/dist/web/assets/chat-tab-Bbe_77oJ.js +15 -0
  40. package/dist/web/assets/chat-tab-Bbe_77oJ.js.br +0 -0
  41. package/dist/web/assets/chat-tab-Bbe_77oJ.js.gz +0 -0
  42. package/dist/web/assets/chat-tab-D3i5O3dO.js +1 -0
  43. package/dist/web/assets/chat-tab-D3i5O3dO.js.br +0 -0
  44. package/dist/web/assets/chat-tab-D3i5O3dO.js.gz +0 -0
  45. package/dist/web/assets/code-editor-DXsIWoQ1.js +7 -0
  46. package/dist/web/assets/code-editor-DXsIWoQ1.js.br +0 -0
  47. package/dist/web/assets/code-editor-DXsIWoQ1.js.gz +0 -0
  48. package/dist/web/assets/{codex-reset-credit-button-Fr45FUxi.js → codex-reset-credit-button-DRhievFQ.js} +1 -1
  49. package/dist/web/assets/codex-reset-credit-button-DRhievFQ.js.br +0 -0
  50. package/dist/web/assets/codex-reset-credit-button-DRhievFQ.js.gz +0 -0
  51. package/dist/web/assets/{conflict-editor-B-wdjSXI.js → conflict-editor-D_RImo_V.js} +3 -3
  52. package/dist/web/assets/conflict-editor-D_RImo_V.js.br +0 -0
  53. package/dist/web/assets/conflict-editor-D_RImo_V.js.gz +0 -0
  54. package/dist/web/assets/{csv-preview-B6RodObB.js → csv-preview-BuFdfLo3.js} +1 -1
  55. package/dist/web/assets/csv-preview-BuFdfLo3.js.br +0 -0
  56. package/dist/web/assets/csv-preview-BuFdfLo3.js.gz +0 -0
  57. package/dist/web/assets/{database-viewer-CdPBUQPY.js → database-viewer-ChcbYn9E.js} +1 -1
  58. package/dist/web/assets/database-viewer-ChcbYn9E.js.br +0 -0
  59. package/dist/web/assets/database-viewer-ChcbYn9E.js.gz +0 -0
  60. package/dist/web/assets/{design-settings-section-BpCpaBwL.js → design-settings-section-B_xOf51h.js} +1 -1
  61. package/dist/web/assets/design-settings-section-B_xOf51h.js.br +0 -0
  62. package/dist/web/assets/design-settings-section-B_xOf51h.js.gz +0 -0
  63. package/dist/web/assets/{design-skill-suggestion-DkT2E2IV.js → design-skill-suggestion-BbEDg15Z.js} +1 -1
  64. package/dist/web/assets/design-skill-suggestion-BbEDg15Z.js.br +0 -0
  65. package/dist/web/assets/design-skill-suggestion-BbEDg15Z.js.gz +0 -0
  66. package/dist/web/assets/{design-tab-BZb9eemI.js → design-tab-BQSYJ4cS.js} +6 -6
  67. package/dist/web/assets/design-tab-BQSYJ4cS.js.br +0 -0
  68. package/dist/web/assets/design-tab-BQSYJ4cS.js.gz +0 -0
  69. package/dist/web/assets/{designs-sidebar-panel-CF4tp6j-.js → designs-sidebar-panel-AiPaYfSU.js} +1 -1
  70. package/dist/web/assets/designs-sidebar-panel-AiPaYfSU.js.br +0 -0
  71. package/dist/web/assets/designs-sidebar-panel-AiPaYfSU.js.gz +0 -0
  72. package/dist/web/assets/{diff-viewer-BqEqwSw6.js → diff-viewer-BZ6glGK-.js} +1 -1
  73. package/dist/web/assets/diff-viewer-BZ6glGK-.js.br +0 -0
  74. package/dist/web/assets/diff-viewer-BZ6glGK-.js.gz +0 -0
  75. package/dist/web/assets/{edit-diff-preview-DD4HayXY.js → edit-diff-preview-D-FNLOU2.js} +1 -1
  76. package/dist/web/assets/edit-diff-preview-D-FNLOU2.js.br +0 -0
  77. package/dist/web/assets/edit-diff-preview-D-FNLOU2.js.gz +0 -0
  78. package/dist/web/assets/{editor-language-service-CkA5sEdV.js → editor-language-service-4sbsdqBV.js} +1 -1
  79. package/dist/web/assets/editor-language-service-4sbsdqBV.js.br +0 -0
  80. package/dist/web/assets/editor-language-service-4sbsdqBV.js.gz +0 -0
  81. package/dist/web/assets/explorer-body-DbgrG1fe.js +2 -0
  82. package/dist/web/assets/explorer-body-DbgrG1fe.js.br +0 -0
  83. package/dist/web/assets/explorer-body-DbgrG1fe.js.gz +0 -0
  84. package/dist/web/assets/{explorer-window-content-CRJ2eMZ4.js → explorer-window-content-BCjWvHpp.js} +1 -1
  85. package/dist/web/assets/explorer-window-content-BCjWvHpp.js.br +0 -0
  86. package/dist/web/assets/explorer-window-content-BCjWvHpp.js.gz +0 -0
  87. package/dist/web/assets/{extension-manager-section-DwQTdCII.js → extension-manager-section-DiMF4RnT.js} +1 -1
  88. package/dist/web/assets/extension-manager-section-DiMF4RnT.js.br +0 -0
  89. package/dist/web/assets/extension-manager-section-DiMF4RnT.js.gz +0 -0
  90. package/dist/web/assets/{extension-webview-CMq-VTLu.js → extension-webview-Coti6Vfw.js} +1 -1
  91. package/dist/web/assets/extension-webview-Coti6Vfw.js.br +0 -0
  92. package/dist/web/assets/extension-webview-Coti6Vfw.js.gz +0 -0
  93. package/dist/web/assets/file-store-Cm7-pHux.js +1 -0
  94. package/dist/web/assets/file-store-Cm7-pHux.js.br +0 -0
  95. package/dist/web/assets/file-store-Cm7-pHux.js.gz +0 -0
  96. package/dist/web/assets/{files-settings-section-B2lcyh-6.js → files-settings-section-ZTVwOwx0.js} +1 -1
  97. package/dist/web/assets/files-settings-section-ZTVwOwx0.js.br +0 -0
  98. package/dist/web/assets/files-settings-section-ZTVwOwx0.js.gz +0 -0
  99. package/dist/web/assets/{general-settings-section-Doepm2sH.js → general-settings-section-B7Hjx_fb.js} +1 -1
  100. package/dist/web/assets/general-settings-section-B7Hjx_fb.js.br +0 -0
  101. package/dist/web/assets/general-settings-section-B7Hjx_fb.js.gz +0 -0
  102. package/dist/web/assets/{git-log-panel-CJakNMDc.js → git-log-panel-CbcJ_YEH.js} +1 -1
  103. package/dist/web/assets/git-log-panel-CbcJ_YEH.js.br +0 -0
  104. package/dist/web/assets/git-log-panel-CbcJ_YEH.js.gz +0 -0
  105. package/dist/web/assets/{glide-data-grid-C_SeOhSq.js → glide-data-grid-BfByGSf1.js} +3 -3
  106. package/dist/web/assets/glide-data-grid-BfByGSf1.js.br +0 -0
  107. package/dist/web/assets/glide-data-grid-BfByGSf1.js.gz +0 -0
  108. package/dist/web/assets/group-chat-tab-DCo9OP3D.js +1 -0
  109. package/dist/web/assets/group-chat-tab-DCo9OP3D.js.br +0 -0
  110. package/dist/web/assets/group-chat-tab-DCo9OP3D.js.gz +0 -0
  111. package/dist/web/assets/{image-preview-C1Frlx22.js → image-preview-Cjlps7Pt.js} +1 -1
  112. package/dist/web/assets/index-CyDLBddf.js +52 -0
  113. package/dist/web/assets/index-CyDLBddf.js.br +0 -0
  114. package/dist/web/assets/index-CyDLBddf.js.gz +0 -0
  115. package/dist/web/assets/index-Dh5apC3I.css +1 -0
  116. package/dist/web/assets/index-Dh5apC3I.css.br +0 -0
  117. package/dist/web/assets/index-Dh5apC3I.css.gz +0 -0
  118. package/dist/web/assets/{jira-watcher-section-BmkIGQFV.js → jira-watcher-section-Dz_-sUC2.js} +1 -1
  119. package/dist/web/assets/keybindings-store-BTq5Z7XP.js +1 -0
  120. package/dist/web/assets/{keyboard-shortcuts-section-BgZe3QDr.js → keyboard-shortcuts-section-Ba5NYwlG.js} +1 -1
  121. package/dist/web/assets/keyboard-shortcuts-section-Ba5NYwlG.js.br +0 -0
  122. package/dist/web/assets/keyboard-shortcuts-section-Ba5NYwlG.js.gz +0 -0
  123. package/dist/web/assets/{language-servers-section-CCMzgOaE.js → language-servers-section-DJJ7SFXo.js} +2 -2
  124. package/dist/web/assets/language-servers-section-DJJ7SFXo.js.br +0 -0
  125. package/dist/web/assets/language-servers-section-DJJ7SFXo.js.gz +0 -0
  126. package/dist/web/assets/{lsp-client-B10suuaZ.js → lsp-client-Dr3vQ1bb.js} +1 -1
  127. package/dist/web/assets/lsp-client-Dr3vQ1bb.js.br +0 -0
  128. package/dist/web/assets/lsp-client-Dr3vQ1bb.js.gz +0 -0
  129. package/dist/web/assets/{markdown-renderer-AHHFS3jc.js → markdown-renderer-CmnqFfTU.js} +3 -3
  130. package/dist/web/assets/markdown-renderer-CmnqFfTU.js.br +0 -0
  131. package/dist/web/assets/markdown-renderer-CmnqFfTU.js.gz +0 -0
  132. package/dist/web/assets/markdown-renderer-LJdVMbGl.js +1 -0
  133. package/dist/web/assets/{mobile-explorer-sheet-CqCsLWSr.js → mobile-explorer-sheet-BiIhZtJ2.js} +1 -1
  134. package/dist/web/assets/mobile-explorer-sheet-BiIhZtJ2.js.br +0 -0
  135. package/dist/web/assets/mobile-explorer-sheet-BiIhZtJ2.js.gz +0 -0
  136. package/dist/web/assets/new-design-dialog-DI_hxvFV.js +1 -0
  137. package/dist/web/assets/new-design-dialog-DI_hxvFV.js.br +0 -0
  138. package/dist/web/assets/new-design-dialog-DI_hxvFV.js.gz +0 -0
  139. package/dist/web/assets/notification-store-9lBSplOV.js +1 -0
  140. package/dist/web/assets/{notifications-settings-section-CXgzH_bK.js → notifications-settings-section-0s-2OKRv.js} +1 -1
  141. package/dist/web/assets/notifications-settings-section-0s-2OKRv.js.br +0 -0
  142. package/dist/web/assets/notifications-settings-section-0s-2OKRv.js.gz +0 -0
  143. package/dist/web/assets/{overview-panel-BYB8mgRE.js → overview-panel-3htxfZS2.js} +1 -1
  144. package/dist/web/assets/overview-panel-3htxfZS2.js.br +0 -0
  145. package/dist/web/assets/overview-panel-3htxfZS2.js.gz +0 -0
  146. package/dist/web/assets/panel-store-B6PoCwBA.js +1 -0
  147. package/dist/web/assets/panel-store-B6PoCwBA.js.br +0 -0
  148. package/dist/web/assets/panel-store-B6PoCwBA.js.gz +0 -0
  149. package/dist/web/assets/{performance-panel-Dpuq3g0W.js → performance-panel-V8gpnnf7.js} +2 -2
  150. package/dist/web/assets/performance-panel-V8gpnnf7.js.br +0 -0
  151. package/dist/web/assets/performance-panel-V8gpnnf7.js.gz +0 -0
  152. package/dist/web/assets/{postgres-viewer-DW0Gkmow.js → postgres-viewer-sWS-ogTC.js} +1 -1
  153. package/dist/web/assets/postgres-viewer-sWS-ogTC.js.br +0 -0
  154. package/dist/web/assets/postgres-viewer-sWS-ogTC.js.gz +0 -0
  155. package/dist/web/assets/{ppmbot-settings-section-DFtlGoW6.js → ppmbot-settings-section-CT8vvlzk.js} +1 -1
  156. package/dist/web/assets/ppmbot-settings-section-CT8vvlzk.js.br +0 -0
  157. package/dist/web/assets/ppmbot-settings-section-CT8vvlzk.js.gz +0 -0
  158. package/dist/web/assets/{pptx-export-CNyG32Xr.js → pptx-export-Ugjv0hso.js} +4 -4
  159. package/dist/web/assets/pptx-export-Ugjv0hso.js.br +0 -0
  160. package/dist/web/assets/pptx-export-Ugjv0hso.js.gz +0 -0
  161. package/dist/web/assets/{problems-panel-V5zqpuXq.js → problems-panel-Gcy79pSS.js} +1 -1
  162. package/dist/web/assets/problems-panel-Gcy79pSS.js.br +0 -0
  163. package/dist/web/assets/problems-panel-Gcy79pSS.js.gz +0 -0
  164. package/dist/web/assets/{process-row-format-CiJJSf_V.js → process-row-format-u6y7lOee.js} +1 -1
  165. package/dist/web/assets/process-table-BUTIp9Oy.js +1 -0
  166. package/dist/web/assets/process-table-BUTIp9Oy.js.br +0 -0
  167. package/dist/web/assets/process-table-BUTIp9Oy.js.gz +0 -0
  168. package/dist/web/assets/{proxy-settings-section-B7AVlTfY.js → proxy-settings-section-Buv_8y54.js} +1 -1
  169. package/dist/web/assets/proxy-settings-section-Buv_8y54.js.br +0 -0
  170. package/dist/web/assets/proxy-settings-section-Buv_8y54.js.gz +0 -0
  171. package/dist/web/assets/{query-audit-section-gHmRfWKr.js → query-audit-section-N2DQtgJx.js} +1 -1
  172. package/dist/web/assets/query-audit-section-N2DQtgJx.js.br +0 -0
  173. package/dist/web/assets/query-audit-section-N2DQtgJx.js.gz +0 -0
  174. package/dist/web/assets/remote-desktop-clipboard-notice-CRvJqmaz.js +1 -0
  175. package/dist/web/assets/remote-desktop-clipboard-notice-CRvJqmaz.js.br +0 -0
  176. package/dist/web/assets/remote-desktop-clipboard-notice-CRvJqmaz.js.gz +0 -0
  177. package/dist/web/assets/remote-desktop-mobile-sheet-BoYINLbJ.js +2 -0
  178. package/dist/web/assets/remote-desktop-mobile-sheet-BoYINLbJ.js.br +0 -0
  179. package/dist/web/assets/remote-desktop-mobile-sheet-BoYINLbJ.js.gz +0 -0
  180. package/dist/web/assets/remote-desktop-mobile-view-DPdP0yYu.js +1 -0
  181. package/dist/web/assets/remote-desktop-mobile-view-DPdP0yYu.js.br +0 -0
  182. package/dist/web/assets/remote-desktop-mobile-view-DPdP0yYu.js.gz +0 -0
  183. package/dist/web/assets/remote-desktop-readiness-gate-BeQ_Jrp7.js +1 -0
  184. package/dist/web/assets/remote-desktop-readiness-gate-BeQ_Jrp7.js.br +0 -0
  185. package/dist/web/assets/remote-desktop-readiness-gate-BeQ_Jrp7.js.gz +0 -0
  186. package/dist/web/assets/remote-desktop-settings-section-Bs4sHS3l.js +1 -0
  187. package/dist/web/assets/remote-desktop-settings-section-Bs4sHS3l.js.br +0 -0
  188. package/dist/web/assets/remote-desktop-settings-section-Bs4sHS3l.js.gz +0 -0
  189. package/dist/web/assets/remote-desktop-window-content-DDr__JxQ.js +1 -0
  190. package/dist/web/assets/remote-desktop-window-content-DDr__JxQ.js.br +0 -0
  191. package/dist/web/assets/remote-desktop-window-content-DDr__JxQ.js.gz +0 -0
  192. package/dist/web/assets/{run-in-terminal-DKBcEGVu.js → run-in-terminal-DCBNtYHQ.js} +1 -1
  193. package/dist/web/assets/{schedules-settings-section-sYDAsBMO.js → schedules-settings-section-_cHX2qOa.js} +1 -1
  194. package/dist/web/assets/schedules-settings-section-_cHX2qOa.js.br +0 -0
  195. package/dist/web/assets/schedules-settings-section-_cHX2qOa.js.gz +0 -0
  196. package/dist/web/assets/{send-to-chat-CCdOrKc7.js → send-to-chat-dNqNV7Y4.js} +1 -1
  197. package/dist/web/assets/send-to-chat-dNqNV7Y4.js.br +0 -0
  198. package/dist/web/assets/send-to-chat-dNqNV7Y4.js.gz +0 -0
  199. package/dist/web/assets/{services-panel-CKa2aP1d.js → services-panel-Dm54cnW3.js} +1 -1
  200. package/dist/web/assets/services-panel-Dm54cnW3.js.br +0 -0
  201. package/dist/web/assets/services-panel-Dm54cnW3.js.gz +0 -0
  202. package/dist/web/assets/settings-body-DRRQNaDA.js +1 -0
  203. package/dist/web/assets/settings-body-DRRQNaDA.js.br +0 -0
  204. package/dist/web/assets/settings-body-DRRQNaDA.js.gz +0 -0
  205. package/dist/web/assets/settings-section-content-DRaHfvm8.js +1 -0
  206. package/dist/web/assets/settings-section-content-U3mIx6hF.js +2 -0
  207. package/dist/web/assets/settings-section-content-U3mIx6hF.js.br +0 -0
  208. package/dist/web/assets/settings-section-content-U3mIx6hF.js.gz +0 -0
  209. package/dist/web/assets/{settings-store-DfuiLq_9.js → settings-store-Cyuo8ceh.js} +2 -2
  210. package/dist/web/assets/settings-store-Cyuo8ceh.js.br +0 -0
  211. package/dist/web/assets/settings-store-Cyuo8ceh.js.gz +0 -0
  212. package/dist/web/assets/settings-tab-CB_BhHTK.js +1 -0
  213. package/dist/web/assets/settings-window-content-CgZIMNX8.js +1 -0
  214. package/dist/web/assets/{slash-command-picker-CLevA_Gf.js → slash-command-picker-CQlIqMW0.js} +1 -1
  215. package/dist/web/assets/slash-command-picker-CQlIqMW0.js.br +0 -0
  216. package/dist/web/assets/slash-command-picker-CQlIqMW0.js.gz +0 -0
  217. package/dist/web/assets/{sql-query-editor-Db8b0jEr.js → sql-query-editor--Z4bcqd8.js} +1 -1
  218. package/dist/web/assets/sql-query-editor--Z4bcqd8.js.br +0 -0
  219. package/dist/web/assets/sql-query-editor--Z4bcqd8.js.gz +0 -0
  220. package/dist/web/assets/{sqlite-viewer-VzYLMqkD.js → sqlite-viewer-CSmX3qaj.js} +1 -1
  221. package/dist/web/assets/sqlite-viewer-CSmX3qaj.js.br +0 -0
  222. package/dist/web/assets/sqlite-viewer-CSmX3qaj.js.gz +0 -0
  223. package/dist/web/assets/{system-monitor-body-BR60W_mG.js → system-monitor-body-LannpU6H.js} +2 -2
  224. package/dist/web/assets/system-monitor-body-LannpU6H.js.br +0 -0
  225. package/dist/web/assets/system-monitor-body-LannpU6H.js.gz +0 -0
  226. package/dist/web/assets/system-monitor-tab-BfVucwEF.js +1 -0
  227. package/dist/web/assets/{system-monitor-window-content-BLw1QBXo.js → system-monitor-window-content-kLNW9Ln3.js} +1 -1
  228. package/dist/web/assets/{tab-host-window-content-BL61SB5m.js → tab-host-window-content-II_FtOYM.js} +1 -1
  229. package/dist/web/assets/tab-host-window-content-II_FtOYM.js.br +0 -0
  230. package/dist/web/assets/tab-host-window-content-II_FtOYM.js.gz +0 -0
  231. package/dist/web/assets/{tab-store-CbPh8CAX.js → tab-store-DCpc0g0Q.js} +1 -1
  232. package/dist/web/assets/{terminal-tab-B4y6at4h.js → terminal-tab-CTe2polt.js} +1 -1
  233. package/dist/web/assets/terminal-tab-CTe2polt.js.br +0 -0
  234. package/dist/web/assets/terminal-tab-CTe2polt.js.gz +0 -0
  235. package/dist/web/assets/{tool-cards-K0r_yxI0.js → tool-cards-rxLXbBSm.js} +5 -5
  236. package/dist/web/assets/tool-cards-rxLXbBSm.js.br +0 -0
  237. package/dist/web/assets/tool-cards-rxLXbBSm.js.gz +0 -0
  238. package/dist/web/assets/use-h264-canvas-decoder-BfO9QrlH.js +1 -0
  239. package/dist/web/assets/use-h264-canvas-decoder-BfO9QrlH.js.br +0 -0
  240. package/dist/web/assets/use-h264-canvas-decoder-BfO9QrlH.js.gz +0 -0
  241. package/dist/web/assets/{use-inline-blame-BK23icsE.js → use-inline-blame-CxZ_QWzS.js} +3 -3
  242. package/dist/web/assets/use-inline-blame-CxZ_QWzS.js.br +0 -0
  243. package/dist/web/assets/use-inline-blame-CxZ_QWzS.js.gz +0 -0
  244. package/dist/web/assets/{use-lsp-CRdCPCKW.js → use-lsp-gCNE5A9K.js} +1 -1
  245. package/dist/web/assets/use-lsp-gCNE5A9K.js.br +0 -0
  246. package/dist/web/assets/use-lsp-gCNE5A9K.js.gz +0 -0
  247. package/dist/web/assets/{use-monaco-theme-CpXT2uh3.js → use-monaco-theme-DvNGcCvz.js} +1 -1
  248. package/dist/web/assets/{use-process-kill-BWYX03KF.js → use-process-kill-Cbiu1mbY.js} +1 -1
  249. package/dist/web/assets/use-process-kill-Cbiu1mbY.js.br +0 -0
  250. package/dist/web/assets/use-process-kill-Cbiu1mbY.js.gz +0 -0
  251. package/dist/web/assets/{use-websocket-CiWQKhHr.js → use-websocket-AC7EG1LK.js} +1 -1
  252. package/dist/web/assets/video-preview-C2Y61WUE.js +1 -0
  253. package/dist/web/assets/video-preview-C2Y61WUE.js.br +0 -0
  254. package/dist/web/assets/video-preview-C2Y61WUE.js.gz +0 -0
  255. package/dist/web/assets/{voice-settings-section-Ceihggqf.js → voice-settings-section-DCXZeLYI.js} +1 -1
  256. package/dist/web/assets/voice-settings-section-DCXZeLYI.js.br +0 -0
  257. package/dist/web/assets/voice-settings-section-DCXZeLYI.js.gz +0 -0
  258. package/dist/web/assets/{window-pip-registry-mDgwpAwP.js → window-pip-registry-9Y9LQQYS.js} +1 -1
  259. package/dist/web/index.html +50 -7
  260. package/dist/web/index.html.br +0 -0
  261. package/dist/web/index.html.gz +0 -0
  262. package/dist/web/sw.js +1 -1
  263. package/dist/web/sw.js.br +0 -0
  264. package/dist/web/sw.js.gz +0 -0
  265. package/docs/architecture/android-emulator-phase0-report-260921.md +310 -0
  266. package/docs/architecture/android-emulator-phase2-report-260921.md +163 -0
  267. package/docs/architecture/android-emulator-phase3-report-260921.md +203 -0
  268. package/docs/architecture/android-emulator-phase4-report-260921.md +191 -0
  269. package/docs/architecture/android-emulator-plan-260920.md +376 -0
  270. package/docs/architecture/plugins-and-tracing.md +48 -7
  271. package/package.json +4 -2
  272. package/scripts/gen-android-proto.ts +37 -0
  273. package/scripts/release.sh +3 -1
  274. package/src/cli/commands/chat-cmd.ts +2 -2
  275. package/src/providers/claude-agent-sdk-query-options.ts +28 -0
  276. package/src/providers/claude-agent-sdk.ts +153 -71
  277. package/src/providers/claude-warm-spare.ts +176 -0
  278. package/src/providers/provider.interface.ts +1 -0
  279. package/src/server/index.ts +53 -0
  280. package/src/server/middleware/gzip-json.ts +13 -1
  281. package/src/server/routes/android.ts +616 -0
  282. package/src/server/routes/chat.ts +31 -0
  283. package/src/server/routes/files.ts +52 -5
  284. package/src/server/routes/remote-desktop.ts +95 -2
  285. package/src/server/routes/trace.ts +66 -0
  286. package/src/server/ws/android.ts +101 -0
  287. package/src/server/ws/chat.ts +12 -17
  288. package/src/server/ws/global.ts +13 -1
  289. package/src/server/ws/remote-desktop.ts +6 -1
  290. package/src/services/android/android-apk.ts +387 -0
  291. package/src/services/android/android-capabilities.ts +154 -0
  292. package/src/services/android/android-flag.ts +12 -0
  293. package/src/services/android/android-grpc.ts +124 -0
  294. package/src/services/android/android-input.ts +129 -0
  295. package/src/services/android/android-logcat.ts +169 -0
  296. package/src/services/android/android-nonce.ts +50 -0
  297. package/src/services/android/android-operations.ts +124 -0
  298. package/src/services/android/android-screenshot.ts +53 -0
  299. package/src/services/android/android-session.ts +510 -0
  300. package/src/services/android/android-video.ts +495 -0
  301. package/src/services/android/avd-list.ts +131 -0
  302. package/src/services/android/avd-manager.ts +397 -0
  303. package/src/services/android/device-registry.ts +223 -0
  304. package/src/services/android/emulator-discovery.ts +160 -0
  305. package/src/services/android/emulator-launcher.ts +290 -0
  306. package/src/services/android/proto/emulator-controller-descriptor.ts +10 -0
  307. package/src/services/android/proto/emulator_controller.proto +1791 -0
  308. package/src/services/android/sdk-discovery.ts +178 -0
  309. package/src/services/android/system-images.ts +124 -0
  310. package/src/services/chat.service.ts +81 -4
  311. package/src/services/config.service.ts +1 -1
  312. package/src/services/file-filter.service.ts +2 -61
  313. package/src/services/file-glob.ts +67 -0
  314. package/src/services/file-index/file-index-runner.ts +169 -0
  315. package/src/services/file-index/file-index-worker.ts +73 -0
  316. package/src/services/file-index/index-search.ts +152 -0
  317. package/src/services/file-index/index-walk.ts +109 -0
  318. package/src/services/file-list-index.service.ts +189 -70
  319. package/src/services/file-watcher/linux-inotify.ts +238 -0
  320. package/src/services/file-watcher/watch-tree.ts +37 -6
  321. package/src/services/file-watcher.service.ts +40 -8
  322. package/src/services/file.service.ts +17 -6
  323. package/src/services/group-chat/group-chat.service.ts +1 -1
  324. package/src/services/jira-debug-session.service.ts +1 -1
  325. package/src/services/ppmbot/ppmbot-delegation.ts +1 -1
  326. package/src/services/ppmbot/ppmbot-service.ts +1 -1
  327. package/src/services/project-content-search.service.ts +45 -11
  328. package/src/services/proxy-agent-turn.ts +7 -3
  329. package/src/services/remote-desktop/mediamtx-catalog.ts +89 -0
  330. package/src/services/remote-desktop/mediamtx-config.ts +91 -0
  331. package/src/services/remote-desktop/mediamtx-install.service.ts +191 -0
  332. package/src/services/remote-desktop/mediamtx-paths.ts +51 -0
  333. package/src/services/remote-desktop/mediamtx-process.ts +152 -0
  334. package/src/services/remote-desktop/remote-desktop-capture-wayland.ts +312 -0
  335. package/src/services/remote-desktop/remote-desktop-capture.ts +60 -4
  336. package/src/services/remote-desktop/remote-desktop-portal.ts +529 -0
  337. package/src/services/remote-desktop/remote-desktop-requirements.ts +56 -20
  338. package/src/services/remote-desktop/remote-desktop-session.ts +77 -3
  339. package/src/services/remote-desktop/remote-desktop-whep-registry.ts +36 -0
  340. package/src/services/scheduler-runner.ts +2 -3
  341. package/src/services/session-trace/browser-trace-ingest.ts +131 -0
  342. package/src/services/session-trace/session-trace-cleanup.ts +65 -0
  343. package/src/services/session-trace/session-trace-db.ts +89 -0
  344. package/src/services/session-trace/session-trace-store.ts +132 -0
  345. package/src/services/session-trace/trace-coalescer.ts +86 -0
  346. package/src/services/session-trace/trace-recorder.ts +304 -0
  347. package/src/services/session-trace/trace-writer.ts +83 -0
  348. package/src/shared/android-avd.ts +53 -0
  349. package/src/shared/android-protocol.ts +184 -0
  350. package/src/shared/file-index-limits.ts +17 -0
  351. package/src/shared/session-trace.ts +61 -0
  352. package/src/types/chat.ts +17 -0
  353. package/src/types/config.ts +27 -0
  354. package/src/types/project.ts +9 -0
  355. package/src/web/app.tsx +5 -0
  356. package/src/web/components/android/android-apk-upload.ts +75 -0
  357. package/src/web/components/android/android-avd-create-dialog.tsx +305 -0
  358. package/src/web/components/android/android-avd-destroy-dialog.tsx +151 -0
  359. package/src/web/components/android/android-clipboard-panel.tsx +94 -0
  360. package/src/web/components/android/android-controls.tsx +128 -0
  361. package/src/web/components/android/android-coords.ts +99 -0
  362. package/src/web/components/android/android-device-list.tsx +329 -0
  363. package/src/web/components/android/android-install-panel.tsx +280 -0
  364. package/src/web/components/android/android-log-filter.ts +75 -0
  365. package/src/web/components/android/android-logcat-panel.tsx +214 -0
  366. package/src/web/components/android/android-screenshot-download.ts +56 -0
  367. package/src/web/components/android/android-tab.tsx +66 -0
  368. package/src/web/components/android/android-tools-drawer.tsx +76 -0
  369. package/src/web/components/android/android-viewer.tsx +200 -0
  370. package/src/web/components/android/android-ws-url.ts +21 -0
  371. package/src/web/components/android/open-android.ts +21 -0
  372. package/src/web/components/android/use-android-available.ts +29 -0
  373. package/src/web/components/android/use-android-keyboard.ts +111 -0
  374. package/src/web/components/android/use-android-session.ts +277 -0
  375. package/src/web/components/android/use-android-touch.ts +123 -0
  376. package/src/web/components/chat/chat-tab.tsx +28 -2
  377. package/src/web/components/chat/message-input.tsx +3 -5
  378. package/src/web/components/editor/compare-picker.tsx +12 -2
  379. package/src/web/components/explorer/file-tree.tsx +6 -7
  380. package/src/web/components/explorer/use-tree-row-dnd.ts +0 -2
  381. package/src/web/components/floating-window/window-content-registry.ts +8 -8
  382. package/src/web/components/layout/command-palette.tsx +31 -9
  383. package/src/web/components/layout/mobile-drawer.tsx +8 -1
  384. package/src/web/components/layout/nav-section-rail.tsx +8 -1
  385. package/src/web/components/layout/reparenting-tab.tsx +2 -2
  386. package/src/web/components/layout/tab-content.tsx +5 -0
  387. package/src/web/components/layout/tab-pool.tsx +31 -20
  388. package/src/web/components/remote-desktop/use-h264-canvas-decoder.ts +23 -1
  389. package/src/web/components/remote-desktop/use-remote-desktop-connection.ts +64 -6
  390. package/src/web/components/remote-desktop/use-webrtc-canvas-video.ts +255 -0
  391. package/src/web/components/root-error-boundary.tsx +5 -0
  392. package/src/web/components/settings/remote-desktop-settings-section.tsx +176 -0
  393. package/src/web/components/settings/settings-categories.ts +3 -0
  394. package/src/web/components/settings/settings-section-content.tsx +25 -18
  395. package/src/web/hooks/use-chat-prewarm.ts +53 -0
  396. package/src/web/hooks/use-chat.ts +18 -4
  397. package/src/web/hooks/use-file-index-invalidation.ts +7 -5
  398. package/src/web/hooks/use-global-events.ts +12 -1
  399. package/src/web/hooks/use-remote-file-search.ts +39 -0
  400. package/src/web/hooks/use-ui-preload.ts +38 -0
  401. package/src/web/index.html +43 -0
  402. package/src/web/lib/chunk-recovery.ts +5 -1
  403. package/src/web/lib/device-id.ts +39 -0
  404. package/src/web/lib/feature-badges.ts +2 -0
  405. package/src/web/lib/lazy-with-preload.tsx +43 -0
  406. package/src/web/lib/preload-ui.ts +60 -0
  407. package/src/web/lib/score-file-search.ts +27 -14
  408. package/src/web/lib/tab-type-icons.ts +2 -1
  409. package/src/web/lib/trace-boot.ts +8 -0
  410. package/src/web/lib/trace-client.ts +516 -0
  411. package/src/web/main.tsx +12 -0
  412. package/src/web/stores/file-store.ts +124 -18
  413. package/src/web/stores/panel-utils.ts +3 -0
  414. package/src/web/stores/tab-store.ts +2 -1
  415. package/src/web/stores/window-panel-persistence.ts +3 -0
  416. package/vite.config.ts +3 -0
  417. package/dist/web/assets/accounts-settings-section-DEIm-W-D.js +0 -1
  418. package/dist/web/assets/accounts-settings-section-DEIm-W-D.js.br +0 -0
  419. package/dist/web/assets/accounts-settings-section-DEIm-W-D.js.gz +0 -0
  420. package/dist/web/assets/agent-session-sheet-CBMsnl_p.js.br +0 -0
  421. package/dist/web/assets/agent-session-sheet-CBMsnl_p.js.gz +0 -0
  422. package/dist/web/assets/agent-session-window-content-D9C9FA7E.js.br +0 -0
  423. package/dist/web/assets/agent-session-window-content-D9C9FA7E.js.gz +0 -0
  424. package/dist/web/assets/ai-resource-editor-DhUVgDEG.js.br +0 -0
  425. package/dist/web/assets/ai-resource-editor-DhUVgDEG.js.gz +0 -0
  426. package/dist/web/assets/ai-settings-section-CETpSpYd.js.br +0 -0
  427. package/dist/web/assets/ai-settings-section-CETpSpYd.js.gz +0 -0
  428. package/dist/web/assets/appearance-settings-section-BJUiLUb7.js.br +0 -0
  429. package/dist/web/assets/appearance-settings-section-BJUiLUb7.js.gz +0 -0
  430. package/dist/web/assets/apps-panel-1VElRgV3.js.br +0 -0
  431. package/dist/web/assets/apps-panel-1VElRgV3.js.gz +0 -0
  432. package/dist/web/assets/audio-preview-DYkyHu5X.js.br +0 -0
  433. package/dist/web/assets/audio-preview-DYkyHu5X.js.gz +0 -0
  434. package/dist/web/assets/branch-review-tab-DhTMSpSU.js +0 -1
  435. package/dist/web/assets/branch-review-tab-DhTMSpSU.js.br +0 -0
  436. package/dist/web/assets/branch-review-tab-DhTMSpSU.js.gz +0 -0
  437. package/dist/web/assets/chat-tab-Cg3BKugo.js +0 -15
  438. package/dist/web/assets/chat-tab-Cg3BKugo.js.br +0 -0
  439. package/dist/web/assets/chat-tab-Cg3BKugo.js.gz +0 -0
  440. package/dist/web/assets/chat-tab-tbUDK__Y.js +0 -1
  441. package/dist/web/assets/chat-tab-tbUDK__Y.js.br +0 -0
  442. package/dist/web/assets/chat-tab-tbUDK__Y.js.gz +0 -0
  443. package/dist/web/assets/code-editor-6ZiQehbr.js +0 -7
  444. package/dist/web/assets/code-editor-6ZiQehbr.js.br +0 -0
  445. package/dist/web/assets/code-editor-6ZiQehbr.js.gz +0 -0
  446. package/dist/web/assets/codex-reset-credit-button-Fr45FUxi.js.br +0 -0
  447. package/dist/web/assets/codex-reset-credit-button-Fr45FUxi.js.gz +0 -0
  448. package/dist/web/assets/conflict-editor-B-wdjSXI.js.br +0 -0
  449. package/dist/web/assets/conflict-editor-B-wdjSXI.js.gz +0 -0
  450. package/dist/web/assets/csv-preview-B6RodObB.js.br +0 -0
  451. package/dist/web/assets/csv-preview-B6RodObB.js.gz +0 -0
  452. package/dist/web/assets/database-viewer-CdPBUQPY.js.br +0 -0
  453. package/dist/web/assets/database-viewer-CdPBUQPY.js.gz +0 -0
  454. package/dist/web/assets/design-settings-section-BpCpaBwL.js.br +0 -0
  455. package/dist/web/assets/design-settings-section-BpCpaBwL.js.gz +0 -0
  456. package/dist/web/assets/design-skill-suggestion-DkT2E2IV.js.br +0 -0
  457. package/dist/web/assets/design-skill-suggestion-DkT2E2IV.js.gz +0 -0
  458. package/dist/web/assets/design-tab-BZb9eemI.js.br +0 -0
  459. package/dist/web/assets/design-tab-BZb9eemI.js.gz +0 -0
  460. package/dist/web/assets/designs-sidebar-panel-CF4tp6j-.js.br +0 -0
  461. package/dist/web/assets/designs-sidebar-panel-CF4tp6j-.js.gz +0 -0
  462. package/dist/web/assets/diff-viewer-BqEqwSw6.js.br +0 -0
  463. package/dist/web/assets/diff-viewer-BqEqwSw6.js.gz +0 -0
  464. package/dist/web/assets/edit-diff-preview-DD4HayXY.js.br +0 -0
  465. package/dist/web/assets/edit-diff-preview-DD4HayXY.js.gz +0 -0
  466. package/dist/web/assets/editor-language-service-CkA5sEdV.js.br +0 -0
  467. package/dist/web/assets/editor-language-service-CkA5sEdV.js.gz +0 -0
  468. package/dist/web/assets/explorer-body-Ds7oKuz7.js +0 -2
  469. package/dist/web/assets/explorer-body-Ds7oKuz7.js.br +0 -0
  470. package/dist/web/assets/explorer-body-Ds7oKuz7.js.gz +0 -0
  471. package/dist/web/assets/explorer-window-content-CRJ2eMZ4.js.br +0 -0
  472. package/dist/web/assets/explorer-window-content-CRJ2eMZ4.js.gz +0 -0
  473. package/dist/web/assets/extension-manager-section-DwQTdCII.js.br +0 -0
  474. package/dist/web/assets/extension-manager-section-DwQTdCII.js.gz +0 -0
  475. package/dist/web/assets/extension-webview-CMq-VTLu.js.br +0 -0
  476. package/dist/web/assets/extension-webview-CMq-VTLu.js.gz +0 -0
  477. package/dist/web/assets/file-store-DY1_0nzY.js +0 -1
  478. package/dist/web/assets/file-store-DY1_0nzY.js.br +0 -0
  479. package/dist/web/assets/file-store-DY1_0nzY.js.gz +0 -0
  480. package/dist/web/assets/files-settings-section-B2lcyh-6.js.br +0 -0
  481. package/dist/web/assets/files-settings-section-B2lcyh-6.js.gz +0 -0
  482. package/dist/web/assets/general-settings-section-Doepm2sH.js.br +0 -0
  483. package/dist/web/assets/general-settings-section-Doepm2sH.js.gz +0 -0
  484. package/dist/web/assets/git-log-panel-CJakNMDc.js.br +0 -0
  485. package/dist/web/assets/git-log-panel-CJakNMDc.js.gz +0 -0
  486. package/dist/web/assets/glide-data-grid-C_SeOhSq.js.br +0 -0
  487. package/dist/web/assets/glide-data-grid-C_SeOhSq.js.gz +0 -0
  488. package/dist/web/assets/group-chat-tab--by1xCQW.js +0 -1
  489. package/dist/web/assets/group-chat-tab--by1xCQW.js.br +0 -0
  490. package/dist/web/assets/group-chat-tab--by1xCQW.js.gz +0 -0
  491. package/dist/web/assets/index-DhWjx5iR.js +0 -52
  492. package/dist/web/assets/index-DhWjx5iR.js.br +0 -0
  493. package/dist/web/assets/index-DhWjx5iR.js.gz +0 -0
  494. package/dist/web/assets/index-oIvYSKTT.css +0 -1
  495. package/dist/web/assets/index-oIvYSKTT.css.br +0 -0
  496. package/dist/web/assets/index-oIvYSKTT.css.gz +0 -0
  497. package/dist/web/assets/keybindings-store-30LYZU0l.js +0 -1
  498. package/dist/web/assets/keyboard-shortcuts-section-BgZe3QDr.js.br +0 -0
  499. package/dist/web/assets/keyboard-shortcuts-section-BgZe3QDr.js.gz +0 -0
  500. package/dist/web/assets/language-servers-section-CCMzgOaE.js.br +0 -0
  501. package/dist/web/assets/language-servers-section-CCMzgOaE.js.gz +0 -0
  502. package/dist/web/assets/lsp-client-B10suuaZ.js.br +0 -0
  503. package/dist/web/assets/lsp-client-B10suuaZ.js.gz +0 -0
  504. package/dist/web/assets/markdown-renderer-AHHFS3jc.js.br +0 -0
  505. package/dist/web/assets/markdown-renderer-AHHFS3jc.js.gz +0 -0
  506. package/dist/web/assets/markdown-renderer-DxnXtBoj.js +0 -1
  507. package/dist/web/assets/mobile-explorer-sheet-CqCsLWSr.js.br +0 -0
  508. package/dist/web/assets/mobile-explorer-sheet-CqCsLWSr.js.gz +0 -0
  509. package/dist/web/assets/new-design-dialog-B9tgnQXq.js +0 -1
  510. package/dist/web/assets/new-design-dialog-B9tgnQXq.js.br +0 -0
  511. package/dist/web/assets/new-design-dialog-B9tgnQXq.js.gz +0 -0
  512. package/dist/web/assets/notification-store-HUVzO_bp.js +0 -1
  513. package/dist/web/assets/notifications-settings-section-CXgzH_bK.js.br +0 -0
  514. package/dist/web/assets/notifications-settings-section-CXgzH_bK.js.gz +0 -0
  515. package/dist/web/assets/overview-panel-BYB8mgRE.js.br +0 -0
  516. package/dist/web/assets/overview-panel-BYB8mgRE.js.gz +0 -0
  517. package/dist/web/assets/panel-store-CWHPmHwj.js +0 -1
  518. package/dist/web/assets/panel-store-CWHPmHwj.js.br +0 -0
  519. package/dist/web/assets/panel-store-CWHPmHwj.js.gz +0 -0
  520. package/dist/web/assets/performance-panel-Dpuq3g0W.js.br +0 -0
  521. package/dist/web/assets/performance-panel-Dpuq3g0W.js.gz +0 -0
  522. package/dist/web/assets/postgres-viewer-DW0Gkmow.js.br +0 -0
  523. package/dist/web/assets/postgres-viewer-DW0Gkmow.js.gz +0 -0
  524. package/dist/web/assets/ppmbot-settings-section-DFtlGoW6.js.br +0 -0
  525. package/dist/web/assets/ppmbot-settings-section-DFtlGoW6.js.gz +0 -0
  526. package/dist/web/assets/pptx-export-CNyG32Xr.js.br +0 -0
  527. package/dist/web/assets/pptx-export-CNyG32Xr.js.gz +0 -0
  528. package/dist/web/assets/problems-panel-V5zqpuXq.js.br +0 -0
  529. package/dist/web/assets/problems-panel-V5zqpuXq.js.gz +0 -0
  530. package/dist/web/assets/process-table-DAj46HIl.js +0 -1
  531. package/dist/web/assets/process-table-DAj46HIl.js.br +0 -0
  532. package/dist/web/assets/process-table-DAj46HIl.js.gz +0 -0
  533. package/dist/web/assets/proxy-settings-section-B7AVlTfY.js.br +0 -0
  534. package/dist/web/assets/proxy-settings-section-B7AVlTfY.js.gz +0 -0
  535. package/dist/web/assets/query-audit-section-gHmRfWKr.js.br +0 -0
  536. package/dist/web/assets/query-audit-section-gHmRfWKr.js.gz +0 -0
  537. package/dist/web/assets/remote-desktop-clipboard-notice-Cgoznh8L.js +0 -1
  538. package/dist/web/assets/remote-desktop-clipboard-notice-Cgoznh8L.js.br +0 -0
  539. package/dist/web/assets/remote-desktop-clipboard-notice-Cgoznh8L.js.gz +0 -0
  540. package/dist/web/assets/remote-desktop-mobile-sheet-X12QbPhO.js +0 -2
  541. package/dist/web/assets/remote-desktop-mobile-sheet-X12QbPhO.js.br +0 -0
  542. package/dist/web/assets/remote-desktop-mobile-sheet-X12QbPhO.js.gz +0 -0
  543. package/dist/web/assets/remote-desktop-mobile-view-DsSHawSK.js +0 -1
  544. package/dist/web/assets/remote-desktop-mobile-view-DsSHawSK.js.br +0 -0
  545. package/dist/web/assets/remote-desktop-mobile-view-DsSHawSK.js.gz +0 -0
  546. package/dist/web/assets/remote-desktop-readiness-gate-CHiQEd9Q.js +0 -1
  547. package/dist/web/assets/remote-desktop-readiness-gate-CHiQEd9Q.js.br +0 -0
  548. package/dist/web/assets/remote-desktop-readiness-gate-CHiQEd9Q.js.gz +0 -0
  549. package/dist/web/assets/remote-desktop-window-content-BnznIby3.js +0 -1
  550. package/dist/web/assets/remote-desktop-window-content-BnznIby3.js.br +0 -0
  551. package/dist/web/assets/remote-desktop-window-content-BnznIby3.js.gz +0 -0
  552. package/dist/web/assets/schedules-settings-section-sYDAsBMO.js.br +0 -0
  553. package/dist/web/assets/schedules-settings-section-sYDAsBMO.js.gz +0 -0
  554. package/dist/web/assets/send-to-chat-CCdOrKc7.js.br +0 -0
  555. package/dist/web/assets/send-to-chat-CCdOrKc7.js.gz +0 -0
  556. package/dist/web/assets/services-panel-CKa2aP1d.js.br +0 -0
  557. package/dist/web/assets/services-panel-CKa2aP1d.js.gz +0 -0
  558. package/dist/web/assets/settings-body-CEIF8lkl.js +0 -2
  559. package/dist/web/assets/settings-body-CEIF8lkl.js.br +0 -0
  560. package/dist/web/assets/settings-body-CEIF8lkl.js.gz +0 -0
  561. package/dist/web/assets/settings-store-DfuiLq_9.js.br +0 -0
  562. package/dist/web/assets/settings-store-DfuiLq_9.js.gz +0 -0
  563. package/dist/web/assets/settings-tab-Du5AUPni.js +0 -1
  564. package/dist/web/assets/settings-window-content-BTflfLez.js +0 -1
  565. package/dist/web/assets/slash-command-picker-CLevA_Gf.js.br +0 -0
  566. package/dist/web/assets/slash-command-picker-CLevA_Gf.js.gz +0 -0
  567. package/dist/web/assets/sql-query-editor-Db8b0jEr.js.br +0 -0
  568. package/dist/web/assets/sql-query-editor-Db8b0jEr.js.gz +0 -0
  569. package/dist/web/assets/sqlite-viewer-VzYLMqkD.js.br +0 -0
  570. package/dist/web/assets/sqlite-viewer-VzYLMqkD.js.gz +0 -0
  571. package/dist/web/assets/system-monitor-body-BR60W_mG.js.br +0 -0
  572. package/dist/web/assets/system-monitor-body-BR60W_mG.js.gz +0 -0
  573. package/dist/web/assets/system-monitor-tab-BYpantE2.js +0 -1
  574. package/dist/web/assets/tab-host-window-content-BL61SB5m.js.br +0 -0
  575. package/dist/web/assets/tab-host-window-content-BL61SB5m.js.gz +0 -0
  576. package/dist/web/assets/terminal-tab-B4y6at4h.js.br +0 -0
  577. package/dist/web/assets/terminal-tab-B4y6at4h.js.gz +0 -0
  578. package/dist/web/assets/tool-cards-K0r_yxI0.js.br +0 -0
  579. package/dist/web/assets/tool-cards-K0r_yxI0.js.gz +0 -0
  580. package/dist/web/assets/use-inline-blame-BK23icsE.js.br +0 -0
  581. package/dist/web/assets/use-inline-blame-BK23icsE.js.gz +0 -0
  582. package/dist/web/assets/use-lsp-CRdCPCKW.js.br +0 -0
  583. package/dist/web/assets/use-lsp-CRdCPCKW.js.gz +0 -0
  584. package/dist/web/assets/use-process-kill-BWYX03KF.js.br +0 -0
  585. package/dist/web/assets/use-process-kill-BWYX03KF.js.gz +0 -0
  586. package/dist/web/assets/video-preview-DugVkMB7.js +0 -1
  587. package/dist/web/assets/video-preview-DugVkMB7.js.br +0 -0
  588. package/dist/web/assets/video-preview-DugVkMB7.js.gz +0 -0
  589. package/dist/web/assets/voice-settings-section-Ceihggqf.js.br +0 -0
  590. package/dist/web/assets/voice-settings-section-Ceihggqf.js.gz +0 -0
  591. /package/dist/web/assets/{__vite-browser-external-B_XMEguz.js → __vite-browser-external-C5mXParC.js} +0 -0
  592. /package/dist/web/assets/{agent-session-sheet-store-DO682WSg.js → agent-session-sheet-store-_OeezCXB.js} +0 -0
  593. /package/dist/web/assets/{api-settings-CpO8SPk9.js → api-settings-Cc_CpfmC.js} +0 -0
  594. /package/dist/web/assets/{api-settings-CpO8SPk9.js.br → api-settings-Cc_CpfmC.js.br} +0 -0
  595. /package/dist/web/assets/{api-settings-CpO8SPk9.js.gz → api-settings-Cc_CpfmC.js.gz} +0 -0
  596. /package/dist/web/assets/{api-themes-CJipDH_l.js → api-themes-BtMBSjuM.js} +0 -0
  597. /package/dist/web/assets/{can-open-in-ppm-Bw_xiOXX.js → can-open-in-ppm-DeE2-kd5.js} +0 -0
  598. /package/dist/web/assets/{can-open-in-ppm-Bw_xiOXX.js.br → can-open-in-ppm-DeE2-kd5.js.br} +0 -0
  599. /package/dist/web/assets/{can-open-in-ppm-Bw_xiOXX.js.gz → can-open-in-ppm-DeE2-kd5.js.gz} +0 -0
  600. /package/dist/web/assets/{csv-parser-DcRWxcNV.js → csv-parser-CoxPdyLw.js} +0 -0
  601. /package/dist/web/assets/{csv-parser-DcRWxcNV.js.br → csv-parser-CoxPdyLw.js.br} +0 -0
  602. /package/dist/web/assets/{csv-parser-DcRWxcNV.js.gz → csv-parser-CoxPdyLw.js.gz} +0 -0
  603. /package/dist/web/assets/{design-export-client-CHffN3DM.js → design-export-client-BfdZB7xK.js} +0 -0
  604. /package/dist/web/assets/{design-export-client-CHffN3DM.js.br → design-export-client-BfdZB7xK.js.br} +0 -0
  605. /package/dist/web/assets/{design-export-client-CHffN3DM.js.gz → design-export-client-BfdZB7xK.js.gz} +0 -0
  606. /package/dist/web/assets/{design-responsive-dialog-DSCpv6O1.js → design-responsive-dialog-BiYy3ibS.js} +0 -0
  607. /package/dist/web/assets/{design-responsive-dialog-DSCpv6O1.js.br → design-responsive-dialog-BiYy3ibS.js.br} +0 -0
  608. /package/dist/web/assets/{design-responsive-dialog-DSCpv6O1.js.gz → design-responsive-dialog-BiYy3ibS.js.gz} +0 -0
  609. /package/dist/web/assets/{design-system-status-label-B_0I_3a8.js → design-system-status-label-ekiLyjKn.js} +0 -0
  610. /package/dist/web/assets/{docx-preview-B3eJyY2y.js → docx-preview-Dt6RQpfA.js} +0 -0
  611. /package/dist/web/assets/{docx-preview-B3eJyY2y.js.br → docx-preview-Dt6RQpfA.js.br} +0 -0
  612. /package/dist/web/assets/{docx-preview-B3eJyY2y.js.gz → docx-preview-Dt6RQpfA.js.gz} +0 -0
  613. /package/dist/web/assets/{editor-font-BNwo7zo5.js → editor-font-CBszn6vt.js} +0 -0
  614. /package/dist/web/assets/{format-bytes-CRZfxO2J.js → format-bytes-FQmpZ25A.js} +0 -0
  615. /package/dist/web/assets/{image-gallery-DC3gRAqk.js → image-gallery-B16hO6Ab.js} +0 -0
  616. /package/dist/web/assets/{image-preview-geometry-DN3DZQp0.js → image-preview-geometry-D4Ihz4UV.js} +0 -0
  617. /package/dist/web/assets/{lib-B9k5tn9s.js → lib-6APiJkU3.js} +0 -0
  618. /package/dist/web/assets/{lib-B9k5tn9s.js.br → lib-6APiJkU3.js.br} +0 -0
  619. /package/dist/web/assets/{lib-B9k5tn9s.js.gz → lib-6APiJkU3.js.gz} +0 -0
  620. /package/dist/web/assets/{libesm-5f7T4AHA.js → libesm-5wREk7JN.js} +0 -0
  621. /package/dist/web/assets/{libesm-5f7T4AHA.js.br → libesm-5wREk7JN.js.br} +0 -0
  622. /package/dist/web/assets/{libesm-5f7T4AHA.js.gz → libesm-5wREk7JN.js.gz} +0 -0
  623. /package/dist/web/assets/{media-url-CCGOIxzS.js → media-url-CrT-qulZ.js} +0 -0
  624. /package/dist/web/assets/{metric-chart-canvas-CjFbZuhk.js → metric-chart-canvas-D7f-LQ8x.js} +0 -0
  625. /package/dist/web/assets/{metric-chart-canvas-CjFbZuhk.js.br → metric-chart-canvas-D7f-LQ8x.js.br} +0 -0
  626. /package/dist/web/assets/{metric-chart-canvas-CjFbZuhk.js.gz → metric-chart-canvas-D7f-LQ8x.js.gz} +0 -0
  627. /package/dist/web/assets/{monaco-dotenv-language-Bwq3gQsi.js → monaco-dotenv-language-DbSss7YR.js} +0 -0
  628. /package/dist/web/assets/{onboarding-entry-CuH1Kqwu.js → onboarding-entry-CGFdGLLP.js} +0 -0
  629. /package/dist/web/assets/{onboarding-entry-CuH1Kqwu.js.br → onboarding-entry-CGFdGLLP.js.br} +0 -0
  630. /package/dist/web/assets/{onboarding-entry-CuH1Kqwu.js.gz → onboarding-entry-CGFdGLLP.js.gz} +0 -0
  631. /package/dist/web/assets/{pdf-preview-X8bdpfE9.js → pdf-preview-B8A4T-ES.js} +0 -0
  632. /package/dist/web/assets/{pdf-preview-X8bdpfE9.js.br → pdf-preview-B8A4T-ES.js.br} +0 -0
  633. /package/dist/web/assets/{pdf-preview-X8bdpfE9.js.gz → pdf-preview-B8A4T-ES.js.gz} +0 -0
  634. /package/dist/web/assets/{separator-DMu7VFQa.js → separator-dHj3Km--.js} +0 -0
  635. /package/dist/web/assets/{sortable-header-CipIP7aH.js → sortable-header-BOa8o7NJ.js} +0 -0
  636. /package/dist/web/assets/{sortable-header-CipIP7aH.js.br → sortable-header-BOa8o7NJ.js.br} +0 -0
  637. /package/dist/web/assets/{sortable-header-CipIP7aH.js.gz → sortable-header-BOa8o7NJ.js.gz} +0 -0
  638. /package/dist/web/assets/{use-blob-url-DjuRQWnR.js → use-blob-url-BC69n15x.js} +0 -0
  639. /package/dist/web/assets/{vendor-xterm-Be4L1NHd.js → vendor-xterm-CfasyE0m.js} +0 -0
  640. /package/dist/web/assets/{vendor-xterm-Be4L1NHd.js.br → vendor-xterm-CfasyE0m.js.br} +0 -0
  641. /package/dist/web/assets/{vendor-xterm-Be4L1NHd.js.gz → vendor-xterm-CfasyE0m.js.gz} +0 -0
@@ -0,0 +1,277 @@
1
+ /**
2
+ * The viewer's half of `/ws/android`: mint a nonce, connect, decode, keep the lease alive.
3
+ *
4
+ * Four things here are load-bearing and each one looks optional until it is missing:
5
+ *
6
+ * - **The nonce is minted per connection attempt, and it is single use.** A reconnect mints a
7
+ * new one; reusing the old one is rejected by design, so the retry path must go through the
8
+ * route again rather than caching it.
9
+ * - **The heartbeat is what holds the controller lease.** Stop sending for 15 seconds and the
10
+ * server hands control to someone else — which is the intended behaviour for a closed laptop
11
+ * lid, and a bug if the timer is tied to a render.
12
+ * - **The decoder is reconfigured from the server's `codec` message, never from a guess.** The
13
+ * encoder sets no profile or level, so the string is encoder-default and only knowable from
14
+ * the real SPS. A rung switch and a rotation both produce a new one.
15
+ * - **Deltas before a keyframe are dropped.** A `VideoDecoder` cannot start on one, and neither
16
+ * can a freshly recreated one after an error — `shouldDecodeAccessUnit` is the shared rule.
17
+ */
18
+ import { useCallback, useEffect, useRef, useState } from "react";
19
+ import { api } from "@/lib/api-client";
20
+ import { useH264CanvasDecoder, shouldDecodeAccessUnit } from "@/components/remote-desktop/use-h264-canvas-decoder";
21
+ import { resolveAndroidWsUrl } from "./android-ws-url";
22
+ import {
23
+ ANDROID_PROTOCOL_VERSION, decodeVideoFrame,
24
+ type AndroidClientMessage, type AndroidGeometry, type AndroidLogEntry, type AndroidQuality,
25
+ type AndroidServerMessage,
26
+ } from "../../../shared/android-protocol";
27
+
28
+ const HEARTBEAT_MS = 5_000;
29
+ /** Long enough that a phone switching cell tower does not lose the picture, short enough that a
30
+ * dead socket is noticed. Backed off so a server that is down is not hammered. */
31
+ const RECONNECT_DELAYS_MS = [500, 1_000, 2_000, 4_000, 8_000];
32
+
33
+ export type AndroidConnectionState = "connecting" | "live" | "reconnecting" | "failed";
34
+
35
+ export interface UseAndroidSessionOptions {
36
+ deviceId: string;
37
+ quality: AndroidQuality;
38
+ canvasRef: React.RefObject<HTMLCanvasElement | null>;
39
+ /** Paused sessions stop receiving frames; the emulator and the lease stay up. */
40
+ paused?: boolean;
41
+ }
42
+
43
+ export interface AndroidSession {
44
+ state: AndroidConnectionState;
45
+ errorMessage: string | null;
46
+ geometry: AndroidGeometry | null;
47
+ controller: boolean;
48
+ controllerReason: string | null;
49
+ encoder: string | null;
50
+ /** Null until the first keyframe has been decoded — the viewer shows a spinner until then. */
51
+ hasPicture: boolean;
52
+ send: (message: AndroidClientMessage) => void;
53
+ takeControl: () => void;
54
+ reconnect: () => void;
55
+ /**
56
+ * Turn the device's log feed on or off.
57
+ *
58
+ * The server stops the gRPC stream when the last watcher leaves (plan gate: "logs hidden
59
+ * ngừng subscription"), so this is not a client-side filter — asking for it off really does
60
+ * stop the work on the host.
61
+ */
62
+ setLogcat: (on: boolean) => void;
63
+ /**
64
+ * Listen for log batches. Deliberately a subscription rather than state: a chatty device
65
+ * produces hundreds of entries a second, and putting them in this hook's state would
66
+ * re-render the canvas's parent at that rate.
67
+ */
68
+ onLog: (listener: (entries: AndroidLogEntry[]) => void) => () => void;
69
+ }
70
+
71
+ export function useAndroidSession(opts: UseAndroidSessionOptions): AndroidSession {
72
+ const { deviceId, quality, canvasRef, paused = false } = opts;
73
+
74
+ const [state, setState] = useState<AndroidConnectionState>("connecting");
75
+ const [errorMessage, setErrorMessage] = useState<string | null>(null);
76
+ const [geometry, setGeometry] = useState<AndroidGeometry | null>(null);
77
+ const [controller, setController] = useState(false);
78
+ const [controllerReason, setControllerReason] = useState<string | null>(null);
79
+ const [encoder, setEncoder] = useState<string | null>(null);
80
+ const [hasPicture, setHasPicture] = useState(false);
81
+
82
+ const decoder = useH264CanvasDecoder(canvasRef);
83
+ const socketRef = useRef<WebSocket | null>(null);
84
+ const attemptRef = useRef(0);
85
+ // Bumped by every connection attempt, and only the newest may act. The socket a manual
86
+ // reconnect replaced still fires `onclose` after its handshake, and a nonce request can still
87
+ // be out when the viewer closes: left to run, either one would clear the live socket's ref,
88
+ // schedule a retry nobody asked for, or open a socket nothing will ever close.
89
+ const generationRef = useRef(0);
90
+ const reconnectTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
91
+ const configuredCodecRef = useRef<string | null>(null);
92
+ // Read inside the socket's handlers, which are created once per connection — a state read
93
+ // there would be the value from the render that created them.
94
+ const qualityRef = useRef(quality);
95
+ qualityRef.current = quality;
96
+ const hasPictureRef = useRef(false);
97
+ const logListenersRef = useRef(new Set<(entries: AndroidLogEntry[]) => void>());
98
+ // Survives a reconnect: the subscription is per socket on the server, so a dropped connection
99
+ // silently ends it and the panel would show a log that stopped without saying why.
100
+ const logcatWantedRef = useRef(false);
101
+
102
+ const send = useCallback((message: AndroidClientMessage) => {
103
+ const socket = socketRef.current;
104
+ if (socket?.readyState === WebSocket.OPEN) socket.send(JSON.stringify(message));
105
+ }, []);
106
+
107
+ const takeControl = useCallback(() => send({ type: "take-control" }), [send]);
108
+
109
+ const handleServerMessage = useCallback((message: AndroidServerMessage) => {
110
+ switch (message.type) {
111
+ case "ready":
112
+ attemptRef.current = 0;
113
+ setState("live");
114
+ setErrorMessage(null);
115
+ setGeometry(message.geometry);
116
+ setController(message.controller);
117
+ setControllerReason(null);
118
+ setEncoder(message.encoder);
119
+ if (message.codec) {
120
+ configuredCodecRef.current = message.codec;
121
+ void decoder.configure(message.codec);
122
+ }
123
+ if (logcatWantedRef.current) send({ type: "logcat", subscribe: true });
124
+ return;
125
+ case "log":
126
+ for (const listener of logListenersRef.current) listener(message.entries);
127
+ return;
128
+ case "codec":
129
+ if (message.codec === configuredCodecRef.current) return;
130
+ configuredCodecRef.current = message.codec;
131
+ void decoder.configure(message.codec);
132
+ return;
133
+ case "geometry":
134
+ setGeometry(message.geometry);
135
+ // The decoder is holding parameters for a size that no longer exists; the server's
136
+ // `codec` message for the new bitstream is what reconfigures it.
137
+ decoder.reset();
138
+ setHasPicture(false);
139
+ return;
140
+ case "controller":
141
+ setController(message.controller);
142
+ setControllerReason(message.reason ?? null);
143
+ return;
144
+ case "error":
145
+ setErrorMessage(message.message);
146
+ return;
147
+ default:
148
+ return;
149
+ }
150
+ }, [decoder, send]);
151
+
152
+ /** Silence the current attempt: its callbacks stop acting, its socket and retry are dropped. */
153
+ const retire = useCallback(() => {
154
+ generationRef.current += 1;
155
+ if (reconnectTimerRef.current) clearTimeout(reconnectTimerRef.current);
156
+ reconnectTimerRef.current = null;
157
+ socketRef.current?.close();
158
+ socketRef.current = null;
159
+ }, []);
160
+
161
+ const connect = useCallback(async () => {
162
+ retire();
163
+ const generation = generationRef.current;
164
+ const current = () => generationRef.current === generation;
165
+ setErrorMessage(null);
166
+
167
+ let nonce: string;
168
+ try {
169
+ // Single use and short-lived: a reconnect must mint a fresh one, never replay this.
170
+ const res = await api.post<{ nonce: string }>(`/api/android/devices/${encodeURIComponent(deviceId)}/sessions`, {});
171
+ nonce = res.nonce;
172
+ } catch (e) {
173
+ if (!current()) return;
174
+ setErrorMessage((e as Error).message);
175
+ setState("failed");
176
+ return;
177
+ }
178
+ if (!current()) return;
179
+
180
+ const socket = new WebSocket(resolveAndroidWsUrl(window.location, import.meta.env.DEV));
181
+ socket.binaryType = "arraybuffer";
182
+ socketRef.current = socket;
183
+
184
+ socket.onopen = () => {
185
+ socket.send(JSON.stringify({ type: "auth", nonce, quality: qualityRef.current } satisfies AndroidClientMessage));
186
+ };
187
+
188
+ socket.onmessage = (event) => {
189
+ // A replaced socket can still deliver what was queued before its close: its `ready` would
190
+ // mark the viewer live, and its frames would feed the decoder, while the new one connects.
191
+ if (!current()) return;
192
+ if (typeof event.data === "string") {
193
+ try { handleServerMessage(JSON.parse(event.data) as AndroidServerMessage); } catch { /* not ours */ }
194
+ return;
195
+ }
196
+ const frame = decodeVideoFrame(event.data as ArrayBuffer);
197
+ // A frame that fails to decode here was truncated or coalesced in transit; feeding its
198
+ // bytes to the decoder anyway is how a stream turns to garbage rather than erroring.
199
+ if (!frame || frame.header.version !== ANDROID_PROTOCOL_VERSION) return;
200
+ if (!shouldDecodeAccessUnit(hasPictureRef.current, frame.header.keyframe)) return;
201
+ hasPictureRef.current = true;
202
+ setHasPicture(true);
203
+ decoder.decodeAccessUnit(frame.payload, frame.header.keyframe);
204
+ };
205
+
206
+ socket.onclose = () => {
207
+ if (!current()) return;
208
+ socketRef.current = null;
209
+ const delay = RECONNECT_DELAYS_MS[Math.min(attemptRef.current, RECONNECT_DELAYS_MS.length - 1)]!;
210
+ attemptRef.current += 1;
211
+ setState(attemptRef.current > RECONNECT_DELAYS_MS.length ? "failed" : "reconnecting");
212
+ hasPictureRef.current = false;
213
+ setHasPicture(false);
214
+ decoder.reset();
215
+ configuredCodecRef.current = null;
216
+ reconnectTimerRef.current = setTimeout(() => { void connect(); }, delay);
217
+ };
218
+
219
+ socket.onerror = () => { /* onclose does the work; this only stops an unhandled event */ };
220
+ }, [deviceId, decoder, handleServerMessage, retire]);
221
+
222
+ useEffect(() => {
223
+ void connect();
224
+ return () => {
225
+ retire();
226
+ decoder.reset();
227
+ };
228
+ // `connect` is stable per deviceId; re-running on every decoder identity change would
229
+ // reconnect the socket for no reason.
230
+ // eslint-disable-next-line react-hooks/exhaustive-deps
231
+ }, [deviceId]);
232
+
233
+ // The heartbeat is the lease. A tab that stops sending loses control after 15s but keeps
234
+ // watching, so this runs regardless of whether this client currently holds it.
235
+ useEffect(() => {
236
+ const timer = setInterval(() => send({ type: "heartbeat" }), HEARTBEAT_MS);
237
+ return () => clearInterval(timer);
238
+ }, [send]);
239
+
240
+ useEffect(() => { send({ type: "visibility", visible: !paused }); }, [paused, send]);
241
+
242
+ useEffect(() => {
243
+ if (state === "live") send({ type: "quality", quality });
244
+ }, [quality, state, send]);
245
+
246
+ const setLogcat = useCallback((on: boolean) => {
247
+ logcatWantedRef.current = on;
248
+ send({ type: "logcat", subscribe: on });
249
+ }, [send]);
250
+
251
+ const onLog = useCallback((listener: (entries: AndroidLogEntry[]) => void) => {
252
+ const set = logListenersRef.current;
253
+ set.add(listener);
254
+ return () => { set.delete(listener); };
255
+ }, []);
256
+
257
+ const reconnect = useCallback(() => {
258
+ attemptRef.current = 0;
259
+ setState("connecting");
260
+ void connect();
261
+ }, [connect]);
262
+
263
+ return {
264
+ state,
265
+ errorMessage: errorMessage ?? decoder.errorMessage,
266
+ geometry,
267
+ controller,
268
+ controllerReason,
269
+ encoder,
270
+ hasPicture,
271
+ send,
272
+ takeControl,
273
+ reconnect,
274
+ setLogcat,
275
+ onLog,
276
+ };
277
+ }
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Pointer events on the canvas -> `sendTouch` messages.
3
+ *
4
+ * Five things that are not optional:
5
+ *
6
+ * - **`touch-action: none`** on the canvas, or the browser eats the gesture as a scroll or a
7
+ * pinch and the guest sees a fraction of it. Set in the viewer's class list, not here.
8
+ * - **Pointer capture**, so a drag that leaves the canvas keeps reporting. Without it a swipe
9
+ * off the edge simply stops, and the finger is never released.
10
+ * - **Every active contact is sent on every update.** The emulator tracks slots by identifier
11
+ * and a message describing only the finger that moved would be read as the others lifting.
12
+ * - **A release is pressure 0, and it must be sent.** The proto is explicit: "Make sure to
13
+ * deliver a pressure of 0 for the given identifier when the touch event is completed,
14
+ * otherwise the touch identifier will not be unregistered". The 120-second expiry is not a
15
+ * substitute.
16
+ * - **`pointercancel` is handled exactly like `pointerup`.** The browser fires it instead of
17
+ * `pointerup` once it decides a gesture belongs to something else, and then delivers nothing
18
+ * further for that pointer — the same class of bug as the long-press trap in CLAUDE.md, only
19
+ * here it leaves a finger pressed on the guest forever.
20
+ */
21
+ import { useCallback, useEffect, useRef } from "react";
22
+ import { pointerToDevice } from "./android-coords";
23
+ import type { AndroidClientMessage, AndroidGeometry, AndroidTouchPoint } from "../../../shared/android-protocol";
24
+
25
+ /** What the emulator reports for a finger that is down. Any non-zero value means "touching". */
26
+ const PRESSURE_DOWN = 1;
27
+ const MAX_SLOTS = 10;
28
+
29
+ export interface UseAndroidTouchOptions {
30
+ canvasRef: React.RefObject<HTMLCanvasElement | null>;
31
+ geometry: AndroidGeometry | null;
32
+ enabled: boolean;
33
+ send: (message: AndroidClientMessage) => void;
34
+ }
35
+
36
+ export interface AndroidTouchHandlers {
37
+ onPointerDown: (e: React.PointerEvent<HTMLCanvasElement>) => void;
38
+ onPointerMove: (e: React.PointerEvent<HTMLCanvasElement>) => void;
39
+ onPointerUp: (e: React.PointerEvent<HTMLCanvasElement>) => void;
40
+ onPointerCancel: (e: React.PointerEvent<HTMLCanvasElement>) => void;
41
+ }
42
+
43
+ export function useAndroidTouch(opts: UseAndroidTouchOptions): AndroidTouchHandlers {
44
+ const { canvasRef, geometry, enabled, send } = opts;
45
+
46
+ /** Browser pointerId -> the emulator slot it owns, plus its last position. */
47
+ const contacts = useRef(new Map<number, { slot: number; x: number; y: number }>());
48
+
49
+ const geometryRef = useRef(geometry);
50
+ geometryRef.current = geometry;
51
+ const enabledRef = useRef(enabled);
52
+ enabledRef.current = enabled;
53
+
54
+ const publish = useCallback((releasing?: { slot: number; x: number; y: number }) => {
55
+ const g = geometryRef.current;
56
+ if (!g) return;
57
+ const touches: AndroidTouchPoint[] = [...contacts.current.values()].map((c) => ({
58
+ x: c.x, y: c.y, id: c.slot, pressure: PRESSURE_DOWN,
59
+ }));
60
+ if (releasing) touches.push({ x: releasing.x, y: releasing.y, id: releasing.slot, pressure: 0 });
61
+ if (touches.length === 0) return;
62
+ send({ type: "touch", geometryGeneration: g.generation, touches });
63
+ }, [send]);
64
+
65
+ const locate = useCallback((e: React.PointerEvent<HTMLCanvasElement>) => {
66
+ const canvas = canvasRef.current;
67
+ const g = geometryRef.current;
68
+ if (!canvas || !g) return null;
69
+ const rect = canvas.getBoundingClientRect();
70
+ return pointerToDevice(rect, g, e.clientX - rect.left, e.clientY - rect.top);
71
+ }, [canvasRef]);
72
+
73
+ const onPointerDown = useCallback((e: React.PointerEvent<HTMLCanvasElement>) => {
74
+ if (!enabledRef.current) return;
75
+ const point = locate(e);
76
+ if (!point) return; // a letterbox bar belongs to no pixel of the guest
77
+ if (contacts.current.size >= MAX_SLOTS) return;
78
+ const used = new Set([...contacts.current.values()].map((c) => c.slot));
79
+ let slot = 0;
80
+ while (used.has(slot)) slot++;
81
+ contacts.current.set(e.pointerId, { slot, x: point.x, y: point.y });
82
+ // Capture, so a drag that leaves the canvas keeps reporting and can still be released.
83
+ try { e.currentTarget.setPointerCapture(e.pointerId); } catch { /* not capturable */ }
84
+ publish();
85
+ }, [locate, publish]);
86
+
87
+ const onPointerMove = useCallback((e: React.PointerEvent<HTMLCanvasElement>) => {
88
+ const contact = contacts.current.get(e.pointerId);
89
+ if (!contact || !enabledRef.current) return;
90
+ const point = locate(e);
91
+ // Outside the picture the finger keeps its last position rather than jumping or lifting: a
92
+ // swipe that overshoots the edge is still one continuous gesture to the guest.
93
+ if (point) { contact.x = point.x; contact.y = point.y; }
94
+ publish();
95
+ }, [locate, publish]);
96
+
97
+ const release = useCallback((e: React.PointerEvent<HTMLCanvasElement>) => {
98
+ const contact = contacts.current.get(e.pointerId);
99
+ if (!contact) return;
100
+ contacts.current.delete(e.pointerId);
101
+ try { e.currentTarget.releasePointerCapture(e.pointerId); } catch { /* already released */ }
102
+ publish(contact);
103
+ }, [publish]);
104
+
105
+ // A tab hidden or a window blurred mid-drag delivers no further pointer events at all, so the
106
+ // only way the guest ever sees those fingers lift is for the server to clear every slot.
107
+ useEffect(() => {
108
+ const reset = () => {
109
+ if (contacts.current.size === 0) return;
110
+ contacts.current.clear();
111
+ send({ type: "input-reset" });
112
+ };
113
+ window.addEventListener("blur", reset);
114
+ document.addEventListener("visibilitychange", reset);
115
+ return () => {
116
+ window.removeEventListener("blur", reset);
117
+ document.removeEventListener("visibilitychange", reset);
118
+ reset();
119
+ };
120
+ }, [send]);
121
+
122
+ return { onPointerDown, onPointerMove, onPointerUp: release, onPointerCancel: release };
123
+ }
@@ -6,12 +6,15 @@ import { selectInlineImages } from "@/lib/image-resize-limits";
6
6
  import { splitAttachmentMarkers } from "@/lib/attachment-marker-split";
7
7
  import type { ChatAttemptEvent } from "@/lib/chat-attempt-lifecycle";
8
8
  import { useChat } from "@/hooks/use-chat";
9
+ import { useChatPrewarm } from "@/hooks/use-chat-prewarm";
9
10
  import { useUsage } from "@/hooks/use-usage";
10
11
  import { useDesignSessionRedirect } from "@/hooks/use-design-session-redirect";
11
12
  import { useTabStore } from "@/stores/tab-store";
12
13
  import { useSettingsStore } from "@/stores/settings-store";
13
14
  import { usePanelStore } from "@/stores/panel-store";
14
15
  import { useNotificationStore } from "@/stores/notification-store";
16
+ import { useFileStore } from "@/stores/file-store";
17
+ import { useRemoteFileSearch } from "@/hooks/use-remote-file-search";
15
18
  import { openBugReportPopup } from "@/lib/report-bug";
16
19
  import { useChatAccountClaim } from "@/hooks/use-chat-account-claim";
17
20
  import { startPrepare, getPrepare, isPrepared, forgetPrepare } from "@/lib/new-chat-prepare-client";
@@ -399,6 +402,7 @@ function ChatTabContent({ metadata, tabId, onNewSession, onFork, historyFilter }
399
402
  setEffort,
400
403
  thinking,
401
404
  setThinking,
405
+ turnSettings,
402
406
  sendMessage,
403
407
  respondToApproval,
404
408
  cancelStreaming,
@@ -413,6 +417,19 @@ function ChatTabContent({ metadata, tabId, onNewSession, onFork, historyFilter }
413
417
  killBackgroundShell,
414
418
  } = useChat(sessionId, providerId, projectName, handleSessionMigrated, observeAttempt);
415
419
 
420
+ // `model`/`effort`/`thinking` are what change when a pick does; the picks sent are read
421
+ // through `turnSettings`, which leaves out whatever the user has not chosen.
422
+ const firstMessagePicks = useMemo(() => turnSettings(), [turnSettings, model, effort, thinking]);
423
+ const touchPrewarm = useChatPrewarm({
424
+ // Not while the provider is still being resolved: the process would start for a guess.
425
+ enabled: !sessionId && !designSlug && tourTabActive && !!projectName && !preparation.pending,
426
+ projectName,
427
+ providerId,
428
+ permissionMode,
429
+ accountId: claimMatchesProvider ? pickedAccountId : undefined,
430
+ picks: firstMessagePicks,
431
+ });
432
+
416
433
  useEffect(() => {
417
434
  if (!tabId || !tourTabActive || draftLoading || isStreaming) return;
418
435
  const announce = () => window.dispatchEvent(new CustomEvent("ppm:onboarding-evidence", {
@@ -1006,8 +1023,9 @@ function ChatTabContent({ metadata, tabId, onNewSession, onFork, historyFilter }
1006
1023
  const handleContentChange = useCallback(
1007
1024
  (content: string, attachments?: DraftAttachment[]) => {
1008
1025
  saveDraft(content, attachments);
1026
+ touchPrewarm();
1009
1027
  },
1010
- [saveDraft],
1028
+ [saveDraft, touchPrewarm],
1011
1029
  );
1012
1030
 
1013
1031
  /** Stable callback for slash items loaded — prevents MessageInput memo break */
@@ -1057,6 +1075,14 @@ function ChatTabContent({ metadata, tabId, onNewSession, onFork, historyFilter }
1057
1075
  }, []);
1058
1076
 
1059
1077
  // --- File picker handlers ---
1078
+ // An open @-picker keeps the file index current, like the palette — see `openIndexReader`.
1079
+ useEffect(() => {
1080
+ if (showFilePicker && projectName) return useFileStore.getState().openIndexReader(projectName);
1081
+ }, [showFilePicker, projectName]);
1082
+ // A project too long to send is searched on the server as the @-query changes.
1083
+ const indexRemote = useFileStore((s) => s.indexRemote);
1084
+ const remoteFileItems = useRemoteFileSearch(projectName, fileFilter, { enabled: showFilePicker && indexRemote, kind: "all" });
1085
+
1060
1086
  const handleFileStateChange = useCallback((visible: boolean, filter: string) => {
1061
1087
  setShowFilePicker(visible);
1062
1088
  setFileFilter(filter);
@@ -1247,7 +1273,7 @@ function ChatTabContent({ metadata, tabId, onNewSession, onFork, historyFilter }
1247
1273
  projectName={projectName}
1248
1274
  />
1249
1275
  <FilePicker
1250
- items={fileItems}
1276
+ items={indexRemote ? remoteFileItems : fileItems}
1251
1277
  filter={fileFilter}
1252
1278
  onSelect={handleFileSelect}
1253
1279
  onClose={handleFileClose}
@@ -470,7 +470,7 @@ export const MessageInput = memo(function MessageInput({
470
470
  void files.loadIndex(projectName);
471
471
  return;
472
472
  }
473
- const nodes: FileNode[] = files.indexProjectName === projectName ? files.fileIndex : [];
473
+ const nodes: FileNode[] = files.indexProject === projectName ? files.fileIndex : [];
474
474
  fileItemsRef.current = nodes;
475
475
  onFileItemsLoaded?.(nodes);
476
476
  };
@@ -826,10 +826,8 @@ export const MessageInput = memo(function MessageInput({
826
826
  if (hasAt) {
827
827
  const atMatch = textBefore.match(/@(\S*)$/);
828
828
  if (atMatch) {
829
- const files = useFileStore.getState();
830
- if (projectName && (files.indexProjectName !== projectName || files.indexStatus === "idle" || files.indexStatus === "error")) {
831
- void files.loadIndex(projectName);
832
- }
829
+ // The index is refreshed only when something opens to read it — see `indexStale`.
830
+ if (!filePickerOpenRef.current && projectName) useFileStore.getState().ensureIndex(projectName);
833
831
  onFileStateChange?.(true, atMatch[1] ?? "");
834
832
  filePickerOpenRef.current = true;
835
833
  if (slashPickerOpenRef.current) { onSlashStateChange?.(false, ""); slashPickerOpenRef.current = false; }
@@ -3,6 +3,7 @@ import { Columns2, FileCode, X } from "@/lib/icons";
3
3
  import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
4
4
  import { useTabStore } from "@/stores/tab-store";
5
5
  import { useFileStore } from "@/stores/file-store";
6
+ import { useRemoteFileSearch } from "@/hooks/use-remote-file-search";
6
7
  import { useProjectStore } from "@/stores/project-store";
7
8
  import { useCompareStore, type CompareSelection } from "@/stores/compare-store";
8
9
  import { openCompareTab } from "@/lib/open-compare-tab";
@@ -64,6 +65,15 @@ export function ComparePicker({ open: openProp, onOpenChange, initialA }: Compar
64
65
  const tabs = useTabStore((s) => s.tabs);
65
66
  const fileIndex = useFileStore((s) => s.fileIndex);
66
67
  const activeProject = useProjectStore((s) => s.activeProject);
68
+ // A project too long to send is searched on the server, and ranked below like the list itself.
69
+ const indexRemote = useFileStore((s) => s.indexRemote);
70
+ const remoteFiles = useRemoteFileSearch(activeProject?.name, query, { enabled: open && indexRemote });
71
+
72
+ // The index is refreshed only when something opens to read it — see `indexStale` — and kept
73
+ // current while that stays open.
74
+ useEffect(() => {
75
+ if (open && activeProject) return useFileStore.getState().openIndexReader(activeProject.name);
76
+ }, [open, activeProject]);
67
77
 
68
78
  useEffect(() => {
69
79
  if (!open) return;
@@ -88,7 +98,7 @@ export function ComparePicker({ open: openProp, onOpenChange, initialA }: Compar
88
98
  dirtyContent: t.metadata!.unsavedContent as string | undefined,
89
99
  }));
90
100
  const seenPaths = new Set(tabCands.map((c) => c.path));
91
- const fileCands: Candidate[] = fileIndex
101
+ const fileCands: Candidate[] = (indexRemote ? remoteFiles : fileIndex)
92
102
  .filter((f) => f.type === "file" && !seenPaths.has(f.path))
93
103
  .map((f) => ({
94
104
  id: `file:${f.path}`,
@@ -97,7 +107,7 @@ export function ComparePicker({ open: openProp, onOpenChange, initialA }: Compar
97
107
  source: "file",
98
108
  }));
99
109
  return [...tabCands, ...fileCands];
100
- }, [tabs, fileIndex]);
110
+ }, [tabs, fileIndex, indexRemote, remoteFiles]);
101
111
 
102
112
  const filtered = useMemo<Candidate[]>(() => {
103
113
  if (!query.trim()) return candidates.slice(0, MAX_RESULTS);
@@ -358,8 +358,9 @@ export function FileTree({ onFileOpen }: FileTreeProps = {}) {
358
358
  savePersistedExpanded(activeProject.name, useFileStore.getState().expandedPaths);
359
359
  }, [expandedPaths, activeProject?.name]); // eslint-disable-line react-hooks/exhaustive-deps
360
360
 
361
- // Index invalidation is app-wide so it also works with this drawer closed.
362
- // The mounted tree only refreshes the affected folder.
361
+ // Handle WS file:changed → invalidate folder. The index is only marked stale, and app-wide
362
+ // (useGlobalEvents, useFileIndexInvalidation) so that also works with this drawer closed;
363
+ // refetching it here was a 22 MB download per change on a large project.
363
364
  useEffect(() => {
364
365
  if (!activeProject) return;
365
366
  const projectName = activeProject.name;
@@ -389,7 +390,8 @@ export function FileTree({ onFileOpen }: FileTreeProps = {}) {
389
390
 
390
391
  // Symmetric with the fsChanged(...) calls this file emits after its own mutations: an
391
392
  // explorer window mutating a directory inside this project must invalidate the matching
392
- // tree node too.
393
+ // tree node too. It is also what marks the index stale after the tree's own renames, creates
394
+ // and moves.
393
395
  useEffect(() => {
394
396
  if (!activeProject) return;
395
397
  const projectName = activeProject.name;
@@ -398,6 +400,7 @@ export function FileTree({ onFileOpen }: FileTreeProps = {}) {
398
400
  const relative = relativeProjectPath(root, absoluteDir);
399
401
  if (relative == null) return; // outside this project — not the tree's concern
400
402
  const store = useFileStore.getState();
403
+ store.markIndexStale(projectName);
401
404
  store.invalidateFolder(projectName, relative);
402
405
  });
403
406
  }, [activeProject]);
@@ -529,8 +532,6 @@ export function FileTree({ onFileOpen }: FileTreeProps = {}) {
529
532
  const newPath = parentPath ? `${parentPath}/${value}` : value;
530
533
  await api.post(`${projectUrl(projectName)}/files/rename`, { oldPath: node.path, newPath });
531
534
  clearInlineAction();
532
- store.invalidateIndex();
533
- store.loadIndex(projectName);
534
535
  store.invalidateFolder(projectName, parentPath);
535
536
  fsChanged(absoluteProjectPath(activeProject!.path, parentPath));
536
537
  } else {
@@ -538,8 +539,6 @@ export function FileTree({ onFileOpen }: FileTreeProps = {}) {
538
539
  const fullPath = row.targetPath ? `${row.targetPath}/${value}` : value;
539
540
  await api.post(`${projectUrl(projectName)}/files/create`, { path: fullPath, type });
540
541
  clearInlineAction();
541
- store.invalidateIndex();
542
- store.loadIndex(projectName);
543
542
  store.invalidateFolder(projectName, row.targetPath);
544
543
  fsChanged(absoluteProjectPath(activeProject!.path, row.targetPath));
545
544
  }
@@ -178,8 +178,6 @@ export function useTreeRowDnd(options: TreeRowDndOptions): TreeRowDnd {
178
178
  api.post(`${projectUrl(projectName)}/files/move`, { source: sourcePath, destination })
179
179
  .then(() => {
180
180
  const store = useFileStore.getState();
181
- store.invalidateIndex();
182
- store.loadIndex(projectName);
183
181
  store.invalidateFolder(projectName, sourceParent);
184
182
  store.invalidateFolder(projectName, dropTargetDir);
185
183
  // An explorer window showing either folder must refresh too.
@@ -7,7 +7,7 @@
7
7
  * icon/virtualisation deps) never lands in the initial bundle.
8
8
  */
9
9
 
10
- import { lazy, type ComponentType, type LazyExoticComponent } from "react";
10
+ import { lazyWithPreload, type PreloadableComponent } from "@/lib/lazy-with-preload";
11
11
  import type { WindowKind } from "./window-store-types";
12
12
 
13
13
  export interface WindowContentProps {
@@ -17,13 +17,13 @@ export interface WindowContentProps {
17
17
  payload?: Record<string, unknown>;
18
18
  }
19
19
 
20
- export const WINDOW_CONTENT: Record<WindowKind, LazyExoticComponent<ComponentType<WindowContentProps>>> = {
21
- explorer: lazy(() => import("@/components/os-explorer/explorer-window-content")),
22
- "agent-session": lazy(() => import("@/components/chat/agent-session-window-content")),
23
- "system-monitor": lazy(() => import("@/components/system/system-monitor-window-content")),
24
- "tab-host": lazy(() => import("./tab-host-window-content")),
25
- "remote-desktop": lazy(() => import("@/components/remote-desktop/remote-desktop-window-content")),
26
- settings: lazy(() => import("@/components/settings/settings-window-content")),
20
+ export const WINDOW_CONTENT: Record<WindowKind, PreloadableComponent<WindowContentProps>> = {
21
+ explorer: lazyWithPreload(() => import("@/components/os-explorer/explorer-window-content")),
22
+ "agent-session": lazyWithPreload(() => import("@/components/chat/agent-session-window-content")),
23
+ "system-monitor": lazyWithPreload(() => import("@/components/system/system-monitor-window-content")),
24
+ "tab-host": lazyWithPreload(() => import("./tab-host-window-content")),
25
+ "remote-desktop": lazyWithPreload(() => import("@/components/remote-desktop/remote-desktop-window-content")),
26
+ settings: lazyWithPreload(() => import("@/components/settings/settings-window-content")),
27
27
  };
28
28
 
29
29
  /** Titlebar text for a window. Falls back to the kind's generic name. */