@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
|
@@ -11,12 +11,19 @@
|
|
|
11
11
|
* An expired account (past `expiresAt` AND no refresh token) is dimmed and loses every
|
|
12
12
|
* control except delete — toggling or exporting a token the server can no longer renew only
|
|
13
13
|
* produces confusing failures.
|
|
14
|
+
*
|
|
15
|
+
* The footer states what the user can act on and nothing else. It deliberately no longer
|
|
16
|
+
* counts down the access token: that number renews itself every few hours, no one can do
|
|
17
|
+
* anything about it, and it is what made two revoked accounts look healthy for six days
|
|
18
|
+
* while every turn on them failed. The sign-in deadline replaced it.
|
|
14
19
|
*/
|
|
15
20
|
|
|
16
|
-
import { Download, Eye, Trash2 } from "lucide-react";
|
|
21
|
+
import { Download, Eye, KeyRound, RefreshCw, Trash2 } from "lucide-react";
|
|
17
22
|
import { Switch } from "@/components/ui/switch";
|
|
18
23
|
import type { AccountInfo, AccountUsageEntry, OAuthProfileData } from "../../../lib/api-settings";
|
|
19
24
|
import { AccountBucketRow } from "./account-bucket-row";
|
|
25
|
+
import { AccountHint } from "./account-hint";
|
|
26
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
|
20
27
|
import { AccountCardShell } from "./accounts-pane-header";
|
|
21
28
|
import { formatExpiry, formatLastUpdated, tokenStatus } from "./account-usage-format";
|
|
22
29
|
|
|
@@ -31,6 +38,16 @@ export interface AccountCardProps {
|
|
|
31
38
|
onViewProfile?: (profile: OAuthProfileData, accountId: string) => void;
|
|
32
39
|
/** Brief highlight when this account's usage numbers just changed. */
|
|
33
40
|
flash?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Move the current chat session onto this account. Omitted in Settings, where a card
|
|
43
|
+
* manages an account rather than routing a conversation to it — leaving it out is what
|
|
44
|
+
* keeps that screen behaving exactly as before.
|
|
45
|
+
*/
|
|
46
|
+
onSelect?: (id: string) => void;
|
|
47
|
+
/** Why this account cannot be selected. Present means the choice is refused, and says so. */
|
|
48
|
+
unselectableReason?: string | null;
|
|
49
|
+
/** A selection is in flight for this card. */
|
|
50
|
+
selecting?: boolean;
|
|
34
51
|
/** `list` fills the width, `strip` is a fixed-width card in a sideways scroller, `grid`
|
|
35
52
|
* fills a cell of the fullscreen grid. */
|
|
36
53
|
layout?: "list" | "strip" | "grid";
|
|
@@ -43,6 +60,7 @@ const STRIP_WIDTH = { readOnly: "min-w-[220px]", withActions: "min-w-[300px]" }
|
|
|
43
60
|
|
|
44
61
|
export function AccountCard({
|
|
45
62
|
entry, isActive, accountInfo, onToggle, toggling, onDelete, onExport, onViewProfile, flash,
|
|
63
|
+
onSelect, unselectableReason, selecting,
|
|
46
64
|
layout = "list",
|
|
47
65
|
}: AccountCardProps) {
|
|
48
66
|
const { usage } = entry;
|
|
@@ -53,7 +71,32 @@ export function AccountCard({
|
|
|
53
71
|
&& accountInfo.expiresAt < Math.floor(Date.now() / 1000)
|
|
54
72
|
);
|
|
55
73
|
const ts = tokenStatus(accountInfo);
|
|
74
|
+
// Distinct from isExpired: this account still holds a refresh token, it is just one the
|
|
75
|
+
// server will not honour. Dim it like an expired card, but keep every control — signing
|
|
76
|
+
// in again goes through the same add flow, and delete has to stay reachable.
|
|
77
|
+
const needsReauth = !!accountInfo?.reauthRequired;
|
|
78
|
+
// A sign-in dies on a fixed schedule regardless of use, so the warning has to lead the
|
|
79
|
+
// last stretch of it rather than appear once it is already too late.
|
|
80
|
+
const grantExpiresAtMs = accountInfo?.grantExpiresAt ? accountInfo.grantExpiresAt * 1000 : null;
|
|
81
|
+
const grantEndingSoon = !!grantExpiresAtMs && grantExpiresAtMs - Date.now() < 5 * 86_400_000;
|
|
82
|
+
// The header already badges Expired, Sign in again and API key, and "long-lived" only
|
|
83
|
+
// restates what the sign-in chip beside it implies. That leaves the two states nothing
|
|
84
|
+
// else announces: a temporary token with no way to renew, and one that has lapsed but
|
|
85
|
+
// still has a refresh token behind it.
|
|
86
|
+
const showTokenStatus = ts.label === "temp" || ts.label === "unknown"
|
|
87
|
+
|| (ts.label === "expired" && !isExpired && !needsReauth);
|
|
88
|
+
const grantExpiresOn = grantExpiresAtMs
|
|
89
|
+
? new Date(grantExpiresAtMs).toLocaleString(undefined, { dateStyle: "medium", timeStyle: "short" })
|
|
90
|
+
: null;
|
|
56
91
|
const hasActions = Boolean(onToggle || onDelete || onExport || onViewProfile);
|
|
92
|
+
// Every state that already dims this card also refuses the chat to it. `needsReauth` is
|
|
93
|
+
// the one worth naming separately: the token is present and unexpired, so nothing else
|
|
94
|
+
// here looks wrong, but the server has rejected it and no turn can run on it.
|
|
95
|
+
const refusedReason = isExpired
|
|
96
|
+
? "Token expired — sign in again"
|
|
97
|
+
: needsReauth
|
|
98
|
+
? "Sign in again to use this account"
|
|
99
|
+
: unselectableReason ?? null;
|
|
57
100
|
|
|
58
101
|
const layoutClass = layout === "list"
|
|
59
102
|
? ""
|
|
@@ -67,7 +110,7 @@ export function AccountCard({
|
|
|
67
110
|
active={isActive}
|
|
68
111
|
flash={flash}
|
|
69
112
|
dense={layout !== "list"}
|
|
70
|
-
className={[layoutClass, isExpired ? "opacity-50" : ""].filter(Boolean).join(" ") || undefined}
|
|
113
|
+
className={[layoutClass, isExpired || needsReauth ? "opacity-50" : ""].filter(Boolean).join(" ") || undefined}
|
|
71
114
|
data-testid="account-card"
|
|
72
115
|
data-account-id={entry.accountId}
|
|
73
116
|
>
|
|
@@ -75,32 +118,90 @@ export function AccountCard({
|
|
|
75
118
|
<span className="text-sm font-medium truncate flex-1 min-w-0">
|
|
76
119
|
{entry.accountLabel ?? entry.accountId.slice(0, 8)}
|
|
77
120
|
</span>
|
|
78
|
-
{isActive &&
|
|
79
|
-
|
|
121
|
+
{isActive && (
|
|
122
|
+
<AccountHint className="text-[10px] text-primary shrink-0 font-medium" hint="The next turn will run on this account.">
|
|
123
|
+
Active
|
|
124
|
+
</AccountHint>
|
|
125
|
+
)}
|
|
126
|
+
{/* Occupies the slot the "Active" badge would, so a card is the same height whether
|
|
127
|
+
it is the one serving or one you could switch to. A row of its own below cost
|
|
128
|
+
every card that height, including the ones whose button did nothing. */}
|
|
129
|
+
{onSelect && !isActive && (
|
|
130
|
+
// Shown even when the account cannot serve, and clicking it then reports why.
|
|
131
|
+
// Hiding it instead left a blank where the other cards have a control, which reads
|
|
132
|
+
// as a missing feature rather than as a refusal with a reason behind it.
|
|
133
|
+
<Tooltip>
|
|
134
|
+
<TooltipTrigger asChild>
|
|
135
|
+
<button
|
|
136
|
+
type="button"
|
|
137
|
+
onClick={() => onSelect(entry.accountId)}
|
|
138
|
+
disabled={selecting}
|
|
139
|
+
className={[
|
|
140
|
+
"shrink-0 rounded px-1.5 py-0.5 text-[10px] font-medium transition-colors cursor-pointer disabled:cursor-wait",
|
|
141
|
+
refusedReason
|
|
142
|
+
? "text-text-subtle hover:text-error hover:bg-error/10"
|
|
143
|
+
: "text-text-secondary hover:text-foreground hover:bg-surface-elevated",
|
|
144
|
+
].join(" ")}
|
|
145
|
+
>
|
|
146
|
+
{selecting ? "Switching…" : "Use"}
|
|
147
|
+
</button>
|
|
148
|
+
</TooltipTrigger>
|
|
149
|
+
<TooltipContent side="top">{refusedReason ?? "Use this account for this chat"}</TooltipContent>
|
|
150
|
+
</Tooltip>
|
|
151
|
+
)}
|
|
152
|
+
{isExpired && (
|
|
153
|
+
<AccountHint
|
|
154
|
+
className="text-[10px] text-error shrink-0 font-medium"
|
|
155
|
+
hint="This token has expired and carries no refresh token, so nothing can renew it. Add the account again."
|
|
156
|
+
>
|
|
157
|
+
Expired
|
|
158
|
+
</AccountHint>
|
|
159
|
+
)}
|
|
160
|
+
{needsReauth && !isExpired && (
|
|
161
|
+
<AccountHint
|
|
162
|
+
className="text-[10px] text-error shrink-0 font-medium"
|
|
163
|
+
hint="Anthropic rejected this account's refresh token, so no turn can run on it. Signing in again is the only thing that restores it."
|
|
164
|
+
>
|
|
165
|
+
Sign in again
|
|
166
|
+
</AccountHint>
|
|
167
|
+
)}
|
|
80
168
|
{!entry.isOAuth && !isExpired && (
|
|
81
|
-
<
|
|
169
|
+
<AccountHint
|
|
170
|
+
className="text-[10px] text-text-subtle shrink-0"
|
|
171
|
+
hint="A static API key rather than a sign-in: nothing to expire and nothing to renew."
|
|
172
|
+
>
|
|
173
|
+
API key
|
|
174
|
+
</AccountHint>
|
|
82
175
|
)}
|
|
83
176
|
|
|
84
177
|
<div className="flex items-center gap-0.5 shrink-0">
|
|
85
178
|
{!isExpired && onViewProfile && accountInfo?.profileData && (
|
|
86
|
-
<
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
179
|
+
<Tooltip>
|
|
180
|
+
<TooltipTrigger asChild>
|
|
181
|
+
<button
|
|
182
|
+
className="p-2 rounded cursor-pointer text-text-subtle hover:text-foreground hover:bg-surface-elevated transition-colors"
|
|
183
|
+
onClick={() => onViewProfile(accountInfo.profileData!, entry.accountId)}
|
|
184
|
+
aria-label="View profile"
|
|
185
|
+
>
|
|
186
|
+
<Eye className="size-4" />
|
|
187
|
+
</button>
|
|
188
|
+
</TooltipTrigger>
|
|
189
|
+
<TooltipContent side="top">View profile</TooltipContent>
|
|
190
|
+
</Tooltip>
|
|
94
191
|
)}
|
|
95
192
|
{!isExpired && onExport && entry.isOAuth && (
|
|
96
|
-
<
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
193
|
+
<Tooltip>
|
|
194
|
+
<TooltipTrigger asChild>
|
|
195
|
+
<button
|
|
196
|
+
className="p-2 rounded cursor-pointer text-text-subtle hover:text-primary hover:bg-surface-elevated transition-colors"
|
|
197
|
+
onClick={() => onExport(entry.accountId)}
|
|
198
|
+
aria-label="Export this account"
|
|
199
|
+
>
|
|
200
|
+
<Download className="size-4" />
|
|
201
|
+
</button>
|
|
202
|
+
</TooltipTrigger>
|
|
203
|
+
<TooltipContent side="top">Export this account</TooltipContent>
|
|
204
|
+
</Tooltip>
|
|
104
205
|
)}
|
|
105
206
|
{!isExpired && onToggle && (
|
|
106
207
|
<Switch
|
|
@@ -112,14 +213,18 @@ export function AccountCard({
|
|
|
112
213
|
/>
|
|
113
214
|
)}
|
|
114
215
|
{onDelete && (
|
|
115
|
-
<
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
216
|
+
<Tooltip>
|
|
217
|
+
<TooltipTrigger asChild>
|
|
218
|
+
<button
|
|
219
|
+
className="p-2 rounded cursor-pointer text-text-subtle hover:text-error hover:bg-surface-elevated transition-colors"
|
|
220
|
+
onClick={() => onDelete(entry.accountId, entry.accountLabel ?? entry.accountId.slice(0, 8))}
|
|
221
|
+
aria-label="Remove account"
|
|
222
|
+
>
|
|
223
|
+
<Trash2 className="size-4" />
|
|
224
|
+
</button>
|
|
225
|
+
</TooltipTrigger>
|
|
226
|
+
<TooltipContent side="top">Remove account</TooltipContent>
|
|
227
|
+
</Tooltip>
|
|
123
228
|
)}
|
|
124
229
|
</div>
|
|
125
230
|
</div>
|
|
@@ -139,13 +244,28 @@ export function AccountCard({
|
|
|
139
244
|
|
|
140
245
|
<div className="flex items-center gap-2 text-[10px] text-text-subtle flex-wrap">
|
|
141
246
|
{usage.lastFetchedAt && (
|
|
142
|
-
<
|
|
247
|
+
<AccountHint
|
|
248
|
+
className="inline-flex items-center gap-1"
|
|
249
|
+
hint={`Usage last read from Anthropic on ${new Date(usage.lastFetchedAt).toLocaleString(undefined, { dateStyle: "medium", timeStyle: "short" })}. The bars above are only as current as this.`}
|
|
250
|
+
>
|
|
251
|
+
<RefreshCw className="size-3 shrink-0" aria-hidden />
|
|
252
|
+
{formatLastUpdated(new Date(usage.lastFetchedAt).getTime())}
|
|
253
|
+
</AccountHint>
|
|
254
|
+
)}
|
|
255
|
+
{grantExpiresAtMs && !needsReauth && (
|
|
256
|
+
<AccountHint
|
|
257
|
+
className={["inline-flex items-center gap-1", grantEndingSoon ? "text-warning" : ""].filter(Boolean).join(" ")}
|
|
258
|
+
hint={`Sign-in stops working on ${grantExpiresOn}. Anthropic ends every sign-in on a fixed schedule from the day it was made, whether or not the account gets used — refreshing does not extend it. After that the account fails until you sign in again.`}
|
|
259
|
+
>
|
|
260
|
+
<KeyRound className="size-3 shrink-0" aria-hidden />
|
|
261
|
+
sign-in {formatExpiry(grantExpiresAtMs)}
|
|
262
|
+
</AccountHint>
|
|
143
263
|
)}
|
|
144
|
-
{
|
|
145
|
-
<
|
|
264
|
+
{showTokenStatus && (
|
|
265
|
+
<AccountHint className={ts.color} hint={ts.tip}>© {ts.label}</AccountHint>
|
|
146
266
|
)}
|
|
147
|
-
<span className={ts.color} title={ts.tip}>© {ts.label}</span>
|
|
148
267
|
</div>
|
|
268
|
+
|
|
149
269
|
</AccountCardShell>
|
|
150
270
|
);
|
|
151
271
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A card label that carries an explanation.
|
|
3
|
+
*
|
|
4
|
+
* The trigger is a button rather than a span on purpose. Radix opens a tooltip on focus as
|
|
5
|
+
* well as hover, so tapping the chip reaches the explanation on touch, where hover does not
|
|
6
|
+
* exist at all — `docs/design-guidelines.md` rules out hiding anything behind hover alone.
|
|
7
|
+
* The visible label still carries the information; this only makes the reasoning behind it
|
|
8
|
+
* reachable on every device rather than on desktop only.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { ReactNode } from "react";
|
|
12
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
|
13
|
+
|
|
14
|
+
export interface AccountHintProps {
|
|
15
|
+
hint: ReactNode;
|
|
16
|
+
className?: string;
|
|
17
|
+
children: ReactNode;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function AccountHint({ hint, className, children }: AccountHintProps) {
|
|
21
|
+
return (
|
|
22
|
+
<Tooltip>
|
|
23
|
+
<TooltipTrigger asChild>
|
|
24
|
+
<button type="button" className={["cursor-help text-left", className].filter(Boolean).join(" ")}>
|
|
25
|
+
{children}
|
|
26
|
+
</button>
|
|
27
|
+
</TooltipTrigger>
|
|
28
|
+
<TooltipContent side="top" className="max-w-[260px] text-xs leading-relaxed">
|
|
29
|
+
{hint}
|
|
30
|
+
</TooltipContent>
|
|
31
|
+
</Tooltip>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
@@ -49,6 +49,24 @@ export function formatResetTime(bucket?: LimitBucket): string | null {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
/** Time until a token expires. Takes milliseconds. */
|
|
52
|
+
/**
|
|
53
|
+
* The exact moment a bucket resets, for the tooltip behind the relative "↻ 2h 10m".
|
|
54
|
+
*
|
|
55
|
+
* The relative figure is the right thing on the card — it answers "can I keep working" at a
|
|
56
|
+
* glance — but it cannot answer "will this be back before my meeting", and it goes stale as
|
|
57
|
+
* soon as it is rendered. The absolute time answers both, so it belongs one hover away rather
|
|
58
|
+
* than competing for the same few pixels.
|
|
59
|
+
*
|
|
60
|
+
* Rendered in the viewer's own locale and zone: the server sends UTC, and a reset time shown
|
|
61
|
+
* in UTC is a small puzzle to solve at exactly the moment someone is in a hurry.
|
|
62
|
+
*/
|
|
63
|
+
export function formatResetAt(resetsAt?: string | null): string | null {
|
|
64
|
+
if (!resetsAt) return null;
|
|
65
|
+
const ms = new Date(resetsAt).getTime();
|
|
66
|
+
if (!Number.isFinite(ms)) return null;
|
|
67
|
+
return new Date(ms).toLocaleString(undefined, { dateStyle: "medium", timeStyle: "short" });
|
|
68
|
+
}
|
|
69
|
+
|
|
52
70
|
export function formatExpiry(expiresAtMs: number): string {
|
|
53
71
|
const diff = expiresAtMs - Date.now();
|
|
54
72
|
if (diff <= 0) return "expired";
|
|
@@ -65,10 +83,18 @@ export function formatExpiry(expiresAtMs: number): string {
|
|
|
65
83
|
*
|
|
66
84
|
* An expired token with a refresh token is only a warning, not an error: the server renews
|
|
67
85
|
* it on the next call. Without one it is genuinely dead, which is why the two cases differ.
|
|
86
|
+
*
|
|
87
|
+
* Unless the server has already refused to renew it. PPM kept a rejected account holding its
|
|
88
|
+
* refresh token — deliberately, so a re-import can recover it — and this function read that
|
|
89
|
+
* as "will auto-renew" for six days while every turn on the account failed. A rejection
|
|
90
|
+
* outranks both other cases.
|
|
68
91
|
*/
|
|
69
92
|
export function tokenStatus(info?: AccountInfo): { label: string; tip: string; color: string } {
|
|
70
93
|
if (!info) return { label: "unknown", tip: "No account info available", color: "text-text-subtle" };
|
|
71
94
|
if (!info.expiresAt) return { label: "key", tip: "API key (no expiry)", color: "text-text-subtle" };
|
|
95
|
+
if (info.reauthRequired) {
|
|
96
|
+
return { label: "sign in again", tip: "Anthropic rejected this account's refresh token. Sign in again to restore it.", color: "text-error" };
|
|
97
|
+
}
|
|
72
98
|
const expired = info.expiresAt * 1000 < Date.now(); // expiresAt is seconds
|
|
73
99
|
if (expired && info.hasRefreshToken) return { label: "expired", tip: "Token expired but has refresh token — will auto-renew", color: "text-warning" };
|
|
74
100
|
if (expired) return { label: "expired", tip: "Token expired, no refresh token", color: "text-error" };
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
import { useState } from "react";
|
|
10
10
|
import { Download, KeyRound, Loader2, Plus, Settings, Trash2, Upload } from "lucide-react";
|
|
11
11
|
import { Button } from "@/components/ui/button";
|
|
12
|
+
import { Switch } from "@/components/ui/switch";
|
|
13
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
|
12
14
|
import { AccountCardRow, AccountCardShell, AccountsPaneHeader, AccountsPaneMessage } from "./accounts-pane-header";
|
|
13
15
|
import { CodexAddAccountDialog } from "./codex-add-account-dialog";
|
|
14
16
|
import { CodexBackupDialog } from "./codex-backup-dialog";
|
|
@@ -85,15 +87,28 @@ export function CodexAccountsSection() {
|
|
|
85
87
|
{a.type}
|
|
86
88
|
</span>
|
|
87
89
|
{a.planType && <span className="text-[10px] text-text-subtle shrink-0">{a.planType}</span>}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
90
|
+
{/* Same control the Claude card uses, in the same place, so the two
|
|
91
|
+
sub-tabs read as one screen rather than two designs. */}
|
|
92
|
+
<Switch
|
|
93
|
+
checked={a.status !== "disabled"}
|
|
94
|
+
onCheckedChange={() => void c.toggle(a.id, a.status ?? "active")}
|
|
95
|
+
disabled={c.toggling.has(a.id)}
|
|
96
|
+
aria-label={a.status === "disabled" ? "Enable account" : "Disable account"}
|
|
97
|
+
className="cursor-pointer shrink-0"
|
|
98
|
+
/>
|
|
99
|
+
<Tooltip>
|
|
100
|
+
<TooltipTrigger asChild>
|
|
101
|
+
<button
|
|
102
|
+
type="button"
|
|
103
|
+
onClick={() => void c.remove(a.id)}
|
|
104
|
+
aria-label="Remove account"
|
|
105
|
+
className="p-2 rounded cursor-pointer text-text-subtle hover:text-error hover:bg-surface-elevated transition-colors shrink-0"
|
|
106
|
+
>
|
|
107
|
+
<Trash2 className="size-4" />
|
|
108
|
+
</button>
|
|
109
|
+
</TooltipTrigger>
|
|
110
|
+
<TooltipContent side="top">Remove account</TooltipContent>
|
|
111
|
+
</Tooltip>
|
|
97
112
|
</div>
|
|
98
113
|
<CodexUsageRows usage={u} />
|
|
99
114
|
</AccountCardShell>
|
|
@@ -19,8 +19,8 @@ function toPct(v?: number): number | null { return v != null ? Math.round(v * 10
|
|
|
19
19
|
export function CodexUsageRows({ usage }: { usage: Usage }) {
|
|
20
20
|
return (
|
|
21
21
|
<div className="space-y-2">
|
|
22
|
-
<AccountUsageBar label="5-Hour Session" pct={toPct(usage.fiveHour)} reset={formatResetTime(usage.session)} />
|
|
23
|
-
<AccountUsageBar label="Weekly" pct={toPct(usage.sevenDay)} reset={formatResetTime(usage.weekly)} />
|
|
22
|
+
<AccountUsageBar label="5-Hour Session" pct={toPct(usage.fiveHour)} reset={formatResetTime(usage.session)} resetsAt={usage.session?.resetsAt} />
|
|
23
|
+
<AccountUsageBar label="Weekly" pct={toPct(usage.sevenDay)} reset={formatResetTime(usage.weekly)} resetsAt={usage.weekly?.resetsAt} />
|
|
24
24
|
</div>
|
|
25
25
|
);
|
|
26
26
|
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
10
10
|
import {
|
|
11
|
-
getAccounts, getActiveAccount, getAllAccountUsages,
|
|
11
|
+
getAccounts, getActiveAccount, getAllAccountUsages, patchAccount,
|
|
12
12
|
type AccountInfo, type AccountUsageEntry,
|
|
13
13
|
} from "../../../lib/api-settings";
|
|
14
14
|
|
|
@@ -22,6 +22,10 @@ export interface AccountsData {
|
|
|
22
22
|
/** Accounts whose utilisation changed on the last refresh — briefly highlighted. */
|
|
23
23
|
flashIds: Set<string>;
|
|
24
24
|
reload: () => Promise<void>;
|
|
25
|
+
/** Switch an account on or off, reloading when it lands. Resolves to an error message, or null. */
|
|
26
|
+
toggle: (id: string, status: string) => Promise<string | null>;
|
|
27
|
+
/** Account whose switch is mid-flight, or null. */
|
|
28
|
+
togglingId: string | null;
|
|
25
29
|
}
|
|
26
30
|
|
|
27
31
|
/** Utilisation across all four buckets, as a comparable string. */
|
|
@@ -37,6 +41,7 @@ export function useAccountsData(enabled = true): AccountsData {
|
|
|
37
41
|
const [initialLoading, setInitialLoading] = useState(true);
|
|
38
42
|
const [refreshing, setRefreshing] = useState(false);
|
|
39
43
|
const [flashIds, setFlashIds] = useState<Set<string>>(new Set());
|
|
44
|
+
const [togglingId, setTogglingId] = useState<string | null>(null);
|
|
40
45
|
const prevUsages = useRef<AccountUsageEntry[]>([]);
|
|
41
46
|
const flashTimer = useRef<ReturnType<typeof setTimeout>>(undefined);
|
|
42
47
|
|
|
@@ -72,10 +77,35 @@ export function useAccountsData(enabled = true): AccountsData {
|
|
|
72
77
|
setRefreshing(false);
|
|
73
78
|
}, []);
|
|
74
79
|
|
|
80
|
+
/**
|
|
81
|
+
* Switch an account on or off.
|
|
82
|
+
*
|
|
83
|
+
* Lives here rather than in either pane so Settings and the chat toolbar drive one
|
|
84
|
+
* implementation. Two of them is how those screens drifted apart before, and the drift is
|
|
85
|
+
* invisible until someone notices the same switch behaving differently in two places.
|
|
86
|
+
*
|
|
87
|
+
* Returns the server's message instead of throwing: enabling a parked account makes the
|
|
88
|
+
* server prove its token first, which can take most of a minute and can come back 400 with
|
|
89
|
+
* a message worth reading — "sign in again" and "could not reach Anthropic" call for
|
|
90
|
+
* different actions, and swallowing the difference leaves the switch silently snapping back.
|
|
91
|
+
*/
|
|
92
|
+
const toggle = useCallback(async (id: string, status: string): Promise<string | null> => {
|
|
93
|
+
setTogglingId(id);
|
|
94
|
+
let error: string | null = null;
|
|
95
|
+
try {
|
|
96
|
+
await patchAccount(id, { status: status === "disabled" ? "active" : "disabled" });
|
|
97
|
+
} catch (e) {
|
|
98
|
+
error = (e as Error).message;
|
|
99
|
+
}
|
|
100
|
+
setTogglingId(null);
|
|
101
|
+
await reload();
|
|
102
|
+
return error;
|
|
103
|
+
}, [reload]);
|
|
104
|
+
|
|
75
105
|
useEffect(() => {
|
|
76
106
|
if (enabled) void reload();
|
|
77
107
|
return () => clearTimeout(flashTimer.current);
|
|
78
108
|
}, [enabled, reload]);
|
|
79
109
|
|
|
80
|
-
return { usages, accounts, activeAccountId, initialLoading, refreshing, flashIds, reload };
|
|
110
|
+
return { usages, accounts, activeAccountId, initialLoading, refreshing, flashIds, reload, toggle, togglingId };
|
|
81
111
|
}
|
|
@@ -15,7 +15,7 @@ import type { LimitBucket } from "../../../../types/chat";
|
|
|
15
15
|
import type { CodexStrategy } from "./codex-rotation-dialog";
|
|
16
16
|
|
|
17
17
|
export type Strategy = CodexStrategy;
|
|
18
|
-
export interface CodexAccount { id: string; label: string; type: string; planType?: string | null }
|
|
18
|
+
export interface CodexAccount { id: string; label: string; type: string; planType?: string | null; status?: "active" | "disabled" }
|
|
19
19
|
/**
|
|
20
20
|
* One account's quota. The two percentages are what the bars read; the buckets carry the
|
|
21
21
|
* reset clock the server already sends, so the Codex card can show "resets in" the way the
|
|
@@ -41,6 +41,7 @@ export function useCodexAccounts(onDone: () => void) {
|
|
|
41
41
|
const [strategy, setStrategy] = useState<Strategy>("round-robin");
|
|
42
42
|
const [usages, setUsages] = useState<Record<string, Usage>>({});
|
|
43
43
|
const [loading, setLoading] = useState(false);
|
|
44
|
+
const [toggling, setToggling] = useState<Set<string>>(new Set());
|
|
44
45
|
const [apiKey, setApiKey] = useState("");
|
|
45
46
|
const [label, setLabel] = useState("");
|
|
46
47
|
const [adding, setAdding] = useState(false);
|
|
@@ -117,6 +118,26 @@ export function useCodexAccounts(onDone: () => void) {
|
|
|
117
118
|
};
|
|
118
119
|
|
|
119
120
|
const remove = async (id: string) => { await api.del(`/api/codex-accounts/${id}`); await load(); };
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Switch an account on or off.
|
|
124
|
+
*
|
|
125
|
+
* Tracked per account rather than with one shared flag: the pane shows every account side
|
|
126
|
+
* by side, and a single flag would grey out all of them while one is changing.
|
|
127
|
+
*/
|
|
128
|
+
const toggle = async (id: string, status: string) => {
|
|
129
|
+
const next = status === "disabled" ? "active" : "disabled";
|
|
130
|
+
setToggling((prev) => new Set(prev).add(id));
|
|
131
|
+
setErr(null);
|
|
132
|
+
try {
|
|
133
|
+
await api.patch(`/api/codex-accounts/${id}`, { status: next });
|
|
134
|
+
await load();
|
|
135
|
+
} catch (e) {
|
|
136
|
+
setErr((e as Error).message || "Could not change the account");
|
|
137
|
+
} finally {
|
|
138
|
+
setToggling((prev) => { const n = new Set(prev); n.delete(id); return n; });
|
|
139
|
+
}
|
|
140
|
+
};
|
|
120
141
|
const changeStrategy = async (s: Strategy) => { setStrategy(s); try { await api.put("/api/codex-accounts/strategy", { strategy: s }); } catch { /* revert on reload */ } };
|
|
121
142
|
|
|
122
143
|
const doExport = async () => {
|
|
@@ -157,6 +178,7 @@ export function useCodexAccounts(onDone: () => void) {
|
|
|
157
178
|
accounts, strategy, usages, loading, apiKey, setApiKey, label, setLabel, adding,
|
|
158
179
|
err, setErr, device, deviceWaiting, backupPassword, setBackupPassword,
|
|
159
180
|
exporting, importing, msg, setMsg,
|
|
160
|
-
|
|
181
|
+
toggling,
|
|
182
|
+
load, addApiKey, startDevice, remove, toggle, changeStrategy, doExport, doImport,
|
|
161
183
|
};
|
|
162
184
|
}
|
|
@@ -66,6 +66,13 @@ interface UseChatReturn {
|
|
|
66
66
|
compactStatus: "compacting" | null;
|
|
67
67
|
statusMessage: string | null;
|
|
68
68
|
sessionTitle: string | null;
|
|
69
|
+
/**
|
|
70
|
+
* Account the server last reported as serving this session, from the stream rather than
|
|
71
|
+
* the polled usage endpoint — so a forced account switch shows up immediately instead of
|
|
72
|
+
* up to two minutes later. Carries the id as well as the label because the panel marks the
|
|
73
|
+
* serving card by id.
|
|
74
|
+
*/
|
|
75
|
+
liveAccount: { id: string; label: string | null } | null;
|
|
69
76
|
/** Per-session model override (null = provider default) */
|
|
70
77
|
model: string | null;
|
|
71
78
|
/** Switch the per-session model (persists + recreates query on next message) */
|
|
@@ -166,6 +173,17 @@ export function useChat(
|
|
|
166
173
|
const streamingEventsRef = useRef<ChatEvent[]>([]);
|
|
167
174
|
const bashOutputRef = useRef<Map<string, BashPartialEntry>>(new Map());
|
|
168
175
|
const streamingAccountRef = useRef<{ accountId: string; accountLabel: string } | null>(null);
|
|
176
|
+
/**
|
|
177
|
+
* Which account last reported serving this session, as state rather than a ref so the
|
|
178
|
+
* toolbar re-renders on it.
|
|
179
|
+
*
|
|
180
|
+
* Deliberately sticky across turns: it is cleared when the session changes, not when a
|
|
181
|
+
* turn ends. The account that just served is still the account bound to the session, and
|
|
182
|
+
* the only other source for that label is the usage endpoint on a two-minute poll — so
|
|
183
|
+
* dropping it at turn end would leave the chip showing a stale name for minutes after the
|
|
184
|
+
* server was forced onto a different account.
|
|
185
|
+
*/
|
|
186
|
+
const [liveAccount, setLiveAccount] = useState<{ id: string; label: string | null } | null>(null);
|
|
169
187
|
const phaseRef = useRef<SessionPhase>("idle");
|
|
170
188
|
const pendingMessageRef = useRef<string | null>(null);
|
|
171
189
|
const sendRef = useRef<(data: string) => void>(() => {});
|
|
@@ -402,6 +420,7 @@ export function useChat(
|
|
|
402
420
|
switch (evType) {
|
|
403
421
|
case "account_info": {
|
|
404
422
|
streamingAccountRef.current = { accountId: ev.accountId, accountLabel: ev.accountLabel };
|
|
423
|
+
setLiveAccount({ id: ev.accountId, label: ev.accountLabel ?? null });
|
|
405
424
|
setStatusMessage(null);
|
|
406
425
|
break;
|
|
407
426
|
}
|
|
@@ -410,6 +429,9 @@ export function useChat(
|
|
|
410
429
|
// Update streaming account to the new one being tried
|
|
411
430
|
if (ev.accountId && ev.accountLabel) {
|
|
412
431
|
streamingAccountRef.current = { accountId: ev.accountId, accountLabel: ev.accountLabel };
|
|
432
|
+
// A retry means the server was forced off the account the toolbar is naming —
|
|
433
|
+
// rate limit, usage cap or auth. Say so now rather than at the next usage poll.
|
|
434
|
+
setLiveAccount({ id: ev.accountId, label: ev.accountLabel });
|
|
413
435
|
}
|
|
414
436
|
// Clear previous streaming events (error text from failed attempt)
|
|
415
437
|
// and start fresh with only the retry notification
|
|
@@ -961,6 +983,9 @@ export function useChat(
|
|
|
961
983
|
if (prevSessionIdRef.current && prevSessionIdRef.current !== sessionId) {
|
|
962
984
|
pendingModelRef.current = null;
|
|
963
985
|
thinkingRef.current = null;
|
|
986
|
+
// One session's account must not label another's. Only on a real session-to-session
|
|
987
|
+
// switch: the draft→real transition (null → id) keeps serving the same conversation.
|
|
988
|
+
setLiveAccount(null);
|
|
964
989
|
}
|
|
965
990
|
prevSessionIdRef.current = sessionId ?? null;
|
|
966
991
|
|
|
@@ -1359,6 +1384,8 @@ export function useChat(
|
|
|
1359
1384
|
compactStatus,
|
|
1360
1385
|
statusMessage,
|
|
1361
1386
|
sessionTitle,
|
|
1387
|
+
/** Account the server last reported for this session — beats the polled usage label. */
|
|
1388
|
+
liveAccount,
|
|
1362
1389
|
model,
|
|
1363
1390
|
setModel,
|
|
1364
1391
|
effort,
|