@hienlh/ppm 0.19.12 → 0.20.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 +31 -0
- package/CLAUDE.md +8 -1
- package/assets/skills/ppm/SKILL.md +1 -1
- package/assets/skills/ppm/references/http-api.md +4 -1
- package/dist/web/assets/accounts-settings-section-BQyru31C.js +1 -0
- package/dist/web/assets/{ai-resource-editor-CqL-X2zu.js → ai-resource-editor-BgYIPGVP.js} +1 -1
- package/dist/web/assets/{ai-settings-section-CcqokdzF.js → ai-settings-section-Cywmtnps.js} +1 -1
- package/dist/web/assets/ai-settings-section-iBRCQvBR.js +1 -0
- package/dist/web/assets/{angular-html-C8nsn2VC.js → angular-html-BtJLNcMw.js} +1 -1
- package/dist/web/assets/{angular-ts-Caw9nRCH.js → angular-ts-CQiu5F_W.js} +1 -1
- package/dist/web/assets/api-client-QfreGirP.js +1 -0
- package/dist/web/assets/api-settings-D18Q4Ocp.js +1 -0
- package/dist/web/assets/{api-themes-BoOJPqwM.js → api-themes-sSv-6OLk.js} +1 -1
- package/dist/web/assets/{apl-Dhu1LORF.js → apl-CKZvgs-w.js} +1 -1
- package/dist/web/assets/{appearance-settings-section-2hZBnpPS.js → appearance-settings-section-mhu9T6Fk.js} +1 -1
- package/dist/web/assets/architecture-PBZL5I3N-DfR8k8Bc.js +1 -0
- package/dist/web/assets/{astro-DwkX9sLZ.js → astro-D0b8ssLD.js} +1 -1
- package/dist/web/assets/{audio-preview-CkGZO8Uy.js → audio-preview-B4E5_7TW.js} +1 -1
- package/dist/web/assets/{blade-D1fC9Osx.js → blade-DOIpINLw.js} +1 -1
- package/dist/web/assets/{bsl-pur-PRip.js → bsl-CJ7_JlQ8.js} +1 -1
- package/dist/web/assets/{button-2WCAS8dI.js → button-DX32ffge.js} +1 -1
- package/dist/web/assets/{cairo-DDJs9amR.js → cairo-5CpV2T1P.js} +1 -1
- package/dist/web/assets/chat-tab-Bs9OD6ss.js +15 -0
- package/dist/web/assets/{cobol-Bo0usdoM.js → cobol-BreMVrtc.js} +1 -1
- package/dist/web/assets/code-editor-Cv_pzHo7.js +7 -0
- package/dist/web/assets/{coffee-DBBsbX1e.js → coffee-D7DoKh5b.js} +1 -1
- package/dist/web/assets/{conflict-editor-NqFAJP47.js → conflict-editor-BnWaMSEa.js} +1 -1
- package/dist/web/assets/{context-menu-C13b1uT3.js → context-menu-CJL8L98s.js} +1 -1
- package/dist/web/assets/{cpp-6CTO78A-.js → cpp-BnWeWAoE.js} +1 -1
- package/dist/web/assets/{crystal-DAlvpAP7.js → crystal-D2pJbFUn.js} +1 -1
- package/dist/web/assets/{csv-preview-Dvriy18_.js → csv-preview-DG4YAabr.js} +1 -1
- package/dist/web/assets/{data-grid-overlay-editor-iRrpAV0e.js → data-grid-overlay-editor-CbBkhhJa.js} +1 -1
- package/dist/web/assets/{database-viewer-Bs2RMm5a.js → database-viewer-B-wA2qT-.js} +1 -1
- package/dist/web/assets/diff-viewer-DRtzdfXk.js +4 -0
- package/dist/web/assets/{docx-preview-Bgw838ry.js → docx-preview-Xy7SZ7Vf.js} +1 -1
- package/dist/web/assets/{edge-D0tZrpC7.js → edge-aqGMtbY3.js} +1 -1
- package/dist/web/assets/{edit-diff-preview-DUgmjNre.js → edit-diff-preview-6tzdk7Pn.js} +1 -1
- package/dist/web/assets/{elixir-C1E6-o4G.js → elixir-CsYMBLc5.js} +1 -1
- package/dist/web/assets/{elm-BKyTfeQX.js → elm-BU8C2gFp.js} +1 -1
- package/dist/web/assets/{erb-Cdnr4BMI.js → erb-Z_E49ab8.js} +1 -1
- package/dist/web/assets/{erlang-N1237mFq.js → erlang-BRqOjXH9.js} +1 -1
- package/dist/web/assets/{esm-ClNnmD9t.js → esm-B-vXxHUd.js} +1 -1
- package/dist/web/assets/{explorer-body-DfWunSf9.js → explorer-body-uh8oJp6J.js} +2 -2
- package/dist/web/assets/explorer-window-content-DLn_4egF.js +1 -0
- package/dist/web/assets/{extension-manager-section-Zc9yag2c.js → extension-manager-section-C_ZGYvWf.js} +1 -1
- package/dist/web/assets/{extension-webview-B5FayDPz.js → extension-webview-DbFWxJpj.js} +1 -1
- package/dist/web/assets/{files-settings-section-CxAtO3GL.js → files-settings-section-BrUbJglA.js} +1 -1
- package/dist/web/assets/{fortran-fixed-form-D_f9tVv0.js → fortran-fixed-form-CWbxmDGR.js} +1 -1
- package/dist/web/assets/{fsharp-DEJmdhc2.js → fsharp-DKMYbq6I.js} +1 -1
- package/dist/web/assets/{gdresource-Dmmrx2gP.js → gdresource-B3yFau4V.js} +1 -1
- package/dist/web/assets/{general-settings-section-9DfKrBns.js → general-settings-section-B9ZPZVso.js} +1 -1
- package/dist/web/assets/{git-commit-qwJ867ig.js → git-commit-BW0p3Ekk.js} +1 -1
- package/dist/web/assets/{git-log-panel-BsmDZurR.js → git-log-panel-C0jnhmgC.js} +1 -1
- package/dist/web/assets/{git-rebase-Dkv8Zw86.js → git-rebase-DrvmQCS7.js} +1 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-CPd78aaT.js +1 -0
- package/dist/web/assets/{glide-data-grid-BuYwXIoS.js → glide-data-grid-CZVKIum-.js} +6 -6
- package/dist/web/assets/{glimmer-js-CcrXn8lQ.js → glimmer-js-BY1XApJJ.js} +1 -1
- package/dist/web/assets/{glimmer-ts-CLab3lU7.js → glimmer-ts-DPZtxF8N.js} +1 -1
- package/dist/web/assets/{glsl-DXhkG9dU.js → glsl-DZ8G_Hvf.js} +1 -1
- package/dist/web/assets/{graphql-Cuqsx8r3.js → graphql-Ck06uCZy.js} +1 -1
- package/dist/web/assets/group-chat-tab-DnPtmbPc.js +1 -0
- package/dist/web/assets/{hack-C_CBVdBc.js → hack-pLogDwAs.js} +1 -1
- package/dist/web/assets/{haml-CBWfzZpv.js → haml-CBqeCQ2A.js} +1 -1
- package/dist/web/assets/{handlebars-4e8OR-VI.js → handlebars-B66JmVtp.js} +1 -1
- package/dist/web/assets/{html-C5X4urn_.js → html-Dk4wmfwC.js} +1 -1
- package/dist/web/assets/{html-derivative-nH-jXfsL.js → html-derivative-CJWqfN51.js} +1 -1
- package/dist/web/assets/{http-CQgaG7eu.js → http-CexhotTh.js} +1 -1
- package/dist/web/assets/{hurl-B8gidJhu.js → hurl-BScKFk3Z.js} +1 -1
- package/dist/web/assets/{hxml-BAK94GX8.js → hxml-DAle7vtu.js} +1 -1
- package/dist/web/assets/{image-preview-1kGj4W9R.js → image-preview-fuU4h2nr.js} +1 -1
- package/dist/web/assets/index-CHQAPZIX.js +52 -0
- package/dist/web/assets/index-CoaVwAF6.css +2 -0
- package/dist/web/assets/info-3K5VOQVL-BciNds6R.js +1 -0
- package/dist/web/assets/{jinja-BuAjvj8M.js → jinja-yHpyD80t.js} +1 -1
- package/dist/web/assets/{jira-watcher-section-rJVz40wR.js → jira-watcher-section-Bjldyhru.js} +1 -1
- package/dist/web/assets/{jison-Dhhs26-L.js → jison-BY6WLJVu.js} +1 -1
- package/dist/web/assets/{julia-gZavIyll.js → julia-Dd_iYQDT.js} +1 -1
- package/dist/web/assets/{just-DDHrFReO.js → just-BTuUKpxF.js} +1 -1
- package/dist/web/assets/keybindings-store-DBVXZKp8.js +1 -0
- package/dist/web/assets/{keyboard-shortcuts-section-Dv4e3M8h.js → keyboard-shortcuts-section-D7jaCPrv.js} +1 -1
- package/dist/web/assets/{latex-BdGZa8ov.js → latex-Dt3OUlMZ.js} +1 -1
- package/dist/web/assets/{liquid-urFfd9Fr.js → liquid-BxPJU2ix.js} +1 -1
- package/dist/web/assets/{lua-CcS6tnB5.js → lua-DEGIvn7d.js} +1 -1
- package/dist/web/assets/{markdown-renderer-CEloCR06.js → markdown-renderer-B4B5oxQS.js} +3 -3
- package/dist/web/assets/markdown-renderer-DwHh-jd_.js +1 -0
- package/dist/web/assets/{marko-CSQqk4oG.js → marko-BDxwBJur.js} +1 -1
- package/dist/web/assets/{mdc-CYIe80m_.js → mdc-B8aXPTg7.js} +1 -1
- package/dist/web/assets/{media-url-DBmHJRft.js → media-url-BMeBaB0q.js} +1 -1
- package/dist/web/assets/{mobile-bottom-sheet-B3uDlD79.js → mobile-bottom-sheet-CmySDB6I.js} +1 -1
- package/dist/web/assets/mobile-explorer-sheet-JJxZYWkV.js +1 -0
- package/dist/web/assets/{nextflow-C-W8vX8I.js → nextflow-DBVZnnT_.js} +1 -1
- package/dist/web/assets/{nginx-COGi5you.js → nginx-Crpyd-Do.js} +1 -1
- package/dist/web/assets/{nim-CPitYGel.js → nim-BSqTBYDj.js} +1 -1
- package/dist/web/assets/notification-store-CCWNUHsS.js +1 -0
- package/dist/web/assets/{notifications-settings-section-B2GkfB0l.js → notifications-settings-section-DbbX7-Qp.js} +1 -1
- package/dist/web/assets/{number-overlay-editor-C_eD-Bdr.js → number-overlay-editor-DRjTIcPH.js} +1 -1
- package/dist/web/assets/{overview-panel-Cm2HRtEz.js → overview-panel-DiiA3B_Z.js} +1 -1
- package/dist/web/assets/packet-RMMSAZCW-ClD3l2Bs.js +1 -0
- package/dist/web/assets/{panel-store-BRrnDO_i.js → panel-store-BmdcSBNb.js} +1 -1
- package/dist/web/assets/{pdf-preview-Dmz8jkZZ.js → pdf-preview-DxNjWa1v.js} +1 -1
- package/dist/web/assets/{perl-B0yW3hbb.js → perl-BU0kbB-K.js} +1 -1
- package/dist/web/assets/{php-BNMYN5Jp.js → php-D9n1ikHN.js} +1 -1
- package/dist/web/assets/pie-UPGHQEXC-PU6kD0rW.js +1 -0
- package/dist/web/assets/{postgres-viewer-BtZbfC3f.js → postgres-viewer-BScvk5ev.js} +3 -3
- package/dist/web/assets/{ppmbot-settings-section-OYfVG5-l.js → ppmbot-settings-section-jFA9Ncgo.js} +1 -1
- package/dist/web/assets/{process-table-DRu3NyO_.js → process-table-BexeYWQm.js} +1 -1
- package/dist/web/assets/{project-store-BuY7rxn_.js → project-store-wWZ6bjwq.js} +1 -1
- package/dist/web/assets/{proxy-settings-section-xMbrcyr8.js → proxy-settings-section-C_seKq56.js} +1 -1
- package/dist/web/assets/{pug-CP5fxsxD.js → pug-BOpaczDr.js} +1 -1
- package/dist/web/assets/{qml-CHGpQosY.js → qml-B-cQXhrg.js} +1 -1
- package/dist/web/assets/{query-audit-section-CC5Mf6qp.js → query-audit-section-BWXJmPDV.js} +1 -1
- package/dist/web/assets/radar-KQ55EAFF-4LzjL0Zt.js +1 -0
- package/dist/web/assets/{razor-Cfe0AYo4.js → razor-DFB0pNCh.js} +1 -1
- package/dist/web/assets/remote-desktop-mobile-sheet-B--PeL5i.js +2 -0
- package/dist/web/assets/{remote-desktop-mobile-view-B4BSHIxU.js → remote-desktop-mobile-view-jgn8nmQa.js} +1 -1
- package/dist/web/assets/{remote-desktop-readiness-gate-C0d0PSX4.js → remote-desktop-readiness-gate-CjDth-W_.js} +1 -1
- package/dist/web/assets/{remote-desktop-window-content-DjeCZzqq.js → remote-desktop-window-content-CrHAUA03.js} +1 -1
- package/dist/web/assets/{rst-DxSSGshb.js → rst-DAXedsgm.js} +1 -1
- package/dist/web/assets/{ruby-rbK5SsJa.js → ruby-BCO4polC.js} +1 -1
- package/dist/web/assets/{run-in-terminal-DHAvC1AG.js → run-in-terminal-wHKwAnWP.js} +1 -1
- package/dist/web/assets/{sas-DDM28fbb.js → sas-DfB5dcFC.js} +1 -1
- package/dist/web/assets/schedules-settings-section-DMFn3mYL.js +1 -0
- package/dist/web/assets/{scss-TDl-6ro4.js → scss-BknIVgn7.js} +1 -1
- package/dist/web/assets/{send-to-chat-BOyUWNKw.js → send-to-chat-BI7-fFpG.js} +1 -1
- package/dist/web/assets/{separator-BzGoTlR7.js → separator-W_hJcXPk.js} +1 -1
- package/dist/web/assets/{settings-body-CXTQwhbM.js → settings-body-Dz2vPTEP.js} +2 -2
- package/dist/web/assets/settings-store-CzLnTR8p.js +2 -0
- package/dist/web/assets/settings-tab-B5QbsvS4.js +1 -0
- package/dist/web/assets/settings-window-content-iM2bPoX5.js +1 -0
- package/dist/web/assets/{shaderlab-CCvmDgGS.js → shaderlab-CPu31TpY.js} +1 -1
- package/dist/web/assets/{shellsession-BzKS9yKh.js → shellsession-CFX4c_TM.js} +1 -1
- package/dist/web/assets/{soy-QfO35nDh.js → soy-rz9hilgm.js} +1 -1
- package/dist/web/assets/{sparql-D29RDfp2.js → sparql-Bo8ZDeqE.js} +1 -1
- package/dist/web/assets/{sql-query-editor-B-VwfyTf.js → sql-query-editor-C0mGgLfX.js} +1 -1
- package/dist/web/assets/{sqlite-viewer-CpUhDEhN.js → sqlite-viewer-Bs3RKf1X.js} +1 -1
- package/dist/web/assets/{stata-Be6j_SNv.js → stata-B5J-QMyE.js} +1 -1
- package/dist/web/assets/{streaming-store-9_9j70zt.js → streaming-store-9XVmAMfV.js} +1 -1
- package/dist/web/assets/{surrealql-CykFjgNG.js → surrealql-Caw1ADnG.js} +1 -1
- package/dist/web/assets/{svelte-CdBF8z4-.js → svelte-D3oMVcxz.js} +1 -1
- package/dist/web/assets/{system-monitor-body-DzWXd1wM.js → system-monitor-body-DvUmEdNe.js} +2 -2
- package/dist/web/assets/system-monitor-tab-CHZ5F_ZE.js +1 -0
- package/dist/web/assets/{system-monitor-window-content-DIIP3dff.js → system-monitor-window-content-DjzqoeCN.js} +1 -1
- package/dist/web/assets/{tab-host-window-content-CLdcz84K.js → tab-host-window-content-DKeAr5mD.js} +1 -1
- package/dist/web/assets/{tab-store-PZsP5ZwG.js → tab-store-1xYJb0gM.js} +1 -1
- package/dist/web/assets/team-member-sheet-Cl9ZB3dl.js +2 -0
- package/dist/web/assets/team-member-window-content-DfP7WdFD.js +1 -0
- package/dist/web/assets/{templ-BVHE2CNb.js → templ-BAdNyIOL.js} +1 -1
- package/dist/web/assets/{terminal-tab-BeKLpaPM.js → terminal-tab-J1Fqa_rm.js} +2 -2
- package/dist/web/assets/{tex-C8XwrbBw.js → tex-IQ8mELOR.js} +1 -1
- package/dist/web/assets/tool-cards-DQghSuKX.js +9 -0
- package/dist/web/assets/treemap-KZPCXAKY-DiumLNMg.js +1 -0
- package/dist/web/assets/{ts-tags-BIHk5AM0.js → ts-tags-D1DDO2jl.js} +1 -1
- package/dist/web/assets/{twig-YcE6Q2Ox.js → twig-C8Fd5RvV.js} +1 -1
- package/dist/web/assets/use-accounts-data-KWcMgRlc.js +1 -0
- package/dist/web/assets/{use-blob-url-De-ZtcpX.js → use-blob-url-CrwoZJUJ.js} +1 -1
- package/dist/web/assets/use-inline-blame-C1G-_NjC.js +8 -0
- package/dist/web/assets/{use-monaco-theme-Kj2OGDlm.js → use-monaco-theme-aa4-wjKQ.js} +1 -1
- package/dist/web/assets/{use-open-team-member-DWhhWdmJ.js → use-open-team-member-CSUU7Z8N.js} +1 -1
- package/dist/web/assets/{use-remote-desktop-display-choice-8AkPpM_0.js → use-remote-desktop-display-choice-B-TpQ-Ua.js} +1 -1
- package/dist/web/assets/{use-websocket-kR1omKO1.js → use-websocket-BBslmv3D.js} +1 -1
- package/dist/web/assets/{vendor-mermaid-CKKtYuTV.js → vendor-mermaid-CfY1-PkA.js} +1 -1
- package/dist/web/assets/{video-preview-BdV7LjbZ.js → video-preview-Bd0lcK3d.js} +1 -1
- package/dist/web/assets/{vue-q7m01V0h.js → vue-EvccXlkP.js} +1 -1
- package/dist/web/assets/{vue-html-D1y9feOj.js → vue-html-Dg0xUJCh.js} +1 -1
- package/dist/web/assets/{vue-vine-DrIGs03v.js → vue-vine-BdrMg72k.js} +1 -1
- package/dist/web/assets/{window-pip-registry-CSLhJfpL.js → window-pip-registry-CrCYYTTf.js} +1 -1
- package/dist/web/assets/{window-store-DGKf9rPG.js → window-store-D0HbQ1pM.js} +1 -1
- package/dist/web/assets/ws-auth-D111jj32.js +1 -0
- package/dist/web/assets/{xml-oyvc1920.js → xml-MmDclzxK.js} +1 -1
- package/dist/web/assets/{xsl-BbIGdNPF.js → xsl-DQEFs6Dd.js} +1 -1
- package/dist/web/index.html +23 -23
- package/dist/web/sw.js +1 -1
- package/docs/extension-development-guide.md +47 -0
- package/docs/project-roadmap.md +3 -1
- package/package.json +2 -2
- package/packages/ext-git-graph/package.json +23 -5
- package/packages/ext-git-graph/src/blame-parser.test.ts +154 -0
- package/packages/ext-git-graph/src/blame-parser.ts +148 -0
- package/packages/ext-git-graph/src/blame-view.ts +389 -0
- package/packages/ext-git-graph/src/commit-search.test.ts +118 -0
- package/packages/ext-git-graph/src/commit-search.ts +102 -0
- package/packages/ext-git-graph/src/compare-args.test.ts +111 -0
- package/packages/ext-git-graph/src/compare-args.ts +109 -0
- package/packages/ext-git-graph/src/compare-view.ts +343 -0
- package/packages/ext-git-graph/src/extension-integration.test.ts +6 -3
- package/packages/ext-git-graph/src/extension.ts +202 -249
- package/packages/ext-git-graph/src/file-history-parser.test.ts +102 -0
- package/packages/ext-git-graph/src/file-history-parser.ts +89 -0
- package/packages/ext-git-graph/src/file-history-view.ts +386 -0
- package/packages/ext-git-graph/src/git-exec.ts +122 -0
- package/packages/ext-git-graph/src/git-state.ts +81 -0
- package/packages/ext-git-graph/src/panel-nav.ts +65 -0
- package/packages/ext-git-graph/src/panel-registry.ts +72 -0
- package/packages/ext-git-graph/src/ppm-api.ts +48 -0
- package/packages/ext-git-graph/src/rebase-todo.test.ts +196 -0
- package/packages/ext-git-graph/src/rebase-todo.ts +119 -0
- package/packages/ext-git-graph/src/rebase-view.ts +483 -0
- package/packages/ext-git-graph/src/reflog-parser.test.ts +93 -0
- package/packages/ext-git-graph/src/reflog-parser.ts +86 -0
- package/packages/ext-git-graph/src/reflog-view.ts +375 -0
- package/packages/ext-git-graph/src/register-view-command.ts +47 -0
- package/packages/ext-git-graph/src/submodule-parser.test.ts +79 -0
- package/packages/ext-git-graph/src/submodule-parser.ts +77 -0
- package/packages/ext-git-graph/src/types.ts +10 -1
- package/packages/ext-git-graph/src/webview-html.ts +438 -3
- package/packages/ext-git-graph/src/webview-shell.ts +236 -0
- package/src/providers/claude-agent-sdk.ts +7 -0
- package/src/providers/codex-app-server/codex-provider.ts +89 -1
- package/src/server/index.ts +1 -1
- package/src/server/routes/accounts.ts +25 -1
- package/src/server/routes/chat.ts +29 -1
- package/src/server/routes/codex-accounts.ts +41 -1
- package/src/server/routes/git.ts +97 -0
- package/src/server/routes/settings.ts +1 -0
- package/src/server/routes/teams.ts +14 -3
- package/src/services/account-selector.service.ts +125 -20
- package/src/services/account.service.ts +176 -54
- package/src/services/autostart-generator.ts +12 -1
- package/src/services/codex-account.service.ts +87 -13
- package/src/services/db.service.ts +58 -3
- package/src/services/extension.service.ts +55 -5
- package/src/services/git-blame/blame-porcelain.ts +89 -0
- package/src/services/git-blame/git-blame.service.ts +90 -0
- package/src/services/git-hunks/git-hunks.service.ts +264 -0
- package/src/services/git-hunks/unified-diff.ts +359 -0
- package/src/services/picked-account-binding.ts +57 -0
- package/src/services/team-member-activity/member-transcript-tail.ts +64 -0
- package/src/shared/blame.ts +79 -0
- package/src/web/components/chat/chat-history-bar.tsx +34 -5
- package/src/web/components/chat/chat-tab.tsx +120 -3
- package/src/web/components/chat/codex-usage-panel.tsx +132 -14
- package/src/web/components/chat/team-member-window-content.tsx +8 -39
- package/src/web/components/chat/usage-badge.tsx +176 -52
- package/src/web/components/editor/code-editor.tsx +33 -1
- package/src/web/components/editor/diff-viewer.tsx +83 -8
- package/src/web/components/editor/editor-toolbar.tsx +25 -1
- package/src/web/components/git/git-status-panel.tsx +46 -0
- package/src/web/components/git/hunk-selection.ts +132 -0
- package/src/web/components/git/hunk-stage-dialog.tsx +419 -0
- package/src/web/components/settings/accounts/account-bucket-row.tsx +19 -3
- package/src/web/components/settings/accounts/account-card.tsx +153 -33
- package/src/web/components/settings/accounts/account-hint.tsx +33 -0
- package/src/web/components/settings/accounts/account-usage-format.ts +26 -0
- package/src/web/components/settings/accounts/codex-accounts-section.tsx +24 -9
- package/src/web/components/settings/accounts/codex-usage-rows.tsx +2 -2
- package/src/web/components/settings/accounts/use-accounts-data.ts +32 -2
- package/src/web/components/settings/accounts/use-codex-accounts.ts +24 -2
- package/src/web/hooks/use-chat.ts +27 -0
- package/src/web/hooks/use-inline-blame.ts +175 -0
- package/src/web/hooks/use-member-transcript-tail.ts +95 -0
- package/src/web/lib/api-settings.ts +24 -0
- package/src/web/stores/settings-store.ts +12 -0
- package/dist/web/assets/accounts-settings-section-Cd1Nh04o.js +0 -1
- package/dist/web/assets/ai-settings-section-OaYGT5OF.js +0 -1
- package/dist/web/assets/api-client-BogqsFna.js +0 -1
- package/dist/web/assets/api-settings-BVfemzZW.js +0 -1
- package/dist/web/assets/architecture-PBZL5I3N-BZBxtErN.js +0 -1
- package/dist/web/assets/chat-tab-Cc1W2pTu.js +0 -15
- package/dist/web/assets/code-editor-gLRyCPWE.js +0 -7
- package/dist/web/assets/diff-viewer-ix1Ko0xk.js +0 -4
- package/dist/web/assets/explorer-window-content-B7HmteDI.js +0 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-BHxJbXfV.js +0 -1
- package/dist/web/assets/group-chat-tab-DmZ1vLk-.js +0 -1
- package/dist/web/assets/index-N-pnuwFP.css +0 -2
- package/dist/web/assets/index-yV5RgBDh.js +0 -52
- package/dist/web/assets/info-3K5VOQVL-DI4p3vbH.js +0 -1
- package/dist/web/assets/keybindings-store-88_Zkn5F.js +0 -1
- package/dist/web/assets/markdown-renderer-UhszMWSn.js +0 -1
- package/dist/web/assets/mobile-explorer-sheet-D9cqcU3X.js +0 -1
- package/dist/web/assets/notification-store-B6X6RYqQ.js +0 -1
- package/dist/web/assets/packet-RMMSAZCW-YNQWTi_-.js +0 -1
- package/dist/web/assets/pie-UPGHQEXC-E3Wiw76n.js +0 -1
- package/dist/web/assets/radar-KQ55EAFF-BPQJUKON.js +0 -1
- package/dist/web/assets/remote-desktop-mobile-sheet-DefltAPl.js +0 -2
- package/dist/web/assets/schedules-settings-section-D76Ob6tI.js +0 -1
- package/dist/web/assets/settings-store-DWUx3865.js +0 -2
- package/dist/web/assets/settings-tab-DToHQXfC.js +0 -1
- package/dist/web/assets/settings-window-content-B2rlq-Dv.js +0 -1
- package/dist/web/assets/system-monitor-tab-CuDpwAZU.js +0 -1
- package/dist/web/assets/team-member-sheet-DLrT_Kxo.js +0 -2
- package/dist/web/assets/team-member-window-content-S8cdLcg3.js +0 -1
- package/dist/web/assets/tool-cards-CLxrQlUs.js +0 -9
- package/dist/web/assets/treemap-KZPCXAKY-C2CvKBQR.js +0 -1
- package/dist/web/assets/use-accounts-data-snYPJED_.js +0 -1
- package/dist/web/assets/ws-auth-ZSZkAT6p.js +0 -1
- /package/dist/web/assets/{abap-Cq9nYQyd.js → abap-D-o-bvtI.js} +0 -0
- /package/dist/web/assets/{actionscript-3-DL8rUIy0.js → actionscript-3-D2tIzAm8.js} +0 -0
- /package/dist/web/assets/{ada-BY8TLuhY.js → ada-DxatBcxS.js} +0 -0
- /package/dist/web/assets/{andromeeda-CyVfHsS0.js → andromeeda-D0LK9YJ3.js} +0 -0
- /package/dist/web/assets/{apache-DTQUy73x.js → apache-B7uMPNiW.js} +0 -0
- /package/dist/web/assets/{apex-CiSZngrZ.js → apex-B7GiXrAr.js} +0 -0
- /package/dist/web/assets/{api-client-oY1tC3tM.js → api-client-CEYCBt_0.js} +0 -0
- /package/dist/web/assets/{applescript-CzuRDd3h.js → applescript-D_SIFkKe.js} +0 -0
- /package/dist/web/assets/{apply-theme-r8tKDrCR.js → apply-theme-BVXAUYoN.js} +0 -0
- /package/dist/web/assets/{ara-CSFV93kR.js → ara-C3iECgdg.js} +0 -0
- /package/dist/web/assets/{asciidoc-hOQNnXhz.js → asciidoc-gc8z_MMY.js} +0 -0
- /package/dist/web/assets/{asm-DdG53NGR.js → asm-27qeoPOI.js} +0 -0
- /package/dist/web/assets/{aurora-x-DJ7ORZG6.js → aurora-x-C5J9fWe1.js} +0 -0
- /package/dist/web/assets/{awk-B3FFpX_J.js → awk-B7GBzF92.js} +0 -0
- /package/dist/web/assets/{ayu-dark-D_P1CfTe.js → ayu-dark-XlnQhuLL.js} +0 -0
- /package/dist/web/assets/{ayu-light-Cz3YLUqH.js → ayu-light-DKLLilkU.js} +0 -0
- /package/dist/web/assets/{ayu-mirage-JcMq5tgJ.js → ayu-mirage-Dv-XOprJ.js} +0 -0
- /package/dist/web/assets/{ballerina-7Hs4Rfpk.js → ballerina-DACRT-sT.js} +0 -0
- /package/dist/web/assets/{bat-COgQdnFA.js → bat-BlQCq2XE.js} +0 -0
- /package/dist/web/assets/{beancount-BJ3CutBG.js → beancount-DYDztRI0.js} +0 -0
- /package/dist/web/assets/{berry-D7so2Cui.js → berry-BE5YKqvx.js} +0 -0
- /package/dist/web/assets/{bibtex-Ct3acHa1.js → bibtex-CAm7mgEm.js} +0 -0
- /package/dist/web/assets/{bicep-jXDwCQcm.js → bicep-BKgAbfVa.js} +0 -0
- /package/dist/web/assets/{bird2-CjLD2cna.js → bird2-yrBNbW1G.js} +0 -0
- /package/dist/web/assets/{c-CdgZojnL.js → c-txZFr4RM.js} +0 -0
- /package/dist/web/assets/{c3-DtpZYego.js → c3-D_DrzBUd.js} +0 -0
- /package/dist/web/assets/{cadence-CPLkixoh.js → cadence-CUFCEgG9.js} +0 -0
- /package/dist/web/assets/{can-open-in-ppm-BntPywsy.js → can-open-in-ppm-_yqa69dV.js} +0 -0
- /package/dist/web/assets/{catppuccin-frappe-Xt0_qJa9.js → catppuccin-frappe-Dn32MuhA.js} +0 -0
- /package/dist/web/assets/{catppuccin-latte-CJnbLec5.js → catppuccin-latte-Dewo7yDf.js} +0 -0
- /package/dist/web/assets/{catppuccin-macchiato-BPHP1wYB.js → catppuccin-macchiato-D8WOWZeo.js} +0 -0
- /package/dist/web/assets/{catppuccin-mocha-ssWJodGc.js → catppuccin-mocha-b0md48XM.js} +0 -0
- /package/dist/web/assets/{clarity-DB0408F6.js → clarity-Ct3UqLBj.js} +0 -0
- /package/dist/web/assets/{clojure-dpRPlAO4.js → clojure-CwP5aPEo.js} +0 -0
- /package/dist/web/assets/{cmake-r2VkeX9y.js → cmake-D5RF49e5.js} +0 -0
- /package/dist/web/assets/{codeowners-v5nQqHiB.js → codeowners-Cl5897L_.js} +0 -0
- /package/dist/web/assets/{codeql-BSSEhKRR.js → codeql-BKh1RCg0.js} +0 -0
- /package/dist/web/assets/{common-lisp-BXb2MMMA.js → common-lisp-BhHuD6t0.js} +0 -0
- /package/dist/web/assets/{coq-BjBNi73X.js → coq-Dco3qwD8.js} +0 -0
- /package/dist/web/assets/{csharp-CdPjZRbV.js → csharp-Dpjw9ZA9.js} +0 -0
- /package/dist/web/assets/{css-BuxbQOHL.js → css-CPe4cabH.js} +0 -0
- /package/dist/web/assets/{csv-Dj-eoZwH.js → csv-BAcTN8Kt.js} +0 -0
- /package/dist/web/assets/{csv-parser-CBbALsFU.js → csv-parser-DbxGKdMG.js} +0 -0
- /package/dist/web/assets/{cue-Dz73VksN.js → cue-Cr6y60TA.js} +0 -0
- /package/dist/web/assets/{cypher-CkVkDywZ.js → cypher-DWtqXXFP.js} +0 -0
- /package/dist/web/assets/{d-oU51Pbpf.js → d-BASiTZk4.js} +0 -0
- /package/dist/web/assets/{dark-plus-DcwvjjfO.js → dark-plus-56kmnrcl.js} +0 -0
- /package/dist/web/assets/{dart-DGvu8mcX.js → dart-C5mZnjoE.js} +0 -0
- /package/dist/web/assets/{data-grid-types-B6kUrF-M.js → data-grid-types-DeY60CLU.js} +0 -0
- /package/dist/web/assets/{dax-BmbjKQ90.js → dax-B4I2e7d8.js} +0 -0
- /package/dist/web/assets/{desktop-IRBSiFfh.js → desktop-DekGg0BT.js} +0 -0
- /package/dist/web/assets/{diff-Bdik2JR8.js → diff-CWnJBd73.js} +0 -0
- /package/dist/web/assets/{dist-Bv4zQpS1.js → dist-B0J2eYBz.js} +0 -0
- /package/dist/web/assets/{dist-C8YPBIOb.js → dist-BKOM_AoT.js} +0 -0
- /package/dist/web/assets/{docker-Bgdl0H8I.js → docker-DevchDxK.js} +0 -0
- /package/dist/web/assets/{dotenv-COe5NdnA.js → dotenv-DCvtdaGJ.js} +0 -0
- /package/dist/web/assets/{dracula-DjURaSAa.js → dracula-8U6Vkk3l.js} +0 -0
- /package/dist/web/assets/{dracula-soft-Do7d0bo-.js → dracula-soft-Bfeew5Wg.js} +0 -0
- /package/dist/web/assets/{dream-maker-DYwQvJIj.js → dream-maker-DDTJVuoy.js} +0 -0
- /package/dist/web/assets/{emacs-lisp-BXjlHyDW.js → emacs-lisp-CEHJqI4m.js} +0 -0
- /package/dist/web/assets/{everforest-dark-h8Q2loiX.js → everforest-dark-CHHLt7cq.js} +0 -0
- /package/dist/web/assets/{everforest-light-CdsJiL-9.js → everforest-light-TcTbK1sM.js} +0 -0
- /package/dist/web/assets/{fennel-D0CBtOvY.js → fennel-Bh4vsA1A.js} +0 -0
- /package/dist/web/assets/{fish-DzayfrMj.js → fish-D0T0fILG.js} +0 -0
- /package/dist/web/assets/{fluent-CatjkUfx.js → fluent-CgXDXdyy.js} +0 -0
- /package/dist/web/assets/{format-bytes-BulRSULX.js → format-bytes-Bc6SqF7M.js} +0 -0
- /package/dist/web/assets/{fortran-free-form-DM04ud0b.js → fortran-free-form-CUg3Xhz4.js} +0 -0
- /package/dist/web/assets/{gdscript-BLN5iZwf.js → gdscript-Cx056p_k.js} +0 -0
- /package/dist/web/assets/{gdshader-BK-1iZQs.js → gdshader-Dj1qP4I5.js} +0 -0
- /package/dist/web/assets/{genie-BGPOFWDN.js → genie-SBt15wn7.js} +0 -0
- /package/dist/web/assets/{gherkin-CdVOJ_JY.js → gherkin-bTJiHIrB.js} +0 -0
- /package/dist/web/assets/{github-dark-B_xW8TaY.js → github-dark-BOPhyugs.js} +0 -0
- /package/dist/web/assets/{github-dark-default-h_iKq7CX.js → github-dark-default-Cktqe6NF.js} +0 -0
- /package/dist/web/assets/{github-dark-dimmed-nKDYE62T.js → github-dark-dimmed-CeWfII_B.js} +0 -0
- /package/dist/web/assets/{github-dark-high-contrast-l8I771QT.js → github-dark-high-contrast-CTx8LpdV.js} +0 -0
- /package/dist/web/assets/{github-light-DNhjkg8Z.js → github-light-DVsO5Un1.js} +0 -0
- /package/dist/web/assets/{github-light-default-DQ06uS3g.js → github-light-default-BiyPxicr.js} +0 -0
- /package/dist/web/assets/{github-light-high-contrast-qc2sZCVN.js → github-light-high-contrast-BPlnke0a.js} +0 -0
- /package/dist/web/assets/{gleam-DPPatkNH.js → gleam-CWSxvD0C.js} +0 -0
- /package/dist/web/assets/{gn-BnzGQTy5.js → gn-D7NUVJuv.js} +0 -0
- /package/dist/web/assets/{gnuplot-Ct1leoo3.js → gnuplot-c5uXmJKc.js} +0 -0
- /package/dist/web/assets/{go-qysCdbrn.js → go-CRsHi4Cw.js} +0 -0
- /package/dist/web/assets/{groovy-BkWIdrRQ.js → groovy-DpV0RA5r.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-hard-L6Iiybbl.js → gruvbox-dark-hard-DD9fguUN.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-medium-DbD3yl0b.js → gruvbox-dark-medium-BvcwpYJ-.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-soft-Ban2cHK3.js → gruvbox-dark-soft-48dCxX9M.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-hard-CK_SjS3e.js → gruvbox-light-hard-CjJ994Ip.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-medium-Bjgvhc2w.js → gruvbox-light-medium-DQJQZ_rV.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-soft-EWnyYGUu.js → gruvbox-light-soft-AqsC9mKw.js} +0 -0
- /package/dist/web/assets/{haskell-rT1N9nYr.js → haskell-B5V7yvmh.js} +0 -0
- /package/dist/web/assets/{haxe-Bbc91axo.js → haxe-LWTKfPKr.js} +0 -0
- /package/dist/web/assets/{hcl-D-3qJA4v.js → hcl-QmNZdAtI.js} +0 -0
- /package/dist/web/assets/{hjson-DWtewcyQ.js → hjson-CboJsAzK.js} +0 -0
- /package/dist/web/assets/{hlsl-C7t6hCrO.js → hlsl-Bd-DO_WL.js} +0 -0
- /package/dist/web/assets/{horizon-zJAGUQ3j.js → horizon-CU8Bi_ss.js} +0 -0
- /package/dist/web/assets/{horizon-bright-CRNyrJy8.js → horizon-bright-iSQ3_3jP.js} +0 -0
- /package/dist/web/assets/{houston-BFKa4W1-.js → houston-CTXv3Puz.js} +0 -0
- /package/dist/web/assets/{hy-W1hNjMBM.js → hy-Cd4hTidD.js} +0 -0
- /package/dist/web/assets/{image-gallery-BycaVWOu.js → image-gallery-J8h8IY0c.js} +0 -0
- /package/dist/web/assets/{imba-CPphaV-V.js → imba-BipCHEY2.js} +0 -0
- /package/dist/web/assets/{ini-CayaZ4vW.js → ini-Dd1KkMrB.js} +0 -0
- /package/dist/web/assets/{java-BPAE_9Ej.js → java-rGr0OQD8.js} +0 -0
- /package/dist/web/assets/{javascript-C5SKIVqe.js → javascript-DHhCH2TN.js} +0 -0
- /package/dist/web/assets/{json-C-0EUYmu.js → json-CMMLBCUf.js} +0 -0
- /package/dist/web/assets/{json5-BqVetEZL.js → json5-mPOwTOsB.js} +0 -0
- /package/dist/web/assets/{jsonc-DnjIzkUB.js → jsonc-Bie0E6D3.js} +0 -0
- /package/dist/web/assets/{jsonl-rbV-3EqO.js → jsonl-CpqHmtC1.js} +0 -0
- /package/dist/web/assets/{jsonnet-9hvXw4qr.js → jsonnet-D21Ms51e.js} +0 -0
- /package/dist/web/assets/{jssm-1ifXyIhu.js → jssm-BXnl1nur.js} +0 -0
- /package/dist/web/assets/{jsx-hPumywTY.js → jsx-Chnii_vB.js} +0 -0
- /package/dist/web/assets/{kanagawa-dragon-BCPjmoMO.js → kanagawa-dragon-DhxR0UjL.js} +0 -0
- /package/dist/web/assets/{kanagawa-lotus-DN2VNjy7.js → kanagawa-lotus-fupRg5wp.js} +0 -0
- /package/dist/web/assets/{kanagawa-wave-B8RZEsgc.js → kanagawa-wave-DSWbRS9M.js} +0 -0
- /package/dist/web/assets/{kdl-CkvjWQIa.js → kdl-DirGHqUy.js} +0 -0
- /package/dist/web/assets/{kotlin-D8cnRi5o.js → kotlin-DkhUVMDC.js} +0 -0
- /package/dist/web/assets/{kusto-CmeeBEJG.js → kusto-DU7OpovF.js} +0 -0
- /package/dist/web/assets/{laserwave--rtV8Mv9.js → laserwave-iLKb1ZCT.js} +0 -0
- /package/dist/web/assets/{lean-CS-Pyi42.js → lean-Cuvh_QAP.js} +0 -0
- /package/dist/web/assets/{less-Ddi_7qGm.js → less-B5tQZOdI.js} +0 -0
- /package/dist/web/assets/{lib-DIrxSyLb.js → lib-el9rA30D.js} +0 -0
- /package/dist/web/assets/{libesm-CYLrAeW3.js → libesm-CdttTIAd.js} +0 -0
- /package/dist/web/assets/{light-plus-CrEjpkkX.js → light-plus-DiHU2KL5.js} +0 -0
- /package/dist/web/assets/{llvm-Cm7ZXmAO.js → llvm-sCBTKCKN.js} +0 -0
- /package/dist/web/assets/{log-lW2sYxtp.js → log-DtyR34Ie.js} +0 -0
- /package/dist/web/assets/{logo-B-PiRb53.js → logo-CP91aQyG.js} +0 -0
- /package/dist/web/assets/{luau-8St0pC-R.js → luau-nfGo2ulP.js} +0 -0
- /package/dist/web/assets/{make-DbNdhH54.js → make-XbDn9GHe.js} +0 -0
- /package/dist/web/assets/{markdown-CYocYSGd.js → markdown-BB1_3xLr.js} +0 -0
- /package/dist/web/assets/{material-theme-CVBF1Cbi.js → material-theme-CIx_sxAy.js} +0 -0
- /package/dist/web/assets/{material-theme-darker-HFrlNZD8.js → material-theme-darker-DsQeueDD.js} +0 -0
- /package/dist/web/assets/{material-theme-lighter-BM-uz2ft.js → material-theme-lighter-x_lMi74j.js} +0 -0
- /package/dist/web/assets/{material-theme-ocean-PbvUez8m.js → material-theme-ocean-Bkgh25P8.js} +0 -0
- /package/dist/web/assets/{material-theme-palenight-B22Ck6qd.js → material-theme-palenight-CoOjdbti.js} +0 -0
- /package/dist/web/assets/{matlab-GFFNzucw.js → matlab-D6xZas_8.js} +0 -0
- /package/dist/web/assets/{mdx-BVnJzMDx.js → mdx-Cl5_YNoz.js} +0 -0
- /package/dist/web/assets/{mermaid-BlCmSTCM.js → mermaid-8Eow4jz_.js} +0 -0
- /package/dist/web/assets/{min-dark-B0PwlAfC.js → min-dark-D4DhjgRE.js} +0 -0
- /package/dist/web/assets/{min-light-C_H2ABKu.js → min-light-DzAckd0t.js} +0 -0
- /package/dist/web/assets/{mipsasm-C3rf2fo6.js → mipsasm-zI_zu0tj.js} +0 -0
- /package/dist/web/assets/{mojo-CDRuzM3o.js → mojo-DQnGGPHf.js} +0 -0
- /package/dist/web/assets/{monokai-KiebNy_Y.js → monokai-CrFoD2VD.js} +0 -0
- /package/dist/web/assets/{moonbit-CdVUHoWl.js → moonbit-DdphIGs6.js} +0 -0
- /package/dist/web/assets/{move-Dq9iKZRY.js → move-BUlkn0xW.js} +0 -0
- /package/dist/web/assets/{narrat-7y5L5sGM.js → narrat-CbOBdtCn.js} +0 -0
- /package/dist/web/assets/{nextflow-groovy-Cwcbkcrx.js → nextflow-groovy-CeVLUTuQ.js} +0 -0
- /package/dist/web/assets/{night-owl-TBgLPzj4.js → night-owl-Bm4LhUR1.js} +0 -0
- /package/dist/web/assets/{night-owl-light-Dehb9My2.js → night-owl-light-DtcBiMEf.js} +0 -0
- /package/dist/web/assets/{nix-BKj-gzoX.js → nix-DrdwQ70g.js} +0 -0
- /package/dist/web/assets/{nord-BF_r8PeX.js → nord-B0Head1v.js} +0 -0
- /package/dist/web/assets/{nushell-CeAjk9_E.js → nushell-CmUdltKn.js} +0 -0
- /package/dist/web/assets/{objective-c-Xl1OMcDp.js → objective-c-BGDR33XN.js} +0 -0
- /package/dist/web/assets/{objective-cpp-C4ZgzrRq.js → objective-cpp-Buc1Zhd0.js} +0 -0
- /package/dist/web/assets/{ocaml-CGj8IYTq.js → ocaml-xdzl9rRD.js} +0 -0
- /package/dist/web/assets/{odin-C4veqvW2.js → odin-DiUFngnd.js} +0 -0
- /package/dist/web/assets/{one-dark-pro-gAcWejsV.js → one-dark-pro-BURxmIp3.js} +0 -0
- /package/dist/web/assets/{one-light-B7E-UUXy.js → one-light-BMRMInAj.js} +0 -0
- /package/dist/web/assets/{openscad-C-wN9BEv.js → openscad-DAgLA5J3.js} +0 -0
- /package/dist/web/assets/{pascal-C8WBtxMs.js → pascal-CoDZxcRB.js} +0 -0
- /package/dist/web/assets/{pkl-BBfF4G2A.js → pkl-DzKM6BuM.js} +0 -0
- /package/dist/web/assets/{plastic-DFOAb89P.js → plastic-vF15LmNS.js} +0 -0
- /package/dist/web/assets/{plsql-BS2CEMMV.js → plsql-C1v07teA.js} +0 -0
- /package/dist/web/assets/{po-BKJDtPmy.js → po-CLLC8BuA.js} +0 -0
- /package/dist/web/assets/{poimandres-Cg3hL13e.js → poimandres-Uq0xysxM.js} +0 -0
- /package/dist/web/assets/{polar-B9ebiXUU.js → polar-2-luY1Pc.js} +0 -0
- /package/dist/web/assets/{portal-container-context-a399Hd9F.js → portal-container-context-DKymYOY-.js} +0 -0
- /package/dist/web/assets/{postcss-B5REwNY_.js → postcss-lbFhe0rz.js} +0 -0
- /package/dist/web/assets/{powerquery-stc0Gzkf.js → powerquery-RL8O9Wie.js} +0 -0
- /package/dist/web/assets/{powershell-jgd0ahJN.js → powershell-upFWhb-b.js} +0 -0
- /package/dist/web/assets/{prisma-CWuZjRhI.js → prisma-DBtYaWFk.js} +0 -0
- /package/dist/web/assets/{prolog-D5zNFE63.js → prolog-D8jSMykJ.js} +0 -0
- /package/dist/web/assets/{proto-B-6RrU7n.js → proto-BM4eadF7.js} +0 -0
- /package/dist/web/assets/{puppet-DONP8TDR.js → puppet-BQDQ3Snu.js} +0 -0
- /package/dist/web/assets/{purescript-DDJITf-A.js → purescript-Cj2aTX4s.js} +0 -0
- /package/dist/web/assets/{python-bmWy9j1-.js → python-BYpqq6u4.js} +0 -0
- /package/dist/web/assets/{qmldir-CKDwMt7X.js → qmldir-4uiTcwAu.js} +0 -0
- /package/dist/web/assets/{qss-DptQQzBn.js → qss-D5xB-Mqo.js} +0 -0
- /package/dist/web/assets/{r-BorKpJ8r.js → r-P6Ujy8rf.js} +0 -0
- /package/dist/web/assets/{racket-BaHpYVsZ.js → racket-COKgCFuM.js} +0 -0
- /package/dist/web/assets/{raku-DFJIV4k_.js → raku-z14Q9i7K.js} +0 -0
- /package/dist/web/assets/{react-X4jUntEf.js → react-D1Yg256j.js} +0 -0
- /package/dist/web/assets/{red-70oJGrkb.js → red-ObAvgvQ5.js} +0 -0
- /package/dist/web/assets/{reg-C4g235Dv.js → reg-Qv4f0JFA.js} +0 -0
- /package/dist/web/assets/{regexp-Cw1UVmti.js → regexp-CEOVGIzy.js} +0 -0
- /package/dist/web/assets/{rel-DeLhIz5L.js → rel-cWOyTUwd.js} +0 -0
- /package/dist/web/assets/{resolve-theme-b_yGlUFQ.js → resolve-theme-CMp5xxCW.js} +0 -0
- /package/dist/web/assets/{riscv-ChnTiBRK.js → riscv-BZDwx-lv.js} +0 -0
- /package/dist/web/assets/{ron-C5IWeqDn.js → ron-BpjosPS0.js} +0 -0
- /package/dist/web/assets/{rose-pine-DCR0ohYW.js → rose-pine-DhxfJWcm.js} +0 -0
- /package/dist/web/assets/{rose-pine-dawn-DvGZs1PR.js → rose-pine-dawn-BbGt9Wy_.js} +0 -0
- /package/dist/web/assets/{rose-pine-moon-BWACHjeS.js → rose-pine-moon-dj4Zxgik.js} +0 -0
- /package/dist/web/assets/{rosmsg-CQlIVazE.js → rosmsg-EciOaHo2.js} +0 -0
- /package/dist/web/assets/{rust-DGGVzWI8.js → rust-C_eH59O_.js} +0 -0
- /package/dist/web/assets/{sass-bj1C0rFI.js → sass-Dos3R5lL.js} +0 -0
- /package/dist/web/assets/{scala-MU1tLKDi.js → scala-8lVKsTvf.js} +0 -0
- /package/dist/web/assets/{scheme-CeGXtv7t.js → scheme-Bd3VdaZj.js} +0 -0
- /package/dist/web/assets/{sdbl-D0HLSGKp.js → sdbl-C4Yrjlgh.js} +0 -0
- /package/dist/web/assets/{shellscript-DkYiDvh1.js → shellscript-OFZbPKZH.js} +0 -0
- /package/dist/web/assets/{slack-dark-YqeHiGWH.js → slack-dark-aL6vlxZs.js} +0 -0
- /package/dist/web/assets/{slack-ochin-9Oe0DljJ.js → slack-ochin-BKt1E7sL.js} +0 -0
- /package/dist/web/assets/{smalltalk-CaaxedR7.js → smalltalk-D6qVBL1U.js} +0 -0
- /package/dist/web/assets/{snazzy-light-C8s2FHNC.js → snazzy-light-BrWerT0t.js} +0 -0
- /package/dist/web/assets/{solarized-dark-0ryY6oN3.js → solarized-dark-CzxTJgVQ.js} +0 -0
- /package/dist/web/assets/{solarized-light-bltgZeNh.js → solarized-light-BecaDAnG.js} +0 -0
- /package/dist/web/assets/{solidity-DOf5HmLZ.js → solidity-C9t7pZ84.js} +0 -0
- /package/dist/web/assets/{splunk-D1u4WHDW.js → splunk-CtxfFKtF.js} +0 -0
- /package/dist/web/assets/{sql-G8uQZljX.js → sql-7FE_Duz0.js} +0 -0
- /package/dist/web/assets/{ssh-config-rbXvly7Q.js → ssh-config-3Fr37ACC.js} +0 -0
- /package/dist/web/assets/{stylus-Cii1iADy.js → stylus-BP4mE1sO.js} +0 -0
- /package/dist/web/assets/{swift-DpIgonN4.js → swift-0uqTFouD.js} +0 -0
- /package/dist/web/assets/{synthwave-84-DjGLzU2b.js → synthwave-84-DQaNM3DI.js} +0 -0
- /package/dist/web/assets/{system-verilog-_Z0M0Hs1.js → system-verilog-DUIVrZHZ.js} +0 -0
- /package/dist/web/assets/{systemd-BhyDCh9P.js → systemd-CtNo_WIh.js} +0 -0
- /package/dist/web/assets/{talonscript-CNchgCRD.js → talonscript-B0HXaCVT.js} +0 -0
- /package/dist/web/assets/{tasl-m--W7Pyr.js → tasl-Cb7RAl11.js} +0 -0
- /package/dist/web/assets/{tcl-gY-M_2BC.js → tcl-B79aC6l5.js} +0 -0
- /package/dist/web/assets/{terraform-CB6CzMwo.js → terraform-75jJDjpW.js} +0 -0
- /package/dist/web/assets/{tokyo-night-COh9oRYi.js → tokyo-night-Cz9Uh9qb.js} +0 -0
- /package/dist/web/assets/{toml-Bg2bsY-v.js → toml-CD6y9A-z.js} +0 -0
- /package/dist/web/assets/{tsv-DceGDiJC.js → tsv-CXJMbBrb.js} +0 -0
- /package/dist/web/assets/{tsx-EccDYa7i.js → tsx-BuYN0I2t.js} +0 -0
- /package/dist/web/assets/{turtle-C_l2JhvX.js → turtle-DWH-Rjse.js} +0 -0
- /package/dist/web/assets/{typescript-C1ytmMcr.js → typescript-BPd-hsrR.js} +0 -0
- /package/dist/web/assets/{typespec-De2ztzG5.js → typespec-B0wvVykx.js} +0 -0
- /package/dist/web/assets/{typst-DNcS7gSr.js → typst-DWG2c48-.js} +0 -0
- /package/dist/web/assets/{use-is-mobile-CYW3FJFE.js → use-is-mobile-D1weanbj.js} +0 -0
- /package/dist/web/assets/{use-visual-viewport-D9O48HQt.js → use-visual-viewport-Bfl3Bxxb.js} +0 -0
- /package/dist/web/assets/{utils-C9OjCrou.js → utils-IrnQujwR.js} +0 -0
- /package/dist/web/assets/{v-DSMUsdMO.js → v-Ch6E4Egd.js} +0 -0
- /package/dist/web/assets/{vala-BEazLpkO.js → vala-CGVKwbPx.js} +0 -0
- /package/dist/web/assets/{vb-CV9pEJzW.js → vb-BIEbSxyT.js} +0 -0
- /package/dist/web/assets/{vendor-xterm-cuGQmUjN.js → vendor-xterm-YQlzIWv2.js} +0 -0
- /package/dist/web/assets/{verilog-DhXkLVMk.js → verilog-A-3pYPad.js} +0 -0
- /package/dist/web/assets/{vesper-n4IltYq-.js → vesper-CK1naZAV.js} +0 -0
- /package/dist/web/assets/{vhdl-CePjFbvs.js → vhdl-CVawmgMg.js} +0 -0
- /package/dist/web/assets/{viml-ChndFsm5.js → viml-CiWjaqBY.js} +0 -0
- /package/dist/web/assets/{vitesse-black-BytVdvhC.js → vitesse-black-PYXW4-BO.js} +0 -0
- /package/dist/web/assets/{vitesse-dark-83Wech0N.js → vitesse-dark-BU5s4AuG.js} +0 -0
- /package/dist/web/assets/{vitesse-light-Xg4wbeMO.js → vitesse-light-DgkIT-N7.js} +0 -0
- /package/dist/web/assets/{vyper-eW1NC3FG.js → vyper-D8cRDPp3.js} +0 -0
- /package/dist/web/assets/{wasm-BucxKwBZ.js → wasm-C02wlRbP.js} +0 -0
- /package/dist/web/assets/{wasm-Bhucqu1z.js → wasm-ClFJgzmT.js} +0 -0
- /package/dist/web/assets/{wenyan-BJFBuXuH.js → wenyan-B8plxIr8.js} +0 -0
- /package/dist/web/assets/{wgsl-6z4FUyMK.js → wgsl-CVujtm3V.js} +0 -0
- /package/dist/web/assets/{wikitext-BoDU6teJ.js → wikitext-CmcG9B69.js} +0 -0
- /package/dist/web/assets/{wit-DerMPpgt.js → wit--gl2Jium.js} +0 -0
- /package/dist/web/assets/{wolfram-D78v0Ap-.js → wolfram-6T_1D-NS.js} +0 -0
- /package/dist/web/assets/{x-YDu4m2Iq.js → x-CxoBA0po.js} +0 -0
- /package/dist/web/assets/{yaml-C4TIgIw4.js → yaml-CIVV9uA1.js} +0 -0
- /package/dist/web/assets/{zenscript-BydW-dLC.js → zenscript-CoT4yNdA.js} +0 -0
- /package/dist/web/assets/{zig-b9lTqL5E.js → zig-BWs2Qo3G.js} +0 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Navigation between this extension's panels.
|
|
3
|
+
*
|
|
4
|
+
* `createWebviewPanel` alone is not enough: the frontend only *creates a tab*
|
|
5
|
+
* for a `webview:create` that some browser tab explicitly asked for
|
|
6
|
+
* (`locallyDispatchedViews` in `use-extension-ws.ts`). A panel the extension
|
|
7
|
+
* opens on its own initiative would exist server-side with no tab to show it.
|
|
8
|
+
*
|
|
9
|
+
* So navigation goes the other way round: ask the frontend to open an extension
|
|
10
|
+
* tab for the target viewType. `extension-webview.tsx` then finds a tab with no
|
|
11
|
+
* panel and dispatches the matching command itself — which *is* a local
|
|
12
|
+
* dispatch, so the panel binds to the tab.
|
|
13
|
+
*
|
|
14
|
+
* That recovery dispatch only forwards the project path, so the actual target
|
|
15
|
+
* (file to blame, refs to compare) is stashed here and picked up by the panel
|
|
16
|
+
* when it opens. If the stash is empty the panel just shows its own picker.
|
|
17
|
+
*/
|
|
18
|
+
import type { ExtensionContext } from "@ppm/vscode-compat";
|
|
19
|
+
import type { VscodeApi } from "./git-exec.ts";
|
|
20
|
+
import { resolveProjectName } from "./ppm-api.ts";
|
|
21
|
+
|
|
22
|
+
const pendingTargets = new Map<string, unknown>();
|
|
23
|
+
|
|
24
|
+
function targetKey(viewType: string, projectPath: string): string {
|
|
25
|
+
return `${viewType} ${projectPath}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function setPendingTarget(viewType: string, projectPath: string, target: unknown): void {
|
|
29
|
+
pendingTargets.set(targetKey(viewType, projectPath), target);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Read and clear the target a navigation left for this panel. */
|
|
33
|
+
export function takePendingTarget<T>(viewType: string, projectPath: string): T | undefined {
|
|
34
|
+
const k = targetKey(viewType, projectPath);
|
|
35
|
+
const value = pendingTargets.get(k) as T | undefined;
|
|
36
|
+
pendingTargets.delete(k);
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface NavigateOptions {
|
|
41
|
+
vscode: VscodeApi;
|
|
42
|
+
context: ExtensionContext;
|
|
43
|
+
/** Target viewType, identical to the command that opens it. */
|
|
44
|
+
viewType: string;
|
|
45
|
+
title: string;
|
|
46
|
+
projectPath: string;
|
|
47
|
+
/** Handed to the target panel via the stash. */
|
|
48
|
+
target?: unknown;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function navigateToPanel(options: NavigateOptions): Promise<void> {
|
|
52
|
+
const { vscode, context, viewType, title, projectPath, target } = options;
|
|
53
|
+
if (target !== undefined) setPendingTarget(viewType, projectPath, target);
|
|
54
|
+
const projectName = await resolveProjectName(projectPath);
|
|
55
|
+
await vscode.window.openTab("extension", title, projectName, {
|
|
56
|
+
viewType,
|
|
57
|
+
extensionId: context.extensionId,
|
|
58
|
+
projectName,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Test seam. */
|
|
63
|
+
export function _resetPendingTargets(): void {
|
|
64
|
+
pendingTargets.clear();
|
|
65
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One live panel per (viewType, project).
|
|
3
|
+
*
|
|
4
|
+
* Keying by project alone would make opening Blame dispose the Git Graph panel
|
|
5
|
+
* of the same project, since both go through this registry. Keying by viewType
|
|
6
|
+
* alone would let one project's panel hijack another's tab across browser tabs
|
|
7
|
+
* — the bug fixed in v0.9.x. Both parts of the key are required.
|
|
8
|
+
*/
|
|
9
|
+
import type { ExtWebviewPanel, VscodeApi } from "./git-exec.ts";
|
|
10
|
+
|
|
11
|
+
const panels = new Map<string, ExtWebviewPanel>();
|
|
12
|
+
|
|
13
|
+
function key(viewType: string, projectPath: string): string {
|
|
14
|
+
// Written as an escape, never as the byte itself: one raw NUL makes git
|
|
15
|
+
// call the whole file binary, and every diff of it becomes "Binary files
|
|
16
|
+
// differ" — the file stops being reviewable at all.
|
|
17
|
+
return `${viewType}\u0000${projectPath}`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface OpenPanelOptions {
|
|
21
|
+
vscode: VscodeApi;
|
|
22
|
+
viewType: string;
|
|
23
|
+
title: string;
|
|
24
|
+
projectPath: string;
|
|
25
|
+
html: string;
|
|
26
|
+
onMessage: (msg: unknown, panel: ExtWebviewPanel) => void | Promise<void>;
|
|
27
|
+
/** Extra teardown for timers the caller owns. */
|
|
28
|
+
onDispose?: () => void;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function openPanel(options: OpenPanelOptions): ExtWebviewPanel {
|
|
32
|
+
const { vscode, viewType, title, projectPath, html, onMessage, onDispose } = options;
|
|
33
|
+
const k = key(viewType, projectPath);
|
|
34
|
+
|
|
35
|
+
// Dispose the stale panel for THIS viewType + project only. On page reload the
|
|
36
|
+
// browser's WS close message can be lost, leaving the map referencing a panel
|
|
37
|
+
// the browser no longer knows about.
|
|
38
|
+
panels.get(k)?.dispose(); // fires onDidDispose → clears map entry & timers
|
|
39
|
+
|
|
40
|
+
const panel = vscode.window.createWebviewPanel(
|
|
41
|
+
viewType,
|
|
42
|
+
title,
|
|
43
|
+
vscode.ViewColumn.Active,
|
|
44
|
+
{ projectPath },
|
|
45
|
+
);
|
|
46
|
+
panels.set(k, panel);
|
|
47
|
+
panel.webview.html = html;
|
|
48
|
+
|
|
49
|
+
const msgDisposable = panel.webview.onDidReceiveMessage((raw: unknown) => {
|
|
50
|
+
void onMessage(raw, panel);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const disposeDisposable = panel.onDidDispose(() => {
|
|
54
|
+
// Only clear if the map still points at THIS panel — a newer panel for the
|
|
55
|
+
// same key must not be evicted by an older one's teardown.
|
|
56
|
+
if (panels.get(k) === panel) panels.delete(k);
|
|
57
|
+
msgDisposable.dispose();
|
|
58
|
+
disposeDisposable.dispose();
|
|
59
|
+
onDispose?.();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
return panel;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Test seam: drop all registry state. */
|
|
66
|
+
export function _resetPanelRegistry(): void {
|
|
67
|
+
panels.clear();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function _panelCount(): number {
|
|
71
|
+
return panels.size;
|
|
72
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin client for the PPM HTTP API, used to map a project path to the project
|
|
3
|
+
* name that `window.openTab` needs. Initialised once from `activate`.
|
|
4
|
+
*/
|
|
5
|
+
let baseUrl = "";
|
|
6
|
+
let authToken = "";
|
|
7
|
+
|
|
8
|
+
export function initPpmApi(): void {
|
|
9
|
+
baseUrl = (globalThis as any).__PPM_BASE_URL__ || "";
|
|
10
|
+
authToken = (globalThis as any).__PPM_AUTH_TOKEN__ || "";
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Build fetch options with auth header when token is available */
|
|
14
|
+
export function authHeaders(): RequestInit {
|
|
15
|
+
return authToken ? { headers: { Authorization: `Bearer ${authToken}` } } : {};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function getBaseUrl(): string {
|
|
19
|
+
return baseUrl;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Resolve project path from PPM API as fallback */
|
|
23
|
+
export async function resolveProjectPath(): Promise<string | null> {
|
|
24
|
+
try {
|
|
25
|
+
const res = await fetch(`${baseUrl}/api/projects`, authHeaders());
|
|
26
|
+
const json = await res.json() as { ok: boolean; data?: { name: string; path: string }[] };
|
|
27
|
+
if (!json.ok || !json.data || json.data.length === 0) return null;
|
|
28
|
+
// Single project — safe to auto-select
|
|
29
|
+
if (json.data.length === 1) return json.data[0]?.path ?? null;
|
|
30
|
+
// Multiple projects — cannot guess which is active, return null
|
|
31
|
+
return null;
|
|
32
|
+
} catch {}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Resolve project name from path via PPM API */
|
|
37
|
+
export async function resolveProjectName(projectPath: string): Promise<string> {
|
|
38
|
+
try {
|
|
39
|
+
const res = await fetch(`${baseUrl}/api/projects`, authHeaders());
|
|
40
|
+
const json = await res.json() as { ok: boolean; data?: { name: string; path: string }[] };
|
|
41
|
+
if (json.ok && json.data) {
|
|
42
|
+
const match = json.data.find((p) => p.path === projectPath);
|
|
43
|
+
if (match) return match.name;
|
|
44
|
+
}
|
|
45
|
+
} catch {}
|
|
46
|
+
// Fallback to directory name
|
|
47
|
+
return projectPath.split(/[\\/]/).filter(Boolean).pop() || "project";
|
|
48
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { describe, it, expect } from "bun:test";
|
|
2
|
+
import {
|
|
3
|
+
REWORD_ENV_PREFIX, SEQUENCE_EDITOR, TODO_ENV_VAR,
|
|
4
|
+
buildRebaseTodo, isNoopPlan, isRebaseAction,
|
|
5
|
+
} from "./rebase-todo.ts";
|
|
6
|
+
|
|
7
|
+
const A = "a".repeat(40);
|
|
8
|
+
const B = "b".repeat(40);
|
|
9
|
+
const C = "c".repeat(40);
|
|
10
|
+
|
|
11
|
+
describe("buildRebaseTodo", () => {
|
|
12
|
+
it("writes one line per commit, oldest first", () => {
|
|
13
|
+
const { todo } = buildRebaseTodo([
|
|
14
|
+
{ hash: A, action: "pick", subject: "first" },
|
|
15
|
+
{ hash: B, action: "pick", subject: "second" },
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
expect(todo).toBe(`pick ${A} first\npick ${B} second\n`);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("keeps squash and fixup actions", () => {
|
|
22
|
+
const { todo } = buildRebaseTodo([
|
|
23
|
+
{ hash: A, action: "pick", subject: "base" },
|
|
24
|
+
{ hash: B, action: "squash", subject: "fold in" },
|
|
25
|
+
{ hash: C, action: "fixup", subject: "typo" },
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
expect(todo).toBe(`pick ${A} base\nsquash ${B} fold in\nfixup ${C} typo\n`);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("omits a dropped commit rather than writing a drop line", () => {
|
|
32
|
+
const { todo } = buildRebaseTodo([
|
|
33
|
+
{ hash: A, action: "pick", subject: "keep" },
|
|
34
|
+
{ hash: B, action: "drop", subject: "remove" },
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
expect(todo).toBe(`pick ${A} keep\n`);
|
|
38
|
+
expect(todo).not.toContain("drop");
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("refuses a plan that drops everything", () => {
|
|
42
|
+
expect(() => buildRebaseTodo([{ hash: A, action: "drop" }]))
|
|
43
|
+
.toThrow(/nothing to rebase/);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("refuses to squash the oldest kept commit", () => {
|
|
47
|
+
// There is no earlier commit for it to fold into; git would abort.
|
|
48
|
+
expect(() => buildRebaseTodo([
|
|
49
|
+
{ hash: A, action: "squash", subject: "x" },
|
|
50
|
+
{ hash: B, action: "pick", subject: "y" },
|
|
51
|
+
])).toThrow(/oldest kept commit/);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("looks past dropped commits when checking the oldest kept one", () => {
|
|
55
|
+
expect(() => buildRebaseTodo([
|
|
56
|
+
{ hash: A, action: "drop" },
|
|
57
|
+
{ hash: B, action: "fixup", subject: "y" },
|
|
58
|
+
])).toThrow(/oldest kept commit/);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("refuses a hash that is not a hash", () => {
|
|
62
|
+
expect(() => buildRebaseTodo([{ hash: "--exec=rm -rf /", action: "pick" }]))
|
|
63
|
+
.toThrow(/Invalid commit hash/);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("collapses a multi-line subject so it cannot add a second command", () => {
|
|
67
|
+
const { todo } = buildRebaseTodo([
|
|
68
|
+
{ hash: A, action: "pick", subject: "first line\nexec touch /tmp/pwned" },
|
|
69
|
+
]);
|
|
70
|
+
|
|
71
|
+
expect(todo.trimEnd().split("\n")).toHaveLength(1);
|
|
72
|
+
expect(todo).toBe(`pick ${A} first line exec touch /tmp/pwned\n`);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("writes a bare command when a commit has no subject", () => {
|
|
76
|
+
expect(buildRebaseTodo([{ hash: A, action: "pick" }]).todo).toBe(`pick ${A}\n`);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("ends with a newline so git can parse the last command", () => {
|
|
80
|
+
expect(buildRebaseTodo([{ hash: A, action: "pick", subject: "x" }]).todo.endsWith("\n")).toBe(true);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe("isRebaseAction", () => {
|
|
85
|
+
it("accepts every supported action", () => {
|
|
86
|
+
for (const action of ["pick", "reword", "edit", "squash", "fixup", "drop"]) {
|
|
87
|
+
expect(isRebaseAction(action)).toBe(true);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("rejects anything else", () => {
|
|
92
|
+
expect(isRebaseAction("exec")).toBe(false);
|
|
93
|
+
expect(isRebaseAction(undefined)).toBe(false);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe("buildRebaseTodo — reword", () => {
|
|
98
|
+
it("picks the commit and amends it from an environment variable", () => {
|
|
99
|
+
const { todo, env } = buildRebaseTodo([
|
|
100
|
+
{ hash: A, action: "reword", subject: "old subject", message: "new subject" },
|
|
101
|
+
]);
|
|
102
|
+
|
|
103
|
+
// The message never enters the todo text, so nothing in it can be read as a
|
|
104
|
+
// second command however it is punctuated.
|
|
105
|
+
expect(todo).toBe(
|
|
106
|
+
`pick ${A} old subject\nexec git commit --amend --allow-empty -m "$${REWORD_ENV_PREFIX}0"\n`,
|
|
107
|
+
);
|
|
108
|
+
expect(env).toEqual({ [`${REWORD_ENV_PREFIX}0`]: "new subject" });
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("numbers each reword separately", () => {
|
|
112
|
+
const { todo, env } = buildRebaseTodo([
|
|
113
|
+
{ hash: A, action: "reword", subject: "one", message: "first" },
|
|
114
|
+
{ hash: B, action: "pick", subject: "two" },
|
|
115
|
+
{ hash: C, action: "reword", subject: "three", message: "third" },
|
|
116
|
+
]);
|
|
117
|
+
|
|
118
|
+
expect(env).toEqual({
|
|
119
|
+
[`${REWORD_ENV_PREFIX}0`]: "first",
|
|
120
|
+
[`${REWORD_ENV_PREFIX}1`]: "third",
|
|
121
|
+
});
|
|
122
|
+
expect(todo).toContain(`$${REWORD_ENV_PREFIX}1`);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("keeps a message that would be a shell disaster inline", () => {
|
|
126
|
+
const nasty = '"; rm -rf / #';
|
|
127
|
+
const { env } = buildRebaseTodo([
|
|
128
|
+
{ hash: A, action: "reword", subject: "x", message: nasty },
|
|
129
|
+
]);
|
|
130
|
+
|
|
131
|
+
expect(env[`${REWORD_ENV_PREFIX}0`]).toBe(nasty);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it("keeps a multi-line message whole, unlike a subject", () => {
|
|
135
|
+
const { env } = buildRebaseTodo([
|
|
136
|
+
{ hash: A, action: "reword", subject: "x", message: "title\n\nbody line" },
|
|
137
|
+
]);
|
|
138
|
+
|
|
139
|
+
expect(env[`${REWORD_ENV_PREFIX}0`]).toBe("title\n\nbody line");
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it("refuses a reword with no message", () => {
|
|
143
|
+
expect(() => buildRebaseTodo([{ hash: A, action: "reword", subject: "x", message: " " }]))
|
|
144
|
+
.toThrow(/Reword needs a new message/);
|
|
145
|
+
expect(() => buildRebaseTodo([{ hash: A, action: "reword", subject: "x" }]))
|
|
146
|
+
.toThrow(/Reword needs a new message/);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("adds no environment when nothing is reworded", () => {
|
|
150
|
+
expect(buildRebaseTodo([{ hash: A, action: "pick" }]).env).toEqual({});
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
describe("buildRebaseTodo — edit", () => {
|
|
155
|
+
it("writes edit straight through, since git only stops there", () => {
|
|
156
|
+
const { todo, env } = buildRebaseTodo([
|
|
157
|
+
{ hash: A, action: "pick", subject: "base" },
|
|
158
|
+
{ hash: B, action: "edit", subject: "stop here" },
|
|
159
|
+
]);
|
|
160
|
+
|
|
161
|
+
expect(todo).toBe(`pick ${A} base\nedit ${B} stop here\n`);
|
|
162
|
+
expect(env).toEqual({});
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
describe("isNoopPlan", () => {
|
|
167
|
+
it("recognises an unchanged plan", () => {
|
|
168
|
+
expect(isNoopPlan(
|
|
169
|
+
[{ hash: A, action: "pick" }, { hash: B, action: "pick" }],
|
|
170
|
+
[A, B],
|
|
171
|
+
)).toBe(true);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it("sees a reorder", () => {
|
|
175
|
+
expect(isNoopPlan(
|
|
176
|
+
[{ hash: B, action: "pick" }, { hash: A, action: "pick" }],
|
|
177
|
+
[A, B],
|
|
178
|
+
)).toBe(false);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it("sees a changed action", () => {
|
|
182
|
+
expect(isNoopPlan(
|
|
183
|
+
[{ hash: A, action: "pick" }, { hash: B, action: "squash" }],
|
|
184
|
+
[A, B],
|
|
185
|
+
)).toBe(false);
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
describe("sequence editor wiring", () => {
|
|
190
|
+
it("reads the todo from an environment variable, not from the command string", () => {
|
|
191
|
+
// Git pastes the editor string into a shell command; keeping the todo in an
|
|
192
|
+
// env var means no quoting of the todo or of the todo path can go wrong.
|
|
193
|
+
expect(SEQUENCE_EDITOR).toContain(`$${TODO_ENV_VAR}`);
|
|
194
|
+
expect(SEQUENCE_EDITOR.trimEnd().endsWith(">")).toBe(true);
|
|
195
|
+
});
|
|
196
|
+
});
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Todo-list generation for interactive rebase.
|
|
3
|
+
*
|
|
4
|
+
* `git rebase -i` opens its todo list in `$GIT_SEQUENCE_EDITOR`. Git invokes it
|
|
5
|
+
* as `sh -c "$GIT_SEQUENCE_EDITOR \"$@\"" <editor> <todo-path>`, so the editor
|
|
6
|
+
* string is pasted into a shell command while the todo path arrives as `$@`.
|
|
7
|
+
* Passing the replacement todo through an *environment variable* keeps it out of
|
|
8
|
+
* that command string entirely — no quoting to get wrong, no temp file to write
|
|
9
|
+
* or clean up, and a path containing spaces still works:
|
|
10
|
+
*
|
|
11
|
+
* GIT_SEQUENCE_EDITOR='printf "%s" "$PPM_REBASE_TODO" >'
|
|
12
|
+
* PPM_REBASE_TODO='pick abc1234 …'
|
|
13
|
+
*
|
|
14
|
+
* `reword` uses the same trick a second time. Git's own `reword` would open
|
|
15
|
+
* `$GIT_EDITOR` mid-rebase and block, which the extension API cannot answer, so
|
|
16
|
+
* the message is collected up front and applied by an `exec` line that reads it
|
|
17
|
+
* from the environment:
|
|
18
|
+
*
|
|
19
|
+
* pick abc1234 old subject
|
|
20
|
+
* exec git commit --amend --allow-empty -m "$PPM_REWORD_0"
|
|
21
|
+
*
|
|
22
|
+
* `edit` needs none of that. It only *stops* the rebase — git does not open an
|
|
23
|
+
* editor for it — and the panel already offers Continue/Skip/Abort for a rebase
|
|
24
|
+
* that is paused.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
export const REBASE_ACTIONS = ["pick", "reword", "edit", "squash", "fixup", "drop"] as const;
|
|
28
|
+
export type RebaseAction = (typeof REBASE_ACTIONS)[number];
|
|
29
|
+
|
|
30
|
+
export const TODO_ENV_VAR = "PPM_REBASE_TODO";
|
|
31
|
+
export const SEQUENCE_EDITOR = `printf "%s" "$${TODO_ENV_VAR}" >`;
|
|
32
|
+
/** Each reworded commit gets `${REWORD_ENV_PREFIX}<n>` holding its new message. */
|
|
33
|
+
export const REWORD_ENV_PREFIX = "PPM_REWORD_";
|
|
34
|
+
|
|
35
|
+
export interface RebaseTodoEntry {
|
|
36
|
+
hash: string;
|
|
37
|
+
action: RebaseAction;
|
|
38
|
+
/** Only used to comment the line, so a human reading `git status` can follow. */
|
|
39
|
+
subject?: string;
|
|
40
|
+
/** The replacement commit message. Required when `action` is "reword". */
|
|
41
|
+
message?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface RebasePlan {
|
|
45
|
+
/** The todo body, for `TODO_ENV_VAR`. */
|
|
46
|
+
todo: string;
|
|
47
|
+
/** Reword messages, keyed by their environment variable name. */
|
|
48
|
+
env: Record<string, string>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function isRebaseAction(value: unknown): value is RebaseAction {
|
|
52
|
+
return typeof value === "string" && (REBASE_ACTIONS as readonly string[]).includes(value);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Build the todo body from entries in **oldest-first** order — the order git
|
|
57
|
+
* itself uses. The panel lists commits newest-first, so it reverses before
|
|
58
|
+
* calling this.
|
|
59
|
+
*/
|
|
60
|
+
export function buildRebaseTodo(entries: RebaseTodoEntry[]): RebasePlan {
|
|
61
|
+
const kept = entries.filter((e) => e.action !== "drop");
|
|
62
|
+
const oldest = kept[0];
|
|
63
|
+
if (!oldest) {
|
|
64
|
+
throw new Error("Dropping every commit would leave nothing to rebase.");
|
|
65
|
+
}
|
|
66
|
+
if (oldest.action === "squash" || oldest.action === "fixup") {
|
|
67
|
+
throw new Error(
|
|
68
|
+
`The oldest kept commit cannot be "${oldest.action}" — there is no earlier commit to fold it into.`,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const lines: string[] = [];
|
|
73
|
+
const env: Record<string, string> = {};
|
|
74
|
+
let rewordCount = 0;
|
|
75
|
+
|
|
76
|
+
for (const entry of entries) {
|
|
77
|
+
if (!/^[0-9a-f]{4,40}$/i.test(entry.hash)) {
|
|
78
|
+
throw new Error(`Invalid commit hash in rebase plan: "${entry.hash}"`);
|
|
79
|
+
}
|
|
80
|
+
if (!isRebaseAction(entry.action)) {
|
|
81
|
+
throw new Error(`Invalid rebase action: "${String(entry.action)}"`);
|
|
82
|
+
}
|
|
83
|
+
// A dropped commit is simply omitted rather than written as `drop`: both are
|
|
84
|
+
// valid to git, and omission keeps the todo readable if a conflict stops the
|
|
85
|
+
// rebase and the user inspects it.
|
|
86
|
+
if (entry.action === "drop") continue;
|
|
87
|
+
|
|
88
|
+
// Subjects are collapsed to one line — a newline here would inject an extra
|
|
89
|
+
// todo command.
|
|
90
|
+
const subject = (entry.subject ?? "").replace(/[\r\n]+/g, " ").trim();
|
|
91
|
+
|
|
92
|
+
if (entry.action === "reword") {
|
|
93
|
+
const message = (entry.message ?? "").trim();
|
|
94
|
+
if (!message) {
|
|
95
|
+
throw new Error(`Reword needs a new message for commit ${entry.hash.slice(0, 7)}.`);
|
|
96
|
+
}
|
|
97
|
+
const variable = `${REWORD_ENV_PREFIX}${rewordCount++}`;
|
|
98
|
+
env[variable] = message;
|
|
99
|
+
lines.push(`pick ${entry.hash}${subject ? ` ${subject}` : ""}`);
|
|
100
|
+
// `--allow-empty` because a commit that was already empty survives the
|
|
101
|
+
// rebase, and amending it without the flag would abort the whole run.
|
|
102
|
+
lines.push(`exec git commit --amend --allow-empty -m "$${variable}"`);
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
lines.push(`${entry.action} ${entry.hash}${subject ? ` ${subject}` : ""}`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Trailing newline: git's todo parser expects the final command to be
|
|
110
|
+
// terminated like any other line.
|
|
111
|
+
return { todo: `${lines.join("\n")}\n`, env };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Does this plan actually change anything, or is it the identity rebase? */
|
|
115
|
+
export function isNoopPlan(entries: RebaseTodoEntry[], originalOrder: string[]): boolean {
|
|
116
|
+
if (entries.some((e) => e.action !== "pick")) return false;
|
|
117
|
+
if (entries.length !== originalOrder.length) return false;
|
|
118
|
+
return entries.every((e, i) => e.hash === originalOrder[i]);
|
|
119
|
+
}
|