@hienlh/ppm 0.18.16 → 0.19.0

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 (508) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/CLAUDE.md +4 -0
  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 +2 -1
  6. package/dist/web/assets/{adaptive-context-menu-BbuRVTLn.js → adaptive-context-menu-JfuUSqbe.js} +1 -1
  7. package/dist/web/assets/{ai-resource-editor-Cm4n3VXt.js → ai-resource-editor-MfdJKVM5.js} +1 -1
  8. package/dist/web/assets/{ai-settings-section-BEM1z7kp.js → ai-settings-section-CJE7jFsj.js} +1 -1
  9. package/dist/web/assets/{angular-html-BlcrLjUx.js → angular-html-VzxSzTCy.js} +1 -1
  10. package/dist/web/assets/{angular-ts-BUjtceAJ.js → angular-ts-CmJ4bmzM.js} +1 -1
  11. package/dist/web/assets/api-client-DLWwuUkJ.js +1 -0
  12. package/dist/web/assets/{api-client-BzWM7XPR.js → api-client-qRWu6_s5.js} +1 -1
  13. package/dist/web/assets/api-settings-BFuaZrNC.js +1 -0
  14. package/dist/web/assets/{api-settings-Cubuqjsu.js → api-settings-CPg4yGvS.js} +1 -1
  15. package/dist/web/assets/{api-themes-YsZgf6Xj.js → api-themes-DsAp8m7m.js} +1 -1
  16. package/dist/web/assets/{apl-B3g0v1pk.js → apl-do17bnVx.js} +1 -1
  17. package/dist/web/assets/{astro-FRnyn7cH.js → astro-ClY0e0OA.js} +1 -1
  18. package/dist/web/assets/{audio-preview-DWTRm4HC.js → audio-preview-Fw935Mok.js} +1 -1
  19. package/dist/web/assets/{blade-NLdOqUKw.js → blade-BXSdXDGq.js} +1 -1
  20. package/dist/web/assets/{bot-D6sx11y0.js → bot-CWZAuloY.js} +1 -1
  21. package/dist/web/assets/{bsl-CenHZfaU.js → bsl-MxsMKDUZ.js} +1 -1
  22. package/dist/web/assets/{button-Cq8yixjf.js → button-CrwgGd1y.js} +1 -1
  23. package/dist/web/assets/{cairo-B_WR0aiQ.js → cairo-4CIrzX_p.js} +1 -1
  24. package/dist/web/assets/chat-tab-bSOVoC5N.js +15 -0
  25. package/dist/web/assets/{chevron-down-Dp0rpHb8.js → chevron-down-D7CbB5f6.js} +1 -1
  26. package/dist/web/assets/chevron-right-D7Pn5agw.js +1 -0
  27. package/dist/web/assets/circle-4g2coECH.js +1 -0
  28. package/dist/web/assets/{circle-question-mark-CzR7YRc-.js → circle-question-mark-BBzFZrNh.js} +1 -1
  29. package/dist/web/assets/clock-DbiNiwCG.js +1 -0
  30. package/dist/web/assets/{cobol-DOynhWYj.js → cobol-phPOE89d.js} +1 -1
  31. package/dist/web/assets/code-DdKXqtf_.js +1 -0
  32. package/dist/web/assets/code-editor-COTa37oe.js +10 -0
  33. package/dist/web/assets/{coffee-Bn6dbw4P.js → coffee-Cjuukfz3.js} +1 -1
  34. package/dist/web/assets/{conflict-editor-Bi2RWfKQ.js → conflict-editor-Ni0PPsKu.js} +1 -1
  35. package/dist/web/assets/{context-menu-DfWZAxQy.js → context-menu-BEAa1qhd.js} +1 -1
  36. package/dist/web/assets/{copy-BewHj66E.js → copy-CbPdNp7E.js} +1 -1
  37. package/dist/web/assets/{cpp-CoZR9t8r.js → cpp-CT7tGBC3.js} +1 -1
  38. package/dist/web/assets/{createLucideIcon-D8pyde1A.js → createLucideIcon-ConhwJnb.js} +1 -1
  39. package/dist/web/assets/{crystal-BSheONSg.js → crystal-D8U1Qnqh.js} +1 -1
  40. package/dist/web/assets/{csv-preview-DxGZ1qg5.js → csv-preview-Coc2oed1.js} +1 -1
  41. package/dist/web/assets/{data-grid-overlay-editor-gwPoVSCU.js → data-grid-overlay-editor-B8WQkZDB.js} +1 -1
  42. package/dist/web/assets/{data-grid-types-Dq_H1IPa.js → data-grid-types-KpjaKAci.js} +1 -1
  43. package/dist/web/assets/{database-Y3ESDK4-.js → database-BqyeGogo.js} +1 -1
  44. package/dist/web/assets/{database-viewer-DSPRzZny.js → database-viewer-Dta7lSSk.js} +1 -1
  45. package/dist/web/assets/{dialog-BSfuiTkJ.js → dialog-DrL3WEXd.js} +1 -1
  46. package/dist/web/assets/{diff-viewer-YbpnNv5Y.js → diff-viewer-CT0lmDc9.js} +1 -1
  47. package/dist/web/assets/{dist-aDSxTR_v.js → dist-C2_dKAa_.js} +1 -1
  48. package/dist/web/assets/{docx-preview-DqVDOMmv.js → docx-preview-76KDw1eE.js} +1 -1
  49. package/dist/web/assets/{download-DG3_9xXy.js → download-DAF7SXcf.js} +1 -1
  50. package/dist/web/assets/{edge-DTtoslQu.js → edge-Db4ze6bw.js} +1 -1
  51. package/dist/web/assets/{edit-diff-preview-CXSbSUBX.js → edit-diff-preview-DyC_-4My.js} +1 -1
  52. package/dist/web/assets/{elixir-CIBCZ3Zi.js → elixir-RZPZ0472.js} +1 -1
  53. package/dist/web/assets/{elm-BLmb7K1H.js → elm-DNZcQt_7.js} +1 -1
  54. package/dist/web/assets/{erb-BBY8ApRd.js → erb-DRONsZXs.js} +1 -1
  55. package/dist/web/assets/{erlang-D5Zb5_P0.js → erlang-DeG0-QwJ.js} +1 -1
  56. package/dist/web/assets/{esm-6F8v1jLL.js → esm-CZy-sSoK.js} +1 -1
  57. package/dist/web/assets/explorer-body-BGjR2Gbt.js +2 -0
  58. package/dist/web/assets/explorer-window-content-DAhHh5EQ.js +1 -0
  59. package/dist/web/assets/{extension-store-Bhu--fPX.js → extension-store-ClQpnxQr.js} +1 -1
  60. package/dist/web/assets/{extension-webview-BSh6KGd7.js → extension-webview-BW5GuBnH.js} +1 -1
  61. package/dist/web/assets/{external-link-B6Hl0U4X.js → external-link-ojDHcQxQ.js} +1 -1
  62. package/dist/web/assets/{eye-off-CUdzgmX8.js → eye-off-Rq1n2-oe.js} +1 -1
  63. package/dist/web/assets/{file-exclamation-point-JzhTU2TX.js → file-exclamation-point-Bpg-zDPP.js} +1 -1
  64. package/dist/web/assets/{file-store-6HW2Lfl4.js → file-store-28e44KP3.js} +1 -1
  65. package/dist/web/assets/{folder-symlink-Dx1hOk8X.js → folder-symlink-LD-mr54V.js} +1 -1
  66. package/dist/web/assets/{fortran-fixed-form-CBdwkEUJ.js → fortran-fixed-form-CK5VRJNd.js} +1 -1
  67. package/dist/web/assets/{fsharp-AT4XdVYH.js → fsharp-TVVgzHWp.js} +1 -1
  68. package/dist/web/assets/{gdresource-D852fWBR.js → gdresource-DiUcyewI.js} +1 -1
  69. package/dist/web/assets/{git-commit-DWfWAyxy.js → git-commit-CfOruFrg.js} +1 -1
  70. package/dist/web/assets/{git-log-panel-CkTiR70w.js → git-log-panel-D1-YXZ0O.js} +1 -1
  71. package/dist/web/assets/{git-rebase-C6opk8gr.js → git-rebase-Bxyr24oc.js} +1 -1
  72. package/dist/web/assets/{glide-data-grid-BB5mCaCD.js → glide-data-grid-BSsqxe2v.js} +6 -6
  73. package/dist/web/assets/{glimmer-js-DLi5mWYO.js → glimmer-js-CT5aJxz_.js} +1 -1
  74. package/dist/web/assets/{glimmer-ts-D-scFX30.js → glimmer-ts-CRjP9Yey.js} +1 -1
  75. package/dist/web/assets/{glsl-dbQzF4iA.js → glsl-BX2--oI_.js} +1 -1
  76. package/dist/web/assets/{graphql-D1m5Wen2.js → graphql-BIQsvBxQ.js} +1 -1
  77. package/dist/web/assets/{group-chat-tab-BPIOdFJw.js → group-chat-tab-CMtzoaFc.js} +1 -1
  78. package/dist/web/assets/{hack-CqYmpO7u.js → hack-CTWk0k1m.js} +1 -1
  79. package/dist/web/assets/{haml-BXzKWEag.js → haml-DGh27hXc.js} +1 -1
  80. package/dist/web/assets/{hand-x0ejq3fS.js → hand-DKSxDa5c.js} +1 -1
  81. package/dist/web/assets/{handlebars-DXnrtHtq.js → handlebars-BJL1jNrG.js} +1 -1
  82. package/dist/web/assets/{html-DhgQJ-xL.js → html-DALyDNqU.js} +1 -1
  83. package/dist/web/assets/{html-derivative-xZRft-YE.js → html-derivative-B4XcU57e.js} +1 -1
  84. package/dist/web/assets/{http-B53mSj14.js → http-DfMo7nNu.js} +1 -1
  85. package/dist/web/assets/{hurl-CsomVy04.js → hurl-CGc_n2Qf.js} +1 -1
  86. package/dist/web/assets/{hxml-LffjWwKA.js → hxml-40zZ1t4x.js} +1 -1
  87. package/dist/web/assets/{image-preview-CNA6XMNS.js → image-preview-Cp3XB8UO.js} +1 -1
  88. package/dist/web/assets/index-BkHlMfPD.js +52 -0
  89. package/dist/web/assets/index-SFFv2-VM.css +2 -0
  90. package/dist/web/assets/{input-D0ol5jW0.js → input-DTmI80l2.js} +1 -1
  91. package/dist/web/assets/{jinja-CCHURl7V.js → jinja-D5zuIn9u.js} +1 -1
  92. package/dist/web/assets/{jison-7qBd8dnR.js → jison-Dxut9QwA.js} +1 -1
  93. package/dist/web/assets/{julia-BY9pnTcy.js → julia-Cu85tTPo.js} +1 -1
  94. package/dist/web/assets/{just-D7wRSNRr.js → just-CctM4uLa.js} +1 -1
  95. package/dist/web/assets/{keybindings-store-BGkB72gM.js → keybindings-store-Bi0q9x4Q.js} +1 -1
  96. package/dist/web/assets/keybindings-store-_KfovSz7.js +1 -0
  97. package/dist/web/assets/{keyboard-DBBaoQSu.js → keyboard-AdwmYDSu.js} +1 -1
  98. package/dist/web/assets/{latex-RgsCae55.js → latex-kpwS_CC7.js} +1 -1
  99. package/dist/web/assets/{lib-v9x_qXgI.js → lib-Ca7ai8xa.js} +1 -1
  100. package/dist/web/assets/{liquid-CnDFzvOZ.js → liquid-B8Rr0xue.js} +1 -1
  101. package/dist/web/assets/loader-circle-DD65e9-7.js +1 -0
  102. package/dist/web/assets/{lock-CNxUGpTk.js → lock-BEtt_eTO.js} +1 -1
  103. package/dist/web/assets/{lua-0lj5W8sm.js → lua-C_3gixwM.js} +1 -1
  104. package/dist/web/assets/markdown-renderer-DroTmKbJ.js +1 -0
  105. package/dist/web/assets/markdown-renderer-ntDi3Tx-.js +3 -0
  106. package/dist/web/assets/{marko-CQgqAhbV.js → marko-B28rvNL-.js} +1 -1
  107. package/dist/web/assets/{mdc-BkIcTiIy.js → mdc-DQ6-STFO.js} +1 -1
  108. package/dist/web/assets/{media-url-DDciwvLU.js → media-url-BjvbfTTl.js} +1 -1
  109. package/dist/web/assets/{mobile-bottom-sheet-pjcHKorA.js → mobile-bottom-sheet-DXn5g2LB.js} +1 -1
  110. package/dist/web/assets/mobile-explorer-sheet-BP6iU4ma.js +1 -0
  111. package/dist/web/assets/{monitor-CxPV_fQI.js → monitor-CXI_XpWJ.js} +1 -1
  112. package/dist/web/assets/{monitor-smartphone-DLZ6Q05s.js → monitor-smartphone-C1wUPOSg.js} +1 -1
  113. package/dist/web/assets/{nextflow-DvIW68DS.js → nextflow-B1xq2O8L.js} +1 -1
  114. package/dist/web/assets/{nginx-O-hft0gG.js → nginx-xsLxRGxO.js} +1 -1
  115. package/dist/web/assets/{nim-BxrR4HwQ.js → nim-wqcek3WO.js} +1 -1
  116. package/dist/web/assets/notification-store-II5Kw9rD.js +1 -0
  117. package/dist/web/assets/{number-overlay-editor-BoLRYuZT.js → number-overlay-editor-1c4sgZJt.js} +1 -1
  118. package/dist/web/assets/{overview-panel-l3g1gT5e.js → overview-panel-Cr2xsHc6.js} +1 -1
  119. package/dist/web/assets/{panel-store-UOV8wrfR.js → panel-store-Dv28vz3L.js} +1 -1
  120. package/dist/web/assets/{pdf-preview-B2CJ_EKv.js → pdf-preview-DKeIIJTv.js} +1 -1
  121. package/dist/web/assets/{pencil-BlIIg6RH.js → pencil-CHKLOgLs.js} +1 -1
  122. package/dist/web/assets/{perl-Da-uNqmL.js → perl-BH54gN9q.js} +1 -1
  123. package/dist/web/assets/{php-JMu45mEx.js → php-Br5ZrEZm.js} +1 -1
  124. package/dist/web/assets/play-CPbVzGgq.js +1 -0
  125. package/dist/web/assets/{plus-BR0qewJI.js → plus-BxqD2FIZ.js} +1 -1
  126. package/dist/web/assets/portal-container-context-BmG4--I-.js +1 -0
  127. package/dist/web/assets/{postgres-viewer-23xdZYxp.js → postgres-viewer-BJPr_FYL.js} +3 -3
  128. package/dist/web/assets/{process-table-CUsPf7wv.js → process-table-BqTcAJ-h.js} +1 -1
  129. package/dist/web/assets/{project-store-DTUCS65m.js → project-store-KHjFTCxf.js} +1 -1
  130. package/dist/web/assets/{pug-CLzyQhia.js → pug-8VCubZ3H.js} +1 -1
  131. package/dist/web/assets/{qml-BHXfGB1l.js → qml-D8AF1RZ6.js} +1 -1
  132. package/dist/web/assets/{razor-BqetJLN3.js → razor-6v042IJW.js} +1 -1
  133. package/dist/web/assets/{react-B549sK_T.js → react-DHs6efqI.js} +1 -1
  134. package/dist/web/assets/{refresh-cw-BQU5g2CY.js → refresh-cw-BALzw7L3.js} +1 -1
  135. package/dist/web/assets/remote-desktop-mobile-sheet-CYZUjYTO.js +2 -0
  136. package/dist/web/assets/remote-desktop-mobile-view-DaoB49k4.js +1 -0
  137. package/dist/web/assets/remote-desktop-readiness-gate--LJgsuoj.js +1 -0
  138. package/dist/web/assets/remote-desktop-window-content-Cihx4PNZ.js +1 -0
  139. package/dist/web/assets/{rotate-ccw-CpjRo90J.js → rotate-ccw-CuvNBM80.js} +1 -1
  140. package/dist/web/assets/{rst-iQdJZ0BP.js → rst-mTGFo4AQ.js} +1 -1
  141. package/dist/web/assets/{ruby-CNwzZGdH.js → ruby-BBaOP636.js} +1 -1
  142. package/dist/web/assets/{run-in-terminal-CBXmVGWa.js → run-in-terminal-Zl5iIx-B.js} +1 -1
  143. package/dist/web/assets/{sas-BpBIT2eR.js → sas-D5QEkIH2.js} +1 -1
  144. package/dist/web/assets/{save-DYLiVzU8.js → save-Dxq-Zepc.js} +1 -1
  145. package/dist/web/assets/{scroll-area-lc0ZHORr.js → scroll-area-B5yhPpZo.js} +1 -1
  146. package/dist/web/assets/{scss-D1MotuEp.js → scss-C5Mu6y2u.js} +1 -1
  147. package/dist/web/assets/{send-C_sLFt56.js → send-Cits8q0l.js} +1 -1
  148. package/dist/web/assets/{send-to-chat-DJAdlvJ8.js → send-to-chat-Cv7ikFUC.js} +1 -1
  149. package/dist/web/assets/{settings-store-zbPkG7o-.js → settings-store-Dp4i8zeA.js} +2 -2
  150. package/dist/web/assets/{settings-tab-xyRpt2gq.js → settings-tab-BtMvvKAF.js} +1 -1
  151. package/dist/web/assets/settings-tab-UPPycGRF.js +1 -0
  152. package/dist/web/assets/{shaderlab-BNqmcPQi.js → shaderlab-mLnpMpN3.js} +1 -1
  153. package/dist/web/assets/{shallow-CmPMAPte.js → shallow-BBJ_qoEF.js} +1 -1
  154. package/dist/web/assets/{shellsession-BBLOVOO-.js → shellsession-qdcDfUuk.js} +1 -1
  155. package/dist/web/assets/{shield-check-CUI36aSc.js → shield-check-CH7F1P7m.js} +1 -1
  156. package/dist/web/assets/{shield-off-GdkkVhb3.js → shield-off-C9HiF0-6.js} +1 -1
  157. package/dist/web/assets/{soy-BpTGo9uy.js → soy-CGGf1G43.js} +1 -1
  158. package/dist/web/assets/{sparkles-Dh-_J2Rd.js → sparkles-BnuLH6Ck.js} +1 -1
  159. package/dist/web/assets/{sparql-D7A3yKob.js → sparql-tQHB50VD.js} +1 -1
  160. package/dist/web/assets/{sql-query-editor-BE3mLYyz.js → sql-query-editor-DZqnYIB7.js} +1 -1
  161. package/dist/web/assets/{sqlite-viewer-wN5RP8sd.js → sqlite-viewer-DXutos0e.js} +1 -1
  162. package/dist/web/assets/{stata-B7N56ptS.js → stata-DVQNLMcR.js} +1 -1
  163. package/dist/web/assets/{surrealql-CTmnD-lv.js → surrealql-lu576tBi.js} +1 -1
  164. package/dist/web/assets/{svelte-BppwY9TR.js → svelte-CSCrOBjT.js} +1 -1
  165. package/dist/web/assets/{switch-BmjKoIpR.js → switch-BPol3BFC.js} +1 -1
  166. package/dist/web/assets/{system-monitor-body-B2M0MC6y.js → system-monitor-body-Ct8_GX_f.js} +2 -2
  167. package/dist/web/assets/system-monitor-tab-DC_EerdI.js +1 -0
  168. package/dist/web/assets/system-monitor-window-content-D25DMNpw.js +1 -0
  169. package/dist/web/assets/{tab-host-window-content-DKev3GJz.js → tab-host-window-content-C5kYP0-z.js} +1 -1
  170. package/dist/web/assets/{tab-store-DzYSsw5a.js → tab-store-DS3YlUwR.js} +1 -1
  171. package/dist/web/assets/{table-D5Q4ap4W.js → table-BXDyImqk.js} +1 -1
  172. package/dist/web/assets/team-member-sheet-DvmioeQL.js +2 -0
  173. package/dist/web/assets/{team-member-window-content-CK08f5cK.js → team-member-window-content-BJ_XFw8-.js} +1 -1
  174. package/dist/web/assets/{templ-CvTMKKra.js → templ-CPt8AS4Q.js} +1 -1
  175. package/dist/web/assets/{terminal-tab-DZ3QiYcs.js → terminal-tab-8bKQ3CWR.js} +3 -3
  176. package/dist/web/assets/{tex--c4POEUE.js → tex-BrTByT7i.js} +1 -1
  177. package/dist/web/assets/{text-wrap-DBvQD3kc.js → text-wrap-CzMjB6GD.js} +1 -1
  178. package/dist/web/assets/{tool-cards-Bw1P0MKU.js → tool-cards-pIu_cmEL.js} +4 -4
  179. package/dist/web/assets/{trash-2-By4VMbC4.js → trash-2-BjShZ7if.js} +1 -1
  180. package/dist/web/assets/{triangle-alert-CUTURFbG.js → triangle-alert-qQ6Plydp.js} +1 -1
  181. package/dist/web/assets/{ts-tags--kx7D-Hw.js → ts-tags-DSqEZ7A8.js} +1 -1
  182. package/dist/web/assets/{twig-UG9uMHGd.js → twig-DW9qc1bf.js} +1 -1
  183. package/dist/web/assets/{upload-BNlX8D07.js → upload-7Zs6oaK5.js} +1 -1
  184. package/dist/web/assets/{use-blob-url-Bdxopwwc.js → use-blob-url-DKbV7PM_.js} +1 -1
  185. package/dist/web/assets/{use-coarse-long-press-BmI_GVW5.js → use-coarse-long-press-DGyC48NF.js} +1 -1
  186. package/dist/web/assets/{use-is-mobile-B9UZNwSm.js → use-is-mobile-ClE-UcPF.js} +1 -1
  187. package/dist/web/assets/{use-monaco-theme-BOmd2wpe.js → use-monaco-theme-DaxAk3gB.js} +1 -1
  188. package/dist/web/assets/use-open-team-member-CopUuDHG.js +1 -0
  189. package/dist/web/assets/use-remote-desktop-display-choice-BUA_1eaH.js +1 -0
  190. package/dist/web/assets/{use-visual-viewport-Sfb6CZ80.js → use-visual-viewport-CCX21c9w.js} +1 -1
  191. package/dist/web/assets/{use-websocket-B7Xj-Q4W.js → use-websocket-CWpG7PWN.js} +1 -1
  192. package/dist/web/assets/vendor-markdown-CKTlmXbz.js +291 -0
  193. package/dist/web/assets/{vendor-ui-CaeeFO0B.js → vendor-ui-CXJtBULr.js} +1 -1
  194. package/dist/web/assets/{video-preview-B0nbTGjQ.js → video-preview-C2_fdunE.js} +1 -1
  195. package/dist/web/assets/{vue-jjTomlbt.js → vue-CJNDL1g-.js} +1 -1
  196. package/dist/web/assets/{vue-html-8KEW6-I9.js → vue-html-CG5BKtq1.js} +1 -1
  197. package/dist/web/assets/{vue-vine-C5tohCkU.js → vue-vine-Dd5HZ7T5.js} +1 -1
  198. package/dist/web/assets/{window-pip-registry-FdZdGF7h.js → window-pip-registry-9H6Y-HqR.js} +1 -1
  199. package/dist/web/assets/{window-store-D0x_fxr0.js → window-store-CNzkeUyO.js} +1 -1
  200. package/dist/web/assets/x-Dzu9ecti.js +1 -0
  201. package/dist/web/assets/{xml-ByFFosqL.js → xml-DTOm5niz.js} +1 -1
  202. package/dist/web/assets/{xsl-CZUwiLkj.js → xsl-BD_kBC04.js} +1 -1
  203. package/dist/web/index.html +60 -60
  204. package/dist/web/monacoeditorwork/css.worker.bundle.js +122 -122
  205. package/dist/web/monacoeditorwork/editor.worker.bundle.js +78 -78
  206. package/dist/web/monacoeditorwork/html.worker.bundle.js +110 -110
  207. package/dist/web/monacoeditorwork/json.worker.bundle.js +108 -108
  208. package/dist/web/monacoeditorwork/ts.worker.bundle.js +81 -81
  209. package/dist/web/sw.js +1 -1
  210. package/docs/system-architecture.md +19 -0
  211. package/package.json +1 -1
  212. package/scripts/check-ppm-dir-usage.sh +0 -0
  213. package/scripts/install.sh +0 -0
  214. package/scripts/release.sh +0 -0
  215. package/src/server/routes/remote-desktop.ts +23 -6
  216. package/src/server/ws/remote-desktop.ts +4 -2
  217. package/src/services/edge-forwarder.ts +191 -191
  218. package/src/services/remote-desktop/remote-desktop-capture-input.ts +62 -0
  219. package/src/services/remote-desktop/remote-desktop-capture.ts +29 -12
  220. package/src/services/remote-desktop/remote-desktop-cg-key-map.ts +45 -0
  221. package/src/services/remote-desktop/remote-desktop-displays.ts +90 -0
  222. package/src/services/remote-desktop/remote-desktop-encoder-args.ts +7 -0
  223. package/src/services/remote-desktop/remote-desktop-input-backend.ts +36 -0
  224. package/src/services/remote-desktop/remote-desktop-input-darwin.ts +200 -0
  225. package/src/services/remote-desktop/remote-desktop-input-win32.ts +243 -0
  226. package/src/services/remote-desktop/remote-desktop-input.ts +43 -193
  227. package/src/services/remote-desktop/remote-desktop-macos-permissions.ts +62 -0
  228. package/src/services/remote-desktop/remote-desktop-requirements.ts +124 -0
  229. package/src/services/remote-desktop/remote-desktop-session.ts +16 -5
  230. package/src/services/windows-process-tree.ts +351 -351
  231. package/src/web/components/remote-desktop/remote-desktop-mobile-sheet.tsx +6 -3
  232. package/src/web/components/remote-desktop/remote-desktop-mobile-toolbar.tsx +12 -1
  233. package/src/web/components/remote-desktop/remote-desktop-mobile-view.tsx +5 -1
  234. package/src/web/components/remote-desktop/remote-desktop-readiness-gate.tsx +128 -0
  235. package/src/web/components/remote-desktop/remote-desktop-window-content.tsx +22 -2
  236. package/src/web/components/remote-desktop/use-remote-desktop-available.ts +7 -24
  237. package/src/web/components/remote-desktop/use-remote-desktop-connection.ts +10 -3
  238. package/src/web/components/remote-desktop/use-remote-desktop-display-choice.ts +30 -0
  239. package/src/web/components/remote-desktop/use-remote-desktop-readiness.ts +82 -0
  240. package/src/web/components/remote-desktop/use-remote-desktop-virtual-keyboard.ts +61 -10
  241. package/.rdtest2-cf.out +0 -0
  242. package/dist/web/assets/api-client-D1CePgQ4.js +0 -1
  243. package/dist/web/assets/api-settings-CNtKkHqW.js +0 -1
  244. package/dist/web/assets/chat-tab-C2e6Pwc2.js +0 -15
  245. package/dist/web/assets/chevron-right-De-Wz5Lz.js +0 -1
  246. package/dist/web/assets/circle-BCy0AUCo.js +0 -1
  247. package/dist/web/assets/clock-C1CQUQPZ.js +0 -1
  248. package/dist/web/assets/code-DJgYrFzm.js +0 -1
  249. package/dist/web/assets/code-editor-DWtv_VDo.js +0 -10
  250. package/dist/web/assets/explorer-body-DpQndBVw.js +0 -2
  251. package/dist/web/assets/explorer-window-content-m0K6Mtcg.js +0 -1
  252. package/dist/web/assets/index-BHdtSKzb.js +0 -52
  253. package/dist/web/assets/index-DN9XUQ8D.css +0 -2
  254. package/dist/web/assets/keybindings-store-Bq6VWl5X.js +0 -1
  255. package/dist/web/assets/loader-circle-DouKgIXy.js +0 -1
  256. package/dist/web/assets/markdown-renderer-B9wzBWm9.js +0 -3
  257. package/dist/web/assets/markdown-renderer-DeSV1CCQ.js +0 -1
  258. package/dist/web/assets/mobile-explorer-sheet-DPdxpnKh.js +0 -1
  259. package/dist/web/assets/notification-store-BtijuGez.js +0 -1
  260. package/dist/web/assets/play-DpEEBJTT.js +0 -1
  261. package/dist/web/assets/portal-container-context-D-495m5C.js +0 -1
  262. package/dist/web/assets/remote-desktop-mobile-sheet-FTF6IBhF.js +0 -2
  263. package/dist/web/assets/remote-desktop-mobile-view-DSwG4OSk.js +0 -1
  264. package/dist/web/assets/remote-desktop-stats-overlay-Bz6xZ5y9.js +0 -1
  265. package/dist/web/assets/remote-desktop-warning-gate-DrYySLZ5.js +0 -1
  266. package/dist/web/assets/remote-desktop-window-content-Ndvezorb.js +0 -1
  267. package/dist/web/assets/settings-tab-Cn-AqUP_.js +0 -1
  268. package/dist/web/assets/system-monitor-tab-OYca4qaf.js +0 -1
  269. package/dist/web/assets/system-monitor-window-content-CHz9WdTm.js +0 -1
  270. package/dist/web/assets/team-member-sheet-BQ738Lli.js +0 -2
  271. package/dist/web/assets/use-open-team-member-D9EOB8-i.js +0 -1
  272. package/dist/web/assets/vendor-markdown-Ls5AcU91.js +0 -291
  273. package/dist/web/assets/x-DiOUR_mp.js +0 -1
  274. package/packages/vscode-compat/node_modules/.bin/tsc.bunx +0 -0
  275. package/packages/vscode-compat/node_modules/.bin/tsc.exe +0 -0
  276. package/packages/vscode-compat/node_modules/.bin/tsserver.bunx +0 -0
  277. package/packages/vscode-compat/node_modules/.bin/tsserver.exe +0 -0
  278. /package/dist/web/assets/{abap-C5DFF0s3.js → abap-Cjj2olMr.js} +0 -0
  279. /package/dist/web/assets/{actionscript-3-B0qyRNbc.js → actionscript-3-DCkW7kWC.js} +0 -0
  280. /package/dist/web/assets/{ada-bAJzplug.js → ada-AEWvZXuX.js} +0 -0
  281. /package/dist/web/assets/{andromeeda-DUCpISy6.js → andromeeda-D7cSpME6.js} +0 -0
  282. /package/dist/web/assets/{apache-BhAr84Z_.js → apache-DjvX0RSe.js} +0 -0
  283. /package/dist/web/assets/{apex-CRv8YnNq.js → apex-70xIZ0pH.js} +0 -0
  284. /package/dist/web/assets/{applescript-CD-Cw8bt.js → applescript-C3qKqxmA.js} +0 -0
  285. /package/dist/web/assets/{ara-BUzeQYoG.js → ara-iGt2zixk.js} +0 -0
  286. /package/dist/web/assets/{asciidoc-CT2hTg-v.js → asciidoc-Bs2t-06W.js} +0 -0
  287. /package/dist/web/assets/{asm-CAu35JeY.js → asm-B-T0eqhE.js} +0 -0
  288. /package/dist/web/assets/{aurora-x-BeFJoIA1.js → aurora-x-D3h_9M3E.js} +0 -0
  289. /package/dist/web/assets/{awk-Vau-DrGN.js → awk-CZwfwpPi.js} +0 -0
  290. /package/dist/web/assets/{ayu-dark-CLe-OTsQ.js → ayu-dark-DulkglEA.js} +0 -0
  291. /package/dist/web/assets/{ayu-light-CJbQfu4M.js → ayu-light-DyNQiRpn.js} +0 -0
  292. /package/dist/web/assets/{ayu-mirage-GWaKwdrx.js → ayu-mirage-CcEt4xkI.js} +0 -0
  293. /package/dist/web/assets/{ballerina-DWu0USI5.js → ballerina-OhQPjyHS.js} +0 -0
  294. /package/dist/web/assets/{bat-_07Huw7E.js → bat-DqKcJTUn.js} +0 -0
  295. /package/dist/web/assets/{beancount-EcvogrcF.js → beancount-BdXlMi8T.js} +0 -0
  296. /package/dist/web/assets/{berry-BBB87SY7.js → berry-DvgY6BkY.js} +0 -0
  297. /package/dist/web/assets/{bibtex-DbueS87A.js → bibtex-BSRJq0yM.js} +0 -0
  298. /package/dist/web/assets/{bicep-tcmXnUo4.js → bicep-DpAdumKt.js} +0 -0
  299. /package/dist/web/assets/{bird2-B2qwilIR.js → bird2-rlbtp8di.js} +0 -0
  300. /package/dist/web/assets/{c-Z58oivSt.js → c-AjOLBXIs.js} +0 -0
  301. /package/dist/web/assets/{c3-U40cGT9p.js → c3-DqIXz4Ia.js} +0 -0
  302. /package/dist/web/assets/{cadence-BFcD-LyM.js → cadence-DAJtUpOE.js} +0 -0
  303. /package/dist/web/assets/{catppuccin-frappe-RkC9AX6J.js → catppuccin-frappe-DUPoeZfb.js} +0 -0
  304. /package/dist/web/assets/{catppuccin-latte-Bsi4l-nO.js → catppuccin-latte-CK3U68Ql.js} +0 -0
  305. /package/dist/web/assets/{catppuccin-macchiato-B8JIdTW-.js → catppuccin-macchiato-BzteLnIX.js} +0 -0
  306. /package/dist/web/assets/{catppuccin-mocha-oH8wwrDj.js → catppuccin-mocha-ozLd0aBR.js} +0 -0
  307. /package/dist/web/assets/{clarity-C3WNeRCR.js → clarity-6KqEoBfz.js} +0 -0
  308. /package/dist/web/assets/{clojure-CFoAz7u9.js → clojure-B_uBL5C7.js} +0 -0
  309. /package/dist/web/assets/{cmake-Bcsqp9yR.js → cmake-B3nxBNmj.js} +0 -0
  310. /package/dist/web/assets/{codeowners-DC2Z7heP.js → codeowners-DkS-51at.js} +0 -0
  311. /package/dist/web/assets/{codeql-BfG68Al5.js → codeql-rpr1z_mR.js} +0 -0
  312. /package/dist/web/assets/{common-lisp-Z5P-lKQe.js → common-lisp-BfNbv_W6.js} +0 -0
  313. /package/dist/web/assets/{coq-BJslqrnv.js → coq-EAW-1nw0.js} +0 -0
  314. /package/dist/web/assets/{csharp-DhhZTOTs.js → csharp-BE3Gn5bq.js} +0 -0
  315. /package/dist/web/assets/{css-DiFIpMfE.js → css-Cr27CfPY.js} +0 -0
  316. /package/dist/web/assets/{csv-DViLbHxI.js → csv-B5VtaPtH.js} +0 -0
  317. /package/dist/web/assets/{cue-CoG3mg2I.js → cue-jm6lYNrx.js} +0 -0
  318. /package/dist/web/assets/{cypher-jTxC8NAB.js → cypher-DXkvK-pn.js} +0 -0
  319. /package/dist/web/assets/{d-6hKIUAbU.js → d-CgG1_yzd.js} +0 -0
  320. /package/dist/web/assets/{dark-plus-D_-np2Sr.js → dark-plus-CALzcIVB.js} +0 -0
  321. /package/dist/web/assets/{dart-CVpxLpVn.js → dart-BEVoLlWg.js} +0 -0
  322. /package/dist/web/assets/{dax-BPqJ8qIY.js → dax-H0BW3Q_P.js} +0 -0
  323. /package/dist/web/assets/{desktop-u_y5_Hr5.js → desktop-BtYq1l_0.js} +0 -0
  324. /package/dist/web/assets/{diff-Duj1XMrd.js → diff-CnBYdfTg.js} +0 -0
  325. /package/dist/web/assets/{dist-CEZ4plgH.js → dist-CJJKpgR7.js} +0 -0
  326. /package/dist/web/assets/{docker-8UGSCc7N.js → docker-COBitpld.js} +0 -0
  327. /package/dist/web/assets/{dotenv-DVnj8kyR.js → dotenv-CG0x1m7c.js} +0 -0
  328. /package/dist/web/assets/{dracula-D0cEmSg7.js → dracula-CLCi47X-.js} +0 -0
  329. /package/dist/web/assets/{dracula-soft-DfGS5Yfr.js → dracula-soft-BMv5cCAV.js} +0 -0
  330. /package/dist/web/assets/{dream-maker-qG8kMGTF.js → dream-maker-1Bd4OSww.js} +0 -0
  331. /package/dist/web/assets/{emacs-lisp-iVqyMLyT.js → emacs-lisp-B-2zKLn-.js} +0 -0
  332. /package/dist/web/assets/{everforest-dark-Cruygd5U.js → everforest-dark-BGVynCbV.js} +0 -0
  333. /package/dist/web/assets/{everforest-light-BiJa8RHL.js → everforest-light--rn3R7Cm.js} +0 -0
  334. /package/dist/web/assets/{fennel-H6XSEIoz.js → fennel-SWg9Us_u.js} +0 -0
  335. /package/dist/web/assets/{fish-ho_7CxX6.js → fish-B04oqgKk.js} +0 -0
  336. /package/dist/web/assets/{fluent-1l5ssJTF.js → fluent-BefzAGMN.js} +0 -0
  337. /package/dist/web/assets/{fortran-free-form-Bo1x233A.js → fortran-free-form-alw7jMxb.js} +0 -0
  338. /package/dist/web/assets/{gdscript-DffFMp_k.js → gdscript-BAyhvjCx.js} +0 -0
  339. /package/dist/web/assets/{gdshader-CvnpAiAF.js → gdshader-Crl09X-B.js} +0 -0
  340. /package/dist/web/assets/{genie-2eOvAEzW.js → genie-BSIGRHu7.js} +0 -0
  341. /package/dist/web/assets/{gherkin-DL-5OUlV.js → gherkin-DJhSnSWH.js} +0 -0
  342. /package/dist/web/assets/{github-dark-default-_mDH8IMP.js → github-dark-default-FVtXjcm8.js} +0 -0
  343. /package/dist/web/assets/{github-dark-dimmed-C48hJuLn.js → github-dark-dimmed-CzrUPgSb.js} +0 -0
  344. /package/dist/web/assets/{github-dark-high-contrast-BQ-zGGNe.js → github-dark-high-contrast-1s9rF5EB.js} +0 -0
  345. /package/dist/web/assets/{github-dark-DrTZprgG.js → github-dark-r8C6qwug.js} +0 -0
  346. /package/dist/web/assets/{github-light-BYc90Yx8.js → github-light-BvIWCkl7.js} +0 -0
  347. /package/dist/web/assets/{github-light-default-Daecj4J5.js → github-light-default-CVdhzrxV.js} +0 -0
  348. /package/dist/web/assets/{github-light-high-contrast-DoafCbsU.js → github-light-high-contrast-CBAf6hjF.js} +0 -0
  349. /package/dist/web/assets/{gleam-hUWWjmnp.js → gleam-DvL1q7aa.js} +0 -0
  350. /package/dist/web/assets/{gn-7PJ3R70o.js → gn-BG5NvwAf.js} +0 -0
  351. /package/dist/web/assets/{gnuplot-V-Pxck7b.js → gnuplot-BARnM-zG.js} +0 -0
  352. /package/dist/web/assets/{go-ARF6DprD.js → go-DXvjjq4q.js} +0 -0
  353. /package/dist/web/assets/{groovy-CKGBIPOz.js → groovy-CRylSKhr.js} +0 -0
  354. /package/dist/web/assets/{gruvbox-dark-hard-Cm9wqdI3.js → gruvbox-dark-hard-BVPjnqm7.js} +0 -0
  355. /package/dist/web/assets/{gruvbox-dark-medium-Cmsd6dG8.js → gruvbox-dark-medium-U_PDgq0a.js} +0 -0
  356. /package/dist/web/assets/{gruvbox-dark-soft-BBGlGGCz.js → gruvbox-dark-soft-CkzNtpbr.js} +0 -0
  357. /package/dist/web/assets/{gruvbox-light-hard-BCR7KmqG.js → gruvbox-light-hard-DfMmR2G8.js} +0 -0
  358. /package/dist/web/assets/{gruvbox-light-medium-CP32bga8.js → gruvbox-light-medium-BGCak-dd.js} +0 -0
  359. /package/dist/web/assets/{gruvbox-light-soft-qjxk8ps6.js → gruvbox-light-soft-DnpM3fkN.js} +0 -0
  360. /package/dist/web/assets/{haskell-C5DzwGRa.js → haskell-DXgGIDD_.js} +0 -0
  361. /package/dist/web/assets/{haxe-C8f_jCVv.js → haxe-B7qlTpRz.js} +0 -0
  362. /package/dist/web/assets/{hcl-DetJkf6E.js → hcl-ADWfVe_Q.js} +0 -0
  363. /package/dist/web/assets/{hjson-DeZhwIdo.js → hjson-BaM9gLbq.js} +0 -0
  364. /package/dist/web/assets/{hlsl-SMIDLRyX.js → hlsl-iqaRA4MG.js} +0 -0
  365. /package/dist/web/assets/{horizon-BMAVDxMx.js → horizon-BPpTnhB9.js} +0 -0
  366. /package/dist/web/assets/{horizon-bright-3BaV_VGD.js → horizon-bright-yBLg3QbE.js} +0 -0
  367. /package/dist/web/assets/{houston-BWBbEPvb.js → houston-eFOp0ySL.js} +0 -0
  368. /package/dist/web/assets/{hy-DcsmE-Nm.js → hy-BALeMn3i.js} +0 -0
  369. /package/dist/web/assets/{imba-CnkgnYMf.js → imba-CdGUm_GD.js} +0 -0
  370. /package/dist/web/assets/{ini-BnvTdau7.js → ini-DG7imsqe.js} +0 -0
  371. /package/dist/web/assets/{java-BLFACXat.js → java-D5GTT474.js} +0 -0
  372. /package/dist/web/assets/{javascript-BKcFWENk.js → javascript-V7WwJvRm.js} +0 -0
  373. /package/dist/web/assets/{json-3AJtJADf.js → json-B9FhwAgR.js} +0 -0
  374. /package/dist/web/assets/{json5-DLyDVlwe.js → json5--Abk7MUy.js} +0 -0
  375. /package/dist/web/assets/{jsonc-B5DgF1jW.js → jsonc-C8jb-Z6x.js} +0 -0
  376. /package/dist/web/assets/{jsonl-CV_O71a2.js → jsonl-CaJNxfkm.js} +0 -0
  377. /package/dist/web/assets/{jsonnet-DZfPLgYz.js → jsonnet-CPrKMSCU.js} +0 -0
  378. /package/dist/web/assets/{jssm-Cr1zYOsY.js → jssm-DEu-9_uo.js} +0 -0
  379. /package/dist/web/assets/{jsx-VN-JS2_q.js → jsx-MfAsCWDW.js} +0 -0
  380. /package/dist/web/assets/{kanagawa-dragon-D9CcT3xg.js → kanagawa-dragon-DnbcAduF.js} +0 -0
  381. /package/dist/web/assets/{kanagawa-lotus-DLXsB8fr.js → kanagawa-lotus-yxsY3ew8.js} +0 -0
  382. /package/dist/web/assets/{kanagawa-wave-weDs3Wjz.js → kanagawa-wave-C2RyvZaM.js} +0 -0
  383. /package/dist/web/assets/{kdl-0XF56jnT.js → kdl-CvssmvcU.js} +0 -0
  384. /package/dist/web/assets/{kotlin-BPkH9vzk.js → kotlin-jufO49mU.js} +0 -0
  385. /package/dist/web/assets/{kusto-DMVpADb9.js → kusto-BAFnylR7.js} +0 -0
  386. /package/dist/web/assets/{laserwave-Cl6T2cGP.js → laserwave-BgxFC4Ge.js} +0 -0
  387. /package/dist/web/assets/{lean-BvwzQGl1.js → lean-Bgt24V1D.js} +0 -0
  388. /package/dist/web/assets/{less-DI_03xkC.js → less-Bk_UaqsM.js} +0 -0
  389. /package/dist/web/assets/{light-plus-hIiIsQN_.js → light-plus-nMxFfInN.js} +0 -0
  390. /package/dist/web/assets/{llvm-B5hjjSWc.js → llvm-3_PASDOO.js} +0 -0
  391. /package/dist/web/assets/{log-D28raQiE.js → log-CKm0CxB1.js} +0 -0
  392. /package/dist/web/assets/{logo-6Cdd4ASb.js → logo-DFP1_5U6.js} +0 -0
  393. /package/dist/web/assets/{luau-CK9nKpOX.js → luau-BcZnoyh3.js} +0 -0
  394. /package/dist/web/assets/{make-CVnxKCB6.js → make-CpNSwgwT.js} +0 -0
  395. /package/dist/web/assets/{markdown-B9mMQzD5.js → markdown-DhU3Djpv.js} +0 -0
  396. /package/dist/web/assets/{material-theme-BZSs2o0j.js → material-theme-DD5L3OqF.js} +0 -0
  397. /package/dist/web/assets/{material-theme-darker-BeAQBzQ_.js → material-theme-darker-CG2_tD3R.js} +0 -0
  398. /package/dist/web/assets/{material-theme-lighter-BAe0IXcs.js → material-theme-lighter-CzNkUc9Y.js} +0 -0
  399. /package/dist/web/assets/{material-theme-ocean-CiPIf0Np.js → material-theme-ocean-BtLiAaF_.js} +0 -0
  400. /package/dist/web/assets/{material-theme-palenight-DJBb4_XN.js → material-theme-palenight-Brvjz035.js} +0 -0
  401. /package/dist/web/assets/{matlab-CY0dbp_s.js → matlab-CCY0KtMD.js} +0 -0
  402. /package/dist/web/assets/{mdx-DU9zbF7h.js → mdx-CH6YY0aS.js} +0 -0
  403. /package/dist/web/assets/{mermaid-UmmjGwdh.js → mermaid-Bqh3gFI0.js} +0 -0
  404. /package/dist/web/assets/{min-dark-C59GkzOm.js → min-dark-BUwfTzI6.js} +0 -0
  405. /package/dist/web/assets/{min-light-B9QglrdQ.js → min-light-oLanXCOk.js} +0 -0
  406. /package/dist/web/assets/{mipsasm-Dc6Jsr_D.js → mipsasm-CTh3hPWK.js} +0 -0
  407. /package/dist/web/assets/{mojo-D5ij-nGW.js → mojo-DXdszZKa.js} +0 -0
  408. /package/dist/web/assets/{monokai-QAtE_yCP.js → monokai-TNE_tRxc.js} +0 -0
  409. /package/dist/web/assets/{moonbit-BMtIdiTL.js → moonbit-jrk-Gx1X.js} +0 -0
  410. /package/dist/web/assets/{move-BB_jMHYZ.js → move-DItn61bH.js} +0 -0
  411. /package/dist/web/assets/{narrat-BvvoKGVn.js → narrat-3kB4dkh0.js} +0 -0
  412. /package/dist/web/assets/{nextflow-groovy-CqxtAsnF.js → nextflow-groovy-CSKtwETr.js} +0 -0
  413. /package/dist/web/assets/{night-owl-CLoE9kjt.js → night-owl-Dm91nSIq.js} +0 -0
  414. /package/dist/web/assets/{night-owl-light-BwIFT5Xd.js → night-owl-light-riwrcPRg.js} +0 -0
  415. /package/dist/web/assets/{nix-BKlnVT9r.js → nix-sLgl9zGF.js} +0 -0
  416. /package/dist/web/assets/{nord-B7R1KRAK.js → nord-TWkJPI6M.js} +0 -0
  417. /package/dist/web/assets/{nushell-CzUme-NJ.js → nushell-CDZi4-Gf.js} +0 -0
  418. /package/dist/web/assets/{objective-c-D-UKKiOc.js → objective-c-DW9PboNp.js} +0 -0
  419. /package/dist/web/assets/{objective-cpp-BucWn4Fx.js → objective-cpp-2XCCX6q1.js} +0 -0
  420. /package/dist/web/assets/{ocaml-EJCNXMGo.js → ocaml-CxPdnjrh.js} +0 -0
  421. /package/dist/web/assets/{odin-CEjnRU_3.js → odin-BJjlKpkM.js} +0 -0
  422. /package/dist/web/assets/{one-dark-pro-Bv469NX5.js → one-dark-pro-K1KOmBZ7.js} +0 -0
  423. /package/dist/web/assets/{one-light-BlzO5RWI.js → one-light-DQB-vUyZ.js} +0 -0
  424. /package/dist/web/assets/{openscad-Czl6alLw.js → openscad-DCoLwn4j.js} +0 -0
  425. /package/dist/web/assets/{pascal-BI9fLvG4.js → pascal-z2ktbbWp.js} +0 -0
  426. /package/dist/web/assets/{pkl-ChQLsyjU.js → pkl-Dyq4-v6z.js} +0 -0
  427. /package/dist/web/assets/{plastic-DGL3mtdL.js → plastic-C4Rq5PnD.js} +0 -0
  428. /package/dist/web/assets/{plsql-BJDjqcWp.js → plsql-BCIjO63y.js} +0 -0
  429. /package/dist/web/assets/{po-DK5O3OpF.js → po-_kXpd_0n.js} +0 -0
  430. /package/dist/web/assets/{poimandres-CnuqZT4T.js → poimandres-C9XggjB3.js} +0 -0
  431. /package/dist/web/assets/{polar-DCsUxSsQ.js → polar-DV_1L04z.js} +0 -0
  432. /package/dist/web/assets/{postcss-8UjdFmis.js → postcss-BuqS91mQ.js} +0 -0
  433. /package/dist/web/assets/{powerquery-CAp0CGQj.js → powerquery-DK13B2Dc.js} +0 -0
  434. /package/dist/web/assets/{powershell-CbBsKZJP.js → powershell-tkuhhtuP.js} +0 -0
  435. /package/dist/web/assets/{prisma-BnNpXCJN.js → prisma-WNXfBtpr.js} +0 -0
  436. /package/dist/web/assets/{prolog-CC-l9E8Y.js → prolog-BzEdNKfO.js} +0 -0
  437. /package/dist/web/assets/{proto-CV_1k2rL.js → proto-B5meVsrA.js} +0 -0
  438. /package/dist/web/assets/{puppet-2D8HU2bv.js → puppet-C7h_p4r2.js} +0 -0
  439. /package/dist/web/assets/{purescript-BEJAORgU.js → purescript-qH1KxAs6.js} +0 -0
  440. /package/dist/web/assets/{python-BBdbb7nn.js → python-DeTIYd5V.js} +0 -0
  441. /package/dist/web/assets/{qmldir-D5ZYI4Ie.js → qmldir-4Rm5AvD3.js} +0 -0
  442. /package/dist/web/assets/{qss-BOJVGyXE.js → qss-YRbeNtr0.js} +0 -0
  443. /package/dist/web/assets/{r-B5dlEtkj.js → r-DHwHK0Jh.js} +0 -0
  444. /package/dist/web/assets/{racket-BUAEygJ6.js → racket-gKn4SFLQ.js} +0 -0
  445. /package/dist/web/assets/{raku-BG0Ss0ID.js → raku-DWTmAj3A.js} +0 -0
  446. /package/dist/web/assets/{red-C5yyQbtG.js → red-BFuHRZFS.js} +0 -0
  447. /package/dist/web/assets/{reg-DHoBWtLf.js → reg-DYoOF5tH.js} +0 -0
  448. /package/dist/web/assets/{regexp-DDtJrlKv.js → regexp-6rXQHD2-.js} +0 -0
  449. /package/dist/web/assets/{rel-CR6WJDRD.js → rel-Z_tTruCU.js} +0 -0
  450. /package/dist/web/assets/{riscv-2V9tC-nx.js → riscv-DfTfKuvU.js} +0 -0
  451. /package/dist/web/assets/{ron-QLTmqGUw.js → ron-CsUtW8gQ.js} +0 -0
  452. /package/dist/web/assets/{rose-pine-be0__X3A.js → rose-pine-Dng_vCo6.js} +0 -0
  453. /package/dist/web/assets/{rose-pine-dawn-n_WQJwsW.js → rose-pine-dawn-Ds88Nezf.js} +0 -0
  454. /package/dist/web/assets/{rose-pine-moon-t2aLtzgB.js → rose-pine-moon-DWpzrqNz.js} +0 -0
  455. /package/dist/web/assets/{rosmsg-Ce1JD8vt.js → rosmsg-h2sO84oD.js} +0 -0
  456. /package/dist/web/assets/{rust-5fHH-ZoW.js → rust-Dx86yfCi.js} +0 -0
  457. /package/dist/web/assets/{sass-DPmGFiOJ.js → sass-BwwVhdT9.js} +0 -0
  458. /package/dist/web/assets/{scala-BRMVFbZF.js → scala-WJqNEN-w.js} +0 -0
  459. /package/dist/web/assets/{scheme-BMSJWevF.js → scheme-uwj-t1Rp.js} +0 -0
  460. /package/dist/web/assets/{sdbl-DTZ-pEHI.js → sdbl-BYy5hgEz.js} +0 -0
  461. /package/dist/web/assets/{shellscript-6-9yg5Cz.js → shellscript-DWTW_w2K.js} +0 -0
  462. /package/dist/web/assets/{slack-dark-DKJgiR7e.js → slack-dark-BvhGq5vQ.js} +0 -0
  463. /package/dist/web/assets/{slack-ochin-WjehVgyK.js → slack-ochin-C9g0ZRFr.js} +0 -0
  464. /package/dist/web/assets/{smalltalk-BChf4SN1.js → smalltalk-ZEiZ6i7U.js} +0 -0
  465. /package/dist/web/assets/{snazzy-light-C1WAwOx6.js → snazzy-light-29OA24q5.js} +0 -0
  466. /package/dist/web/assets/{solarized-dark-BxREZrGx.js → solarized-dark-Bnq9IOn_.js} +0 -0
  467. /package/dist/web/assets/{solarized-light-DG29B1pR.js → solarized-light-CYdI7T5g.js} +0 -0
  468. /package/dist/web/assets/{solidity-bDbtGwdR.js → solidity-DcuIDB-_.js} +0 -0
  469. /package/dist/web/assets/{splunk-Ceqx5njI.js → splunk-D5ONE91g.js} +0 -0
  470. /package/dist/web/assets/{sql-OwMEKbz8.js → sql-DIwJJDU-.js} +0 -0
  471. /package/dist/web/assets/{ssh-config-WAR3lu2k.js → ssh-config-D8717tLI.js} +0 -0
  472. /package/dist/web/assets/{stylus-Cjmoz3d8.js → stylus-Cm1lO4Y6.js} +0 -0
  473. /package/dist/web/assets/{swift-S_ZSBG1q.js → swift-X8oWpSeU.js} +0 -0
  474. /package/dist/web/assets/{synthwave-84-DiUeWMSh.js → synthwave-84-DF-t1UKY.js} +0 -0
  475. /package/dist/web/assets/{system-verilog-RRjqPjUV.js → system-verilog-JYyHFpPf.js} +0 -0
  476. /package/dist/web/assets/{systemd-BD3SkXoU.js → systemd-Da-5Ma_c.js} +0 -0
  477. /package/dist/web/assets/{talonscript-DWefISOm.js → talonscript-CXraVPho.js} +0 -0
  478. /package/dist/web/assets/{tasl-DCjSS-09.js → tasl-BCmWezH9.js} +0 -0
  479. /package/dist/web/assets/{tcl-DTYqT_66.js → tcl-BTufzfbf.js} +0 -0
  480. /package/dist/web/assets/{terraform-CkbP5QnR.js → terraform-CtUA9c8W.js} +0 -0
  481. /package/dist/web/assets/{tokyo-night-qVSqO9xy.js → tokyo-night-BCThYXmz.js} +0 -0
  482. /package/dist/web/assets/{toml-D9F5Osqs.js → toml-D5YlDr6Y.js} +0 -0
  483. /package/dist/web/assets/{tsv-C5tpHoxm.js → tsv-CBB-P_Sl.js} +0 -0
  484. /package/dist/web/assets/{tsx-CMWkGtGm.js → tsx-D5B4juDp.js} +0 -0
  485. /package/dist/web/assets/{turtle-Cn4z3xOi.js → turtle-D8PCeJ-v.js} +0 -0
  486. /package/dist/web/assets/{typescript-DSyAEWsn.js → typescript-CMPnTawp.js} +0 -0
  487. /package/dist/web/assets/{typespec-B93Qap7y.js → typespec-CHxldxIW.js} +0 -0
  488. /package/dist/web/assets/{typst-DtlOmFlX.js → typst-Q8CqX5OR.js} +0 -0
  489. /package/dist/web/assets/{v-Dfzf-szS.js → v-Bf2hKAMu.js} +0 -0
  490. /package/dist/web/assets/{vala-CyiQZQ9f.js → vala-Cbete-UE.js} +0 -0
  491. /package/dist/web/assets/{vb-C7DGSldd.js → vb-CA-xWAcX.js} +0 -0
  492. /package/dist/web/assets/{verilog-kF-hClY2.js → verilog-B3wb8CfT.js} +0 -0
  493. /package/dist/web/assets/{vesper-BmKcADuz.js → vesper-D6r3VJq_.js} +0 -0
  494. /package/dist/web/assets/{vhdl-CDnb16KJ.js → vhdl-C42S_797.js} +0 -0
  495. /package/dist/web/assets/{viml-B8cs6Ady.js → viml-BePrIAs8.js} +0 -0
  496. /package/dist/web/assets/{vitesse-black-BR6o9Gto.js → vitesse-black-DRqn61Ck.js} +0 -0
  497. /package/dist/web/assets/{vitesse-dark-Dye14-KB.js → vitesse-dark-39_8UE-w.js} +0 -0
  498. /package/dist/web/assets/{vitesse-light-BLpswIxf.js → vitesse-light-B1jwrxDm.js} +0 -0
  499. /package/dist/web/assets/{vyper-DU51Z97W.js → vyper-tDJv3imF.js} +0 -0
  500. /package/dist/web/assets/{wasm-CVqlJAR2.js → wasm-DR7eskZH.js} +0 -0
  501. /package/dist/web/assets/{wenyan-DhCa3hCn.js → wenyan-CezVfqN3.js} +0 -0
  502. /package/dist/web/assets/{wgsl-Bj1Ec4vv.js → wgsl-CwOcIiIG.js} +0 -0
  503. /package/dist/web/assets/{wikitext-C90DHNa0.js → wikitext-C5coomYc.js} +0 -0
  504. /package/dist/web/assets/{wit-DUgbOD7Z.js → wit-C9yyX6-U.js} +0 -0
  505. /package/dist/web/assets/{wolfram-BgwUqD1I.js → wolfram-BWi0rMAa.js} +0 -0
  506. /package/dist/web/assets/{yaml-DEQulj-8.js → yaml-CGXhKUtT.js} +0 -0
  507. /package/dist/web/assets/{zenscript-C7Jp7TgT.js → zenscript-Do7WLoJh.js} +0 -0
  508. /package/dist/web/assets/{zig-9x4qXYLg.js → zig-Cr80Qrru.js} +0 -0
