@hienlh/ppm 0.18.13 → 0.18.15
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/.rdtest2-cf.out +0 -0
- package/CHANGELOG.md +23 -0
- package/CLAUDE.md +3 -0
- package/assets/skills/ppm/SKILL.md +1 -1
- package/assets/skills/ppm/references/cli-reference.md +18 -0
- package/assets/skills/ppm/references/http-api.md +8 -1
- package/dist/web/assets/{adaptive-context-menu-CbgPYQmj.js → adaptive-context-menu-ChBZWLLq.js} +1 -1
- package/dist/web/assets/{ai-resource-editor-unM2HNmg.js → ai-resource-editor-C8aAY8Po.js} +1 -1
- package/dist/web/assets/{ai-settings-section-CdktdrwF.js → ai-settings-section-ERu1WiZc.js} +1 -1
- package/dist/web/assets/{angular-html-DcpbdcNG.js → angular-html-CXgHIQ2U.js} +1 -1
- package/dist/web/assets/{angular-ts-Bv_WIk34.js → angular-ts-C8renMV9.js} +1 -1
- package/dist/web/assets/api-client-WpZ2FDHg.js +1 -0
- package/dist/web/assets/api-settings-BgSEE6et.js +1 -0
- package/dist/web/assets/{api-settings-BdCtex9i.js → api-settings-R59c66DE.js} +1 -1
- package/dist/web/assets/{api-themes-pRRoLvgD.js → api-themes-DKxrwJda.js} +1 -1
- package/dist/web/assets/{apl-D5_lLszu.js → apl-DkGH8laI.js} +1 -1
- package/dist/web/assets/architecture-PBZL5I3N-CCalSKAt.js +1 -0
- package/dist/web/assets/{astro-CBHwddYL.js → astro-tHUd8Zlg.js} +1 -1
- package/dist/web/assets/{audio-preview-BD1iDovf.js → audio-preview-B_C6Dpvz.js} +1 -1
- package/dist/web/assets/{blade-DGe1d2Pq.js → blade-BbKpjIYK.js} +1 -1
- package/dist/web/assets/{bsl-2lyAzRJA.js → bsl-DA0UYto0.js} +1 -1
- package/dist/web/assets/{button-2WCAS8dI.js → button-DBSpDPS0.js} +1 -1
- package/dist/web/assets/{cairo-DjzZeJrq.js → cairo-79ayucbB.js} +1 -1
- package/dist/web/assets/chat-tab-BraiJsYG.js +15 -0
- package/dist/web/assets/{cobol-cC3XIuAJ.js → cobol-DSS2EOYo.js} +1 -1
- package/dist/web/assets/code-editor-lhmLNhXu.js +10 -0
- package/dist/web/assets/{coffee-4eGboVoU.js → coffee-DvSxq2iD.js} +1 -1
- package/dist/web/assets/{conflict-editor-DdJag4QK.js → conflict-editor-C4VCv51R.js} +1 -1
- package/dist/web/assets/{context-menu-BO1c9jKy.js → context-menu-BpwUny-o.js} +1 -1
- package/dist/web/assets/{cpp-DF9_G-OU.js → cpp-BhWZ-FSn.js} +1 -1
- package/dist/web/assets/{crystal-OavZlzcm.js → crystal-yP2OeF-f.js} +1 -1
- package/dist/web/assets/{csv-preview-DnXwouF3.js → csv-preview-D46W_icb.js} +1 -1
- package/dist/web/assets/{data-grid-overlay-editor-B9Gpjm3l.js → data-grid-overlay-editor-DeGkPYtA.js} +1 -1
- package/dist/web/assets/{database-viewer-Dk8oGyZx.js → database-viewer-DFI4YtrH.js} +1 -1
- package/dist/web/assets/{dialog-B-l2ZxVm.js → dialog-RIpeb87v.js} +1 -1
- package/dist/web/assets/diff-viewer-eaNOLcBL.js +4 -0
- package/dist/web/assets/{docx-preview-DAdHPlhZ.js → docx-preview-Cbj-pOK3.js} +1 -1
- package/dist/web/assets/{edge-TzYh8OlN.js → edge-D3K9cd-p.js} +1 -1
- package/dist/web/assets/{edit-diff-preview-DlwP3UIU.js → edit-diff-preview-DDqy3KLA.js} +1 -1
- package/dist/web/assets/{elixir-Cy3RhKKH.js → elixir-D4PBmsLp.js} +1 -1
- package/dist/web/assets/{elm-BTBZdweK.js → elm-DSpXjuaH.js} +1 -1
- package/dist/web/assets/{erb-dv1mI0y4.js → erb-CaYZMTJW.js} +1 -1
- package/dist/web/assets/{erlang-DjeiDrkl.js → erlang-DX5BFqJj.js} +1 -1
- package/dist/web/assets/{esm-FRwvcesu.js → esm-Cv7HsFnk.js} +1 -1
- package/dist/web/assets/explorer-body-BxbSttfl.js +2 -0
- package/dist/web/assets/explorer-window-content-DUqQCC7A.js +1 -0
- package/dist/web/assets/{extension-store-DUaZINyC.js → extension-store-B1EClUAr.js} +1 -1
- package/dist/web/assets/{extension-webview-W0qRfaAU.js → extension-webview-BaGvDk8v.js} +1 -1
- package/dist/web/assets/{file-store-yReVbR8q.js → file-store-m9crh6uq.js} +1 -1
- package/dist/web/assets/{fortran-fixed-form-BCH9DAIJ.js → fortran-fixed-form-B0bAufUe.js} +1 -1
- package/dist/web/assets/{fsharp-C93NYKYD.js → fsharp-C2AL2b1M.js} +1 -1
- package/dist/web/assets/{gdresource-BcIEE2eX.js → gdresource-DRQsDMNI.js} +1 -1
- package/dist/web/assets/{git-commit-BVjlXEy8.js → git-commit-GtBdnrzk.js} +1 -1
- package/dist/web/assets/{git-log-panel-2_4HuP47.js → git-log-panel-hwV1flSO.js} +1 -1
- package/dist/web/assets/{git-rebase-Bj_21B1V.js → git-rebase-h_0CGA6F.js} +1 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-DAUrJCqi.js +1 -0
- package/dist/web/assets/glide-data-grid-YsN-xm6x.js +138 -0
- package/dist/web/assets/{glimmer-js-C20yj4Zv.js → glimmer-js-Bxwu93bS.js} +1 -1
- package/dist/web/assets/{glimmer-ts-ipbYC7kh.js → glimmer-ts-CIUYdR4E.js} +1 -1
- package/dist/web/assets/{glsl-Dovazu3t.js → glsl-CfxLI5jn.js} +1 -1
- package/dist/web/assets/{graphql-DpMlM7hC.js → graphql-CstqDPde.js} +1 -1
- package/dist/web/assets/group-chat-tab-B7mJO1gG.js +1 -0
- package/dist/web/assets/{hack-gc7LCBuV.js → hack-Bjqnssb5.js} +1 -1
- package/dist/web/assets/{haml-vtlFm9gm.js → haml-lVc-DfbM.js} +1 -1
- package/dist/web/assets/hand-x0ejq3fS.js +1 -0
- package/dist/web/assets/{handlebars-DGKhpCji.js → handlebars-Yy6fPQI_.js} +1 -1
- package/dist/web/assets/{html-derivative-C3oTSSCN.js → html-derivative-CchzCY1z.js} +1 -1
- package/dist/web/assets/{html-DvfhQbXb.js → html-lqtcy6-2.js} +1 -1
- package/dist/web/assets/{http-SG6zLTo7.js → http-ChclRrko.js} +1 -1
- package/dist/web/assets/{hurl-BWXsYlqW.js → hurl-CoOK7sXa.js} +1 -1
- package/dist/web/assets/{hxml-DUdMzaoh.js → hxml-fZSZ7Iyt.js} +1 -1
- package/dist/web/assets/{image-preview-DxgFOV1T.js → image-preview-ClAcqDlL.js} +1 -1
- package/dist/web/assets/index-CtJbMNNI.css +2 -0
- package/dist/web/assets/index-H_gScEOk.js +52 -0
- package/dist/web/assets/info-3K5VOQVL-Cx1ZfJbp.js +1 -0
- package/dist/web/assets/{input-DuAF-Stj.js → input-DXPgUtxi.js} +1 -1
- package/dist/web/assets/{jinja-955w9RKM.js → jinja-Y0w_KBdE.js} +1 -1
- package/dist/web/assets/{jison-WWfqPo50.js → jison-BILj3F2e.js} +1 -1
- package/dist/web/assets/{julia-BFrfnaNl.js → julia-DRfOPDts.js} +1 -1
- package/dist/web/assets/{just-BrZdyR_I.js → just-n3U_oODO.js} +1 -1
- package/dist/web/assets/keybindings-store-BqctreaI.js +1 -0
- package/dist/web/assets/{keybindings-store-0Wqfa7Qp.js → keybindings-store-D-CHdq-G.js} +1 -1
- package/dist/web/assets/keyboard-DBBaoQSu.js +1 -0
- package/dist/web/assets/{latex-CwtPPASD.js → latex-D6gztEOL.js} +1 -1
- package/dist/web/assets/{liquid-CYZuvDDd.js → liquid-DPqCEVb9.js} +1 -1
- package/dist/web/assets/{lua-BV095ZyY.js → lua-I40KPUm-.js} +1 -1
- package/dist/web/assets/markdown-renderer-BwOkiZC1.js +1 -0
- package/dist/web/assets/{markdown-renderer-CnRcKK3d.js → markdown-renderer-oJ9jPdQ2.js} +3 -3
- package/dist/web/assets/{marko-CNeljZ1C.js → marko-DMjsThSW.js} +1 -1
- package/dist/web/assets/{mdc-JfGapShy.js → mdc-CN2alsbo.js} +1 -1
- package/dist/web/assets/{media-url-Bm4PsduE.js → media-url-COUvasjn.js} +1 -1
- package/dist/web/assets/mobile-bottom-sheet-Cj1YBq-c.js +1 -0
- package/dist/web/assets/mobile-explorer-sheet-D1nM0Ik1.js +1 -0
- package/dist/web/assets/{nextflow-DD5C8nqX.js → nextflow-CGa4JeZ6.js} +1 -1
- package/dist/web/assets/{nginx-Ca7B__x_.js → nginx-CmqrIj84.js} +1 -1
- package/dist/web/assets/{nim-B8FogsDw.js → nim-CqhmTeMa.js} +1 -1
- package/dist/web/assets/notification-store-n-XEQ5pH.js +1 -0
- package/dist/web/assets/{number-overlay-editor-DG9xJwEG.js → number-overlay-editor-BEzJ1YeD.js} +1 -1
- package/dist/web/assets/{overview-panel-s53bnl0q.js → overview-panel-Bf9Gn8qV.js} +1 -1
- package/dist/web/assets/packet-RMMSAZCW-Bqwiy7dz.js +1 -0
- package/dist/web/assets/{panel-store-B4rijFKN.js → panel-store-UWCH6B-_.js} +1 -1
- package/dist/web/assets/{pdf-preview-DYt3aBzM.js → pdf-preview-BMHM5cR2.js} +1 -1
- package/dist/web/assets/{perl-BR78hS8R.js → perl-BOwpb6Tb.js} +1 -1
- package/dist/web/assets/{php-DXBOMC9z.js → php-wT9fVxxu.js} +1 -1
- package/dist/web/assets/pie-UPGHQEXC-BXSJ2God.js +1 -0
- package/dist/web/assets/{postgres-viewer-BvoWSuoZ.js → postgres-viewer-D8rfEY5b.js} +3 -3
- package/dist/web/assets/process-table-CsQ-hDJr.js +1 -0
- package/dist/web/assets/{project-store-BuY7rxn_.js → project-store-CKAOr23O.js} +1 -1
- package/dist/web/assets/{pug-CvTwVVKV.js → pug-DdlNnj-p.js} +1 -1
- package/dist/web/assets/{qml-CPSoZ1-B.js → qml-C5zBdydf.js} +1 -1
- package/dist/web/assets/radar-KQ55EAFF-D6XrM7HH.js +1 -0
- package/dist/web/assets/{razor-BUA1YKtF.js → razor-DqcJX4iq.js} +1 -1
- package/dist/web/assets/remote-desktop-mobile-sheet-CTiipn4k.js +2 -0
- package/dist/web/assets/remote-desktop-mobile-view-Cf5B0dKo.js +1 -0
- package/dist/web/assets/remote-desktop-stats-overlay-vhJBIW7s.js +1 -0
- package/dist/web/assets/remote-desktop-window-content-wCJUfI6G.js +1 -0
- package/dist/web/assets/{rst-BJsq7ISz.js → rst-DiMiMYUO.js} +1 -1
- package/dist/web/assets/{ruby-DSDMeYf5.js → ruby-DK62iflA.js} +1 -1
- package/dist/web/assets/{run-in-terminal-C9xpskFn.js → run-in-terminal-DJSL6nwZ.js} +1 -1
- package/dist/web/assets/{sas-B1nJOjbN.js → sas-6QPGJRa5.js} +1 -1
- package/dist/web/assets/{scroll-area-DBcG1fQo.js → scroll-area-DXGXeiRz.js} +1 -1
- package/dist/web/assets/{scss-BZFyRiQn.js → scss-DmUqh48_.js} +1 -1
- package/dist/web/assets/{send-to-chat-BIg70MUZ.js → send-to-chat-BOony0sO.js} +1 -1
- package/dist/web/assets/settings-store-BpcY5aYE.js +2 -0
- package/dist/web/assets/settings-tab-BVqRO01j.js +1 -0
- package/dist/web/assets/settings-tab-FH581A_i.js +3 -0
- package/dist/web/assets/{shaderlab-laINRXBo.js → shaderlab-DopFR1mi.js} +1 -1
- package/dist/web/assets/{shellsession-BpOOvWVR.js → shellsession-Bp_TGuXb.js} +1 -1
- package/dist/web/assets/{soy-BW4d91Sa.js → soy-DEB9KEFb.js} +1 -1
- package/dist/web/assets/{sparql-BsvvyoHn.js → sparql-C8lEwO1p.js} +1 -1
- package/dist/web/assets/{sql-query-editor-qQce5QJ6.js → sql-query-editor-MMfk0Kbz.js} +1 -1
- package/dist/web/assets/{sqlite-viewer-yhc0YNvu.js → sqlite-viewer-vglzrgnZ.js} +1 -1
- package/dist/web/assets/{stata-K-dZInUt.js → stata-DVep4apN.js} +1 -1
- package/dist/web/assets/{surrealql-CpNp2sgf.js → surrealql-fSlJApOA.js} +1 -1
- package/dist/web/assets/{svelte-BG6iBA8R.js → svelte-DezWBEDi.js} +1 -1
- package/dist/web/assets/{switch-_NcrktLe.js → switch-DqL_ZjB_.js} +1 -1
- package/dist/web/assets/system-monitor-body-BYUfyF8f.js +2 -0
- package/dist/web/assets/system-monitor-tab-CKGXB8lH.js +1 -0
- package/dist/web/assets/{system-monitor-window-content-CaOhD0AU.js → system-monitor-window-content-CiJ2yIsr.js} +1 -1
- package/dist/web/assets/tab-host-window-content-DYlXR0cg.js +1 -0
- package/dist/web/assets/{tab-store-tFnyPmm4.js → tab-store-D2thn4Vv.js} +1 -1
- package/dist/web/assets/team-member-sheet-Bnygu7Xw.js +2 -0
- package/dist/web/assets/{team-member-window-content-B1jkKkDi.js → team-member-window-content-qrgFTC96.js} +1 -1
- package/dist/web/assets/{templ-gxNKXmt3.js → templ-D0zB42a9.js} +1 -1
- package/dist/web/assets/{terminal-tab-UvwM0sty.js → terminal-tab-D_-R5rGm.js} +3 -3
- package/dist/web/assets/{tex-C0ON27g5.js → tex-BJiPijcH.js} +1 -1
- package/dist/web/assets/{tool-cards-jB7GT-wj.js → tool-cards-BW7Xc20J.js} +4 -4
- package/dist/web/assets/treemap-KZPCXAKY-BobWhcHN.js +1 -0
- package/dist/web/assets/{ts-tags-BX_CTVgQ.js → ts-tags-Dcr4u6y5.js} +1 -1
- package/dist/web/assets/{twig-CwL7XnU0.js → twig-B-SQU0bg.js} +1 -1
- package/dist/web/assets/{use-blob-url-DcRcjNTE.js → use-blob-url-BtPlqwLr.js} +1 -1
- package/dist/web/assets/use-monaco-theme-DQY1ZSaf.js +1 -0
- package/dist/web/assets/{use-open-team-member-1hRsHLKg.js → use-open-team-member-C5LQ_TCM.js} +1 -1
- package/dist/web/assets/use-visual-viewport-pVxLfE5B.js +1 -0
- package/dist/web/assets/{use-websocket-rxJLbFqy.js → use-websocket-l39Odhlp.js} +1 -1
- package/dist/web/assets/{vendor-mermaid-CHTa6D69.js → vendor-mermaid-CtDsSxbM.js} +1 -1
- package/dist/web/assets/{video-preview-DMhnf2Gi.js → video-preview-C5AhkIDl.js} +1 -1
- package/dist/web/assets/{vue-Bykm6wSS.js → vue-Cw10Sfbh.js} +1 -1
- package/dist/web/assets/{vue-html-CETdK_iV.js → vue-html-Cc5C_Z_w.js} +1 -1
- package/dist/web/assets/{vue-vine-DiJb6fO5.js → vue-vine-BuNMlnvu.js} +1 -1
- package/dist/web/assets/window-pip-registry-C7oz3Dfy.js +1 -0
- package/dist/web/assets/window-store-B5ZswdYg.js +1 -0
- package/dist/web/assets/{xml-BNw8J5YW.js → xml-DqT1wkky.js} +1 -1
- package/dist/web/assets/{xsl-CsG9L31b.js → xsl-DLkge3A2.js} +1 -1
- package/dist/web/index.html +45 -42
- package/dist/web/sw.js +1 -1
- package/docs/architecture/ai-chat-and-providers.md +3 -0
- package/docs/architecture/workspace-and-ui.md +8 -0
- package/docs/design-guidelines.md +3 -2
- package/package.json +1 -1
- package/src/cli/commands/backup-cmd.ts +119 -0
- package/src/index.ts +3 -0
- package/src/providers/claude-agent-sdk.ts +29 -1
- package/src/server/index.ts +42 -15
- package/src/server/routes/accounts.ts +30 -2
- package/src/server/routes/chat.ts +8 -1
- package/src/server/routes/named-tunnel.ts +5 -2
- package/src/server/routes/remote-desktop.ts +64 -0
- package/src/server/routes/settings.ts +1 -0
- package/src/server/ws/chat.ts +126 -7
- package/src/server/ws/remote-desktop.ts +86 -0
- package/src/services/account.service.ts +106 -23
- package/src/services/db-backup/db-backup-paths.ts +66 -0
- package/src/services/db-backup/db-backup-restore.ts +105 -0
- package/src/services/db-backup/db-backup-retention.ts +96 -0
- package/src/services/db-backup/db-backup-sync.ts +113 -0
- package/src/services/db-backup/db-backup.service.ts +61 -0
- package/src/services/db.service.ts +29 -1
- package/src/services/fs-credential-path-guard.ts +19 -1
- package/src/services/named-tunnel/cloudflared-login.service.ts +2 -2
- package/src/services/nested-subagent-spy.ts +206 -0
- package/src/services/remote-desktop/access-unit-assembler.ts +90 -0
- package/src/services/remote-desktop/avc1-codec-string.ts +18 -0
- package/src/services/remote-desktop/nal-splitter.ts +100 -0
- package/src/services/remote-desktop/remote-desktop-capture.ts +120 -0
- package/src/services/remote-desktop/remote-desktop-encoder-args.ts +42 -0
- package/src/services/remote-desktop/remote-desktop-flag.ts +10 -0
- package/src/services/remote-desktop/remote-desktop-input.ts +217 -0
- package/src/services/remote-desktop/remote-desktop-nonce.ts +38 -0
- package/src/services/remote-desktop/remote-desktop-session.ts +189 -0
- package/src/services/remote-desktop/remote-desktop-vk-map.ts +41 -0
- package/src/services/subagent-transcript-merger.ts +92 -75
- package/src/services/subprocess-retention.ts +29 -6
- package/src/services/supervisor.ts +58 -0
- package/src/services/team-member-activity/subagent-transcript-index.ts +56 -0
- package/src/types/chat.ts +2 -0
- package/src/web/app.tsx +8 -0
- package/src/web/components/chat/account-dialogs.tsx +11 -4
- package/src/web/components/chat/question-card.tsx +9 -2
- package/src/web/components/chat/usage-badge.tsx +17 -3
- package/src/web/components/floating-window/floating-window.tsx +8 -1
- package/src/web/components/floating-window/open-tab-in-pip.ts +78 -0
- package/src/web/components/floating-window/window-content-registry.ts +2 -0
- package/src/web/components/floating-window/window-pip-registry.ts +46 -0
- package/src/web/components/floating-window/window-store-types.ts +1 -1
- package/src/web/components/layout/mobile-drawer.tsx +38 -27
- package/src/web/components/layout/nav-section-rail.tsx +10 -2
- package/src/web/components/layout/tab-pop-out-menu-item.tsx +25 -2
- package/src/web/components/remote-desktop/open-remote-desktop.ts +26 -0
- package/src/web/components/remote-desktop/remote-desktop-coords.ts +98 -0
- package/src/web/components/remote-desktop/remote-desktop-frame-timestamp.ts +21 -0
- package/src/web/components/remote-desktop/remote-desktop-gesture-classifiers.ts +56 -0
- package/src/web/components/remote-desktop/remote-desktop-mobile-key-bar.tsx +131 -0
- package/src/web/components/remote-desktop/remote-desktop-mobile-sheet.tsx +43 -0
- package/src/web/components/remote-desktop/remote-desktop-mobile-toolbar.tsx +79 -0
- package/src/web/components/remote-desktop/remote-desktop-mobile-view.tsx +190 -0
- package/src/web/components/remote-desktop/remote-desktop-stats-overlay.tsx +54 -0
- package/src/web/components/remote-desktop/remote-desktop-stats.ts +37 -0
- package/src/web/components/remote-desktop/remote-desktop-touch-gesture-tracker.ts +195 -0
- package/src/web/components/remote-desktop/remote-desktop-window-content.tsx +72 -0
- package/src/web/components/remote-desktop/remote-desktop-ws-url.ts +22 -0
- package/src/web/components/remote-desktop/use-h264-canvas-decoder.ts +175 -0
- package/src/web/components/remote-desktop/use-remote-desktop-available.ts +29 -0
- package/src/web/components/remote-desktop/use-remote-desktop-connection.ts +147 -0
- package/src/web/components/remote-desktop/use-remote-desktop-mobile-open-state.ts +18 -0
- package/src/web/components/remote-desktop/use-remote-desktop-touch.ts +224 -0
- package/src/web/components/remote-desktop/use-remote-desktop-virtual-keyboard.ts +50 -0
- package/src/web/components/remote-desktop/use-remote-input-capture.ts +92 -0
- package/src/web/components/tunnels/named-tunnel/named-tunnel-copy.ts +59 -59
- package/src/web/components/tunnels/named-tunnel/named-tunnel-section.tsx +2 -2
- package/src/web/hooks/use-notification-badge.ts +19 -11
- package/src/web/lib/feature-badges.ts +2 -0
- package/src/web/stores/settings-store.ts +13 -0
- package/src/web/stores/streaming-store.ts +21 -2
- package/dist/web/assets/api-client-BogqsFna.js +0 -1
- package/dist/web/assets/api-settings-DfXViPKz.js +0 -1
- package/dist/web/assets/architecture-PBZL5I3N-BzFm9ELu.js +0 -1
- package/dist/web/assets/chat-tab-DzK8LGti.js +0 -15
- package/dist/web/assets/code-editor-CuMfPN9X.js +0 -10
- package/dist/web/assets/diff-viewer-eaUBjLCH.js +0 -4
- package/dist/web/assets/explorer-body-DkCkSVrN.js +0 -2
- package/dist/web/assets/explorer-window-content-MsnTVngu.js +0 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-B72vcb2r.js +0 -1
- package/dist/web/assets/glide-data-grid-B0IY6n_Q.js +0 -138
- package/dist/web/assets/group-chat-tab-DRLDHOp6.js +0 -1
- package/dist/web/assets/index-CBn4SOoT.js +0 -52
- package/dist/web/assets/index-DWpNR3nB.css +0 -2
- package/dist/web/assets/info-3K5VOQVL-3irPxRK_.js +0 -1
- package/dist/web/assets/keybindings-store-d54pv2NK.js +0 -1
- package/dist/web/assets/markdown-renderer-BTS8ifdu.js +0 -1
- package/dist/web/assets/mobile-bottom-sheet-M5iZDX7y.js +0 -1
- package/dist/web/assets/mobile-explorer-sheet-BqK2-WuN.js +0 -1
- package/dist/web/assets/notification-store-Yvy1I8gd.js +0 -1
- package/dist/web/assets/packet-RMMSAZCW-Z5jWjFCV.js +0 -1
- package/dist/web/assets/pie-UPGHQEXC-Bq7UMi8P.js +0 -1
- package/dist/web/assets/process-table-BYwKigiD.js +0 -1
- package/dist/web/assets/radar-KQ55EAFF-BLo4BOTD.js +0 -1
- package/dist/web/assets/settings-store-GV5pkkke.js +0 -2
- package/dist/web/assets/settings-tab-BePvIcjd.js +0 -3
- package/dist/web/assets/settings-tab-Br-lJSPa.js +0 -1
- package/dist/web/assets/system-monitor-body-D8Z5QXBB.js +0 -2
- package/dist/web/assets/system-monitor-tab-BclYXFTe.js +0 -1
- package/dist/web/assets/tab-host-window-content-f_Rc5rJK.js +0 -1
- package/dist/web/assets/team-member-sheet-QDHoYCs0.js +0 -2
- package/dist/web/assets/treemap-KZPCXAKY-dyrE6SW2.js +0 -1
- package/dist/web/assets/use-monaco-theme--9tOOkDA.js +0 -1
- package/dist/web/assets/window-store-CQ1lghvv.js +0 -1
- /package/dist/web/assets/{abap-DGlrX_8G.js → abap-CKgBNk5K.js} +0 -0
- /package/dist/web/assets/{actionscript-3-CUqRtUJE.js → actionscript-3-oARhAY6c.js} +0 -0
- /package/dist/web/assets/{ada-CY3Spa33.js → ada-2vi8ASy3.js} +0 -0
- /package/dist/web/assets/{andromeeda-Lxs2TJn7.js → andromeeda-B3vMjPKd.js} +0 -0
- /package/dist/web/assets/{apache-DfxgGdgM.js → apache-DnIEr338.js} +0 -0
- /package/dist/web/assets/{apex-QVDF1dnI.js → apex-B7Qs5AY5.js} +0 -0
- /package/dist/web/assets/{api-client-oY1tC3tM.js → api-client-BkGKTjsq.js} +0 -0
- /package/dist/web/assets/{applescript-pwJ8QXh-.js → applescript-EbQNUCWb.js} +0 -0
- /package/dist/web/assets/{apply-theme-B_bMVL6a.js → apply-theme-Dd_cqGa-.js} +0 -0
- /package/dist/web/assets/{ara-mPjeX5YX.js → ara-CtHdhFRw.js} +0 -0
- /package/dist/web/assets/{asciidoc-Bf7LsDkI.js → asciidoc-BH6qin_O.js} +0 -0
- /package/dist/web/assets/{asm-rRKC1OUx.js → asm-D1XU1ACk.js} +0 -0
- /package/dist/web/assets/{aurora-x-DwQr0DkG.js → aurora-x-BkhcxkuS.js} +0 -0
- /package/dist/web/assets/{awk-Bpt768fN.js → awk-DijzB6o7.js} +0 -0
- /package/dist/web/assets/{ayu-dark-D9lwQHMs.js → ayu-dark-tysG77WC.js} +0 -0
- /package/dist/web/assets/{ayu-light-B4-ptrU5.js → ayu-light-Dg0WMETC.js} +0 -0
- /package/dist/web/assets/{ayu-mirage-DAyRzH5Q.js → ayu-mirage--RsJCjC4.js} +0 -0
- /package/dist/web/assets/{ballerina-Cgdpj1HC.js → ballerina-DT_5-l82.js} +0 -0
- /package/dist/web/assets/{bat-4FrN36S2.js → bat-BRpDlkv0.js} +0 -0
- /package/dist/web/assets/{beancount-DkcxQER3.js → beancount--8Df-WGG.js} +0 -0
- /package/dist/web/assets/{berry-Dnuut-Ml.js → berry-Bc9fo35J.js} +0 -0
- /package/dist/web/assets/{bibtex-CCDwmbuA.js → bibtex-CwyBpp9X.js} +0 -0
- /package/dist/web/assets/{bicep-BC0ryoGM.js → bicep-DiyH2Bs7.js} +0 -0
- /package/dist/web/assets/{bird2-C50V_A-0.js → bird2-CHgK4a2A.js} +0 -0
- /package/dist/web/assets/{c-Ddorhq0U.js → c-BM1qL6-f.js} +0 -0
- /package/dist/web/assets/{c3-DdIf5r9E.js → c3-D2oOjrP7.js} +0 -0
- /package/dist/web/assets/{cadence-s9pd1Wng.js → cadence-nS45wqF6.js} +0 -0
- /package/dist/web/assets/{can-open-in-ppm-C6hZV1OP.js → can-open-in-ppm-DwP9KU8o.js} +0 -0
- /package/dist/web/assets/{catppuccin-frappe-CzJNTyu-.js → catppuccin-frappe-BpnJHiCj.js} +0 -0
- /package/dist/web/assets/{catppuccin-latte-BNpt-m7s.js → catppuccin-latte-Bb95Wj3h.js} +0 -0
- /package/dist/web/assets/{catppuccin-macchiato-FYGkfsJ5.js → catppuccin-macchiato-Btx8c17W.js} +0 -0
- /package/dist/web/assets/{catppuccin-mocha-it2g5D1q.js → catppuccin-mocha-B4DZOUGI.js} +0 -0
- /package/dist/web/assets/{clarity-BVFBTjMe.js → clarity-1ps69jU6.js} +0 -0
- /package/dist/web/assets/{clipboard-BWznymz7.js → clipboard-CrHX8AYw.js} +0 -0
- /package/dist/web/assets/{clojure-BSYvXMI8.js → clojure-BklC0hKV.js} +0 -0
- /package/dist/web/assets/{cmake-DGEqH-Np.js → cmake-B573LYAQ.js} +0 -0
- /package/dist/web/assets/{codeowners-D8FCOxWg.js → codeowners-BoYeX89M.js} +0 -0
- /package/dist/web/assets/{codeql-COV0eluG.js → codeql-7Dhf5Udk.js} +0 -0
- /package/dist/web/assets/{common-lisp-xHnZUlgt.js → common-lisp-m1nxlthv.js} +0 -0
- /package/dist/web/assets/{coq-Cs_2i3Xg.js → coq-DNhGmd4a.js} +0 -0
- /package/dist/web/assets/{csharp-B1ONB4PT.js → csharp-P-nAurtS.js} +0 -0
- /package/dist/web/assets/{css-BgGQbvrZ.js → css-LYbSeh3-.js} +0 -0
- /package/dist/web/assets/{csv-CNHmQBzs.js → csv-DHZZZm7X.js} +0 -0
- /package/dist/web/assets/{csv-parser-DWR24J17.js → csv-parser-CY9Ggtg5.js} +0 -0
- /package/dist/web/assets/{cue-ZXAKzwIy.js → cue-D8yLUOle.js} +0 -0
- /package/dist/web/assets/{cypher-4PL5bmT5.js → cypher-9cbW8RYA.js} +0 -0
- /package/dist/web/assets/{d-CEsV3RT2.js → d-ZihzhxqI.js} +0 -0
- /package/dist/web/assets/{dark-plus-NtFnYSDz.js → dark-plus-C8xIA1u3.js} +0 -0
- /package/dist/web/assets/{dart-CKHn4mDf.js → dart-CBm6jKc7.js} +0 -0
- /package/dist/web/assets/{data-grid-types-CxU8oo-j.js → data-grid-types-DkEQ-Na9.js} +0 -0
- /package/dist/web/assets/{dax-Cmkj1I_M.js → dax-B3cx6YeV.js} +0 -0
- /package/dist/web/assets/{desktop-DJLalvPQ.js → desktop-C03SpXth.js} +0 -0
- /package/dist/web/assets/{diff-BnS2wpj6.js → diff-D8LnnvFY.js} +0 -0
- /package/dist/web/assets/{dist-uYjs3Wv6.js → dist-C4XoS_zo.js} +0 -0
- /package/dist/web/assets/{dist-CuIdRyUB.js → dist-Do3fljss.js} +0 -0
- /package/dist/web/assets/{docker-CovNy7ml.js → docker-DwFiOQWW.js} +0 -0
- /package/dist/web/assets/{dotenv-Gh4w3I6B.js → dotenv-CVJ_vFSQ.js} +0 -0
- /package/dist/web/assets/{dracula-NF0tAHXl.js → dracula-DMw8UACY.js} +0 -0
- /package/dist/web/assets/{dracula-soft-Cj_qBurK.js → dracula-soft-BnB3nDrA.js} +0 -0
- /package/dist/web/assets/{dream-maker-CRuZPcl7.js → dream-maker-2riXUQXo.js} +0 -0
- /package/dist/web/assets/{emacs-lisp-BACmC9qj.js → emacs-lisp-BQdYaiB5.js} +0 -0
- /package/dist/web/assets/{everforest-dark-Dd9av4QG.js → everforest-dark-C2FPIWdj.js} +0 -0
- /package/dist/web/assets/{everforest-light-C-jsss-B.js → everforest-light-DMOWf9sD.js} +0 -0
- /package/dist/web/assets/{fennel-BJA9XnL8.js → fennel-JtSHp0vW.js} +0 -0
- /package/dist/web/assets/{fish-CKlhNSc2.js → fish-CflDcRfe.js} +0 -0
- /package/dist/web/assets/{fluent-wfglRk5D.js → fluent-D4eu6Vac.js} +0 -0
- /package/dist/web/assets/{format-bytes-utwHMTry.js → format-bytes-DR3228bu.js} +0 -0
- /package/dist/web/assets/{fortran-free-form-BlXCdid3.js → fortran-free-form-hO4RuC9Z.js} +0 -0
- /package/dist/web/assets/{gdscript-BwAVG6Et.js → gdscript-D7n3a8VM.js} +0 -0
- /package/dist/web/assets/{gdshader-DPc_T7ib.js → gdshader-BhUmrlzG.js} +0 -0
- /package/dist/web/assets/{genie-B-zZSLCX.js → genie-VbyftqBp.js} +0 -0
- /package/dist/web/assets/{gherkin-CJ0ajpGs.js → gherkin-BZzhvUUb.js} +0 -0
- /package/dist/web/assets/{github-dark-Dz8ZkzSP.js → github-dark-C4q41woW.js} +0 -0
- /package/dist/web/assets/{github-dark-default-CNUYo_kp.js → github-dark-default-CMNi7dQX.js} +0 -0
- /package/dist/web/assets/{github-dark-dimmed-241z_Ycx.js → github-dark-dimmed-BRf_-LlY.js} +0 -0
- /package/dist/web/assets/{github-dark-high-contrast-7r6yZZYH.js → github-dark-high-contrast-xanKBsRI.js} +0 -0
- /package/dist/web/assets/{github-light-C8gmZn-D.js → github-light-BEQeNQCs.js} +0 -0
- /package/dist/web/assets/{github-light-default-GAEgq1eA.js → github-light-default-ceQrtdXg.js} +0 -0
- /package/dist/web/assets/{github-light-high-contrast-3z91352E.js → github-light-high-contrast-doKbXzgW.js} +0 -0
- /package/dist/web/assets/{gleam-C7vxW5zd.js → gleam-Chzqf4fT.js} +0 -0
- /package/dist/web/assets/{gn-CyJIXwHP.js → gn-CCN_-6fp.js} +0 -0
- /package/dist/web/assets/{gnuplot-CqLbGgPd.js → gnuplot-CIplgwwP.js} +0 -0
- /package/dist/web/assets/{go-DdDcK9aX.js → go-Cjj-9dRs.js} +0 -0
- /package/dist/web/assets/{groovy-UYHggEqH.js → groovy-PqD4K7X1.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-hard-B4ierkJn.js → gruvbox-dark-hard-BAbpUSF-.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-medium-DYwkDSZ6.js → gruvbox-dark-medium-BWcWXDBB.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-soft-CVL4P-Lz.js → gruvbox-dark-soft-Dkx1okkk.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-hard-BJ0LewUZ.js → gruvbox-light-hard-WVsDK0MM.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-medium-CaqqjeGJ.js → gruvbox-light-medium-tiR9t7gr.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-soft-ByKMoVNr.js → gruvbox-light-soft-HzHohaX6.js} +0 -0
- /package/dist/web/assets/{haskell-BilYRVPp.js → haskell-B1eIZfr6.js} +0 -0
- /package/dist/web/assets/{haxe-Brs9Bj1s.js → haxe-BcDdi5xr.js} +0 -0
- /package/dist/web/assets/{hcl-ClrZpol-.js → hcl-Dq-HKVni.js} +0 -0
- /package/dist/web/assets/{hjson-9PvHnYMX.js → hjson-Dr9D9WVU.js} +0 -0
- /package/dist/web/assets/{hlsl-BbBk4vqV.js → hlsl-Bo8t2FuZ.js} +0 -0
- /package/dist/web/assets/{horizon-E5Py0cny.js → horizon-DWG5IXc-.js} +0 -0
- /package/dist/web/assets/{horizon-bright-DVCsNGEk.js → horizon-bright-DFr9wTTx.js} +0 -0
- /package/dist/web/assets/{houston-JdFEax9Y.js → houston-NAVNhkup.js} +0 -0
- /package/dist/web/assets/{hy-W_W3tLKT.js → hy-DnyvUk3B.js} +0 -0
- /package/dist/web/assets/{image-gallery-5IURd6db.js → image-gallery-DoFdNF9i.js} +0 -0
- /package/dist/web/assets/{imba-CfAqJz7T.js → imba-CPbR5W_A.js} +0 -0
- /package/dist/web/assets/{ini-C_3gvKWs.js → ini-lc8_Lt0y.js} +0 -0
- /package/dist/web/assets/{java-CKqtXZ-j.js → java-B0KCo2vK.js} +0 -0
- /package/dist/web/assets/{javascript-C7gSEfae.js → javascript-YqEbNRpd.js} +0 -0
- /package/dist/web/assets/{json-BoN6e11T.js → json-Co0WtOlh.js} +0 -0
- /package/dist/web/assets/{json5-CgE7C8HW.js → json5-2Jhb0QUN.js} +0 -0
- /package/dist/web/assets/{jsonc-DjyoattY.js → jsonc-e72F11nU.js} +0 -0
- /package/dist/web/assets/{jsonl-lI82wGSw.js → jsonl-Bff8IPye.js} +0 -0
- /package/dist/web/assets/{jsonnet-CszEI8va.js → jsonnet-DRCkb-_H.js} +0 -0
- /package/dist/web/assets/{jssm-CZXDlszb.js → jssm-C6UiwVEm.js} +0 -0
- /package/dist/web/assets/{jsx-BeRz0eEz.js → jsx-u1aZlnfA.js} +0 -0
- /package/dist/web/assets/{kanagawa-dragon-BOmnUiyd.js → kanagawa-dragon-De-RQFV3.js} +0 -0
- /package/dist/web/assets/{kanagawa-lotus-ffU_P5w-.js → kanagawa-lotus-K8elE4QU.js} +0 -0
- /package/dist/web/assets/{kanagawa-wave-DKTJZrMu.js → kanagawa-wave-BrStl_zx.js} +0 -0
- /package/dist/web/assets/{kdl-BWECJveH.js → kdl-CLCfMeZy.js} +0 -0
- /package/dist/web/assets/{kotlin-DpVwBCSt.js → kotlin-DEEC3pNh.js} +0 -0
- /package/dist/web/assets/{kusto-BgVqjUZA.js → kusto-DTqRhCup.js} +0 -0
- /package/dist/web/assets/{laserwave-CEVCi8em.js → laserwave-DsgpUSbA.js} +0 -0
- /package/dist/web/assets/{lean-CGyfRhLJ.js → lean-Dl9MI0Y3.js} +0 -0
- /package/dist/web/assets/{less-B0_H10Ku.js → less-Bg21rao6.js} +0 -0
- /package/dist/web/assets/{lib-hrC3bruJ.js → lib-CXUwxw1M.js} +0 -0
- /package/dist/web/assets/{libesm-DVpFmwJu.js → libesm-CVkncAQz.js} +0 -0
- /package/dist/web/assets/{light-plus-DqzMqPID.js → light-plus-DGNbbK1V.js} +0 -0
- /package/dist/web/assets/{llvm-9ucnfNiF.js → llvm-91_ZX3p-.js} +0 -0
- /package/dist/web/assets/{loader-circle-Dpqcv0eS.js → loader-circle-DouKgIXy.js} +0 -0
- /package/dist/web/assets/{lock-DldbT31x.js → lock-CNxUGpTk.js} +0 -0
- /package/dist/web/assets/{log-m6A2dOSj.js → log-DSXzbq8u.js} +0 -0
- /package/dist/web/assets/{logo-ByPcsC9P.js → logo-BQRNd4tc.js} +0 -0
- /package/dist/web/assets/{luau-DFCo8nBn.js → luau-BE-TMpiZ.js} +0 -0
- /package/dist/web/assets/{make-C90w3MeA.js → make-ePYGKTpc.js} +0 -0
- /package/dist/web/assets/{markdown-DT0uywZG.js → markdown-Cst3wgMW.js} +0 -0
- /package/dist/web/assets/{material-theme-BVUO93tJ.js → material-theme-9XyvnNLb.js} +0 -0
- /package/dist/web/assets/{material-theme-darker-xXgGDwnF.js → material-theme-darker-H1VN4MTS.js} +0 -0
- /package/dist/web/assets/{material-theme-lighter-BjStPzWB.js → material-theme-lighter-DAreBbuF.js} +0 -0
- /package/dist/web/assets/{material-theme-ocean-wFmSiKjy.js → material-theme-ocean-BpTR-3II.js} +0 -0
- /package/dist/web/assets/{material-theme-palenight-e-0CtRoe.js → material-theme-palenight-DUNzX711.js} +0 -0
- /package/dist/web/assets/{matlab-BNaNQAUH.js → matlab-BpYNufBU.js} +0 -0
- /package/dist/web/assets/{mdx-DfpVWAqO.js → mdx-BYjfe1DK.js} +0 -0
- /package/dist/web/assets/{mermaid-Bpq7wFCP.js → mermaid-DPCcIW9B.js} +0 -0
- /package/dist/web/assets/{min-dark-CL2Y0WH6.js → min-dark-DnGOAGCo.js} +0 -0
- /package/dist/web/assets/{min-light-DOgr_IdP.js → min-light-CwV-o31y.js} +0 -0
- /package/dist/web/assets/{mipsasm-C9ie2zlB.js → mipsasm-DdeCPC97.js} +0 -0
- /package/dist/web/assets/{mojo-Md2A2sJy.js → mojo-BmxxZ3iM.js} +0 -0
- /package/dist/web/assets/{monitor-4vWfqzuN.js → monitor-C835ucWH.js} +0 -0
- /package/dist/web/assets/{monokai-nbXIEySB.js → monokai-Cx6cQ-MA.js} +0 -0
- /package/dist/web/assets/{moonbit-CJ-T-s4A.js → moonbit-D2-Gj5vj.js} +0 -0
- /package/dist/web/assets/{move-DwEehLIK.js → move-DSWGoQa8.js} +0 -0
- /package/dist/web/assets/{narrat-CuF4sIKr.js → narrat-D9TnZSSu.js} +0 -0
- /package/dist/web/assets/{nextflow-groovy-1dYhfPfq.js → nextflow-groovy-BbL-8PwD.js} +0 -0
- /package/dist/web/assets/{night-owl-gZS5mfzY.js → night-owl-CZitKm5d.js} +0 -0
- /package/dist/web/assets/{night-owl-light-DDAster5.js → night-owl-light-DJVX1Pno.js} +0 -0
- /package/dist/web/assets/{nix-BlbEYs2c.js → nix-R6hr3oln.js} +0 -0
- /package/dist/web/assets/{nord-BRiPb8Vy.js → nord-ChGHya3l.js} +0 -0
- /package/dist/web/assets/{nushell-DgWicB1i.js → nushell-QQWB-GWx.js} +0 -0
- /package/dist/web/assets/{objective-c-D8mPZ42Y.js → objective-c-C0pZ6y73.js} +0 -0
- /package/dist/web/assets/{objective-cpp-B0pNXzuj.js → objective-cpp-BdzVM31a.js} +0 -0
- /package/dist/web/assets/{ocaml-lQd7aK5d.js → ocaml-D4KktNlS.js} +0 -0
- /package/dist/web/assets/{odin-CxtIOBjm.js → odin-Y-fypmGT.js} +0 -0
- /package/dist/web/assets/{one-dark-pro-T_UFnWfe.js → one-dark-pro-BwecIxQL.js} +0 -0
- /package/dist/web/assets/{one-light-LBVqwyV-.js → one-light-Cz7UVzLU.js} +0 -0
- /package/dist/web/assets/{openscad-C5dnS6J7.js → openscad-DtRe3qYD.js} +0 -0
- /package/dist/web/assets/{pascal-CmWCWQML.js → pascal-Ck6I1fGG.js} +0 -0
- /package/dist/web/assets/{pkl-BWWOtDQk.js → pkl-DdFK94VW.js} +0 -0
- /package/dist/web/assets/{plastic-ekhupckF.js → plastic-BQSAzZwR.js} +0 -0
- /package/dist/web/assets/{play-CpCAoQRZ.js → play-CJlWpVMw.js} +0 -0
- /package/dist/web/assets/{plsql-BPsDHXx4.js → plsql-DjXg_CP0.js} +0 -0
- /package/dist/web/assets/{po-BX2FiuW2.js → po-Bw2SuM4r.js} +0 -0
- /package/dist/web/assets/{poimandres-DzXOdZcN.js → poimandres-YMK6c5VN.js} +0 -0
- /package/dist/web/assets/{polar-DDRXgJzG.js → polar-BMu17-O0.js} +0 -0
- /package/dist/web/assets/{portal-container-context-a399Hd9F.js → portal-container-context-DSGdlXrJ.js} +0 -0
- /package/dist/web/assets/{postcss-BHW6XBhx.js → postcss-DWib10RZ.js} +0 -0
- /package/dist/web/assets/{powerquery-B89c2NBb.js → powerquery-Dht4CK0b.js} +0 -0
- /package/dist/web/assets/{powershell-Dvxrd3Tu.js → powershell-DvrCg8ji.js} +0 -0
- /package/dist/web/assets/{prisma-lMK8EFLU.js → prisma-DbodYvCM.js} +0 -0
- /package/dist/web/assets/{prolog-D0YrtMG4.js → prolog-DZ-m6ysB.js} +0 -0
- /package/dist/web/assets/{proto-J0mM25aw.js → proto-HHpG3tGO.js} +0 -0
- /package/dist/web/assets/{puppet-CUT0xkhh.js → puppet-BljHe_75.js} +0 -0
- /package/dist/web/assets/{purescript-CoJWSGRp.js → purescript-DzCiFR0B.js} +0 -0
- /package/dist/web/assets/{python-BBrmuz6P.js → python-B5BQ27cC.js} +0 -0
- /package/dist/web/assets/{qmldir-CUtzs4Q7.js → qmldir-CYlcXrnn.js} +0 -0
- /package/dist/web/assets/{qss-Dm1RZ8aL.js → qss-Ce7T_TEg.js} +0 -0
- /package/dist/web/assets/{r-BGP7qZTe.js → r-Bku-2Jp_.js} +0 -0
- /package/dist/web/assets/{racket-K_6U1lqJ.js → racket-DtfSL3WR.js} +0 -0
- /package/dist/web/assets/{raku-bNSiZeW_.js → raku-CnDLsBJr.js} +0 -0
- /package/dist/web/assets/{react-X4jUntEf.js → react-Dfio3PfK.js} +0 -0
- /package/dist/web/assets/{red-uVeIEF0f.js → red-vbqcE9b-.js} +0 -0
- /package/dist/web/assets/{refresh-cw-B9iv7csb.js → refresh-cw-84mz8aOE.js} +0 -0
- /package/dist/web/assets/{reg-DSm9GWjh.js → reg-DIDLT8-A.js} +0 -0
- /package/dist/web/assets/{regexp-CljRm_j_.js → regexp-CB4h-VPu.js} +0 -0
- /package/dist/web/assets/{rel-d3JcopZg.js → rel-CiQEnWXa.js} +0 -0
- /package/dist/web/assets/{resolve-theme-DLKmm-lE.js → resolve-theme-DPTQHAy1.js} +0 -0
- /package/dist/web/assets/{riscv-CJmaELjT.js → riscv-R78iq5Rf.js} +0 -0
- /package/dist/web/assets/{ron-D0g9B1rW.js → ron-DhYENxc4.js} +0 -0
- /package/dist/web/assets/{rose-pine-DOrD2BSH.js → rose-pine-5wBeyjfJ.js} +0 -0
- /package/dist/web/assets/{rose-pine-dawn-CzZQ-LCF.js → rose-pine-dawn-FpKtPCuH.js} +0 -0
- /package/dist/web/assets/{rose-pine-moon-CdmsYp7C.js → rose-pine-moon-BwcIYc7C.js} +0 -0
- /package/dist/web/assets/{rosmsg-CmDth6pO.js → rosmsg-DWIGAYMl.js} +0 -0
- /package/dist/web/assets/{rotate-ccw-DnuPzJRE.js → rotate-ccw-CNTk2J7l.js} +0 -0
- /package/dist/web/assets/{rust-DGdP6jiR.js → rust-DU_ofKgt.js} +0 -0
- /package/dist/web/assets/{sass-Dkgt2FKZ.js → sass-U9SUuYEy.js} +0 -0
- /package/dist/web/assets/{save-DxonD4y1.js → save-CtW9ojKk.js} +0 -0
- /package/dist/web/assets/{scala-BQLTON6C.js → scala-op4gjA6j.js} +0 -0
- /package/dist/web/assets/{scheme-DKlxUGCi.js → scheme-tEjkR8rL.js} +0 -0
- /package/dist/web/assets/{sdbl-C3iZJQeZ.js → sdbl-C6SSRmZg.js} +0 -0
- /package/dist/web/assets/{send-B-t5Mz26.js → send-C_sLFt56.js} +0 -0
- /package/dist/web/assets/{shallow-jwPzEunk.js → shallow-C1YoJQA-.js} +0 -0
- /package/dist/web/assets/{shellscript-BRu-ZQAx.js → shellscript-Bf2KiwQj.js} +0 -0
- /package/dist/web/assets/{shield-check-CdTiK1j2.js → shield-check-Du6HKYn9.js} +0 -0
- /package/dist/web/assets/{shield-off-DuetkXKM.js → shield-off-CWKb1MQL.js} +0 -0
- /package/dist/web/assets/{slack-dark-qkm_u2zz.js → slack-dark-oGgCey8r.js} +0 -0
- /package/dist/web/assets/{slack-ochin-BP8_8e4m.js → slack-ochin-CemlB8cE.js} +0 -0
- /package/dist/web/assets/{smalltalk-Bnz4gt07.js → smalltalk-CTE-6jrZ.js} +0 -0
- /package/dist/web/assets/{snazzy-light-fbPZg5io.js → snazzy-light-DoEIGnmz.js} +0 -0
- /package/dist/web/assets/{solarized-dark-CM0vTRoi.js → solarized-dark-CStiJyBg.js} +0 -0
- /package/dist/web/assets/{solarized-light-C4_W9mvs.js → solarized-light-CfuNycS9.js} +0 -0
- /package/dist/web/assets/{solidity-Bz6v1PoD.js → solidity-DL6sO5rd.js} +0 -0
- /package/dist/web/assets/{splunk-DyNxu-0i.js → splunk-yK4ZUFBB.js} +0 -0
- /package/dist/web/assets/{sql-DXRL1gw1.js → sql-Lw03eoN8.js} +0 -0
- /package/dist/web/assets/{ssh-config-2z1QHuOG.js → ssh-config-DeRp25TR.js} +0 -0
- /package/dist/web/assets/{stylus-D4I-VaeY.js → stylus-D8OMsPh-.js} +0 -0
- /package/dist/web/assets/{swift-C_2G-tIc.js → swift-nsl9stPV.js} +0 -0
- /package/dist/web/assets/{synthwave-84-osBjChRP.js → synthwave-84-exAgsgpf.js} +0 -0
- /package/dist/web/assets/{system-verilog-zQP2qPNU.js → system-verilog-DBWf00T6.js} +0 -0
- /package/dist/web/assets/{systemd-UGfjRSBM.js → systemd-nhu27Dw5.js} +0 -0
- /package/dist/web/assets/{table-FLU8ei8b.js → table-GAR4upb6.js} +0 -0
- /package/dist/web/assets/{talonscript-C1XrFPwo.js → talonscript-CNOEcGDc.js} +0 -0
- /package/dist/web/assets/{tasl-B5raVn43.js → tasl-CjYZenSH.js} +0 -0
- /package/dist/web/assets/{tcl-3-vbeMIc.js → tcl-BVA09RDz.js} +0 -0
- /package/dist/web/assets/{terraform-a8VXZDm9.js → terraform-C5EloH1q.js} +0 -0
- /package/dist/web/assets/{text-wrap-B05VGRa-.js → text-wrap-D71Jqz5U.js} +0 -0
- /package/dist/web/assets/{tokyo-night-4SRhf-Hz.js → tokyo-night-D764DdhO.js} +0 -0
- /package/dist/web/assets/{toml-B4N0i8wT.js → toml-CCDYblSB.js} +0 -0
- /package/dist/web/assets/{trash-2-CTXlTflF.js → trash-2-C08fhIg1.js} +0 -0
- /package/dist/web/assets/{triangle-alert-BRq0VAfd.js → triangle-alert-85BQgs3T.js} +0 -0
- /package/dist/web/assets/{tsv-DA5k9Lvw.js → tsv-BZ4sA05e.js} +0 -0
- /package/dist/web/assets/{tsx-D9SZ8owC.js → tsx-DPLBzAt5.js} +0 -0
- /package/dist/web/assets/{turtle-BTHTvyY_.js → turtle-CwGHCq40.js} +0 -0
- /package/dist/web/assets/{typescript-DYzV9p5L.js → typescript-CUH0vBAl.js} +0 -0
- /package/dist/web/assets/{typespec-BC15eh7F.js → typespec-Bq31Vq4u.js} +0 -0
- /package/dist/web/assets/{typst-BJUqzA6D.js → typst-MD9BR9W5.js} +0 -0
- /package/dist/web/assets/{use-is-mobile-DFWViA4S.js → use-is-mobile-DUuki97q.js} +0 -0
- /package/dist/web/assets/{utils-C9OjCrou.js → utils-u2fbYPrY.js} +0 -0
- /package/dist/web/assets/{v-50Lps6oA.js → v-DZV7IxIT.js} +0 -0
- /package/dist/web/assets/{vala-8wiUvvrD.js → vala-urd1zi9S.js} +0 -0
- /package/dist/web/assets/{vb-BlgPLt6B.js → vb-DIZU3zbJ.js} +0 -0
- /package/dist/web/assets/{vendor-xterm-BZOrwF_F.js → vendor-xterm-DCj0zgSC.js} +0 -0
- /package/dist/web/assets/{verilog-CD9pVW5p.js → verilog-Ndz_ciTA.js} +0 -0
- /package/dist/web/assets/{vesper-BrivW4PT.js → vesper-C86M7LJp.js} +0 -0
- /package/dist/web/assets/{vhdl-Bunxfa7N.js → vhdl-_klwDUjd.js} +0 -0
- /package/dist/web/assets/{viml-DndUb2qM.js → viml-DzRXWoQh.js} +0 -0
- /package/dist/web/assets/{vitesse-black-BurZSkxW.js → vitesse-black-TWZ20_3U.js} +0 -0
- /package/dist/web/assets/{vitesse-dark-8UShspzC.js → vitesse-dark-Dl7R6fy9.js} +0 -0
- /package/dist/web/assets/{vitesse-light-DghCAZ90.js → vitesse-light-Cs0Dt2_n.js} +0 -0
- /package/dist/web/assets/{vyper-b-Clr-8u.js → vyper-qnCVj8ia.js} +0 -0
- /package/dist/web/assets/{wasm-CpcKStkw.js → wasm-Ckx9jBPe.js} +0 -0
- /package/dist/web/assets/{wasm-C5v2T06Y.js → wasm-Dy-SJ3W9.js} +0 -0
- /package/dist/web/assets/{wenyan-Dz2srLZR.js → wenyan-DTEy2Lx-.js} +0 -0
- /package/dist/web/assets/{wgsl-CeD1-dRb.js → wgsl-DC7n63Sy.js} +0 -0
- /package/dist/web/assets/{wikitext-CyeS-9FH.js → wikitext-1zFoZiW6.js} +0 -0
- /package/dist/web/assets/{wit-Kr-q-qBQ.js → wit-DtTfCRph.js} +0 -0
- /package/dist/web/assets/{wolfram-BKm8bmln.js → wolfram-B2Bgu1KL.js} +0 -0
- /package/dist/web/assets/{x-YDu4m2Iq.js → x-CeN0Qybc.js} +0 -0
- /package/dist/web/assets/{yaml-2XSxomjM.js → yaml-B-Jxk0IQ.js} +0 -0
- /package/dist/web/assets/{zenscript-CKIS34vf.js → zenscript-BWRfvNER.js} +0 -0
- /package/dist/web/assets/{zig-DdHGNllX.js → zig-CkwuvETm.js} +0 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compact function/combo key row shown above the toolbar while the virtual keyboard is open.
|
|
3
|
+
* Modifiers (Ctrl/Alt/Shift/Win) are STICKY: tap to hold (sends the key-down immediately,
|
|
4
|
+
* highlights the button), tap again to release (key-down was already sent, so this just sends
|
|
5
|
+
* the matching key-up) — lets a chord like "Ctrl + tap on the video" be built one tap at a time.
|
|
6
|
+
* The combo buttons (Ctrl+C etc.) are a shortcut for the same idea: one tap sends the whole
|
|
7
|
+
* down/up sequence, independent of whatever sticky modifiers happen to be held.
|
|
8
|
+
*
|
|
9
|
+
* No server changes needed — every button here is just the existing `{type:"key",code,down}`
|
|
10
|
+
* protocol `use-remote-input-capture.ts` already speaks; the server's own `heldKeyCodes`
|
|
11
|
+
* tracking (`remote-desktop-session.ts`) already releases anything sent down:true here too on
|
|
12
|
+
* disconnect/heartbeat-timeout, the same as a real held key.
|
|
13
|
+
*/
|
|
14
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
15
|
+
import { cn } from "@/lib/utils";
|
|
16
|
+
|
|
17
|
+
export interface RemoteDesktopMobileKeyBarProps {
|
|
18
|
+
sendMessage: (msg: Record<string, unknown>) => void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const MODIFIERS = [
|
|
22
|
+
{ label: "Ctrl", code: "ControlLeft" },
|
|
23
|
+
{ label: "Alt", code: "AltLeft" },
|
|
24
|
+
{ label: "Shift", code: "ShiftLeft" },
|
|
25
|
+
{ label: "Win", code: "MetaLeft" },
|
|
26
|
+
] as const;
|
|
27
|
+
|
|
28
|
+
const FUNCTION_KEYS = [
|
|
29
|
+
{ label: "Esc", code: "Escape" },
|
|
30
|
+
{ label: "Tab", code: "Tab" },
|
|
31
|
+
{ label: "←", code: "ArrowLeft" },
|
|
32
|
+
{ label: "↑", code: "ArrowUp" },
|
|
33
|
+
{ label: "↓", code: "ArrowDown" },
|
|
34
|
+
{ label: "→", code: "ArrowRight" },
|
|
35
|
+
] as const;
|
|
36
|
+
|
|
37
|
+
const COMBOS: readonly { label: string; codes: readonly string[] }[] = [
|
|
38
|
+
{ label: "Ctrl+C", codes: ["ControlLeft", "KeyC"] },
|
|
39
|
+
{ label: "Ctrl+V", codes: ["ControlLeft", "KeyV"] },
|
|
40
|
+
{ label: "Ctrl+Z", codes: ["ControlLeft", "KeyZ"] },
|
|
41
|
+
{ label: "Ctrl+A", codes: ["ControlLeft", "KeyA"] },
|
|
42
|
+
{ label: "Alt+Tab", codes: ["AltLeft", "Tab"] },
|
|
43
|
+
// Windows blocks synthetic Ctrl+Alt+Del (the Secure Attention Sequence) from SendInput
|
|
44
|
+
// regardless of privilege level — this is an OS restriction, not a bug here. The button is
|
|
45
|
+
// best-effort: harmless to try, and there is no way to detect the block ahead of time.
|
|
46
|
+
{ label: "Ctrl+Alt+Del", codes: ["ControlLeft", "AltLeft", "Delete"] },
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
function KeyBarButton({
|
|
50
|
+
onClick,
|
|
51
|
+
active,
|
|
52
|
+
className,
|
|
53
|
+
children,
|
|
54
|
+
}: {
|
|
55
|
+
onClick: () => void;
|
|
56
|
+
active?: boolean;
|
|
57
|
+
className?: string;
|
|
58
|
+
children: React.ReactNode;
|
|
59
|
+
}) {
|
|
60
|
+
return (
|
|
61
|
+
<button
|
|
62
|
+
type="button"
|
|
63
|
+
onClick={onClick}
|
|
64
|
+
className={cn(
|
|
65
|
+
"min-h-11 min-w-11 shrink-0 whitespace-nowrap rounded-md px-2.5 text-xs font-medium text-white/80",
|
|
66
|
+
"active:bg-white/15 transition-colors",
|
|
67
|
+
active && "bg-primary text-primary-foreground",
|
|
68
|
+
className,
|
|
69
|
+
)}
|
|
70
|
+
>
|
|
71
|
+
{children}
|
|
72
|
+
</button>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function RemoteDesktopMobileKeyBar({ sendMessage }: RemoteDesktopMobileKeyBarProps) {
|
|
77
|
+
const [held, setHeld] = useState<ReadonlySet<string>>(new Set());
|
|
78
|
+
const heldRef = useRef(held);
|
|
79
|
+
heldRef.current = held;
|
|
80
|
+
|
|
81
|
+
// Safety net: a modifier left toggled on must not stay logically held on the host once this
|
|
82
|
+
// bar goes away (keyboard dismissed, sheet closed) — same reasoning as `releaseAll` elsewhere.
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
return () => {
|
|
85
|
+
heldRef.current.forEach((code) => sendMessage({ type: "key", code, down: false }));
|
|
86
|
+
};
|
|
87
|
+
}, [sendMessage]);
|
|
88
|
+
|
|
89
|
+
const toggleModifier = useCallback((code: string) => {
|
|
90
|
+
setHeld((prev) => {
|
|
91
|
+
const isHeld = prev.has(code);
|
|
92
|
+
sendMessage({ type: "key", code, down: !isHeld });
|
|
93
|
+
const next = new Set(prev);
|
|
94
|
+
if (isHeld) next.delete(code); else next.add(code);
|
|
95
|
+
return next;
|
|
96
|
+
});
|
|
97
|
+
}, [sendMessage]);
|
|
98
|
+
|
|
99
|
+
const tapKey = useCallback((code: string) => {
|
|
100
|
+
sendMessage({ type: "key", code, down: true });
|
|
101
|
+
sendMessage({ type: "key", code, down: false });
|
|
102
|
+
}, [sendMessage]);
|
|
103
|
+
|
|
104
|
+
const sendCombo = useCallback((codes: readonly string[]) => {
|
|
105
|
+
// Press down in order, release in reverse — mirrors physically pressing then releasing a
|
|
106
|
+
// chord (last key down first key up would read as a different shortcut on some apps).
|
|
107
|
+
codes.forEach((code) => sendMessage({ type: "key", code, down: true }));
|
|
108
|
+
[...codes].reverse().forEach((code) => sendMessage({ type: "key", code, down: false }));
|
|
109
|
+
}, [sendMessage]);
|
|
110
|
+
|
|
111
|
+
return (
|
|
112
|
+
<div
|
|
113
|
+
className="flex shrink-0 gap-1 overflow-x-auto border-t border-white/10 bg-black/90 px-2 py-1.5"
|
|
114
|
+
data-testid="remote-desktop-mobile-key-bar"
|
|
115
|
+
>
|
|
116
|
+
{MODIFIERS.map((m) => (
|
|
117
|
+
<KeyBarButton key={m.code} onClick={() => toggleModifier(m.code)} active={held.has(m.code)}>
|
|
118
|
+
{m.label}
|
|
119
|
+
</KeyBarButton>
|
|
120
|
+
))}
|
|
121
|
+
<div className="mx-0.5 w-px shrink-0 bg-white/10" />
|
|
122
|
+
{FUNCTION_KEYS.map((k) => (
|
|
123
|
+
<KeyBarButton key={k.code} onClick={() => tapKey(k.code)}>{k.label}</KeyBarButton>
|
|
124
|
+
))}
|
|
125
|
+
<div className="mx-0.5 w-px shrink-0 bg-white/10" />
|
|
126
|
+
{COMBOS.map((c) => (
|
|
127
|
+
<KeyBarButton key={c.label} onClick={() => sendCombo(c.codes)}>{c.label}</KeyBarButton>
|
|
128
|
+
))}
|
|
129
|
+
</div>
|
|
130
|
+
);
|
|
131
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Full-screen mobile presentation of the Remote Desktop viewer — `WindowLayer` renders nothing
|
|
3
|
+
* below `md`, so this is what mobile opens instead of the desktop floating window (see
|
|
4
|
+
* `open-remote-desktop.ts`). Self-gated singleton, mounted once at the app root beside the
|
|
5
|
+
* other mobile overlays (`MobileExplorerSheet`, `TeamMemberSheet`) — renders nothing until
|
|
6
|
+
* `useRemoteDesktopMobileOpenState().open()` is called.
|
|
7
|
+
*
|
|
8
|
+
* Deliberately NOT the shared `BottomSheet`: that component tracks `visualViewport` and shrinks
|
|
9
|
+
* its panel to leave room above the on-screen keyboard (right for a chat input you need visible
|
|
10
|
+
* next to the keyboard), but this viewer's "keyboard" is just a hidden input forwarding
|
|
11
|
+
* keystrokes — nothing here needs to stay visible above it, and shrinking the canvas viewport
|
|
12
|
+
* every time the toolbar's Keyboard button is tapped is exactly the wrong behavior. It also
|
|
13
|
+
* wires swipe-to-dismiss on touchstart/move/end, which fights the gesture engine's own
|
|
14
|
+
* `stopPropagation()`-based touch handling. A plain `fixed inset-0` portal sidesteps both —
|
|
15
|
+
* this stays full-viewport no matter what the keyboard does; `remote-desktop-mobile-view.tsx`
|
|
16
|
+
* reads `visualViewport` itself and floats just its toolbar/key-bar row above the keyboard.
|
|
17
|
+
*
|
|
18
|
+
* The heavy viewer (canvas, WebCodecs decoder, gesture engine) is a nested lazy import, same as
|
|
19
|
+
* `TeamMemberSheet` does for its content — nothing here loads until the sheet actually opens.
|
|
20
|
+
*/
|
|
21
|
+
import { Suspense, lazy } from "react";
|
|
22
|
+
import { createPortal } from "react-dom";
|
|
23
|
+
import { usePortalContainer } from "@/components/ui/portal-container-context";
|
|
24
|
+
import { useRemoteDesktopMobileOpenState } from "./use-remote-desktop-mobile-open-state";
|
|
25
|
+
|
|
26
|
+
const RemoteDesktopMobileView = lazy(() => import("./remote-desktop-mobile-view"));
|
|
27
|
+
|
|
28
|
+
export function RemoteDesktopMobileSheet() {
|
|
29
|
+
const isOpen = useRemoteDesktopMobileOpenState((s) => s.isOpen);
|
|
30
|
+
const close = useRemoteDesktopMobileOpenState((s) => s.close);
|
|
31
|
+
const portalContainer = usePortalContainer();
|
|
32
|
+
|
|
33
|
+
if (!isOpen) return null;
|
|
34
|
+
|
|
35
|
+
return createPortal(
|
|
36
|
+
<div className="fixed inset-0 z-[45] bg-black" data-testid="remote-desktop-mobile-sheet">
|
|
37
|
+
<Suspense fallback={<div className="flex h-full items-center justify-center text-sm text-text-2">Loading…</div>}>
|
|
38
|
+
<RemoteDesktopMobileView onClose={close} />
|
|
39
|
+
</Suspense>
|
|
40
|
+
</div>,
|
|
41
|
+
portalContainer ?? document.getElementById("root") ?? document.body,
|
|
42
|
+
);
|
|
43
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bottom (thumb-zone) toolbar for the mobile remote-desktop viewer: Touch/Mouse mode toggle,
|
|
3
|
+
* virtual keyboard, zoom reset, close. Every button is a 44px+ touch target per
|
|
4
|
+
* `docs/design-guidelines.md`'s Mobile-First UI Rules.
|
|
5
|
+
*/
|
|
6
|
+
import { Hand, MousePointer2, Keyboard, ZoomOut, Gauge, X } from "lucide-react";
|
|
7
|
+
import { cn } from "@/lib/utils";
|
|
8
|
+
import { useSettingsStore } from "@/stores/settings-store";
|
|
9
|
+
import type { RemoteDesktopInputMode } from "./use-remote-desktop-touch";
|
|
10
|
+
|
|
11
|
+
export interface RemoteDesktopMobileToolbarProps {
|
|
12
|
+
mode: RemoteDesktopInputMode;
|
|
13
|
+
onToggleMode: () => void;
|
|
14
|
+
onOpenKeyboard: () => void;
|
|
15
|
+
onResetZoom: () => void;
|
|
16
|
+
onClose: () => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function ToolbarButton({
|
|
20
|
+
onClick,
|
|
21
|
+
label,
|
|
22
|
+
active,
|
|
23
|
+
children,
|
|
24
|
+
}: {
|
|
25
|
+
onClick: () => void;
|
|
26
|
+
label: string;
|
|
27
|
+
active?: boolean;
|
|
28
|
+
children: React.ReactNode;
|
|
29
|
+
}) {
|
|
30
|
+
return (
|
|
31
|
+
<button
|
|
32
|
+
type="button"
|
|
33
|
+
onClick={onClick}
|
|
34
|
+
aria-label={label}
|
|
35
|
+
className={cn(
|
|
36
|
+
"flex min-h-11 min-w-11 flex-1 flex-col items-center justify-center gap-0.5 rounded-lg py-1.5 text-[10px]",
|
|
37
|
+
"text-white/80 active:bg-white/10 transition-colors",
|
|
38
|
+
active && "bg-white/15 text-white",
|
|
39
|
+
)}
|
|
40
|
+
>
|
|
41
|
+
{children}
|
|
42
|
+
<span>{label}</span>
|
|
43
|
+
</button>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function RemoteDesktopMobileToolbar({
|
|
48
|
+
mode,
|
|
49
|
+
onToggleMode,
|
|
50
|
+
onOpenKeyboard,
|
|
51
|
+
onResetZoom,
|
|
52
|
+
onClose,
|
|
53
|
+
}: RemoteDesktopMobileToolbarProps) {
|
|
54
|
+
const statsVisible = useSettingsStore((s) => s.remoteDesktopStatsVisible);
|
|
55
|
+
const toggleStats = useSettingsStore((s) => s.toggleRemoteDesktopStatsVisible);
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<div
|
|
59
|
+
className="flex shrink-0 items-stretch gap-1 border-t border-white/10 bg-black/90 px-2 pb-[max(0.375rem,env(safe-area-inset-bottom))] pt-1.5"
|
|
60
|
+
data-testid="remote-desktop-mobile-toolbar"
|
|
61
|
+
>
|
|
62
|
+
<ToolbarButton onClick={onToggleMode} label={mode === "mouse" ? "Mouse" : "Touch"} active>
|
|
63
|
+
{mode === "mouse" ? <MousePointer2 className="size-5" /> : <Hand className="size-5" />}
|
|
64
|
+
</ToolbarButton>
|
|
65
|
+
<ToolbarButton onClick={onOpenKeyboard} label="Keyboard">
|
|
66
|
+
<Keyboard className="size-5" />
|
|
67
|
+
</ToolbarButton>
|
|
68
|
+
<ToolbarButton onClick={onResetZoom} label="Reset zoom">
|
|
69
|
+
<ZoomOut className="size-5" />
|
|
70
|
+
</ToolbarButton>
|
|
71
|
+
<ToolbarButton onClick={toggleStats} label="Stats" active={statsVisible}>
|
|
72
|
+
<Gauge className="size-5" />
|
|
73
|
+
</ToolbarButton>
|
|
74
|
+
<ToolbarButton onClick={onClose} label="Close">
|
|
75
|
+
<X className="size-5" />
|
|
76
|
+
</ToolbarButton>
|
|
77
|
+
</div>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mobile full-screen remote-desktop viewer: canvas + pinch-zoom/pan stage, gesture engine
|
|
3
|
+
* (`use-remote-desktop-touch`), virtual keyboard, and the bottom toolbar — hosted inside
|
|
4
|
+
* `remote-desktop-mobile-sheet.tsx`'s plain full-viewport portal. Shares the exact WS/nonce/
|
|
5
|
+
* ping/decode connection logic the desktop floating window uses
|
|
6
|
+
* (`use-remote-desktop-connection`), so this is presentation + input wiring only, not a
|
|
7
|
+
* parallel connection implementation.
|
|
8
|
+
*/
|
|
9
|
+
import { useRef, useState, useCallback } from "react";
|
|
10
|
+
import { RotateCw, MonitorX } from "lucide-react";
|
|
11
|
+
import { useVisualViewport } from "@/hooks/use-visual-viewport";
|
|
12
|
+
import { useRemoteDesktopConnection } from "./use-remote-desktop-connection";
|
|
13
|
+
import { useRemoteDesktopTouch, type RemoteDesktopInputMode } from "./use-remote-desktop-touch";
|
|
14
|
+
import { useRemoteDesktopVirtualKeyboard } from "./use-remote-desktop-virtual-keyboard";
|
|
15
|
+
import { RemoteDesktopMobileToolbar } from "./remote-desktop-mobile-toolbar";
|
|
16
|
+
import { RemoteDesktopMobileKeyBar } from "./remote-desktop-mobile-key-bar";
|
|
17
|
+
import { RemoteDesktopStatsOverlay } from "./remote-desktop-stats-overlay";
|
|
18
|
+
import { letterboxedContentRect } from "./remote-desktop-coords";
|
|
19
|
+
|
|
20
|
+
export interface RemoteDesktopMobileViewProps {
|
|
21
|
+
onClose: () => void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default function RemoteDesktopMobileView({ onClose }: RemoteDesktopMobileViewProps) {
|
|
25
|
+
const containerRef = useRef<HTMLDivElement | null>(null);
|
|
26
|
+
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
|
27
|
+
const [mode, setMode] = useState<RemoteDesktopInputMode>("mouse");
|
|
28
|
+
const [keyBarOpen, setKeyBarOpen] = useState(false);
|
|
29
|
+
|
|
30
|
+
const {
|
|
31
|
+
connState, errorMessage, decoderStatus, decoderErrorMessage, sendMessage, reconnect,
|
|
32
|
+
getTotalBytes, getFrameCount,
|
|
33
|
+
} = useRemoteDesktopConnection(canvasRef);
|
|
34
|
+
const streaming = connState === "streaming";
|
|
35
|
+
|
|
36
|
+
const { transform, virtualCursor, resetZoom } = useRemoteDesktopTouch({
|
|
37
|
+
containerRef,
|
|
38
|
+
canvasRef,
|
|
39
|
+
mode,
|
|
40
|
+
sendMessage,
|
|
41
|
+
enabled: streaming,
|
|
42
|
+
});
|
|
43
|
+
const { inputRef: keyboardInputRef, show: showKeyboard } = useRemoteDesktopVirtualKeyboard(sendMessage, streaming);
|
|
44
|
+
|
|
45
|
+
// Lifts the toolbar/key-bar row above the on-screen keyboard instead of the whole sheet
|
|
46
|
+
// shrinking to make room for it (that was the previous, since-reverted behavior — see
|
|
47
|
+
// remote-desktop-mobile-sheet.tsx's header comment for why it was wrong here).
|
|
48
|
+
const viewportInsets = useVisualViewport(true);
|
|
49
|
+
const keyboardInset = viewportInsets?.keyboardInset ?? 0;
|
|
50
|
+
|
|
51
|
+
const toggleMode = useCallback(() => setMode((m) => (m === "mouse" ? "touch" : "mouse")), []);
|
|
52
|
+
const openKeyboard = useCallback(() => { showKeyboard(); setKeyBarOpen(true); }, [showKeyboard]);
|
|
53
|
+
|
|
54
|
+
const overlayMessage = decoderStatus === "unsupported"
|
|
55
|
+
? decoderErrorMessage
|
|
56
|
+
: connState === "error"
|
|
57
|
+
? errorMessage
|
|
58
|
+
: connState === "closed"
|
|
59
|
+
? "Disconnected"
|
|
60
|
+
: connState === "connecting"
|
|
61
|
+
? "Connecting…"
|
|
62
|
+
: null;
|
|
63
|
+
|
|
64
|
+
const stageStyle = { transform: `translate(${transform.panX}px, ${transform.panY}px) scale(${transform.scale})` };
|
|
65
|
+
|
|
66
|
+
// Positions the virtual-cursor marker (mouse mode) at the video's actual displayed pixel,
|
|
67
|
+
// not a raw percentage of the stage — the canvas is letterboxed (object-contain) inside the
|
|
68
|
+
// stage whenever the capture's aspect ratio doesn't match the phone's, so a naive `left:
|
|
69
|
+
// xFrac*100%` would land inside the letterbox bars instead of on the video. Mirrors exactly
|
|
70
|
+
// what the gesture engine does for hit-testing (`letterboxedContentRect`), just forward
|
|
71
|
+
// instead of inverse. Reading layout here (not in an effect) is fine: it only feeds a cosmetic
|
|
72
|
+
// overlay position, recomputed on every render the cursor itself changes, which already
|
|
73
|
+
// happens on every drag frame.
|
|
74
|
+
let cursorLeft = `${(virtualCursor?.xFrac ?? 0.5) * 100}%`;
|
|
75
|
+
let cursorTop = `${(virtualCursor?.yFrac ?? 0.5) * 100}%`;
|
|
76
|
+
if (virtualCursor && containerRef.current && canvasRef.current?.width && canvasRef.current.height) {
|
|
77
|
+
const box = containerRef.current.getBoundingClientRect();
|
|
78
|
+
const videoRect = letterboxedContentRect({ left: 0, top: 0, width: box.width, height: box.height }, canvasRef.current.width, canvasRef.current.height);
|
|
79
|
+
cursorLeft = `${videoRect.left + virtualCursor.xFrac * videoRect.width}px`;
|
|
80
|
+
cursorTop = `${videoRect.top + virtualCursor.yFrac * videoRect.height}px`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<div className="relative h-full w-full bg-black" data-testid="remote-desktop-mobile-view" data-conn-state={connState}>
|
|
85
|
+
{/* Shrinks to the space above the on-screen keyboard (`bottom: keyboardInset`) so the
|
|
86
|
+
whole desktop stays visible, just smaller — `object-contain` on the canvas then
|
|
87
|
+
refits the video into whatever's left, same as it does for the bottom toolbar/notch
|
|
88
|
+
insets on a normal phone. `top:0` + explicit `bottom` (not `inset-0`) is what lets
|
|
89
|
+
`bottom` do that shrinking; gesture hit-testing already reads this element's own
|
|
90
|
+
`getBoundingClientRect()` fresh per gesture, so it stays correct as the box resizes. */}
|
|
91
|
+
<div
|
|
92
|
+
ref={containerRef}
|
|
93
|
+
className="absolute inset-x-0 top-0 flex items-center justify-center overflow-hidden"
|
|
94
|
+
style={{ touchAction: "none", bottom: keyboardInset }}
|
|
95
|
+
data-testid="remote-desktop-mobile-stage-container"
|
|
96
|
+
>
|
|
97
|
+
{/* `h-full w-full` on both this stage and the canvas below is load-bearing: it gives
|
|
98
|
+
the canvas a definite box to be letterboxed within (`object-contain` needs one), and
|
|
99
|
+
keeps the CSS transform's default `transform-origin: 50% 50%` exactly at this
|
|
100
|
+
container's center — the same point `fractionFromZoomedPoint` assumes. `min-h-0
|
|
101
|
+
min-w-0` on the canvas defeats the flex-item automatic-minimum-size floor (a
|
|
102
|
+
well-known flexbox gotcha for replaced elements): without it, a flex item's implicit
|
|
103
|
+
`min-height/width: auto` can hold the canvas at its intrinsic capture resolution
|
|
104
|
+
despite `height/width: 100%`, which is what made the video render oversized and get
|
|
105
|
+
clipped instead of actually shrinking to fit. */}
|
|
106
|
+
<div className="relative flex h-full w-full items-center justify-center" style={stageStyle}>
|
|
107
|
+
<canvas
|
|
108
|
+
ref={canvasRef}
|
|
109
|
+
data-testid="remote-desktop-canvas"
|
|
110
|
+
className="block h-full w-full min-h-0 min-w-0 object-contain outline-none"
|
|
111
|
+
/>
|
|
112
|
+
{mode === "mouse" && virtualCursor && streaming && (
|
|
113
|
+
<div
|
|
114
|
+
className="pointer-events-none absolute rounded-full border-2 border-white bg-primary shadow-[0_0_0_1px_rgba(0,0,0,0.6)]"
|
|
115
|
+
style={{
|
|
116
|
+
left: cursorLeft,
|
|
117
|
+
top: cursorTop,
|
|
118
|
+
width: 14,
|
|
119
|
+
height: 14,
|
|
120
|
+
// Counter-scale by 1/zoom: this marker lives inside the zoomed stage, so
|
|
121
|
+
// without this it would grow right along with the video when zoomed in.
|
|
122
|
+
transform: `translate(-50%, -50%) scale(${1 / transform.scale})`,
|
|
123
|
+
}}
|
|
124
|
+
data-testid="remote-desktop-virtual-cursor"
|
|
125
|
+
/>
|
|
126
|
+
)}
|
|
127
|
+
</div>
|
|
128
|
+
|
|
129
|
+
{overlayMessage && (
|
|
130
|
+
<div className="absolute inset-0 flex flex-col items-center justify-center gap-3 bg-black/70 text-sm text-white">
|
|
131
|
+
<MonitorX className="size-6 opacity-70" />
|
|
132
|
+
<span className="max-w-sm px-4 text-center">{overlayMessage}</span>
|
|
133
|
+
{(connState === "error" || connState === "closed") && decoderStatus !== "unsupported" && (
|
|
134
|
+
<button
|
|
135
|
+
onClick={reconnect}
|
|
136
|
+
className="flex items-center gap-1.5 rounded-md bg-white/10 px-3 py-1.5 hover:bg-white/20"
|
|
137
|
+
>
|
|
138
|
+
<RotateCw className="size-3.5" /> Reconnect
|
|
139
|
+
</button>
|
|
140
|
+
)}
|
|
141
|
+
</div>
|
|
142
|
+
)}
|
|
143
|
+
|
|
144
|
+
{/* Hidden input the toolbar's Keyboard button focuses to bring up the soft keyboard —
|
|
145
|
+
kept in normal layout flow (not display:none) since a hidden element cannot receive
|
|
146
|
+
focus, just visually collapsed to nothing. */}
|
|
147
|
+
<input
|
|
148
|
+
ref={keyboardInputRef}
|
|
149
|
+
type="text"
|
|
150
|
+
autoComplete="off"
|
|
151
|
+
autoCapitalize="off"
|
|
152
|
+
autoCorrect="off"
|
|
153
|
+
spellCheck={false}
|
|
154
|
+
className="absolute size-px opacity-0"
|
|
155
|
+
aria-hidden="true"
|
|
156
|
+
tabIndex={-1}
|
|
157
|
+
onBlur={() => setKeyBarOpen(false)}
|
|
158
|
+
data-testid="remote-desktop-virtual-keyboard-input"
|
|
159
|
+
/>
|
|
160
|
+
</div>
|
|
161
|
+
|
|
162
|
+
{/* Floats above the on-screen keyboard via `keyboardInset` instead of the sheet shrinking
|
|
163
|
+
to make room — `translateY` rather than `bottom` so it animates smoothly and needs no
|
|
164
|
+
extra reflow. Sits at the true bottom (translateY(0)) once the keyboard is closed. */}
|
|
165
|
+
<div
|
|
166
|
+
className="absolute inset-x-0 bottom-0"
|
|
167
|
+
style={keyboardInset > 0 ? { transform: `translateY(-${keyboardInset}px)` } : undefined}
|
|
168
|
+
data-testid="remote-desktop-mobile-bottom-bar"
|
|
169
|
+
>
|
|
170
|
+
{keyBarOpen && <RemoteDesktopMobileKeyBar sendMessage={sendMessage} />}
|
|
171
|
+
<RemoteDesktopMobileToolbar
|
|
172
|
+
mode={mode}
|
|
173
|
+
onToggleMode={toggleMode}
|
|
174
|
+
onOpenKeyboard={openKeyboard}
|
|
175
|
+
onResetZoom={resetZoom}
|
|
176
|
+
onClose={onClose}
|
|
177
|
+
/>
|
|
178
|
+
</div>
|
|
179
|
+
|
|
180
|
+
{/* Pushed below the app's top-left device-name pill + the notch safe-area (they overlap a
|
|
181
|
+
plain top-1 position on the phone). */}
|
|
182
|
+
<RemoteDesktopStatsOverlay
|
|
183
|
+
canvasRef={canvasRef}
|
|
184
|
+
getFrameCount={getFrameCount}
|
|
185
|
+
getTotalBytes={getTotalBytes}
|
|
186
|
+
positionClassName="left-1 top-[calc(env(safe-area-inset-top,0px)+2.75rem)]"
|
|
187
|
+
/>
|
|
188
|
+
</div>
|
|
189
|
+
);
|
|
190
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Small, unobtrusive fps/KB-per-s/resolution overlay — shared by the desktop floating window
|
|
3
|
+
* (`remote-desktop-window-content.tsx`) and the mobile full-screen viewer
|
|
4
|
+
* (`remote-desktop-mobile-view.tsx`). Self-gated on `useSettingsStore().remoteDesktopStatsVisible`
|
|
5
|
+
* so both call sites just render it unconditionally; the toggle button on either surface flips
|
|
6
|
+
* that one shared setting.
|
|
7
|
+
*
|
|
8
|
+
* Polls the connection/decoder's ref-backed counters at ~2Hz — not a render dependency on every
|
|
9
|
+
* frame/message, which would defeat the point of keeping them refs.
|
|
10
|
+
*/
|
|
11
|
+
import { useEffect, useRef, useState } from "react";
|
|
12
|
+
import { useSettingsStore } from "@/stores/settings-store";
|
|
13
|
+
import { computeRemoteDesktopStats, type RemoteDesktopStatsSample } from "./remote-desktop-stats";
|
|
14
|
+
|
|
15
|
+
const POLL_INTERVAL_MS = 500;
|
|
16
|
+
|
|
17
|
+
export interface RemoteDesktopStatsOverlayProps {
|
|
18
|
+
canvasRef: React.RefObject<HTMLCanvasElement | null>;
|
|
19
|
+
getFrameCount: () => number;
|
|
20
|
+
getTotalBytes: () => number;
|
|
21
|
+
/** Position utility classes (default top-left). The mobile viewer overrides this to sit below
|
|
22
|
+
* the app's top-left device-name pill + the notch safe-area, which otherwise cover it. */
|
|
23
|
+
positionClassName?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function RemoteDesktopStatsOverlay({ canvasRef, getFrameCount, getTotalBytes, positionClassName = "left-1 top-1" }: RemoteDesktopStatsOverlayProps) {
|
|
27
|
+
const visible = useSettingsStore((s) => s.remoteDesktopStatsVisible);
|
|
28
|
+
const [display, setDisplay] = useState({ fps: 0, kbps: 0, width: 0, height: 0 });
|
|
29
|
+
const lastSampleRef = useRef<RemoteDesktopStatsSample>({ frameCount: 0, totalBytes: 0, atMs: 0 });
|
|
30
|
+
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
if (!visible) return;
|
|
33
|
+
lastSampleRef.current = { frameCount: getFrameCount(), totalBytes: getTotalBytes(), atMs: performance.now() };
|
|
34
|
+
const id = setInterval(() => {
|
|
35
|
+
const next: RemoteDesktopStatsSample = { frameCount: getFrameCount(), totalBytes: getTotalBytes(), atMs: performance.now() };
|
|
36
|
+
const { fps, kbps } = computeRemoteDesktopStats(lastSampleRef.current, next);
|
|
37
|
+
lastSampleRef.current = next;
|
|
38
|
+
const canvas = canvasRef.current;
|
|
39
|
+
setDisplay({ fps, kbps, width: canvas?.width ?? 0, height: canvas?.height ?? 0 });
|
|
40
|
+
}, POLL_INTERVAL_MS);
|
|
41
|
+
return () => clearInterval(id);
|
|
42
|
+
}, [visible, canvasRef, getFrameCount, getTotalBytes]);
|
|
43
|
+
|
|
44
|
+
if (!visible) return null;
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<div
|
|
48
|
+
className={`pointer-events-none absolute z-40 rounded bg-black/60 px-1.5 py-0.5 font-mono text-[10px] text-white/80 ${positionClassName}`}
|
|
49
|
+
data-testid="remote-desktop-stats-overlay"
|
|
50
|
+
>
|
|
51
|
+
{Math.round(display.fps)} fps · {Math.round(display.kbps)} KB/s · {display.width}×{display.height}
|
|
52
|
+
</div>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure fps/KB-per-second math for the stats overlay. Both inputs are cumulative, monotonically
|
|
3
|
+
* increasing counters (decoder frame count, total bytes received) sampled at two points in
|
|
4
|
+
* time — the overlay polls at ~2Hz and calls this on each tick with the previous and current
|
|
5
|
+
* sample, giving a rolling ~500ms window without needing a ring buffer of history.
|
|
6
|
+
*/
|
|
7
|
+
export interface RemoteDesktopStatsSample {
|
|
8
|
+
frameCount: number;
|
|
9
|
+
totalBytes: number;
|
|
10
|
+
/** Sample timestamp, e.g. `performance.now()` — any monotonic clock, ms. */
|
|
11
|
+
atMs: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface RemoteDesktopStats {
|
|
15
|
+
fps: number;
|
|
16
|
+
kbps: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const ZERO_STATS: RemoteDesktopStats = { fps: 0, kbps: 0 };
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Derives instantaneous fps/KB-per-s from two samples. Returns zeros (rather than dividing by
|
|
23
|
+
* zero or a negative interval) when `elapsedMs` isn't positive, and clamps a negative frame/byte
|
|
24
|
+
* delta to zero — the counters reset to 0 on a reconnect, so a sample straddling that moment
|
|
25
|
+
* would otherwise read as a huge negative rate for one tick.
|
|
26
|
+
*/
|
|
27
|
+
export function computeRemoteDesktopStats(prev: RemoteDesktopStatsSample, next: RemoteDesktopStatsSample): RemoteDesktopStats {
|
|
28
|
+
const elapsedMs = next.atMs - prev.atMs;
|
|
29
|
+
if (elapsedMs <= 0) return ZERO_STATS;
|
|
30
|
+
const elapsedSec = elapsedMs / 1000;
|
|
31
|
+
const frames = Math.max(0, next.frameCount - prev.frameCount);
|
|
32
|
+
const bytes = Math.max(0, next.totalBytes - prev.totalBytes);
|
|
33
|
+
return {
|
|
34
|
+
fps: frames / elapsedSec,
|
|
35
|
+
kbps: bytes / 1024 / elapsedSec,
|
|
36
|
+
};
|
|
37
|
+
}
|