@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,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared chrome for the panels added in v0.2.0 (blame, file history, compare,
|
|
3
|
+
* interactive rebase).
|
|
4
|
+
*
|
|
5
|
+
* The graph panel keeps its own self-contained HTML — it predates this shell and
|
|
6
|
+
* its CSS is tuned to the graph canvas. This shell only carries what the four
|
|
7
|
+
* newer panels have in common: the theme tokens, base typography, a toolbar, and
|
|
8
|
+
* the `acquireVsCodeApi` bridge. Webviews are sandboxed with `allow-scripts`
|
|
9
|
+
* only, so everything is inlined.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** Theme tokens, kept in sync with the graph panel's palette. */
|
|
13
|
+
export const SHELL_CSS = `
|
|
14
|
+
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
15
|
+
:root {
|
|
16
|
+
--bg: #ffffff; --surface: #f4f4f5; --text: #09090b; --subtext: #71717a; --subtle: #a1a1aa;
|
|
17
|
+
--border: #e4e4e7; --border2: #d4d4d8; --blue: #3b82f6; --red: #ef4444; --green: #22c55e;
|
|
18
|
+
--yellow: #eab308; --purple: #8b5cf6; --orange: #f97316;
|
|
19
|
+
--surface-hover: #f4f4f5; --selected: #eff6ff;
|
|
20
|
+
}
|
|
21
|
+
@media (prefers-color-scheme: dark) {
|
|
22
|
+
:root {
|
|
23
|
+
--bg: #09090b; --surface: #18181b; --text: #fafafa; --subtext: #a1a1aa; --subtle: #52525b;
|
|
24
|
+
--border: #27272a; --border2: #3f3f46; --selected: #1e293b; --surface-hover: #27272a;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: var(--bg); color: var(--text); font-size: 12px; overflow: hidden; height: 100vh; display: flex; flex-direction: column; }
|
|
28
|
+
#app { display: flex; flex-direction: column; height: 100vh; min-height: 0; }
|
|
29
|
+
code, .mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
|
|
30
|
+
|
|
31
|
+
#toolbar { display: flex; justify-content: space-between; align-items: center; gap: 8px; padding: 4px 10px; border-bottom: 1px solid var(--border); background: var(--surface); flex-shrink: 0; }
|
|
32
|
+
.toolbar-left, .toolbar-right { display: flex; align-items: center; gap: 4px; min-width: 0; }
|
|
33
|
+
.toolbar-title { font-weight: 600; font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
34
|
+
.toolbar-sub { color: var(--subtext); font-size: 11px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
35
|
+
|
|
36
|
+
button { background: transparent; color: var(--text); border: 1px solid var(--border); border-radius: 5px; padding: 3px 8px; font-size: 11px; cursor: pointer; min-width: 24px; min-height: 24px; transition: background 0.15s, border-color 0.15s; }
|
|
37
|
+
button:hover { background: var(--surface-hover); border-color: var(--border2); }
|
|
38
|
+
button:active { background: var(--surface); }
|
|
39
|
+
button:disabled { opacity: 0.5; cursor: default; }
|
|
40
|
+
button.primary { background: var(--blue); border-color: var(--blue); color: #fff; }
|
|
41
|
+
button.danger { border-color: var(--red); color: var(--red); }
|
|
42
|
+
select, input[type=text], input[type=search] { background: var(--bg); color: var(--text); border: 1px solid var(--border2); border-radius: 4px; padding: 3px 6px; font-size: 11px; outline: none; }
|
|
43
|
+
select:focus, input:focus { border-color: var(--blue); }
|
|
44
|
+
|
|
45
|
+
.scroll { flex: 1; overflow: auto; min-height: 0; }
|
|
46
|
+
.empty { display: flex; align-items: center; justify-content: center; height: 100%; color: var(--subtext); font-size: 12px; padding: 24px; text-align: center; }
|
|
47
|
+
.spinner { width: 16px; height: 16px; border: 2px solid var(--border2); border-top-color: var(--blue); border-radius: 50%; animation: spin 0.7s linear infinite; }
|
|
48
|
+
@keyframes spin { to { transform: rotate(360deg); } }
|
|
49
|
+
.banner { padding: 6px 10px; font-size: 11px; border-bottom: 1px solid var(--border); background: rgba(234,179,8,0.12); color: var(--text); display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
|
|
50
|
+
.banner.error { background: rgba(239,68,68,0.12); }
|
|
51
|
+
.hidden { display: none !important; }
|
|
52
|
+
|
|
53
|
+
/* Author chip — initials only. Rendering a real avatar would mean sending the
|
|
54
|
+
committer's email to a third-party service (gravatar), which a self-hosted
|
|
55
|
+
tool must not do silently. */
|
|
56
|
+
.avatar { width: 18px; height: 18px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; font-size: 8px; font-weight: 700; color: #fff; flex-shrink: 0; letter-spacing: -0.2px; }
|
|
57
|
+
|
|
58
|
+
/* Touch devices get taller rows — matches the graph panel's coarse-pointer rule */
|
|
59
|
+
@media (pointer: coarse) {
|
|
60
|
+
button { min-height: 32px; padding: 5px 10px; }
|
|
61
|
+
.row { min-height: 40px; }
|
|
62
|
+
}
|
|
63
|
+
`;
|
|
64
|
+
|
|
65
|
+
/** Deterministic author colour + initials, shared by every panel. */
|
|
66
|
+
export const AVATAR_JS = `
|
|
67
|
+
const AVATAR_COLORS = ['#3b82f6','#8b5cf6','#ec4899','#f97316','#22c55e','#14b8a6','#eab308','#ef4444','#6366f1','#0ea5e9'];
|
|
68
|
+
function authorInitials(name) {
|
|
69
|
+
const parts = String(name || '?').trim().split(/\\s+/).filter(Boolean);
|
|
70
|
+
if (!parts.length) return '?';
|
|
71
|
+
if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();
|
|
72
|
+
return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
|
|
73
|
+
}
|
|
74
|
+
function authorColor(key) {
|
|
75
|
+
const s = String(key || '');
|
|
76
|
+
let h = 0;
|
|
77
|
+
for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) | 0;
|
|
78
|
+
return AVATAR_COLORS[Math.abs(h) % AVATAR_COLORS.length];
|
|
79
|
+
}
|
|
80
|
+
function avatarHtml(name, email) {
|
|
81
|
+
return '<span class="avatar" style="background:' + authorColor(email || name) + '">' + escHtml(authorInitials(name)) + '</span>';
|
|
82
|
+
}
|
|
83
|
+
`;
|
|
84
|
+
|
|
85
|
+
/** HTML escaping + relative time, needed by every panel's renderer. */
|
|
86
|
+
export const SHELL_JS = `
|
|
87
|
+
const vscode = acquireVsCodeApi();
|
|
88
|
+
function escHtml(s) {
|
|
89
|
+
return String(s == null ? '' : s)
|
|
90
|
+
.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
|
91
|
+
.replace(/"/g, '"').replace(/'/g, ''');
|
|
92
|
+
}
|
|
93
|
+
function relTime(ts) {
|
|
94
|
+
const diff = Math.floor(Date.now() / 1000) - Number(ts);
|
|
95
|
+
if (!Number.isFinite(diff)) return '';
|
|
96
|
+
if (diff < 60) return 'just now';
|
|
97
|
+
if (diff < 3600) return Math.floor(diff / 60) + 'm ago';
|
|
98
|
+
if (diff < 86400) return Math.floor(diff / 3600) + 'h ago';
|
|
99
|
+
if (diff < 2592000) return Math.floor(diff / 86400) + 'd ago';
|
|
100
|
+
if (diff < 31536000) return Math.floor(diff / 2592000) + 'mo ago';
|
|
101
|
+
return Math.floor(diff / 31536000) + 'y ago';
|
|
102
|
+
}
|
|
103
|
+
function showError(message) {
|
|
104
|
+
const el = document.getElementById('banner');
|
|
105
|
+
if (!el) return;
|
|
106
|
+
el.textContent = message;
|
|
107
|
+
el.classList.remove('hidden');
|
|
108
|
+
el.classList.add('error');
|
|
109
|
+
}
|
|
110
|
+
function clearError() {
|
|
111
|
+
const el = document.getElementById('banner');
|
|
112
|
+
if (el) el.classList.add('hidden');
|
|
113
|
+
}
|
|
114
|
+
${AVATAR_JS}
|
|
115
|
+
`;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Tracked-file picker, shared by the blame and file-history panels.
|
|
119
|
+
*
|
|
120
|
+
* The extension holds the full `git ls-files` list and filters it per keystroke,
|
|
121
|
+
* so the webview never receives a large repo's entire path list. The host panel
|
|
122
|
+
* supplies `onFileChosen` and forwards `loadFiles` to `applyFileList`.
|
|
123
|
+
*/
|
|
124
|
+
export const FILE_PICKER_CSS = `
|
|
125
|
+
.picker { position: absolute; top: 34px; left: 8px; right: 8px; max-width: 620px; z-index: 40; background: var(--surface); border: 1px solid var(--border2); border-radius: 8px; box-shadow: 0 8px 24px rgba(0,0,0,0.25); display: flex; flex-direction: column; max-height: 60vh; }
|
|
126
|
+
.picker input { margin: 8px; }
|
|
127
|
+
.picker-list { overflow: auto; min-height: 0; }
|
|
128
|
+
.picker-item { padding: 6px 10px; cursor: pointer; font-family: ui-monospace, monospace; font-size: 11px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
129
|
+
.picker-item:hover, .picker-item.active { background: var(--selected); }
|
|
130
|
+
.picker-foot { padding: 6px 10px; border-top: 1px solid var(--border); color: var(--subtext); font-size: 10px; }
|
|
131
|
+
@media (pointer: coarse) { .picker-item { padding: 10px; } }
|
|
132
|
+
`;
|
|
133
|
+
|
|
134
|
+
export const FILE_PICKER_HTML = `
|
|
135
|
+
<div id="picker" class="picker hidden">
|
|
136
|
+
<input id="picker-input" type="search" placeholder="Filter tracked files…" autocomplete="off" />
|
|
137
|
+
<div id="picker-list" class="picker-list"></div>
|
|
138
|
+
<div class="picker-foot" id="picker-foot"></div>
|
|
139
|
+
</div>`;
|
|
140
|
+
|
|
141
|
+
export const FILE_PICKER_JS = `
|
|
142
|
+
const picker = {
|
|
143
|
+
root: document.getElementById('picker'),
|
|
144
|
+
input: document.getElementById('picker-input'),
|
|
145
|
+
list: document.getElementById('picker-list'),
|
|
146
|
+
foot: document.getElementById('picker-foot'),
|
|
147
|
+
files: [],
|
|
148
|
+
idx: 0,
|
|
149
|
+
timer: undefined,
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
function openFilePicker() {
|
|
153
|
+
picker.root.classList.remove('hidden');
|
|
154
|
+
picker.input.value = '';
|
|
155
|
+
picker.input.focus();
|
|
156
|
+
vscode.postMessage({ command: 'requestFiles', query: '' });
|
|
157
|
+
}
|
|
158
|
+
function closeFilePicker() { picker.root.classList.add('hidden'); }
|
|
159
|
+
function isFilePickerOpen() { return !picker.root.classList.contains('hidden'); }
|
|
160
|
+
|
|
161
|
+
function applyFileList(data) {
|
|
162
|
+
picker.files = data.files || [];
|
|
163
|
+
picker.idx = 0;
|
|
164
|
+
renderFilePicker();
|
|
165
|
+
picker.foot.textContent = picker.files.length + ' of ' + data.total + ' tracked files';
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function renderFilePicker() {
|
|
169
|
+
picker.list.innerHTML = picker.files.map((f, i) =>
|
|
170
|
+
'<div class="picker-item' + (i === picker.idx ? ' active' : '') + '" data-file="' + escHtml(f) + '">' + escHtml(f) + '</div>'
|
|
171
|
+
).join('') || '<div class="picker-foot">No tracked file matches.</div>';
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
picker.input.addEventListener('input', () => {
|
|
175
|
+
clearTimeout(picker.timer);
|
|
176
|
+
picker.timer = setTimeout(() => {
|
|
177
|
+
vscode.postMessage({ command: 'requestFiles', query: picker.input.value });
|
|
178
|
+
}, 120);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
picker.input.addEventListener('keydown', (e) => {
|
|
182
|
+
if (e.key === 'Escape') { closeFilePicker(); return; }
|
|
183
|
+
if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
|
|
184
|
+
e.preventDefault();
|
|
185
|
+
picker.idx = Math.max(0, Math.min(picker.files.length - 1, picker.idx + (e.key === 'ArrowDown' ? 1 : -1)));
|
|
186
|
+
renderFilePicker();
|
|
187
|
+
const active = picker.list.querySelector('.picker-item.active');
|
|
188
|
+
if (active) active.scrollIntoView({ block: 'nearest' });
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (e.key === 'Enter') {
|
|
192
|
+
const file = picker.files[picker.idx];
|
|
193
|
+
if (file) { closeFilePicker(); onFileChosen(file); }
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
picker.list.addEventListener('click', (e) => {
|
|
198
|
+
const item = e.target.closest('.picker-item');
|
|
199
|
+
if (item) { closeFilePicker(); onFileChosen(item.dataset.file); }
|
|
200
|
+
});
|
|
201
|
+
`;
|
|
202
|
+
|
|
203
|
+
export interface ShellOptions {
|
|
204
|
+
/** Toolbar markup (left/right groups). */
|
|
205
|
+
toolbar: string;
|
|
206
|
+
/** Panel body markup, placed inside `#app` below the toolbar. */
|
|
207
|
+
body: string;
|
|
208
|
+
/** Panel-specific CSS appended after the shell CSS. */
|
|
209
|
+
css?: string;
|
|
210
|
+
/** Panel-specific JS appended after the shell JS. */
|
|
211
|
+
script: string;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function shellHtml(options: ShellOptions): string {
|
|
215
|
+
return `<!DOCTYPE html>
|
|
216
|
+
<html>
|
|
217
|
+
<head>
|
|
218
|
+
<meta charset="utf-8">
|
|
219
|
+
<style>
|
|
220
|
+
${SHELL_CSS}
|
|
221
|
+
${options.css ?? ""}
|
|
222
|
+
</style>
|
|
223
|
+
</head>
|
|
224
|
+
<body>
|
|
225
|
+
<div id="app">
|
|
226
|
+
<div id="toolbar">${options.toolbar}</div>
|
|
227
|
+
<div id="banner" class="banner hidden"></div>
|
|
228
|
+
${options.body}
|
|
229
|
+
</div>
|
|
230
|
+
<script>
|
|
231
|
+
${SHELL_JS}
|
|
232
|
+
${options.script}
|
|
233
|
+
</script>
|
|
234
|
+
</body>
|
|
235
|
+
</html>`;
|
|
236
|
+
}
|
|
@@ -812,6 +812,13 @@ export class ClaudeAgentSdkProvider implements AIProvider {
|
|
|
812
812
|
if (oldCount != null) { this.messageCount.set(newId, oldCount); this.messageCount.delete(sessionId); }
|
|
813
813
|
const oldStream = this.streamingSessions.get(sessionId);
|
|
814
814
|
if (oldStream) { this.streamingSessions.set(newId, oldStream); this.streamingSessions.delete(sessionId); }
|
|
815
|
+
// The account binding lives in the database under the old id, and the lookup below
|
|
816
|
+
// runs after this block — so without moving it, a session that migrates silently
|
|
817
|
+
// loses whichever account was chosen for it and gets re-routed to another one. That
|
|
818
|
+
// costs a full prompt-cache write, and when the choice was the user's it also
|
|
819
|
+
// quietly overrides them.
|
|
820
|
+
const boundAccount = getSessionAccount(sessionId);
|
|
821
|
+
if (boundAccount) accountSelector.bindSession(newId, boundAccount);
|
|
815
822
|
yield { type: "session_migrated" as const, oldSessionId: sessionId, newSessionId: newId };
|
|
816
823
|
sessionId = newId;
|
|
817
824
|
}
|
|
@@ -106,6 +106,26 @@ interface LiveSession {
|
|
|
106
106
|
compactRequested?: boolean;
|
|
107
107
|
/** Token counts from the most recent usage notification, attached to `done`. */
|
|
108
108
|
lastUsage?: import("../../shared/turn-usage.ts").TurnUsage;
|
|
109
|
+
/** A turn is running on the thread. Codex accepts exactly one at a time: a
|
|
110
|
+
* second `turn/start` resolves with the ALREADY-RUNNING turn (same id,
|
|
111
|
+
* status `inProgress`) and silently discards the new input — no error, so
|
|
112
|
+
* nothing downstream can notice. Follow-ups therefore wait in `pendingTurns`
|
|
113
|
+
* and are sent when the running turn reports `turn/completed`. */
|
|
114
|
+
turnInFlight?: boolean;
|
|
115
|
+
/** Id of the running turn — `turn/interrupt` needs it, `threadId` alone is rejected. */
|
|
116
|
+
activeTurnId?: string | null;
|
|
117
|
+
/** Interrupt asked for before `turn/started` named the turn; fired on arrival. */
|
|
118
|
+
interruptRequested?: boolean;
|
|
119
|
+
/** Follow-ups waiting for the current turn, in the order they will be sent. */
|
|
120
|
+
pendingTurns: QueuedTurn[];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** A follow-up held back because a turn was already running. */
|
|
124
|
+
interface QueuedTurn {
|
|
125
|
+
message: string;
|
|
126
|
+
opts?: SendMessageOpts;
|
|
127
|
+
/** `later` sinks to the end of the queue; `now`/`next` go in front of it. */
|
|
128
|
+
priority: "now" | "next" | "later";
|
|
109
129
|
}
|
|
110
130
|
|
|
111
131
|
interface EventChannel {
|
|
@@ -310,6 +330,15 @@ export class CodexAppServerProvider implements AIProvider {
|
|
|
310
330
|
|
|
311
331
|
private startTurn(live: LiveSession, message: string, opts?: SendMessageOpts): void {
|
|
312
332
|
if (!live.threadId) return;
|
|
333
|
+
// One turn at a time — anything sent now would be dropped without a trace.
|
|
334
|
+
// `now` additionally cuts the running turn short so this one answers next.
|
|
335
|
+
if (live.turnInFlight) {
|
|
336
|
+
const priority = opts?.priority ?? "next";
|
|
337
|
+
this.enqueueTurn(live, { message, opts, priority });
|
|
338
|
+
if (priority === "now") this.interruptActiveTurn(live);
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
live.turnInFlight = true;
|
|
313
342
|
// `/compact` → trigger codex's real compaction (thread/compact/start), not a text turn.
|
|
314
343
|
// Completion is surfaced on the `thread/compacted` notification (handleNotification).
|
|
315
344
|
if (message.trim() === "/compact") {
|
|
@@ -318,6 +347,7 @@ export class CodexAppServerProvider implements AIProvider {
|
|
|
318
347
|
live.compactRequested = false;
|
|
319
348
|
if (!live.client.isClosed) live.channel.push({ type: "error", message: redactTruncate((err as Error)?.message ?? String(err), 256) });
|
|
320
349
|
live.channel.push({ type: "done", sessionId: live.threadId ?? "", resultSubtype: "error_during_execution" });
|
|
350
|
+
this.endTurn(live);
|
|
321
351
|
});
|
|
322
352
|
return;
|
|
323
353
|
}
|
|
@@ -335,11 +365,55 @@ export class CodexAppServerProvider implements AIProvider {
|
|
|
335
365
|
threadId: live.threadId,
|
|
336
366
|
input,
|
|
337
367
|
...(turnModel ? { model: turnModel } : {}),
|
|
368
|
+
}).then((res) => {
|
|
369
|
+
// Backup for the `turn/started` notification, which is what normally names
|
|
370
|
+
// the turn — an interrupt arriving in between has nothing to address.
|
|
371
|
+
const id = (res as { turn?: { id?: string } })?.turn?.id;
|
|
372
|
+
if (id && !live.activeTurnId) this.setActiveTurn(live, id);
|
|
338
373
|
}).catch((err) => {
|
|
339
374
|
if (!live.client.isClosed) live.channel.push({ type: "error", message: redactTruncate((err as Error)?.message ?? String(err), 256) });
|
|
375
|
+
// No turn is running, so nothing will report `turn/completed` — release the
|
|
376
|
+
// queue here or every later follow-up waits on a turn that never existed.
|
|
377
|
+
this.endTurn(live);
|
|
340
378
|
});
|
|
341
379
|
}
|
|
342
380
|
|
|
381
|
+
/** Insert a follow-up: `later` sinks behind everything, `now`/`next` queue ahead of it. */
|
|
382
|
+
private enqueueTurn(live: LiveSession, item: QueuedTurn): void {
|
|
383
|
+
if (item.priority === "later") {
|
|
384
|
+
live.pendingTurns.push(item);
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
const firstLater = live.pendingTurns.findIndex((q) => q.priority === "later");
|
|
388
|
+
if (firstLater === -1) live.pendingTurns.push(item);
|
|
389
|
+
else live.pendingTurns.splice(firstLater, 0, item);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/** Cut the running turn short. Falls back to waiting it out when the interrupt
|
|
393
|
+
* cannot be sent — the follow-up stays queued either way, never dropped. */
|
|
394
|
+
private interruptActiveTurn(live: LiveSession): void {
|
|
395
|
+
if (!live.activeTurnId) { live.interruptRequested = true; return; }
|
|
396
|
+
live.client.request("turn/interrupt", { threadId: live.threadId, turnId: live.activeTurnId })
|
|
397
|
+
.catch(() => { /* turn already ending — the queue drains on turn/completed */ });
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
private setActiveTurn(live: LiveSession, turnId: string): void {
|
|
401
|
+
live.activeTurnId = turnId;
|
|
402
|
+
if (live.interruptRequested) {
|
|
403
|
+
live.interruptRequested = false;
|
|
404
|
+
this.interruptActiveTurn(live);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/** Running turn is over: send the next follow-up, if one is waiting. */
|
|
409
|
+
private endTurn(live: LiveSession): void {
|
|
410
|
+
live.turnInFlight = false;
|
|
411
|
+
live.activeTurnId = null;
|
|
412
|
+
live.interruptRequested = false;
|
|
413
|
+
const next = live.pendingTurns.shift();
|
|
414
|
+
if (next) this.startTurn(live, next.message, next.opts);
|
|
415
|
+
}
|
|
416
|
+
|
|
343
417
|
private async connect(sessionId: string, opts?: SendMessageOpts): Promise<LiveSession> {
|
|
344
418
|
const meta = this.sessions.get(sessionId);
|
|
345
419
|
const cwd = meta?.projectPath || getSessionProjectPath(sessionId) || process.cwd();
|
|
@@ -352,6 +426,7 @@ export class CodexAppServerProvider implements AIProvider {
|
|
|
352
426
|
client, threadId: null, cwd, channel, permission, model,
|
|
353
427
|
pendingApprovals: new Map(), answeredCodexIds: new Set(),
|
|
354
428
|
history: [], transcript: [], currentAssistant: "", currentEvents: [],
|
|
429
|
+
pendingTurns: [],
|
|
355
430
|
};
|
|
356
431
|
this.live.set(sessionId, live);
|
|
357
432
|
|
|
@@ -434,6 +509,14 @@ export class CodexAppServerProvider implements AIProvider {
|
|
|
434
509
|
}
|
|
435
510
|
live.currentAssistant = "";
|
|
436
511
|
live.currentEvents = [];
|
|
512
|
+
this.endTurn(live);
|
|
513
|
+
}
|
|
514
|
+
if (notif.method === "turn/started") {
|
|
515
|
+
// Codex is the authority on whether the thread is busy — take the flag from
|
|
516
|
+
// it rather than only from what this side believes it started.
|
|
517
|
+
live.turnInFlight = true;
|
|
518
|
+
const id = (notif.params as { turn?: { id?: string } })?.turn?.id;
|
|
519
|
+
if (id) this.setActiveTurn(live, id);
|
|
437
520
|
}
|
|
438
521
|
// Manual /compact finished — codex signals via the contextCompaction item
|
|
439
522
|
// (the thread/compacted notification is deprecated). Surface the compact-summary
|
|
@@ -724,7 +807,12 @@ export class CodexAppServerProvider implements AIProvider {
|
|
|
724
807
|
|
|
725
808
|
// Managed accounts are assigned on first send; don't show an unrelated
|
|
726
809
|
// ambient login while that assignment is pending or the binding is missing.
|
|
727
|
-
|
|
810
|
+
//
|
|
811
|
+
// Disabled ones do not count towards "managed accounts exist": with every one switched
|
|
812
|
+
// off the selector returns null and chats really do run on the ambient ~/.codex login,
|
|
813
|
+
// so that is the login whose usage belongs here.
|
|
814
|
+
const selectable = listCodexAccounts().filter((a) => a.status !== "disabled");
|
|
815
|
+
if (accountId || selectable.length > 0) return {};
|
|
728
816
|
return getOrFetchUsage(this.id, AMBIENT_ACCOUNT_KEY);
|
|
729
817
|
}
|
|
730
818
|
}
|
package/src/server/index.ts
CHANGED
|
@@ -952,7 +952,7 @@ if (process.argv.includes("__serve__")) {
|
|
|
952
952
|
});
|
|
953
953
|
|
|
954
954
|
// Start background account token refresh in daemon child
|
|
955
|
-
import("../services/account.service.ts").then(({ accountService }) => accountService.
|
|
955
|
+
import("../services/account.service.ts").then(({ accountService }) => accountService.startAccountMaintenance()).catch(() => {});
|
|
956
956
|
|
|
957
957
|
// Start background usage limit polling (every 5 min)
|
|
958
958
|
import("../services/claude-usage.service.ts").then(({ startUsagePolling }) => startUsagePolling()).catch(() => {});
|
|
@@ -32,6 +32,9 @@ accountsRoutes.get("/", (c) => {
|
|
|
32
32
|
const accounts = accountService.list().map((acc) => ({
|
|
33
33
|
...acc,
|
|
34
34
|
hasRefreshToken: accountService.hasRefreshToken(acc.id),
|
|
35
|
+
// When this account's sign-in stops being valid, so the card can warn instead of
|
|
36
|
+
// waiting for the turn that fails. Null where PPM never recorded the grant.
|
|
37
|
+
grantExpiresAt: accountService.grantExpiresAt(acc),
|
|
35
38
|
}));
|
|
36
39
|
return c.json(ok(accounts));
|
|
37
40
|
});
|
|
@@ -44,6 +47,25 @@ accountsRoutes.get("/active", (c) => {
|
|
|
44
47
|
return c.json(ok(account));
|
|
45
48
|
});
|
|
46
49
|
|
|
50
|
+
/**
|
|
51
|
+
* POST /api/accounts/pick — claim the account that will serve a new chat tab.
|
|
52
|
+
*
|
|
53
|
+
* POST, not GET, because this consumes a pick: round-robin advances its cursor, so asking
|
|
54
|
+
* twice is meant to hand back two different accounts. That is the point — a tab asks once
|
|
55
|
+
* when it opens, keeps the answer for as long as it lives, and several tabs opened in a row
|
|
56
|
+
* spread across the pool instead of piling onto whichever account happens to be next.
|
|
57
|
+
*
|
|
58
|
+
* Unlike `/active`, which only previews, the answer here is the one the first message will
|
|
59
|
+
* actually run on: the client sends the id back when it creates the session, and it becomes
|
|
60
|
+
* that session's binding. Null means nothing is usable right now, and the caller must say so
|
|
61
|
+
* rather than showing a stale name.
|
|
62
|
+
*/
|
|
63
|
+
accountsRoutes.post("/pick", (c) => {
|
|
64
|
+
const picked = accountSelector.next();
|
|
65
|
+
if (!picked) return c.json(ok(null));
|
|
66
|
+
return c.json(ok({ id: picked.id, label: picked.label ?? picked.email ?? null }));
|
|
67
|
+
});
|
|
68
|
+
|
|
47
69
|
/** GET /api/accounts/settings */
|
|
48
70
|
accountsRoutes.get("/settings", (c) => {
|
|
49
71
|
return c.json(ok({
|
|
@@ -389,7 +411,9 @@ accountsRoutes.patch("/:id", async (c) => {
|
|
|
389
411
|
// Only for an account that has a refresh token: a temporary one has nothing to prove,
|
|
390
412
|
// and setEnabled()'s own guard says something more useful than a refresh error would.
|
|
391
413
|
if (wasParked && accountService.hasRefreshToken(id)) {
|
|
392
|
-
|
|
414
|
+
// Enabling is a deliberate gesture, so it re-tests a grant PPM has already
|
|
415
|
+
// written off — the check exists to catch a rejection, not to trust a stored one.
|
|
416
|
+
const proof = await accountService.ensureFreshTokenChecked(id, { retryRejected: true });
|
|
393
417
|
if (!proof.account) {
|
|
394
418
|
// Worth splitting: invalid_grant means the token is gone and only a fresh sign-in
|
|
395
419
|
// brings it back, while a network drop or a 429 is worth trying again in a minute.
|
|
@@ -13,6 +13,7 @@ import { ensureSdkCommands, invalidateSdkCommands } from "../../services/slash-d
|
|
|
13
13
|
import { upsertSlashRecent, getSlashRecents, setSessionClearedFrom, listTurnUsage, getSessionAccount, getSessionProvider, resolveMigratedSession } from "../../services/db.service.ts";
|
|
14
14
|
import type { TurnUsage } from "../../shared/turn-usage.ts";
|
|
15
15
|
import { getCachedUsage, refreshUsageNow } from "../../services/claude-usage.service.ts";
|
|
16
|
+
import { bindPickedAccount, bindRefusalReason } from "../../services/picked-account-binding.ts";
|
|
16
17
|
import { getSessionLog } from "../../services/session-log.service.ts";
|
|
17
18
|
import { parseJsonlTranscript, validateJsonlPath } from "../../services/jsonl-transcript-parser.ts";
|
|
18
19
|
import { aggregateTasks } from "../../services/task-status-aggregator.ts";
|
|
@@ -361,13 +362,19 @@ chatRoutes.post("/sessions", async (c) => {
|
|
|
361
362
|
try {
|
|
362
363
|
const projectName = c.get("projectName");
|
|
363
364
|
const projectPath = c.get("projectPath");
|
|
364
|
-
const body = await c.req.json<{ providerId?: string; title?: string; clearedFrom?: string }>();
|
|
365
|
+
const body = await c.req.json<{ providerId?: string; title?: string; clearedFrom?: string; accountId?: string }>();
|
|
365
366
|
const session = await chatService.createSession(body.providerId, {
|
|
366
367
|
projectName,
|
|
367
368
|
projectPath,
|
|
368
369
|
title: body.title,
|
|
369
370
|
});
|
|
370
371
|
if (body.clearedFrom) setSessionClearedFrom(session.id, body.clearedFrom);
|
|
372
|
+
// The tab claimed an account when it opened and showed its name; honour that here so
|
|
373
|
+
// the first message runs on the account the user was actually looking at. Advisory,
|
|
374
|
+
// never authoritative: bindPickedAccount re-checks the id against the server's own
|
|
375
|
+
// pool and simply declines an id it does not recognise, because this arrives from a
|
|
376
|
+
// client and selecting a token by client-supplied id is not something to allow.
|
|
377
|
+
if (body.accountId) bindPickedAccount(session.id, session.providerId, body.accountId);
|
|
371
378
|
// Auto-assign default tag if project has one
|
|
372
379
|
const defaultTagId = getProjectDefaultTagId(projectPath);
|
|
373
380
|
if (defaultTagId) setSessionTag(session.id, defaultTagId, projectPath);
|
|
@@ -473,6 +480,27 @@ chatRoutes.patch("/sessions/:id", async (c) => {
|
|
|
473
480
|
}
|
|
474
481
|
});
|
|
475
482
|
|
|
483
|
+
/**
|
|
484
|
+
* PUT /chat/sessions/:id/account — move a live session onto an account the user picked.
|
|
485
|
+
*
|
|
486
|
+
* Unlike the same choice made at session creation, this one answers: the user is looking at
|
|
487
|
+
* a panel and pressed something, so a silently ignored pick would read as a broken button.
|
|
488
|
+
*
|
|
489
|
+
* Worth knowing before calling: the prompt cache is scoped per account, so moving a session
|
|
490
|
+
* mid-conversation re-sends its whole transcript as a cache write rather than a read. On a
|
|
491
|
+
* long session that is a real cost, not a formality.
|
|
492
|
+
*/
|
|
493
|
+
chatRoutes.put("/sessions/:id/account", async (c) => {
|
|
494
|
+
const sessionId = c.req.param("id");
|
|
495
|
+
const body = await c.req.json<{ accountId?: string }>().catch(() => ({} as { accountId?: string }));
|
|
496
|
+
if (!body.accountId) return c.json(err("accountId is required"), 400);
|
|
497
|
+
const providerId = getSessionProvider(sessionId) ?? "claude";
|
|
498
|
+
if (!bindPickedAccount(sessionId, providerId, body.accountId)) {
|
|
499
|
+
return c.json(err(bindRefusalReason(providerId, body.accountId)), 400);
|
|
500
|
+
}
|
|
501
|
+
return c.json(ok({ accountId: body.accountId }));
|
|
502
|
+
});
|
|
503
|
+
|
|
476
504
|
/** PUT /chat/sessions/:id/pin — pin a session */
|
|
477
505
|
chatRoutes.put("/sessions/:id/pin", (c) => {
|
|
478
506
|
try {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
2
|
import { ok, err } from "../../types/api.ts";
|
|
3
|
-
import { listCodexAccounts, removeCodexAccount, getAllCodexUsages, getCodexStrategy, setCodexStrategy, type CodexStrategy } from "../../services/codex-account.service.ts";
|
|
3
|
+
import { listCodexAccounts, removeCodexAccount, getAllCodexUsages, getCodexStrategy, setCodexStrategy, selectCodexAccount, setCodexAccountStatus, type CodexStrategy } from "../../services/codex-account.service.ts";
|
|
4
4
|
import { addApiKeyAccount, startDeviceLogin, getDeviceLoginStatus, cancelDeviceLogin } from "../../services/codex-account-login.ts";
|
|
5
5
|
import { exportCodexEncrypted, importCodexEncrypted } from "../../services/codex-account-portability.ts";
|
|
6
6
|
|
|
@@ -12,6 +12,46 @@ codexAccountsRoutes.get("/", (c) => c.json(ok({ accounts: listCodexAccounts(), s
|
|
|
12
12
|
/** Per-account quota map { [accountId]: UsageInfo }. */
|
|
13
13
|
codexAccountsRoutes.get("/usage", async (c) => c.json(ok(await getAllCodexUsages())));
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* POST /api/codex-accounts/pick — claim the account that will serve a new chat tab.
|
|
17
|
+
*
|
|
18
|
+
* Mirrors the Claude side: a consuming pick, so round-robin advances and consecutive tabs
|
|
19
|
+
* land on different accounts. Usage is read once here and handed to the selector, which is
|
|
20
|
+
* what lets it skip accounts with no five-hour room left — the synchronous `peekCodexAccount`
|
|
21
|
+
* has no way to reach that data, which is why the toolbar could never name an account before
|
|
22
|
+
* the first turn under round-robin.
|
|
23
|
+
*
|
|
24
|
+
* Null when no account is managed: chats then run on the ambient ~/.codex login, which has
|
|
25
|
+
* no id to bind and nothing to choose between.
|
|
26
|
+
*/
|
|
27
|
+
codexAccountsRoutes.post("/pick", async (c) => {
|
|
28
|
+
if (listCodexAccounts().length === 0) return c.json(ok(null));
|
|
29
|
+
const usages = await getAllCodexUsages();
|
|
30
|
+
// A failed usage fetch yields {} → +Infinity, which the selector reads as "unknown", not
|
|
31
|
+
// as "capped": an account we could not measure stays a candidate.
|
|
32
|
+
const picked = selectCodexAccount({ usageOf: (id) => usages[id]?.fiveHour ?? Number.POSITIVE_INFINITY });
|
|
33
|
+
if (!picked) return c.json(ok(null));
|
|
34
|
+
return c.json(ok({ id: picked.id, label: picked.label }));
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* PATCH /api/codex-accounts/:id — switch an account on or off.
|
|
39
|
+
*
|
|
40
|
+
* Mirrors the shape of the Claude route so the two panels can share one control, but not its
|
|
41
|
+
* pre-flight token check: that exists for Anthropic OAuth refresh tokens, which have nothing
|
|
42
|
+
* to do with how a Codex account authenticates. Nothing to prove here means this answers
|
|
43
|
+
* immediately instead of taking the better part of a minute.
|
|
44
|
+
*/
|
|
45
|
+
codexAccountsRoutes.patch("/:id", async (c) => {
|
|
46
|
+
const body = await c.req.json<{ status?: string }>().catch(() => ({} as { status?: string }));
|
|
47
|
+
if (body.status !== "active" && body.status !== "disabled") {
|
|
48
|
+
return c.json(err("status must be active or disabled"), 400);
|
|
49
|
+
}
|
|
50
|
+
const updated = setCodexAccountStatus(c.req.param("id"), body.status);
|
|
51
|
+
if (!updated) return c.json(err("Account not found"), 404);
|
|
52
|
+
return c.json(ok(updated));
|
|
53
|
+
});
|
|
54
|
+
|
|
15
55
|
/** Set the selection strategy. */
|
|
16
56
|
codexAccountsRoutes.put("/strategy", async (c) => {
|
|
17
57
|
const body = await c.req.json<{ strategy?: CodexStrategy }>().catch(() => ({} as { strategy?: CodexStrategy }));
|