@hienlh/ppm 0.21.0 → 0.22.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 +41 -0
- package/CLAUDE.md +29 -4
- package/THIRD-PARTY-NOTICES.md +1 -1
- package/assets/skills/ppm/SKILL.md +1 -1
- package/assets/skills/ppm/references/http-api.md +1 -1
- package/bun.lock +16 -1
- package/dist/web/assets/accounts-settings-section-BVr9-FZJ.js +1 -0
- package/dist/web/assets/{ai-resource-editor-F09pf8Hd.js → ai-resource-editor-CdDpOwjh.js} +1 -1
- package/dist/web/assets/ai-settings-section-B1hQyjAd.js +1 -0
- package/dist/web/assets/ai-settings-section-X2UOfSaS.js +1 -0
- package/dist/web/assets/{angular-html-DkR4iCth.js → angular-html-G66FNlyn.js} +1 -1
- package/dist/web/assets/{angular-ts-DcyL4Chx.js → angular-ts-B-PnUMyt.js} +1 -1
- package/dist/web/assets/{api-client-bfB7cXRM.js → api-client-C1ZwRL4Y.js} +1 -1
- package/dist/web/assets/api-client-pQL53cDQ.js +1 -0
- package/dist/web/assets/{api-settings-BcNjZIvG.js → api-settings-_7Q-ULxp.js} +1 -1
- package/dist/web/assets/{api-themes-DBfyVUtX.js → api-themes-DRAERo4Q.js} +1 -1
- package/dist/web/assets/{apl-BVlrVtjt.js → apl-BmDqqwE8.js} +1 -1
- package/dist/web/assets/{appearance-settings-section-whgnq3ss.js → appearance-settings-section-DLoPK0LD.js} +1 -1
- package/dist/web/assets/architecture-PBZL5I3N-Dx6nBEKT.js +1 -0
- package/dist/web/assets/{astro-MjJlozLj.js → astro-BfwMNfxr.js} +1 -1
- package/dist/web/assets/{audio-preview-Bc54GCAV.js → audio-preview-DsYvgKqj.js} +1 -1
- package/dist/web/assets/{blade-DnJWi1fz.js → blade-BsFV4pnN.js} +1 -1
- package/dist/web/assets/branch-review-tab-G84Ojzae.js +1 -0
- package/dist/web/assets/{bsl-DCmJxSIz.js → bsl-Cw9Outng.js} +1 -1
- package/dist/web/assets/{button-VW4QGKCn.js → button-B9CTrsEf.js} +1 -1
- package/dist/web/assets/{cairo-C8dDenub.js → cairo-DOK5Wqnd.js} +1 -1
- package/dist/web/assets/chat-tab-DQJfjm8E.js +15 -0
- package/dist/web/assets/{cobol-DPx_nifl.js → cobol-DRaRy4FK.js} +1 -1
- package/dist/web/assets/code-editor-D04iUPsZ.js +7 -0
- package/dist/web/assets/{codex-daily-guard-CfybHv_c.js → codex-daily-guard-BSccLRGT.js} +1 -1
- package/dist/web/assets/{coffee-DvFzqhgN.js → coffee-DvXuBmEJ.js} +1 -1
- package/dist/web/assets/{conflict-editor-DenQUOT2.js → conflict-editor-C4ay676e.js} +2 -2
- package/dist/web/assets/context-menu-C6pIsf83.js +1 -0
- package/dist/web/assets/{cpp-BLJN0umi.js → cpp-zCmm8rKA.js} +1 -1
- package/dist/web/assets/{crystal-BXR-9fGw.js → crystal-CdQx6lB6.js} +1 -1
- package/dist/web/assets/{csv-preview-CClrJOuR.js → csv-preview-DbwEmG7o.js} +1 -1
- package/dist/web/assets/{data-grid-overlay-editor-D4fhcMfT.js → data-grid-overlay-editor-6xUCeLHh.js} +1 -1
- package/dist/web/assets/{database-viewer-DItjm8wB.js → database-viewer-S6LsALjX.js} +1 -1
- package/dist/web/assets/{dialog-DQTn55Mt.js → dialog-B84_DJQ8.js} +1 -1
- package/dist/web/assets/diff-viewer-3oBA7vgQ.js +4 -0
- package/dist/web/assets/{docx-preview-DYBkiUaE.js → docx-preview-CXuk-emm.js} +1 -1
- package/dist/web/assets/{edge-rWOlCmXT.js → edge-E8QLt7oi.js} +1 -1
- package/dist/web/assets/{edit-diff-preview-COtmo54G.js → edit-diff-preview-BGGGecVw.js} +1 -1
- package/dist/web/assets/{editor-language-service-DxkZ51E5.js → editor-language-service-DJmaKVY0.js} +1 -1
- package/dist/web/assets/{elixir-E7Y82okW.js → elixir-DPb_-72P.js} +1 -1
- package/dist/web/assets/{elm-DTPGbc-w.js → elm-BMzJY5b0.js} +1 -1
- package/dist/web/assets/{erb-DHqLHrE6.js → erb-AABQNtMK.js} +1 -1
- package/dist/web/assets/{erlang-Byg0nNiV.js → erlang-CEIKOkfP.js} +1 -1
- package/dist/web/assets/{esm-D_DmOM7n.js → esm-B9RVMpwV.js} +1 -1
- package/dist/web/assets/explorer-body-BQ0jyAGv.js +2 -0
- package/dist/web/assets/explorer-window-content-jdkWt34J.js +1 -0
- package/dist/web/assets/{extension-manager-section-BhwB6nqr.js → extension-manager-section-DG1guILi.js} +1 -1
- package/dist/web/assets/{extension-webview-DI4VL2tJ.js → extension-webview-BGSDUbdK.js} +1 -1
- package/dist/web/assets/file-icons-BXdvggK2.js +1 -0
- package/dist/web/assets/file-icons.generated-BtBErEwb.css +1 -0
- package/dist/web/assets/file-store-ojgMXgAw.js +1 -0
- package/dist/web/assets/files-settings-section-Gnyd7han.js +1 -0
- package/dist/web/assets/{format-file-meta-DOpzJJhS.js → format-file-meta-C77h04cZ.js} +1 -1
- package/dist/web/assets/{fortran-fixed-form-CY1n0Xf6.js → fortran-fixed-form-CrXvLHyi.js} +1 -1
- package/dist/web/assets/{fsharp-_vEDSCJz.js → fsharp-DCI4qAz_.js} +1 -1
- package/dist/web/assets/{gdresource-B6XT0wAC.js → gdresource-Vb2kO1MG.js} +1 -1
- package/dist/web/assets/{general-settings-section-BcxwiZ0X.js → general-settings-section-V6Khh15V.js} +1 -1
- package/dist/web/assets/{git-commit-Wk9ovq59.js → git-commit-BMI_OWn-.js} +1 -1
- package/dist/web/assets/git-log-panel-Bi513qjT.js +1 -0
- package/dist/web/assets/{git-rebase-DSarGPvK.js → git-rebase-D-d_2iqZ.js} +1 -1
- package/dist/web/assets/{git-repo-store-Dy69YCHN.js → git-repo-store-88Z8v-pF.js} +1 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-C6qRruHI.js +1 -0
- package/dist/web/assets/{glide-data-grid-BvgAqSUr.js → glide-data-grid-DnQwYSmQ.js} +7 -7
- package/dist/web/assets/{glimmer-js-CkYI35_P.js → glimmer-js-BmNNiR0Z.js} +1 -1
- package/dist/web/assets/{glimmer-ts-BqmIZcds.js → glimmer-ts-DQE2Ufha.js} +1 -1
- package/dist/web/assets/{glsl-Be-yBCF5.js → glsl-BcIDsxRJ.js} +1 -1
- package/dist/web/assets/{graphql-a5DrENHY.js → graphql-DGKSLmGH.js} +1 -1
- package/dist/web/assets/group-chat-tab-DpczdwyV.js +1 -0
- package/dist/web/assets/{hack-BLAg20aD.js → hack-YiC8bEIb.js} +1 -1
- package/dist/web/assets/{haml-Dj6bGOQH.js → haml-DjblxAsz.js} +1 -1
- package/dist/web/assets/{handlebars-dmEa36Oh.js → handlebars-BQepOXaU.js} +1 -1
- package/dist/web/assets/{html-yacZYzSE.js → html-Cayj_2GP.js} +1 -1
- package/dist/web/assets/{html-derivative-lLi3-RNT.js → html-derivative-Weq79bL6.js} +1 -1
- package/dist/web/assets/{http-1KaxcE4e.js → http-BeLfdGdz.js} +1 -1
- package/dist/web/assets/{hurl-ST-dEEZR.js → hurl-D9rypCLK.js} +1 -1
- package/dist/web/assets/{hxml-CpDtuq__.js → hxml-clG8mmSH.js} +1 -1
- package/dist/web/assets/{image-preview-CJVxL0uH.js → image-preview-CzCc4_Do.js} +1 -1
- package/dist/web/assets/image-preview-geometry-9lgeLYB3.js +1 -0
- package/dist/web/assets/index-BH99TIE0.css +1 -0
- package/dist/web/assets/index-DU68xk06.js +52 -0
- package/dist/web/assets/info-3K5VOQVL-BOSpdwlz.js +1 -0
- package/dist/web/assets/{jinja-Cjw-evAn.js → jinja-BShBWfs5.js} +1 -1
- package/dist/web/assets/{jira-watcher-section-DIC1yIV3.js → jira-watcher-section-f2HUbGDY.js} +1 -1
- package/dist/web/assets/{jison-DfATMYoY.js → jison-dN2Nf_Dr.js} +1 -1
- package/dist/web/assets/{julia-DDCaxiqT.js → julia-5U0vR-uJ.js} +1 -1
- package/dist/web/assets/{just-CJL529u6.js → just-BMppVRy6.js} +1 -1
- package/dist/web/assets/keybindings-store-DCRsnKg5.js +1 -0
- package/dist/web/assets/{keyboard-shortcuts-section-BW03P3AF.js → keyboard-shortcuts-section-BpvVEw_Y.js} +1 -1
- package/dist/web/assets/{latex-Zc4EpLpW.js → latex-DSSOKjis.js} +1 -1
- package/dist/web/assets/{liquid-B_6KMNYS.js → liquid-C24dKoyI.js} +1 -1
- package/dist/web/assets/{lua-BtS2idK7.js → lua-DNOsnyhn.js} +1 -1
- package/dist/web/assets/markdown-renderer-Bcaf9_bA.js +1 -0
- package/dist/web/assets/markdown-renderer-DGJp8HC3.js +3 -0
- package/dist/web/assets/{marko-Wk6LBnck.js → marko-26MHO_Q9.js} +1 -1
- package/dist/web/assets/{mdc-cRaXBrzL.js → mdc-Dhw9mGrC.js} +1 -1
- package/dist/web/assets/{media-url-DL8OCGGg.js → media-url-yfqHpPz0.js} +1 -1
- package/dist/web/assets/{mobile-bottom-sheet-CoUauvLF.js → mobile-bottom-sheet-C3TniEP-.js} +1 -1
- package/dist/web/assets/mobile-explorer-sheet-Bol3XAO1.js +1 -0
- package/dist/web/assets/{nextflow-Bos2wvDV.js → nextflow-xH4oJTex.js} +1 -1
- package/dist/web/assets/{nginx-DMlOwzdr.js → nginx-CMz4w0M8.js} +1 -1
- package/dist/web/assets/{nim-D2dZQA88.js → nim-D8FnIYsx.js} +1 -1
- package/dist/web/assets/notification-store-BXt54VIn.js +1 -0
- package/dist/web/assets/{notifications-settings-section-DPUROMw-.js → notifications-settings-section-DqlPDfNX.js} +1 -1
- package/dist/web/assets/{number-overlay-editor-DZ2i_Bnb.js → number-overlay-editor-CRGwULhR.js} +1 -1
- package/dist/web/assets/{overview-panel-R146OIom.js → overview-panel-Di-3aV9p.js} +1 -1
- package/dist/web/assets/packet-RMMSAZCW-BGIE1QdU.js +1 -0
- package/dist/web/assets/panel-store-CboGJAwh.js +1 -0
- package/dist/web/assets/{pdf-preview-BWUPGLp6.js → pdf-preview-CmBI6E7d.js} +1 -1
- package/dist/web/assets/{perl-C5-Z6fPn.js → perl-D9iNWYwb.js} +1 -1
- package/dist/web/assets/{php-BXNNhkMH.js → php-Bw3Qo0Zv.js} +1 -1
- package/dist/web/assets/pie-UPGHQEXC-Dg-QTbXX.js +1 -0
- package/dist/web/assets/{postgres-viewer-CjOvaruz.js → postgres-viewer-8ZAqEALv.js} +3 -3
- package/dist/web/assets/{ppmbot-settings-section-DrvWh8l8.js → ppmbot-settings-section-DKJUA3Kq.js} +1 -1
- package/dist/web/assets/problems-panel-CFcoUx8h.js +1 -0
- package/dist/web/assets/{process-table-BRMS1MrX.js → process-table-EKr2dqG6.js} +1 -1
- package/dist/web/assets/project-framework-store-B70Q3dNM.js +1 -0
- package/dist/web/assets/project-store-B87hLRSk.js +1 -0
- package/dist/web/assets/{proxy-settings-section-Bv0Ny-Yy.js → proxy-settings-section-B7qRVl8w.js} +1 -1
- package/dist/web/assets/{pug-C1zf1yWy.js → pug-fT5JC6Vm.js} +1 -1
- package/dist/web/assets/{qml-OW58jGpD.js → qml-P_wEDB1w.js} +1 -1
- package/dist/web/assets/{query-audit-section-BcHJZcVC.js → query-audit-section-By6qyqdB.js} +1 -1
- package/dist/web/assets/radar-KQ55EAFF-DZ6IVu2s.js +1 -0
- package/dist/web/assets/{razor-DzjSaobM.js → razor-Drrt2_Ph.js} +1 -1
- package/dist/web/assets/remote-desktop-clipboard-notice-0WDYsyCA.js +1 -0
- package/dist/web/assets/remote-desktop-mobile-sheet-COYDg16H.js +2 -0
- package/dist/web/assets/remote-desktop-mobile-view-2DqSuS2U.js +1 -0
- package/dist/web/assets/{remote-desktop-readiness-gate-BTzss2wX.js → remote-desktop-readiness-gate-Dm_qMJMH.js} +1 -1
- package/dist/web/assets/remote-desktop-window-content-UodhsF7W.js +1 -0
- package/dist/web/assets/{rst-RfuihOUW.js → rst-Dr2mW4-h.js} +1 -1
- package/dist/web/assets/{ruby-CNO3pli1.js → ruby-C6EpUsTY.js} +1 -1
- package/dist/web/assets/{run-in-terminal-Cb_hVU8x.js → run-in-terminal-CdefEe_R.js} +1 -1
- package/dist/web/assets/{sas-DZY5BtXx.js → sas-D8ZEeP6v.js} +1 -1
- package/dist/web/assets/{schedules-settings-section-DO8xAOVj.js → schedules-settings-section-bG2lYfGv.js} +1 -1
- package/dist/web/assets/{scroll-area-LtNV8g5o.js → scroll-area-BzeiOd-B.js} +1 -1
- package/dist/web/assets/{scss--4g-REcN.js → scss-ITL7jQ1f.js} +1 -1
- package/dist/web/assets/{send-to-chat-Cnpru1M0.js → send-to-chat-F207yH3u.js} +1 -1
- package/dist/web/assets/{separator-Cds1yxy8.js → separator-TRMsJUYe.js} +1 -1
- package/dist/web/assets/settings-body-C06bFarv.js +2 -0
- package/dist/web/assets/settings-store-krC6bc79.js +2 -0
- package/dist/web/assets/settings-tab-34RSaASd.js +1 -0
- package/dist/web/assets/settings-window-content-Dr_2lmHH.js +1 -0
- package/dist/web/assets/{shaderlab-Cu4sBiVn.js → shaderlab-mW-IIlhk.js} +1 -1
- package/dist/web/assets/{shellsession-DWzJfEv7.js → shellsession-DhNl5STt.js} +1 -1
- package/dist/web/assets/{soy-BnqE-5Ff.js → soy-SdY0Qjtr.js} +1 -1
- package/dist/web/assets/{sparql-Ctg__NhZ.js → sparql-ftqZo2i-.js} +1 -1
- package/dist/web/assets/{sql-query-editor-D9k19T_M.js → sql-query-editor-BgRH0cHD.js} +1 -1
- package/dist/web/assets/{sqlite-viewer-DoV9hnWJ.js → sqlite-viewer-DUxGslpa.js} +1 -1
- package/dist/web/assets/{stata-Qk2h66XW.js → stata-Bj7Yjb4b.js} +1 -1
- package/dist/web/assets/{surrealql-8QEX7EfH.js → surrealql-CLhYGAfB.js} +1 -1
- package/dist/web/assets/{svelte-D_mubC5-.js → svelte-DTAlRHhK.js} +1 -1
- package/dist/web/assets/system-monitor-body-IZuLn7Qh.js +2 -0
- package/dist/web/assets/system-monitor-tab-DiSA490Z.js +1 -0
- package/dist/web/assets/{system-monitor-window-content-qXLcicFM.js → system-monitor-window-content-CEaFISgj.js} +1 -1
- package/dist/web/assets/{tab-host-window-content-7fPZNEni.js → tab-host-window-content-D0TN8Wrq.js} +1 -1
- package/dist/web/assets/{tab-store-DGJGnbwv.js → tab-store-OJa1GPCq.js} +1 -1
- package/dist/web/assets/team-member-sheet-DDPNcCjd.js +2 -0
- package/dist/web/assets/{team-member-window-content-BgvJFypM.js → team-member-window-content-DCiW_1-E.js} +1 -1
- package/dist/web/assets/{templ-C1jV0fsO.js → templ-BMfhACxb.js} +1 -1
- package/dist/web/assets/{terminal-tab-Cs0sGljx.js → terminal-tab-4ypP0NDY.js} +3 -3
- package/dist/web/assets/{tex-D_StFXy_.js → tex-BcssM-QA.js} +1 -1
- package/dist/web/assets/tool-cards-Dd6E7Q3k.js +9 -0
- package/dist/web/assets/treemap-KZPCXAKY-CE30wCXa.js +1 -0
- package/dist/web/assets/{ts-tags-B5m47rEm.js → ts-tags-CrR5IYI8.js} +1 -1
- package/dist/web/assets/{twig-C_br6GIl.js → twig-DchfUmyI.js} +1 -1
- package/dist/web/assets/{use-blob-url-C-SGi_Hb.js → use-blob-url-CPtCcY3i.js} +1 -1
- package/dist/web/assets/{use-inline-blame-B4trxGcR.js → use-inline-blame-vrfFHuaR.js} +1 -1
- package/dist/web/assets/{use-lsp-CV0vzILO.js → use-lsp-CsuZu49X.js} +1 -1
- package/dist/web/assets/{use-monaco-theme-DBqbWjWX.js → use-monaco-theme-BIVYJBIn.js} +1 -1
- package/dist/web/assets/{use-open-team-member-DENsjp0s.js → use-open-team-member-C3mrUzIC.js} +1 -1
- package/dist/web/assets/{use-websocket-LgXiX1qN.js → use-websocket-BMbufhX7.js} +1 -1
- package/dist/web/assets/{vendor-mermaid-Cbig5zA6.js → vendor-mermaid-BitiXJlL.js} +1 -1
- package/dist/web/assets/{vendor-ui-BBdkypKW.js → vendor-ui-DmZ-Mb-H.js} +1 -1
- package/dist/web/assets/video-preview-Byj8P_jA.js +1 -0
- package/dist/web/assets/{vue-DQwCnLdr.js → vue-1EL6Pw1h.js} +1 -1
- package/dist/web/assets/{vue-html-T_EoGpPJ.js → vue-html-BPQNwPC6.js} +1 -1
- package/dist/web/assets/{vue-vine-DmxLqX50.js → vue-vine-Bb_dsWLt.js} +1 -1
- package/dist/web/assets/{window-pip-registry-3l4hoxuD.js → window-pip-registry-yD6esSju.js} +1 -1
- package/dist/web/assets/ws-auth-CEYVQ8NH.js +1 -0
- package/dist/web/assets/{xml-Bs6Bw8is.js → xml-dxkLNA0K.js} +1 -1
- package/dist/web/assets/{xsl-f5ZNhRCc.js → xsl-Bw2adwrl.js} +1 -1
- package/dist/web/index.html +27 -26
- package/dist/web/sw.js +1 -1
- package/docs/architecture/plugins-and-tracing.md +285 -0
- package/docs/lessons-learned.md +31 -0
- package/docs/system-architecture.md +1 -0
- package/package.json +2 -1
- package/packages/ext-git-graph/src/compare-diff-base.test.ts +173 -0
- package/packages/ext-git-graph/src/compare-view.ts +22 -3
- package/packages/ext-git-graph/src/extension.ts +6 -0
- package/packages/ext-git-graph/src/types.ts +19 -0
- package/packages/ext-git-graph/src/webview-html.test.ts +134 -1
- package/packages/ext-git-graph/src/webview-html.ts +277 -27
- package/scripts/gen-file-icons.ts +493 -242
- package/src/providers/claude-agent-sdk.ts +42 -1
- package/src/providers/codex-app-server/codex-history.ts +77 -23
- package/src/server/index.ts +42 -7
- package/src/server/routes/chat.ts +15 -11
- package/src/server/routes/git.ts +83 -3
- package/src/server/routes/remote-desktop.ts +7 -2
- package/src/server/ws/global.ts +1 -1
- package/src/server/ws/remote-desktop.ts +12 -5
- package/src/services/binary-content.ts +25 -0
- package/src/services/chat-search.service.ts +1 -1
- package/src/services/chat.service.ts +9 -0
- package/src/services/event-loop-lag.ts +208 -0
- package/src/services/file-lines.ts +114 -0
- package/src/services/file-watcher/recreated-dir-poller.ts +108 -6
- package/src/services/file-watcher/watch-tree.ts +131 -10
- package/src/services/file-watcher.service.ts +73 -23
- package/src/services/git-branch-diff/branch-diff-parse.ts +151 -0
- package/src/services/git-branch-diff/branch-diff.service.ts +79 -0
- package/src/services/git-hunks/git-hunks.service.ts +26 -11
- package/src/services/git-hunks/unified-diff.ts +15 -1
- package/src/services/git.service.ts +74 -24
- package/src/services/jsonl-transcript-parser.ts +138 -6
- package/src/services/log-rotate.ts +142 -0
- package/src/services/media-transcode/ffmpeg-capabilities.ts +72 -2
- package/src/services/remote-desktop/remote-desktop-audio.ts +193 -0
- package/src/services/remote-desktop/remote-desktop-capture-input.ts +87 -14
- package/src/services/remote-desktop/remote-desktop-capture.ts +40 -7
- package/src/services/remote-desktop/remote-desktop-clipboard.ts +166 -0
- package/src/services/remote-desktop/remote-desktop-displays.ts +77 -1
- package/src/services/remote-desktop/remote-desktop-encoder-args.ts +25 -6
- package/src/services/remote-desktop/remote-desktop-evdev-key-map.ts +69 -0
- package/src/services/remote-desktop/remote-desktop-input-linux.ts +253 -0
- package/src/services/remote-desktop/remote-desktop-input-uinput.ts +242 -0
- package/src/services/remote-desktop/remote-desktop-input.ts +40 -8
- package/src/services/remote-desktop/remote-desktop-linux-session.ts +146 -0
- package/src/services/remote-desktop/remote-desktop-ogg-opus.ts +144 -0
- package/src/services/remote-desktop/remote-desktop-privacy.ts +163 -0
- package/src/services/remote-desktop/remote-desktop-quality.ts +138 -0
- package/src/services/remote-desktop/remote-desktop-requirements.ts +158 -5
- package/src/services/remote-desktop/remote-desktop-resolution.ts +302 -0
- package/src/services/remote-desktop/remote-desktop-session.ts +523 -21
- package/src/services/remote-desktop/remote-desktop-x11.ts +250 -0
- package/src/services/supervisor.ts +47 -4
- package/src/shared/remote-desktop-custom-quality.ts +117 -0
- package/src/shared/remote-desktop-quality.ts +127 -0
- package/src/types/chat.ts +4 -0
- package/src/types/git.ts +52 -0
- package/src/web/components/branch-review/branch-review-tab.tsx +360 -0
- package/src/web/components/branch-review/branch-review-tree-row.tsx +138 -0
- package/src/web/components/chat/attachment-chips.tsx +30 -6
- package/src/web/components/chat/chat-tab.tsx +12 -1
- package/src/web/components/chat/message-events.tsx +273 -0
- package/src/web/components/chat/message-list.tsx +107 -861
- package/src/web/components/chat/message-markdown.tsx +34 -0
- package/src/web/components/chat/message-media.tsx +174 -0
- package/src/web/components/chat/message-user-bubble.tsx +399 -0
- package/src/web/components/chat/provider-selector.tsx +30 -22
- package/src/web/components/chat/session-history-list.tsx +2 -2
- package/src/web/components/chat/session-list-panel.tsx +3 -2
- package/src/web/components/editor/binary-diff-view.tsx +212 -0
- package/src/web/components/editor/diff-viewer.tsx +76 -3
- package/src/web/components/explorer/file-tree.tsx +235 -134
- package/src/web/components/explorer/flatten-visible-tree.ts +35 -40
- package/src/web/components/explorer/tree-node.tsx +69 -69
- package/src/web/components/explorer/use-tree-row-dnd.ts +3 -4
- package/src/web/components/git/git-status-panel.tsx +25 -0
- package/src/web/components/layout/draggable-tab.tsx +4 -2
- package/src/web/components/layout/nav-section-rail.tsx +12 -3
- package/src/web/components/layout/status-bar.tsx +10 -6
- package/src/web/components/layout/tab-content.tsx +5 -0
- package/src/web/components/layout/tab-pool.tsx +1 -0
- package/src/web/components/layout/upgrade-button.tsx +5 -1
- package/src/web/components/remote-desktop/remote-desktop-clipboard-client.ts +67 -0
- package/src/web/components/remote-desktop/remote-desktop-clipboard-notice.tsx +127 -0
- package/src/web/components/remote-desktop/remote-desktop-codec-labels.ts +28 -0
- package/src/web/components/remote-desktop/remote-desktop-custom-quality-dialog.tsx +144 -0
- package/src/web/components/remote-desktop/remote-desktop-mobile-key-bar.tsx +13 -2
- package/src/web/components/remote-desktop/remote-desktop-mobile-settings.tsx +232 -0
- package/src/web/components/remote-desktop/remote-desktop-mobile-toolbar.tsx +21 -8
- package/src/web/components/remote-desktop/remote-desktop-mobile-view.tsx +82 -3
- package/src/web/components/remote-desktop/remote-desktop-recording.ts +76 -0
- package/src/web/components/remote-desktop/remote-desktop-resolution-list.ts +100 -0
- package/src/web/components/remote-desktop/remote-desktop-toolbar.tsx +497 -0
- package/src/web/components/remote-desktop/remote-desktop-view-style.ts +145 -0
- package/src/web/components/remote-desktop/remote-desktop-window-content.tsx +126 -35
- package/src/web/components/remote-desktop/use-h264-canvas-decoder.ts +11 -0
- package/src/web/components/remote-desktop/use-opus-audio-player.ts +148 -0
- package/src/web/components/remote-desktop/use-remote-desktop-connection.ts +284 -1
- package/src/web/components/remote-desktop/use-remote-desktop-readiness.ts +75 -1
- package/src/web/components/remote-desktop/use-remote-desktop-recorder.ts +103 -0
- package/src/web/components/remote-desktop/use-remote-desktop-virtual-keyboard.ts +19 -2
- package/src/web/components/remote-desktop/use-remote-input-capture.ts +39 -3
- package/src/web/components/settings/theme-picker.tsx +2 -1
- package/src/web/components/ui/tooltip.tsx +15 -2
- package/src/web/hooks/use-delayed-flag.ts +35 -0
- package/src/web/lib/branch-review-state.ts +175 -0
- package/src/web/lib/file-icon-name.ts +62 -0
- package/src/web/lib/file-icons.generated.ts +3287 -25
- package/src/web/lib/file-icons.tsx +71 -73
- package/src/web/lib/flatten-expansions.ts +18 -5
- package/src/web/lib/message-ordinals.ts +23 -0
- package/src/web/lib/provider-logos.tsx +143 -0
- package/src/web/lib/tab-type-icons.ts +11 -0
- package/src/web/stores/file-store.ts +73 -35
- package/src/web/stores/panel-utils.ts +5 -0
- package/src/web/stores/project-framework-store.ts +82 -0
- package/src/web/stores/project-store.ts +7 -1
- package/src/web/stores/settings-store.ts +128 -0
- package/src/web/stores/tab-store.ts +1 -0
- package/src/web/stores/window-panel-persistence.ts +1 -0
- package/src/web/styles/file-icons.generated.css +3282 -234
- package/src/web/styles/globals.css +26 -0
- package/dist/web/assets/accounts-settings-section-BuxD9zH3.js +0 -1
- package/dist/web/assets/ai-settings-section-Dd9tOUp0.js +0 -1
- package/dist/web/assets/ai-settings-section-FrU2X8Fu.js +0 -1
- package/dist/web/assets/api-client-BUCo1lms.js +0 -1
- package/dist/web/assets/architecture-PBZL5I3N-5K0wEkAn.js +0 -1
- package/dist/web/assets/chat-tab-Cr6_KSGO.js +0 -15
- package/dist/web/assets/code-editor-DfmlGamv.js +0 -7
- package/dist/web/assets/context-menu-DOODKZdF.js +0 -1
- package/dist/web/assets/diff-viewer-BpNj7pN9.js +0 -4
- package/dist/web/assets/explorer-body-D-5z464e.js +0 -2
- package/dist/web/assets/explorer-window-content-7-1wuRul.js +0 -1
- package/dist/web/assets/file-icons-EzPL0q-k.js +0 -1
- package/dist/web/assets/file-icons.generated-CfaJuvZu.css +0 -1
- package/dist/web/assets/file-store-DMABxOkV.js +0 -1
- package/dist/web/assets/files-settings-section-CZzABuKz.js +0 -1
- package/dist/web/assets/git-log-panel-CRN4JVOx.js +0 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-DkVT2MAM.js +0 -1
- package/dist/web/assets/group-chat-tab-DFa5mAx5.js +0 -1
- package/dist/web/assets/index-D7DbXD3G.css +0 -1
- package/dist/web/assets/index-ZtfxZOyb.js +0 -52
- package/dist/web/assets/info-3K5VOQVL-BBsmKGzA.js +0 -1
- package/dist/web/assets/keybindings-store-tVfhPANS.js +0 -1
- package/dist/web/assets/markdown-renderer-CvL1pWeK.js +0 -3
- package/dist/web/assets/markdown-renderer-sJNNVhlS.js +0 -1
- package/dist/web/assets/mobile-explorer-sheet-CZ0HZ2E_.js +0 -1
- package/dist/web/assets/notification-store-BZbsPnW0.js +0 -1
- package/dist/web/assets/packet-RMMSAZCW-DwmYPOlx.js +0 -1
- package/dist/web/assets/panel-store-BVLBRn6M.js +0 -1
- package/dist/web/assets/pie-UPGHQEXC-D0oogIFi.js +0 -1
- package/dist/web/assets/problems-panel-B7BWVhdy.js +0 -1
- package/dist/web/assets/project-store-BhMbVln3.js +0 -1
- package/dist/web/assets/radar-KQ55EAFF-CQIJOQGa.js +0 -1
- package/dist/web/assets/remote-desktop-mobile-sheet-ByjaeZKX.js +0 -2
- package/dist/web/assets/remote-desktop-mobile-view-D13mtr9F.js +0 -1
- package/dist/web/assets/remote-desktop-window-content-CbqTCaC6.js +0 -1
- package/dist/web/assets/settings-body-Rnu-FcX-.js +0 -2
- package/dist/web/assets/settings-store-DZPgPtEA.js +0 -2
- package/dist/web/assets/settings-tab-C6PHxs0Z.js +0 -1
- package/dist/web/assets/settings-window-content-CQ4gT2qj.js +0 -1
- package/dist/web/assets/system-monitor-body-2vQ1kMxE.js +0 -2
- package/dist/web/assets/system-monitor-tab-krAcvwF7.js +0 -1
- package/dist/web/assets/team-member-sheet-2FnRmk1a.js +0 -2
- package/dist/web/assets/tool-cards-DU_S2o51.js +0 -9
- package/dist/web/assets/treemap-KZPCXAKY-BhEtjljr.js +0 -1
- package/dist/web/assets/use-remote-desktop-display-choice-BK_f787P.js +0 -1
- package/dist/web/assets/video-preview-BDNwumbW.js +0 -1
- package/dist/web/assets/ws-auth-DEAJiGCR.js +0 -1
- /package/dist/web/assets/{abap-tkptwpsK.js → abap-IN2yINdN.js} +0 -0
- /package/dist/web/assets/{actionscript-3-Di9TMGwg.js → actionscript-3-C_t772eR.js} +0 -0
- /package/dist/web/assets/{ada-CB9F079D.js → ada-DPme3emm.js} +0 -0
- /package/dist/web/assets/{andromeeda-Dcl8jpb7.js → andromeeda-DSV4i4mZ.js} +0 -0
- /package/dist/web/assets/{apache-D0Nk0qsH.js → apache-CWr8s8I8.js} +0 -0
- /package/dist/web/assets/{apex-mQIoWncD.js → apex-Z20JnDF5.js} +0 -0
- /package/dist/web/assets/{applescript-C6ljbcsI.js → applescript-BKNPu3ga.js} +0 -0
- /package/dist/web/assets/{apply-theme-BwIMNXyX.js → apply-theme-CbR31K6I.js} +0 -0
- /package/dist/web/assets/{ara-WFqRPGPe.js → ara-hWdY3O96.js} +0 -0
- /package/dist/web/assets/{asciidoc-bNd8w4nf.js → asciidoc-eynhq496.js} +0 -0
- /package/dist/web/assets/{asm-BQ1_F580.js → asm-Bu02Kq4P.js} +0 -0
- /package/dist/web/assets/{aurora-x-Ciqs_Dbu.js → aurora-x-BHBGXbIZ.js} +0 -0
- /package/dist/web/assets/{awk-GCR-6KZV.js → awk-B4ojezyv.js} +0 -0
- /package/dist/web/assets/{ayu-dark-DxJXuj99.js → ayu-dark-D0GQzFi9.js} +0 -0
- /package/dist/web/assets/{ayu-light-jvSyJWDV.js → ayu-light-BVYb5Yn8.js} +0 -0
- /package/dist/web/assets/{ayu-mirage-Cg3BPO6H.js → ayu-mirage-BGFW6oNb.js} +0 -0
- /package/dist/web/assets/{ballerina-BPz4CrX6.js → ballerina-B6Og3IKT.js} +0 -0
- /package/dist/web/assets/{bat-CSTs2hIG.js → bat-D8YFILfx.js} +0 -0
- /package/dist/web/assets/{beancount-DMateAZX.js → beancount-BjlbBBuX.js} +0 -0
- /package/dist/web/assets/{berry-9lXg0GRM.js → berry-Cz1ygeoj.js} +0 -0
- /package/dist/web/assets/{bibtex-DIbqmvFg.js → bibtex-CVkJceJh.js} +0 -0
- /package/dist/web/assets/{bicep-02WGsoJ8.js → bicep-DrhYI5GX.js} +0 -0
- /package/dist/web/assets/{bird2-BY0tbu3k.js → bird2-BV-KJtmc.js} +0 -0
- /package/dist/web/assets/{c-jMBX_9Je.js → c-CuLlvxbG.js} +0 -0
- /package/dist/web/assets/{c3-B8JVbNjW.js → c3-CI7-1CJ0.js} +0 -0
- /package/dist/web/assets/{cadence-DUIf1tvL.js → cadence-DjxhNP3Q.js} +0 -0
- /package/dist/web/assets/{can-open-in-ppm-DGEc0s4P.js → can-open-in-ppm-cv5--yHX.js} +0 -0
- /package/dist/web/assets/{catppuccin-frappe-FhUCVxex.js → catppuccin-frappe-CEozpJRh.js} +0 -0
- /package/dist/web/assets/{catppuccin-latte-CzfrtmgL.js → catppuccin-latte-C4sXQFMc.js} +0 -0
- /package/dist/web/assets/{catppuccin-macchiato-6xL0lOtj.js → catppuccin-macchiato-Ci-fDhXy.js} +0 -0
- /package/dist/web/assets/{catppuccin-mocha-D39-xS3P.js → catppuccin-mocha-CH7YXw8d.js} +0 -0
- /package/dist/web/assets/{clarity-D8TeGvoT.js → clarity-BWrJ7UmT.js} +0 -0
- /package/dist/web/assets/{clojure-Dx_acSX7.js → clojure-C94fh2hb.js} +0 -0
- /package/dist/web/assets/{cmake-CyeYPq8o.js → cmake-CtvCkMsG.js} +0 -0
- /package/dist/web/assets/{codeowners-DrTI0lmI.js → codeowners-CJnubqzH.js} +0 -0
- /package/dist/web/assets/{codeql-CPkFxVW-.js → codeql-BOoP5HY2.js} +0 -0
- /package/dist/web/assets/{common-lisp-B0k-3LTj.js → common-lisp-B9nlMFJd.js} +0 -0
- /package/dist/web/assets/{coq-uygpo7to.js → coq-CgzIQiSH.js} +0 -0
- /package/dist/web/assets/{csharp-DKUYGfSp.js → csharp-4xAIx3jw.js} +0 -0
- /package/dist/web/assets/{css-DoOUp7v4.js → css-D4YoLYGn.js} +0 -0
- /package/dist/web/assets/{csv-B3YE13gi.js → csv-C2v-ZakW.js} +0 -0
- /package/dist/web/assets/{csv-parser-DXePqmXe.js → csv-parser-_oDBUkwQ.js} +0 -0
- /package/dist/web/assets/{cue-DViO2wvn.js → cue-Bbznh_an.js} +0 -0
- /package/dist/web/assets/{cypher-4BMCIW5N.js → cypher-BKhVEARF.js} +0 -0
- /package/dist/web/assets/{d-BT_sTHas.js → d-D6IhWopl.js} +0 -0
- /package/dist/web/assets/{dark-plus-DVVXu-1N.js → dark-plus-xAWu790O.js} +0 -0
- /package/dist/web/assets/{dart-DHz6f6_6.js → dart-Bj6S9O1j.js} +0 -0
- /package/dist/web/assets/{data-grid-types-OgUxMLdQ.js → data-grid-types-C5bthITf.js} +0 -0
- /package/dist/web/assets/{dax-ChYCjweK.js → dax-CF2zvaqq.js} +0 -0
- /package/dist/web/assets/{desktop-D6Lt4p04.js → desktop-CnmyxMKE.js} +0 -0
- /package/dist/web/assets/{diff-D8TDe4hP.js → diff-Qi8WuJsI.js} +0 -0
- /package/dist/web/assets/{dist-DOTlFQJA.js → dist-Bjseolz4.js} +0 -0
- /package/dist/web/assets/{dist-CrBOCtCW.js → dist-Czq7BUMy.js} +0 -0
- /package/dist/web/assets/{docker-Bsp0YbPx.js → docker-vLbbNP5d.js} +0 -0
- /package/dist/web/assets/{dotenv-B_7giSY_.js → dotenv-DJMvvNzn.js} +0 -0
- /package/dist/web/assets/{dracula-TrQL2iB5.js → dracula-CtAh2rhB.js} +0 -0
- /package/dist/web/assets/{dracula-soft-n2XOlgh-.js → dracula-soft-CXtuAAf6.js} +0 -0
- /package/dist/web/assets/{dream-maker-CdIEdhpT.js → dream-maker-D5YQyK49.js} +0 -0
- /package/dist/web/assets/{editor-font-BDrdiGf1.js → editor-font-ClTC9gaD.js} +0 -0
- /package/dist/web/assets/{emacs-lisp-CSbixs6u.js → emacs-lisp-CGTjbiqF.js} +0 -0
- /package/dist/web/assets/{everforest-dark-BuZahZYp.js → everforest-dark-D59p7vAQ.js} +0 -0
- /package/dist/web/assets/{everforest-light-DPH53z5H.js → everforest-light-B3Qtud3V.js} +0 -0
- /package/dist/web/assets/{fennel-B1-K_hlJ.js → fennel-BCyHNUwf.js} +0 -0
- /package/dist/web/assets/{fish-Cy12Dbua.js → fish-DDDlLV2G.js} +0 -0
- /package/dist/web/assets/{fluent-B_82ys8E.js → fluent-By54R9tl.js} +0 -0
- /package/dist/web/assets/{format-bytes-D5sBgIqU.js → format-bytes-D1AQyUCt.js} +0 -0
- /package/dist/web/assets/{fortran-free-form-BRQdNzZF.js → fortran-free-form-tP8mRym_.js} +0 -0
- /package/dist/web/assets/{gdscript-XBDmLUm9.js → gdscript-nHC2z6Te.js} +0 -0
- /package/dist/web/assets/{gdshader-DhVfB3u4.js → gdshader-K-cSmPlW.js} +0 -0
- /package/dist/web/assets/{genie-X3K29Y49.js → genie-BAcRhvEe.js} +0 -0
- /package/dist/web/assets/{gherkin-DKIx-6cX.js → gherkin-CcifxMLb.js} +0 -0
- /package/dist/web/assets/{github-dark-Bf7zdp5s.js → github-dark-67KKlMdB.js} +0 -0
- /package/dist/web/assets/{github-dark-default-DqsBBV11.js → github-dark-default-tja8pidR.js} +0 -0
- /package/dist/web/assets/{github-dark-dimmed-X2gqRz6w.js → github-dark-dimmed-CtjhYQtx.js} +0 -0
- /package/dist/web/assets/{github-dark-high-contrast-Z7tvbGX4.js → github-dark-high-contrast-Bf-LPFgn.js} +0 -0
- /package/dist/web/assets/{github-light-BKxt-7zZ.js → github-light-CbJxE7n3.js} +0 -0
- /package/dist/web/assets/{github-light-default-B0liF4d4.js → github-light-default-DRQmdHxt.js} +0 -0
- /package/dist/web/assets/{github-light-high-contrast-F4encJwr.js → github-light-high-contrast-CJkDgTeM.js} +0 -0
- /package/dist/web/assets/{gleam-Pz2bHgBB.js → gleam-D4LCl2ri.js} +0 -0
- /package/dist/web/assets/{gn-CgmdNsPU.js → gn-COY8zZmC.js} +0 -0
- /package/dist/web/assets/{gnuplot-qLNF5yZ9.js → gnuplot-ROk1VitR.js} +0 -0
- /package/dist/web/assets/{go-CSDUNhE7.js → go-H30c3p0C.js} +0 -0
- /package/dist/web/assets/{groovy-B3NEnOHF.js → groovy-DBLCaelV.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-hard-DcoftiUX.js → gruvbox-dark-hard-DJy4irps.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-medium-D1wVSuD8.js → gruvbox-dark-medium-Dhfw1QTO.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-soft-DWpunymH.js → gruvbox-dark-soft-BncJU-8w.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-hard-rHMvyUdS.js → gruvbox-light-hard-Cq12_YvM.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-medium-3O2KiXO7.js → gruvbox-light-medium-DqVja5_v.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-soft-lNlXJPwH.js → gruvbox-light-soft-IvwhveGF.js} +0 -0
- /package/dist/web/assets/{haskell-aaG3aC0h.js → haskell-DA1JV-MC.js} +0 -0
- /package/dist/web/assets/{haxe-CPcHhUDd.js → haxe-V9RTRc5E.js} +0 -0
- /package/dist/web/assets/{hcl-CMQNlR_4.js → hcl-hr1lyZ10.js} +0 -0
- /package/dist/web/assets/{hjson-DeuklPFj.js → hjson-D3vW3i7X.js} +0 -0
- /package/dist/web/assets/{hlsl-BI3hlsaM.js → hlsl-BB85gfJT.js} +0 -0
- /package/dist/web/assets/{horizon-C7-gOcjs.js → horizon-DU8OOnj8.js} +0 -0
- /package/dist/web/assets/{horizon-bright-Cxt9bRW7.js → horizon-bright-DNMUclW6.js} +0 -0
- /package/dist/web/assets/{houston-DbF2G7iD.js → houston-DAiZKf6T.js} +0 -0
- /package/dist/web/assets/{hy-DRG6Hs2I.js → hy-C6Zv0LMe.js} +0 -0
- /package/dist/web/assets/{image-gallery-CObzTisL.js → image-gallery-CGvlv8zR.js} +0 -0
- /package/dist/web/assets/{imba-CxKv_2TW.js → imba-Didnp6Y-.js} +0 -0
- /package/dist/web/assets/{ini-BiHsyCdO.js → ini-m0T7q4gQ.js} +0 -0
- /package/dist/web/assets/{input-D-dR8RI6.js → input-C7Q8FxeL.js} +0 -0
- /package/dist/web/assets/{java-D3RZzgDB.js → java-Buy07jGn.js} +0 -0
- /package/dist/web/assets/{javascript-4FmSqG-T.js → javascript-Cjq3CbRL.js} +0 -0
- /package/dist/web/assets/{json-CjB236_E.js → json-BSVu8IqD.js} +0 -0
- /package/dist/web/assets/{json5-bkfO-Rco.js → json5-NN_Vb7e6.js} +0 -0
- /package/dist/web/assets/{jsonc-BwaTeLyv.js → jsonc-BdyZhBUw.js} +0 -0
- /package/dist/web/assets/{jsonl-CfxNSwGd.js → jsonl-BsDSNvwi.js} +0 -0
- /package/dist/web/assets/{jsonnet-CQETCYrp.js → jsonnet-Gm963F-U.js} +0 -0
- /package/dist/web/assets/{jssm-D5o3Zzdz.js → jssm-Ds3bKWgJ.js} +0 -0
- /package/dist/web/assets/{jsx-BAMILPLo.js → jsx-BT5mW58X.js} +0 -0
- /package/dist/web/assets/{kanagawa-dragon-BhGVxu3R.js → kanagawa-dragon-BvNvf7xd.js} +0 -0
- /package/dist/web/assets/{kanagawa-lotus-FH4Y59ro.js → kanagawa-lotus-CuSiliSn.js} +0 -0
- /package/dist/web/assets/{kanagawa-wave-eilvP9MB.js → kanagawa-wave-C6g-d6jT.js} +0 -0
- /package/dist/web/assets/{kdl-BQ-yvXUK.js → kdl-Bx4kZef-.js} +0 -0
- /package/dist/web/assets/{kotlin-BuCx0-VT.js → kotlin-qvaVvCcm.js} +0 -0
- /package/dist/web/assets/{kusto-DAN_n_Cy.js → kusto-BPLyNCN6.js} +0 -0
- /package/dist/web/assets/{laserwave-Cq2BlxrT.js → laserwave-Sf8ZEhWP.js} +0 -0
- /package/dist/web/assets/{lean-Dhq7uXKw.js → lean-DGe6uqgk.js} +0 -0
- /package/dist/web/assets/{less-CxaMQb8j.js → less-CqM85x-9.js} +0 -0
- /package/dist/web/assets/{lib-BVwmtv2i.js → lib-CD6pOS4u.js} +0 -0
- /package/dist/web/assets/{libesm-DdtEjhIi.js → libesm-CPh1Av0r.js} +0 -0
- /package/dist/web/assets/{light-plus-B4sMAend.js → light-plus-DBtKwhFA.js} +0 -0
- /package/dist/web/assets/{llvm-DQ6-B_6Y.js → llvm-Dpbq3gky.js} +0 -0
- /package/dist/web/assets/{log-Bb_vqvu7.js → log-qKMcPm9S.js} +0 -0
- /package/dist/web/assets/{logo-BzfYv3A_.js → logo-DFo7i5KH.js} +0 -0
- /package/dist/web/assets/{luau-BnAeNR77.js → luau-DGlqWFyL.js} +0 -0
- /package/dist/web/assets/{make-B6IFuxpC.js → make-CixdvUQe.js} +0 -0
- /package/dist/web/assets/{markdown-Z0xi77Fn.js → markdown-CmRnbngE.js} +0 -0
- /package/dist/web/assets/{material-theme-1scy1SYn.js → material-theme-WDUbafB-.js} +0 -0
- /package/dist/web/assets/{material-theme-darker-G-JykXQP.js → material-theme-darker-u8y2xpOk.js} +0 -0
- /package/dist/web/assets/{material-theme-lighter-BtKkCMVq.js → material-theme-lighter-cyQZK3Tg.js} +0 -0
- /package/dist/web/assets/{material-theme-ocean-irrDC89L.js → material-theme-ocean-LBAVvw7k.js} +0 -0
- /package/dist/web/assets/{material-theme-palenight-Eq5_K4LM.js → material-theme-palenight-BJqhBxL0.js} +0 -0
- /package/dist/web/assets/{matlab-BE3oCXZC.js → matlab-B38sOJee.js} +0 -0
- /package/dist/web/assets/{mdx-DYiuFTGw.js → mdx-C8QrE_IW.js} +0 -0
- /package/dist/web/assets/{mermaid-DVlaEvtw.js → mermaid-BQ-eeU8A.js} +0 -0
- /package/dist/web/assets/{min-dark-DSkeTpQa.js → min-dark-D4gNa5tv.js} +0 -0
- /package/dist/web/assets/{min-light-CFv2Q0uF.js → min-light-Dfbhn_PL.js} +0 -0
- /package/dist/web/assets/{mipsasm-CiAFuZQ2.js → mipsasm-bLOhJkdm.js} +0 -0
- /package/dist/web/assets/{mojo-ARshcaSQ.js → mojo-B0BCOMQW.js} +0 -0
- /package/dist/web/assets/{monaco-dotenv-language-BDYnKcnr.js → monaco-dotenv-language-uB-wok6C.js} +0 -0
- /package/dist/web/assets/{monokai-CPTV-SOg.js → monokai-Bdw_07OM.js} +0 -0
- /package/dist/web/assets/{moonbit-D57B0dZr.js → moonbit-BFdGaeuk.js} +0 -0
- /package/dist/web/assets/{move-qgy3G_o9.js → move-CyXQFGPM.js} +0 -0
- /package/dist/web/assets/{narrat-CZ3rC-oc.js → narrat-9DvTD3_3.js} +0 -0
- /package/dist/web/assets/{nextflow-groovy-BxxIOcI4.js → nextflow-groovy-C4VY3kNY.js} +0 -0
- /package/dist/web/assets/{night-owl-B1ryGCda.js → night-owl-feYm66V0.js} +0 -0
- /package/dist/web/assets/{night-owl-light-BZvNnvMF.js → night-owl-light-DALqFjvx.js} +0 -0
- /package/dist/web/assets/{nix-BVBQi8yv.js → nix-C1kPA9Sw.js} +0 -0
- /package/dist/web/assets/{nord-3NXw613h.js → nord-1dvrj1Al.js} +0 -0
- /package/dist/web/assets/{nushell-BMOLRalb.js → nushell-DSQ_9Uqj.js} +0 -0
- /package/dist/web/assets/{objective-c-AxruEUgh.js → objective-c-Dhcn5ZK8.js} +0 -0
- /package/dist/web/assets/{objective-cpp-cFT0qRsD.js → objective-cpp-CGVYCSrp.js} +0 -0
- /package/dist/web/assets/{ocaml-_8JH6O8J.js → ocaml-rxXW21An.js} +0 -0
- /package/dist/web/assets/{odin-nwYvR0O6.js → odin-ClSJX2DE.js} +0 -0
- /package/dist/web/assets/{one-dark-pro-C1LaYJaH.js → one-dark-pro-3gbWDfFW.js} +0 -0
- /package/dist/web/assets/{one-light-B7rMc__c.js → one-light-C2q5ZZAw.js} +0 -0
- /package/dist/web/assets/{openscad-Cwes8bKO.js → openscad-eHXeafBl.js} +0 -0
- /package/dist/web/assets/{pascal-DMt-6tdZ.js → pascal-kKha3Txs.js} +0 -0
- /package/dist/web/assets/{pkl-QBTyYQmB.js → pkl-CTiPytg-.js} +0 -0
- /package/dist/web/assets/{plastic-BTSUp8Fk.js → plastic-D1Ygezi4.js} +0 -0
- /package/dist/web/assets/{plsql-DQv3IFXP.js → plsql-D_gbgZIr.js} +0 -0
- /package/dist/web/assets/{po-CNwZx4tW.js → po-BkhbDUnq.js} +0 -0
- /package/dist/web/assets/{poimandres-8Os7Qequ.js → poimandres-Do6NywrA.js} +0 -0
- /package/dist/web/assets/{polar-b4yK8oD8.js → polar-B3B23wca.js} +0 -0
- /package/dist/web/assets/{postcss-sFctwpxx.js → postcss-Dv_YBvWd.js} +0 -0
- /package/dist/web/assets/{powerquery-Cg_WrtuN.js → powerquery-fKJWI-Sh.js} +0 -0
- /package/dist/web/assets/{powershell-C7Ifz95h.js → powershell-B4Y7KIgx.js} +0 -0
- /package/dist/web/assets/{prisma-Z52HUJzQ.js → prisma-J--XdTXX.js} +0 -0
- /package/dist/web/assets/{prolog-DLqW-zR1.js → prolog-BBTg4Vb3.js} +0 -0
- /package/dist/web/assets/{proto-BOIzxCCJ.js → proto-DWlum3T8.js} +0 -0
- /package/dist/web/assets/{puppet-De7TjZIf.js → puppet-ZiUfc9JV.js} +0 -0
- /package/dist/web/assets/{purescript-R76ou1WT.js → purescript-CmT8SCHB.js} +0 -0
- /package/dist/web/assets/{python-Dprm0tNP.js → python-BURpj-fq.js} +0 -0
- /package/dist/web/assets/{qmldir-Clp_jpNw.js → qmldir-CpaiJywQ.js} +0 -0
- /package/dist/web/assets/{qss-eQPYYzSo.js → qss-C-zdz0i5.js} +0 -0
- /package/dist/web/assets/{r-CCcRHIf5.js → r-K56BeS-s.js} +0 -0
- /package/dist/web/assets/{racket-CzE472yp.js → racket-gkn6Z_Jw.js} +0 -0
- /package/dist/web/assets/{raku-DQwXrTSi.js → raku-C0ZBuKlx.js} +0 -0
- /package/dist/web/assets/{red-KCS6wyop.js → red-BWZ6x-3p.js} +0 -0
- /package/dist/web/assets/{reg-CcrAddUh.js → reg-Dt_EsyGR.js} +0 -0
- /package/dist/web/assets/{regexp-DDbud-Ri.js → regexp-CT6BLG2C.js} +0 -0
- /package/dist/web/assets/{rel-99GDgaHp.js → rel-MJ2LcyzJ.js} +0 -0
- /package/dist/web/assets/{resolve-theme-CW2niGV2.js → resolve-theme-B_ukrg_8.js} +0 -0
- /package/dist/web/assets/{riscv-Dm2eNv0R.js → riscv-BZYWzMAA.js} +0 -0
- /package/dist/web/assets/{ron-CzFt0ygH.js → ron-CLbwHWMn.js} +0 -0
- /package/dist/web/assets/{rose-pine-Ck6lCrCP.js → rose-pine-BPiI_daR.js} +0 -0
- /package/dist/web/assets/{rose-pine-dawn-vtALRJNj.js → rose-pine-dawn-BjkJc7dW.js} +0 -0
- /package/dist/web/assets/{rose-pine-moon-DPto8qXw.js → rose-pine-moon-BQSxkt7g.js} +0 -0
- /package/dist/web/assets/{rosmsg-NtS6429H.js → rosmsg-yt4151pJ.js} +0 -0
- /package/dist/web/assets/{rust-BjIXvF9h.js → rust-1dAcengL.js} +0 -0
- /package/dist/web/assets/{sass-CQ23wf5E.js → sass-DUgtQJsg.js} +0 -0
- /package/dist/web/assets/{scala-LfKLM143.js → scala-CxoGYpS0.js} +0 -0
- /package/dist/web/assets/{scheme-D3SLBXlF.js → scheme-B2K3u9e3.js} +0 -0
- /package/dist/web/assets/{sdbl-ODRd2rGN.js → sdbl-C0Ygk0n6.js} +0 -0
- /package/dist/web/assets/{shellscript-3KB1G_oA.js → shellscript-C2CvhqMk.js} +0 -0
- /package/dist/web/assets/{slack-dark-DTtyWmIy.js → slack-dark-DYdgzRGj.js} +0 -0
- /package/dist/web/assets/{slack-ochin-Bu0PApk3.js → slack-ochin-X-WD0WCg.js} +0 -0
- /package/dist/web/assets/{smalltalk-haMT-Sbe.js → smalltalk-Dp7g_zb9.js} +0 -0
- /package/dist/web/assets/{snazzy-light-DXJzefE9.js → snazzy-light-DWHWr-a6.js} +0 -0
- /package/dist/web/assets/{solarized-dark-B6LyYa18.js → solarized-dark-CxdLsjvb.js} +0 -0
- /package/dist/web/assets/{solarized-light-QLADebkv.js → solarized-light-Cz9rAvG5.js} +0 -0
- /package/dist/web/assets/{solidity-mzfq1aDc.js → solidity-D33gHHdn.js} +0 -0
- /package/dist/web/assets/{splunk-CyH4RWBp.js → splunk-BFbOroYw.js} +0 -0
- /package/dist/web/assets/{sql-CWVlT5D3.js → sql-ChpxvmdY.js} +0 -0
- /package/dist/web/assets/{ssh-config-DNvQJ1Qx.js → ssh-config-CUuSQ6JG.js} +0 -0
- /package/dist/web/assets/{streaming-store-uDu3PoN1.js → streaming-store-BYukdB56.js} +0 -0
- /package/dist/web/assets/{stylus-ByQQdtmf.js → stylus-DDihP0_W.js} +0 -0
- /package/dist/web/assets/{swift-D1cpY4ow.js → swift-XoalUn4x.js} +0 -0
- /package/dist/web/assets/{synthwave-84-BDF_-QiW.js → synthwave-84-DjkG0Bff.js} +0 -0
- /package/dist/web/assets/{system-verilog-BYrkMydC.js → system-verilog-f_pLKMYd.js} +0 -0
- /package/dist/web/assets/{systemd-CQm3zyK0.js → systemd-DjDcLfxp.js} +0 -0
- /package/dist/web/assets/{talonscript-DMW5CzE1.js → talonscript-BpUzYcDl.js} +0 -0
- /package/dist/web/assets/{tasl-BhFzMCr2.js → tasl-DP-cUbg8.js} +0 -0
- /package/dist/web/assets/{tcl-D7VEAF-9.js → tcl-BHoy-dfE.js} +0 -0
- /package/dist/web/assets/{terraform-DdT5A0Vy.js → terraform-vCVxN25B.js} +0 -0
- /package/dist/web/assets/{tokyo-night-WGLMM6Q9.js → tokyo-night-D0u4xZba.js} +0 -0
- /package/dist/web/assets/{toml-CZ7ipXHZ.js → toml-BS8D0__A.js} +0 -0
- /package/dist/web/assets/{tsv-B1zVvoTr.js → tsv-DCb-NQh3.js} +0 -0
- /package/dist/web/assets/{tsx-D4HwRUBq.js → tsx-DF8YeRgG.js} +0 -0
- /package/dist/web/assets/{turtle-B4r8KEJH.js → turtle-Bhx1GlIy.js} +0 -0
- /package/dist/web/assets/{typescript-BnHhxMtp.js → typescript-CAVD2Wb6.js} +0 -0
- /package/dist/web/assets/{typespec-DP2q46GT.js → typespec-D7mchdY_.js} +0 -0
- /package/dist/web/assets/{typst-DFsD_yaD.js → typst-CeXzq6Yw.js} +0 -0
- /package/dist/web/assets/{use-is-mobile-BQ9Bv5Ga.js → use-is-mobile-TkcwP1Gx.js} +0 -0
- /package/dist/web/assets/{use-visual-viewport-CbwnktSG.js → use-visual-viewport-Lp878uzs.js} +0 -0
- /package/dist/web/assets/{v-Bji8hSVT.js → v-DhzO54je.js} +0 -0
- /package/dist/web/assets/{vala-D3TIgGmU.js → vala-Dw_RAk3E.js} +0 -0
- /package/dist/web/assets/{vb-DvcxFS2q.js → vb-CiXdNh2Y.js} +0 -0
- /package/dist/web/assets/{vendor-xterm-BqPDsE9b.js → vendor-xterm-BHEY6Ctu.js} +0 -0
- /package/dist/web/assets/{verilog-Dy7cF35d.js → verilog-fITdxqwV.js} +0 -0
- /package/dist/web/assets/{vesper-BzjwUv9J.js → vesper-B1wm_At7.js} +0 -0
- /package/dist/web/assets/{vhdl-DuFnFNmW.js → vhdl-ebHjqKp9.js} +0 -0
- /package/dist/web/assets/{viml-yAPgKlbz.js → viml-Dgmnoxoo.js} +0 -0
- /package/dist/web/assets/{vitesse-black-oSSQdyIZ.js → vitesse-black-BZ5gMb7l.js} +0 -0
- /package/dist/web/assets/{vitesse-dark-BZxwPihB.js → vitesse-dark-Lij7kkOt.js} +0 -0
- /package/dist/web/assets/{vitesse-light-uIigycvt.js → vitesse-light-DW6Wq7VC.js} +0 -0
- /package/dist/web/assets/{vyper-CX3NAxm5.js → vyper-6FxDnVNg.js} +0 -0
- /package/dist/web/assets/{wasm-6mEOOvVa.js → wasm-BDxaNPCj.js} +0 -0
- /package/dist/web/assets/{wasm-D9td37qO.js → wasm-DKhAtzfe.js} +0 -0
- /package/dist/web/assets/{wenyan-lwNIHgk6.js → wenyan-C0k0DYG_.js} +0 -0
- /package/dist/web/assets/{wgsl-DQAzCfog.js → wgsl-Y2zagbdn.js} +0 -0
- /package/dist/web/assets/{wikitext-DTXmt5NE.js → wikitext-C4QGWzkY.js} +0 -0
- /package/dist/web/assets/{wit-Bdw_7WDH.js → wit-CQESzjHy.js} +0 -0
- /package/dist/web/assets/{wolfram-DqyHDZ07.js → wolfram-B-ia69Ia.js} +0 -0
- /package/dist/web/assets/{yaml-CPXhdByd.js → yaml-CEYYTQPm.js} +0 -0
- /package/dist/web/assets/{zenscript-6s_i5zek.js → zenscript-BggWXIeJ.js} +0 -0
- /package/dist/web/assets/{zig-B2htezJ_.js → zig-CbaUfBQe.js} +0 -0
|
@@ -13,17 +13,11 @@ import { useShallow } from "zustand/react/shallow";
|
|
|
13
13
|
import { useFileStore, getVisiblePaths, absoluteProjectPath, type FileNode } from "@/stores/file-store";
|
|
14
14
|
import { useProjectStore } from "@/stores/project-store";
|
|
15
15
|
import { useTabStore } from "@/stores/tab-store";
|
|
16
|
-
import { useCompareStore } from "@/stores/compare-store";
|
|
17
16
|
import { useGitStatusStore, GIT_STATUS_COLORS, type GitFileStatus } from "@/stores/git-status-store";
|
|
18
17
|
import { cn } from "@/lib/utils";
|
|
19
|
-
import {
|
|
20
|
-
ContextMenu,
|
|
21
|
-
ContextMenuTrigger,
|
|
22
|
-
} from "@/components/ui/adaptive-context-menu";
|
|
23
18
|
import { DROP_TARGET_CLASS } from "@/components/os-explorer/dnd/drop-target-style";
|
|
24
19
|
import type { DropRunner } from "@/components/os-explorer/dnd/entry-drop-executor";
|
|
25
20
|
import { FileIcon } from "@/lib/file-icons";
|
|
26
|
-
import { TreeNodeContextMenu } from "./tree-node-context-menu";
|
|
27
21
|
import { useTreeRowDnd } from "./use-tree-row-dnd";
|
|
28
22
|
import type { NodeRow } from "./flatten-visible-tree";
|
|
29
23
|
|
|
@@ -39,7 +33,7 @@ export interface TreeRowProps {
|
|
|
39
33
|
}
|
|
40
34
|
|
|
41
35
|
export const TreeRow = memo(function TreeRow({ row, projectName, onAction, onFileDrop, onFileOpen, transferRun }: TreeRowProps) {
|
|
42
|
-
const { node,
|
|
36
|
+
const { node, depth } = row;
|
|
43
37
|
const { expandedPaths, loadedPaths, inflight, toggleExpand, selectedFiles, toggleFileSelect, clipboard, focusedPath, setFocusedPath } = useFileStore(
|
|
44
38
|
useShallow((s) => ({
|
|
45
39
|
expandedPaths: s.expandedPaths,
|
|
@@ -55,7 +49,6 @@ export const TreeRow = memo(function TreeRow({ row, projectName, onAction, onFil
|
|
|
55
49
|
);
|
|
56
50
|
const openTab = useTabStore((s) => s.openTab);
|
|
57
51
|
const projectRoot = useProjectStore((s) => s.activeProject?.path);
|
|
58
|
-
const compareSelection = useCompareStore((s) => s.selection);
|
|
59
52
|
const isDir = node.type === "directory";
|
|
60
53
|
// Git decoration: per-file and per-folder status
|
|
61
54
|
const gitStatus: GitFileStatus | undefined = useGitStatusStore((s) => {
|
|
@@ -72,8 +65,8 @@ export const TreeRow = memo(function TreeRow({ row, projectName, onAction, onFil
|
|
|
72
65
|
clipboard?.operation === "cut" &&
|
|
73
66
|
projectRoot != null &&
|
|
74
67
|
clipboard.paths.includes(absoluteProjectPath(projectRoot, node.path));
|
|
75
|
-
const isFocused = focusedPath === node.path
|
|
76
|
-
const isLoadingChildren = isDir && isExpanded && !loadedPaths.has(
|
|
68
|
+
const isFocused = focusedPath === node.path;
|
|
69
|
+
const isLoadingChildren = isDir && isExpanded && !loadedPaths.has(node.path) && inflight.has(node.path);
|
|
77
70
|
const rowRef = useRef<HTMLButtonElement>(null);
|
|
78
71
|
|
|
79
72
|
useEffect(() => {
|
|
@@ -93,7 +86,7 @@ export const TreeRow = memo(function TreeRow({ row, projectName, onAction, onFil
|
|
|
93
86
|
if (e.shiftKey && focusedPath != null) {
|
|
94
87
|
const paths = getVisiblePaths();
|
|
95
88
|
const fromIdx = paths.indexOf(focusedPath);
|
|
96
|
-
const toIdx = paths.indexOf(
|
|
89
|
+
const toIdx = paths.indexOf(node.path);
|
|
97
90
|
if (fromIdx >= 0 && toIdx >= 0) {
|
|
98
91
|
const start = Math.min(fromIdx, toIdx);
|
|
99
92
|
const end = Math.max(fromIdx, toIdx);
|
|
@@ -124,7 +117,6 @@ export const TreeRow = memo(function TreeRow({ row, projectName, onAction, onFil
|
|
|
124
117
|
path: node.path,
|
|
125
118
|
name: node.name,
|
|
126
119
|
isDir,
|
|
127
|
-
effectivePath: effectiveNode.path,
|
|
128
120
|
isSelected,
|
|
129
121
|
selectedFiles,
|
|
130
122
|
isExpanded,
|
|
@@ -137,64 +129,72 @@ export const TreeRow = memo(function TreeRow({ row, projectName, onAction, onFil
|
|
|
137
129
|
|
|
138
130
|
return (
|
|
139
131
|
<div {...dnd.containerHandlers}>
|
|
140
|
-
<
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
<
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
</span>
|
|
185
|
-
)}
|
|
186
|
-
</button>
|
|
187
|
-
</ContextMenuTrigger>
|
|
188
|
-
<TreeNodeContextMenu
|
|
189
|
-
node={node}
|
|
190
|
-
isDir={isDir}
|
|
191
|
-
projectName={projectName}
|
|
192
|
-
selectedFiles={selectedFiles}
|
|
193
|
-
compareSelection={compareSelection}
|
|
194
|
-
clipboard={clipboard}
|
|
195
|
-
onAction={onAction}
|
|
132
|
+
<button
|
|
133
|
+
ref={rowRef}
|
|
134
|
+
{...dnd.entrySource}
|
|
135
|
+
onClick={handleClick}
|
|
136
|
+
className={cn(
|
|
137
|
+
// `leading-[18px]` is what makes the row exactly as tall as the
|
|
138
|
+
// virtualizer's `estimateSize` says it is. Without it the 13px text
|
|
139
|
+
// inherits line-height 1.5 — a 19.5px line box, so the row measured
|
|
140
|
+
// 27.5px against an estimate of 26, and every row scrolling into
|
|
141
|
+
// view corrected the total size underneath the scrollbar.
|
|
142
|
+
"flex items-center w-full gap-1.5 px-2 py-1 rounded-[var(--rad-sm)] text-[13px] leading-[18px]",
|
|
143
|
+
"min-h-[32px] md:min-h-[26px] hover:bg-surface-elevated transition-colors text-left",
|
|
144
|
+
"select-none",
|
|
145
|
+
// A gitignored row is dimmed by `text-text-dim` on its label, not by
|
|
146
|
+
// fading the whole row: an alpha blend is not symmetric between light
|
|
147
|
+
// and dark, and at 40% a gitignored name measured 1.72–1.89:1 against
|
|
148
|
+
// the panel on the three light themes (2.07–2.23:1 on the dark ones) —
|
|
149
|
+
// under a 3:1 floor either way, and on light past the point where it
|
|
150
|
+
// can be read at all. The icon keeps its colours for the same reason:
|
|
151
|
+
// it is full-colour artwork with no `currentColor`, so an opacity on it
|
|
152
|
+
// is that same fade to the background. `cut` is a momentary state and
|
|
153
|
+
// stays a fade.
|
|
154
|
+
isCut && "opacity-40",
|
|
155
|
+
isFocused && "bg-surface-elevated",
|
|
156
|
+
isSelected && "bg-accent-wash",
|
|
157
|
+
dnd.isDragOver && DROP_TARGET_CLASS,
|
|
158
|
+
)}
|
|
159
|
+
style={{ paddingLeft: `${depth * 16 + 8}px` }}
|
|
160
|
+
>
|
|
161
|
+
{isDir ? (
|
|
162
|
+
isLoadingChildren ? (
|
|
163
|
+
<Loader2 className="size-3.5 shrink-0 text-text-subtle animate-spin" />
|
|
164
|
+
) : isExpanded ? (
|
|
165
|
+
<ChevronDown className="size-3.5 shrink-0 text-text-subtle" />
|
|
166
|
+
) : (
|
|
167
|
+
<ChevronRight className="size-3.5 shrink-0 text-text-subtle" />
|
|
168
|
+
)
|
|
169
|
+
) : (
|
|
170
|
+
<span className="w-3.5 shrink-0" />
|
|
171
|
+
)}
|
|
172
|
+
<FileIcon
|
|
173
|
+
name={node.name}
|
|
174
|
+
kind={isDir ? "directory" : "file"}
|
|
175
|
+
open={isExpanded}
|
|
196
176
|
/>
|
|
197
|
-
|
|
177
|
+
<span
|
|
178
|
+
className={cn(
|
|
179
|
+
"truncate",
|
|
180
|
+
gitColor ??
|
|
181
|
+
(isSelected
|
|
182
|
+
? "text-text"
|
|
183
|
+
: isIgnored
|
|
184
|
+
? "text-text-dim"
|
|
185
|
+
: isDir && isExpanded
|
|
186
|
+
? "text-text font-medium"
|
|
187
|
+
: "text-text-2"),
|
|
188
|
+
)}
|
|
189
|
+
>
|
|
190
|
+
{node.name}
|
|
191
|
+
</span>
|
|
192
|
+
{gitStatus && !isDir && (
|
|
193
|
+
<span className={cn("text-[10px] ml-auto shrink-0 font-mono", gitColor)}>
|
|
194
|
+
{gitStatus}
|
|
195
|
+
</span>
|
|
196
|
+
)}
|
|
197
|
+
</button>
|
|
198
198
|
</div>
|
|
199
199
|
);
|
|
200
200
|
});
|
|
@@ -61,8 +61,6 @@ export interface TreeRowDndOptions {
|
|
|
61
61
|
path: string;
|
|
62
62
|
name: string;
|
|
63
63
|
isDir: boolean;
|
|
64
|
-
/** Flat rows: dropping on a file targets its parent directory. */
|
|
65
|
-
effectivePath: string;
|
|
66
64
|
isSelected: boolean;
|
|
67
65
|
selectedFiles: string[];
|
|
68
66
|
isExpanded: boolean;
|
|
@@ -88,7 +86,7 @@ export interface TreeRowDnd {
|
|
|
88
86
|
|
|
89
87
|
export function useTreeRowDnd(options: TreeRowDndOptions): TreeRowDnd {
|
|
90
88
|
const {
|
|
91
|
-
path, name, isDir,
|
|
89
|
+
path, name, isDir, isSelected, selectedFiles, isExpanded,
|
|
92
90
|
projectName, projectRoot, toggleExpand, onFileDrop, transferRun,
|
|
93
91
|
} = options;
|
|
94
92
|
|
|
@@ -111,7 +109,8 @@ export function useTreeRowDnd(options: TreeRowDndOptions): TreeRowDnd {
|
|
|
111
109
|
extraData: { [TREE_LEGACY_DRAG_MIME]: isDir ? `${path}/` : path },
|
|
112
110
|
});
|
|
113
111
|
|
|
114
|
-
|
|
112
|
+
// Dropping on a file targets its parent directory.
|
|
113
|
+
const dropTargetDir = isDir ? path : parentDirOf(path);
|
|
115
114
|
const dropTargetAbsolute = isDir && projectRoot ? absoluteProjectPath(projectRoot, dropTargetDir) : null;
|
|
116
115
|
|
|
117
116
|
const entryTarget = useEntryDropTarget({
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
GitBranch,
|
|
17
17
|
Check,
|
|
18
18
|
SquareDashedMousePointer,
|
|
19
|
+
FileDiff,
|
|
19
20
|
} from "@/lib/icons";
|
|
20
21
|
import { SidebarHeader } from "@/components/ui/sidebar-header";
|
|
21
22
|
import { api, projectUrl } from "@/lib/api-client";
|
|
@@ -506,6 +507,30 @@ export function GitStatusPanel({ metadata, tabId, onNavigate }: GitStatusPanelPr
|
|
|
506
507
|
>
|
|
507
508
|
<GitBranch className="size-3.5" />
|
|
508
509
|
</Button>
|
|
510
|
+
{/*
|
|
511
|
+
Review the whole branch at once, rather than one commit at a time.
|
|
512
|
+
Core rather than the Git Graph extension's compare panel: a review is
|
|
513
|
+
the one git surface that wants Monaco, and a webview cannot have it.
|
|
514
|
+
The tab picks its own defaults — main/master against the current
|
|
515
|
+
branch — so there is nothing to pass here.
|
|
516
|
+
*/}
|
|
517
|
+
<Button
|
|
518
|
+
variant="ghost"
|
|
519
|
+
size="icon-xs"
|
|
520
|
+
onClick={() => {
|
|
521
|
+
openTab({
|
|
522
|
+
type: "branch-review",
|
|
523
|
+
title: "Branch Review",
|
|
524
|
+
projectId: projectName ?? null,
|
|
525
|
+
closable: true,
|
|
526
|
+
metadata: { projectName },
|
|
527
|
+
});
|
|
528
|
+
onNavigate?.();
|
|
529
|
+
}}
|
|
530
|
+
title="Review this branch against another"
|
|
531
|
+
>
|
|
532
|
+
<FileDiff className="size-3.5" />
|
|
533
|
+
</Button>
|
|
509
534
|
<Button
|
|
510
535
|
variant="ghost"
|
|
511
536
|
size="icon-xs"
|
|
@@ -119,8 +119,10 @@ export function DraggableTab({
|
|
|
119
119
|
* an indicator that looked broken rather than animated. `MessageCircle` is
|
|
120
120
|
* `chat-empty`: the same bubble, nothing in it. Safe to swap unconditionally
|
|
121
121
|
* because `isStreaming` is only ever set for a chat tab (both tab-bar.tsx and
|
|
122
|
-
* mobile-tab-switcher-sheet.tsx read `sessionId` only when `type === "chat"`)
|
|
123
|
-
*
|
|
122
|
+
* mobile-tab-switcher-sheet.tsx read `sessionId` only when `type === "chat"`).
|
|
123
|
+
* A chat tab at rest now shows its provider's logo, so this is a visible
|
|
124
|
+
* change of shape — deliberately so: the dots can no more sit on a logo
|
|
125
|
+
* than they could sit on `chat`'s message lines.
|
|
124
126
|
*/}
|
|
125
127
|
{isStreaming ? <MessageCircle className="size-4" /> : <Icon className="size-4" />}
|
|
126
128
|
{isStreaming ? (
|
|
@@ -22,6 +22,15 @@ import { openBugReportPopup } from "@/lib/report-bug";
|
|
|
22
22
|
import { isMobileDevice } from "@/hooks/use-is-mobile";
|
|
23
23
|
import { cn } from "@/lib/utils";
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* The rail's own hover label, standing in for a tooltip on an icon-only button.
|
|
27
|
+
*
|
|
28
|
+
* `hover-label-delayed` (globals.css) is what keeps it from firing as the pointer merely
|
|
29
|
+
* sweeps down the rail, the same wait the shared tooltip takes.
|
|
30
|
+
*/
|
|
31
|
+
const HOVER_LABEL_CLASS =
|
|
32
|
+
"pointer-events-none absolute left-[calc(100%+8px)] z-50 hidden can-hover:group-hover:block hover-label-delayed whitespace-nowrap rounded-md border border-border bg-surface-elevated px-2 py-1 text-xs font-medium text-foreground shadow-[0_4px_12px_rgba(0,0,0,.4)]";
|
|
33
|
+
|
|
25
34
|
function Badge({ count }: { count: number }) {
|
|
26
35
|
return (
|
|
27
36
|
<span className="absolute top-1 right-1 min-w-[16px] h-4 px-1 flex items-center justify-center rounded-full bg-primary text-primary-foreground text-[10px] font-medium leading-none">
|
|
@@ -60,7 +69,7 @@ function NavItem({ icon: Icon, label, active, badge, featureBadge, onClick, drag
|
|
|
60
69
|
{badge != null && badge > 0 && <Badge count={badge} />}
|
|
61
70
|
<FeatureBadge id={featureBadge} variant="corner" />
|
|
62
71
|
{/* hover tooltip (pointer devices only) */}
|
|
63
|
-
<span className=
|
|
72
|
+
<span className={HOVER_LABEL_CLASS}>
|
|
64
73
|
{label}
|
|
65
74
|
</span>
|
|
66
75
|
</button>
|
|
@@ -84,7 +93,7 @@ function FooterUtil({ icon: Icon, label, onClick, active, featureBadge }: {
|
|
|
84
93
|
>
|
|
85
94
|
<Icon className="size-4" />
|
|
86
95
|
<FeatureBadge id={featureBadge} variant="corner" />
|
|
87
|
-
<span className=
|
|
96
|
+
<span className={HOVER_LABEL_CLASS}>
|
|
88
97
|
{label}
|
|
89
98
|
</span>
|
|
90
99
|
</button>
|
|
@@ -202,7 +211,7 @@ export const NavSectionRail = memo(function NavSectionRail({ className }: { clas
|
|
|
202
211
|
)}
|
|
203
212
|
>
|
|
204
213
|
<Cloud className="size-4" />
|
|
205
|
-
<span className=
|
|
214
|
+
<span className={HOVER_LABEL_CLASS}>
|
|
206
215
|
Cloud & Share
|
|
207
216
|
</span>
|
|
208
217
|
</button>
|
|
@@ -27,8 +27,10 @@ export const StatusBar = memo(function StatusBar() {
|
|
|
27
27
|
.sort((a, b) => b.priority - a.priority);
|
|
28
28
|
|
|
29
29
|
return (
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
// `@container`: items compact on the bar's own width, which the sidebar decides, not the viewport.
|
|
31
|
+
<div className="@container hidden md:flex items-center justify-between gap-3 h-[26px] px-3.5 bg-panel border-t border-border-soft text-[11px] font-mono text-text-3 select-none shrink-0">
|
|
32
|
+
{/* The side that gives way: the branch name ellipsizes, then the group clips — never paints over the right. */}
|
|
33
|
+
<div className="flex items-center gap-3 min-w-0 overflow-hidden">
|
|
32
34
|
{/* Git: branch · ahead/behind · synced (design status bar). */}
|
|
33
35
|
<GitStatus />
|
|
34
36
|
{/* Errors/warnings across every open file — VS Code's leftmost item. */}
|
|
@@ -39,7 +41,8 @@ export const StatusBar = memo(function StatusBar() {
|
|
|
39
41
|
{/* Native panel toggle — the sole dock toggle (sidebar/tab-bar toggles removed). */}
|
|
40
42
|
<DockToggle />
|
|
41
43
|
</div>
|
|
42
|
-
|
|
44
|
+
{/* Never shrinks: squeezed, CPU/MEM was its only shrinkable item and wrapped onto two lines. */}
|
|
45
|
+
<div className="flex items-center gap-3 shrink-0">
|
|
43
46
|
{/* Screen-awake marker — only while a wake lock is actually held. */}
|
|
44
47
|
<WakeLockStatusBarItem />
|
|
45
48
|
{/* CPU/MEM moved here from the sidebar resource strip. */}
|
|
@@ -66,19 +69,20 @@ const GitStatus = memo(function GitStatus() {
|
|
|
66
69
|
const synced = !!tracking && ahead === 0 && behind === 0;
|
|
67
70
|
|
|
68
71
|
return (
|
|
69
|
-
<span className="flex items-center gap-2 min-w-0
|
|
72
|
+
<span className="flex items-center gap-2 min-w-0">
|
|
70
73
|
<span className="flex items-center gap-1 text-primary min-w-0" title={tracking ? `Tracking ${tracking}` : "No upstream"}>
|
|
71
74
|
<GitBranch className="size-3 shrink-0" />
|
|
72
75
|
<span className="truncate max-w-[140px]">{branch}</span>
|
|
73
76
|
</span>
|
|
77
|
+
{/* On a bar under 36rem the counts give way, so the branch name stays readable. */}
|
|
74
78
|
{(ahead > 0 || behind > 0) && (
|
|
75
|
-
<span className="flex items-center gap-1.5 shrink-0">
|
|
79
|
+
<span className="flex items-center gap-1.5 shrink-0 @max-xl:hidden">
|
|
76
80
|
{ahead > 0 && <span className="flex items-center gap-0.5"><ArrowUp className="size-3" />{ahead}</span>}
|
|
77
81
|
{behind > 0 && <span className="flex items-center gap-0.5"><ArrowDown className="size-3" />{behind}</span>}
|
|
78
82
|
</span>
|
|
79
83
|
)}
|
|
80
84
|
{synced && (
|
|
81
|
-
<span className="flex items-center gap-1 text-success shrink-0">
|
|
85
|
+
<span className="flex items-center gap-1 text-success shrink-0 @max-xl:hidden">
|
|
82
86
|
<Check className="size-3" />synced
|
|
83
87
|
</span>
|
|
84
88
|
)}
|
|
@@ -39,6 +39,11 @@ const TAB_COMPONENTS: Record<TabType, React.LazyExoticComponent<React.ComponentT
|
|
|
39
39
|
default: m.DiffViewer,
|
|
40
40
|
})),
|
|
41
41
|
),
|
|
42
|
+
"branch-review": lazy(() =>
|
|
43
|
+
import("@/components/branch-review/branch-review-tab").then((m) => ({
|
|
44
|
+
default: m.BranchReviewTab,
|
|
45
|
+
})),
|
|
46
|
+
),
|
|
42
47
|
settings: lazy(() =>
|
|
43
48
|
import("@/components/settings/settings-tab").then((m) => ({
|
|
44
49
|
default: m.SettingsTab,
|
|
@@ -34,6 +34,7 @@ const TAB_COMPONENTS: Record<TabType, React.LazyExoticComponent<React.ComponentT
|
|
|
34
34
|
sqlite: lazy(() => import("@/components/sqlite/sqlite-viewer").then((m) => ({ default: m.SqliteViewer }))),
|
|
35
35
|
postgres: lazy(() => import("@/components/postgres/postgres-viewer").then((m) => ({ default: m.PostgresViewer }))),
|
|
36
36
|
"git-diff": lazy(() => import("@/components/editor/diff-viewer").then((m) => ({ default: m.DiffViewer }))),
|
|
37
|
+
"branch-review": lazy(() => import("@/components/branch-review/branch-review-tab").then((m) => ({ default: m.BranchReviewTab }))),
|
|
37
38
|
settings: lazy(() => import("@/components/settings/settings-tab").then((m) => ({ default: m.SettingsTab }))),
|
|
38
39
|
extension: lazy(() => import("@/components/extensions/extension-webview").then((m) => ({ default: m.ExtensionWebview }))),
|
|
39
40
|
"extension-webview": lazy(() => import("@/components/extensions/extension-webview").then((m) => ({ default: m.ExtensionWebview }))),
|
|
@@ -281,7 +281,11 @@ export function UpgradeButton({ align = "right" }: { align?: "left" | "right" })
|
|
|
281
281
|
)}
|
|
282
282
|
>
|
|
283
283
|
{hasUpdate && <ArrowUpCircle className="size-3" />}
|
|
284
|
-
<span>
|
|
284
|
+
<span>
|
|
285
|
+
{/* Dropped on a status bar under 48rem (its `@container`); the icon and colour still say "update". */}
|
|
286
|
+
{hasUpdate && <span className="@max-3xl:hidden">New version · </span>}
|
|
287
|
+
v{hasUpdate ? effectiveAvailable : current}
|
|
288
|
+
</span>
|
|
285
289
|
</button>
|
|
286
290
|
|
|
287
291
|
{open && current && (
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The browser half of clipboard sync: which key combos mean copy/paste, and how to put text on
|
|
3
|
+
* the *client's* clipboard when `navigator.clipboard` is not there.
|
|
4
|
+
*
|
|
5
|
+
* `navigator.clipboard` is **secure-context only**, and PPM is routinely reached over plain HTTP
|
|
6
|
+
* on a LAN — so on the common deployment the async clipboard API does not exist at all and the
|
|
7
|
+
* host→client direction has to fall back to a real user gesture. Even in a secure context
|
|
8
|
+
* `writeText` rejects when the document is not focused, so the fallback is not dead code there.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/** Ctrl/Cmd+V. This is the one keydown the input capture must *not* `preventDefault()`:
|
|
12
|
+
* cancelling that keydown cancels the browser's paste default action, and with it the `paste`
|
|
13
|
+
* event — measured, not assumed (with the guard removed the event simply never fires). */
|
|
14
|
+
export function isPasteCombo(e: Pick<KeyboardEvent, "code" | "ctrlKey" | "metaKey">): boolean {
|
|
15
|
+
return e.code === "KeyV" && (e.ctrlKey || e.metaKey);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Ctrl/Cmd+C or +X — forwarded to the host normally; the client only uses this as the cue to
|
|
19
|
+
* ask for the host's clipboard a moment later. */
|
|
20
|
+
export function isCopyCombo(e: Pick<KeyboardEvent, "code" | "ctrlKey" | "metaKey">): boolean {
|
|
21
|
+
return (e.code === "KeyC" || e.code === "KeyX") && (e.ctrlKey || e.metaKey);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** How long to wait after forwarding a copy combo before asking the host for its clipboard.
|
|
25
|
+
* The keystroke still has to reach the host, be handled by the focused app, and land on the
|
|
26
|
+
* X/Win32 clipboard; reading too early just reads the previous contents. */
|
|
27
|
+
export const CLIPBOARD_READ_DELAY_MS = 350;
|
|
28
|
+
|
|
29
|
+
/** Put `text` on the client's clipboard from inside a click handler, without a secure context.
|
|
30
|
+
* Must be called synchronously from a user gesture — `execCommand` is ignored otherwise. */
|
|
31
|
+
export function copyTextWithGesture(text: string): boolean {
|
|
32
|
+
const ta = document.createElement("textarea");
|
|
33
|
+
ta.value = text;
|
|
34
|
+
// Off-screen but still rendered and selectable: `display:none` / `visibility:hidden` give an
|
|
35
|
+
// empty selection, and `execCommand("copy")` then copies *nothing* and still returns true.
|
|
36
|
+
ta.style.cssText = "position:fixed;top:0;left:-9999px;opacity:0";
|
|
37
|
+
// iOS Safari refuses to select a readonly textarea, so it is left writable and made
|
|
38
|
+
// non-editable via `inputMode`/`readOnly`-free selection instead of a `readonly` attribute.
|
|
39
|
+
ta.setAttribute("aria-hidden", "true");
|
|
40
|
+
document.body.appendChild(ta);
|
|
41
|
+
const previous = document.activeElement as HTMLElement | null;
|
|
42
|
+
let ok = false;
|
|
43
|
+
try {
|
|
44
|
+
ta.focus({ preventScroll: true });
|
|
45
|
+
ta.setSelectionRange(0, text.length); // iOS: `select()` alone is not enough
|
|
46
|
+
ta.select();
|
|
47
|
+
ok = document.execCommand("copy");
|
|
48
|
+
} catch {
|
|
49
|
+
ok = false;
|
|
50
|
+
}
|
|
51
|
+
ta.remove();
|
|
52
|
+
// Give focus back, or the remote viewer stops receiving keystrokes after one manual copy.
|
|
53
|
+
try { previous?.focus({ preventScroll: true }); } catch { /* element went away */ }
|
|
54
|
+
return ok;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Try the modern API first, falling back to reporting "needs a gesture" to the caller so it can
|
|
58
|
+
* show a button. Never throws. */
|
|
59
|
+
export async function writeClientClipboard(text: string): Promise<boolean> {
|
|
60
|
+
try {
|
|
61
|
+
if (!navigator.clipboard?.writeText) return false;
|
|
62
|
+
await navigator.clipboard.writeText(text);
|
|
63
|
+
return true;
|
|
64
|
+
} catch {
|
|
65
|
+
return false; // insecure origin, denied permission, or an unfocused document
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one piece of clipboard UI, shared by the desktop window and the mobile viewer.
|
|
3
|
+
*
|
|
4
|
+
* It exists because both failure modes of clipboard sync are otherwise *silent*: text the host
|
|
5
|
+
* copied cannot be written to this device without a secure context (PPM is usually plain HTTP on
|
|
6
|
+
* a LAN), and a host with no `xclip`/`wl-copy` installed simply does nothing. One is fixed by a
|
|
7
|
+
* click, the other by a command — so this renders whichever applies and nothing at all when
|
|
8
|
+
* sync is working.
|
|
9
|
+
*/
|
|
10
|
+
import { useState } from "react";
|
|
11
|
+
import { Clipboard, ClipboardCheck, TerminalSquare, X } from "@/lib/icons";
|
|
12
|
+
import { runInTerminal } from "@/lib/run-in-terminal";
|
|
13
|
+
import { cn } from "@/lib/utils";
|
|
14
|
+
import { copyTextWithGesture } from "./remote-desktop-clipboard-client";
|
|
15
|
+
import type { RequirementAction } from "./use-remote-desktop-readiness";
|
|
16
|
+
|
|
17
|
+
export interface RemoteDesktopClipboardNoticeProps {
|
|
18
|
+
/** Host text that needs a real click to reach this device's clipboard. */
|
|
19
|
+
pendingText: string | null;
|
|
20
|
+
onDismiss: () => void;
|
|
21
|
+
/** Set when the user tried to paste and the *host* has no clipboard tool. */
|
|
22
|
+
missingToolAction?: RequirementAction | null;
|
|
23
|
+
onDismissMissingTool?: () => void;
|
|
24
|
+
/** Override the bar's placement. The desktop window floats it over the video; the mobile
|
|
25
|
+
* viewer puts it in normal flow above the toolbar, which the on-screen keyboard already
|
|
26
|
+
* lifts — floating it there would land it behind the toolbar instead. */
|
|
27
|
+
positionClassName?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function RemoteDesktopClipboardNotice({
|
|
31
|
+
pendingText,
|
|
32
|
+
onDismiss,
|
|
33
|
+
missingToolAction,
|
|
34
|
+
onDismissMissingTool,
|
|
35
|
+
positionClassName = "absolute bottom-2 left-1/2 max-w-[92%] -translate-x-1/2",
|
|
36
|
+
}: RemoteDesktopClipboardNoticeProps) {
|
|
37
|
+
const [copyFailed, setCopyFailed] = useState(false);
|
|
38
|
+
|
|
39
|
+
if (pendingText !== null) {
|
|
40
|
+
const onCopy = () => {
|
|
41
|
+
// Synchronous inside the click: `execCommand` is ignored outside a user gesture.
|
|
42
|
+
if (copyTextWithGesture(pendingText)) onDismiss();
|
|
43
|
+
else setCopyFailed(true);
|
|
44
|
+
};
|
|
45
|
+
return (
|
|
46
|
+
<NoticeBar onDismiss={onDismiss} testId="remote-desktop-clipboard-pending" positionClassName={positionClassName}>
|
|
47
|
+
<ClipboardCheck className="size-4 shrink-0 text-emerald-400" />
|
|
48
|
+
<span className="min-w-0 flex-1 truncate">
|
|
49
|
+
{copyFailed ? "Could not copy — select the text manually:" : "Copied on the host:"}
|
|
50
|
+
<span className="ml-1 text-white/60">{preview(pendingText)}</span>
|
|
51
|
+
</span>
|
|
52
|
+
<button
|
|
53
|
+
type="button"
|
|
54
|
+
onClick={onCopy}
|
|
55
|
+
className="min-h-8 shrink-0 rounded bg-white/15 px-2.5 text-xs hover:bg-white/25"
|
|
56
|
+
data-testid="remote-desktop-clipboard-copy"
|
|
57
|
+
>
|
|
58
|
+
Copy here
|
|
59
|
+
</button>
|
|
60
|
+
</NoticeBar>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (missingToolAction !== undefined && missingToolAction !== null) {
|
|
65
|
+
return (
|
|
66
|
+
<NoticeBar
|
|
67
|
+
onDismiss={onDismissMissingTool ?? (() => {})}
|
|
68
|
+
testId="remote-desktop-clipboard-missing-tool"
|
|
69
|
+
positionClassName={positionClassName}
|
|
70
|
+
>
|
|
71
|
+
<Clipboard className="size-4 shrink-0 text-amber-300" />
|
|
72
|
+
<span className="min-w-0 flex-1">The host has no clipboard tool installed.</span>
|
|
73
|
+
{missingToolAction.kind === "terminal" && (
|
|
74
|
+
<button
|
|
75
|
+
type="button"
|
|
76
|
+
onClick={() => runInTerminal(missingToolAction.command)}
|
|
77
|
+
title={missingToolAction.command}
|
|
78
|
+
className="inline-flex min-h-8 shrink-0 items-center gap-1.5 rounded bg-white/15 px-2.5 text-xs hover:bg-white/25"
|
|
79
|
+
>
|
|
80
|
+
<TerminalSquare className="size-3.5" /> {missingToolAction.label}
|
|
81
|
+
</button>
|
|
82
|
+
)}
|
|
83
|
+
</NoticeBar>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Bottom-centre by default, so it never covers the corner controls. */
|
|
91
|
+
function NoticeBar({
|
|
92
|
+
children,
|
|
93
|
+
onDismiss,
|
|
94
|
+
testId,
|
|
95
|
+
positionClassName,
|
|
96
|
+
}: {
|
|
97
|
+
children: React.ReactNode;
|
|
98
|
+
onDismiss: () => void;
|
|
99
|
+
testId: string;
|
|
100
|
+
positionClassName: string;
|
|
101
|
+
}) {
|
|
102
|
+
return (
|
|
103
|
+
<div
|
|
104
|
+
className={cn(
|
|
105
|
+
"z-50 flex items-center gap-2 rounded-lg border border-white/10 bg-black/85 px-3 py-2 text-xs text-white shadow-lg",
|
|
106
|
+
positionClassName,
|
|
107
|
+
)}
|
|
108
|
+
data-testid={testId}
|
|
109
|
+
>
|
|
110
|
+
{children}
|
|
111
|
+
<button
|
|
112
|
+
type="button"
|
|
113
|
+
onClick={onDismiss}
|
|
114
|
+
aria-label="Dismiss"
|
|
115
|
+
className="flex size-8 shrink-0 items-center justify-center rounded text-white/60 hover:bg-white/10 hover:text-white"
|
|
116
|
+
>
|
|
117
|
+
<X className="size-3.5" />
|
|
118
|
+
</button>
|
|
119
|
+
</div>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Enough to recognise the text, on one line — a pasted stack trace must not take the screen. */
|
|
124
|
+
function preview(text: string): string {
|
|
125
|
+
const oneLine = text.replace(/\s+/g, " ").trim();
|
|
126
|
+
return oneLine.length > 60 ? `${oneLine.slice(0, 60)}…` : oneLine;
|
|
127
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Human names for ffmpeg's H.264 encoders, for the codec picker.
|
|
3
|
+
*
|
|
4
|
+
* The wire values are ffmpeg's own encoder names, because that is what the host probes and
|
|
5
|
+
* spawns — but `h264_qsv` says nothing to anyone who has not read the capture code, and the
|
|
6
|
+
* choice a user is actually making is "the GPU or the CPU". Unknown names are shown verbatim
|
|
7
|
+
* rather than hidden: a newer ffmpeg gaining an encoder should add a row here, and until it
|
|
8
|
+
* does the picker must still be able to name what the host is running.
|
|
9
|
+
*/
|
|
10
|
+
const CODEC_LABELS: Record<string, string> = {
|
|
11
|
+
h264_nvenc: "NVIDIA NVENC",
|
|
12
|
+
h264_qsv: "Intel QuickSync",
|
|
13
|
+
h264_vaapi: "VAAPI (GPU)",
|
|
14
|
+
h264_amf: "AMD AMF",
|
|
15
|
+
h264_videotoolbox: "Apple VideoToolbox",
|
|
16
|
+
libx264: "Software (x264)",
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export function codecLabel(encoder: string): string {
|
|
20
|
+
return Object.hasOwn(CODEC_LABELS, encoder) ? CODEC_LABELS[encoder]! : encoder;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Whether this encoder runs on the GPU — the part that matters for "why is my CPU at 100%".
|
|
24
|
+
* Everything except libx264 is hardware; the list is the encoders, not a name pattern, so a
|
|
25
|
+
* future software encoder cannot be mislabelled by matching `h264_*`. */
|
|
26
|
+
export function isHardwareCodec(encoder: string): boolean {
|
|
27
|
+
return encoder !== "libx264" && Object.hasOwn(CODEC_LABELS, encoder);
|
|
28
|
+
}
|