@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.
- package/CHANGELOG.md +10 -0
- package/CLAUDE.md +4 -0
- package/assets/skills/ppm/SKILL.md +1 -1
- package/assets/skills/ppm/references/cli-reference.md +4 -4
- package/assets/skills/ppm/references/http-api.md +2 -1
- package/dist/web/assets/{adaptive-context-menu-BbuRVTLn.js → adaptive-context-menu-JfuUSqbe.js} +1 -1
- package/dist/web/assets/{ai-resource-editor-Cm4n3VXt.js → ai-resource-editor-MfdJKVM5.js} +1 -1
- package/dist/web/assets/{ai-settings-section-BEM1z7kp.js → ai-settings-section-CJE7jFsj.js} +1 -1
- package/dist/web/assets/{angular-html-BlcrLjUx.js → angular-html-VzxSzTCy.js} +1 -1
- package/dist/web/assets/{angular-ts-BUjtceAJ.js → angular-ts-CmJ4bmzM.js} +1 -1
- package/dist/web/assets/api-client-DLWwuUkJ.js +1 -0
- package/dist/web/assets/{api-client-BzWM7XPR.js → api-client-qRWu6_s5.js} +1 -1
- package/dist/web/assets/api-settings-BFuaZrNC.js +1 -0
- package/dist/web/assets/{api-settings-Cubuqjsu.js → api-settings-CPg4yGvS.js} +1 -1
- package/dist/web/assets/{api-themes-YsZgf6Xj.js → api-themes-DsAp8m7m.js} +1 -1
- package/dist/web/assets/{apl-B3g0v1pk.js → apl-do17bnVx.js} +1 -1
- package/dist/web/assets/{astro-FRnyn7cH.js → astro-ClY0e0OA.js} +1 -1
- package/dist/web/assets/{audio-preview-DWTRm4HC.js → audio-preview-Fw935Mok.js} +1 -1
- package/dist/web/assets/{blade-NLdOqUKw.js → blade-BXSdXDGq.js} +1 -1
- package/dist/web/assets/{bot-D6sx11y0.js → bot-CWZAuloY.js} +1 -1
- package/dist/web/assets/{bsl-CenHZfaU.js → bsl-MxsMKDUZ.js} +1 -1
- package/dist/web/assets/{button-Cq8yixjf.js → button-CrwgGd1y.js} +1 -1
- package/dist/web/assets/{cairo-B_WR0aiQ.js → cairo-4CIrzX_p.js} +1 -1
- package/dist/web/assets/chat-tab-bSOVoC5N.js +15 -0
- package/dist/web/assets/{chevron-down-Dp0rpHb8.js → chevron-down-D7CbB5f6.js} +1 -1
- package/dist/web/assets/chevron-right-D7Pn5agw.js +1 -0
- package/dist/web/assets/circle-4g2coECH.js +1 -0
- package/dist/web/assets/{circle-question-mark-CzR7YRc-.js → circle-question-mark-BBzFZrNh.js} +1 -1
- package/dist/web/assets/clock-DbiNiwCG.js +1 -0
- package/dist/web/assets/{cobol-DOynhWYj.js → cobol-phPOE89d.js} +1 -1
- package/dist/web/assets/code-DdKXqtf_.js +1 -0
- package/dist/web/assets/code-editor-COTa37oe.js +10 -0
- package/dist/web/assets/{coffee-Bn6dbw4P.js → coffee-Cjuukfz3.js} +1 -1
- package/dist/web/assets/{conflict-editor-Bi2RWfKQ.js → conflict-editor-Ni0PPsKu.js} +1 -1
- package/dist/web/assets/{context-menu-DfWZAxQy.js → context-menu-BEAa1qhd.js} +1 -1
- package/dist/web/assets/{copy-BewHj66E.js → copy-CbPdNp7E.js} +1 -1
- package/dist/web/assets/{cpp-CoZR9t8r.js → cpp-CT7tGBC3.js} +1 -1
- package/dist/web/assets/{createLucideIcon-D8pyde1A.js → createLucideIcon-ConhwJnb.js} +1 -1
- package/dist/web/assets/{crystal-BSheONSg.js → crystal-D8U1Qnqh.js} +1 -1
- package/dist/web/assets/{csv-preview-DxGZ1qg5.js → csv-preview-Coc2oed1.js} +1 -1
- package/dist/web/assets/{data-grid-overlay-editor-gwPoVSCU.js → data-grid-overlay-editor-B8WQkZDB.js} +1 -1
- package/dist/web/assets/{data-grid-types-Dq_H1IPa.js → data-grid-types-KpjaKAci.js} +1 -1
- package/dist/web/assets/{database-Y3ESDK4-.js → database-BqyeGogo.js} +1 -1
- package/dist/web/assets/{database-viewer-DSPRzZny.js → database-viewer-Dta7lSSk.js} +1 -1
- package/dist/web/assets/{dialog-BSfuiTkJ.js → dialog-DrL3WEXd.js} +1 -1
- package/dist/web/assets/{diff-viewer-YbpnNv5Y.js → diff-viewer-CT0lmDc9.js} +1 -1
- package/dist/web/assets/{dist-aDSxTR_v.js → dist-C2_dKAa_.js} +1 -1
- package/dist/web/assets/{docx-preview-DqVDOMmv.js → docx-preview-76KDw1eE.js} +1 -1
- package/dist/web/assets/{download-DG3_9xXy.js → download-DAF7SXcf.js} +1 -1
- package/dist/web/assets/{edge-DTtoslQu.js → edge-Db4ze6bw.js} +1 -1
- package/dist/web/assets/{edit-diff-preview-CXSbSUBX.js → edit-diff-preview-DyC_-4My.js} +1 -1
- package/dist/web/assets/{elixir-CIBCZ3Zi.js → elixir-RZPZ0472.js} +1 -1
- package/dist/web/assets/{elm-BLmb7K1H.js → elm-DNZcQt_7.js} +1 -1
- package/dist/web/assets/{erb-BBY8ApRd.js → erb-DRONsZXs.js} +1 -1
- package/dist/web/assets/{erlang-D5Zb5_P0.js → erlang-DeG0-QwJ.js} +1 -1
- package/dist/web/assets/{esm-6F8v1jLL.js → esm-CZy-sSoK.js} +1 -1
- package/dist/web/assets/explorer-body-BGjR2Gbt.js +2 -0
- package/dist/web/assets/explorer-window-content-DAhHh5EQ.js +1 -0
- package/dist/web/assets/{extension-store-Bhu--fPX.js → extension-store-ClQpnxQr.js} +1 -1
- package/dist/web/assets/{extension-webview-BSh6KGd7.js → extension-webview-BW5GuBnH.js} +1 -1
- package/dist/web/assets/{external-link-B6Hl0U4X.js → external-link-ojDHcQxQ.js} +1 -1
- package/dist/web/assets/{eye-off-CUdzgmX8.js → eye-off-Rq1n2-oe.js} +1 -1
- package/dist/web/assets/{file-exclamation-point-JzhTU2TX.js → file-exclamation-point-Bpg-zDPP.js} +1 -1
- package/dist/web/assets/{file-store-6HW2Lfl4.js → file-store-28e44KP3.js} +1 -1
- package/dist/web/assets/{folder-symlink-Dx1hOk8X.js → folder-symlink-LD-mr54V.js} +1 -1
- package/dist/web/assets/{fortran-fixed-form-CBdwkEUJ.js → fortran-fixed-form-CK5VRJNd.js} +1 -1
- package/dist/web/assets/{fsharp-AT4XdVYH.js → fsharp-TVVgzHWp.js} +1 -1
- package/dist/web/assets/{gdresource-D852fWBR.js → gdresource-DiUcyewI.js} +1 -1
- package/dist/web/assets/{git-commit-DWfWAyxy.js → git-commit-CfOruFrg.js} +1 -1
- package/dist/web/assets/{git-log-panel-CkTiR70w.js → git-log-panel-D1-YXZ0O.js} +1 -1
- package/dist/web/assets/{git-rebase-C6opk8gr.js → git-rebase-Bxyr24oc.js} +1 -1
- package/dist/web/assets/{glide-data-grid-BB5mCaCD.js → glide-data-grid-BSsqxe2v.js} +6 -6
- package/dist/web/assets/{glimmer-js-DLi5mWYO.js → glimmer-js-CT5aJxz_.js} +1 -1
- package/dist/web/assets/{glimmer-ts-D-scFX30.js → glimmer-ts-CRjP9Yey.js} +1 -1
- package/dist/web/assets/{glsl-dbQzF4iA.js → glsl-BX2--oI_.js} +1 -1
- package/dist/web/assets/{graphql-D1m5Wen2.js → graphql-BIQsvBxQ.js} +1 -1
- package/dist/web/assets/{group-chat-tab-BPIOdFJw.js → group-chat-tab-CMtzoaFc.js} +1 -1
- package/dist/web/assets/{hack-CqYmpO7u.js → hack-CTWk0k1m.js} +1 -1
- package/dist/web/assets/{haml-BXzKWEag.js → haml-DGh27hXc.js} +1 -1
- package/dist/web/assets/{hand-x0ejq3fS.js → hand-DKSxDa5c.js} +1 -1
- package/dist/web/assets/{handlebars-DXnrtHtq.js → handlebars-BJL1jNrG.js} +1 -1
- package/dist/web/assets/{html-DhgQJ-xL.js → html-DALyDNqU.js} +1 -1
- package/dist/web/assets/{html-derivative-xZRft-YE.js → html-derivative-B4XcU57e.js} +1 -1
- package/dist/web/assets/{http-B53mSj14.js → http-DfMo7nNu.js} +1 -1
- package/dist/web/assets/{hurl-CsomVy04.js → hurl-CGc_n2Qf.js} +1 -1
- package/dist/web/assets/{hxml-LffjWwKA.js → hxml-40zZ1t4x.js} +1 -1
- package/dist/web/assets/{image-preview-CNA6XMNS.js → image-preview-Cp3XB8UO.js} +1 -1
- package/dist/web/assets/index-BkHlMfPD.js +52 -0
- package/dist/web/assets/index-SFFv2-VM.css +2 -0
- package/dist/web/assets/{input-D0ol5jW0.js → input-DTmI80l2.js} +1 -1
- package/dist/web/assets/{jinja-CCHURl7V.js → jinja-D5zuIn9u.js} +1 -1
- package/dist/web/assets/{jison-7qBd8dnR.js → jison-Dxut9QwA.js} +1 -1
- package/dist/web/assets/{julia-BY9pnTcy.js → julia-Cu85tTPo.js} +1 -1
- package/dist/web/assets/{just-D7wRSNRr.js → just-CctM4uLa.js} +1 -1
- package/dist/web/assets/{keybindings-store-BGkB72gM.js → keybindings-store-Bi0q9x4Q.js} +1 -1
- package/dist/web/assets/keybindings-store-_KfovSz7.js +1 -0
- package/dist/web/assets/{keyboard-DBBaoQSu.js → keyboard-AdwmYDSu.js} +1 -1
- package/dist/web/assets/{latex-RgsCae55.js → latex-kpwS_CC7.js} +1 -1
- package/dist/web/assets/{lib-v9x_qXgI.js → lib-Ca7ai8xa.js} +1 -1
- package/dist/web/assets/{liquid-CnDFzvOZ.js → liquid-B8Rr0xue.js} +1 -1
- package/dist/web/assets/loader-circle-DD65e9-7.js +1 -0
- package/dist/web/assets/{lock-CNxUGpTk.js → lock-BEtt_eTO.js} +1 -1
- package/dist/web/assets/{lua-0lj5W8sm.js → lua-C_3gixwM.js} +1 -1
- package/dist/web/assets/markdown-renderer-DroTmKbJ.js +1 -0
- package/dist/web/assets/markdown-renderer-ntDi3Tx-.js +3 -0
- package/dist/web/assets/{marko-CQgqAhbV.js → marko-B28rvNL-.js} +1 -1
- package/dist/web/assets/{mdc-BkIcTiIy.js → mdc-DQ6-STFO.js} +1 -1
- package/dist/web/assets/{media-url-DDciwvLU.js → media-url-BjvbfTTl.js} +1 -1
- package/dist/web/assets/{mobile-bottom-sheet-pjcHKorA.js → mobile-bottom-sheet-DXn5g2LB.js} +1 -1
- package/dist/web/assets/mobile-explorer-sheet-BP6iU4ma.js +1 -0
- package/dist/web/assets/{monitor-CxPV_fQI.js → monitor-CXI_XpWJ.js} +1 -1
- package/dist/web/assets/{monitor-smartphone-DLZ6Q05s.js → monitor-smartphone-C1wUPOSg.js} +1 -1
- package/dist/web/assets/{nextflow-DvIW68DS.js → nextflow-B1xq2O8L.js} +1 -1
- package/dist/web/assets/{nginx-O-hft0gG.js → nginx-xsLxRGxO.js} +1 -1
- package/dist/web/assets/{nim-BxrR4HwQ.js → nim-wqcek3WO.js} +1 -1
- package/dist/web/assets/notification-store-II5Kw9rD.js +1 -0
- package/dist/web/assets/{number-overlay-editor-BoLRYuZT.js → number-overlay-editor-1c4sgZJt.js} +1 -1
- package/dist/web/assets/{overview-panel-l3g1gT5e.js → overview-panel-Cr2xsHc6.js} +1 -1
- package/dist/web/assets/{panel-store-UOV8wrfR.js → panel-store-Dv28vz3L.js} +1 -1
- package/dist/web/assets/{pdf-preview-B2CJ_EKv.js → pdf-preview-DKeIIJTv.js} +1 -1
- package/dist/web/assets/{pencil-BlIIg6RH.js → pencil-CHKLOgLs.js} +1 -1
- package/dist/web/assets/{perl-Da-uNqmL.js → perl-BH54gN9q.js} +1 -1
- package/dist/web/assets/{php-JMu45mEx.js → php-Br5ZrEZm.js} +1 -1
- package/dist/web/assets/play-CPbVzGgq.js +1 -0
- package/dist/web/assets/{plus-BR0qewJI.js → plus-BxqD2FIZ.js} +1 -1
- package/dist/web/assets/portal-container-context-BmG4--I-.js +1 -0
- package/dist/web/assets/{postgres-viewer-23xdZYxp.js → postgres-viewer-BJPr_FYL.js} +3 -3
- package/dist/web/assets/{process-table-CUsPf7wv.js → process-table-BqTcAJ-h.js} +1 -1
- package/dist/web/assets/{project-store-DTUCS65m.js → project-store-KHjFTCxf.js} +1 -1
- package/dist/web/assets/{pug-CLzyQhia.js → pug-8VCubZ3H.js} +1 -1
- package/dist/web/assets/{qml-BHXfGB1l.js → qml-D8AF1RZ6.js} +1 -1
- package/dist/web/assets/{razor-BqetJLN3.js → razor-6v042IJW.js} +1 -1
- package/dist/web/assets/{react-B549sK_T.js → react-DHs6efqI.js} +1 -1
- package/dist/web/assets/{refresh-cw-BQU5g2CY.js → refresh-cw-BALzw7L3.js} +1 -1
- package/dist/web/assets/remote-desktop-mobile-sheet-CYZUjYTO.js +2 -0
- package/dist/web/assets/remote-desktop-mobile-view-DaoB49k4.js +1 -0
- package/dist/web/assets/remote-desktop-readiness-gate--LJgsuoj.js +1 -0
- package/dist/web/assets/remote-desktop-window-content-Cihx4PNZ.js +1 -0
- package/dist/web/assets/{rotate-ccw-CpjRo90J.js → rotate-ccw-CuvNBM80.js} +1 -1
- package/dist/web/assets/{rst-iQdJZ0BP.js → rst-mTGFo4AQ.js} +1 -1
- package/dist/web/assets/{ruby-CNwzZGdH.js → ruby-BBaOP636.js} +1 -1
- package/dist/web/assets/{run-in-terminal-CBXmVGWa.js → run-in-terminal-Zl5iIx-B.js} +1 -1
- package/dist/web/assets/{sas-BpBIT2eR.js → sas-D5QEkIH2.js} +1 -1
- package/dist/web/assets/{save-DYLiVzU8.js → save-Dxq-Zepc.js} +1 -1
- package/dist/web/assets/{scroll-area-lc0ZHORr.js → scroll-area-B5yhPpZo.js} +1 -1
- package/dist/web/assets/{scss-D1MotuEp.js → scss-C5Mu6y2u.js} +1 -1
- package/dist/web/assets/{send-C_sLFt56.js → send-Cits8q0l.js} +1 -1
- package/dist/web/assets/{send-to-chat-DJAdlvJ8.js → send-to-chat-Cv7ikFUC.js} +1 -1
- package/dist/web/assets/{settings-store-zbPkG7o-.js → settings-store-Dp4i8zeA.js} +2 -2
- package/dist/web/assets/{settings-tab-xyRpt2gq.js → settings-tab-BtMvvKAF.js} +1 -1
- package/dist/web/assets/settings-tab-UPPycGRF.js +1 -0
- package/dist/web/assets/{shaderlab-BNqmcPQi.js → shaderlab-mLnpMpN3.js} +1 -1
- package/dist/web/assets/{shallow-CmPMAPte.js → shallow-BBJ_qoEF.js} +1 -1
- package/dist/web/assets/{shellsession-BBLOVOO-.js → shellsession-qdcDfUuk.js} +1 -1
- package/dist/web/assets/{shield-check-CUI36aSc.js → shield-check-CH7F1P7m.js} +1 -1
- package/dist/web/assets/{shield-off-GdkkVhb3.js → shield-off-C9HiF0-6.js} +1 -1
- package/dist/web/assets/{soy-BpTGo9uy.js → soy-CGGf1G43.js} +1 -1
- package/dist/web/assets/{sparkles-Dh-_J2Rd.js → sparkles-BnuLH6Ck.js} +1 -1
- package/dist/web/assets/{sparql-D7A3yKob.js → sparql-tQHB50VD.js} +1 -1
- package/dist/web/assets/{sql-query-editor-BE3mLYyz.js → sql-query-editor-DZqnYIB7.js} +1 -1
- package/dist/web/assets/{sqlite-viewer-wN5RP8sd.js → sqlite-viewer-DXutos0e.js} +1 -1
- package/dist/web/assets/{stata-B7N56ptS.js → stata-DVQNLMcR.js} +1 -1
- package/dist/web/assets/{surrealql-CTmnD-lv.js → surrealql-lu576tBi.js} +1 -1
- package/dist/web/assets/{svelte-BppwY9TR.js → svelte-CSCrOBjT.js} +1 -1
- package/dist/web/assets/{switch-BmjKoIpR.js → switch-BPol3BFC.js} +1 -1
- package/dist/web/assets/{system-monitor-body-B2M0MC6y.js → system-monitor-body-Ct8_GX_f.js} +2 -2
- package/dist/web/assets/system-monitor-tab-DC_EerdI.js +1 -0
- package/dist/web/assets/system-monitor-window-content-D25DMNpw.js +1 -0
- package/dist/web/assets/{tab-host-window-content-DKev3GJz.js → tab-host-window-content-C5kYP0-z.js} +1 -1
- package/dist/web/assets/{tab-store-DzYSsw5a.js → tab-store-DS3YlUwR.js} +1 -1
- package/dist/web/assets/{table-D5Q4ap4W.js → table-BXDyImqk.js} +1 -1
- package/dist/web/assets/team-member-sheet-DvmioeQL.js +2 -0
- package/dist/web/assets/{team-member-window-content-CK08f5cK.js → team-member-window-content-BJ_XFw8-.js} +1 -1
- package/dist/web/assets/{templ-CvTMKKra.js → templ-CPt8AS4Q.js} +1 -1
- package/dist/web/assets/{terminal-tab-DZ3QiYcs.js → terminal-tab-8bKQ3CWR.js} +3 -3
- package/dist/web/assets/{tex--c4POEUE.js → tex-BrTByT7i.js} +1 -1
- package/dist/web/assets/{text-wrap-DBvQD3kc.js → text-wrap-CzMjB6GD.js} +1 -1
- package/dist/web/assets/{tool-cards-Bw1P0MKU.js → tool-cards-pIu_cmEL.js} +4 -4
- package/dist/web/assets/{trash-2-By4VMbC4.js → trash-2-BjShZ7if.js} +1 -1
- package/dist/web/assets/{triangle-alert-CUTURFbG.js → triangle-alert-qQ6Plydp.js} +1 -1
- package/dist/web/assets/{ts-tags--kx7D-Hw.js → ts-tags-DSqEZ7A8.js} +1 -1
- package/dist/web/assets/{twig-UG9uMHGd.js → twig-DW9qc1bf.js} +1 -1
- package/dist/web/assets/{upload-BNlX8D07.js → upload-7Zs6oaK5.js} +1 -1
- package/dist/web/assets/{use-blob-url-Bdxopwwc.js → use-blob-url-DKbV7PM_.js} +1 -1
- package/dist/web/assets/{use-coarse-long-press-BmI_GVW5.js → use-coarse-long-press-DGyC48NF.js} +1 -1
- package/dist/web/assets/{use-is-mobile-B9UZNwSm.js → use-is-mobile-ClE-UcPF.js} +1 -1
- package/dist/web/assets/{use-monaco-theme-BOmd2wpe.js → use-monaco-theme-DaxAk3gB.js} +1 -1
- package/dist/web/assets/use-open-team-member-CopUuDHG.js +1 -0
- package/dist/web/assets/use-remote-desktop-display-choice-BUA_1eaH.js +1 -0
- package/dist/web/assets/{use-visual-viewport-Sfb6CZ80.js → use-visual-viewport-CCX21c9w.js} +1 -1
- package/dist/web/assets/{use-websocket-B7Xj-Q4W.js → use-websocket-CWpG7PWN.js} +1 -1
- package/dist/web/assets/vendor-markdown-CKTlmXbz.js +291 -0
- package/dist/web/assets/{vendor-ui-CaeeFO0B.js → vendor-ui-CXJtBULr.js} +1 -1
- package/dist/web/assets/{video-preview-B0nbTGjQ.js → video-preview-C2_fdunE.js} +1 -1
- package/dist/web/assets/{vue-jjTomlbt.js → vue-CJNDL1g-.js} +1 -1
- package/dist/web/assets/{vue-html-8KEW6-I9.js → vue-html-CG5BKtq1.js} +1 -1
- package/dist/web/assets/{vue-vine-C5tohCkU.js → vue-vine-Dd5HZ7T5.js} +1 -1
- package/dist/web/assets/{window-pip-registry-FdZdGF7h.js → window-pip-registry-9H6Y-HqR.js} +1 -1
- package/dist/web/assets/{window-store-D0x_fxr0.js → window-store-CNzkeUyO.js} +1 -1
- package/dist/web/assets/x-Dzu9ecti.js +1 -0
- package/dist/web/assets/{xml-ByFFosqL.js → xml-DTOm5niz.js} +1 -1
- package/dist/web/assets/{xsl-CZUwiLkj.js → xsl-BD_kBC04.js} +1 -1
- package/dist/web/index.html +60 -60
- package/dist/web/monacoeditorwork/css.worker.bundle.js +122 -122
- package/dist/web/monacoeditorwork/editor.worker.bundle.js +78 -78
- package/dist/web/monacoeditorwork/html.worker.bundle.js +110 -110
- package/dist/web/monacoeditorwork/json.worker.bundle.js +108 -108
- package/dist/web/monacoeditorwork/ts.worker.bundle.js +81 -81
- package/dist/web/sw.js +1 -1
- package/docs/system-architecture.md +19 -0
- package/package.json +1 -1
- package/scripts/check-ppm-dir-usage.sh +0 -0
- package/scripts/install.sh +0 -0
- package/scripts/release.sh +0 -0
- package/src/server/routes/remote-desktop.ts +23 -6
- package/src/server/ws/remote-desktop.ts +4 -2
- package/src/services/edge-forwarder.ts +191 -191
- package/src/services/remote-desktop/remote-desktop-capture-input.ts +62 -0
- package/src/services/remote-desktop/remote-desktop-capture.ts +29 -12
- package/src/services/remote-desktop/remote-desktop-cg-key-map.ts +45 -0
- package/src/services/remote-desktop/remote-desktop-displays.ts +90 -0
- package/src/services/remote-desktop/remote-desktop-encoder-args.ts +7 -0
- package/src/services/remote-desktop/remote-desktop-input-backend.ts +36 -0
- package/src/services/remote-desktop/remote-desktop-input-darwin.ts +200 -0
- package/src/services/remote-desktop/remote-desktop-input-win32.ts +243 -0
- package/src/services/remote-desktop/remote-desktop-input.ts +43 -193
- package/src/services/remote-desktop/remote-desktop-macos-permissions.ts +62 -0
- package/src/services/remote-desktop/remote-desktop-requirements.ts +124 -0
- package/src/services/remote-desktop/remote-desktop-session.ts +16 -5
- package/src/services/windows-process-tree.ts +351 -351
- package/src/web/components/remote-desktop/remote-desktop-mobile-sheet.tsx +6 -3
- package/src/web/components/remote-desktop/remote-desktop-mobile-toolbar.tsx +12 -1
- package/src/web/components/remote-desktop/remote-desktop-mobile-view.tsx +5 -1
- package/src/web/components/remote-desktop/remote-desktop-readiness-gate.tsx +128 -0
- package/src/web/components/remote-desktop/remote-desktop-window-content.tsx +22 -2
- package/src/web/components/remote-desktop/use-remote-desktop-available.ts +7 -24
- package/src/web/components/remote-desktop/use-remote-desktop-connection.ts +10 -3
- package/src/web/components/remote-desktop/use-remote-desktop-display-choice.ts +30 -0
- package/src/web/components/remote-desktop/use-remote-desktop-readiness.ts +82 -0
- package/src/web/components/remote-desktop/use-remote-desktop-virtual-keyboard.ts +61 -10
- package/.rdtest2-cf.out +0 -0
- package/dist/web/assets/api-client-D1CePgQ4.js +0 -1
- package/dist/web/assets/api-settings-CNtKkHqW.js +0 -1
- package/dist/web/assets/chat-tab-C2e6Pwc2.js +0 -15
- package/dist/web/assets/chevron-right-De-Wz5Lz.js +0 -1
- package/dist/web/assets/circle-BCy0AUCo.js +0 -1
- package/dist/web/assets/clock-C1CQUQPZ.js +0 -1
- package/dist/web/assets/code-DJgYrFzm.js +0 -1
- package/dist/web/assets/code-editor-DWtv_VDo.js +0 -10
- package/dist/web/assets/explorer-body-DpQndBVw.js +0 -2
- package/dist/web/assets/explorer-window-content-m0K6Mtcg.js +0 -1
- package/dist/web/assets/index-BHdtSKzb.js +0 -52
- package/dist/web/assets/index-DN9XUQ8D.css +0 -2
- package/dist/web/assets/keybindings-store-Bq6VWl5X.js +0 -1
- package/dist/web/assets/loader-circle-DouKgIXy.js +0 -1
- package/dist/web/assets/markdown-renderer-B9wzBWm9.js +0 -3
- package/dist/web/assets/markdown-renderer-DeSV1CCQ.js +0 -1
- package/dist/web/assets/mobile-explorer-sheet-DPdxpnKh.js +0 -1
- package/dist/web/assets/notification-store-BtijuGez.js +0 -1
- package/dist/web/assets/play-DpEEBJTT.js +0 -1
- package/dist/web/assets/portal-container-context-D-495m5C.js +0 -1
- package/dist/web/assets/remote-desktop-mobile-sheet-FTF6IBhF.js +0 -2
- package/dist/web/assets/remote-desktop-mobile-view-DSwG4OSk.js +0 -1
- package/dist/web/assets/remote-desktop-stats-overlay-Bz6xZ5y9.js +0 -1
- package/dist/web/assets/remote-desktop-warning-gate-DrYySLZ5.js +0 -1
- package/dist/web/assets/remote-desktop-window-content-Ndvezorb.js +0 -1
- package/dist/web/assets/settings-tab-Cn-AqUP_.js +0 -1
- package/dist/web/assets/system-monitor-tab-OYca4qaf.js +0 -1
- package/dist/web/assets/system-monitor-window-content-CHz9WdTm.js +0 -1
- package/dist/web/assets/team-member-sheet-BQ738Lli.js +0 -2
- package/dist/web/assets/use-open-team-member-D9EOB8-i.js +0 -1
- package/dist/web/assets/vendor-markdown-Ls5AcU91.js +0 -291
- package/dist/web/assets/x-DiOUR_mp.js +0 -1
- package/packages/vscode-compat/node_modules/.bin/tsc.bunx +0 -0
- package/packages/vscode-compat/node_modules/.bin/tsc.exe +0 -0
- package/packages/vscode-compat/node_modules/.bin/tsserver.bunx +0 -0
- package/packages/vscode-compat/node_modules/.bin/tsserver.exe +0 -0
- /package/dist/web/assets/{abap-C5DFF0s3.js → abap-Cjj2olMr.js} +0 -0
- /package/dist/web/assets/{actionscript-3-B0qyRNbc.js → actionscript-3-DCkW7kWC.js} +0 -0
- /package/dist/web/assets/{ada-bAJzplug.js → ada-AEWvZXuX.js} +0 -0
- /package/dist/web/assets/{andromeeda-DUCpISy6.js → andromeeda-D7cSpME6.js} +0 -0
- /package/dist/web/assets/{apache-BhAr84Z_.js → apache-DjvX0RSe.js} +0 -0
- /package/dist/web/assets/{apex-CRv8YnNq.js → apex-70xIZ0pH.js} +0 -0
- /package/dist/web/assets/{applescript-CD-Cw8bt.js → applescript-C3qKqxmA.js} +0 -0
- /package/dist/web/assets/{ara-BUzeQYoG.js → ara-iGt2zixk.js} +0 -0
- /package/dist/web/assets/{asciidoc-CT2hTg-v.js → asciidoc-Bs2t-06W.js} +0 -0
- /package/dist/web/assets/{asm-CAu35JeY.js → asm-B-T0eqhE.js} +0 -0
- /package/dist/web/assets/{aurora-x-BeFJoIA1.js → aurora-x-D3h_9M3E.js} +0 -0
- /package/dist/web/assets/{awk-Vau-DrGN.js → awk-CZwfwpPi.js} +0 -0
- /package/dist/web/assets/{ayu-dark-CLe-OTsQ.js → ayu-dark-DulkglEA.js} +0 -0
- /package/dist/web/assets/{ayu-light-CJbQfu4M.js → ayu-light-DyNQiRpn.js} +0 -0
- /package/dist/web/assets/{ayu-mirage-GWaKwdrx.js → ayu-mirage-CcEt4xkI.js} +0 -0
- /package/dist/web/assets/{ballerina-DWu0USI5.js → ballerina-OhQPjyHS.js} +0 -0
- /package/dist/web/assets/{bat-_07Huw7E.js → bat-DqKcJTUn.js} +0 -0
- /package/dist/web/assets/{beancount-EcvogrcF.js → beancount-BdXlMi8T.js} +0 -0
- /package/dist/web/assets/{berry-BBB87SY7.js → berry-DvgY6BkY.js} +0 -0
- /package/dist/web/assets/{bibtex-DbueS87A.js → bibtex-BSRJq0yM.js} +0 -0
- /package/dist/web/assets/{bicep-tcmXnUo4.js → bicep-DpAdumKt.js} +0 -0
- /package/dist/web/assets/{bird2-B2qwilIR.js → bird2-rlbtp8di.js} +0 -0
- /package/dist/web/assets/{c-Z58oivSt.js → c-AjOLBXIs.js} +0 -0
- /package/dist/web/assets/{c3-U40cGT9p.js → c3-DqIXz4Ia.js} +0 -0
- /package/dist/web/assets/{cadence-BFcD-LyM.js → cadence-DAJtUpOE.js} +0 -0
- /package/dist/web/assets/{catppuccin-frappe-RkC9AX6J.js → catppuccin-frappe-DUPoeZfb.js} +0 -0
- /package/dist/web/assets/{catppuccin-latte-Bsi4l-nO.js → catppuccin-latte-CK3U68Ql.js} +0 -0
- /package/dist/web/assets/{catppuccin-macchiato-B8JIdTW-.js → catppuccin-macchiato-BzteLnIX.js} +0 -0
- /package/dist/web/assets/{catppuccin-mocha-oH8wwrDj.js → catppuccin-mocha-ozLd0aBR.js} +0 -0
- /package/dist/web/assets/{clarity-C3WNeRCR.js → clarity-6KqEoBfz.js} +0 -0
- /package/dist/web/assets/{clojure-CFoAz7u9.js → clojure-B_uBL5C7.js} +0 -0
- /package/dist/web/assets/{cmake-Bcsqp9yR.js → cmake-B3nxBNmj.js} +0 -0
- /package/dist/web/assets/{codeowners-DC2Z7heP.js → codeowners-DkS-51at.js} +0 -0
- /package/dist/web/assets/{codeql-BfG68Al5.js → codeql-rpr1z_mR.js} +0 -0
- /package/dist/web/assets/{common-lisp-Z5P-lKQe.js → common-lisp-BfNbv_W6.js} +0 -0
- /package/dist/web/assets/{coq-BJslqrnv.js → coq-EAW-1nw0.js} +0 -0
- /package/dist/web/assets/{csharp-DhhZTOTs.js → csharp-BE3Gn5bq.js} +0 -0
- /package/dist/web/assets/{css-DiFIpMfE.js → css-Cr27CfPY.js} +0 -0
- /package/dist/web/assets/{csv-DViLbHxI.js → csv-B5VtaPtH.js} +0 -0
- /package/dist/web/assets/{cue-CoG3mg2I.js → cue-jm6lYNrx.js} +0 -0
- /package/dist/web/assets/{cypher-jTxC8NAB.js → cypher-DXkvK-pn.js} +0 -0
- /package/dist/web/assets/{d-6hKIUAbU.js → d-CgG1_yzd.js} +0 -0
- /package/dist/web/assets/{dark-plus-D_-np2Sr.js → dark-plus-CALzcIVB.js} +0 -0
- /package/dist/web/assets/{dart-CVpxLpVn.js → dart-BEVoLlWg.js} +0 -0
- /package/dist/web/assets/{dax-BPqJ8qIY.js → dax-H0BW3Q_P.js} +0 -0
- /package/dist/web/assets/{desktop-u_y5_Hr5.js → desktop-BtYq1l_0.js} +0 -0
- /package/dist/web/assets/{diff-Duj1XMrd.js → diff-CnBYdfTg.js} +0 -0
- /package/dist/web/assets/{dist-CEZ4plgH.js → dist-CJJKpgR7.js} +0 -0
- /package/dist/web/assets/{docker-8UGSCc7N.js → docker-COBitpld.js} +0 -0
- /package/dist/web/assets/{dotenv-DVnj8kyR.js → dotenv-CG0x1m7c.js} +0 -0
- /package/dist/web/assets/{dracula-D0cEmSg7.js → dracula-CLCi47X-.js} +0 -0
- /package/dist/web/assets/{dracula-soft-DfGS5Yfr.js → dracula-soft-BMv5cCAV.js} +0 -0
- /package/dist/web/assets/{dream-maker-qG8kMGTF.js → dream-maker-1Bd4OSww.js} +0 -0
- /package/dist/web/assets/{emacs-lisp-iVqyMLyT.js → emacs-lisp-B-2zKLn-.js} +0 -0
- /package/dist/web/assets/{everforest-dark-Cruygd5U.js → everforest-dark-BGVynCbV.js} +0 -0
- /package/dist/web/assets/{everforest-light-BiJa8RHL.js → everforest-light--rn3R7Cm.js} +0 -0
- /package/dist/web/assets/{fennel-H6XSEIoz.js → fennel-SWg9Us_u.js} +0 -0
- /package/dist/web/assets/{fish-ho_7CxX6.js → fish-B04oqgKk.js} +0 -0
- /package/dist/web/assets/{fluent-1l5ssJTF.js → fluent-BefzAGMN.js} +0 -0
- /package/dist/web/assets/{fortran-free-form-Bo1x233A.js → fortran-free-form-alw7jMxb.js} +0 -0
- /package/dist/web/assets/{gdscript-DffFMp_k.js → gdscript-BAyhvjCx.js} +0 -0
- /package/dist/web/assets/{gdshader-CvnpAiAF.js → gdshader-Crl09X-B.js} +0 -0
- /package/dist/web/assets/{genie-2eOvAEzW.js → genie-BSIGRHu7.js} +0 -0
- /package/dist/web/assets/{gherkin-DL-5OUlV.js → gherkin-DJhSnSWH.js} +0 -0
- /package/dist/web/assets/{github-dark-default-_mDH8IMP.js → github-dark-default-FVtXjcm8.js} +0 -0
- /package/dist/web/assets/{github-dark-dimmed-C48hJuLn.js → github-dark-dimmed-CzrUPgSb.js} +0 -0
- /package/dist/web/assets/{github-dark-high-contrast-BQ-zGGNe.js → github-dark-high-contrast-1s9rF5EB.js} +0 -0
- /package/dist/web/assets/{github-dark-DrTZprgG.js → github-dark-r8C6qwug.js} +0 -0
- /package/dist/web/assets/{github-light-BYc90Yx8.js → github-light-BvIWCkl7.js} +0 -0
- /package/dist/web/assets/{github-light-default-Daecj4J5.js → github-light-default-CVdhzrxV.js} +0 -0
- /package/dist/web/assets/{github-light-high-contrast-DoafCbsU.js → github-light-high-contrast-CBAf6hjF.js} +0 -0
- /package/dist/web/assets/{gleam-hUWWjmnp.js → gleam-DvL1q7aa.js} +0 -0
- /package/dist/web/assets/{gn-7PJ3R70o.js → gn-BG5NvwAf.js} +0 -0
- /package/dist/web/assets/{gnuplot-V-Pxck7b.js → gnuplot-BARnM-zG.js} +0 -0
- /package/dist/web/assets/{go-ARF6DprD.js → go-DXvjjq4q.js} +0 -0
- /package/dist/web/assets/{groovy-CKGBIPOz.js → groovy-CRylSKhr.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-hard-Cm9wqdI3.js → gruvbox-dark-hard-BVPjnqm7.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-medium-Cmsd6dG8.js → gruvbox-dark-medium-U_PDgq0a.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-soft-BBGlGGCz.js → gruvbox-dark-soft-CkzNtpbr.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-hard-BCR7KmqG.js → gruvbox-light-hard-DfMmR2G8.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-medium-CP32bga8.js → gruvbox-light-medium-BGCak-dd.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-soft-qjxk8ps6.js → gruvbox-light-soft-DnpM3fkN.js} +0 -0
- /package/dist/web/assets/{haskell-C5DzwGRa.js → haskell-DXgGIDD_.js} +0 -0
- /package/dist/web/assets/{haxe-C8f_jCVv.js → haxe-B7qlTpRz.js} +0 -0
- /package/dist/web/assets/{hcl-DetJkf6E.js → hcl-ADWfVe_Q.js} +0 -0
- /package/dist/web/assets/{hjson-DeZhwIdo.js → hjson-BaM9gLbq.js} +0 -0
- /package/dist/web/assets/{hlsl-SMIDLRyX.js → hlsl-iqaRA4MG.js} +0 -0
- /package/dist/web/assets/{horizon-BMAVDxMx.js → horizon-BPpTnhB9.js} +0 -0
- /package/dist/web/assets/{horizon-bright-3BaV_VGD.js → horizon-bright-yBLg3QbE.js} +0 -0
- /package/dist/web/assets/{houston-BWBbEPvb.js → houston-eFOp0ySL.js} +0 -0
- /package/dist/web/assets/{hy-DcsmE-Nm.js → hy-BALeMn3i.js} +0 -0
- /package/dist/web/assets/{imba-CnkgnYMf.js → imba-CdGUm_GD.js} +0 -0
- /package/dist/web/assets/{ini-BnvTdau7.js → ini-DG7imsqe.js} +0 -0
- /package/dist/web/assets/{java-BLFACXat.js → java-D5GTT474.js} +0 -0
- /package/dist/web/assets/{javascript-BKcFWENk.js → javascript-V7WwJvRm.js} +0 -0
- /package/dist/web/assets/{json-3AJtJADf.js → json-B9FhwAgR.js} +0 -0
- /package/dist/web/assets/{json5-DLyDVlwe.js → json5--Abk7MUy.js} +0 -0
- /package/dist/web/assets/{jsonc-B5DgF1jW.js → jsonc-C8jb-Z6x.js} +0 -0
- /package/dist/web/assets/{jsonl-CV_O71a2.js → jsonl-CaJNxfkm.js} +0 -0
- /package/dist/web/assets/{jsonnet-DZfPLgYz.js → jsonnet-CPrKMSCU.js} +0 -0
- /package/dist/web/assets/{jssm-Cr1zYOsY.js → jssm-DEu-9_uo.js} +0 -0
- /package/dist/web/assets/{jsx-VN-JS2_q.js → jsx-MfAsCWDW.js} +0 -0
- /package/dist/web/assets/{kanagawa-dragon-D9CcT3xg.js → kanagawa-dragon-DnbcAduF.js} +0 -0
- /package/dist/web/assets/{kanagawa-lotus-DLXsB8fr.js → kanagawa-lotus-yxsY3ew8.js} +0 -0
- /package/dist/web/assets/{kanagawa-wave-weDs3Wjz.js → kanagawa-wave-C2RyvZaM.js} +0 -0
- /package/dist/web/assets/{kdl-0XF56jnT.js → kdl-CvssmvcU.js} +0 -0
- /package/dist/web/assets/{kotlin-BPkH9vzk.js → kotlin-jufO49mU.js} +0 -0
- /package/dist/web/assets/{kusto-DMVpADb9.js → kusto-BAFnylR7.js} +0 -0
- /package/dist/web/assets/{laserwave-Cl6T2cGP.js → laserwave-BgxFC4Ge.js} +0 -0
- /package/dist/web/assets/{lean-BvwzQGl1.js → lean-Bgt24V1D.js} +0 -0
- /package/dist/web/assets/{less-DI_03xkC.js → less-Bk_UaqsM.js} +0 -0
- /package/dist/web/assets/{light-plus-hIiIsQN_.js → light-plus-nMxFfInN.js} +0 -0
- /package/dist/web/assets/{llvm-B5hjjSWc.js → llvm-3_PASDOO.js} +0 -0
- /package/dist/web/assets/{log-D28raQiE.js → log-CKm0CxB1.js} +0 -0
- /package/dist/web/assets/{logo-6Cdd4ASb.js → logo-DFP1_5U6.js} +0 -0
- /package/dist/web/assets/{luau-CK9nKpOX.js → luau-BcZnoyh3.js} +0 -0
- /package/dist/web/assets/{make-CVnxKCB6.js → make-CpNSwgwT.js} +0 -0
- /package/dist/web/assets/{markdown-B9mMQzD5.js → markdown-DhU3Djpv.js} +0 -0
- /package/dist/web/assets/{material-theme-BZSs2o0j.js → material-theme-DD5L3OqF.js} +0 -0
- /package/dist/web/assets/{material-theme-darker-BeAQBzQ_.js → material-theme-darker-CG2_tD3R.js} +0 -0
- /package/dist/web/assets/{material-theme-lighter-BAe0IXcs.js → material-theme-lighter-CzNkUc9Y.js} +0 -0
- /package/dist/web/assets/{material-theme-ocean-CiPIf0Np.js → material-theme-ocean-BtLiAaF_.js} +0 -0
- /package/dist/web/assets/{material-theme-palenight-DJBb4_XN.js → material-theme-palenight-Brvjz035.js} +0 -0
- /package/dist/web/assets/{matlab-CY0dbp_s.js → matlab-CCY0KtMD.js} +0 -0
- /package/dist/web/assets/{mdx-DU9zbF7h.js → mdx-CH6YY0aS.js} +0 -0
- /package/dist/web/assets/{mermaid-UmmjGwdh.js → mermaid-Bqh3gFI0.js} +0 -0
- /package/dist/web/assets/{min-dark-C59GkzOm.js → min-dark-BUwfTzI6.js} +0 -0
- /package/dist/web/assets/{min-light-B9QglrdQ.js → min-light-oLanXCOk.js} +0 -0
- /package/dist/web/assets/{mipsasm-Dc6Jsr_D.js → mipsasm-CTh3hPWK.js} +0 -0
- /package/dist/web/assets/{mojo-D5ij-nGW.js → mojo-DXdszZKa.js} +0 -0
- /package/dist/web/assets/{monokai-QAtE_yCP.js → monokai-TNE_tRxc.js} +0 -0
- /package/dist/web/assets/{moonbit-BMtIdiTL.js → moonbit-jrk-Gx1X.js} +0 -0
- /package/dist/web/assets/{move-BB_jMHYZ.js → move-DItn61bH.js} +0 -0
- /package/dist/web/assets/{narrat-BvvoKGVn.js → narrat-3kB4dkh0.js} +0 -0
- /package/dist/web/assets/{nextflow-groovy-CqxtAsnF.js → nextflow-groovy-CSKtwETr.js} +0 -0
- /package/dist/web/assets/{night-owl-CLoE9kjt.js → night-owl-Dm91nSIq.js} +0 -0
- /package/dist/web/assets/{night-owl-light-BwIFT5Xd.js → night-owl-light-riwrcPRg.js} +0 -0
- /package/dist/web/assets/{nix-BKlnVT9r.js → nix-sLgl9zGF.js} +0 -0
- /package/dist/web/assets/{nord-B7R1KRAK.js → nord-TWkJPI6M.js} +0 -0
- /package/dist/web/assets/{nushell-CzUme-NJ.js → nushell-CDZi4-Gf.js} +0 -0
- /package/dist/web/assets/{objective-c-D-UKKiOc.js → objective-c-DW9PboNp.js} +0 -0
- /package/dist/web/assets/{objective-cpp-BucWn4Fx.js → objective-cpp-2XCCX6q1.js} +0 -0
- /package/dist/web/assets/{ocaml-EJCNXMGo.js → ocaml-CxPdnjrh.js} +0 -0
- /package/dist/web/assets/{odin-CEjnRU_3.js → odin-BJjlKpkM.js} +0 -0
- /package/dist/web/assets/{one-dark-pro-Bv469NX5.js → one-dark-pro-K1KOmBZ7.js} +0 -0
- /package/dist/web/assets/{one-light-BlzO5RWI.js → one-light-DQB-vUyZ.js} +0 -0
- /package/dist/web/assets/{openscad-Czl6alLw.js → openscad-DCoLwn4j.js} +0 -0
- /package/dist/web/assets/{pascal-BI9fLvG4.js → pascal-z2ktbbWp.js} +0 -0
- /package/dist/web/assets/{pkl-ChQLsyjU.js → pkl-Dyq4-v6z.js} +0 -0
- /package/dist/web/assets/{plastic-DGL3mtdL.js → plastic-C4Rq5PnD.js} +0 -0
- /package/dist/web/assets/{plsql-BJDjqcWp.js → plsql-BCIjO63y.js} +0 -0
- /package/dist/web/assets/{po-DK5O3OpF.js → po-_kXpd_0n.js} +0 -0
- /package/dist/web/assets/{poimandres-CnuqZT4T.js → poimandres-C9XggjB3.js} +0 -0
- /package/dist/web/assets/{polar-DCsUxSsQ.js → polar-DV_1L04z.js} +0 -0
- /package/dist/web/assets/{postcss-8UjdFmis.js → postcss-BuqS91mQ.js} +0 -0
- /package/dist/web/assets/{powerquery-CAp0CGQj.js → powerquery-DK13B2Dc.js} +0 -0
- /package/dist/web/assets/{powershell-CbBsKZJP.js → powershell-tkuhhtuP.js} +0 -0
- /package/dist/web/assets/{prisma-BnNpXCJN.js → prisma-WNXfBtpr.js} +0 -0
- /package/dist/web/assets/{prolog-CC-l9E8Y.js → prolog-BzEdNKfO.js} +0 -0
- /package/dist/web/assets/{proto-CV_1k2rL.js → proto-B5meVsrA.js} +0 -0
- /package/dist/web/assets/{puppet-2D8HU2bv.js → puppet-C7h_p4r2.js} +0 -0
- /package/dist/web/assets/{purescript-BEJAORgU.js → purescript-qH1KxAs6.js} +0 -0
- /package/dist/web/assets/{python-BBdbb7nn.js → python-DeTIYd5V.js} +0 -0
- /package/dist/web/assets/{qmldir-D5ZYI4Ie.js → qmldir-4Rm5AvD3.js} +0 -0
- /package/dist/web/assets/{qss-BOJVGyXE.js → qss-YRbeNtr0.js} +0 -0
- /package/dist/web/assets/{r-B5dlEtkj.js → r-DHwHK0Jh.js} +0 -0
- /package/dist/web/assets/{racket-BUAEygJ6.js → racket-gKn4SFLQ.js} +0 -0
- /package/dist/web/assets/{raku-BG0Ss0ID.js → raku-DWTmAj3A.js} +0 -0
- /package/dist/web/assets/{red-C5yyQbtG.js → red-BFuHRZFS.js} +0 -0
- /package/dist/web/assets/{reg-DHoBWtLf.js → reg-DYoOF5tH.js} +0 -0
- /package/dist/web/assets/{regexp-DDtJrlKv.js → regexp-6rXQHD2-.js} +0 -0
- /package/dist/web/assets/{rel-CR6WJDRD.js → rel-Z_tTruCU.js} +0 -0
- /package/dist/web/assets/{riscv-2V9tC-nx.js → riscv-DfTfKuvU.js} +0 -0
- /package/dist/web/assets/{ron-QLTmqGUw.js → ron-CsUtW8gQ.js} +0 -0
- /package/dist/web/assets/{rose-pine-be0__X3A.js → rose-pine-Dng_vCo6.js} +0 -0
- /package/dist/web/assets/{rose-pine-dawn-n_WQJwsW.js → rose-pine-dawn-Ds88Nezf.js} +0 -0
- /package/dist/web/assets/{rose-pine-moon-t2aLtzgB.js → rose-pine-moon-DWpzrqNz.js} +0 -0
- /package/dist/web/assets/{rosmsg-Ce1JD8vt.js → rosmsg-h2sO84oD.js} +0 -0
- /package/dist/web/assets/{rust-5fHH-ZoW.js → rust-Dx86yfCi.js} +0 -0
- /package/dist/web/assets/{sass-DPmGFiOJ.js → sass-BwwVhdT9.js} +0 -0
- /package/dist/web/assets/{scala-BRMVFbZF.js → scala-WJqNEN-w.js} +0 -0
- /package/dist/web/assets/{scheme-BMSJWevF.js → scheme-uwj-t1Rp.js} +0 -0
- /package/dist/web/assets/{sdbl-DTZ-pEHI.js → sdbl-BYy5hgEz.js} +0 -0
- /package/dist/web/assets/{shellscript-6-9yg5Cz.js → shellscript-DWTW_w2K.js} +0 -0
- /package/dist/web/assets/{slack-dark-DKJgiR7e.js → slack-dark-BvhGq5vQ.js} +0 -0
- /package/dist/web/assets/{slack-ochin-WjehVgyK.js → slack-ochin-C9g0ZRFr.js} +0 -0
- /package/dist/web/assets/{smalltalk-BChf4SN1.js → smalltalk-ZEiZ6i7U.js} +0 -0
- /package/dist/web/assets/{snazzy-light-C1WAwOx6.js → snazzy-light-29OA24q5.js} +0 -0
- /package/dist/web/assets/{solarized-dark-BxREZrGx.js → solarized-dark-Bnq9IOn_.js} +0 -0
- /package/dist/web/assets/{solarized-light-DG29B1pR.js → solarized-light-CYdI7T5g.js} +0 -0
- /package/dist/web/assets/{solidity-bDbtGwdR.js → solidity-DcuIDB-_.js} +0 -0
- /package/dist/web/assets/{splunk-Ceqx5njI.js → splunk-D5ONE91g.js} +0 -0
- /package/dist/web/assets/{sql-OwMEKbz8.js → sql-DIwJJDU-.js} +0 -0
- /package/dist/web/assets/{ssh-config-WAR3lu2k.js → ssh-config-D8717tLI.js} +0 -0
- /package/dist/web/assets/{stylus-Cjmoz3d8.js → stylus-Cm1lO4Y6.js} +0 -0
- /package/dist/web/assets/{swift-S_ZSBG1q.js → swift-X8oWpSeU.js} +0 -0
- /package/dist/web/assets/{synthwave-84-DiUeWMSh.js → synthwave-84-DF-t1UKY.js} +0 -0
- /package/dist/web/assets/{system-verilog-RRjqPjUV.js → system-verilog-JYyHFpPf.js} +0 -0
- /package/dist/web/assets/{systemd-BD3SkXoU.js → systemd-Da-5Ma_c.js} +0 -0
- /package/dist/web/assets/{talonscript-DWefISOm.js → talonscript-CXraVPho.js} +0 -0
- /package/dist/web/assets/{tasl-DCjSS-09.js → tasl-BCmWezH9.js} +0 -0
- /package/dist/web/assets/{tcl-DTYqT_66.js → tcl-BTufzfbf.js} +0 -0
- /package/dist/web/assets/{terraform-CkbP5QnR.js → terraform-CtUA9c8W.js} +0 -0
- /package/dist/web/assets/{tokyo-night-qVSqO9xy.js → tokyo-night-BCThYXmz.js} +0 -0
- /package/dist/web/assets/{toml-D9F5Osqs.js → toml-D5YlDr6Y.js} +0 -0
- /package/dist/web/assets/{tsv-C5tpHoxm.js → tsv-CBB-P_Sl.js} +0 -0
- /package/dist/web/assets/{tsx-CMWkGtGm.js → tsx-D5B4juDp.js} +0 -0
- /package/dist/web/assets/{turtle-Cn4z3xOi.js → turtle-D8PCeJ-v.js} +0 -0
- /package/dist/web/assets/{typescript-DSyAEWsn.js → typescript-CMPnTawp.js} +0 -0
- /package/dist/web/assets/{typespec-B93Qap7y.js → typespec-CHxldxIW.js} +0 -0
- /package/dist/web/assets/{typst-DtlOmFlX.js → typst-Q8CqX5OR.js} +0 -0
- /package/dist/web/assets/{v-Dfzf-szS.js → v-Bf2hKAMu.js} +0 -0
- /package/dist/web/assets/{vala-CyiQZQ9f.js → vala-Cbete-UE.js} +0 -0
- /package/dist/web/assets/{vb-C7DGSldd.js → vb-CA-xWAcX.js} +0 -0
- /package/dist/web/assets/{verilog-kF-hClY2.js → verilog-B3wb8CfT.js} +0 -0
- /package/dist/web/assets/{vesper-BmKcADuz.js → vesper-D6r3VJq_.js} +0 -0
- /package/dist/web/assets/{vhdl-CDnb16KJ.js → vhdl-C42S_797.js} +0 -0
- /package/dist/web/assets/{viml-B8cs6Ady.js → viml-BePrIAs8.js} +0 -0
- /package/dist/web/assets/{vitesse-black-BR6o9Gto.js → vitesse-black-DRqn61Ck.js} +0 -0
- /package/dist/web/assets/{vitesse-dark-Dye14-KB.js → vitesse-dark-39_8UE-w.js} +0 -0
- /package/dist/web/assets/{vitesse-light-BLpswIxf.js → vitesse-light-B1jwrxDm.js} +0 -0
- /package/dist/web/assets/{vyper-DU51Z97W.js → vyper-tDJv3imF.js} +0 -0
- /package/dist/web/assets/{wasm-CVqlJAR2.js → wasm-DR7eskZH.js} +0 -0
- /package/dist/web/assets/{wenyan-DhCa3hCn.js → wenyan-CezVfqN3.js} +0 -0
- /package/dist/web/assets/{wgsl-Bj1Ec4vv.js → wgsl-CwOcIiIG.js} +0 -0
- /package/dist/web/assets/{wikitext-C90DHNa0.js → wikitext-C5coomYc.js} +0 -0
- /package/dist/web/assets/{wit-DUgbOD7Z.js → wit-C9yyX6-U.js} +0 -0
- /package/dist/web/assets/{wolfram-BgwUqD1I.js → wolfram-BWi0rMAa.js} +0 -0
- /package/dist/web/assets/{yaml-DEQulj-8.js → yaml-CGXhKUtT.js} +0 -0
- /package/dist/web/assets/{zenscript-C7Jp7TgT.js → zenscript-Do7WLoJh.js} +0 -0
- /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
|
-
<
|
|
42
|
-
<
|
|
43
|
-
|
|
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'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
|
-
<
|
|
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
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
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 {
|
|
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
|
|
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
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
):
|
|
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
|
|
29
|
-
const
|
|
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};
|