@@ -25,6 +25,7 @@ import { createPortal } from "react-dom";
25
25
  import { usePortalContainer } from "@/components/ui/portal-container-context";
26
26
  import { useRemoteDesktopMobileOpenState } from "./use-remote-desktop-mobile-open-state";
27
27
  import { RemoteDesktopWarningGate } from "./remote-desktop-warning-gate";
28
+ import { RemoteDesktopReadinessGate } from "./remote-desktop-readiness-gate";
28
29
 
29
30
  const RemoteDesktopMobileView = lazy(() => import("./remote-desktop-mobile-view"));
30
31
 
@@ -38,9 +39,11 @@ export function RemoteDesktopMobileSheet() {
38
39
  return createPortal(
39
40
  <div className="fixed inset-0 z-[45] bg-black" data-testid="remote-desktop-mobile-sheet">
40
41
  <RemoteDesktopWarningGate onCancel={close}>
41
- <Suspense fallback={<div className="flex h-full items-center justify-center text-sm text-text-2">Loading…</div>}>
42
- <RemoteDesktopMobileView onClose={close} />
43
- </Suspense>
42
+ <RemoteDesktopReadinessGate onCancel={close}>
43
+ <Suspense fallback={<div className="flex h-full items-center justify-center text-sm text-text-2">Loading…</div>}>
44
+ <RemoteDesktopMobileView onClose={close} />
45
+ </Suspense>
46
+ </RemoteDesktopReadinessGate>
44
47
  </RemoteDesktopWarningGate>
45
48
  </div>,
46
49
  portalContainer ?? document.getElementById("root") ?? document.body,
@@ -3,12 +3,16 @@
3
3
  * virtual keyboard, zoom reset, close. Every button is a 44px+ touch target per
4
4
  * `docs/design-guidelines.md`'s Mobile-First UI Rules.
5
5
  */
6
- import { Hand, MousePointer2, Keyboard, ZoomOut, Gauge, X } from "lucide-react";
6
+ import { Hand, MousePointer2, Keyboard, ZoomOut, Gauge, Monitor, X } from "lucide-react";
7
7
  import { cn } from "@/lib/utils";
8
8
  import { useSettingsStore } from "@/stores/settings-store";
9
9
  import type { RemoteDesktopInputMode } from "./use-remote-desktop-touch";
10
10
 
11
11
  export interface RemoteDesktopMobileToolbarProps {
12
+ /** Current display's name on a multi-monitor host; null hides the button (single display). */
13
+ displayLabel: string | null;
14
+ /** Cycle to the next display — one tap per hop beats a dropdown in the thumb zone. */
15
+ onNextDisplay: () => void;
12
16
  mode: RemoteDesktopInputMode;
13
17
  onToggleMode: () => void;
14
18
  onOpenKeyboard: () => void;
@@ -45,6 +49,8 @@ function ToolbarButton({
45
49
  }
46
50
 
47
51
  export function RemoteDesktopMobileToolbar({
52
+ displayLabel,
53
+ onNextDisplay,
48
54
  mode,
49
55
  onToggleMode,
50
56
  onOpenKeyboard,
@@ -68,6 +74,11 @@ export function RemoteDesktopMobileToolbar({
68
74
  <ToolbarButton onClick={onResetZoom} label="Reset zoom">
69
75
  <ZoomOut className="size-5" />
70
76
  </ToolbarButton>
77
+ {displayLabel !== null && (
78
+ <ToolbarButton onClick={onNextDisplay} label={displayLabel.length > 10 ? `${displayLabel.slice(0, 9)}…` : displayLabel}>
79
+ <Monitor className="size-5" />
80
+ </ToolbarButton>
81
+ )}
71
82
  <ToolbarButton onClick={toggleStats} label="Stats" active={statsVisible}>
72
83
  <Gauge className="size-5" />
73
84
  </ToolbarButton>
@@ -16,6 +16,7 @@ import { RemoteDesktopMobileToolbar } from "./remote-desktop-mobile-toolbar";
16
16
  import { RemoteDesktopMobileKeyBar } from "./remote-desktop-mobile-key-bar";
17
17
  import { RemoteDesktopStatsOverlay } from "./remote-desktop-stats-overlay";
18
18
  import { letterboxedContentRect } from "./remote-desktop-coords";
19
+ import { useRemoteDesktopDisplayChoice } from "./use-remote-desktop-display-choice";
19
20
 
20
21
  export interface RemoteDesktopMobileViewProps {
21
22
  onClose: () => void;
@@ -26,11 +27,12 @@ export default function RemoteDesktopMobileView({ onClose }: RemoteDesktopMobile
26
27
  const canvasRef = useRef<HTMLCanvasElement | null>(null);
27
28
  const [mode, setMode] = useState<RemoteDesktopInputMode>("mouse");
28
29
  const [keyBarOpen, setKeyBarOpen] = useState(false);
30
+ const display = useRemoteDesktopDisplayChoice();
29
31
 
30
32
  const {
31
33
  connState, errorMessage, decoderStatus, decoderErrorMessage, sendMessage, reconnect,
32
34
  getTotalBytes, getFrameCount,
33
- } = useRemoteDesktopConnection(canvasRef);
35
+ } = useRemoteDesktopConnection(canvasRef, { displayId: display.displayId });
34
36
  const streaming = connState === "streaming";
35
37
 
36
38
  const { transform, virtualCursor, resetZoom } = useRemoteDesktopTouch({
@@ -169,6 +171,8 @@ export default function RemoteDesktopMobileView({ onClose }: RemoteDesktopMobile
169
171
  >
170
172
  {keyBarOpen && <RemoteDesktopMobileKeyBar sendMessage={sendMessage} />}
171
173
  <RemoteDesktopMobileToolbar
174
+ displayLabel={display.displays.length > 1 ? display.current?.label ?? null : null}
175
+ onNextDisplay={display.next}
172
176
  mode={mode}
173
177
  onToggleMode={toggleMode}
174
178
  onOpenKeyboard={openKeyboard}
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Second gate after the warning: the host's requirements checklist. Renders `children` (the
3
+ * viewer) straight away when every requirement is met; otherwise shows one row per item with
4
+ * its actions and re-polls until they clear. Video requirements block; input requirements only
5
+ * downgrade the button to "view only" — a screen you can see but not drive is still useful.
6
+ *
7
+ * Actions are generic (`terminal` types into a PPM dock terminal — the host's shell, so it works
8
+ * from a phone; `host` asks the server to prompt/open Settings on the host; `link` opens on the
9
+ * client). Same dark chrome and ≥44px targets as the warning gate, both surfaces.
10
+ */
11
+ import { useState, type ReactNode } from "react";
12
+ import { CheckCircle2, Circle, ExternalLink, Loader2, MonitorSmartphone, TerminalSquare } from "lucide-react";
13
+ import { runInTerminal } from "@/lib/run-in-terminal";
14
+ import { useRemoteDesktopReadiness, type RemoteDesktopRequirement, type RequirementAction } from "./use-remote-desktop-readiness";
15
+
16
+ export interface RemoteDesktopReadinessGateProps {
17
+ onCancel: () => void;
18
+ children: ReactNode;
19
+ }
20
+
21
+ export function RemoteDesktopReadinessGate({ onCancel, children }: RemoteDesktopReadinessGateProps) {
22
+ const [accepted, setAccepted] = useState(false);
23
+ const { caps, failed, runHostAction } = useRemoteDesktopReadiness(!accepted);
24
+
25
+ if (accepted || (caps?.videoReady && caps.inputReady)) return <>{children}</>;
26
+
27
+ const pending = caps?.requirements.filter((r) => !r.ok) ?? [];
28
+ const viewOnly = !!caps?.videoReady && !caps.inputReady;
29
+ // The dock terminal opens *under* this surface (the mobile sheet covers the whole viewport),
30
+ // so hand the command over and get out of the way; the entry stays in the nav to come back to.
31
+ const onTerminal = (command: string) => { runInTerminal(command); onCancel(); };
32
+
33
+ return (
34
+ <div
35
+ className="flex h-full w-full items-center justify-center overflow-y-auto bg-black p-4 text-white"
36
+ data-testid="remote-desktop-readiness-gate"
37
+ >
38
+ <div className="flex w-full max-w-sm flex-col gap-4 rounded-xl border border-white/10 bg-white/5 p-5">
39
+ <div className="flex items-center gap-2">
40
+ <MonitorSmartphone className="size-5 shrink-0 text-primary" />
41
+ <h2 className="text-base font-semibold">This host needs a few things</h2>
42
+ </div>
43
+
44
+ {!caps && !failed && (
45
+ <div className="flex items-center gap-2 text-sm text-white/70"><Loader2 className="size-4 animate-spin" /> Checking the host…</div>
46
+ )}
47
+ {failed && <p className="text-sm text-red-300">Could not reach the host&apos;s remote-desktop service.</p>}
48
+
49
+ <ul className="flex flex-col gap-3">
50
+ {caps?.requirements.map((r) => (
51
+ <RequirementRow key={r.id} req={r} onHostAction={runHostAction} onTerminal={onTerminal} />
52
+ ))}
53
+ </ul>
54
+
55
+ {pending.length > 0 && (
56
+ <p className="text-xs text-white/50">Re-checking every few seconds — this updates by itself once fixed.</p>
57
+ )}
58
+
59
+ <div className="flex gap-2">
60
+ <button type="button" onClick={onCancel} className="min-h-11 flex-1 rounded-md bg-white/10 px-3 text-sm hover:bg-white/20">
61
+ Cancel
62
+ </button>
63
+ <button
64
+ type="button"
65
+ disabled={!caps?.videoReady}
66
+ onClick={() => setAccepted(true)}
67
+ className="min-h-11 flex-1 rounded-md bg-primary px-3 text-sm font-medium text-primary-foreground hover:bg-primary/90 disabled:cursor-not-allowed disabled:opacity-40"
68
+ data-testid="remote-desktop-readiness-continue"
69
+ >
70
+ {viewOnly ? "Continue (view only)" : "Continue"}
71
+ </button>
72
+ </div>
73
+ </div>
74
+ </div>
75
+ );
76
+ }
77
+
78
+ interface ActionHandlers {
79
+ onHostAction: (id: string, a: "request" | "open-settings") => Promise<void>;
80
+ onTerminal: (command: string) => void;
81
+ }
82
+
83
+ function RequirementRow({ req, ...handlers }: { req: RemoteDesktopRequirement } & ActionHandlers) {
84
+ return (
85
+ <li className="flex gap-2" data-testid={`remote-desktop-requirement-${req.id}`} data-ok={req.ok}>
86
+ {req.ok
87
+ ? <CheckCircle2 className="mt-0.5 size-4 shrink-0 text-emerald-400" />
88
+ : <Circle className="mt-0.5 size-4 shrink-0 text-amber-300" />}
89
+ <div className="flex min-w-0 flex-1 flex-col gap-2">
90
+ <div className="text-sm font-medium">
91
+ {req.title}
92
+ {!req.ok && <span className="ml-2 text-xs font-normal text-white/50">{req.gates === "video" ? "required" : "for mouse & keyboard"}</span>}
93
+ </div>
94
+ {!req.ok && (
95
+ <>
96
+ <p className="text-sm text-white/70">{req.detail}</p>
97
+ <div className="flex flex-wrap gap-2">
98
+ {req.actions.map((a) => <ActionButton key={a.label} action={a} reqId={req.id} {...handlers} />)}
99
+ </div>
100
+ </>
101
+ )}
102
+ </div>
103
+ </li>
104
+ );
105
+ }
106
+
107
+ function ActionButton({ action, reqId, onHostAction, onTerminal }: { action: RequirementAction; reqId: string } & ActionHandlers) {
108
+ const cls = "inline-flex min-h-11 items-center gap-1.5 rounded-md bg-white/10 px-3 text-sm hover:bg-white/20";
109
+ if (action.kind === "terminal") {
110
+ return (
111
+ <button type="button" className={cls} onClick={() => onTerminal(action.command)} title={action.command}>
112
+ <TerminalSquare className="size-4" /> {action.label}
113
+ </button>
114
+ );
115
+ }
116
+ if (action.kind === "link") {
117
+ return (
118
+ <a className={cls} href={action.url} target="_blank" rel="noreferrer">
119
+ <ExternalLink className="size-4" /> {action.label}
120
+ </a>
121
+ );
122
+ }
123
+ return (
124
+ <button type="button" className={cls} onClick={() => void onHostAction(reqId, action.action)}>
125
+ {action.label}
126
+ </button>
127
+ );
128
+ }
@@ -15,21 +15,26 @@ import { useRemoteDesktopConnection } from "./use-remote-desktop-connection";
15
15
  import { useRemoteInputCapture } from "./use-remote-input-capture";
16
16
  import { RemoteDesktopStatsOverlay } from "./remote-desktop-stats-overlay";
17
17
  import { RemoteDesktopWarningGate } from "./remote-desktop-warning-gate";
18
+ import { RemoteDesktopReadinessGate } from "./remote-desktop-readiness-gate";
19
+ import { useRemoteDesktopDisplayChoice } from "./use-remote-desktop-display-choice";
18
20
 
19
21
  export default function RemoteDesktopWindowContent({ id }: WindowContentProps) {
20
22
  const closeWindow = useWindowStore((s) => s.close);
21
23
  const onCancel = useCallback(() => closeWindow(id), [closeWindow, id]);
22
24
  return (
23
25
  <RemoteDesktopWarningGate onCancel={onCancel}>
24
- <RemoteDesktopViewer />
26
+ <RemoteDesktopReadinessGate onCancel={onCancel}>
27
+ <RemoteDesktopViewer />
28
+ </RemoteDesktopReadinessGate>
25
29
  </RemoteDesktopWarningGate>
26
30
  );
27
31
  }
28
32
 
29
33
  function RemoteDesktopViewer() {
30
34
  const canvasRef = useRef<HTMLCanvasElement | null>(null);
35
+ const display = useRemoteDesktopDisplayChoice();
31
36
  const { connState, errorMessage, decoderStatus, decoderErrorMessage, sendMessage, reconnect, getFrameCount, getTotalBytes } =
32
- useRemoteDesktopConnection(canvasRef);
37
+ useRemoteDesktopConnection(canvasRef, { displayId: display.displayId });
33
38
  const statsVisible = useSettingsStore((s) => s.remoteDesktopStatsVisible);
34
39
  const toggleStats = useSettingsStore((s) => s.toggleRemoteDesktopStatsVisible);
35
40
 
@@ -53,6 +58,21 @@ function RemoteDesktopViewer() {
53
58
  >
54
59
  <canvas ref={canvasRef} data-testid="remote-desktop-canvas" className="max-h-full max-w-full outline-none" />
55
60
  <RemoteDesktopStatsOverlay canvasRef={canvasRef} getFrameCount={getFrameCount} getTotalBytes={getTotalBytes} />
61
+ {/* Multi-monitor hosts: pick which display to stream. Left of the stats toggle, same
62
+ corner cluster; a single-display host renders nothing here. */}
63
+ {display.displays.length > 1 && (
64
+ <select
65
+ value={display.current?.id ?? ""}
66
+ onChange={(e) => display.select(e.target.value)}
67
+ aria-label="Display"
68
+ className="absolute right-8 top-1 z-40 h-6 max-w-[45%] rounded bg-black/40 px-1.5 text-xs text-white/80 hover:bg-black/60"
69
+ data-testid="remote-desktop-display-select"
70
+ >
71
+ {display.displays.map((d) => (
72
+ <option key={d.id} value={d.id}>{d.label}{d.primary ? " (main)" : ""} · {d.width}×{d.height}</option>
73
+ ))}
74
+ </select>
75
+ )}
56
76
  {/* Small corner toggle for the stats overlay above — top-right so it never collides with
57
77
  the overlay itself (top-left) or the window's own title bar/controls above this body. */}
58
78
  <button
@@ -1,29 +1,12 @@
1
1
  /**
2
- * Fetches remote-desktop capabilities once and reports whether the feature should surface in
3
- * the UI. Returns `available=false` when the host opted out (`REMOTE_DESKTOP_ENABLED=0` the
4
- * endpoint 404s) or video capture is unavailable (no ffmpeg), so the nav entry stays hidden on
5
- * hosts that can't or won't stream.
2
+ * Whether the Remote Desktop entry should surface in the UI. Hidden only when the host opted
3
+ * out (`REMOTE_DESKTOP_ENABLED=0` the endpoint 404s) or the OS has no capture path at all.
4
+ * Everything the user can fix in place ffmpeg missing, a macOS permission not granted keeps
5
+ * the entry visible; `RemoteDesktopReadinessGate` walks them through it after the warning.
6
6
  */
7
- import { useEffect, useState } from "react";
8
- import { api } from "@/lib/api-client";
9
-
10
- interface Capabilities {
11
- ffmpegAvailable: boolean;
12
- videoAvailable: boolean;
13
- inputAvailable: boolean;
14
- authRequired: boolean;
15
- }
7
+ import { useRemoteDesktopReadiness } from "./use-remote-desktop-readiness";
16
8
 
17
9
  export function useRemoteDesktopAvailable(): { available: boolean; authRequired: boolean } {
18
- const [caps, setCaps] = useState<Capabilities | null>(null);
19
-
20
- useEffect(() => {
21
- let cancelled = false;
22
- api.get<Capabilities>("/api/remote-desktop/capabilities")
23
- .then((c) => { if (!cancelled) setCaps(c); })
24
- .catch(() => { if (!cancelled) setCaps(null); }); // 404 (flag off) or network error — stay hidden
25
- return () => { cancelled = true; };
26
- }, []);
27
-
28
- return { available: !!caps?.videoAvailable, authRequired: !!caps?.authRequired };
10
+ const { caps } = useRemoteDesktopReadiness(false);
11
+ return { available: !!caps?.platformSupported, authRequired: !!caps?.authRequired };
29
12
  }
@@ -39,8 +39,15 @@ export interface UseRemoteDesktopConnectionResult {
39
39
  getFrameCount: () => number;
40
40
  }
41
41
 
42
+ export interface UseRemoteDesktopConnectionOptions {
43
+ /** One of `/capabilities`' `displays[].id`; undefined = the host's primary display. Changing it
44
+ * tears the session down and reconnects on the new display — one ffmpeg per display. */
45
+ displayId?: string;
46
+ }
47
+
42
48
  export function useRemoteDesktopConnection(
43
49
  canvasRef: React.RefObject<HTMLCanvasElement | null>,
50
+ { displayId }: UseRemoteDesktopConnectionOptions = {},
44
51
  ): UseRemoteDesktopConnectionResult {
45
52
  const wsRef = useRef<WebSocket | null>(null);
46
53
  const totalBytesRef = useRef(0);
@@ -82,7 +89,7 @@ export function useRemoteDesktopConnection(
82
89
  wsRef.current = ws;
83
90
 
84
91
  ws.onopen = () => {
85
- ws.send(JSON.stringify({ type: "auth", nonce }));
92
+ ws.send(JSON.stringify({ type: "auth", nonce, ...(displayId ? { displayId } : {}) }));
86
93
  pingTimer = setInterval(() => sendMessage({ type: "ping" }), PING_INTERVAL_MS);
87
94
  };
88
95
  ws.onmessage = (event) => {
@@ -128,8 +135,8 @@ export function useRemoteDesktopConnection(
128
135
  wsRef.current = null;
129
136
  try { decoder.reset(); } catch { /* tearing down regardless */ }
130
137
  };
131
- // eslint-disable-next-line react-hooks/exhaustive-deps -- reconnect is driven by `generation`
132
- }, [generation]);
138
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- reconnect is driven by `generation` + the display pick
139
+ }, [generation, displayId]);
133
140
 
134
141
  const reconnect = useCallback(() => setGeneration((g) => g + 1), []);
135
142
  const getTotalBytes = useCallback(() => totalBytesRef.current, []);
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Which host display the viewer is on. Reads the list from `/capabilities`, defaults to the
3
+ * primary, and exposes `next()` for a one-button cycle (mobile toolbar) plus `select()` for a
4
+ * dropdown (desktop). Hosts with a single display never surface a control — `displays.length`
5
+ * tells the caller whether to render one.
6
+ */
7
+ import { useCallback, useState } from "react";
8
+ import { useRemoteDesktopReadiness, type RemoteDisplay } from "./use-remote-desktop-readiness";
9
+
10
+ export interface RemoteDesktopDisplayChoice {
11
+ displays: RemoteDisplay[];
12
+ /** undefined until the list arrives or when the pick is the primary — the server treats both as "primary". */
13
+ displayId: string | undefined;
14
+ current: RemoteDisplay | undefined;
15
+ select: (id: string) => void;
16
+ next: () => void;
17
+ }
18
+
19
+ export function useRemoteDesktopDisplayChoice(): RemoteDesktopDisplayChoice {
20
+ const { caps } = useRemoteDesktopReadiness(false);
21
+ const displays = caps?.displays ?? [];
22
+ const [picked, setPicked] = useState<string | undefined>(undefined);
23
+ const current = displays.find((d) => d.id === picked) ?? displays.find((d) => d.primary) ?? displays[0];
24
+ const next = useCallback(() => {
25
+ if (displays.length < 2) return;
26
+ const i = displays.findIndex((d) => d.id === (current?.id));
27
+ setPicked(displays[(i + 1) % displays.length]?.id);
28
+ }, [displays, current]);
29
+ return { displays, displayId: picked, current, select: setPicked, next };
30
+ }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Client view of `GET /api/remote-desktop/capabilities`: the host's requirements checklist
3
+ * (ffmpeg, macOS permissions, …) plus the derived `videoReady` / `inputReady` flags.
4
+ *
5
+ * Mirrors the server types in `src/services/remote-desktop/remote-desktop-requirements.ts` —
6
+ * the UI renders whatever items arrive and never branches on the host OS. Polls while `poll`
7
+ * is on so a permission granted in System Settings (or `brew install ffmpeg` finishing in the
8
+ * dock terminal) flips the panel without a reload.
9
+ */
10
+ import { useCallback, useEffect, useState } from "react";
11
+ import { api } from "@/lib/api-client";
12
+
13
+ export type RequirementAction =
14
+ | { kind: "terminal"; label: string; command: string }
15
+ | { kind: "link"; label: string; url: string }
16
+ | { kind: "host"; label: string; action: "request" | "open-settings" };
17
+
18
+ export interface RemoteDesktopRequirement {
19
+ id: string;
20
+ ok: boolean;
21
+ gates: "video" | "input";
22
+ title: string;
23
+ detail: string;
24
+ actions: RequirementAction[];
25
+ }
26
+
27
+ export interface RemoteDisplay {
28
+ id: string;
29
+ label: string;
30
+ primary: boolean;
31
+ width: number;
32
+ height: number;
33
+ }
34
+
35
+ export interface RemoteDesktopCapabilities {
36
+ displays: RemoteDisplay[];
37
+ ffmpegAvailable: boolean;
38
+ videoAvailable: boolean;
39
+ inputAvailable: boolean;
40
+ authRequired: boolean;
41
+ platform: string;
42
+ platformSupported: boolean;
43
+ requirements: RemoteDesktopRequirement[];
44
+ videoReady: boolean;
45
+ inputReady: boolean;
46
+ }
47
+
48
+ export const READINESS_POLL_MS = 2000;
49
+
50
+ export function useRemoteDesktopReadiness(poll: boolean): {
51
+ caps: RemoteDesktopCapabilities | null;
52
+ /** 404 (feature flag off) or network failure — treat as "not available". */
53
+ failed: boolean;
54
+ refresh: () => void;
55
+ runHostAction: (id: string, action: "request" | "open-settings") => Promise<void>;
56
+ } {
57
+ const [caps, setCaps] = useState<RemoteDesktopCapabilities | null>(null);
58
+ const [failed, setFailed] = useState(false);
59
+ const [tick, setTick] = useState(0);
60
+ const refresh = useCallback(() => setTick((t) => t + 1), []);
61
+
62
+ useEffect(() => {
63
+ let cancelled = false;
64
+ api.get<RemoteDesktopCapabilities>("/api/remote-desktop/capabilities")
65
+ .then((c) => { if (!cancelled) { setCaps(c); setFailed(false); } })
66
+ .catch(() => { if (!cancelled) setFailed(true); });
67
+ return () => { cancelled = true; };
68
+ }, [tick]);
69
+
70
+ useEffect(() => {
71
+ if (!poll) return;
72
+ const timer = setInterval(refresh, READINESS_POLL_MS);
73
+ return () => clearInterval(timer);
74
+ }, [poll, refresh]);
75
+
76
+ const runHostAction = useCallback(async (id: string, action: "request" | "open-settings") => {
77
+ try { await api.post(`/api/remote-desktop/requirements/${encodeURIComponent(id)}/${action}`); } catch { /* panel shows state on next poll */ }
78
+ refresh();
79
+ }, [refresh]);
80
+
81
+ return { caps, failed, refresh, runHostAction };
82
+ }
@@ -1,8 +1,17 @@
1
1
  /**
2
- * Forwards keydown/keyup from a hidden `<input>` to the shared connection, so a toolbar
3
- * "Keyboard" button can bring up the phone's soft keyboard without a physical keyboard ever
4
- * being attached. Mirrors `use-remote-input-capture.ts`'s key handling exactly: `code`
5
- * (physical key, layout-independent) is sent, never `key` — the host maps `code` → VK.
2
+ * Forwards typing from a hidden `<input>` to the shared connection, so a toolbar "Keyboard"
3
+ * button can bring up the phone's soft keyboard without a physical keyboard ever being attached.
4
+ *
5
+ * Two paths, because soft keyboards are not keyboards:
6
+ * - A keydown with a real `code` (hardware keyboard, iOS for many keys) is forwarded as a
7
+ * layout-independent `key` message and `preventDefault`-ed, exactly like
8
+ * `use-remote-input-capture.ts` — that also suppresses the `beforeinput` that would follow.
9
+ * - Android soft keyboards send keydown with `code === ""` / keyCode 229 and put the actual
10
+ * characters in `beforeinput` (`insertText`) or, for predictive/IME entry, in a composition
11
+ * that only settles at `compositionend`. Those go up as `{ type: "text" }`, which the host
12
+ * types as Unicode — accents and CJK survive, no `code` mapping involved. Backspace/Enter
13
+ * arrive as `deleteContentBackward` / `insertLineBreak` and are mapped back to `key` events
14
+ * so a host without a text path still handles them.
6
15
  */
7
16
  import { useEffect, useRef, useCallback } from "react";
8
17
 
@@ -12,10 +21,32 @@ export interface UseRemoteDesktopVirtualKeyboardResult {
12
21
  show: () => void;
13
22
  }
14
23
 
15
- export function useRemoteDesktopVirtualKeyboard(
16
- sendMessage: (msg: Record<string, unknown>) => void,
17
- enabled: boolean,
18
- ): UseRemoteDesktopVirtualKeyboardResult {
24
+ type Send = (msg: Record<string, unknown>) => void;
25
+
26
+ /** `beforeinput` → messages. Exported for unit tests; returns whether the event was consumed. */
27
+ export function forwardBeforeInput(inputType: string, data: string | null, send: Send): boolean {
28
+ const tap = (code: string) => { send({ type: "key", code, down: true }); send({ type: "key", code, down: false }); };
29
+ switch (inputType) {
30
+ case "insertText":
31
+ if (data) send({ type: "text", text: data });
32
+ return true;
33
+ case "insertLineBreak":
34
+ case "insertParagraph":
35
+ tap("Enter");
36
+ return true;
37
+ case "deleteContentBackward":
38
+ tap("Backspace");
39
+ return true;
40
+ case "deleteContentForward":
41
+ tap("Delete");
42
+ return true;
43
+ default:
44
+ // insertCompositionText & friends: let the composition run; compositionend sends it.
45
+ return false;
46
+ }
47
+ }
48
+
49
+ export function useRemoteDesktopVirtualKeyboard(sendMessage: Send, enabled: boolean): UseRemoteDesktopVirtualKeyboardResult {
19
50
  const inputRef = useRef<HTMLInputElement | null>(null);
20
51
 
21
52
  useEffect(() => {
@@ -25,19 +56,39 @@ export function useRemoteDesktopVirtualKeyboard(
25
56
  // A lost keyup (blur, tab hidden, WS drop) must not leave a modifier logically held on the
26
57
  // host — force a release on every path that could lose the matching keyup.
27
58
  const releaseAll = () => sendMessage({ type: "releaseAll" });
28
- const onKeyDown = (e: KeyboardEvent) => { e.preventDefault(); sendMessage({ type: "key", code: e.code, down: true }); };
29
- const onKeyUp = (e: KeyboardEvent) => { e.preventDefault(); sendMessage({ type: "key", code: e.code, down: false }); };
59
+ const hasCode = (e: KeyboardEvent) => e.code !== "" && e.key !== "Unidentified";
60
+ const onKeyDown = (e: KeyboardEvent) => {
61
+ if (!hasCode(e)) return; // soft keyboard: the character comes via beforeinput
62
+ e.preventDefault();
63
+ sendMessage({ type: "key", code: e.code, down: true });
64
+ };
65
+ const onKeyUp = (e: KeyboardEvent) => {
66
+ if (!hasCode(e)) return;
67
+ e.preventDefault();
68
+ sendMessage({ type: "key", code: e.code, down: false });
69
+ };
70
+ const onBeforeInput = (e: InputEvent) => {
71
+ if (forwardBeforeInput(e.inputType, e.data, sendMessage)) e.preventDefault();
72
+ };
73
+ const onCompositionEnd = (e: CompositionEvent) => {
74
+ if (e.data) sendMessage({ type: "text", text: e.data });
75
+ input.value = ""; // the composed text landed in the input; the host already has it
76
+ };
30
77
  const onBlur = () => releaseAll();
31
78
  const onVisibilityChange = () => { if (document.hidden) releaseAll(); };
32
79
 
33
80
  input.addEventListener("keydown", onKeyDown);
34
81
  input.addEventListener("keyup", onKeyUp);
82
+ input.addEventListener("beforeinput", onBeforeInput);
83
+ input.addEventListener("compositionend", onCompositionEnd);
35
84
  input.addEventListener("blur", onBlur);
36
85
  document.addEventListener("visibilitychange", onVisibilityChange);
37
86
 
38
87
  return () => {
39
88
  input.removeEventListener("keydown", onKeyDown);
40
89
  input.removeEventListener("keyup", onKeyUp);
90
+ input.removeEventListener("beforeinput", onBeforeInput);
91
+ input.removeEventListener("compositionend", onCompositionEnd);
41
92
  input.removeEventListener("blur", onBlur);
42
93
  document.removeEventListener("visibilitychange", onVisibilityChange);
43
94
  releaseAll();
package/.rdtest2-cf.out DELETED
File without changes
@@ -1 +0,0 @@
1
- import"./vendor-markdown-Ls5AcU91.js";import"./vendor-ui-CaeeFO0B.js";import{i as e,n as t}from"./api-client-BzWM7XPR.js";export{t as api,e as projectUrl};
@@ -1 +0,0 @@
1
- import"./vendor-markdown-Ls5AcU91.js";import"./vendor-ui-CaeeFO0B.js";import"./api-client-BzWM7XPR.js";import{v as e}from"./api-settings-Cubuqjsu.js";export{e as updateDeviceName};