@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,305 @@
1
+ /**
2
+ * Create an AVD, without ever touching the SDK by hand.
3
+ *
4
+ * Three decisions worth stating, all of them from the plan's Phase 4:
5
+ *
6
+ * - **PPM only offers images the host already has.** `/system-images` reads the installed tree;
7
+ * there is no "download 2.9 GB now" button, because "không tự chấp nhận SDK license hay tải
8
+ * nhiều GB khi mở tab" reads forward as well as back. An image for another CPU is listed but
9
+ * unselectable, with the reason on the row, rather than hidden — hidden looks like a bug when
10
+ * the user knows they installed it.
11
+ * - **A device profile is required**, and the form says why in the hint: `avdmanager` with no
12
+ * `--device` builds a 320x640 AVD, which looks like a broken emulator rather than a default.
13
+ * - **The name is validated with the host's own regex** (`@/shared/android-avd`), so the form
14
+ * cannot accept something the host will refuse after the dialog has closed.
15
+ *
16
+ * `SearchSelect` rather than a plain select: this host lists 88 device profiles, most of which
17
+ * agree for most of their length.
18
+ */
19
+ import { useEffect, useMemo, useState } from "react";
20
+ import { api } from "@/lib/api-client";
21
+ import { Button } from "@/components/ui/button";
22
+ import {
23
+ Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle,
24
+ } from "@/components/ui/dialog";
25
+ import { BottomSheet } from "@/components/ui/mobile-bottom-sheet";
26
+ import { Input } from "@/components/ui/input";
27
+ import { SearchSelect } from "@/components/ui/search-select";
28
+ import { Loader2, Smartphone, HardDrive, TriangleAlert } from "@/lib/icons";
29
+ import { useIsMobile } from "@/hooks/use-is-mobile";
30
+ import { AVD_LIMITS, validateAvdName } from "../../../shared/android-avd";
31
+ import type { AvdDeviceProfile } from "../../../shared/android-avd";
32
+
33
+ /** What `/api/android/system-images` returns per row. `sysdir` is stripped host-side. */
34
+ export interface SystemImageRow {
35
+ id: string;
36
+ apiLevel: number | null;
37
+ abi: string;
38
+ tag: string;
39
+ tagDisplay: string;
40
+ description: string;
41
+ playStore: boolean;
42
+ bytes: number;
43
+ hostCompatible: boolean;
44
+ }
45
+
46
+ export interface AndroidAvdCreateDialogProps {
47
+ open: boolean;
48
+ onClose: () => void;
49
+ /** Names already taken, so a duplicate is caught before a round trip. */
50
+ existingNames: string[];
51
+ /** The new AVD landed; the list should refresh. */
52
+ onCreated: (name: string) => void;
53
+ }
54
+
55
+ const KIND_ORDER: AvdDeviceProfile["kind"][] = [
56
+ "phone", "tablet", "tv", "wear", "automotive", "desktop", "other",
57
+ ];
58
+ const KIND_LABEL: Record<AvdDeviceProfile["kind"], string> = {
59
+ phone: "Phones", tablet: "Tablets", tv: "TV", wear: "Wear",
60
+ automotive: "Automotive", desktop: "Desktop", other: "Other",
61
+ };
62
+
63
+ const gb = (bytes: number) => `${(bytes / 1024 ** 3).toFixed(1)} GB`;
64
+
65
+ export function AndroidAvdCreateDialog(
66
+ { open, onClose, existingNames, onCreated }: AndroidAvdCreateDialogProps,
67
+ ) {
68
+ const isMobile = useIsMobile();
69
+ const [images, setImages] = useState<SystemImageRow[] | null>(null);
70
+ const [profiles, setProfiles] = useState<AvdDeviceProfile[] | null>(null);
71
+ const [loadError, setLoadError] = useState<string | null>(null);
72
+
73
+ const [name, setName] = useState("");
74
+ const [image, setImage] = useState("");
75
+ const [profile, setProfile] = useState("");
76
+ const [ramMb, setRamMb] = useState(String(AVD_LIMITS.ramMb.default));
77
+ const [storageMb, setStorageMb] = useState(String(AVD_LIMITS.storageMb.default));
78
+
79
+ const [saving, setSaving] = useState(false);
80
+ const [error, setError] = useState<string | null>(null);
81
+
82
+ // The two lists are the host's, and the profile list costs a real `avdmanager` run (~730 ms
83
+ // cold, cached after), so they are fetched when the dialog opens rather than with the tab.
84
+ useEffect(() => {
85
+ if (!open) return;
86
+ let alive = true;
87
+ setLoadError(null);
88
+ void (async () => {
89
+ try {
90
+ const [i, p] = await Promise.all([
91
+ api.get<{ images: SystemImageRow[] }>("/api/android/system-images"),
92
+ api.get<{ profiles: AvdDeviceProfile[] }>("/api/android/device-profiles"),
93
+ ]);
94
+ if (!alive) return;
95
+ setImages(i.images);
96
+ setProfiles(p.profiles);
97
+ // Default to something that will actually boot here: the newest image this CPU can run.
98
+ const usable = i.images.filter((x) => x.hostCompatible);
99
+ const newest = usable.slice().sort((a, b) => (b.apiLevel ?? 0) - (a.apiLevel ?? 0))[0];
100
+ if (newest) setImage((current) => current || newest.id);
101
+ if (p.profiles.some((x) => x.id === "pixel_9")) setProfile((c) => c || "pixel_9");
102
+ } catch (e) {
103
+ if (alive) setLoadError((e as Error).message);
104
+ }
105
+ })();
106
+ return () => { alive = false; };
107
+ }, [open]);
108
+
109
+ const imageItems = useMemo(() => (images ?? []).map((i) => ({
110
+ value: i.hostCompatible ? i.id : "",
111
+ label: `API ${i.apiLevel ?? "?"} · ${i.tagDisplay} · ${i.abi}`,
112
+ group: i.hostCompatible ? "Runs on this host" : "Wrong CPU for this host",
113
+ hint: i.hostCompatible ? gb(i.bytes) : `needs ${i.abi}`,
114
+ title: i.id,
115
+ })), [images]);
116
+
117
+ const profileItems = useMemo(() => {
118
+ const list = profiles ?? [];
119
+ return KIND_ORDER.flatMap((kind) => list
120
+ .filter((p) => p.kind === kind)
121
+ .map((p) => ({ value: p.id, label: p.name, group: KIND_LABEL[kind], hint: p.oem })));
122
+ }, [profiles]);
123
+
124
+ const nameProblem = name.length === 0
125
+ ? null
126
+ : existingNames.some((n) => n.toLowerCase() === name.toLowerCase())
127
+ ? "an AVD with that name already exists"
128
+ : validateAvdName(name);
129
+ const ram = Number(ramMb);
130
+ const storage = Number(storageMb);
131
+ const ramProblem = Number.isFinite(ram) && ram >= AVD_LIMITS.ramMb.min && ram <= AVD_LIMITS.ramMb.max
132
+ ? null : `RAM must be between ${AVD_LIMITS.ramMb.min} and ${AVD_LIMITS.ramMb.max} MB`;
133
+ const storageProblem = Number.isFinite(storage) && storage >= AVD_LIMITS.storageMb.min
134
+ && storage <= AVD_LIMITS.storageMb.max
135
+ ? null : `Storage must be between ${AVD_LIMITS.storageMb.min} and ${AVD_LIMITS.storageMb.max} MB`;
136
+
137
+ const ready = name.length > 0 && !nameProblem && !ramProblem && !storageProblem
138
+ && image !== "" && profile !== "" && !saving;
139
+
140
+ const reset = () => {
141
+ setName(""); setError(null); setSaving(false);
142
+ setRamMb(String(AVD_LIMITS.ramMb.default));
143
+ setStorageMb(String(AVD_LIMITS.storageMb.default));
144
+ };
145
+
146
+ const submit = async () => {
147
+ if (!ready) return;
148
+ setSaving(true);
149
+ setError(null);
150
+ try {
151
+ await api.post("/api/android/avds", {
152
+ name, systemImage: image, deviceProfile: profile, ramMb: ram, storageMb: storage,
153
+ });
154
+ onCreated(name);
155
+ reset();
156
+ onClose();
157
+ } catch (e) {
158
+ setError((e as Error).message);
159
+ setSaving(false);
160
+ }
161
+ };
162
+
163
+ const loading = !loadError && (images === null || profiles === null);
164
+
165
+ const body = (
166
+ <div className="space-y-4">
167
+ {loadError && (
168
+ <p className="flex items-start gap-2 text-sm leading-relaxed text-destructive">
169
+ <TriangleAlert className="mt-0.5 size-4 shrink-0" />
170
+ <span className="min-w-0 flex-1">{loadError}</span>
171
+ </p>
172
+ )}
173
+ {loading && (
174
+ <p className="flex items-center gap-2 py-6 text-sm text-muted-foreground">
175
+ <Loader2 className="size-4 animate-spin" /> Reading the SDK…
176
+ </p>
177
+ )}
178
+
179
+ {!loading && !loadError && (
180
+ <>
181
+ <div className="space-y-1.5">
182
+ <label htmlFor="avd-name" className="text-sm font-medium">Name</label>
183
+ <Input
184
+ id="avd-name"
185
+ value={name}
186
+ onChange={(e) => setName(e.target.value)}
187
+ placeholder="Pixel_9_API_35"
188
+ autoComplete="off"
189
+ spellCheck={false}
190
+ className="h-11"
191
+ />
192
+ {nameProblem
193
+ ? <p className="text-xs leading-relaxed text-destructive">{nameProblem}</p>
194
+ : <p className="text-xs leading-relaxed text-muted-foreground">
195
+ This becomes a folder name, so no spaces.
196
+ </p>}
197
+ </div>
198
+
199
+ <div className="space-y-1.5">
200
+ <span className="text-sm font-medium">System image</span>
201
+ <SearchSelect
202
+ value={image}
203
+ items={imageItems}
204
+ onChange={setImage}
205
+ label="System image"
206
+ searchPlaceholder="Search images"
207
+ emptyText="No system images are installed"
208
+ placeholder="Pick an installed image"
209
+ icon={HardDrive}
210
+ className="h-11 w-full md:h-9"
211
+ modal={!isMobile}
212
+ />
213
+ <p className="text-xs leading-relaxed text-muted-foreground">
214
+ Only images already downloaded on this host. PPM never downloads one for you —
215
+ use Android Studio's SDK Manager, or{" "}
216
+ <code className="rounded bg-muted px-1 text-[11px]">sdkmanager</code>.
217
+ </p>
218
+ </div>
219
+
220
+ <div className="space-y-1.5">
221
+ <span className="text-sm font-medium">Device</span>
222
+ <SearchSelect
223
+ value={profile}
224
+ items={profileItems}
225
+ onChange={setProfile}
226
+ label="Device profile"
227
+ searchPlaceholder="Search devices"
228
+ emptyText="No device profiles"
229
+ placeholder="Pick a device"
230
+ icon={Smartphone}
231
+ className="h-11 w-full md:h-9"
232
+ modal={!isMobile}
233
+ />
234
+ <p className="text-xs leading-relaxed text-muted-foreground">
235
+ Sets the screen. Without one the emulator is 320x640.
236
+ </p>
237
+ </div>
238
+
239
+ <div className="flex gap-3">
240
+ <div className="min-w-0 flex-1 space-y-1.5">
241
+ <label htmlFor="avd-ram" className="text-sm font-medium">RAM (MB)</label>
242
+ <Input id="avd-ram" inputMode="numeric" value={ramMb} className="h-11"
243
+ onChange={(e) => setRamMb(e.target.value)} />
244
+ </div>
245
+ <div className="min-w-0 flex-1 space-y-1.5">
246
+ <label htmlFor="avd-storage" className="text-sm font-medium">Storage (MB)</label>
247
+ <Input id="avd-storage" inputMode="numeric" value={storageMb} className="h-11"
248
+ onChange={(e) => setStorageMb(e.target.value)} />
249
+ </div>
250
+ </div>
251
+ {(ramProblem || storageProblem) && (
252
+ <p className="text-xs leading-relaxed text-destructive">{ramProblem ?? storageProblem}</p>
253
+ )}
254
+
255
+ <p className="text-xs leading-relaxed text-muted-foreground">
256
+ PPM writes <code className="rounded bg-muted px-1 text-[11px]">hw.keyboard=yes</code>{" "}
257
+ into every AVD it creates — without it the emulator accepts key presses and shows
258
+ nothing.
259
+ </p>
260
+ </>
261
+ )}
262
+
263
+ {error && (
264
+ <p className="flex items-start gap-2 text-sm leading-relaxed text-destructive">
265
+ <TriangleAlert className="mt-0.5 size-4 shrink-0" />
266
+ <span className="min-w-0 flex-1">{error}</span>
267
+ </p>
268
+ )}
269
+
270
+ <div className="flex flex-col-reverse gap-2 pt-2 md:flex-row md:justify-end">
271
+ <Button variant="outline" className="min-h-11" onClick={() => { reset(); onClose(); }}>
272
+ Cancel
273
+ </Button>
274
+ <Button className="min-h-11" disabled={!ready} onClick={() => void submit()}>
275
+ {saving ? <><Loader2 className="animate-spin" /> Creating…</> : "Create device"}
276
+ </Button>
277
+ </div>
278
+ </div>
279
+ );
280
+
281
+ if (!open) return null;
282
+
283
+ if (isMobile) {
284
+ return (
285
+ <BottomSheet open onClose={onClose}>
286
+ <div className="max-h-[80vh] overflow-y-auto px-4 pb-4">
287
+ <h2 className="mb-3 text-base font-semibold">New Android device</h2>
288
+ {body}
289
+ </div>
290
+ </BottomSheet>
291
+ );
292
+ }
293
+
294
+ return (
295
+ <Dialog open onOpenChange={(o) => !o && onClose()}>
296
+ <DialogContent className="sm:max-w-lg">
297
+ <DialogHeader>
298
+ <DialogTitle>New Android device</DialogTitle>
299
+ <DialogDescription>Built from a system image already installed on this host.</DialogDescription>
300
+ </DialogHeader>
301
+ <div className="max-h-[70vh] overflow-y-auto pr-1">{body}</div>
302
+ </DialogContent>
303
+ </Dialog>
304
+ );
305
+ }
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Wipe or delete an AVD, with the name typed back.
3
+ *
4
+ * Plan Phase 4: "Wipe/Delete có xác nhận tên máy và hậu quả, chỉ khi stopped". All three parts
5
+ * are here and none of them is decoration:
6
+ *
7
+ * - **The name is typed, not ticked.** These two rows sit next to each other in one list and
8
+ * every AVD's row looks the same; a checkbox confirms that a dialog was open, not which
9
+ * device it was open on.
10
+ * - **The consequence is spelled out** in the device's own terms — what wipe keeps (the AVD and
11
+ * its settings) against what delete does not.
12
+ * - **Stopped only**, and the host checks again: this dialog is not offered for a running or
13
+ * Studio-locked AVD, and the route refuses one anyway, because a list can be a few seconds old.
14
+ */
15
+ import { useEffect, useState } from "react";
16
+ import { api } from "@/lib/api-client";
17
+ import { Button } from "@/components/ui/button";
18
+ import {
19
+ Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle,
20
+ } from "@/components/ui/dialog";
21
+ import { BottomSheet } from "@/components/ui/mobile-bottom-sheet";
22
+ import { Input } from "@/components/ui/input";
23
+ import { Loader2, TriangleAlert } from "@/lib/icons";
24
+ import { useIsMobile } from "@/hooks/use-is-mobile";
25
+
26
+ export type DestructiveAction = "wipe" | "delete";
27
+
28
+ export interface AndroidAvdDestroyDialogProps {
29
+ action: DestructiveAction | null;
30
+ avdId: string;
31
+ deviceName: string;
32
+ onCancel: () => void;
33
+ /** It happened; the list should refresh. `freed` is bytes, for wipe only. */
34
+ onDone: (action: DestructiveAction, message: string) => void;
35
+ }
36
+
37
+ export function AndroidAvdDestroyDialog(
38
+ { action, avdId, deviceName, onCancel, onDone }: AndroidAvdDestroyDialogProps,
39
+ ) {
40
+ const isMobile = useIsMobile();
41
+ const [typed, setTyped] = useState("");
42
+ const [busy, setBusy] = useState(false);
43
+ const [error, setError] = useState<string | null>(null);
44
+
45
+ useEffect(() => { setTyped(""); setError(null); setBusy(false); }, [action, avdId]);
46
+
47
+ if (!action) return null;
48
+
49
+ const isDelete = action === "delete";
50
+ const title = isDelete ? "Delete this device" : "Wipe this device";
51
+ // Exact match, not case-insensitive: two AVDs may differ only in case, and this is the one
52
+ // place where guessing which was meant is the wrong kind of help.
53
+ const armed = typed === deviceName && !busy;
54
+
55
+ const run = async () => {
56
+ if (!armed) return;
57
+ setBusy(true);
58
+ setError(null);
59
+ try {
60
+ if (isDelete) {
61
+ await api.del(`/api/android/avds/${encodeURIComponent(avdId)}`, { confirmName: deviceName });
62
+ onDone("delete", `Deleted ${deviceName}`);
63
+ } else {
64
+ const res = await api.post<{ message: string; freedBytes: number }>(
65
+ `/api/android/avds/${encodeURIComponent(avdId)}/wipe`, { confirmName: deviceName },
66
+ );
67
+ onDone("wipe", res.message);
68
+ }
69
+ } catch (e) {
70
+ setError((e as Error).message);
71
+ setBusy(false);
72
+ }
73
+ };
74
+
75
+ const body = (
76
+ <div className="space-y-4">
77
+ <p className="text-sm leading-relaxed">
78
+ {isDelete ? (
79
+ <>
80
+ <span className="font-medium">{deviceName}</span> and everything in it will be removed
81
+ from disk. The AVD, its settings and its installed apps are gone for good — this is
82
+ not a recycle bin.
83
+ </>
84
+ ) : (
85
+ <>
86
+ Everything inside <span className="font-medium">{deviceName}</span> goes back to
87
+ factory: installed apps, accounts, files and saved snapshots. The device itself and
88
+ its settings stay, and the next start is a cold boot.
89
+ </>
90
+ )}
91
+ </p>
92
+
93
+ <div className="space-y-1.5">
94
+ <label htmlFor="avd-confirm" className="text-sm font-medium">
95
+ Type <span className="font-mono">{deviceName}</span> to confirm
96
+ </label>
97
+ <Input
98
+ id="avd-confirm"
99
+ value={typed}
100
+ onChange={(e) => setTyped(e.target.value)}
101
+ autoComplete="off"
102
+ spellCheck={false}
103
+ autoCapitalize="off"
104
+ className="h-11 font-mono"
105
+ />
106
+ </div>
107
+
108
+ {error && (
109
+ <p className="flex items-start gap-2 text-sm leading-relaxed text-destructive">
110
+ <TriangleAlert className="mt-0.5 size-4 shrink-0" />
111
+ <span className="min-w-0 flex-1">{error}</span>
112
+ </p>
113
+ )}
114
+
115
+ <div className="flex flex-col-reverse gap-2 pt-2 md:flex-row md:justify-end">
116
+ <Button variant="outline" className="min-h-11" onClick={onCancel}>Cancel</Button>
117
+ <Button variant="destructive" className="min-h-11" autoFocus={false}
118
+ disabled={!armed} onClick={() => void run()}>
119
+ {busy
120
+ ? <><Loader2 className="animate-spin" /> {isDelete ? "Deleting…" : "Wiping…"}</>
121
+ : (isDelete ? "Delete device" : "Wipe data")}
122
+ </Button>
123
+ </div>
124
+ </div>
125
+ );
126
+
127
+ if (isMobile) {
128
+ return (
129
+ <BottomSheet open onClose={onCancel}>
130
+ <div className="px-4 pb-4">
131
+ <h2 className="mb-3 text-base font-semibold">{title}</h2>
132
+ {body}
133
+ </div>
134
+ </BottomSheet>
135
+ );
136
+ }
137
+
138
+ return (
139
+ <Dialog open onOpenChange={(o) => !o && onCancel()}>
140
+ <DialogContent className="sm:max-w-md">
141
+ <DialogHeader>
142
+ <DialogTitle>{title}</DialogTitle>
143
+ <DialogDescription>
144
+ {isDelete ? "This cannot be undone." : "A factory reset — the device itself stays."}
145
+ </DialogDescription>
146
+ </DialogHeader>
147
+ {body}
148
+ </DialogContent>
149
+ </Dialog>
150
+ );
151
+ }
@@ -0,0 +1,94 @@
1
+ /**
2
+ * The device's clipboard, both directions.
3
+ *
4
+ * Typing into a guest works already (`sendText`), so this is not about text entry — it is about
5
+ * the two cases typing cannot reach: getting something *out* of the device (a token an app
6
+ * printed, a deep link) and putting something in that the guest's own keyboard cannot produce.
7
+ *
8
+ * Nothing here is logged, on either side. The plan's Phase 3 gate says so explicitly, and the
9
+ * server route says so again where it would be easiest to slip.
10
+ */
11
+ import { useCallback, useState } from "react";
12
+ import { api } from "@/lib/api-client";
13
+ import { Button } from "@/components/ui/button";
14
+ import { Check, Clipboard, Loader2, Upload } from "@/lib/icons";
15
+
16
+ export interface AndroidClipboardPanelProps {
17
+ deviceId: string;
18
+ }
19
+
20
+ export function AndroidClipboardPanel({ deviceId }: AndroidClipboardPanelProps) {
21
+ const [fromDevice, setFromDevice] = useState<string | null>(null);
22
+ const [toDevice, setToDevice] = useState("");
23
+ const [busy, setBusy] = useState<"read" | "write" | null>(null);
24
+ const [sent, setSent] = useState(false);
25
+ const [error, setError] = useState<string | null>(null);
26
+
27
+ const read = useCallback(async () => {
28
+ setBusy("read"); setError(null);
29
+ try {
30
+ const res = await api.get<{ text: string }>(`/api/android/devices/${encodeURIComponent(deviceId)}/clipboard`);
31
+ setFromDevice(res.text);
32
+ } catch (e) {
33
+ setError((e as Error).message);
34
+ } finally {
35
+ setBusy(null);
36
+ }
37
+ }, [deviceId]);
38
+
39
+ const write = useCallback(async () => {
40
+ setBusy("write"); setError(null);
41
+ try {
42
+ await api.post(`/api/android/devices/${encodeURIComponent(deviceId)}/clipboard`, { text: toDevice });
43
+ setSent(true);
44
+ setTimeout(() => setSent(false), 1500);
45
+ } catch (e) {
46
+ setError((e as Error).message);
47
+ } finally {
48
+ setBusy(null);
49
+ }
50
+ }, [deviceId, toDevice]);
51
+
52
+ return (
53
+ <div className="flex h-full min-h-0 flex-col gap-4 overflow-auto p-3">
54
+ <section className="flex flex-col gap-2">
55
+ <div className="flex items-center gap-2">
56
+ <h3 className="flex-1 text-sm font-medium">From the device</h3>
57
+ <Button variant="secondary" className="h-11 md:h-9" onClick={() => void read()} disabled={busy !== null}>
58
+ {busy === "read" ? <Loader2 className="animate-spin" /> : <Clipboard />} Read
59
+ </Button>
60
+ </div>
61
+ {fromDevice !== null && (
62
+ <textarea
63
+ readOnly
64
+ value={fromDevice}
65
+ rows={3}
66
+ placeholder="(the device's clipboard is empty)"
67
+ className="w-full resize-y rounded-md border bg-muted/40 p-2 font-mono text-xs"
68
+ />
69
+ )}
70
+ </section>
71
+
72
+ <section className="flex flex-col gap-2 border-t pt-3">
73
+ <h3 className="text-sm font-medium">To the device</h3>
74
+ <textarea
75
+ value={toDevice}
76
+ onChange={(e) => setToDevice(e.target.value)}
77
+ rows={3}
78
+ placeholder="Text to put on the device's clipboard"
79
+ className="w-full resize-y rounded-md border bg-background p-2 font-mono text-xs"
80
+ />
81
+ <Button className="h-11 self-start md:h-9" onClick={() => void write()}
82
+ disabled={busy !== null || toDevice.length === 0}>
83
+ {sent ? <Check /> : busy === "write" ? <Loader2 className="animate-spin" /> : <Upload />}
84
+ {sent ? "Sent" : "Send to device"}
85
+ </Button>
86
+ <p className="text-xs text-muted-foreground">
87
+ Paste it in the guest with a long press, or with the keyboard's paste key.
88
+ </p>
89
+ </section>
90
+
91
+ {error && <p className="text-sm text-destructive">{error}</p>}
92
+ </div>
93
+ );
94
+ }
@@ -0,0 +1,128 @@
1
+ /**
2
+ * The device's own buttons, plus the session controls that belong beside them.
3
+ *
4
+ * One component for both form factors rather than two: the *contents* are identical — an
5
+ * emulator has the same three navigation buttons whatever is looking at it — and only the
6
+ * placement differs. Below `md` it sits in the thumb zone at the bottom of the viewer (design
7
+ * guidelines §9: navigation bars go to the bottom, not the top); above it, the same row sits in
8
+ * the toolbar. Two copies would drift.
9
+ */
10
+ import { useState } from "react";
11
+ import { Button } from "@/components/ui/button";
12
+ import {
13
+ ArrowLeft, Circle, Square, Keyboard, RotateCw, Power, Volume2, Volume1, MoreHorizontal,
14
+ } from "@/lib/icons";
15
+ import { cn } from "@/lib/utils";
16
+ import type { AndroidClientMessage, AndroidQuality } from "../../../shared/android-protocol";
17
+
18
+ export interface AndroidControlsProps {
19
+ send: (message: AndroidClientMessage) => void;
20
+ /** Without the lease every button here is a no-op on the server, so they are disabled. */
21
+ controller: boolean;
22
+ rotation: 0 | 90 | 180 | 270;
23
+ quality: AndroidQuality;
24
+ onQualityChange: (quality: AndroidQuality) => void;
25
+ onKeyboard: () => void;
26
+ layout: "bar" | "toolbar";
27
+ }
28
+
29
+ const QUALITY_LABELS: Record<AndroidQuality, string> = {
30
+ low: "Low", balanced: "Balanced", high: "High",
31
+ };
32
+
33
+ export function AndroidControls(props: AndroidControlsProps) {
34
+ const { send, controller, rotation, quality, onQualityChange, onKeyboard, layout } = props;
35
+ const [extrasOpen, setExtrasOpen] = useState(false);
36
+ const bar = layout === "bar";
37
+
38
+ // 44x44 minimum on touch (design guidelines §3); the toolbar variant is desktop-only and may
39
+ // be tighter, but never below 36px.
40
+ const buttonClass = bar ? "size-11" : "size-9";
41
+
42
+ const key = (k: "home" | "back" | "recent" | "power" | "volume-up" | "volume-down") =>
43
+ () => send({ type: "hardware", key: k });
44
+
45
+ return (
46
+ <div className={cn("flex items-center gap-2", containerClass(bar))}>
47
+ <Button variant="ghost" size="icon" className={buttonClass} disabled={!controller}
48
+ onClick={key("back")} title="Back" aria-label="Back">
49
+ <ArrowLeft />
50
+ </Button>
51
+ <Button variant="ghost" size="icon" className={buttonClass} disabled={!controller}
52
+ onClick={key("home")} title="Home" aria-label="Home">
53
+ <Circle />
54
+ </Button>
55
+ <Button variant="ghost" size="icon" className={buttonClass} disabled={!controller}
56
+ onClick={key("recent")} title="Recent apps" aria-label="Recent apps">
57
+ <Square />
58
+ </Button>
59
+
60
+ <span className="mx-1 h-6 w-px shrink-0 bg-border" aria-hidden />
61
+
62
+ <Button variant="ghost" size="icon" className={buttonClass} disabled={!controller}
63
+ onClick={onKeyboard} title="Keyboard" aria-label="Keyboard">
64
+ <Keyboard />
65
+ </Button>
66
+ <Button variant="ghost" size="icon" className={buttonClass} disabled={!controller}
67
+ onClick={() => send({ type: "rotate", rotation: nextRotation(rotation) })}
68
+ title={`Rotate (now ${rotation}°)`} aria-label="Rotate">
69
+ <RotateCw />
70
+ </Button>
71
+
72
+ {/* The rarely-used keys stay behind one more tap: a mis-hit Power on a phone is a guest
73
+ that goes to sleep mid-demo, and §9 puts destructive things out of easy reach. */}
74
+ <Button variant="ghost" size="icon" className={buttonClass} disabled={!controller}
75
+ onClick={() => setExtrasOpen((v) => !v)} title="More keys" aria-label="More keys"
76
+ aria-expanded={extrasOpen}>
77
+ <MoreHorizontal />
78
+ </Button>
79
+ {extrasOpen && (
80
+ <>
81
+ <Button variant="ghost" size="icon" className={buttonClass} disabled={!controller}
82
+ onClick={key("volume-up")} title="Volume up" aria-label="Volume up">
83
+ <Volume2 />
84
+ </Button>
85
+ <Button variant="ghost" size="icon" className={buttonClass} disabled={!controller}
86
+ onClick={key("volume-down")} title="Volume down" aria-label="Volume down">
87
+ <Volume1 />
88
+ </Button>
89
+ <Button variant="ghost" size="icon" className={buttonClass} disabled={!controller}
90
+ onClick={key("power")} title="Power" aria-label="Power">
91
+ <Power />
92
+ </Button>
93
+ </>
94
+ )}
95
+
96
+ <span className="flex-1" />
97
+
98
+ {/* A native select, per design guidelines §8 — a phone gets its own picker for free. */}
99
+ <label className="sr-only" htmlFor="android-quality">Quality</label>
100
+ <select
101
+ id="android-quality"
102
+ className={cn(
103
+ "rounded-md border bg-background px-2 text-sm",
104
+ bar ? "h-11" : "h-9",
105
+ )}
106
+ value={quality}
107
+ disabled={!controller}
108
+ onChange={(e) => onQualityChange(e.target.value as AndroidQuality)}
109
+ >
110
+ {(Object.keys(QUALITY_LABELS) as AndroidQuality[]).map((q) => (
111
+ <option key={q} value={q}>{QUALITY_LABELS[q]}</option>
112
+ ))}
113
+ </select>
114
+ </div>
115
+ );
116
+ }
117
+
118
+ function containerClass(bar: boolean): string {
119
+ return bar
120
+ ? "w-full justify-center border-t bg-background/95 px-3 py-2 backdrop-blur"
121
+ : "px-2 py-1";
122
+ }
123
+
124
+ /** 0 -> 90 -> 180 -> 270 -> 0. One button rather than four: a phone-shaped toolbar has no room
125
+ * for a rotation picker, and every orientation is two taps away at worst. */
126
+ export function nextRotation(current: 0 | 90 | 180 | 270): 0 | 90 | 180 | 270 {
127
+ return (((current + 90) % 360) as 0 | 90 | 180 | 270);
128
+ }