@hienlh/ppm 0.20.9 → 0.21.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 +114 -0
- package/CLAUDE.md +42 -1
- package/LICENSE +5 -0
- package/THIRD-PARTY-NOTICES.md +83 -0
- package/assets/skills/ppm/SKILL.md +1 -1
- package/assets/skills/ppm/references/http-api.md +1 -1
- package/bun.lock +38 -7
- package/dist/web/assets/KaTeX_Size3-Regular-gV2CO0n9.woff2 +0 -0
- package/dist/web/assets/accounts-settings-section-BuxD9zH3.js +1 -0
- package/dist/web/assets/ai-resource-editor-F09pf8Hd.js +1 -0
- package/dist/web/assets/ai-settings-section-Dd9tOUp0.js +1 -0
- package/dist/web/assets/ai-settings-section-FrU2X8Fu.js +1 -0
- package/dist/web/assets/{angular-html-CTx8qAkv.js → angular-html-DkR4iCth.js} +1 -1
- package/dist/web/assets/{angular-ts-BhBGUAnu.js → angular-ts-DcyL4Chx.js} +1 -1
- package/dist/web/assets/api-client-BUCo1lms.js +1 -0
- package/dist/web/assets/{api-settings-CqiVKFUX.js → api-settings-BcNjZIvG.js} +1 -1
- package/dist/web/assets/{api-themes-DtLXqXpC.js → api-themes-DBfyVUtX.js} +1 -1
- package/dist/web/assets/{apl-Dec3pSzF.js → apl-BVlrVtjt.js} +1 -1
- package/dist/web/assets/appearance-settings-section-whgnq3ss.js +1 -0
- package/dist/web/assets/architecture-PBZL5I3N-5K0wEkAn.js +1 -0
- package/dist/web/assets/{astro-Cb_3-S86.js → astro-MjJlozLj.js} +1 -1
- package/dist/web/assets/audio-preview-Bc54GCAV.js +1 -0
- package/dist/web/assets/{blade-1QMj5JxI.js → blade-DnJWi1fz.js} +1 -1
- package/dist/web/assets/{bsl-D0bT3Wjp.js → bsl-DCmJxSIz.js} +1 -1
- package/dist/web/assets/{button-DhnNoqpV.js → button-VW4QGKCn.js} +1 -1
- package/dist/web/assets/{cairo-DTuGb2w7.js → cairo-C8dDenub.js} +1 -1
- package/dist/web/assets/chat-tab-Cr6_KSGO.js +15 -0
- package/dist/web/assets/{cobol-DSx_Nb7T.js → cobol-DPx_nifl.js} +1 -1
- package/dist/web/assets/code-editor-DfmlGamv.js +7 -0
- package/dist/web/assets/codex-daily-guard-CfybHv_c.js +1 -0
- package/dist/web/assets/{coffee-DBcQ7DKB.js → coffee-DvFzqhgN.js} +1 -1
- package/dist/web/assets/conflict-editor-DenQUOT2.js +19 -0
- package/dist/web/assets/context-menu-DOODKZdF.js +1 -0
- package/dist/web/assets/{cpp-Bj1azGr-.js → cpp-BLJN0umi.js} +1 -1
- package/dist/web/assets/{crystal-CCgkDI0i.js → crystal-BXR-9fGw.js} +1 -1
- package/dist/web/assets/csv-preview-CClrJOuR.js +2 -0
- package/dist/web/assets/{data-grid-overlay-editor-C2NAQ-88.js → data-grid-overlay-editor-D4fhcMfT.js} +1 -1
- package/dist/web/assets/database-viewer-DItjm8wB.js +1 -0
- package/dist/web/assets/dialog-DQTn55Mt.js +1 -0
- package/dist/web/assets/diff-viewer-BpNj7pN9.js +4 -0
- package/dist/web/assets/docx-preview-DYBkiUaE.js +12 -0
- package/dist/web/assets/{edge-ClBfeO7z.js → edge-rWOlCmXT.js} +1 -1
- package/dist/web/assets/{edit-diff-preview-CRjufuw_.js → edit-diff-preview-COtmo54G.js} +1 -1
- package/dist/web/assets/editor-font-BDrdiGf1.js +1 -0
- package/dist/web/assets/editor-language-service-DxkZ51E5.js +1 -0
- package/dist/web/assets/{elixir-ir__-3kt.js → elixir-E7Y82okW.js} +1 -1
- package/dist/web/assets/{elm-CUv-xMuU.js → elm-DTPGbc-w.js} +1 -1
- package/dist/web/assets/{erb-DcRwHyUq.js → erb-DHqLHrE6.js} +1 -1
- package/dist/web/assets/{erlang-DW-GAdmv.js → erlang-Byg0nNiV.js} +1 -1
- package/dist/web/assets/{esm-DBm5LSCw.js → esm-D_DmOM7n.js} +1 -1
- package/dist/web/assets/explorer-body-D-5z464e.js +2 -0
- package/dist/web/assets/explorer-window-content-7-1wuRul.js +1 -0
- package/dist/web/assets/extension-manager-section-BhwB6nqr.js +1 -0
- package/dist/web/assets/extension-webview-DI4VL2tJ.js +12 -0
- package/dist/web/assets/file-icons-EzPL0q-k.js +1 -0
- package/dist/web/assets/file-icons.generated-CfaJuvZu.css +1 -0
- package/dist/web/assets/file-store-DMABxOkV.js +1 -0
- package/dist/web/assets/files-settings-section-CZzABuKz.js +1 -0
- package/dist/web/assets/format-file-meta-DOpzJJhS.js +1 -0
- package/dist/web/assets/{fortran-fixed-form-DrQbMSkt.js → fortran-fixed-form-CY1n0Xf6.js} +1 -1
- package/dist/web/assets/{fsharp-DvDj84Ys.js → fsharp-_vEDSCJz.js} +1 -1
- package/dist/web/assets/{gdresource-CPgY7R2s.js → gdresource-B6XT0wAC.js} +1 -1
- package/dist/web/assets/geist-cyrillic-ext-wght-italic-BYAq_K0C.woff2 +0 -0
- package/dist/web/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/dist/web/assets/geist-cyrillic-wght-italic-Da0gXVk3.woff2 +0 -0
- package/dist/web/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/dist/web/assets/geist-latin-ext-wght-italic-XgKbg5n7.woff2 +0 -0
- package/dist/web/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/dist/web/assets/geist-latin-wght-italic-dTnRTFFJ.woff2 +0 -0
- package/dist/web/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/dist/web/assets/geist-vietnamese-wght-italic-0fly6zkB.woff2 +0 -0
- package/dist/web/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/dist/web/assets/general-settings-section-BcxwiZ0X.js +1 -0
- package/dist/web/assets/{git-commit-NJ9iGbTd.js → git-commit-Wk9ovq59.js} +1 -1
- package/dist/web/assets/git-log-panel-CRN4JVOx.js +1 -0
- package/dist/web/assets/{git-rebase-DBSy9OKE.js → git-rebase-DSarGPvK.js} +1 -1
- package/dist/web/assets/git-repo-store-Dy69YCHN.js +1 -0
- package/dist/web/assets/gitGraph-HDMCJU4V-DkVT2MAM.js +1 -0
- package/dist/web/assets/glide-data-grid-BvgAqSUr.js +141 -0
- package/dist/web/assets/{glimmer-js-EGWSSdwJ.js → glimmer-js-CkYI35_P.js} +1 -1
- package/dist/web/assets/{glimmer-ts-BpfXRYRV.js → glimmer-ts-BqmIZcds.js} +1 -1
- package/dist/web/assets/{glsl-BtWQ4j8X.js → glsl-Be-yBCF5.js} +1 -1
- package/dist/web/assets/{graphql-DHR5AO2c.js → graphql-a5DrENHY.js} +1 -1
- package/dist/web/assets/group-chat-tab-DFa5mAx5.js +1 -0
- package/dist/web/assets/{hack-A3iOItmC.js → hack-BLAg20aD.js} +1 -1
- package/dist/web/assets/{haml-DXA8NXfZ.js → haml-Dj6bGOQH.js} +1 -1
- package/dist/web/assets/{handlebars-BdIFK4pP.js → handlebars-dmEa36Oh.js} +1 -1
- package/dist/web/assets/{html-derivative-Cd_vDV4L.js → html-derivative-lLi3-RNT.js} +1 -1
- package/dist/web/assets/{html-BTA4eRkb.js → html-yacZYzSE.js} +1 -1
- package/dist/web/assets/{http-CMY5fp9k.js → http-1KaxcE4e.js} +1 -1
- package/dist/web/assets/{hurl-DJZLRi6K.js → hurl-ST-dEEZR.js} +1 -1
- package/dist/web/assets/{hxml-C2EdtM37.js → hxml-CpDtuq__.js} +1 -1
- package/dist/web/assets/icons-DQjqk0lJ.js +1 -0
- package/dist/web/assets/image-preview-CJVxL0uH.js +1 -0
- package/dist/web/assets/index-D7DbXD3G.css +1 -0
- package/dist/web/assets/index-ZtfxZOyb.js +52 -0
- package/dist/web/assets/info-3K5VOQVL-BBsmKGzA.js +1 -0
- package/dist/web/assets/input-D-dR8RI6.js +1 -0
- package/dist/web/assets/{jinja-DWU92jHm.js → jinja-Cjw-evAn.js} +1 -1
- package/dist/web/assets/{jira-watcher-section-BhQzWJ3r.js → jira-watcher-section-DIC1yIV3.js} +1 -1
- package/dist/web/assets/{jison-Bt3f5-S6.js → jison-DfATMYoY.js} +1 -1
- package/dist/web/assets/{julia-BS-e0jQg.js → julia-DDCaxiqT.js} +1 -1
- package/dist/web/assets/{just-OU89Ot3I.js → just-CJL529u6.js} +1 -1
- package/dist/web/assets/keybindings-store-tVfhPANS.js +1 -0
- package/dist/web/assets/keyboard-shortcuts-section-BW03P3AF.js +1 -0
- package/dist/web/assets/{latex-Bt5mTs31.js → latex-Zc4EpLpW.js} +1 -1
- package/dist/web/assets/{liquid-C03IGCJ6.js → liquid-B_6KMNYS.js} +1 -1
- package/dist/web/assets/{lua-Dl4zPu0P.js → lua-BtS2idK7.js} +1 -1
- package/dist/web/assets/markdown-renderer-CvL1pWeK.js +3 -0
- package/dist/web/assets/markdown-renderer-sJNNVhlS.js +1 -0
- package/dist/web/assets/{marko-BEnkIoHY.js → marko-Wk6LBnck.js} +1 -1
- package/dist/web/assets/{mdc-DGglct19.js → mdc-cRaXBrzL.js} +1 -1
- package/dist/web/assets/{media-url-LiPp2UsV.js → media-url-DL8OCGGg.js} +1 -1
- package/dist/web/assets/{mobile-bottom-sheet-BizNfPAu.js → mobile-bottom-sheet-CoUauvLF.js} +1 -1
- package/dist/web/assets/mobile-explorer-sheet-CZ0HZ2E_.js +1 -0
- package/dist/web/assets/monaco-dotenv-language-BDYnKcnr.js +1 -0
- package/dist/web/assets/monaspace-argon-latin-400-italic-DHf095HZ.woff2 +0 -0
- package/dist/web/assets/monaspace-argon-latin-400-italic-DZMiIHEH.woff +0 -0
- package/dist/web/assets/monaspace-argon-latin-400-normal-DZUN4ACH.woff +0 -0
- package/dist/web/assets/monaspace-argon-latin-400-normal-RS-2TGNg.woff2 +0 -0
- package/dist/web/assets/monaspace-argon-latin-700-normal-4y--rLW1.woff +0 -0
- package/dist/web/assets/monaspace-argon-latin-700-normal-D9kWRRK5.woff2 +0 -0
- package/dist/web/assets/monaspace-krypton-latin-400-italic-BoBCEexI.woff2 +0 -0
- package/dist/web/assets/monaspace-krypton-latin-400-italic-LWEicbvC.woff +0 -0
- package/dist/web/assets/monaspace-krypton-latin-400-normal-D9XlxC9R.woff +0 -0
- package/dist/web/assets/monaspace-krypton-latin-400-normal-DsuVXUab.woff2 +0 -0
- package/dist/web/assets/nerd-symbols-codicons-DSvMY_FK.woff2 +0 -0
- package/dist/web/assets/nerd-symbols-devicons-BBab8P54.woff2 +0 -0
- package/dist/web/assets/nerd-symbols-font-awesome-BzNW1pRA.woff2 +0 -0
- package/dist/web/assets/nerd-symbols-font-awesome-ext-D30e--UY.woff2 +0 -0
- package/dist/web/assets/nerd-symbols-font-awesome-legacy-D_qekXx9.woff2 +0 -0
- package/dist/web/assets/nerd-symbols-font-logos-gdu3INfy.woff2 +0 -0
- package/dist/web/assets/nerd-symbols-iec-power-Cw6TNeDf.woff2 +0 -0
- package/dist/web/assets/nerd-symbols-material-l-CXi9Ay.woff2 +0 -0
- package/dist/web/assets/nerd-symbols-misc-BaVfdyeB.woff2 +0 -0
- package/dist/web/assets/nerd-symbols-octicons-CJLTTzKm.woff2 +0 -0
- package/dist/web/assets/nerd-symbols-pomicons-D7T3Ya3H.woff2 +0 -0
- package/dist/web/assets/nerd-symbols-powerline-Cgg0sdGS.woff2 +0 -0
- package/dist/web/assets/nerd-symbols-seti-DYiUFq6z.woff2 +0 -0
- package/dist/web/assets/nerd-symbols-weather-D4Jrp5XD.woff2 +0 -0
- package/dist/web/assets/{nextflow-DKFYidyC.js → nextflow-Bos2wvDV.js} +1 -1
- package/dist/web/assets/{nginx-Bhq0tzlh.js → nginx-DMlOwzdr.js} +1 -1
- package/dist/web/assets/{nim-CSuBzart.js → nim-D2dZQA88.js} +1 -1
- package/dist/web/assets/notification-store-BZbsPnW0.js +1 -0
- package/dist/web/assets/notifications-settings-section-DPUROMw-.js +1 -0
- package/dist/web/assets/{number-overlay-editor-CYHlP43e.js → number-overlay-editor-DZ2i_Bnb.js} +1 -1
- package/dist/web/assets/{overview-panel-xn2BYvn6.js → overview-panel-R146OIom.js} +1 -1
- package/dist/web/assets/packet-RMMSAZCW-DwmYPOlx.js +1 -0
- package/dist/web/assets/panel-store-BVLBRn6M.js +1 -0
- package/dist/web/assets/pdf-preview-BWUPGLp6.js +1 -0
- package/dist/web/assets/{perl-uDVR7Ceh.js → perl-C5-Z6fPn.js} +1 -1
- package/dist/web/assets/{php-Bby8J3zk.js → php-BXNNhkMH.js} +1 -1
- package/dist/web/assets/pie-UPGHQEXC-D0oogIFi.js +1 -0
- package/dist/web/assets/{postgres-viewer-CJbHLcGE.js → postgres-viewer-CjOvaruz.js} +3 -3
- package/dist/web/assets/ppmbot-settings-section-DrvWh8l8.js +1 -0
- package/dist/web/assets/problems-panel-B7BWVhdy.js +1 -0
- package/dist/web/assets/process-table-BRMS1MrX.js +1 -0
- package/dist/web/assets/{project-store-Dy3A3LJT.js → project-store-BhMbVln3.js} +1 -1
- package/dist/web/assets/proxy-settings-section-Bv0Ny-Yy.js +1 -0
- package/dist/web/assets/{pug-EapfojTW.js → pug-C1zf1yWy.js} +1 -1
- package/dist/web/assets/{qml-DiR6J5_C.js → qml-OW58jGpD.js} +1 -1
- package/dist/web/assets/query-audit-section-BcHJZcVC.js +1 -0
- package/dist/web/assets/radar-KQ55EAFF-CQIJOQGa.js +1 -0
- package/dist/web/assets/{razor-B2QvlGNY.js → razor-DzjSaobM.js} +1 -1
- package/dist/web/assets/remote-desktop-mobile-sheet-ByjaeZKX.js +2 -0
- package/dist/web/assets/remote-desktop-mobile-view-D13mtr9F.js +1 -0
- package/dist/web/assets/remote-desktop-readiness-gate-BTzss2wX.js +1 -0
- package/dist/web/assets/remote-desktop-window-content-CbqTCaC6.js +1 -0
- package/dist/web/assets/{rst-D2eJ0kH4.js → rst-RfuihOUW.js} +1 -1
- package/dist/web/assets/{ruby-DzpnBpz-.js → ruby-CNO3pli1.js} +1 -1
- package/dist/web/assets/{run-in-terminal-DisO5HC-.js → run-in-terminal-Cb_hVU8x.js} +1 -1
- package/dist/web/assets/{sas-WDAKsX0U.js → sas-DZY5BtXx.js} +1 -1
- package/dist/web/assets/schedules-settings-section-DO8xAOVj.js +1 -0
- package/dist/web/assets/scroll-area-LtNV8g5o.js +1 -0
- package/dist/web/assets/{scss-Ci6PdscL.js → scss--4g-REcN.js} +1 -1
- package/dist/web/assets/{send-to-chat-DDFdKWNf.js → send-to-chat-Cnpru1M0.js} +1 -1
- package/dist/web/assets/{separator-CyisF27O.js → separator-Cds1yxy8.js} +1 -1
- package/dist/web/assets/settings-body-Rnu-FcX-.js +2 -0
- package/dist/web/assets/settings-store-DZPgPtEA.js +2 -0
- package/dist/web/assets/settings-tab-C6PHxs0Z.js +1 -0
- package/dist/web/assets/settings-window-content-CQ4gT2qj.js +1 -0
- package/dist/web/assets/{shaderlab-Cv3mWofL.js → shaderlab-Cu4sBiVn.js} +1 -1
- package/dist/web/assets/{shellsession-D1KVtQa1.js → shellsession-DWzJfEv7.js} +1 -1
- package/dist/web/assets/{soy-BGLBwFZT.js → soy-BnqE-5Ff.js} +1 -1
- package/dist/web/assets/{sparql-TvnQmFDh.js → sparql-Ctg__NhZ.js} +1 -1
- package/dist/web/assets/{sql-query-editor-DaQqRBNA.js → sql-query-editor-D9k19T_M.js} +1 -1
- package/dist/web/assets/sqlite-viewer-DoV9hnWJ.js +1 -0
- package/dist/web/assets/{stata-Gi-BKyEv.js → stata-Qk2h66XW.js} +1 -1
- package/dist/web/assets/{streaming-store-DAbnOfLu.js → streaming-store-uDu3PoN1.js} +1 -1
- package/dist/web/assets/{surrealql-BVXs6tkz.js → surrealql-8QEX7EfH.js} +1 -1
- package/dist/web/assets/{svelte-BlnFpLWV.js → svelte-D_mubC5-.js} +1 -1
- package/dist/web/assets/system-monitor-body-2vQ1kMxE.js +2 -0
- package/dist/web/assets/system-monitor-tab-krAcvwF7.js +1 -0
- package/dist/web/assets/system-monitor-window-content-qXLcicFM.js +1 -0
- package/dist/web/assets/tab-host-window-content-7fPZNEni.js +1 -0
- package/dist/web/assets/{tab-store-B7BksLr-.js → tab-store-DGJGnbwv.js} +1 -1
- package/dist/web/assets/team-member-sheet-2FnRmk1a.js +2 -0
- package/dist/web/assets/team-member-window-content-BgvJFypM.js +1 -0
- package/dist/web/assets/{templ-YJYH5r1A.js → templ-C1jV0fsO.js} +1 -1
- package/dist/web/assets/terminal-tab-Cs0sGljx.js +3 -0
- package/dist/web/assets/{tex-IVZLDIeh.js → tex-D_StFXy_.js} +1 -1
- package/dist/web/assets/tool-cards-DU_S2o51.js +9 -0
- package/dist/web/assets/treemap-KZPCXAKY-BhEtjljr.js +1 -0
- package/dist/web/assets/{ts-tags-DBd321I-.js → ts-tags-B5m47rEm.js} +1 -1
- package/dist/web/assets/{twig-DQENQjEn.js → twig-C_br6GIl.js} +1 -1
- package/dist/web/assets/{use-blob-url-CGChX4vi.js → use-blob-url-C-SGi_Hb.js} +1 -1
- package/dist/web/assets/use-inline-blame-B4trxGcR.js +16 -0
- package/dist/web/assets/use-lsp-CV0vzILO.js +6 -0
- package/dist/web/assets/use-monaco-theme-DBqbWjWX.js +1 -0
- package/dist/web/assets/{use-open-team-member-kmNnlzDD.js → use-open-team-member-DENsjp0s.js} +1 -1
- package/dist/web/assets/use-remote-desktop-display-choice-BK_f787P.js +1 -0
- package/dist/web/assets/{use-websocket-D8OGBwH8.js → use-websocket-LgXiX1qN.js} +1 -1
- package/dist/web/assets/{vendor-mermaid-BoRFiriK.js → vendor-mermaid-Cbig5zA6.js} +1 -1
- package/dist/web/assets/video-preview-BDNwumbW.js +1 -0
- package/dist/web/assets/{vue-Do2JXEwi.js → vue-DQwCnLdr.js} +1 -1
- package/dist/web/assets/{vue-html-Bse0CwSC.js → vue-html-T_EoGpPJ.js} +1 -1
- package/dist/web/assets/{vue-vine-C8JSXu5Z.js → vue-vine-DmxLqX50.js} +1 -1
- package/dist/web/assets/{window-pip-registry-C2dcY0pE.js → window-pip-registry-3l4hoxuD.js} +1 -1
- package/dist/web/assets/{window-store-2z4t5YAt.js → window-store-UC1jJeLM.js} +1 -1
- package/dist/web/assets/ws-auth-DEAJiGCR.js +1 -0
- package/dist/web/assets/{xml-OX6YmzGK.js → xml-Bs6Bw8is.js} +1 -1
- package/dist/web/assets/{xsl-RQUQgcgy.js → xsl-f5ZNhRCc.js} +1 -1
- package/dist/web/index.html +105 -71
- package/dist/web/sw.js +1 -1
- package/docs/project-roadmap.md +2 -0
- package/licenses/Apache-2.0.txt +73 -0
- package/licenses/CC-BY-4.0.txt +156 -0
- package/licenses/MIT.txt +18 -0
- package/licenses/OFL-1.1.txt +43 -0
- package/licenses/Unlicense.txt +10 -0
- package/package.json +14 -7
- package/packages/ext-git-graph/src/blame-view.ts +4 -5
- package/packages/ext-git-graph/src/commit-detail-escaping.test.ts +205 -0
- package/packages/ext-git-graph/src/commit-detail-file-cap.test.ts +65 -0
- package/packages/ext-git-graph/src/commit-message-html.test.ts +214 -0
- package/packages/ext-git-graph/src/commit-message-html.ts +190 -0
- package/packages/ext-git-graph/src/commit-request-generation.test.ts +57 -0
- package/packages/ext-git-graph/src/compare-view.ts +4 -5
- package/packages/ext-git-graph/src/extension.ts +148 -36
- package/packages/ext-git-graph/src/file-history-view.ts +4 -5
- package/packages/ext-git-graph/src/log-args.test.ts +57 -0
- package/packages/ext-git-graph/src/ppm-api.ts +39 -6
- package/packages/ext-git-graph/src/project-scope.test.ts +77 -0
- package/packages/ext-git-graph/src/project-scope.ts +69 -0
- package/packages/ext-git-graph/src/shortstat-parser.test.ts +53 -0
- package/packages/ext-git-graph/src/shortstat-parser.ts +45 -0
- package/packages/ext-git-graph/src/types.ts +2 -0
- package/packages/ext-git-graph/src/webview-html.test.ts +407 -0
- package/packages/ext-git-graph/src/webview-html.ts +631 -108
- package/packages/ext-git-graph/src/webview-shell.test.ts +63 -0
- package/packages/ext-git-graph/src/webview-shell.ts +48 -7
- package/scripts/copy-monaco.ts +63 -0
- package/scripts/gen-file-icons.ts +485 -0
- package/scripts/gen-nerd-font.ts +311 -0
- package/scripts/gen-product-icons.ts +442 -0
- package/scripts/monaco-staging.ts +32 -0
- package/scripts/precompress-web.ts +77 -0
- package/scripts/third-party-notices.ts +160 -0
- package/scripts/vite-monaco-dev-assets.ts +63 -0
- package/src/providers/claude-agent-sdk.ts +32 -2
- package/src/server/index.ts +17 -0
- package/src/server/routes/chat.ts +16 -6
- package/src/server/routes/git.ts +113 -1
- package/src/server/routes/lsp.ts +58 -0
- package/src/server/routes/project-scoped.ts +2 -0
- package/src/server/routes/static-encoding.ts +73 -0
- package/src/server/routes/static-fallback.ts +51 -0
- package/src/server/routes/static.ts +41 -5
- package/src/server/ws/lsp.ts +433 -0
- package/src/services/chat-search.service.ts +211 -26
- package/src/services/fs-ops/fs-real-path.ts +23 -0
- package/src/services/git-blame/commit-line-diff.ts +124 -0
- package/src/services/git-blame/git-blame.service.ts +96 -1
- package/src/services/git-repos/git-repo-discovery.ts +152 -0
- package/src/services/lsp/lsp-capabilities.ts +160 -0
- package/src/services/lsp/lsp-manager.ts +442 -0
- package/src/services/lsp/lsp-protocol.ts +170 -0
- package/src/services/lsp/lsp-session.ts +466 -0
- package/src/services/lsp/server-registry.ts +401 -0
- package/src/services/search-index-db.service.ts +10 -2
- package/src/shared/blame.ts +51 -0
- package/src/shared/lsp-uri.ts +108 -0
- package/src/web/app.tsx +6 -0
- package/src/web/components/ai-resources/ai-resource-editor.tsx +1 -1
- package/src/web/components/ai-resources/ai-resources-panel.tsx +1 -1
- package/src/web/components/ai-resources/mcp-group.tsx +1 -1
- package/src/web/components/ai-resources/new-resource-menu.tsx +1 -1
- package/src/web/components/ai-resources/resource-visuals.tsx +1 -1
- package/src/web/components/auth/login-screen.tsx +1 -1
- package/src/web/components/auth/support-dialog.tsx +1 -1
- package/src/web/components/chat/attachment-chips.tsx +113 -5
- package/src/web/components/chat/background-command-bar.tsx +1 -1
- package/src/web/components/chat/background-output-panel.tsx +1 -1
- package/src/web/components/chat/change-edit-list.tsx +1 -1
- package/src/web/components/chat/change-file-row.tsx +1 -1
- package/src/web/components/chat/chat-history-bar.tsx +2 -2
- package/src/web/components/chat/chat-history-panel.tsx +1 -1
- package/src/web/components/chat/chat-placeholder.tsx +1 -1
- package/src/web/components/chat/chat-scroll-nav.tsx +1 -1
- package/src/web/components/chat/chat-tab.tsx +1 -1
- package/src/web/components/chat/chat-toolbar-overflow-menu.tsx +1 -1
- package/src/web/components/chat/chat-welcome.tsx +1 -1
- package/src/web/components/chat/codex-usage-panel.tsx +1 -1
- package/src/web/components/chat/file-picker.tsx +1 -1
- package/src/web/components/chat/image-preview-states.tsx +1 -1
- package/src/web/components/chat/message-action-bar.tsx +1 -1
- package/src/web/components/chat/message-input.tsx +1 -1
- package/src/web/components/chat/message-list.tsx +1 -1
- package/src/web/components/chat/mode-selector.tsx +1 -1
- package/src/web/components/chat/model-selector.tsx +1 -1
- package/src/web/components/chat/model-thinking-selector.tsx +1 -1
- package/src/web/components/chat/pre-compact-button.tsx +1 -1
- package/src/web/components/chat/provider-selector.tsx +1 -1
- package/src/web/components/chat/send-message-card.tsx +1 -1
- package/src/web/components/chat/session-context-menu.tsx +1 -1
- package/src/web/components/chat/session-debug-dialog.tsx +1 -1
- package/src/web/components/chat/session-history-list.tsx +1 -1
- package/src/web/components/chat/session-list-panel.tsx +1 -1
- package/src/web/components/chat/session-picker.tsx +1 -1
- package/src/web/components/chat/slash-command-picker.tsx +1 -1
- package/src/web/components/chat/task-tracker.tsx +1 -1
- package/src/web/components/chat/team-activity-panel.tsx +1 -1
- package/src/web/components/chat/team-member-activity-row.tsx +1 -1
- package/src/web/components/chat/team-member-list.tsx +1 -1
- package/src/web/components/chat/team-member-sheet.tsx +1 -1
- package/src/web/components/chat/team-member-window-content.tsx +1 -1
- package/src/web/components/chat/team-working-bar.tsx +1 -1
- package/src/web/components/chat/tool-cards.tsx +2 -2
- package/src/web/components/chat/turn-change-sheet.tsx +1 -1
- package/src/web/components/chat/turn-change-tray.tsx +1 -1
- package/src/web/components/chat/turn-cost-warning.tsx +1 -1
- package/src/web/components/chat/usage-badge.tsx +1 -1
- package/src/web/components/chat/usage-panel-shell.tsx +1 -1
- package/src/web/components/chat/version-switcher.tsx +1 -1
- package/src/web/components/database/connection-import-export.tsx +1 -1
- package/src/web/components/database/connection-list.tsx +1 -1
- package/src/web/components/database/connection-row.tsx +1 -1
- package/src/web/components/database/database-sidebar.tsx +1 -1
- package/src/web/components/database/database-viewer.tsx +1 -1
- package/src/web/components/database/export-button.tsx +1 -1
- package/src/web/components/database/glide-context-menu.tsx +1 -1
- package/src/web/components/database/glide-data-grid.tsx +1 -1
- package/src/web/components/database/glide-data-preview-panel.tsx +2 -2
- package/src/web/components/database/glide-grid-pagination.tsx +1 -1
- package/src/web/components/database/glide-grid-toolbar.tsx +1 -1
- package/src/web/components/database/glide-header-menu.tsx +1 -1
- package/src/web/components/database/glide-save-bar.tsx +1 -1
- package/src/web/components/database/schema-table-tree.tsx +1 -1
- package/src/web/components/editor/audio-preview.tsx +1 -1
- package/src/web/components/editor/code-editor.tsx +167 -21
- package/src/web/components/editor/compare-picker.tsx +1 -1
- package/src/web/components/editor/conflict-editor.tsx +15 -6
- package/src/web/components/editor/csv-preview.tsx +1 -1
- package/src/web/components/editor/diff-viewer.tsx +59 -37
- package/src/web/components/editor/docx-preview.tsx +1 -1
- package/src/web/components/editor/editor-breadcrumb-external.tsx +1 -1
- package/src/web/components/editor/editor-breadcrumb.tsx +1 -1
- package/src/web/components/editor/editor-language-picker.tsx +3 -1
- package/src/web/components/editor/editor-language-service.tsx +77 -0
- package/src/web/components/editor/editor-mobile-toolbar.tsx +13 -2
- package/src/web/components/editor/editor-placeholder.tsx +1 -1
- package/src/web/components/editor/editor-toolbar.tsx +1 -1
- package/src/web/components/editor/image-preview.tsx +1 -1
- package/src/web/components/editor/lsp-status.tsx +241 -0
- package/src/web/components/editor/pdf-preview.tsx +1 -1
- package/src/web/components/editor/video-player/video-player-controls.tsx +1 -1
- package/src/web/components/editor/video-player/video-player.tsx +1 -1
- package/src/web/components/editor/video-player/video-settings-menu.tsx +1 -1
- package/src/web/components/editor/video-preview.tsx +1 -1
- package/src/web/components/explorer/file-tree.tsx +1 -1
- package/src/web/components/explorer/inline-tree-input.tsx +1 -1
- package/src/web/components/explorer/search-panel.tsx +3 -2
- package/src/web/components/explorer/tree-node-context-menu.tsx +1 -1
- package/src/web/components/explorer/tree-node.tsx +5 -12
- package/src/web/components/extensions/extension-quickpick.tsx +1 -1
- package/src/web/components/extensions/extension-tree-view.tsx +1 -1
- package/src/web/components/extensions/extension-webview.tsx +53 -20
- package/src/web/components/extensions/webview-theme.ts +121 -0
- package/src/web/components/floating-window/pip/pip-caption-button.tsx +1 -1
- package/src/web/components/git/create-worktree-dialog.tsx +5 -3
- package/src/web/components/git/git-log-panel.tsx +4 -2
- package/src/web/components/git/git-placeholder.tsx +1 -1
- package/src/web/components/git/git-ref-badge.tsx +1 -1
- package/src/web/components/git/git-repo-picker.tsx +145 -0
- package/src/web/components/git/git-status-panel.tsx +231 -250
- package/src/web/components/git/git-worktree-panel.tsx +6 -4
- package/src/web/components/git/hunk-stage-dialog.tsx +5 -3
- package/src/web/components/group-chat/group-chat-tab.tsx +1 -1
- package/src/web/components/group-chat/group-create-dialog.tsx +1 -1
- package/src/web/components/group-chat/group-full-transcript-view.tsx +1 -1
- package/src/web/components/group-chat/group-list.tsx +1 -1
- package/src/web/components/group-chat/group-member-edit-dialog.tsx +1 -1
- package/src/web/components/group-chat/group-member-roster.tsx +1 -1
- package/src/web/components/group-chat/group-message-item.tsx +1 -1
- package/src/web/components/group-chat/group-transcript-messages.tsx +1 -1
- package/src/web/components/jira/jira-config-form.tsx +1 -1
- package/src/web/components/jira/jira-filter-builder.tsx +1 -1
- package/src/web/components/jira/jira-panel.tsx +1 -1
- package/src/web/components/jira/jira-results-panel.tsx +1 -1
- package/src/web/components/jira/jira-ticket-card.tsx +1 -1
- package/src/web/components/jira/jira-ticket-detail.tsx +1 -1
- package/src/web/components/jira/jira-watcher-form.tsx +1 -1
- package/src/web/components/jira/jira-watcher-list.tsx +1 -1
- package/src/web/components/layout/add-project-form.tsx +1 -1
- package/src/web/components/layout/cloud-alias-row.tsx +1 -1
- package/src/web/components/layout/cloud-share-named-tunnel-row.tsx +1 -1
- package/src/web/components/layout/cloud-share-popover.tsx +1 -1
- package/src/web/components/layout/command-palette-filter-chips.tsx +1 -1
- package/src/web/components/layout/command-palette.tsx +46 -8
- package/src/web/components/layout/dock-header.tsx +4 -4
- package/src/web/components/layout/dock-panel.tsx +3 -3
- package/src/web/components/layout/draggable-tab.tsx +20 -3
- package/src/web/components/layout/editor-panel.tsx +1 -1
- package/src/web/components/layout/mobile-drawer-tab-bar.tsx +10 -1
- package/src/web/components/layout/mobile-drawer.tsx +1 -1
- package/src/web/components/layout/mobile-nav.tsx +3 -3
- package/src/web/components/layout/mobile-tab-switcher-sheet.tsx +13 -4
- package/src/web/components/layout/nav-section-rail.tsx +1 -1
- package/src/web/components/layout/notification-bell-popover.tsx +1 -1
- package/src/web/components/layout/panel-layout.tsx +1 -1
- package/src/web/components/layout/project-bottom-sheet.tsx +4 -1
- package/src/web/components/layout/project-sort.ts +1 -1
- package/src/web/components/layout/project-switcher.tsx +1 -1
- package/src/web/components/layout/reparenting-tab.tsx +1 -1
- package/src/web/components/layout/sidebar.tsx +1 -1
- package/src/web/components/layout/status-bar.tsx +4 -1
- package/src/web/components/layout/tab-bar.tsx +9 -23
- package/src/web/components/layout/tab-content.tsx +6 -1
- package/src/web/components/layout/tab-pool.tsx +1 -0
- package/src/web/components/layout/tab-pop-out-menu-item.tsx +1 -1
- package/src/web/components/layout/upgrade-button.tsx +1 -1
- package/src/web/components/layout/wake-lock-indicator.tsx +1 -1
- package/src/web/components/os-explorer/explorer-body.tsx +1 -1
- package/src/web/components/os-explorer/explorer-breadcrumb.tsx +1 -1
- package/src/web/components/os-explorer/explorer-sidebar.tsx +1 -1
- package/src/web/components/os-explorer/explorer-sort-menu.tsx +1 -1
- package/src/web/components/os-explorer/explorer-toolbar.tsx +1 -1
- package/src/web/components/os-explorer/icons/file-type-icon-map.ts +10 -94
- package/src/web/components/os-explorer/icons/file-type-icon.tsx +17 -13
- package/src/web/components/os-explorer/mobile/mobile-bottom-toolbar.tsx +1 -1
- package/src/web/components/os-explorer/mobile/mobile-explorer-top-bar.tsx +1 -1
- package/src/web/components/os-explorer/mobile/mobile-more-sheet.tsx +1 -1
- package/src/web/components/os-explorer/mobile/mobile-places-sheet.tsx +1 -1
- package/src/web/components/os-explorer/mobile/mobile-places-strip.tsx +1 -1
- package/src/web/components/os-explorer/properties-dialog.tsx +1 -1
- package/src/web/components/os-explorer/skins/macos-window-chrome.tsx +1 -1
- package/src/web/components/os-explorer/skins/windows-window-chrome.tsx +1 -1
- package/src/web/components/os-explorer/upload/upload-batch-card.tsx +1 -1
- package/src/web/components/os-explorer/upload/upload-batch-row.tsx +1 -1
- package/src/web/components/os-explorer/use-coarse-long-press.ts +8 -0
- package/src/web/components/os-explorer/views/column-view-mobile.tsx +1 -1
- package/src/web/components/os-explorer/views/icons-view-tile.tsx +1 -1
- package/src/web/components/os-explorer/views/list-row.tsx +1 -1
- package/src/web/components/os-explorer/views/list-view.tsx +1 -1
- package/src/web/components/postgres/postgres-viewer.tsx +1 -1
- package/src/web/components/problems/problems-panel.tsx +230 -0
- package/src/web/components/problems/problems-status.tsx +53 -0
- package/src/web/components/projects/dir-suggest.tsx +1 -1
- package/src/web/components/projects/project-list.tsx +1 -1
- package/src/web/components/remote-desktop/remote-desktop-mobile-toolbar.tsx +1 -1
- package/src/web/components/remote-desktop/remote-desktop-mobile-view.tsx +1 -1
- package/src/web/components/remote-desktop/remote-desktop-readiness-gate.tsx +1 -1
- package/src/web/components/remote-desktop/remote-desktop-warning-gate.tsx +1 -1
- package/src/web/components/remote-desktop/remote-desktop-window-content.tsx +1 -1
- package/src/web/components/root-error-boundary.tsx +116 -0
- package/src/web/components/settings/accounts/account-add-dialog.tsx +1 -1
- package/src/web/components/settings/accounts/account-card.tsx +1 -1
- package/src/web/components/settings/accounts/account-export-dialog.tsx +1 -1
- package/src/web/components/settings/accounts/account-export-simulation.tsx +1 -1
- package/src/web/components/settings/accounts/account-import-dialog.tsx +1 -1
- package/src/web/components/settings/accounts/account-profile-panel.tsx +1 -1
- package/src/web/components/settings/accounts/account-rotation-settings.tsx +1 -1
- package/src/web/components/settings/accounts/account-token-test-dialog.tsx +1 -1
- package/src/web/components/settings/accounts/account-usage-pattern-chart.tsx +1 -1
- package/src/web/components/settings/accounts/accounts-pane-header.tsx +1 -1
- package/src/web/components/settings/accounts/accounts-settings-section.tsx +1 -1
- package/src/web/components/settings/accounts/claude-accounts-section.tsx +9 -1
- package/src/web/components/settings/accounts/codex-accounts-section.tsx +10 -1
- package/src/web/components/settings/accounts/codex-add-account-dialog.tsx +1 -1
- package/src/web/components/settings/accounts/codex-backup-dialog.tsx +1 -1
- package/src/web/components/settings/ai-settings-section.tsx +1 -1
- package/src/web/components/settings/appearance-settings-section.tsx +56 -1
- package/src/web/components/settings/change-password-section.tsx +1 -1
- package/src/web/components/settings/extension-manager-section.tsx +1 -1
- package/src/web/components/settings/general-settings-section.tsx +1 -1
- package/src/web/components/settings/glob-list-editor.tsx +1 -1
- package/src/web/components/settings/keyboard-shortcuts-section.tsx +1 -1
- package/src/web/components/settings/mcp-server-dialog.tsx +1 -1
- package/src/web/components/settings/notifications-settings-section.tsx +1 -1
- package/src/web/components/settings/ppmbot-settings-section.tsx +1 -1
- package/src/web/components/settings/proxy-settings-section.tsx +1 -1
- package/src/web/components/settings/proxy-test-section.tsx +1 -1
- package/src/web/components/settings/query-audit-section.tsx +1 -1
- package/src/web/components/settings/schedules/schedule-row.tsx +1 -1
- package/src/web/components/settings/schedules/schedules-settings-section.tsx +1 -1
- package/src/web/components/settings/settings-body.tsx +1 -1
- package/src/web/components/settings/settings-categories.ts +14 -3
- package/src/web/components/settings/settings-category-list.tsx +1 -1
- package/src/web/components/settings/settings-section-content.tsx +1 -1
- package/src/web/components/settings/tag-settings-section.tsx +1 -1
- package/src/web/components/settings/theme-grid.tsx +1 -1
- package/src/web/components/settings/theme-manager-section.tsx +1 -1
- package/src/web/components/settings/theme-picker.tsx +1 -1
- package/src/web/components/shared/bug-report-popup.tsx +1 -1
- package/src/web/components/shared/connection-lost-overlay.tsx +1 -1
- package/src/web/components/shared/diagram-overlay.tsx +1 -1
- package/src/web/components/shared/image-overlay.tsx +1 -1
- package/src/web/components/shared/theme-mode-menu.tsx +1 -1
- package/src/web/components/sqlite/sqlite-data-grid.tsx +1 -1
- package/src/web/components/sqlite/sqlite-table-list.tsx +1 -1
- package/src/web/components/sqlite/sqlite-viewer.tsx +1 -1
- package/src/web/components/system/process-group-row.tsx +1 -1
- package/src/web/components/system/process-row.tsx +1 -1
- package/src/web/components/system/process-table-toolbar.tsx +1 -1
- package/src/web/components/system/resource-status-bar.tsx +1 -1
- package/src/web/components/system/sortable-header.tsx +1 -1
- package/src/web/components/system/system-monitor-body.tsx +1 -1
- package/src/web/components/terminal/terminal-finish-login.tsx +1 -1
- package/src/web/components/terminal/terminal-links-sheet.tsx +1 -1
- package/src/web/components/terminal/terminal-mobile-toolbar.tsx +1 -1
- package/src/web/components/terminal/terminal-placeholder.tsx +1 -1
- package/src/web/components/terminal/terminal-tab.tsx +1 -1
- package/src/web/components/tunnels/named-tunnel/named-tunnel-copy.ts +1 -0
- package/src/web/components/tunnels/named-tunnel/named-tunnel-login-step.tsx +1 -1
- package/src/web/components/tunnels/named-tunnel/named-tunnel-section.tsx +1 -1
- package/src/web/components/tunnels/named-tunnel/named-tunnel-setup-content.tsx +9 -3
- package/src/web/components/tunnels/named-tunnel/named-tunnel-setup-popup.tsx +1 -1
- package/src/web/components/tunnels/named-tunnel/named-tunnel-step-reducer.ts +14 -2
- package/src/web/components/tunnels/tunnel-manager-tab.tsx +1 -1
- package/src/web/components/ui/accordion.tsx +1 -1
- package/src/web/components/ui/adaptive-context-menu.tsx +9 -2
- package/src/web/components/ui/browse-button.tsx +1 -1
- package/src/web/components/ui/context-menu.tsx +1 -1
- package/src/web/components/ui/dialog.tsx +1 -1
- package/src/web/components/ui/dropdown-menu.tsx +1 -1
- package/src/web/components/ui/file-browser-picker.tsx +4 -6
- package/src/web/components/ui/select.tsx +1 -1
- package/src/web/components/ui/sidebar-header.tsx +1 -1
- package/src/web/components/ui/sonner.tsx +1 -1
- package/src/web/hooks/use-git-repo.ts +124 -0
- package/src/web/hooks/use-global-keybindings.ts +11 -6
- package/src/web/hooks/use-inline-blame.ts +161 -11
- package/src/web/hooks/use-is-touch-only.ts +46 -0
- package/src/web/hooks/use-lsp.ts +182 -0
- package/src/web/hooks/use-terminal.ts +60 -2
- package/src/web/hooks/use-touch-tab-drag.ts +18 -2
- package/src/web/hooks/use-url-sync.ts +15 -1
- package/src/web/index.html +74 -0
- package/src/web/lib/blame-hover-commands.ts +82 -0
- package/src/web/lib/blame-hover.ts +199 -0
- package/src/web/lib/chunk-recovery.ts +113 -0
- package/src/web/lib/editor-font.ts +70 -0
- package/src/web/lib/ext-command-dispatch.ts +24 -0
- package/src/web/lib/file-icons.generated.ts +408 -0
- package/src/web/lib/file-icons.tsx +192 -0
- package/src/web/lib/fluent-icon.tsx +62 -0
- package/src/web/lib/git-file-tree.ts +83 -0
- package/src/web/lib/git-repo-scope.ts +157 -0
- package/src/web/lib/icons.generated.tsx +428 -0
- package/src/web/lib/icons.ts +17 -0
- package/src/web/lib/lsp/lsp-client.ts +321 -0
- package/src/web/lib/lsp/lsp-documents.ts +39 -0
- package/src/web/lib/lsp/lsp-monaco.ts +214 -0
- package/src/web/lib/lsp/lsp-navigation.ts +129 -0
- package/src/web/lib/lsp/lsp-responses.ts +244 -0
- package/src/web/lib/lsp/lsp-semantic-tokens.ts +180 -0
- package/src/web/lib/lsp/lsp-shadow-models.ts +254 -0
- package/src/web/lib/lsp/monaco-builtin-typescript.ts +149 -0
- package/src/web/lib/lsp/register-providers.ts +458 -0
- package/src/web/lib/monaco-diff-language.ts +62 -0
- package/src/web/lib/monaco-dotenv-language.ts +134 -0
- package/src/web/lib/sidebar-tabs/tab-registry.ts +11 -2
- package/src/web/lib/tab-type-icons.ts +32 -4
- package/src/web/main.tsx +30 -1
- package/src/web/stores/git-repo-store.ts +117 -0
- package/src/web/stores/git-status-store.ts +10 -7
- package/src/web/stores/keybindings-store.ts +1 -0
- package/src/web/stores/panel-utils.ts +5 -2
- package/src/web/stores/problems-store.ts +116 -0
- package/src/web/stores/settings-store.ts +40 -0
- package/src/web/stores/tab-store.ts +2 -1
- package/src/web/stores/window-panel-persistence.ts +1 -0
- package/src/web/styles/file-icons.generated.css +1007 -0
- package/src/web/styles/fonts/nerd-symbols-codicons.woff2 +0 -0
- package/src/web/styles/fonts/nerd-symbols-devicons.woff2 +0 -0
- package/src/web/styles/fonts/nerd-symbols-font-awesome-ext.woff2 +0 -0
- package/src/web/styles/fonts/nerd-symbols-font-awesome-legacy.woff2 +0 -0
- package/src/web/styles/fonts/nerd-symbols-font-awesome.woff2 +0 -0
- package/src/web/styles/fonts/nerd-symbols-font-logos.woff2 +0 -0
- package/src/web/styles/fonts/nerd-symbols-iec-power.woff2 +0 -0
- package/src/web/styles/fonts/nerd-symbols-material.woff2 +0 -0
- package/src/web/styles/fonts/nerd-symbols-misc.woff2 +0 -0
- package/src/web/styles/fonts/nerd-symbols-octicons.woff2 +0 -0
- package/src/web/styles/fonts/nerd-symbols-pomicons.woff2 +0 -0
- package/src/web/styles/fonts/nerd-symbols-powerline.woff2 +0 -0
- package/src/web/styles/fonts/nerd-symbols-seti.woff2 +0 -0
- package/src/web/styles/fonts/nerd-symbols-weather.woff2 +0 -0
- package/src/web/styles/globals.css +30 -3
- package/src/web/styles/nerd-font.generated.css +153 -0
- package/src/web/sw.ts +69 -2
- package/src/web/theme/adapters/monaco-adapter.ts +20 -1
- package/src/web/theme/diff-token-rules.ts +34 -0
- package/src/web/theme/semantic-token-rules.ts +103 -0
- package/src/web/theme/theme-mode-options.ts +1 -1
- package/vite.config.ts +24 -7
- package/dist/web/assets/accounts-settings-section-ByM99C7p.js +0 -1
- package/dist/web/assets/ai-resource-editor-BsB9wa0z.js +0 -1
- package/dist/web/assets/ai-settings-section-BT-QVUBa.js +0 -1
- package/dist/web/assets/ai-settings-section-BUPOINaw.js +0 -1
- package/dist/web/assets/api-client-C6VTn7cG.js +0 -1
- package/dist/web/assets/appearance-settings-section-DJEoECoJ.js +0 -1
- package/dist/web/assets/architecture-PBZL5I3N-D55g5j-p.js +0 -1
- package/dist/web/assets/arrow-left-DpXaakMC.js +0 -1
- package/dist/web/assets/arrow-right-BqNSIscF.js +0 -1
- package/dist/web/assets/audio-preview-CF4tTFv_.js +0 -1
- package/dist/web/assets/bot-D3-n2i31.js +0 -1
- package/dist/web/assets/brain-CuAQMSBE.js +0 -1
- package/dist/web/assets/calendar-clock-CDP7AVjA.js +0 -1
- package/dist/web/assets/chat-tab-fD2vWwj2.js +0 -15
- package/dist/web/assets/check-BQKbhtoA.js +0 -1
- package/dist/web/assets/chevron-down-B9puixxZ.js +0 -1
- package/dist/web/assets/chevron-right-DK_HbYZC.js +0 -1
- package/dist/web/assets/chevron-up-BPAINgoK.js +0 -1
- package/dist/web/assets/circle-BpGTLAas.js +0 -1
- package/dist/web/assets/circle-question-mark-DUaySkoB.js +0 -1
- package/dist/web/assets/clock-Dqq0H9yH.js +0 -1
- package/dist/web/assets/code-CX6640Tv.js +0 -1
- package/dist/web/assets/code-editor-CyFLXCsD.js +0 -7
- package/dist/web/assets/codex-daily-guard-Cg19EWxk.js +0 -1
- package/dist/web/assets/conflict-editor-DSjo2FRy.js +0 -19
- package/dist/web/assets/context-menu-DchAkdnL.js +0 -1
- package/dist/web/assets/copy-Bt_1tjoJ.js +0 -1
- package/dist/web/assets/csv-preview-BIE3Wpww.js +0 -2
- package/dist/web/assets/database-9_p74TtY.js +0 -1
- package/dist/web/assets/database-viewer-CMvIORcM.js +0 -1
- package/dist/web/assets/diff-viewer-BQQ6fUr6.js +0 -4
- package/dist/web/assets/docx-preview-DcCh59Oe.js +0 -12
- package/dist/web/assets/download-CEPFebri.js +0 -1
- package/dist/web/assets/explorer-body-DRo2JHP9.js +0 -2
- package/dist/web/assets/explorer-window-content-7mnRpSqn.js +0 -1
- package/dist/web/assets/extension-manager-section-CEfr7-Kv.js +0 -1
- package/dist/web/assets/extension-webview-D7HqqdTv.js +0 -3
- package/dist/web/assets/eye-BQaWPI4P.js +0 -1
- package/dist/web/assets/eye-off-BlB3QV90.js +0 -1
- package/dist/web/assets/file-exclamation-point-BDmSZbxs.js +0 -1
- package/dist/web/assets/files-settings-section-h6a-WpSO.js +0 -1
- package/dist/web/assets/flask-conical-Cj-J6cxs.js +0 -1
- package/dist/web/assets/folder-symlink-BSYalAHC.js +0 -1
- package/dist/web/assets/general-settings-section-Bt3KNcyM.js +0 -1
- package/dist/web/assets/git-log-panel-H-Bg9p7m.js +0 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-At-P8hlA.js +0 -1
- package/dist/web/assets/glide-data-grid-D3Xc_WGh.js +0 -141
- package/dist/web/assets/group-chat-tab-BkLt7J_s.js +0 -1
- package/dist/web/assets/hand-BkDyLfcc.js +0 -1
- package/dist/web/assets/image-preview-D_6-FhZP.js +0 -1
- package/dist/web/assets/index-BV_sFdHY.js +0 -52
- package/dist/web/assets/index-Di9nVG3y.css +0 -2
- package/dist/web/assets/info-3K5VOQVL-btSsuixb.js +0 -1
- package/dist/web/assets/key-round-Cv4z7_KG.js +0 -1
- package/dist/web/assets/keybindings-store-alreySno.js +0 -1
- package/dist/web/assets/keyboard-HG0BUtuN.js +0 -1
- package/dist/web/assets/keyboard-shortcuts-section-B_D0FamP.js +0 -1
- package/dist/web/assets/loader-circle-H81ywCuQ.js +0 -1
- package/dist/web/assets/markdown-renderer-CG_nGh7k.js +0 -3
- package/dist/web/assets/markdown-renderer-CgbqvSuc.js +0 -1
- package/dist/web/assets/mobile-explorer-sheet-DAZTzi1k.js +0 -1
- package/dist/web/assets/monitor-CKcYUoAV.js +0 -1
- package/dist/web/assets/notification-store-HCg5Shw9.js +0 -1
- package/dist/web/assets/notifications-settings-section-BAs_p_ai.js +0 -1
- package/dist/web/assets/packet-RMMSAZCW-B20_sIFd.js +0 -1
- package/dist/web/assets/panel-store-XhAruR6D.js +0 -1
- package/dist/web/assets/pdf-preview-BJjqU3AX.js +0 -1
- package/dist/web/assets/pie-UPGHQEXC-nzfJdXCC.js +0 -1
- package/dist/web/assets/power-D_wlLkC0.js +0 -1
- package/dist/web/assets/ppmbot-settings-section-5U0a58oj.js +0 -1
- package/dist/web/assets/process-table-CqGL0Bcj.js +0 -1
- package/dist/web/assets/proxy-settings-section-B1LDpGSc.js +0 -1
- package/dist/web/assets/query-audit-section-DfCvY94_.js +0 -1
- package/dist/web/assets/radar-KQ55EAFF-CS4sHKD5.js +0 -1
- package/dist/web/assets/remote-desktop-mobile-sheet-_LH4uCcA.js +0 -2
- package/dist/web/assets/remote-desktop-mobile-view-C5TUPDh_.js +0 -1
- package/dist/web/assets/remote-desktop-readiness-gate-DuT4A893.js +0 -1
- package/dist/web/assets/remote-desktop-window-content-B4V-1aJn.js +0 -1
- package/dist/web/assets/save-BehCuMf4.js +0 -1
- package/dist/web/assets/schedules-settings-section-C2SjwWFB.js +0 -1
- package/dist/web/assets/send-CtXcGcsk.js +0 -1
- package/dist/web/assets/settings-body-CdFauCAU.js +0 -2
- package/dist/web/assets/settings-store-BkNrLdjk.js +0 -2
- package/dist/web/assets/settings-tab-DGUlmMbc.js +0 -1
- package/dist/web/assets/settings-window-content-CIH5sihe.js +0 -1
- package/dist/web/assets/shield-check-z9PX8HVZ.js +0 -1
- package/dist/web/assets/shield-off-Du0U5dVI.js +0 -1
- package/dist/web/assets/sparkles-CO9zx1f-.js +0 -1
- package/dist/web/assets/sqlite-viewer-Bpz7QnBk.js +0 -1
- package/dist/web/assets/system-monitor-body-BBJOWFCa.js +0 -2
- package/dist/web/assets/system-monitor-tab-D7RIwhmg.js +0 -1
- package/dist/web/assets/system-monitor-window-content-CH0UmMP9.js +0 -1
- package/dist/web/assets/tab-host-window-content-C_yU5MH_.js +0 -1
- package/dist/web/assets/table-BK4h8E7u.js +0 -1
- package/dist/web/assets/team-member-sheet-CopMME1E.js +0 -2
- package/dist/web/assets/team-member-window-content-CUDQ6oX7.js +0 -1
- package/dist/web/assets/terminal-tab-BjI95uG5.js +0 -3
- package/dist/web/assets/text-wrap-DLtVgSnU.js +0 -1
- package/dist/web/assets/tool-cards-I71v1w5Z.js +0 -9
- package/dist/web/assets/treemap-KZPCXAKY-BBrhTPZk.js +0 -1
- package/dist/web/assets/use-coarse-long-press-XrJ4Upuv.js +0 -1
- package/dist/web/assets/use-inline-blame-CRUwd6FF.js +0 -8
- package/dist/web/assets/use-monaco-theme-qzMbRfCW.js +0 -1
- package/dist/web/assets/use-remote-desktop-display-choice-0zDkCmsK.js +0 -1
- package/dist/web/assets/video-preview-Mlgl7hGr.js +0 -1
- package/dist/web/assets/ws-auth-DI1E6Fe7.js +0 -1
- package/dist/web/assets/x-DlnNFF2t.js +0 -1
- package/dist/web/monacoeditorwork/css.worker.bundle.js +0 -54268
- package/dist/web/monacoeditorwork/editor.worker.bundle.js +0 -14316
- package/dist/web/monacoeditorwork/html.worker.bundle.js +0 -30452
- package/dist/web/monacoeditorwork/json.worker.bundle.js +0 -22095
- package/dist/web/monacoeditorwork/ts.worker.bundle.js +0 -225957
- package/src/web/components/explorer/file-icon-map.ts +0 -69
- /package/dist/web/assets/{abap-BIHt2d9F.js → abap-tkptwpsK.js} +0 -0
- /package/dist/web/assets/{actionscript-3-rbCNRHFj.js → actionscript-3-Di9TMGwg.js} +0 -0
- /package/dist/web/assets/{ada-CN94LQTJ.js → ada-CB9F079D.js} +0 -0
- /package/dist/web/assets/{andromeeda-BsmcoXuQ.js → andromeeda-Dcl8jpb7.js} +0 -0
- /package/dist/web/assets/{apache-pRQmqcVZ.js → apache-D0Nk0qsH.js} +0 -0
- /package/dist/web/assets/{apex-CNEumC1X.js → apex-mQIoWncD.js} +0 -0
- /package/dist/web/assets/{api-client-DKvkxuDr.js → api-client-bfB7cXRM.js} +0 -0
- /package/dist/web/assets/{applescript-DjEVsS-3.js → applescript-C6ljbcsI.js} +0 -0
- /package/dist/web/assets/{apply-theme-Abd1TNqE.js → apply-theme-BwIMNXyX.js} +0 -0
- /package/dist/web/assets/{ara-DbhZRlTL.js → ara-WFqRPGPe.js} +0 -0
- /package/dist/web/assets/{asciidoc-BsfbAXIK.js → asciidoc-bNd8w4nf.js} +0 -0
- /package/dist/web/assets/{asm-Vw5NMXjI.js → asm-BQ1_F580.js} +0 -0
- /package/dist/web/assets/{aurora-x-CoahU6sR.js → aurora-x-Ciqs_Dbu.js} +0 -0
- /package/dist/web/assets/{awk-ChKjCxHz.js → awk-GCR-6KZV.js} +0 -0
- /package/dist/web/assets/{ayu-dark-By9RKxTf.js → ayu-dark-DxJXuj99.js} +0 -0
- /package/dist/web/assets/{ayu-light-CEsgeLON.js → ayu-light-jvSyJWDV.js} +0 -0
- /package/dist/web/assets/{ayu-mirage-CYrtwSLW.js → ayu-mirage-Cg3BPO6H.js} +0 -0
- /package/dist/web/assets/{ballerina-CCSVz22W.js → ballerina-BPz4CrX6.js} +0 -0
- /package/dist/web/assets/{bat-DU4--CMj.js → bat-CSTs2hIG.js} +0 -0
- /package/dist/web/assets/{beancount-DF_tlRlF.js → beancount-DMateAZX.js} +0 -0
- /package/dist/web/assets/{berry-CZTnatvO.js → berry-9lXg0GRM.js} +0 -0
- /package/dist/web/assets/{bibtex-BSpXy8jk.js → bibtex-DIbqmvFg.js} +0 -0
- /package/dist/web/assets/{bicep-yv1Ydq5Z.js → bicep-02WGsoJ8.js} +0 -0
- /package/dist/web/assets/{bird2-Db7Z7SMN.js → bird2-BY0tbu3k.js} +0 -0
- /package/dist/web/assets/{c-C3ivhwA4.js → c-jMBX_9Je.js} +0 -0
- /package/dist/web/assets/{c3-N4mKyst9.js → c3-B8JVbNjW.js} +0 -0
- /package/dist/web/assets/{cadence-BYK8tFFk.js → cadence-DUIf1tvL.js} +0 -0
- /package/dist/web/assets/{can-open-in-ppm-DU5MH6XF.js → can-open-in-ppm-DGEc0s4P.js} +0 -0
- /package/dist/web/assets/{catppuccin-frappe-BK9Tv2XG.js → catppuccin-frappe-FhUCVxex.js} +0 -0
- /package/dist/web/assets/{catppuccin-latte-De4mVCp3.js → catppuccin-latte-CzfrtmgL.js} +0 -0
- /package/dist/web/assets/{catppuccin-macchiato-Bxt_D7cs.js → catppuccin-macchiato-6xL0lOtj.js} +0 -0
- /package/dist/web/assets/{catppuccin-mocha-Dz2o8gN7.js → catppuccin-mocha-D39-xS3P.js} +0 -0
- /package/dist/web/assets/{clarity-BJdh0Pw-.js → clarity-D8TeGvoT.js} +0 -0
- /package/dist/web/assets/{clojure-DG-FYGzn.js → clojure-Dx_acSX7.js} +0 -0
- /package/dist/web/assets/{cmake-D1imEXan.js → cmake-CyeYPq8o.js} +0 -0
- /package/dist/web/assets/{codeowners-YpylTFB5.js → codeowners-DrTI0lmI.js} +0 -0
- /package/dist/web/assets/{codeql-ajDEPgqi.js → codeql-CPkFxVW-.js} +0 -0
- /package/dist/web/assets/{common-lisp-C2sCrd-m.js → common-lisp-B0k-3LTj.js} +0 -0
- /package/dist/web/assets/{coq-BqybyDc9.js → coq-uygpo7to.js} +0 -0
- /package/dist/web/assets/{createLucideIcon-D6fVXe3s.js → createLucideIcon-DoiEKeJ_.js} +0 -0
- /package/dist/web/assets/{csharp-nKCEiujy.js → csharp-DKUYGfSp.js} +0 -0
- /package/dist/web/assets/{css-CJSp2CsD.js → css-DoOUp7v4.js} +0 -0
- /package/dist/web/assets/{csv-Nh9vQwQm.js → csv-B3YE13gi.js} +0 -0
- /package/dist/web/assets/{csv-parser-iow1WWML.js → csv-parser-DXePqmXe.js} +0 -0
- /package/dist/web/assets/{cue-Ch79RPsU.js → cue-DViO2wvn.js} +0 -0
- /package/dist/web/assets/{cypher-CgiOQ6hs.js → cypher-4BMCIW5N.js} +0 -0
- /package/dist/web/assets/{d-CmBcySpH.js → d-BT_sTHas.js} +0 -0
- /package/dist/web/assets/{dark-plus-COLEX2ui.js → dark-plus-DVVXu-1N.js} +0 -0
- /package/dist/web/assets/{dart-BTAArMcJ.js → dart-DHz6f6_6.js} +0 -0
- /package/dist/web/assets/{data-grid-types-tNj9zuRV.js → data-grid-types-OgUxMLdQ.js} +0 -0
- /package/dist/web/assets/{dax-BbZzRY9i.js → dax-ChYCjweK.js} +0 -0
- /package/dist/web/assets/{desktop-OmQvU3-J.js → desktop-D6Lt4p04.js} +0 -0
- /package/dist/web/assets/{diff-BEPnb9Fb.js → diff-D8TDe4hP.js} +0 -0
- /package/dist/web/assets/{dist-BiqPfYiP.js → dist-CrBOCtCW.js} +0 -0
- /package/dist/web/assets/{dist-BDpuZ376.js → dist-DOTlFQJA.js} +0 -0
- /package/dist/web/assets/{docker-Cg17HgTD.js → docker-Bsp0YbPx.js} +0 -0
- /package/dist/web/assets/{dotenv-DEMIaY7V.js → dotenv-B_7giSY_.js} +0 -0
- /package/dist/web/assets/{dracula-BkCFq8SO.js → dracula-TrQL2iB5.js} +0 -0
- /package/dist/web/assets/{dracula-soft-Cjp6-ltF.js → dracula-soft-n2XOlgh-.js} +0 -0
- /package/dist/web/assets/{dream-maker-Ce7Y_UKj.js → dream-maker-CdIEdhpT.js} +0 -0
- /package/dist/web/assets/{emacs-lisp-BjpyfTbN.js → emacs-lisp-CSbixs6u.js} +0 -0
- /package/dist/web/assets/{everforest-dark-9r4okw1s.js → everforest-dark-BuZahZYp.js} +0 -0
- /package/dist/web/assets/{everforest-light-GCQTZ3Ju.js → everforest-light-DPH53z5H.js} +0 -0
- /package/dist/web/assets/{fennel-ITjY5Z6u.js → fennel-B1-K_hlJ.js} +0 -0
- /package/dist/web/assets/{fish-DP2uEzyg.js → fish-Cy12Dbua.js} +0 -0
- /package/dist/web/assets/{fluent-irzaP1f6.js → fluent-B_82ys8E.js} +0 -0
- /package/dist/web/assets/{format-bytes-B9Dbme9a.js → format-bytes-D5sBgIqU.js} +0 -0
- /package/dist/web/assets/{fortran-free-form-BAPoainr.js → fortran-free-form-BRQdNzZF.js} +0 -0
- /package/dist/web/assets/{gdscript-DJ098V1z.js → gdscript-XBDmLUm9.js} +0 -0
- /package/dist/web/assets/{gdshader-DS2Jfy-K.js → gdshader-DhVfB3u4.js} +0 -0
- /package/dist/web/assets/{genie-CqfOaFFx.js → genie-X3K29Y49.js} +0 -0
- /package/dist/web/assets/{gherkin-CoZ2ucDH.js → gherkin-DKIx-6cX.js} +0 -0
- /package/dist/web/assets/{github-dark-BNLgnSO9.js → github-dark-Bf7zdp5s.js} +0 -0
- /package/dist/web/assets/{github-dark-default-CYlSggwi.js → github-dark-default-DqsBBV11.js} +0 -0
- /package/dist/web/assets/{github-dark-dimmed-DumUysDk.js → github-dark-dimmed-X2gqRz6w.js} +0 -0
- /package/dist/web/assets/{github-dark-high-contrast-TKrcDJ2I.js → github-dark-high-contrast-Z7tvbGX4.js} +0 -0
- /package/dist/web/assets/{github-light-DVENFHLJ.js → github-light-BKxt-7zZ.js} +0 -0
- /package/dist/web/assets/{github-light-default-DtN8AGcE.js → github-light-default-B0liF4d4.js} +0 -0
- /package/dist/web/assets/{github-light-high-contrast-NSyF37x4.js → github-light-high-contrast-F4encJwr.js} +0 -0
- /package/dist/web/assets/{gleam-hp4NMSb5.js → gleam-Pz2bHgBB.js} +0 -0
- /package/dist/web/assets/{gn-Dptw2PEu.js → gn-CgmdNsPU.js} +0 -0
- /package/dist/web/assets/{gnuplot-9juRzjwv.js → gnuplot-qLNF5yZ9.js} +0 -0
- /package/dist/web/assets/{go-D-QLCda0.js → go-CSDUNhE7.js} +0 -0
- /package/dist/web/assets/{groovy-DHKNy6Eh.js → groovy-B3NEnOHF.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-hard-BFD6aQdB.js → gruvbox-dark-hard-DcoftiUX.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-medium-D5llYBNp.js → gruvbox-dark-medium-D1wVSuD8.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-soft-DOztQmNR.js → gruvbox-dark-soft-DWpunymH.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-hard-BFGD-8Pd.js → gruvbox-light-hard-rHMvyUdS.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-medium-DzoyXgT7.js → gruvbox-light-medium-3O2KiXO7.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-soft-B1Ix2zLN.js → gruvbox-light-soft-lNlXJPwH.js} +0 -0
- /package/dist/web/assets/{haskell-BnrJ0k7G.js → haskell-aaG3aC0h.js} +0 -0
- /package/dist/web/assets/{haxe-DwShHT_-.js → haxe-CPcHhUDd.js} +0 -0
- /package/dist/web/assets/{hcl-VOjEibVB.js → hcl-CMQNlR_4.js} +0 -0
- /package/dist/web/assets/{hjson-BHEn7pNn.js → hjson-DeuklPFj.js} +0 -0
- /package/dist/web/assets/{hlsl-CrvxlGii.js → hlsl-BI3hlsaM.js} +0 -0
- /package/dist/web/assets/{horizon-D3T-P0u6.js → horizon-C7-gOcjs.js} +0 -0
- /package/dist/web/assets/{horizon-bright-vifGsEdk.js → horizon-bright-Cxt9bRW7.js} +0 -0
- /package/dist/web/assets/{houston-IrOgT7r4.js → houston-DbF2G7iD.js} +0 -0
- /package/dist/web/assets/{hy-5eSgTJ8G.js → hy-DRG6Hs2I.js} +0 -0
- /package/dist/web/assets/{image-gallery-COea0nSk.js → image-gallery-CObzTisL.js} +0 -0
- /package/dist/web/assets/{imba-Dp0I4DVl.js → imba-CxKv_2TW.js} +0 -0
- /package/dist/web/assets/{ini-CHDKad9O.js → ini-BiHsyCdO.js} +0 -0
- /package/dist/web/assets/{java-YLv7IDJ1.js → java-D3RZzgDB.js} +0 -0
- /package/dist/web/assets/{javascript-jr4kIdgl.js → javascript-4FmSqG-T.js} +0 -0
- /package/dist/web/assets/{json-EJWSOZfp.js → json-CjB236_E.js} +0 -0
- /package/dist/web/assets/{json5-D6f_uNwJ.js → json5-bkfO-Rco.js} +0 -0
- /package/dist/web/assets/{jsonc-DrHCdmos.js → jsonc-BwaTeLyv.js} +0 -0
- /package/dist/web/assets/{jsonl-Cwc3cXEL.js → jsonl-CfxNSwGd.js} +0 -0
- /package/dist/web/assets/{jsonnet-BwZfaIFT.js → jsonnet-CQETCYrp.js} +0 -0
- /package/dist/web/assets/{jssm-BpEmg_MT.js → jssm-D5o3Zzdz.js} +0 -0
- /package/dist/web/assets/{jsx-DBGPwcr4.js → jsx-BAMILPLo.js} +0 -0
- /package/dist/web/assets/{kanagawa-dragon-DRfVaGIX.js → kanagawa-dragon-BhGVxu3R.js} +0 -0
- /package/dist/web/assets/{kanagawa-lotus-CjOlny0Q.js → kanagawa-lotus-FH4Y59ro.js} +0 -0
- /package/dist/web/assets/{kanagawa-wave-DvdM88a4.js → kanagawa-wave-eilvP9MB.js} +0 -0
- /package/dist/web/assets/{kdl-DSo3PvM0.js → kdl-BQ-yvXUK.js} +0 -0
- /package/dist/web/assets/{kotlin-BV8TwCGu.js → kotlin-BuCx0-VT.js} +0 -0
- /package/dist/web/assets/{kusto-DCo1Fdmm.js → kusto-DAN_n_Cy.js} +0 -0
- /package/dist/web/assets/{laserwave-DPX9FvD9.js → laserwave-Cq2BlxrT.js} +0 -0
- /package/dist/web/assets/{lean-Cya8dqZu.js → lean-Dhq7uXKw.js} +0 -0
- /package/dist/web/assets/{less-CJUPgkIs.js → less-CxaMQb8j.js} +0 -0
- /package/dist/web/assets/{lib-DUBAP_Gd.js → lib-BVwmtv2i.js} +0 -0
- /package/dist/web/assets/{libesm-CcPYXCKp.js → libesm-DdtEjhIi.js} +0 -0
- /package/dist/web/assets/{light-plus-BXXrlFJH.js → light-plus-B4sMAend.js} +0 -0
- /package/dist/web/assets/{llvm-CUbQanZr.js → llvm-DQ6-B_6Y.js} +0 -0
- /package/dist/web/assets/{log-B3fFlSMd.js → log-Bb_vqvu7.js} +0 -0
- /package/dist/web/assets/{logo-ucAyw6zf.js → logo-BzfYv3A_.js} +0 -0
- /package/dist/web/assets/{luau-C4l6TSTB.js → luau-BnAeNR77.js} +0 -0
- /package/dist/web/assets/{make-CDtGIVc7.js → make-B6IFuxpC.js} +0 -0
- /package/dist/web/assets/{markdown-CH1zKu2p.js → markdown-Z0xi77Fn.js} +0 -0
- /package/dist/web/assets/{material-theme-CEuGVTmh.js → material-theme-1scy1SYn.js} +0 -0
- /package/dist/web/assets/{material-theme-darker-CUZ3MoIr.js → material-theme-darker-G-JykXQP.js} +0 -0
- /package/dist/web/assets/{material-theme-lighter-DxAqJSid.js → material-theme-lighter-BtKkCMVq.js} +0 -0
- /package/dist/web/assets/{material-theme-ocean-BSvvHJp6.js → material-theme-ocean-irrDC89L.js} +0 -0
- /package/dist/web/assets/{material-theme-palenight-QVOl50_m.js → material-theme-palenight-Eq5_K4LM.js} +0 -0
- /package/dist/web/assets/{matlab-CZLQLwUp.js → matlab-BE3oCXZC.js} +0 -0
- /package/dist/web/assets/{mdx-DO4NJsOo.js → mdx-DYiuFTGw.js} +0 -0
- /package/dist/web/assets/{mermaid-CIwzg1uO.js → mermaid-DVlaEvtw.js} +0 -0
- /package/dist/web/assets/{min-dark-BdIgslXy.js → min-dark-DSkeTpQa.js} +0 -0
- /package/dist/web/assets/{min-light-DyU-T1r2.js → min-light-CFv2Q0uF.js} +0 -0
- /package/dist/web/assets/{mipsasm-DRad-Klm.js → mipsasm-CiAFuZQ2.js} +0 -0
- /package/dist/web/assets/{mojo-DXp5LDOc.js → mojo-ARshcaSQ.js} +0 -0
- /package/dist/web/assets/{monokai-BiKX3qby.js → monokai-CPTV-SOg.js} +0 -0
- /package/dist/web/assets/{moonbit-BOUoLF5r.js → moonbit-D57B0dZr.js} +0 -0
- /package/dist/web/assets/{move-Dlk64FWK.js → move-qgy3G_o9.js} +0 -0
- /package/dist/web/assets/{narrat-B-qd2Lh0.js → narrat-CZ3rC-oc.js} +0 -0
- /package/dist/web/assets/{nextflow-groovy-iAMYas0F.js → nextflow-groovy-BxxIOcI4.js} +0 -0
- /package/dist/web/assets/{night-owl-3YNqjftR.js → night-owl-B1ryGCda.js} +0 -0
- /package/dist/web/assets/{night-owl-light-GIncHb5w.js → night-owl-light-BZvNnvMF.js} +0 -0
- /package/dist/web/assets/{nix-xjlEb3uw.js → nix-BVBQi8yv.js} +0 -0
- /package/dist/web/assets/{nord-C8tvWx7P.js → nord-3NXw613h.js} +0 -0
- /package/dist/web/assets/{nushell-BqBb4pU6.js → nushell-BMOLRalb.js} +0 -0
- /package/dist/web/assets/{objective-c-Bk6s0Cn5.js → objective-c-AxruEUgh.js} +0 -0
- /package/dist/web/assets/{objective-cpp-DGTVWQ3V.js → objective-cpp-cFT0qRsD.js} +0 -0
- /package/dist/web/assets/{ocaml-D6wPQ4kw.js → ocaml-_8JH6O8J.js} +0 -0
- /package/dist/web/assets/{odin-C9mw2xRN.js → odin-nwYvR0O6.js} +0 -0
- /package/dist/web/assets/{one-dark-pro-SMBlneFb.js → one-dark-pro-C1LaYJaH.js} +0 -0
- /package/dist/web/assets/{one-light-C9JMU1Cx.js → one-light-B7rMc__c.js} +0 -0
- /package/dist/web/assets/{openscad-C0I9CtuA.js → openscad-Cwes8bKO.js} +0 -0
- /package/dist/web/assets/{pascal-D0z4DJnY.js → pascal-DMt-6tdZ.js} +0 -0
- /package/dist/web/assets/{pkl-CITYNGVz.js → pkl-QBTyYQmB.js} +0 -0
- /package/dist/web/assets/{plastic-Bf0T6rmg.js → plastic-BTSUp8Fk.js} +0 -0
- /package/dist/web/assets/{plsql-Chr907pu.js → plsql-DQv3IFXP.js} +0 -0
- /package/dist/web/assets/{po-07U75CYV.js → po-CNwZx4tW.js} +0 -0
- /package/dist/web/assets/{poimandres-B0T4qwXY.js → poimandres-8Os7Qequ.js} +0 -0
- /package/dist/web/assets/{polar-BvNJMMV5.js → polar-b4yK8oD8.js} +0 -0
- /package/dist/web/assets/{portal-container-context-Bqn516X4.js → portal-container-context-BarKmT4S.js} +0 -0
- /package/dist/web/assets/{postcss-BSdf-zzo.js → postcss-sFctwpxx.js} +0 -0
- /package/dist/web/assets/{powerquery-B2Ad_HK8.js → powerquery-Cg_WrtuN.js} +0 -0
- /package/dist/web/assets/{powershell-DO5WY2v2.js → powershell-C7Ifz95h.js} +0 -0
- /package/dist/web/assets/{prisma-BfaRPYzs.js → prisma-Z52HUJzQ.js} +0 -0
- /package/dist/web/assets/{prolog-Cz0Nue9e.js → prolog-DLqW-zR1.js} +0 -0
- /package/dist/web/assets/{proto-FVleUZJO.js → proto-BOIzxCCJ.js} +0 -0
- /package/dist/web/assets/{puppet-BEX5o7hE.js → puppet-De7TjZIf.js} +0 -0
- /package/dist/web/assets/{purescript-BZX6Kw4j.js → purescript-R76ou1WT.js} +0 -0
- /package/dist/web/assets/{python-CO89UIxE.js → python-Dprm0tNP.js} +0 -0
- /package/dist/web/assets/{qmldir-DH526DWB.js → qmldir-Clp_jpNw.js} +0 -0
- /package/dist/web/assets/{qss-wp8im4Iz.js → qss-eQPYYzSo.js} +0 -0
- /package/dist/web/assets/{r-F9n--bvq.js → r-CCcRHIf5.js} +0 -0
- /package/dist/web/assets/{racket-BQvbOE5D.js → racket-CzE472yp.js} +0 -0
- /package/dist/web/assets/{raku-B0iWSynj.js → raku-DQwXrTSi.js} +0 -0
- /package/dist/web/assets/{react-Bcm1Oab0.js → react-Bghu0NUO.js} +0 -0
- /package/dist/web/assets/{red-Cm00r9eI.js → red-KCS6wyop.js} +0 -0
- /package/dist/web/assets/{reg-CBuhna22.js → reg-CcrAddUh.js} +0 -0
- /package/dist/web/assets/{regexp-C9j1_6oU.js → regexp-DDbud-Ri.js} +0 -0
- /package/dist/web/assets/{rel-BYzlBhbq.js → rel-99GDgaHp.js} +0 -0
- /package/dist/web/assets/{resolve-theme-Dm8ehbOv.js → resolve-theme-CW2niGV2.js} +0 -0
- /package/dist/web/assets/{riscv-CQQP0Ro9.js → riscv-Dm2eNv0R.js} +0 -0
- /package/dist/web/assets/{ron-j1ON9YzM.js → ron-CzFt0ygH.js} +0 -0
- /package/dist/web/assets/{rose-pine-Cbri3jon.js → rose-pine-Ck6lCrCP.js} +0 -0
- /package/dist/web/assets/{rose-pine-dawn-BMuvKrGo.js → rose-pine-dawn-vtALRJNj.js} +0 -0
- /package/dist/web/assets/{rose-pine-moon-4biZzaYD.js → rose-pine-moon-DPto8qXw.js} +0 -0
- /package/dist/web/assets/{rosmsg-CXx22rtz.js → rosmsg-NtS6429H.js} +0 -0
- /package/dist/web/assets/{rust-D28cFIhL.js → rust-BjIXvF9h.js} +0 -0
- /package/dist/web/assets/{sass-DtNw4V28.js → sass-CQ23wf5E.js} +0 -0
- /package/dist/web/assets/{scala-hv69_YQC.js → scala-LfKLM143.js} +0 -0
- /package/dist/web/assets/{scheme-Dtp9B_ng.js → scheme-D3SLBXlF.js} +0 -0
- /package/dist/web/assets/{sdbl-DEwEngAE.js → sdbl-ODRd2rGN.js} +0 -0
- /package/dist/web/assets/{shellscript-CCOF9tox.js → shellscript-3KB1G_oA.js} +0 -0
- /package/dist/web/assets/{slack-dark-DT0E0ABt.js → slack-dark-DTtyWmIy.js} +0 -0
- /package/dist/web/assets/{slack-ochin-DHHiQnxo.js → slack-ochin-Bu0PApk3.js} +0 -0
- /package/dist/web/assets/{smalltalk-BF_I35xJ.js → smalltalk-haMT-Sbe.js} +0 -0
- /package/dist/web/assets/{snazzy-light-Cv7tIZo9.js → snazzy-light-DXJzefE9.js} +0 -0
- /package/dist/web/assets/{solarized-dark-wZ5YPCys.js → solarized-dark-B6LyYa18.js} +0 -0
- /package/dist/web/assets/{solarized-light-DGDOr1AS.js → solarized-light-QLADebkv.js} +0 -0
- /package/dist/web/assets/{solidity-BJknuPqJ.js → solidity-mzfq1aDc.js} +0 -0
- /package/dist/web/assets/{splunk-B1ClO_KU.js → splunk-CyH4RWBp.js} +0 -0
- /package/dist/web/assets/{sql-CXZ66NtF.js → sql-CWVlT5D3.js} +0 -0
- /package/dist/web/assets/{ssh-config-kgM67Sf6.js → ssh-config-DNvQJ1Qx.js} +0 -0
- /package/dist/web/assets/{stylus-RcVaInR-.js → stylus-ByQQdtmf.js} +0 -0
- /package/dist/web/assets/{swift-Cm0fh2lX.js → swift-D1cpY4ow.js} +0 -0
- /package/dist/web/assets/{synthwave-84-CwLiazxr.js → synthwave-84-BDF_-QiW.js} +0 -0
- /package/dist/web/assets/{system-verilog-CzYaDVLH.js → system-verilog-BYrkMydC.js} +0 -0
- /package/dist/web/assets/{systemd-DbSagfLw.js → systemd-CQm3zyK0.js} +0 -0
- /package/dist/web/assets/{talonscript-BCFnWejL.js → talonscript-DMW5CzE1.js} +0 -0
- /package/dist/web/assets/{tasl-BUdqtEe1.js → tasl-BhFzMCr2.js} +0 -0
- /package/dist/web/assets/{tcl-CR_lTV9N.js → tcl-D7VEAF-9.js} +0 -0
- /package/dist/web/assets/{terraform-Bqsha-xe.js → terraform-DdT5A0Vy.js} +0 -0
- /package/dist/web/assets/{tokyo-night-Cnuo6JB6.js → tokyo-night-WGLMM6Q9.js} +0 -0
- /package/dist/web/assets/{toml-CLFdhT22.js → toml-CZ7ipXHZ.js} +0 -0
- /package/dist/web/assets/{tsv-Dw3tLrZN.js → tsv-B1zVvoTr.js} +0 -0
- /package/dist/web/assets/{tsx-B8k1sFqw.js → tsx-D4HwRUBq.js} +0 -0
- /package/dist/web/assets/{turtle-BMKMfTrj.js → turtle-B4r8KEJH.js} +0 -0
- /package/dist/web/assets/{typescript-D38LfGSl.js → typescript-BnHhxMtp.js} +0 -0
- /package/dist/web/assets/{typespec-EbgGJHV_.js → typespec-DP2q46GT.js} +0 -0
- /package/dist/web/assets/{typst-CQXV2SzX.js → typst-DFsD_yaD.js} +0 -0
- /package/dist/web/assets/{use-is-mobile-Dypg-2ZO.js → use-is-mobile-BQ9Bv5Ga.js} +0 -0
- /package/dist/web/assets/{use-visual-viewport-Bj3QHRkF.js → use-visual-viewport-CbwnktSG.js} +0 -0
- /package/dist/web/assets/{utils-CwNUra6_.js → utils-Bmf_8h7Y.js} +0 -0
- /package/dist/web/assets/{v-Ci1jObJJ.js → v-Bji8hSVT.js} +0 -0
- /package/dist/web/assets/{vala-C_NdfqrY.js → vala-D3TIgGmU.js} +0 -0
- /package/dist/web/assets/{vb-K_r_h_s8.js → vb-DvcxFS2q.js} +0 -0
- /package/dist/web/assets/{vendor-xterm-BtjgHRUe.js → vendor-xterm-BqPDsE9b.js} +0 -0
- /package/dist/web/assets/{verilog-DQPYLsZI.js → verilog-Dy7cF35d.js} +0 -0
- /package/dist/web/assets/{vesper-BrmzEV8s.js → vesper-BzjwUv9J.js} +0 -0
- /package/dist/web/assets/{vhdl-CjxQGaH2.js → vhdl-DuFnFNmW.js} +0 -0
- /package/dist/web/assets/{viml-CTKJNQH1.js → viml-yAPgKlbz.js} +0 -0
- /package/dist/web/assets/{vitesse-black-i-H_3p1i.js → vitesse-black-oSSQdyIZ.js} +0 -0
- /package/dist/web/assets/{vitesse-dark-DZC_Yy3_.js → vitesse-dark-BZxwPihB.js} +0 -0
- /package/dist/web/assets/{vitesse-light-rf4kpmk8.js → vitesse-light-uIigycvt.js} +0 -0
- /package/dist/web/assets/{vyper-BLnOw4Nf.js → vyper-CX3NAxm5.js} +0 -0
- /package/dist/web/assets/{wasm-DbLMmiW0.js → wasm-6mEOOvVa.js} +0 -0
- /package/dist/web/assets/{wasm-CWeHvGNG.js → wasm-D9td37qO.js} +0 -0
- /package/dist/web/assets/{wenyan-CLwdRuzd.js → wenyan-lwNIHgk6.js} +0 -0
- /package/dist/web/assets/{wgsl-CBTDMVJR.js → wgsl-DQAzCfog.js} +0 -0
- /package/dist/web/assets/{wikitext-CQHrF84e.js → wikitext-DTXmt5NE.js} +0 -0
- /package/dist/web/assets/{wit-8xdbdipU.js → wit-Bdw_7WDH.js} +0 -0
- /package/dist/web/assets/{wolfram-tFwx32a_.js → wolfram-DqyHDZ07.js} +0 -0
- /package/dist/web/assets/{yaml-BP8N91fU.js → yaml-CPXhdByd.js} +0 -0
- /package/dist/web/assets/{zenscript-B10Pj7JT.js → zenscript-6s_i5zek.js} +0 -0
- /package/dist/web/assets/{zig-BZKcTtrK.js → zig-B2htezJ_.js} +0 -0
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* Generate the complete webview HTML for the git graph panel.
|
|
3
3
|
* All JS + CSS is inlined since webview runs in an iframe sandbox.
|
|
4
4
|
*/
|
|
5
|
-
import { AVATAR_JS } from "./webview-shell.ts";
|
|
5
|
+
import { AVATAR_JS, FONT_TOKENS } from "./webview-shell.ts";
|
|
6
|
+
import { COMMIT_MESSAGE_JS } from "./commit-message-html.ts";
|
|
6
7
|
|
|
7
8
|
export function getWebviewHtml(): string {
|
|
8
9
|
return `<!DOCTYPE html>
|
|
@@ -81,10 +82,13 @@ ${getStyles()}
|
|
|
81
82
|
<button id="find-close" title="Close">×</button>
|
|
82
83
|
</div>
|
|
83
84
|
<div id="search-results" class="search-results hidden"></div>
|
|
85
|
+
<div id="graph-area">
|
|
84
86
|
<div id="graph-container">
|
|
85
87
|
<div id="graph-header" class="commit-row header-row">
|
|
88
|
+
<div class="col-refs">Branch / Tag</div>
|
|
86
89
|
<div class="col-graph">Graph<div class="graph-resize-handle" id="graph-resize-handle"></div></div>
|
|
87
90
|
<div class="col-message">Message</div>
|
|
91
|
+
<div class="col-changes">Changes</div>
|
|
88
92
|
<div class="col-author">Author</div>
|
|
89
93
|
<div class="col-date">Date</div>
|
|
90
94
|
<div class="col-hash">Hash</div>
|
|
@@ -94,6 +98,8 @@ ${getStyles()}
|
|
|
94
98
|
<div id="commit-list"></div>
|
|
95
99
|
</div>
|
|
96
100
|
<div id="loading" class="loading hidden">Loading...</div>
|
|
101
|
+
</div>
|
|
102
|
+
<div id="scroll-markers" aria-hidden="true"></div>
|
|
97
103
|
</div>
|
|
98
104
|
<div id="detail-panel" class="detail-panel hidden"></div>
|
|
99
105
|
<div id="settings-panel" class="settings-panel">
|
|
@@ -161,6 +167,24 @@ ${getScript()}
|
|
|
161
167
|
</html>`;
|
|
162
168
|
}
|
|
163
169
|
|
|
170
|
+
/**
|
|
171
|
+
* Dark values, emitted twice by design.
|
|
172
|
+
*
|
|
173
|
+
* The panel is a sandboxed iframe and cannot see the app's theme, so on its own
|
|
174
|
+
* the only question it can ask is prefers-color-scheme — the *OS* setting,
|
|
175
|
+
* which is why a light app still showed a dark graph. The host now stamps
|
|
176
|
+
* data-ppm-theme on this document and injects the app's own tokens
|
|
177
|
+
* (src/web/components/extensions/webview-theme.ts); the media query is the
|
|
178
|
+
* fallback for a host that says nothing, and it must not override an explicit
|
|
179
|
+
* light. Values are a charcoal rather than near-black so the toolbar, the list
|
|
180
|
+
* and the banded rows read as three surfaces instead of one slab.
|
|
181
|
+
*/
|
|
182
|
+
const DARK_TOKENS = `
|
|
183
|
+
--bg: #16171c; --surface: #1d1f26; --text: #ecedf0; --subtext: #a2a5b0; --subtle: #6b6f7c;
|
|
184
|
+
--border: #262932; --border2: #383c48; --selected: #1e293b;
|
|
185
|
+
--band: color-mix(in srgb, var(--text) 6%, transparent);
|
|
186
|
+
`;
|
|
187
|
+
|
|
164
188
|
function getStyles(): string {
|
|
165
189
|
return `
|
|
166
190
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
@@ -168,15 +192,28 @@ function getStyles(): string {
|
|
|
168
192
|
--bg: #ffffff; --surface: #f4f4f5; --text: #09090b; --subtext: #71717a; --subtle: #a1a1aa;
|
|
169
193
|
--border: #e4e4e7; --border2: #d4d4d8; --blue: #3b82f6; --red: #ef4444; --green: #22c55e;
|
|
170
194
|
--yellow: #eab308; --purple: #8b5cf6; --orange: #f97316;
|
|
171
|
-
--
|
|
172
|
-
|
|
195
|
+
--selected: #eff6ff;
|
|
196
|
+
/* Zebra banding. Stronger in dark below, because the same percentage is not
|
|
197
|
+
equally visible in both directions: a wash of near-black over white shows
|
|
198
|
+
up where the same lift of near-white over a near-black row does not, which
|
|
199
|
+
is what left the list looking like one flat slab. */
|
|
200
|
+
--band: color-mix(in srgb, var(--text) 3.5%, transparent);
|
|
201
|
+
/* Derived, never injected: the app has no hover token and some of its themes
|
|
202
|
+
give the same colour to both panel surfaces, which would leave a hovered
|
|
203
|
+
row looking untouched. A tint of the text colour flips with the mode and
|
|
204
|
+
stacks on top of the zebra banding instead of replacing it. */
|
|
205
|
+
--surface-hover: color-mix(in srgb, var(--text) 8%, transparent);
|
|
206
|
+
/* Width of the branch/tag column. Fixed, because the graph is one SVG overlay
|
|
207
|
+
drawn on a single grid and it starts where this column ends. */
|
|
208
|
+
--refs-col-w: 170px;
|
|
209
|
+
|
|
210
|
+
${FONT_TOKENS}
|
|
211
|
+
}
|
|
212
|
+
:root[data-ppm-theme="dark"] { ${DARK_TOKENS} }
|
|
173
213
|
@media (prefers-color-scheme: dark) {
|
|
174
|
-
:root {
|
|
175
|
-
--bg: #09090b; --surface: #18181b; --text: #fafafa; --subtext: #a1a1aa; --subtle: #52525b;
|
|
176
|
-
--border: #27272a; --border2: #3f3f46; --selected: #1e293b; --surface-hover: #27272a;
|
|
177
|
-
}
|
|
214
|
+
:root:not([data-ppm-theme="light"]) { ${DARK_TOKENS} }
|
|
178
215
|
}
|
|
179
|
-
body { font-family: -
|
|
216
|
+
body { font-family: var(--ui-font); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; background: var(--bg); color: var(--text); font-size: 12px; overflow: hidden; height: 100vh; display: flex; flex-direction: column; }
|
|
180
217
|
#app { display: flex; flex-direction: column; height: 100vh; }
|
|
181
218
|
|
|
182
219
|
/* Toolbar */
|
|
@@ -268,25 +305,67 @@ button:active { background: var(--surface); }
|
|
|
268
305
|
#find-count { font-size: 10px; color: var(--subtext); min-width: 50px; }
|
|
269
306
|
.hidden { display: none !important; }
|
|
270
307
|
|
|
271
|
-
/* Graph container
|
|
272
|
-
|
|
273
|
-
|
|
308
|
+
/* Graph container. The scroller is wrapped, because the scroll markers have to
|
|
309
|
+
sit beside it in a box that does not scroll with the rows. */
|
|
310
|
+
#graph-area { position: relative; flex: 1; min-height: 0; display: flex; }
|
|
311
|
+
#graph-container { flex: 1; min-width: 0; overflow-y: auto; overflow-x: hidden; }
|
|
312
|
+
/* Where in the whole history the things worth scrolling to are: the checked-out
|
|
313
|
+
commit, the selected row, and every search match. Over the scrollbar, like
|
|
314
|
+
VS Code's own overview ruler, and never clickable — dragging the scrollbar
|
|
315
|
+
underneath has to keep working. */
|
|
316
|
+
#scroll-markers { position: absolute; right: 0; top: 0; bottom: 0; width: 5px; pointer-events: none; z-index: 3; }
|
|
317
|
+
.scroll-marker { position: absolute; right: 0; width: 5px; height: 2px; border-radius: 1px; transform: translateY(-50%); }
|
|
318
|
+
.sm-search { background: var(--yellow); }
|
|
319
|
+
.sm-head { background: var(--green); height: 3px; }
|
|
320
|
+
.sm-selected { background: var(--blue); height: 3px; }
|
|
321
|
+
.commit-row { display: flex; align-items: center; cursor: pointer; height: 30px; padding: 0 6px; font-size: 12px; box-sizing: border-box; overflow: hidden; }
|
|
322
|
+
/* Banding, before the hover and selected rules on purpose: it has the same
|
|
323
|
+
specificity as they do, so source order is what decides the winner. Do not
|
|
324
|
+
qualify it with #commit-list — an id would raise it above both of them and
|
|
325
|
+
every other row would stop showing hover and selection. The header row is a
|
|
326
|
+
.commit-row too but is the first child of its own parent, so it is odd. */
|
|
327
|
+
.commit-row:nth-child(even) { background: var(--band); }
|
|
274
328
|
.commit-row:hover { background: var(--surface-hover); }
|
|
275
|
-
.commit-row.
|
|
276
|
-
.commit-row.header-row
|
|
277
|
-
|
|
329
|
+
.commit-row.header-row { background: var(--surface); cursor: default; font-weight: 600; font-size: 10px; color: var(--subtext); text-transform: uppercase; letter-spacing: 0.5px; position: sticky; top: 0; z-index: 2; border-bottom: 1px solid var(--border); height: 24px; }
|
|
330
|
+
.commit-row.header-row .col-message::before { display: none; }
|
|
331
|
+
/* A tint and a bar, not a flood: with fifteen rows on screen a 15% yellow
|
|
332
|
+
wash over half of them buried the list it was meant to annotate. The
|
|
333
|
+
scroll markers carry where the rest of the matches are. */
|
|
334
|
+
.commit-row.search-match { background: color-mix(in srgb, var(--yellow) 9%, transparent); box-shadow: inset 2px 0 0 var(--yellow); }
|
|
335
|
+
/* After the match rule on purpose: the row you clicked should look selected,
|
|
336
|
+
even when it is also a match. Which of the two it is stays visible in the
|
|
337
|
+
find bar's count and in the scroll markers. */
|
|
338
|
+
.commit-row.selected { background: var(--selected); box-shadow: inset 2px 0 0 var(--blue); }
|
|
278
339
|
.commit-row.virtual { opacity: 0.85; font-style: italic; }
|
|
279
340
|
.commit-row.virtual .col-message { color: var(--subtext); }
|
|
280
341
|
.commit-row.stash-row { opacity: 0.75; }
|
|
281
342
|
.commit-row.stash-row .col-message { color: var(--subtext); font-style: italic; }
|
|
282
343
|
.file-clickable { cursor: pointer; border-radius: 3px; padding: 2px 4px; margin: 0 -4px; }
|
|
283
344
|
.file-clickable:hover { background: var(--surface-hover); }
|
|
345
|
+
/* Refs get a column of their own so every message starts at the same x — a
|
|
346
|
+
branch badge pushing the text right was most of why the list read as ragged.
|
|
347
|
+
The width has to be fixed rather than content-sized: rows are separate flex
|
|
348
|
+
containers, so an auto width would put each row's graph at a different x, and
|
|
349
|
+
the graph is one SVG overlay drawn on a single grid. */
|
|
350
|
+
.col-refs { width: var(--refs-col-w, 170px); min-width: var(--refs-col-w, 170px); flex-shrink: 0; overflow: hidden; white-space: nowrap; display: flex; align-items: center; justify-content: flex-end; gap: 3px; padding-right: 6px; }
|
|
351
|
+
.col-refs .ref-badge { min-width: 0; overflow: hidden; text-overflow: ellipsis; margin-right: 0; }
|
|
284
352
|
.col-graph { width: var(--graph-col-w, 120px); min-width: var(--graph-col-w, 80px); overflow: hidden; flex-shrink: 0; position: relative; }
|
|
285
353
|
.graph-resize-handle { position: absolute; right: 0; top: 0; bottom: 0; width: 6px; cursor: col-resize; z-index: 3; background: transparent; }
|
|
286
354
|
.graph-resize-handle:hover, .graph-resize-handle.dragging { background: var(--blue); opacity: 0.5; }
|
|
287
|
-
.col-message { flex: 1;
|
|
288
|
-
|
|
289
|
-
|
|
355
|
+
.col-message { flex: 1; min-width: 0; padding: 0 6px 0 12px; position: relative; display: flex; flex-direction: column; justify-content: center; align-self: stretch; overflow: hidden; }
|
|
356
|
+
/* The lane's colour, restated where the eye reads the message. The row centres
|
|
357
|
+
its cells, so this one has to stretch or the tick has nothing to span. */
|
|
358
|
+
.col-message::before { content: ''; position: absolute; left: 3px; top: 7px; bottom: 7px; width: 2px; border-radius: 1px; background: var(--lane, transparent); opacity: 0.65; }
|
|
359
|
+
.msg-subject { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
360
|
+
/* Author, date and hash again, for the phone layout that has no room for their
|
|
361
|
+
columns. Hidden until that layout asks for it. */
|
|
362
|
+
.msg-meta { display: none; }
|
|
363
|
+
/* Lines added and removed. Tabular figures so the columns of digits line up
|
|
364
|
+
down the list rather than jittering with the glyph widths. */
|
|
365
|
+
.col-changes { width: 84px; min-width: 84px; flex-shrink: 0; font-size: 11px; font-variant-numeric: tabular-nums; display: flex; align-items: center; gap: 5px; }
|
|
366
|
+
.col-changes .ch-add { color: var(--green); }
|
|
367
|
+
.col-changes .ch-del { color: var(--red); }
|
|
368
|
+
.col-author { width: 130px; min-width: 130px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: var(--subtext); font-size: 11px; }
|
|
290
369
|
.avatar { width: 16px; height: 16px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; font-size: 7px; font-weight: 700; color: #fff; flex-shrink: 0; letter-spacing: -0.2px; }
|
|
291
370
|
|
|
292
371
|
/* Author hover card */
|
|
@@ -306,9 +385,9 @@ button:active { background: var(--surface); }
|
|
|
306
385
|
.sr-item:hover { background: var(--surface-hover); }
|
|
307
386
|
.sr-subject { font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
308
387
|
.sr-meta { font-size: 10px; color: var(--subtext); display: flex; gap: 6px; align-items: center; margin-top: 2px; }
|
|
309
|
-
.sr-hash { font-family:
|
|
388
|
+
.sr-hash { font-family: var(--mono-font); }
|
|
310
389
|
.col-date { width: 80px; min-width: 80px; color: var(--subtext); font-size: 11px; }
|
|
311
|
-
.col-hash { width: 60px; min-width: 60px; font-family:
|
|
390
|
+
.col-hash { width: 60px; min-width: 60px; font-family: var(--mono-font); font-size: 10px; color: var(--subtle); }
|
|
312
391
|
|
|
313
392
|
/* Ref badges — border + tinted bg + dark text */
|
|
314
393
|
.ref-badge { display: inline-flex; align-items: center; gap: 3px; padding: 0px 5px; border-radius: 3px; font-size: 9px; font-weight: 600; margin-right: 3px; vertical-align: middle; line-height: 16px; border: 1px solid; color: #1a1a1a; }
|
|
@@ -317,26 +396,129 @@ button:active { background: var(--surface); }
|
|
|
317
396
|
.ref-remote { border-color: var(--purple); background: color-mix(in srgb, var(--purple) 12%, transparent); }
|
|
318
397
|
.ref-tag { border-color: var(--yellow); background: color-mix(in srgb, var(--yellow) 12%, transparent); }
|
|
319
398
|
.ref-stash { border-color: #808080; background: color-mix(in srgb, #808080 12%, transparent); }
|
|
399
|
+
:root[data-ppm-theme="dark"] .ref-badge { color: #e4e4e7; }
|
|
320
400
|
@media (prefers-color-scheme: dark) {
|
|
321
|
-
.ref-badge { color: #e4e4e7; }
|
|
401
|
+
:root:not([data-ppm-theme="light"]) .ref-badge { color: #e4e4e7; }
|
|
322
402
|
}
|
|
323
403
|
|
|
324
404
|
/* SVG graph — single SVG overlay */
|
|
325
405
|
#commit-list-wrapper { position: relative; }
|
|
326
|
-
#graph-svg-container { position: absolute; top: 0; left: 8px; z-index: 1; pointer-events: none; }
|
|
406
|
+
#graph-svg-container { position: absolute; top: 0; left: calc(var(--refs-col-w, 170px) + 8px); z-index: 1; pointer-events: none; }
|
|
327
407
|
#graph-svg-container circle { pointer-events: auto; cursor: pointer; }
|
|
328
408
|
#graph-svg-container .line { stroke-width: 2; fill: none; }
|
|
329
|
-
|
|
409
|
+
/* The checked-out commit wears a thicker ring rather than a different shape,
|
|
410
|
+
so it still reads as the same kind of thing as every row above it. */
|
|
411
|
+
#graph-svg-container .graphCurrent { stroke-width: 3.5; }
|
|
412
|
+
#graph-svg-container .node-initials { font-size: 8px; font-weight: 700; fill: #fff; pointer-events: none; user-select: none; letter-spacing: -0.3px; }
|
|
330
413
|
.commit-row.graph-hover { background: var(--surface-hover); }
|
|
331
414
|
|
|
332
|
-
/* Detail panel
|
|
333
|
-
|
|
415
|
+
/* Detail panel. The panel itself carries no padding, because the header is a
|
|
416
|
+
full-width sticky bar and each pane below it pads itself; anything else
|
|
417
|
+
written into the panel wraps itself in .detail-pad. */
|
|
418
|
+
.detail-panel { border-top: 1px solid var(--border2); background: var(--surface); max-height: 40vh; overflow-y: auto; flex-shrink: 0; }
|
|
334
419
|
.detail-panel h3 { font-size: 13px; margin-bottom: 6px; }
|
|
335
|
-
.detail-
|
|
336
|
-
|
|
337
|
-
.detail-
|
|
338
|
-
.
|
|
339
|
-
.
|
|
420
|
+
.detail-pad { padding: 8px 12px; }
|
|
421
|
+
|
|
422
|
+
.detail-head { display: flex; align-items: center; gap: 8px; padding: 7px 12px; border-bottom: 1px solid var(--border); background: var(--surface); position: sticky; top: 0; z-index: 2; flex-shrink: 0; }
|
|
423
|
+
.detail-head .avatar { width: 20px; height: 20px; font-size: 8px; }
|
|
424
|
+
.detail-who { display: flex; align-items: baseline; gap: 6px; min-width: 0; }
|
|
425
|
+
.detail-author { font-size: 12px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
426
|
+
.detail-when { font-size: 11px; color: var(--subtext); white-space: nowrap; }
|
|
427
|
+
.detail-head-actions { margin-left: auto; display: flex; align-items: center; gap: 4px; flex-shrink: 0; }
|
|
428
|
+
|
|
429
|
+
/* The panel's only dismiss. Escape closes it too, but a touch screen has no
|
|
430
|
+
Escape key, so a panel opened by a tap could not be put away at all — it
|
|
431
|
+
sits over a third of the graph until another commit is tapped. It took the
|
|
432
|
+
place of the two hash chips, which were a convenience: the metadata grid
|
|
433
|
+
two lines below carries the hash and the parents in full.
|
|
434
|
+
|
|
435
|
+
It stays 24px of ink on every device. The generic pointer: coarse button rule
|
|
436
|
+
further down took it to 32, which is still under the 44 a finger needs, and
|
|
437
|
+
growing the button itself would grow the header with it - this sits in a
|
|
438
|
+
sticky bar above a panel that is already capped at 35vh on a phone. So the
|
|
439
|
+
tap area grows instead, through an invisible overlay, which is the same thing
|
|
440
|
+
the app does for its own 20px controls. Nothing else lives in
|
|
441
|
+
.detail-head-actions, and what it reaches on the left is the committed-when
|
|
442
|
+
text, which is not clickable. */
|
|
443
|
+
.detail-close { position: relative; display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; padding: 0; border: none; border-radius: 5px; background: none; color: var(--subtext); }
|
|
444
|
+
.detail-close:hover { background: var(--surface-hover); color: var(--text); }
|
|
445
|
+
|
|
446
|
+
/* Two columns when there is room. A commit message is hard-wrapped by whoever
|
|
447
|
+
wrote it, so on a wide panel it fills half the width and the rest of the row
|
|
448
|
+
is empty; the file list goes there instead of below the fold. Each pane pads
|
|
449
|
+
itself, because above the breakpoint each one also scrolls itself. */
|
|
450
|
+
.detail-grid { display: grid; grid-template-columns: minmax(0, 1fr); }
|
|
451
|
+
.detail-msg { padding: 12px 16px 16px; min-width: 0; }
|
|
452
|
+
.detail-files { padding: 8px 12px 12px; min-width: 0; }
|
|
453
|
+
/* Full hashes, both emails and both dates, in a label/value grid. One rule
|
|
454
|
+
under it separates the provenance from the message; the subject carries none
|
|
455
|
+
of its own, because two hairlines in a 360px panel is a lot of furniture and
|
|
456
|
+
14px semibold against 11.5px monospace already reads as two things. The rule
|
|
457
|
+
belongs here rather than to the body, which is capped at a readable measure
|
|
458
|
+
and would stop the border short of the pane for no visible reason. */
|
|
459
|
+
.detail-meta { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 5px 14px; align-items: baseline; font-size: 11.5px; margin-bottom: 14px; padding-bottom: 13px; border-bottom: 1px solid var(--border); }
|
|
460
|
+
.meta-label { color: var(--subtext); font-size: 11.5px; white-space: nowrap; }
|
|
461
|
+
.meta-cells { display: flex; flex-wrap: wrap; align-items: baseline; gap: 3px 14px; min-width: 0; }
|
|
462
|
+
.meta-value { min-width: 0; overflow-wrap: anywhere; cursor: pointer; }
|
|
463
|
+
.meta-value.mono { font-family: var(--mono-font); font-size: 11.5px; color: var(--subtext); }
|
|
464
|
+
/* The eight characters that identify the commit carry the contrast; the other
|
|
465
|
+
thirty-two are there to be copied, not read. */
|
|
466
|
+
.hash-lead { color: var(--text); font-weight: 600; }
|
|
467
|
+
.meta-name { color: var(--text); }
|
|
468
|
+
.meta-email { color: var(--subtext); margin-left: 6px; }
|
|
469
|
+
.meta-value:hover .hash-lead, .meta-value:hover .meta-name, .meta-value:hover .meta-email { color: var(--blue); }
|
|
470
|
+
.meta-value:hover { color: var(--blue); }
|
|
471
|
+
.meta-value.copied, .meta-value.copied .hash-lead, .meta-value.copied .meta-name, .meta-value.copied .meta-email { color: var(--green); }
|
|
472
|
+
.meta-value.copy-failed, .meta-value.copy-failed .hash-lead, .meta-value.copy-failed .meta-name, .meta-value.copy-failed .meta-email { color: var(--red); }
|
|
473
|
+
.meta-when { color: var(--subtext); font-variant-numeric: tabular-nums; }
|
|
474
|
+
.detail-subject { font-size: 15px; font-weight: 600; line-height: 1.4; letter-spacing: -0.1px; }
|
|
475
|
+
|
|
476
|
+
/* The body. A paragraph its author wrapped at 72 columns is set as prose in the
|
|
477
|
+
UI font and reflowed to the pane it is actually in — verbatim monospace gave
|
|
478
|
+
a ragged half-filled column of the wrong width. A list or an aligned block is
|
|
479
|
+
not reflowable and keeps its breaks and its monospace; splitCommitBody
|
|
480
|
+
decides which is which. */
|
|
481
|
+
.detail-text { margin-top: 13px; }
|
|
482
|
+
.msg-p { font-size: 13px; line-height: 1.7; max-width: 68ch; }
|
|
483
|
+
.msg-pre { font-family: var(--mono-font); font-size: 12px; line-height: 1.6; white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
484
|
+
/* The reset zeroes every margin, so the space between paragraphs is set here
|
|
485
|
+
rather than inherited from the browser's default for a p element. */
|
|
486
|
+
.msg-p + .msg-p, .msg-p + .msg-pre, .msg-pre + .msg-p, .msg-pre + .msg-pre { margin-top: 13px; }
|
|
487
|
+
|
|
488
|
+
/* One scrollbar per pane above the breakpoint. A long message and a long file
|
|
489
|
+
list are two lists of unrelated length, and scrolling the pair as one means
|
|
490
|
+
reaching the twentieth file by pushing the message off the screen. Below it
|
|
491
|
+
they are stacked, and two scrollers inside one short panel is a trap for a
|
|
492
|
+
thumb — so there the panel scrolls as a whole, as it always did. */
|
|
493
|
+
@media (min-width: 900px) {
|
|
494
|
+
.detail-panel.split { display: flex; flex-direction: column; overflow: hidden; }
|
|
495
|
+
.detail-grid.has-files { grid-template-columns: minmax(0, 1fr) minmax(260px, 38%); }
|
|
496
|
+
.detail-panel.split .detail-grid { flex: 1 1 auto; min-height: 0; }
|
|
497
|
+
.detail-panel.split .detail-grid > * { overflow-y: auto; overscroll-behavior: contain; min-height: 0; }
|
|
498
|
+
.detail-panel.split .detail-files { border-left: 1px solid var(--border); padding-top: 0; }
|
|
499
|
+
/* The pane's own top padding would sit above a sticky header, leaving a strip
|
|
500
|
+
for rows to scroll through; the header carries that space instead. */
|
|
501
|
+
.detail-panel.split .files-head { position: sticky; top: 0; background: var(--surface); z-index: 1; padding: 8px 0 3px; }
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
.file-list { margin-top: 8px; min-width: 0; }
|
|
505
|
+
.detail-files .file-list { margin-top: 0; }
|
|
506
|
+
.files-head { display: flex; align-items: center; gap: 8px; font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; color: var(--subtext); border-bottom: 1px solid var(--border); margin-bottom: 3px; }
|
|
507
|
+
.files-head .file-view-toggle { margin-bottom: 0; }
|
|
508
|
+
.files-total { margin-left: auto; font-family: var(--mono-font); font-size: 10px; letter-spacing: 0; text-transform: none; font-variant-numeric: tabular-nums; }
|
|
509
|
+
.files-total .add { color: var(--green); }
|
|
510
|
+
.files-total .del { color: var(--red); }
|
|
511
|
+
|
|
512
|
+
.file-item { display: flex; align-items: center; gap: 6px; padding: 2px 4px; border-radius: 4px; font-size: 11px; font-family: var(--mono-font); min-width: 0; }
|
|
513
|
+
.file-item.file-clickable { cursor: pointer; }
|
|
514
|
+
.file-item.file-clickable:hover { background: var(--surface-hover); }
|
|
515
|
+
/* Name first, then the directory it is in: the column is narrow, so what has
|
|
516
|
+
to survive the ellipsis is the file name. */
|
|
517
|
+
.file-item .file-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex-shrink: 0; max-width: 60%; }
|
|
518
|
+
/* The directory takes the slack, which is what leaves the stats and the row's
|
|
519
|
+
buttons together at the right edge instead of both claiming the same auto
|
|
520
|
+
margin and meeting somewhere in the middle. */
|
|
521
|
+
.file-item .file-dir { color: var(--subtle); font-size: 10px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; flex: 1 1 auto; }
|
|
340
522
|
.file-status { display: inline-block; width: 14px; text-align: center; font-weight: 700; font-size: 10px; }
|
|
341
523
|
.file-status-A { color: var(--green); }
|
|
342
524
|
.file-status-M { color: var(--yellow); }
|
|
@@ -355,7 +537,7 @@ button:active { background: var(--surface); }
|
|
|
355
537
|
.tree-dir-count { font-size: 11px; color: var(--subtle); }
|
|
356
538
|
|
|
357
539
|
/* File actions */
|
|
358
|
-
.file-actions { display: flex; gap: 2px; margin-left:
|
|
540
|
+
.file-actions { display: flex; gap: 2px; margin-left: 6px; flex-shrink: 0; }
|
|
359
541
|
.file-action-btn { min-width: 24px; min-height: 24px; padding: 0 4px; border: none; background: transparent; cursor: pointer; border-radius: 4px; font-size: 12px; color: var(--subtext); display: flex; align-items: center; justify-content: center; }
|
|
360
542
|
.file-action-btn:hover { background: var(--surface-hover); color: var(--text); }
|
|
361
543
|
.file-action-btn[data-action="discard"]:hover { color: var(--red); }
|
|
@@ -401,12 +583,12 @@ button:active { background: var(--surface); }
|
|
|
401
583
|
.remote-item { padding: 6px 0; border-bottom: 1px solid var(--border); font-size: 12px; }
|
|
402
584
|
.remote-item:last-child { border-bottom: none; }
|
|
403
585
|
.remote-item .remote-name { font-weight: 600; margin-bottom: 2px; }
|
|
404
|
-
.remote-item .remote-url { color: var(--subtext); font-family:
|
|
586
|
+
.remote-item .remote-url { color: var(--subtext); font-family: var(--mono-font); font-size: 11px; word-break: break-all; }
|
|
405
587
|
.remote-actions { display: flex; gap: 4px; margin-top: 4px; }
|
|
406
588
|
.add-remote-form { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; }
|
|
407
589
|
.add-remote-form input { background: var(--bg); color: var(--text); border: 1px solid var(--border2); border-radius: 4px; padding: 4px 6px; font-size: 12px; }
|
|
408
590
|
.issue-rule-row { display: flex; gap: 4px; align-items: center; margin-bottom: 4px; }
|
|
409
|
-
.issue-rule-row input { flex: 1; background: var(--bg); color: var(--text); border: 1px solid var(--border2); border-radius: 4px; padding: 3px 6px; font-size: 11px; font-family:
|
|
591
|
+
.issue-rule-row input { flex: 1; background: var(--bg); color: var(--text); border: 1px solid var(--border2); border-radius: 4px; padding: 3px 6px; font-size: 11px; font-family: var(--mono-font); }
|
|
410
592
|
.issue-rule-row input.rule-error { border-color: var(--red); }
|
|
411
593
|
.issue-rule-row .rule-remove { min-width: 24px; min-height: 24px; padding: 0; font-size: 14px; color: var(--red); border: none; }
|
|
412
594
|
@media (max-width: 768px) { .settings-panel { width: 100%; } }
|
|
@@ -440,22 +622,42 @@ button:active { background: var(--surface); }
|
|
|
440
622
|
.commit-row { height: 32px; }
|
|
441
623
|
.ctx-item { padding: 8px 12px; min-height: 36px; }
|
|
442
624
|
button { min-width: 32px; min-height: 32px; padding: 2px 6px; }
|
|
625
|
+
/* The one way out of the detail panel, and the only place 32 is not enough:
|
|
626
|
+
a phone has no Escape key, so a panel opened by a tap is closed by this
|
|
627
|
+
button or not at all. 10px each side takes the 24px box to 44, and the 32px
|
|
628
|
+
the rule above gives it to 52. */
|
|
629
|
+
.detail-close::before { content: ''; position: absolute; inset: -10px; }
|
|
443
630
|
#app { flex-direction: column; }
|
|
444
631
|
#toolbar { order: 10; border-bottom: none; border-top: 1px solid var(--border); padding: 2px 6px; }
|
|
445
632
|
#toolbar button { font-size: 10px; }
|
|
446
633
|
.branch-trigger { font-size: 10px !important; padding: 2px 6px !important; }
|
|
447
|
-
#graph-
|
|
634
|
+
#graph-area { order: 1; }
|
|
635
|
+
#graph-container { overflow-x: auto; overflow-y: auto; }
|
|
448
636
|
#find-bar { order: 0; }
|
|
449
637
|
#status-bar { order: 9; }
|
|
450
|
-
|
|
451
|
-
|
|
638
|
+
/* Enough for every column including the branch one, so a tablet scrolls the
|
|
639
|
+
table sideways rather than crushing it. */
|
|
640
|
+
#commit-list-wrapper { min-width: 880px; }
|
|
641
|
+
#graph-header { min-width: 880px; }
|
|
452
642
|
.commit-row.header-row { min-height: 20px; }
|
|
453
643
|
.detail-panel { order: 8; max-height: 35vh; }
|
|
454
644
|
}
|
|
455
|
-
/*
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
645
|
+
/* Phone layout: the graph and the message, with author/date/hash under the
|
|
646
|
+
subject. Six columns of fragments is not a list of commits, and 44px is the
|
|
647
|
+
minimum touch target this row has to be anyway. isNarrowLayout() in the
|
|
648
|
+
script uses this same 640px, because it decides where the ref badges go. */
|
|
649
|
+
@media (max-width: 640px) {
|
|
650
|
+
:root { --refs-col-w: 0px; }
|
|
651
|
+
/* The coarse-pointer rules above keep the whole table and scroll it
|
|
652
|
+
sideways, which is right for a tablet. A phone gets the stacked row
|
|
653
|
+
instead, so the minimum width that forces that scroll has to go. */
|
|
654
|
+
#commit-list-wrapper, #graph-header { min-width: 0; }
|
|
655
|
+
.commit-row { height: 44px; }
|
|
656
|
+
.commit-row.header-row { height: 24px; }
|
|
657
|
+
.col-refs, .col-changes, .col-author, .col-date, .col-hash { display: none; }
|
|
658
|
+
.col-message { gap: 1px; }
|
|
659
|
+
.msg-meta { display: block; font-size: 10px; color: var(--subtext); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
660
|
+
.col-message .ref-badge { max-width: 90px; overflow: hidden; text-overflow: ellipsis; }
|
|
459
661
|
}
|
|
460
662
|
`;
|
|
461
663
|
}
|
|
@@ -468,7 +670,10 @@ const NULL_VERTEX_ID = -1;
|
|
|
468
670
|
const GRAPH_COLORS = ['#4ec9b0','#569cd6','#c586c0','#ce9178','#dcdcaa','#4fc1ff','#d7ba7d','#9cdcfe','#b5cea8','#d16969'];
|
|
469
671
|
const graphConfig = {
|
|
470
672
|
colours: GRAPH_COLORS,
|
|
471
|
-
|
|
673
|
+
// Wide enough apart that two adjacent lanes' author avatars do not touch,
|
|
674
|
+
// and tall enough that an 18px avatar is the row rather than a dot in it.
|
|
675
|
+
grid: { x: 22, y: 30, offsetX: 14, offsetY: 15, expandY: 60 },
|
|
676
|
+
nodeR: 9,
|
|
472
677
|
style: 'rounded'
|
|
473
678
|
};
|
|
474
679
|
|
|
@@ -495,6 +700,8 @@ const state = {
|
|
|
495
700
|
loading: false,
|
|
496
701
|
uncommitted: null,
|
|
497
702
|
searchMatches: [],
|
|
703
|
+
/** hash -> {files, insertions, deletions}; arrives after the commits. */
|
|
704
|
+
stats: {},
|
|
498
705
|
searchIndex: -1,
|
|
499
706
|
settings: { ...DEFAULT_SETTINGS },
|
|
500
707
|
userDetails: { name: '', email: '' },
|
|
@@ -549,6 +756,20 @@ document.getElementById('btn-stash').innerHTML = ICONS.archive + ' <span class="
|
|
|
549
756
|
vscode.postMessage({ command: 'ready' });
|
|
550
757
|
|
|
551
758
|
// --- Message handler ---
|
|
759
|
+
/*
|
|
760
|
+
* The row's layout is decided in JS as well as in CSS (the ref badges live in
|
|
761
|
+
* their own column on a wide screen and inline on a narrow one), so crossing
|
|
762
|
+
* the breakpoint has to rebuild the rows. Without this, rotating a phone would
|
|
763
|
+
* leave the badges in a column that CSS has just hidden.
|
|
764
|
+
*/
|
|
765
|
+
let gWasNarrow = null;
|
|
766
|
+
window.addEventListener('resize', () => {
|
|
767
|
+
const narrow = isNarrowLayout();
|
|
768
|
+
if (gWasNarrow === narrow) return;
|
|
769
|
+
gWasNarrow = narrow;
|
|
770
|
+
if (state.commits.length > 0) renderCommitList();
|
|
771
|
+
});
|
|
772
|
+
|
|
552
773
|
window.addEventListener('message', (event) => {
|
|
553
774
|
const msg = event.data;
|
|
554
775
|
switch (msg.command) {
|
|
@@ -572,14 +793,31 @@ window.addEventListener('message', (event) => {
|
|
|
572
793
|
state.commits = state.commits.concat(msg.data);
|
|
573
794
|
} else {
|
|
574
795
|
state.commits = msg.data;
|
|
796
|
+
// Not an append means the list was replaced — a branch change, a
|
|
797
|
+
// refresh. Hashes are unique so keeping the old numbers would not be
|
|
798
|
+
// wrong, but nothing would ever read them again and nothing would ever
|
|
799
|
+
// drop them either: a long session switching branches grows a map of
|
|
800
|
+
// every commit it has ever looked at.
|
|
801
|
+
state.stats = {};
|
|
575
802
|
}
|
|
576
803
|
renderCommitList();
|
|
577
804
|
updateStatus();
|
|
578
805
|
state.loading = false;
|
|
579
806
|
document.getElementById('loading').classList.add('hidden');
|
|
580
807
|
break;
|
|
808
|
+
case 'loadCommitStats':
|
|
809
|
+
Object.assign(state.stats, msg.data);
|
|
810
|
+
applyCommitStats();
|
|
811
|
+
break;
|
|
581
812
|
case 'commitDetails':
|
|
582
|
-
|
|
813
|
+
// The answer has to still be the question. Arrow-keying down the list
|
|
814
|
+
// spawns one "git show" per row with nothing cancelling the last, so they
|
|
815
|
+
// come back in whatever order they finish: click A then B quickly and the
|
|
816
|
+
// panel could show A while B is highlighted, and deselecting A while its
|
|
817
|
+
// request was in flight popped the panel open again for a commit that is
|
|
818
|
+
// no longer selected. (No backticks in here: this whole script is a
|
|
819
|
+
// template literal, and one would end it hundreds of lines early.)
|
|
820
|
+
if (msg.data && msg.data.hash === state.selectedCommit) renderDetailPanel(msg.data);
|
|
583
821
|
break;
|
|
584
822
|
case 'refresh':
|
|
585
823
|
state.commits = msg.data;
|
|
@@ -705,6 +943,22 @@ document.getElementById('detail-panel').addEventListener('contextmenu', (e) => {
|
|
|
705
943
|
|
|
706
944
|
// --- File click delegation (opens diff tab) ---
|
|
707
945
|
document.getElementById('detail-panel').addEventListener('click', (e) => {
|
|
946
|
+
// The header's dismiss.
|
|
947
|
+
if (e.target.closest('.detail-close')) { e.stopPropagation(); closeDetailPanel(); return; }
|
|
948
|
+
// Metadata values. The clipboard write is silent, so the thing clicked says
|
|
949
|
+
// it happened.
|
|
950
|
+
const copySource = e.target.closest('[data-copy]');
|
|
951
|
+
if (copySource) {
|
|
952
|
+
e.stopPropagation();
|
|
953
|
+
// The green flash is the only evidence the user gets, so it waits for the
|
|
954
|
+
// answer rather than assuming one. A failure says so instead of lying.
|
|
955
|
+
copyText(copySource.dataset.copy).then((ok) => {
|
|
956
|
+
const cls = ok ? 'copied' : 'copy-failed';
|
|
957
|
+
copySource.classList.add(cls);
|
|
958
|
+
setTimeout(() => copySource.classList.remove(cls), 900);
|
|
959
|
+
});
|
|
960
|
+
return;
|
|
961
|
+
}
|
|
708
962
|
// File-level action buttons (stage/unstage/discard/open)
|
|
709
963
|
const actionBtn = e.target.closest('.file-action-btn');
|
|
710
964
|
if (actionBtn) {
|
|
@@ -1387,9 +1641,11 @@ class GBranch {
|
|
|
1387
1641
|
}
|
|
1388
1642
|
|
|
1389
1643
|
class GVertex {
|
|
1390
|
-
constructor(id, isStash) {
|
|
1644
|
+
constructor(id, isStash, author) {
|
|
1391
1645
|
this.id = id;
|
|
1392
1646
|
this.isStash = isStash;
|
|
1647
|
+
/** {name, email} of the commit's author, or null for a row that has none. */
|
|
1648
|
+
this._author = author || null;
|
|
1393
1649
|
this._x = 0;
|
|
1394
1650
|
this._children = [];
|
|
1395
1651
|
this._parents = [];
|
|
@@ -1432,6 +1688,16 @@ class GVertex {
|
|
|
1432
1688
|
setNotCommitted() { this._isCommitted = false; }
|
|
1433
1689
|
setCurrent() { this._isCurrent = true; }
|
|
1434
1690
|
|
|
1691
|
+
/*
|
|
1692
|
+
* The node is the author's avatar, ringed in the lane's colour — which is
|
|
1693
|
+
* what makes a graph readable as *who* rather than as a column of identical
|
|
1694
|
+
* dots. Initials, never a fetched image: an avatar service would mean handing
|
|
1695
|
+
* every committer's email address to a third party, which a self-hosted tool
|
|
1696
|
+
* must not do quietly.
|
|
1697
|
+
*
|
|
1698
|
+
* Rows with no author — uncommitted changes, a stash — keep a plain dot,
|
|
1699
|
+
* because inventing initials for them would be a lie.
|
|
1700
|
+
*/
|
|
1435
1701
|
draw(svg, config, expandOffset, overListener, outListener) {
|
|
1436
1702
|
if (this._onBranch === null) return;
|
|
1437
1703
|
const STASH_COLOR = '#808080';
|
|
@@ -1439,20 +1705,32 @@ class GVertex {
|
|
|
1439
1705
|
: this._isCommitted ? config.colours[this._onBranch.getColour() % config.colours.length] : '#808080';
|
|
1440
1706
|
const cx = (this._x * config.grid.x + config.grid.offsetX).toString();
|
|
1441
1707
|
const cy = (this.id * config.grid.y + config.grid.offsetY + (expandOffset ? config.grid.expandY : 0)).toString();
|
|
1708
|
+
const named = this._isCommitted && !this.isStash && !!this._author && !!this._author.name;
|
|
1709
|
+
const r = named ? (config.nodeR || 9) : 4.5;
|
|
1442
1710
|
|
|
1443
1711
|
const circle = document.createElementNS(SVG_NS, 'circle');
|
|
1444
1712
|
circle.dataset.id = this.id.toString();
|
|
1713
|
+
circle.dataset.r = r.toString();
|
|
1445
1714
|
circle.setAttribute('cx', cx);
|
|
1446
1715
|
circle.setAttribute('cy', cy);
|
|
1447
|
-
circle.setAttribute('r',
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
circle.setAttribute('fill', colour);
|
|
1453
|
-
}
|
|
1716
|
+
circle.setAttribute('r', r.toString());
|
|
1717
|
+
circle.setAttribute('stroke', colour);
|
|
1718
|
+
circle.setAttribute('stroke-width', named ? '2' : '0');
|
|
1719
|
+
circle.setAttribute('fill', named ? authorColor(this._author.email || this._author.name) : colour);
|
|
1720
|
+
if (this._isCurrent) circle.setAttribute('class', 'graphCurrent');
|
|
1454
1721
|
svg.appendChild(circle);
|
|
1455
1722
|
|
|
1723
|
+
if (named) {
|
|
1724
|
+
const label = document.createElementNS(SVG_NS, 'text');
|
|
1725
|
+
label.setAttribute('class', 'node-initials');
|
|
1726
|
+
label.setAttribute('x', cx);
|
|
1727
|
+
label.setAttribute('y', cy);
|
|
1728
|
+
label.setAttribute('text-anchor', 'middle');
|
|
1729
|
+
label.setAttribute('dominant-baseline', 'central');
|
|
1730
|
+
label.textContent = authorInitials(this._author.name);
|
|
1731
|
+
svg.appendChild(label);
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1456
1734
|
circle.addEventListener('mouseover', overListener);
|
|
1457
1735
|
circle.addEventListener('mouseout', outListener);
|
|
1458
1736
|
}
|
|
@@ -1469,7 +1747,7 @@ function graphLoadCommits(commits) {
|
|
|
1469
1747
|
const lookup = {};
|
|
1470
1748
|
for (let i = 0; i < commits.length; i++) {
|
|
1471
1749
|
lookup[commits[i].hash] = i;
|
|
1472
|
-
gVertices.push(new GVertex(i, !!commits[i]._isStash));
|
|
1750
|
+
gVertices.push(new GVertex(i, !!commits[i]._isStash, { name: commits[i].author, email: commits[i].authorEmail }));
|
|
1473
1751
|
}
|
|
1474
1752
|
gCommitLookup = lookup;
|
|
1475
1753
|
|
|
@@ -1593,7 +1871,7 @@ function graphVertexOver(e) {
|
|
|
1593
1871
|
if (id >= 0 && id < gVertices.length) {
|
|
1594
1872
|
const rows = document.querySelectorAll('.commit-row:not(.header-row)');
|
|
1595
1873
|
if (rows[id]) rows[id].classList.add('graph-hover');
|
|
1596
|
-
e.target.setAttribute('r',
|
|
1874
|
+
e.target.setAttribute('r', (Number(e.target.dataset.r || 4.5) + 1.5).toString());
|
|
1597
1875
|
}
|
|
1598
1876
|
}
|
|
1599
1877
|
function graphVertexOut(e) {
|
|
@@ -1602,7 +1880,7 @@ function graphVertexOut(e) {
|
|
|
1602
1880
|
if (id >= 0) {
|
|
1603
1881
|
const rows = document.querySelectorAll('.commit-row:not(.header-row)');
|
|
1604
1882
|
if (rows[id]) rows[id].classList.remove('graph-hover');
|
|
1605
|
-
e.target.setAttribute('r', '4');
|
|
1883
|
+
e.target.setAttribute('r', (e.target.dataset.r || '4.5'));
|
|
1606
1884
|
}
|
|
1607
1885
|
}
|
|
1608
1886
|
|
|
@@ -1663,6 +1941,18 @@ function getDisplayCommits() {
|
|
|
1663
1941
|
return commits;
|
|
1664
1942
|
}
|
|
1665
1943
|
|
|
1944
|
+
/*
|
|
1945
|
+
* Below this width the row carries the graph and the message only, with the
|
|
1946
|
+
* author, date and hash on a second line under the subject: six columns of
|
|
1947
|
+
* fragments on a phone is not a list of commits, and PPM's layout rules ask for
|
|
1948
|
+
* one column there. The value matches the max-width:640px block in the CSS —
|
|
1949
|
+
* both have to agree, because the JS decides where the ref badges are put and
|
|
1950
|
+
* the CSS decides which columns exist.
|
|
1951
|
+
*/
|
|
1952
|
+
function isNarrowLayout() {
|
|
1953
|
+
return typeof window.matchMedia === 'function' && window.matchMedia('(max-width: 640px)').matches;
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1666
1956
|
function renderCommitList() {
|
|
1667
1957
|
const container = document.getElementById('commit-list');
|
|
1668
1958
|
container.innerHTML = '';
|
|
@@ -1682,11 +1972,17 @@ function renderCommitList() {
|
|
|
1682
1972
|
row.className = 'commit-row' + (isVirtual ? ' virtual' : '') + (isStash ? ' stash-row' : '');
|
|
1683
1973
|
row.dataset.hash = commit.hash;
|
|
1684
1974
|
|
|
1975
|
+
// Branch / tag column. Below 640px there is no room for it, so the badges
|
|
1976
|
+
// go inline ahead of the subject instead — one home or the other, never
|
|
1977
|
+
// both, or the second copy's context menu would act on a hidden badge.
|
|
1978
|
+
const narrow = isNarrowLayout();
|
|
1979
|
+
const refsCol = document.createElement('div');
|
|
1980
|
+
refsCol.className = 'col-refs';
|
|
1981
|
+
|
|
1685
1982
|
// Graph spacer column (SVG overlays this area)
|
|
1686
1983
|
const graphCol = document.createElement('div');
|
|
1687
1984
|
graphCol.className = 'col-graph';
|
|
1688
1985
|
|
|
1689
|
-
// Message column with ref badges
|
|
1690
1986
|
const msgCol = document.createElement('div');
|
|
1691
1987
|
msgCol.className = 'col-message';
|
|
1692
1988
|
let badges = '';
|
|
@@ -1721,10 +2017,23 @@ function renderCommitList() {
|
|
|
1721
2017
|
badges += '<span class="ref-badge ref-' + ref.type + '" data-ref="' + escHtml(ref.name) + '">' + syncIcon + escHtml(ref.name) + '</span>';
|
|
1722
2018
|
});
|
|
1723
2019
|
}
|
|
1724
|
-
|
|
2020
|
+
if (!narrow) refsCol.innerHTML = badges;
|
|
2021
|
+
const meta = isVirtual || isStash ? ''
|
|
2022
|
+
: '<span class="msg-meta">' + escHtml(commit.author) + ' \u00b7 '
|
|
2023
|
+
+ escHtml(formatDate(commit.commitDate)) + ' \u00b7 ' + escHtml(commit.hash.substring(0, 7))
|
|
2024
|
+
+ '</span>';
|
|
2025
|
+
msgCol.innerHTML = '<span class="msg-subject">' + (narrow ? badges : '')
|
|
2026
|
+
+ formatCommitMessage(commit.message) + '</span>' + meta;
|
|
2027
|
+
|
|
2028
|
+
// The lane's colour, for the tick at the start of the message.
|
|
2029
|
+
const vertex = gVertices[idx];
|
|
2030
|
+
if (vertex && !isVirtual && !isStash) {
|
|
2031
|
+
row.style.setProperty('--lane', GRAPH_COLORS[vertex.getColour() % GRAPH_COLORS.length]);
|
|
2032
|
+
}
|
|
1725
2033
|
|
|
1726
2034
|
// Attach context menu and double-click to ref badges
|
|
1727
|
-
|
|
2035
|
+
row.appendChild(refsCol);
|
|
2036
|
+
row.querySelectorAll('.ref-badge').forEach(badge => {
|
|
1728
2037
|
const refName = badge.dataset.ref || badge.textContent.trim();
|
|
1729
2038
|
const refType = badge.className.includes('ref-head') ? 'head'
|
|
1730
2039
|
: badge.className.includes('ref-remote') ? 'remote'
|
|
@@ -1751,13 +2060,18 @@ function renderCommitList() {
|
|
|
1751
2060
|
}
|
|
1752
2061
|
});
|
|
1753
2062
|
|
|
2063
|
+
const changesCol = document.createElement('div');
|
|
2064
|
+
changesCol.className = 'col-changes';
|
|
2065
|
+
if (!isVirtual && !isStash) fillChangesCell(changesCol, state.stats[commit.hash]);
|
|
2066
|
+
|
|
1754
2067
|
const authorCol = document.createElement('div');
|
|
1755
2068
|
authorCol.className = 'col-author';
|
|
1756
2069
|
if (isVirtual || isStash) {
|
|
1757
2070
|
authorCol.textContent = '';
|
|
1758
2071
|
} else {
|
|
1759
|
-
|
|
1760
|
-
|
|
2072
|
+
// No avatar here any more — it is the graph node now, and twice on one
|
|
2073
|
+
// row was noise.
|
|
2074
|
+
authorCol.textContent = commit.author;
|
|
1761
2075
|
authorCol.addEventListener('mouseenter', () => showAuthorCard(authorCol, commit));
|
|
1762
2076
|
authorCol.addEventListener('mouseleave', hideAuthorCard);
|
|
1763
2077
|
}
|
|
@@ -1765,6 +2079,9 @@ function renderCommitList() {
|
|
|
1765
2079
|
const dateCol = document.createElement('div');
|
|
1766
2080
|
dateCol.className = 'col-date';
|
|
1767
2081
|
dateCol.textContent = isVirtual ? 'now' : isStash ? '' : formatDate(commit.commitDate);
|
|
2082
|
+
// The column is relative by default, so the exact moment goes in the title
|
|
2083
|
+
// rather than in a wider column.
|
|
2084
|
+
if (!isVirtual && !isStash) dateCol.title = new Date(commit.commitDate * 1000).toLocaleString();
|
|
1768
2085
|
|
|
1769
2086
|
const hashCol = document.createElement('div');
|
|
1770
2087
|
hashCol.className = 'col-hash';
|
|
@@ -1772,6 +2089,7 @@ function renderCommitList() {
|
|
|
1772
2089
|
|
|
1773
2090
|
row.appendChild(graphCol);
|
|
1774
2091
|
row.appendChild(msgCol);
|
|
2092
|
+
row.appendChild(changesCol);
|
|
1775
2093
|
row.appendChild(authorCol);
|
|
1776
2094
|
row.appendChild(dateCol);
|
|
1777
2095
|
row.appendChild(hashCol);
|
|
@@ -1803,6 +2121,68 @@ function renderCommitList() {
|
|
|
1803
2121
|
});
|
|
1804
2122
|
|
|
1805
2123
|
graphRender(-1);
|
|
2124
|
+
renderScrollMarkers();
|
|
2125
|
+
}
|
|
2126
|
+
|
|
2127
|
+
/*
|
|
2128
|
+
* A merge commit has no diffstat — that is git's default, not a failure — so an
|
|
2129
|
+
* absent entry leaves the cell empty rather than claiming it changed nothing.
|
|
2130
|
+
*/
|
|
2131
|
+
function fillChangesCell(cell, stat) {
|
|
2132
|
+
cell.textContent = '';
|
|
2133
|
+
if (!stat) return;
|
|
2134
|
+
if (stat.insertions > 0) {
|
|
2135
|
+
const add = document.createElement('span');
|
|
2136
|
+
add.className = 'ch-add';
|
|
2137
|
+
add.textContent = '+' + stat.insertions;
|
|
2138
|
+
cell.appendChild(add);
|
|
2139
|
+
}
|
|
2140
|
+
if (stat.deletions > 0) {
|
|
2141
|
+
const del = document.createElement('span');
|
|
2142
|
+
del.className = 'ch-del';
|
|
2143
|
+
del.textContent = '-' + stat.deletions;
|
|
2144
|
+
cell.appendChild(del);
|
|
2145
|
+
}
|
|
2146
|
+
cell.title = stat.files + (stat.files === 1 ? ' file changed' : ' files changed');
|
|
2147
|
+
}
|
|
2148
|
+
|
|
2149
|
+
/** Fill the column in place: the rows are already drawn, and rebuilding them
|
|
2150
|
+
* would throw away the scroll position and the open detail panel. */
|
|
2151
|
+
function applyCommitStats() {
|
|
2152
|
+
document.querySelectorAll('#commit-list .commit-row').forEach((row) => {
|
|
2153
|
+
const cell = row.querySelector('.col-changes');
|
|
2154
|
+
const hash = row.dataset.hash;
|
|
2155
|
+
if (cell && hash && hash !== 'uncommitted') fillChangesCell(cell, state.stats[hash]);
|
|
2156
|
+
});
|
|
2157
|
+
}
|
|
2158
|
+
|
|
2159
|
+
/*
|
|
2160
|
+
* One tick per interesting row, positioned by its index in the whole loaded
|
|
2161
|
+
* history rather than by pixels — the rows are a uniform height, so the two
|
|
2162
|
+
* agree, and an index needs no measuring and survives a resize.
|
|
2163
|
+
*
|
|
2164
|
+
* Search matches are drawn first so that the checked-out and selected ticks sit
|
|
2165
|
+
* on top of them when they land on the same row.
|
|
2166
|
+
*/
|
|
2167
|
+
function renderScrollMarkers() {
|
|
2168
|
+
const host = document.getElementById('scroll-markers');
|
|
2169
|
+
if (!host) return;
|
|
2170
|
+
host.innerHTML = '';
|
|
2171
|
+
const commits = getDisplayCommits();
|
|
2172
|
+
if (commits.length === 0) return;
|
|
2173
|
+
|
|
2174
|
+
const mark = (idx, kind) => {
|
|
2175
|
+
const el = document.createElement('div');
|
|
2176
|
+
el.className = 'scroll-marker sm-' + kind;
|
|
2177
|
+
el.style.top = ((idx + 0.5) / commits.length * 100) + '%';
|
|
2178
|
+
host.appendChild(el);
|
|
2179
|
+
};
|
|
2180
|
+
|
|
2181
|
+
for (let i = 0; i < state.searchMatches.length; i++) mark(state.searchMatches[i], 'search');
|
|
2182
|
+
for (let i = 0; i < commits.length; i++) {
|
|
2183
|
+
if (commits[i].hash === state.head) mark(i, 'head');
|
|
2184
|
+
if (commits[i].hash === state.selectedCommit) mark(i, 'selected');
|
|
2185
|
+
}
|
|
1806
2186
|
}
|
|
1807
2187
|
|
|
1808
2188
|
function selectCommit(hash) {
|
|
@@ -1813,6 +2193,7 @@ function selectCommit(hash) {
|
|
|
1813
2193
|
state.selectedCommit = null;
|
|
1814
2194
|
state.expandedCommit = null;
|
|
1815
2195
|
document.getElementById('detail-panel').classList.add('hidden');
|
|
2196
|
+
renderScrollMarkers();
|
|
1816
2197
|
return;
|
|
1817
2198
|
}
|
|
1818
2199
|
|
|
@@ -1820,6 +2201,7 @@ function selectCommit(hash) {
|
|
|
1820
2201
|
state.expandedCommit = hash;
|
|
1821
2202
|
const row = document.querySelector('[data-hash="' + CSS.escape(hash) + '"]');
|
|
1822
2203
|
if (row) row.classList.add('selected');
|
|
2204
|
+
renderScrollMarkers();
|
|
1823
2205
|
|
|
1824
2206
|
if (hash === 'uncommitted') {
|
|
1825
2207
|
renderUncommittedDetail();
|
|
@@ -1828,6 +2210,17 @@ function selectCommit(hash) {
|
|
|
1828
2210
|
vscode.postMessage({ command: 'requestCommitDetails', hash });
|
|
1829
2211
|
}
|
|
1830
2212
|
|
|
2213
|
+
/* One dismiss for the close button and for Escape. They have to leave exactly
|
|
2214
|
+
the same state behind: a row still marked selected, or a marker still in
|
|
2215
|
+
the scroll bar, outlives the panel that explained it. */
|
|
2216
|
+
function closeDetailPanel() {
|
|
2217
|
+
state.selectedCommit = null;
|
|
2218
|
+
state.expandedCommit = null;
|
|
2219
|
+
document.getElementById('detail-panel').classList.add('hidden');
|
|
2220
|
+
document.querySelectorAll('.commit-row.selected').forEach(el => el.classList.remove('selected'));
|
|
2221
|
+
renderScrollMarkers();
|
|
2222
|
+
}
|
|
2223
|
+
|
|
1831
2224
|
// --- File tree helpers ---
|
|
1832
2225
|
function buildFileTree(files) {
|
|
1833
2226
|
const root = { name: '', children: {}, files: [] };
|
|
@@ -1862,7 +2255,7 @@ function renderFileTree(node, depth, hash, parentHash, section) {
|
|
|
1862
2255
|
const sortedFiles = [...node.files].sort((a, b) => a.fileName.localeCompare(b.fileName));
|
|
1863
2256
|
for (const f of sortedFiles) {
|
|
1864
2257
|
html += '<div class="file-item file-clickable" style="padding-left:' + (depth * 16) + 'px" ';
|
|
1865
|
-
html += 'data-path="' + escHtml(f.path) + '" data-hash="' + escHtml(hash) + '" data-parent="' + escHtml(parentHash) + '">';
|
|
2258
|
+
html += 'data-path="' + escHtml(f.path) + '" data-hash="' + escHtml(hash) + '" data-parent="' + escHtml(parentHash || '') + '">';
|
|
1866
2259
|
html += '<span class="file-status file-status-' + escHtml(f.status) + '">' + escHtml(f.status) + '</span>';
|
|
1867
2260
|
html += '<span class="file-name">' + escHtml(f.fileName) + '</span>';
|
|
1868
2261
|
if (f.additions > 0 || f.deletions > 0) {
|
|
@@ -1877,14 +2270,27 @@ function renderFileTree(node, depth, hash, parentHash, section) {
|
|
|
1877
2270
|
return html;
|
|
1878
2271
|
}
|
|
1879
2272
|
|
|
2273
|
+
/* Split so the file name can be shown before the directory it sits in: the
|
|
2274
|
+
list lives in a narrow column, and truncating from the end has to eat the
|
|
2275
|
+
path, not the name. */
|
|
2276
|
+
function basename(path) {
|
|
2277
|
+
const i = String(path).lastIndexOf('/');
|
|
2278
|
+
return i === -1 ? String(path) : String(path).slice(i + 1);
|
|
2279
|
+
}
|
|
2280
|
+
function dirname(path) {
|
|
2281
|
+
const i = String(path).lastIndexOf('/');
|
|
2282
|
+
return i === -1 ? '' : String(path).slice(0, i);
|
|
2283
|
+
}
|
|
2284
|
+
|
|
1880
2285
|
function renderFileListHtml(files, hash, parentHash, section) {
|
|
1881
2286
|
if (state.fileViewMode === 'tree') {
|
|
1882
2287
|
return renderFileTree(buildFileTree(files), 0, hash, parentHash, section);
|
|
1883
2288
|
}
|
|
1884
2289
|
return files.map(f =>
|
|
1885
|
-
'<div class="file-item file-clickable" data-path="' + escHtml(f.path) + '" data-hash="' + escHtml(hash) + '" data-parent="' + escHtml(parentHash || '') + '">' +
|
|
2290
|
+
'<div class="file-item file-clickable" title="' + escHtml(f.path) + '" data-path="' + escHtml(f.path) + '" data-hash="' + escHtml(hash) + '" data-parent="' + escHtml(parentHash || '') + '">' +
|
|
1886
2291
|
'<span class="file-status file-status-' + escHtml(f.status) + '">' + escHtml(f.status) + '</span>' +
|
|
1887
|
-
'<span class="file-name">' + escHtml(f.path) + '</span>' +
|
|
2292
|
+
'<span class="file-name">' + escHtml(basename(f.path)) + '</span>' +
|
|
2293
|
+
(dirname(f.path) ? '<span class="file-dir">' + escHtml(dirname(f.path)) + '</span>' : '') +
|
|
1888
2294
|
'<span class="file-stat">' +
|
|
1889
2295
|
(f.additions > 0 ? '<span class="add">+' + f.additions + '</span> ' : '') +
|
|
1890
2296
|
(f.deletions > 0 ? '<span class="del">-' + f.deletions + '</span>' : '') +
|
|
@@ -1894,6 +2300,15 @@ function renderFileListHtml(files, hash, parentHash, section) {
|
|
|
1894
2300
|
).join('');
|
|
1895
2301
|
}
|
|
1896
2302
|
|
|
2303
|
+
/* A cap that does not say it capped states a wrong number as a fact: a commit
|
|
2304
|
+
touching ten thousand files rendered as "500 files changed", which is a
|
|
2305
|
+
sentence the panel has no evidence for. The message cap already spells its
|
|
2306
|
+
own truncation out, so this says it the same way. */
|
|
2307
|
+
function fileCountLabel(shown, omitted) {
|
|
2308
|
+
return shown + (shown === 1 ? ' file' : ' files') + ' changed'
|
|
2309
|
+
+ (omitted > 0 ? ' [… ' + omitted + ' more]' : '');
|
|
2310
|
+
}
|
|
2311
|
+
|
|
1897
2312
|
function renderFileActions(file, section) {
|
|
1898
2313
|
let html = '<span class="file-actions">';
|
|
1899
2314
|
if (section === 'unstaged') {
|
|
@@ -1917,9 +2332,10 @@ function fileViewToggleHtml() {
|
|
|
1917
2332
|
function renderUncommittedDetail() {
|
|
1918
2333
|
const panel = document.getElementById('detail-panel');
|
|
1919
2334
|
panel.classList.remove('hidden');
|
|
2335
|
+
panel.classList.remove('split');
|
|
1920
2336
|
const u = state.uncommitted;
|
|
1921
2337
|
if (!u) { panel.classList.add('hidden'); return; }
|
|
1922
|
-
let html = '<h3>Uncommitted Changes</h3>';
|
|
2338
|
+
let html = '<div class="detail-pad"><h3>Uncommitted Changes</h3>';
|
|
1923
2339
|
const hasFiles = u.staged.length > 0 || u.unstaged.length > 0 || (u.conflicted && u.conflicted.length > 0);
|
|
1924
2340
|
if (hasFiles) {
|
|
1925
2341
|
html += fileViewToggleHtml();
|
|
@@ -1957,7 +2373,7 @@ function renderUncommittedDetail() {
|
|
|
1957
2373
|
html += '<div class="commit-section">';
|
|
1958
2374
|
html += '<textarea id="commit-message" placeholder="Commit message..." rows="3"></textarea>';
|
|
1959
2375
|
html += '<div class="commit-actions"><button id="btn-commit" class="btn-sm btn-commit" disabled>Commit</button></div>';
|
|
1960
|
-
html += '</div>';
|
|
2376
|
+
html += '</div></div>';
|
|
1961
2377
|
panel.innerHTML = html;
|
|
1962
2378
|
wireCommitControls();
|
|
1963
2379
|
}
|
|
@@ -1983,30 +2399,129 @@ function wireCommitControls() {
|
|
|
1983
2399
|
}
|
|
1984
2400
|
|
|
1985
2401
|
// --- Detail panel ---
|
|
2402
|
+
|
|
2403
|
+
/* A value you can read in full and click to copy. Anything with a data-copy
|
|
2404
|
+
attribute is handled by the panel's one click delegate. */
|
|
2405
|
+
function copyable(inner, text, cls) {
|
|
2406
|
+
return '<span class="meta-value ' + cls + '" data-copy="' + escHtml(text)
|
|
2407
|
+
+ '" title="Click to copy">' + inner + '</span>';
|
|
2408
|
+
}
|
|
2409
|
+
|
|
2410
|
+
/* Forty hex characters in one run is not text anyone reads. The eight that
|
|
2411
|
+
identify the commit carry the contrast and the other thirty-two go quiet —
|
|
2412
|
+
the whole thing is still there, and still what a click copies. */
|
|
2413
|
+
function hashCell(hash) {
|
|
2414
|
+
const lead = String(hash).slice(0, 8);
|
|
2415
|
+
const tail = String(hash).slice(8);
|
|
2416
|
+
return copyable('<span class="hash-lead">' + escHtml(lead) + '</span>' + escHtml(tail), hash, 'mono');
|
|
2417
|
+
}
|
|
2418
|
+
|
|
2419
|
+
/* Name and email are two things, so they are told apart by weight rather than
|
|
2420
|
+
by angle brackets. What a click copies is still the canonical form, which is
|
|
2421
|
+
what git itself wants back. */
|
|
2422
|
+
function personCell(name, email) {
|
|
2423
|
+
return copyable(
|
|
2424
|
+
'<span class="meta-name">' + escHtml(name) + '</span><span class="meta-email">' + escHtml(email) + '</span>',
|
|
2425
|
+
name + ' <' + email + '>',
|
|
2426
|
+
'person',
|
|
2427
|
+
);
|
|
2428
|
+
}
|
|
2429
|
+
|
|
2430
|
+
/* The timezone is part of the answer: a commit stamped 09:13 means nothing
|
|
2431
|
+
without knowing whose morning that was. Spelled out component by component
|
|
2432
|
+
because dateStyle and timeStyle may not be combined with any other option —
|
|
2433
|
+
asking for those plus timeZoneName is a TypeError, and behind a catch it
|
|
2434
|
+
looks exactly like a locale that has no timezone name to give. */
|
|
2435
|
+
const WHEN_FORMAT = {
|
|
2436
|
+
year: 'numeric', month: 'short', day: 'numeric',
|
|
2437
|
+
hour: 'numeric', minute: '2-digit', second: '2-digit',
|
|
2438
|
+
timeZoneName: 'short',
|
|
2439
|
+
};
|
|
2440
|
+
function whenCell(ts) {
|
|
2441
|
+
const when = new Date(ts * 1000);
|
|
2442
|
+
return '<span class="meta-when">' + escHtml(when.toLocaleString(undefined, WHEN_FORMAT)) + '</span>';
|
|
2443
|
+
}
|
|
2444
|
+
|
|
2445
|
+
/* One label, one value, every row the same shape. The date used to ride along
|
|
2446
|
+
in a third column so that the two dates lined up — which put it a name's
|
|
2447
|
+
width away from the name it belonged to, and stranded it against the far edge
|
|
2448
|
+
of the pane on any row that had one. A date is a field like the others. */
|
|
2449
|
+
function metaRow(label, cells) {
|
|
2450
|
+
return '<div class="meta-label">' + escHtml(label) + '</div>'
|
|
2451
|
+
+ '<div class="meta-cells">' + cells.join('') + '</div>';
|
|
2452
|
+
}
|
|
2453
|
+
|
|
1986
2454
|
function renderDetailPanel(detail) {
|
|
1987
2455
|
state._lastDetail = detail;
|
|
1988
2456
|
const panel = document.getElementById('detail-panel');
|
|
1989
2457
|
panel.classList.remove('hidden');
|
|
1990
2458
|
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
2459
|
+
// Who and when, then the way out: the glanceable half.
|
|
2460
|
+
let head = '<div class="detail-head">' + avatarHtml(detail.author, detail.authorEmail);
|
|
2461
|
+
head += '<div class="detail-who">';
|
|
2462
|
+
head += '<span class="detail-author" title="' + escHtml(detail.authorEmail) + '">' + escHtml(detail.author) + '</span>';
|
|
2463
|
+
head += '<span class="detail-when" title="' + escHtml(new Date(detail.authorDate * 1000).toLocaleString()) + '">committed ' + escHtml(formatDate(detail.authorDate)) + '</span>';
|
|
1995
2464
|
if (detail.committer !== detail.author) {
|
|
1996
|
-
|
|
1997
|
-
}
|
|
2465
|
+
head += '<span class="detail-when" title="' + escHtml(detail.committerEmail) + '">via ' + escHtml(detail.committer) + '</span>';
|
|
2466
|
+
}
|
|
2467
|
+
head += '</div><div class="detail-head-actions">';
|
|
2468
|
+
head += '<button class="detail-close" title="Close (Esc)" aria-label="Close commit details">' + ICONS.x + '</button>';
|
|
2469
|
+
head += '</div></div>';
|
|
2470
|
+
|
|
2471
|
+
// Then the values themselves, which since the chips gave up the header is
|
|
2472
|
+
// where both hashes are. Every row is always here, in
|
|
2473
|
+
// the same order, even when the committer repeats the author: a field that
|
|
2474
|
+
// comes and goes cannot be found by muscle memory, and the two dates only
|
|
2475
|
+
// mean anything next to each other — which is what a rebase or an amend does
|
|
2476
|
+
// to them, and the reason both are separate fields rather than one date.
|
|
2477
|
+
let meta = '<div class="detail-meta">';
|
|
2478
|
+
meta += metaRow('Commit', [hashCell(detail.hash)]);
|
|
1998
2479
|
if (detail.parents.length > 0) {
|
|
1999
|
-
|
|
2480
|
+
meta += metaRow(detail.parents.length > 1 ? 'Parents' : 'Parent', detail.parents.map(hashCell));
|
|
2481
|
+
}
|
|
2482
|
+
meta += metaRow('Author', [personCell(detail.author, detail.authorEmail)]);
|
|
2483
|
+
meta += metaRow('Author date', [whenCell(detail.authorDate)]);
|
|
2484
|
+
meta += metaRow('Committer', [personCell(detail.committer, detail.committerEmail)]);
|
|
2485
|
+
meta += metaRow('Commit date', [whenCell(detail.commitDate)]);
|
|
2486
|
+
meta += '</div>';
|
|
2487
|
+
|
|
2488
|
+
// The subject carries the weight; the body keeps the author's own wrapping.
|
|
2489
|
+
const message = String(detail.message || '');
|
|
2490
|
+
const firstBreak = message.indexOf('\\n');
|
|
2491
|
+
const subject = firstBreak === -1 ? message : message.slice(0, firstBreak);
|
|
2492
|
+
const body = firstBreak === -1 ? '' : message.slice(firstBreak + 1).replace(/^\\n+/, '').replace(/\\s+$/, '');
|
|
2493
|
+
let left = '<div class="detail-msg">' + meta + '<div class="detail-subject">' + formatCommitMessage(subject) + '</div>';
|
|
2494
|
+
if (body) {
|
|
2495
|
+
// A paragraph the author wrapped at 72 columns is reflowed to the pane it
|
|
2496
|
+
// is actually in; a list or an aligned block keeps every break it had.
|
|
2497
|
+
let blocks = '';
|
|
2498
|
+
for (const block of splitCommitBody(body)) {
|
|
2499
|
+
blocks += block.kind === 'prose'
|
|
2500
|
+
? '<p class="msg-p">' + formatCommitMessage(block.text) + '</p>'
|
|
2501
|
+
: '<pre class="msg-pre">' + formatCommitMessage(block.text) + '</pre>';
|
|
2502
|
+
}
|
|
2503
|
+
left += '<div class="detail-text">' + blocks + '</div>';
|
|
2000
2504
|
}
|
|
2001
|
-
|
|
2505
|
+
left += '</div>';
|
|
2002
2506
|
|
|
2507
|
+
let right = '';
|
|
2003
2508
|
if (detail.fileChanges && detail.fileChanges.length > 0) {
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2509
|
+
let added = 0, removed = 0;
|
|
2510
|
+
for (const f of detail.fileChanges) { added += f.additions || 0; removed += f.deletions || 0; }
|
|
2511
|
+
right = '<div class="detail-files"><div class="file-list"><div class="files-head">'
|
|
2512
|
+
+ '<span>' + fileCountLabel(detail.fileChanges.length, detail.filesOmitted || 0) + '</span>'
|
|
2513
|
+
+ '<span class="files-total">'
|
|
2514
|
+
+ (added > 0 ? '<span class="add">+' + added + '</span> ' : '')
|
|
2515
|
+
+ (removed > 0 ? '<span class="del">-' + removed + '</span>' : '')
|
|
2516
|
+
+ '</span>' + fileViewToggleHtml() + '</div>'
|
|
2517
|
+
+ renderFileListHtml(detail.fileChanges, detail.hash, detail.parents[0] || '')
|
|
2518
|
+
+ '</div></div>';
|
|
2007
2519
|
}
|
|
2008
2520
|
|
|
2009
|
-
panel
|
|
2521
|
+
// The split panes each own a scrollbar, which means the panel must stop
|
|
2522
|
+
// owning one — and must give it back for any other view written into it.
|
|
2523
|
+
panel.classList.toggle('split', !!right);
|
|
2524
|
+
panel.innerHTML = head + '<div class="detail-grid' + (right ? ' has-files' : '') + '">' + left + right + '</div>';
|
|
2010
2525
|
}
|
|
2011
2526
|
|
|
2012
2527
|
// --- Context menu ---
|
|
@@ -2308,8 +2823,44 @@ function promptResetMode(hash) {
|
|
|
2308
2823
|
});
|
|
2309
2824
|
}
|
|
2310
2825
|
|
|
2311
|
-
|
|
2312
|
-
|
|
2826
|
+
/* A panel is mounted sandbox="allow-scripts" with no allow-same-origin, so it
|
|
2827
|
+
runs at an OPAQUE ORIGIN — and the default Permissions Policy allowlist for
|
|
2828
|
+
clipboard-write is "self", which an opaque origin never matches. Measured in
|
|
2829
|
+
Chromium with the iframe focused and a real click: writeText rejects with
|
|
2830
|
+
NotAllowedError ("blocked because of a permissions policy"), while a textarea
|
|
2831
|
+
plus execCommand('copy') returns true and the text really does land on the
|
|
2832
|
+
system clipboard (pasted it back out to check). So every click-to-copy value
|
|
2833
|
+
in every panel had been copying nothing, and saying that it had.
|
|
2834
|
+
|
|
2835
|
+
The modern call is still tried first, for the day the policy changes. The
|
|
2836
|
+
textarea has to stay RENDERED — display:none or visibility:hidden give an
|
|
2837
|
+
empty selection and execCommand then copies nothing while still returning
|
|
2838
|
+
true — and setSelectionRange is what actually selects on iOS. */
|
|
2839
|
+
async function copyText(text) {
|
|
2840
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
2841
|
+
try {
|
|
2842
|
+
await navigator.clipboard.writeText(text);
|
|
2843
|
+
return true;
|
|
2844
|
+
} catch (e) { /* fall through */ }
|
|
2845
|
+
}
|
|
2846
|
+
const ta = document.createElement('textarea');
|
|
2847
|
+
ta.value = String(text == null ? '' : text);
|
|
2848
|
+
ta.setAttribute('readonly', '');
|
|
2849
|
+
ta.style.cssText = 'position:fixed;top:0;left:0;width:1px;height:1px;padding:0;border:none;opacity:0;';
|
|
2850
|
+
document.body.appendChild(ta);
|
|
2851
|
+
const selection = document.getSelection();
|
|
2852
|
+
const previous = selection && selection.rangeCount > 0 ? selection.getRangeAt(0) : null;
|
|
2853
|
+
try {
|
|
2854
|
+
ta.focus();
|
|
2855
|
+
ta.select();
|
|
2856
|
+
ta.setSelectionRange(0, ta.value.length);
|
|
2857
|
+
return document.execCommand('copy');
|
|
2858
|
+
} catch (e) {
|
|
2859
|
+
return false;
|
|
2860
|
+
} finally {
|
|
2861
|
+
ta.remove();
|
|
2862
|
+
if (selection && previous) { selection.removeAllRanges(); selection.addRange(previous); }
|
|
2863
|
+
}
|
|
2313
2864
|
}
|
|
2314
2865
|
|
|
2315
2866
|
// --- Dialog system ---
|
|
@@ -2390,37 +2941,7 @@ function setupLongPress(el, callback) {
|
|
|
2390
2941
|
}
|
|
2391
2942
|
|
|
2392
2943
|
// --- Text formatter (URLs, issues, commit hashes) ---
|
|
2393
|
-
|
|
2394
|
-
let safe = escHtml(msg);
|
|
2395
|
-
// Apply issue linking rules from settings
|
|
2396
|
-
const rules = state.settings.issueLinkingRules || [];
|
|
2397
|
-
for (const rule of rules) {
|
|
2398
|
-
if (!rule.pattern) continue;
|
|
2399
|
-
if (rule.pattern.length > 200) continue; // ReDoS guard
|
|
2400
|
-
try {
|
|
2401
|
-
const re = new RegExp(rule.pattern, 'g');
|
|
2402
|
-
if (rule.url) {
|
|
2403
|
-
safe = safe.replace(re, function(match) {
|
|
2404
|
-
let href = rule.url;
|
|
2405
|
-
for (let i = 1; i < arguments.length - 2; i++) {
|
|
2406
|
-
if (typeof arguments[i] === 'string') href = href.split('$' + i).join(arguments[i]);
|
|
2407
|
-
}
|
|
2408
|
-
return '<a class="commit-link" href="' + escHtml(href) + '" target="_blank" title="' + escHtml(href) + '">' + match + '</a>';
|
|
2409
|
-
});
|
|
2410
|
-
} else {
|
|
2411
|
-
safe = safe.replace(re, '<span class="commit-link" title="$&">$&</span>');
|
|
2412
|
-
}
|
|
2413
|
-
} catch (e) { /* invalid regex — skip */ }
|
|
2414
|
-
}
|
|
2415
|
-
// Short commit hashes
|
|
2416
|
-
safe = safe.replace(/\\b([0-9a-f]{7,40})\\b/g, '<span class="commit-link" title="$1">$1</span>');
|
|
2417
|
-
// URLs — skip if already inside an <a> tag
|
|
2418
|
-
safe = safe.replace(/(<a[^>]*>.*?<\\/a>)|(https?:\\/\\/[^\\s<]+)/g, (m, linked, url) => {
|
|
2419
|
-
if (linked) return linked;
|
|
2420
|
-
return '<a class="commit-link" href="' + url + '" target="_blank">' + url + '</a>';
|
|
2421
|
-
});
|
|
2422
|
-
return safe;
|
|
2423
|
-
}
|
|
2944
|
+
${COMMIT_MESSAGE_JS}
|
|
2424
2945
|
|
|
2425
2946
|
// --- Find widget ---
|
|
2426
2947
|
const findBar = document.getElementById('find-bar');
|
|
@@ -2531,6 +3052,7 @@ function doSearch(query) {
|
|
|
2531
3052
|
if (match) { state.searchMatches.push(idx); row.classList.add('search-match'); }
|
|
2532
3053
|
});
|
|
2533
3054
|
document.getElementById('find-count').textContent = state.searchMatches.length + ' match(es)';
|
|
3055
|
+
renderScrollMarkers();
|
|
2534
3056
|
if (state.searchMatches.length > 0) navigateSearch(0);
|
|
2535
3057
|
}
|
|
2536
3058
|
|
|
@@ -2551,6 +3073,7 @@ function clearSearch() {
|
|
|
2551
3073
|
state.searchIndex = -1;
|
|
2552
3074
|
findInput.value = '';
|
|
2553
3075
|
document.getElementById('find-count').textContent = '';
|
|
3076
|
+
renderScrollMarkers();
|
|
2554
3077
|
}
|
|
2555
3078
|
|
|
2556
3079
|
function clearSearchHighlights() {
|
|
@@ -2563,7 +3086,7 @@ document.addEventListener('keydown', (e) => {
|
|
|
2563
3086
|
if (e.key === 'Escape') {
|
|
2564
3087
|
hideContextMenu();
|
|
2565
3088
|
if (!findBar.classList.contains('hidden')) { findBar.classList.add('hidden'); clearSearch(); }
|
|
2566
|
-
else if (state.expandedCommit)
|
|
3089
|
+
else if (state.expandedCommit) closeDetailPanel();
|
|
2567
3090
|
}
|
|
2568
3091
|
});
|
|
2569
3092
|
|