@hienlh/ppm 0.19.12 → 0.20.1
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 +36 -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-_sG1AqtH.js +1 -0
- package/dist/web/assets/{ai-resource-editor-CqL-X2zu.js → ai-resource-editor-BLctP1Pr.js} +1 -1
- package/dist/web/assets/ai-settings-section-0hSDp7xx.js +1 -0
- package/dist/web/assets/{ai-settings-section-CcqokdzF.js → ai-settings-section-CTEU2uGa.js} +1 -1
- 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-BkcoSV0E.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-CJi4uYb9.js +15 -0
- package/dist/web/assets/{cobol-Bo0usdoM.js → cobol-BreMVrtc.js} +1 -1
- package/dist/web/assets/code-editor-C4kKhamq.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-BEULKwHk.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-k3eM8nFf.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-Cslir1dr.js} +1 -1
- package/dist/web/assets/diff-viewer-DzAIvBbn.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-xdDQvPhs.js} +2 -2
- 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-D6H-4cz9.js} +2 -2
- package/dist/web/assets/explorer-window-content-C5W_JzCz.js +1 -0
- package/dist/web/assets/{extension-manager-section-Zc9yag2c.js → extension-manager-section-DwicI3CA.js} +1 -1
- package/dist/web/assets/{extension-webview-B5FayDPz.js → extension-webview-Z3HhC8Pd.js} +1 -1
- package/dist/web/assets/{files-settings-section-CxAtO3GL.js → files-settings-section-ByV7bRbB.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-nLksKHur.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-iUXltiTB.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-C1QB4DHt.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-ezXKVVpg.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-CJWbFurD.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-DwARwskB.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-S7YLGd8B.js +1 -0
- package/dist/web/assets/{keyboard-shortcuts-section-Dv4e3M8h.js → keyboard-shortcuts-section-BoUDvdDy.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-DKdfiSIX.js} +3 -3
- package/dist/web/assets/markdown-renderer-DYFmMC_8.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-CMdqLCV-.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-DpSYFj36.js +1 -0
- package/dist/web/assets/{notifications-settings-section-B2GkfB0l.js → notifications-settings-section-CIf3-3xd.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-C-7IdkIK.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-BYXFYlLg.js} +3 -3
- package/dist/web/assets/{ppmbot-settings-section-OYfVG5-l.js → ppmbot-settings-section-B9dXvQ6p.js} +1 -1
- package/dist/web/assets/{process-table-DRu3NyO_.js → process-table-B6NeTWt8.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-CUqq7pdU.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-C1TYrPIp.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-DuKwKe5F.js +2 -0
- package/dist/web/assets/{remote-desktop-mobile-view-B4BSHIxU.js → remote-desktop-mobile-view-DxMTtsa6.js} +1 -1
- package/dist/web/assets/{remote-desktop-readiness-gate-C0d0PSX4.js → remote-desktop-readiness-gate-BwjfLWqg.js} +1 -1
- package/dist/web/assets/{remote-desktop-window-content-DjeCZzqq.js → remote-desktop-window-content-BIl4N5Hn.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-C0qOb7GI.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-qbrJXX_A.js} +2 -2
- package/dist/web/assets/settings-store-CzLnTR8p.js +2 -0
- package/dist/web/assets/settings-tab-BKRgYVxf.js +1 -0
- package/dist/web/assets/settings-window-content-DWsxJDu-.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-CsZOpCW3.js} +1 -1
- package/dist/web/assets/{sqlite-viewer-CpUhDEhN.js → sqlite-viewer-DUrnCaP7.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-C5cMpg7I.js} +2 -2
- package/dist/web/assets/system-monitor-tab-C66ywbVZ.js +1 -0
- package/dist/web/assets/{system-monitor-window-content-DIIP3dff.js → system-monitor-window-content-CENaXYJ-.js} +1 -1
- package/dist/web/assets/{tab-host-window-content-CLdcz84K.js → tab-host-window-content-HViABGoS.js} +1 -1
- package/dist/web/assets/{tab-store-PZsP5ZwG.js → tab-store-1xYJb0gM.js} +1 -1
- package/dist/web/assets/team-member-sheet-CK6eyBRL.js +2 -0
- package/dist/web/assets/team-member-window-content-D6wxg37P.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-BmyTawmN.js} +2 -2
- package/dist/web/assets/{tex-C8XwrbBw.js → tex-IQ8mELOR.js} +1 -1
- package/dist/web/assets/tool-cards-y-XJcJHo.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-BbOb2QNP.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-BkM5rMgL.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-BT2r1td1.js} +1 -1
- package/dist/web/assets/{use-websocket-kR1omKO1.js → use-websocket-BvVTwMjI.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-BEaofjN7.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/edit-diff-preview.tsx +14 -7
- 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/components/shared/markdown-code-block.tsx +10 -5
- 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
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The chat header's usage chip and the panel it opens.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* Adding, removing, exporting and rotating accounts live in Settings → Accounts; this panel
|
|
5
|
+
* links there rather than carrying its own copy of them. What it does carry is the two
|
|
6
|
+
* decisions that belong to the conversation in front of you: which account serves it, and
|
|
7
|
+
* whether an account is available at all.
|
|
8
|
+
*
|
|
9
|
+
* The rule the original separation was protecting still holds, and is what makes those two
|
|
10
|
+
* safe to have here: the cards are the same component the Settings pane renders, and the
|
|
11
|
+
* switch drives the same `useAccountsData().toggle` that pane drives. One implementation, two
|
|
12
|
+
* places it appears — not a display twin that can drift.
|
|
9
13
|
*
|
|
10
14
|
* Accounts sit in a row that scrolls sideways, not a vertical stack: this panel exists to
|
|
11
15
|
* compare them, and stacked in a 350px strip that meant scrolling past one account to see
|
|
@@ -15,11 +19,17 @@
|
|
|
15
19
|
import { useState } from "react";
|
|
16
20
|
import { Activity, ExternalLink, Maximize2, Minimize2, RefreshCw, X } from "lucide-react";
|
|
17
21
|
import type { UsageInfo } from "../../../types/chat";
|
|
22
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
|
18
23
|
import { openSettings } from "@/components/settings/open-settings";
|
|
19
24
|
import { AccountCard } from "@/components/settings/accounts/account-card";
|
|
20
25
|
import { AccountBucketRow } from "@/components/settings/accounts/account-bucket-row";
|
|
21
26
|
import { useAccountsData } from "@/components/settings/accounts/use-accounts-data";
|
|
22
|
-
import { formatLastUpdated, pctColor } from "@/components/settings/accounts/account-usage-format";
|
|
27
|
+
import { formatLastUpdated, formatResetTime, pctColor } from "@/components/settings/accounts/account-usage-format";
|
|
28
|
+
|
|
29
|
+
/** Matches the whole-percent figure the usage bars show, so a refusal agrees with the card. */
|
|
30
|
+
function atCap(util: number | null | undefined): boolean {
|
|
31
|
+
return Math.round((util ?? 0) * 100) >= 100;
|
|
32
|
+
}
|
|
23
33
|
|
|
24
34
|
interface UsageBadgeProps {
|
|
25
35
|
usage: UsageInfo;
|
|
@@ -38,16 +48,20 @@ export function UsageBadge({ usage, loading, onClick }: UsageBadgeProps) {
|
|
|
38
48
|
const colorClass = fiveHourPct != null || sevenDayPct != null ? pctColor(worstPct) : "text-text-subtle";
|
|
39
49
|
|
|
40
50
|
return (
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
+
<Tooltip>
|
|
52
|
+
<TooltipTrigger asChild>
|
|
53
|
+
<button
|
|
54
|
+
onClick={onClick}
|
|
55
|
+
className={`flex items-center gap-1 px-1.5 py-0.5 rounded text-[11px] font-medium tabular-nums transition-colors hover:bg-surface-hover ${colorClass}`}
|
|
56
|
+
>
|
|
57
|
+
{loading ? <RefreshCw className="size-3 animate-spin" /> : <Activity className="size-3" />}
|
|
58
|
+
<span>5h:{fiveHourLabel}</span>
|
|
59
|
+
<span className="text-text-subtle">·</span>
|
|
60
|
+
<span>Wk:{sevenDayLabel}</span>
|
|
61
|
+
</button>
|
|
62
|
+
</TooltipTrigger>
|
|
63
|
+
<TooltipContent side="top">Click for usage details</TooltipContent>
|
|
64
|
+
</Tooltip>
|
|
51
65
|
);
|
|
52
66
|
}
|
|
53
67
|
|
|
@@ -60,17 +74,89 @@ interface UsageDetailPanelProps {
|
|
|
60
74
|
onReload?: () => void;
|
|
61
75
|
loading?: boolean;
|
|
62
76
|
lastFetchedAt?: string | null;
|
|
77
|
+
/** Route this chat to a different account. Omitted when the caller cannot re-route. */
|
|
78
|
+
onSelectAccount?: (accountId: string, label: string | null) => Promise<string | null>;
|
|
79
|
+
/** Account currently claimed or bound for this chat, so the card can mark itself. */
|
|
80
|
+
selectedAccountId?: string | null;
|
|
63
81
|
}
|
|
64
82
|
|
|
65
|
-
export function UsageDetailPanel({
|
|
83
|
+
export function UsageDetailPanel({
|
|
84
|
+
usage, visible, onClose, onReload, loading, lastFetchedAt,
|
|
85
|
+
onSelectAccount, selectedAccountId,
|
|
86
|
+
}: UsageDetailPanelProps) {
|
|
87
|
+
const [selectingId, setSelectingId] = useState<string | null>(null);
|
|
66
88
|
// Fetching is gated on visibility: the panel is collapsed most of the time, and its
|
|
67
89
|
// usage endpoint is the expensive one.
|
|
68
|
-
const { usages, accounts, activeAccountId, initialLoading, refreshing, flashIds, reload } = useAccountsData(visible);
|
|
90
|
+
const { usages, accounts, activeAccountId, initialLoading, refreshing, flashIds, reload, toggle, togglingId } = useAccountsData(visible);
|
|
91
|
+
// One strip for anything that went wrong in this panel — a refused switch or a refused
|
|
92
|
+
// toggle. Two separate messages in two places would be harder to notice, not clearer.
|
|
93
|
+
const [panelError, setPanelError] = useState<string | null>(null);
|
|
69
94
|
const [isFullscreen, setIsFullscreen] = useState(false);
|
|
70
95
|
|
|
71
96
|
if (!visible) return null;
|
|
72
97
|
|
|
73
98
|
const accountMap = new Map(accounts.map((a) => [a.id, a]));
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Why an account cannot take this chat, in the words the card shows.
|
|
102
|
+
*
|
|
103
|
+
* Only the reasons the server itself refuses — disabled, or a token past saving. Being
|
|
104
|
+
* near a quota cap is not one of them: that is a slower turn, not an impossible one, and
|
|
105
|
+
* a user who picks a nearly-capped account on purpose is allowed to.
|
|
106
|
+
*/
|
|
107
|
+
function unselectableReason(
|
|
108
|
+
info?: (typeof accounts)[number],
|
|
109
|
+
entry?: (typeof usages)[number],
|
|
110
|
+
): string | null {
|
|
111
|
+
if (info) {
|
|
112
|
+
if (info.status === "disabled") return "Disabled";
|
|
113
|
+
// A rejected grant is refused before the token's freshness is even consulted, so such
|
|
114
|
+
// an account cannot take a turn however healthy the rest of the card looks.
|
|
115
|
+
if (info.reauthRequired) return "Sign in again to use this account";
|
|
116
|
+
const expired = !info.hasRefreshToken && info.expiresAt && info.expiresAt < Math.floor(Date.now() / 1000);
|
|
117
|
+
if (expired) return "Token expired — sign in again";
|
|
118
|
+
}
|
|
119
|
+
// Reached, not merely approaching. The router steers away from 95% so it can spread load
|
|
120
|
+
// early, but an account at 96% still answers — refusing it would be wrong. At 100% the
|
|
121
|
+
// next turn fails outright, and that is the point where saying no is the truth.
|
|
122
|
+
const session = entry?.usage.session;
|
|
123
|
+
if (atCap(session?.utilization)) {
|
|
124
|
+
return `5-hour limit reached${session?.resetsAt ? ` — resets ${formatResetTime(session)}` : ""}`;
|
|
125
|
+
}
|
|
126
|
+
const weekly = entry?.usage.weekly;
|
|
127
|
+
if (atCap(weekly?.utilization)) {
|
|
128
|
+
return `Weekly limit reached${weekly?.resetsAt ? ` — resets ${formatResetTime(weekly)}` : ""}`;
|
|
129
|
+
}
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async function handleToggle(id: string, status: string) {
|
|
134
|
+
setPanelError(await toggle(id, status));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function handleSelect(accountId: string) {
|
|
138
|
+
if (!onSelectAccount) return;
|
|
139
|
+
// Refuse here rather than leaving the button inert: the user pressed something and is
|
|
140
|
+
// owed the reason. Checked again server-side, which is what catches an account that
|
|
141
|
+
// became unusable between this render and the click.
|
|
142
|
+
const refused = unselectableReason(
|
|
143
|
+
accountMap.get(accountId),
|
|
144
|
+
usages.find((u) => u.accountId === accountId),
|
|
145
|
+
);
|
|
146
|
+
if (refused) { setPanelError(`Cannot switch to this account — ${refused.toLowerCase()}.`); return; }
|
|
147
|
+
|
|
148
|
+
setSelectingId(accountId);
|
|
149
|
+
setPanelError(null);
|
|
150
|
+
try {
|
|
151
|
+
// The label travels with the id: a tab that has no session yet displays the choice
|
|
152
|
+
// straight away, and it is the only place that knows the human-readable name.
|
|
153
|
+
const label = usages.find((u) => u.accountId === accountId)?.accountLabel ?? null;
|
|
154
|
+
setPanelError((await onSelectAccount(accountId, label)) ?? null);
|
|
155
|
+
} finally {
|
|
156
|
+
setSelectingId(null);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
74
160
|
const hasCost = usage.queryCostUsd != null || usage.totalCostUsd != null;
|
|
75
161
|
const hasPerAccountUsage = usages.length > 0;
|
|
76
162
|
|
|
@@ -95,33 +181,45 @@ export function UsageDetailPanel({ usage, visible, onClose, onReload, loading, l
|
|
|
95
181
|
)}
|
|
96
182
|
</div>
|
|
97
183
|
<div className="flex items-center gap-1">
|
|
98
|
-
<
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
184
|
+
<Tooltip>
|
|
185
|
+
<TooltipTrigger asChild>
|
|
186
|
+
<button
|
|
187
|
+
onClick={() => openSettings("accounts")}
|
|
188
|
+
className="flex items-center gap-1 text-[10px] text-text-subtle hover:text-text-primary px-1 cursor-pointer"
|
|
189
|
+
>
|
|
190
|
+
Manage accounts <ExternalLink className="size-3" />
|
|
191
|
+
</button>
|
|
192
|
+
</TooltipTrigger>
|
|
193
|
+
<TooltipContent side="top">Add, remove or rotate accounts</TooltipContent>
|
|
194
|
+
</Tooltip>
|
|
105
195
|
{hasPerAccountUsage && (
|
|
106
|
-
<
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
196
|
+
<Tooltip>
|
|
197
|
+
<TooltipTrigger asChild>
|
|
198
|
+
<button
|
|
199
|
+
onClick={() => setIsFullscreen((v) => !v)}
|
|
200
|
+
className="text-xs text-text-subtle hover:text-text-primary px-1 cursor-pointer"
|
|
201
|
+
aria-label={isFullscreen ? "Exit fullscreen" : "Fullscreen view"}
|
|
202
|
+
>
|
|
203
|
+
{isFullscreen ? <Minimize2 className="size-3" /> : <Maximize2 className="size-3" />}
|
|
204
|
+
</button>
|
|
205
|
+
</TooltipTrigger>
|
|
206
|
+
<TooltipContent side="top">{isFullscreen ? "Exit fullscreen" : "Fullscreen view"}</TooltipContent>
|
|
207
|
+
</Tooltip>
|
|
114
208
|
)}
|
|
115
209
|
{onReload && (
|
|
116
|
-
<
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
210
|
+
<Tooltip>
|
|
211
|
+
<TooltipTrigger asChild>
|
|
212
|
+
<button
|
|
213
|
+
onClick={() => { onReload(); void reload(); }}
|
|
214
|
+
disabled={loading || refreshing}
|
|
215
|
+
className="text-xs text-text-subtle hover:text-text-primary px-1 disabled:opacity-50 cursor-pointer"
|
|
216
|
+
aria-label="Refresh usage"
|
|
217
|
+
>
|
|
218
|
+
<RefreshCw className={`size-3 ${(loading || refreshing) ? "animate-spin" : ""}`} />
|
|
219
|
+
</button>
|
|
220
|
+
</TooltipTrigger>
|
|
221
|
+
<TooltipContent side="top">Refresh</TooltipContent>
|
|
222
|
+
</Tooltip>
|
|
125
223
|
)}
|
|
126
224
|
<button
|
|
127
225
|
onClick={() => { setIsFullscreen(false); onClose(); }}
|
|
@@ -133,6 +231,22 @@ export function UsageDetailPanel({ usage, visible, onClose, onReload, loading, l
|
|
|
133
231
|
</div>
|
|
134
232
|
</div>
|
|
135
233
|
|
|
234
|
+
{/* The server distinguishes "this login was rejected, sign in again" from "could not
|
|
235
|
+
reach Anthropic, try shortly", and the difference decides what the user should do.
|
|
236
|
+
Showing its words verbatim is the only way that survives to them. */}
|
|
237
|
+
{panelError && (
|
|
238
|
+
<div className="shrink-0 flex items-start gap-2 rounded border border-error/40 bg-error/10 px-2 py-1.5 text-[11px] text-error">
|
|
239
|
+
<span className="flex-1">{panelError}</span>
|
|
240
|
+
<button
|
|
241
|
+
onClick={() => setPanelError(null)}
|
|
242
|
+
className="shrink-0 text-error/70 hover:text-error cursor-pointer"
|
|
243
|
+
aria-label="Dismiss"
|
|
244
|
+
>
|
|
245
|
+
<X className="size-3" />
|
|
246
|
+
</button>
|
|
247
|
+
</div>
|
|
248
|
+
)}
|
|
249
|
+
|
|
136
250
|
{hasPerAccountUsage || initialLoading ? (
|
|
137
251
|
<div
|
|
138
252
|
className={isFullscreen
|
|
@@ -147,16 +261,26 @@ export function UsageDetailPanel({ usage, visible, onClose, onReload, loading, l
|
|
|
147
261
|
{initialLoading ? (
|
|
148
262
|
<p className="text-[10px] text-text-subtle">Loading...</p>
|
|
149
263
|
) : (
|
|
150
|
-
usages.map((entry) =>
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
264
|
+
usages.map((entry) => {
|
|
265
|
+
const info = accountMap.get(entry.accountId);
|
|
266
|
+
return (
|
|
267
|
+
<AccountCard
|
|
268
|
+
key={entry.accountId}
|
|
269
|
+
entry={entry}
|
|
270
|
+
// What is serving THIS chat, not what the rotation would pick next —
|
|
271
|
+
// those differ the moment a session is bound to an account.
|
|
272
|
+
isActive={entry.accountId === (selectedAccountId ?? usage.activeAccountId ?? activeAccountId)}
|
|
273
|
+
accountInfo={info}
|
|
274
|
+
flash={flashIds.has(entry.accountId)}
|
|
275
|
+
layout={isFullscreen ? "grid" : "strip"}
|
|
276
|
+
onSelect={onSelectAccount ? handleSelect : undefined}
|
|
277
|
+
unselectableReason={unselectableReason(info, entry)}
|
|
278
|
+
selecting={selectingId === entry.accountId}
|
|
279
|
+
onToggle={handleToggle}
|
|
280
|
+
toggling={togglingId === entry.accountId}
|
|
281
|
+
/>
|
|
282
|
+
);
|
|
283
|
+
})
|
|
160
284
|
)}
|
|
161
285
|
</div>
|
|
162
286
|
) : usage.session || usage.weekly || usage.weeklyOpus || usage.weeklySonnet ? (
|
|
@@ -8,6 +8,7 @@ import { usePanelStore } from "@/stores/panel-store";
|
|
|
8
8
|
import { useSettingsStore } from "@/stores/settings-store";
|
|
9
9
|
import { basename } from "@/lib/utils";
|
|
10
10
|
import { useMonacoTheme } from "@/lib/use-monaco-theme";
|
|
11
|
+
import { useInlineBlame } from "@/hooks/use-inline-blame";
|
|
11
12
|
import { Loader2, FileWarning, Play, Database, ExternalLink, X, GripHorizontal, ShieldCheck, ShieldOff } from "lucide-react";
|
|
12
13
|
import { EditorBreadcrumb } from "./editor-breadcrumb";
|
|
13
14
|
import { EditorToolbar } from "./editor-toolbar";
|
|
@@ -73,8 +74,16 @@ export const CodeEditor = memo(function CodeEditor({ metadata, tabId }: CodeEdit
|
|
|
73
74
|
const saveTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
74
75
|
const latestContentRef = useRef<string>("");
|
|
75
76
|
const editorRef = useRef<MonacoType.editor.IStandaloneCodeEditor | null>(null);
|
|
77
|
+
// Mirrors editorRef as state, so hooks that must react to the editor existing
|
|
78
|
+
// (inline blame) re-run on mount instead of reading a ref that is still null.
|
|
79
|
+
const [mounted, setMounted] = useState<{
|
|
80
|
+
editor: MonacoType.editor.IStandaloneCodeEditor;
|
|
81
|
+
monaco: typeof MonacoType;
|
|
82
|
+
} | null>(null);
|
|
76
83
|
const { tabs, updateTab } = useTabStore(useShallow((s) => ({ tabs: s.tabs, updateTab: s.updateTab })));
|
|
77
84
|
const { wordWrap, toggleWordWrap } = useSettingsStore(useShallow((s) => ({ wordWrap: s.wordWrap, toggleWordWrap: s.toggleWordWrap })));
|
|
85
|
+
const inlineBlame = useSettingsStore((s) => s.inlineBlame);
|
|
86
|
+
const toggleInlineBlame = useSettingsStore((s) => s.toggleInlineBlame);
|
|
78
87
|
const monacoTheme = useMonacoTheme();
|
|
79
88
|
|
|
80
89
|
const isUntitled = metadata?.isUntitled === true;
|
|
@@ -367,6 +376,18 @@ export const CodeEditor = memo(function CodeEditor({ metadata, tabId }: CodeEdit
|
|
|
367
376
|
if (ownTab.title !== newTitle) updateTab(ownTab.id, { title: newTitle });
|
|
368
377
|
}, [unsaved]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
369
378
|
|
|
379
|
+
// GitLens-style annotation on the cursor's line. Off unless the pref is on,
|
|
380
|
+
// and never for an untitled buffer or inline (read-only preview) content —
|
|
381
|
+
// neither has a path git could blame.
|
|
382
|
+
const canBlame = !isUntitled && inlineContent == null && !!filePath && !!projectName;
|
|
383
|
+
const blame = useInlineBlame({
|
|
384
|
+
editor: mounted?.editor ?? null,
|
|
385
|
+
monaco: mounted?.monaco ?? null,
|
|
386
|
+
projectName,
|
|
387
|
+
filePath: canBlame ? filePath : undefined,
|
|
388
|
+
enabled: inlineBlame && canBlame,
|
|
389
|
+
});
|
|
390
|
+
|
|
370
391
|
const saveFile = useCallback(
|
|
371
392
|
async (text: string) => {
|
|
372
393
|
if (!filePath) return;
|
|
@@ -378,9 +399,12 @@ export const CodeEditor = memo(function CodeEditor({ metadata, tabId }: CodeEdit
|
|
|
378
399
|
await api.put(`${projectUrl(projectName!)}/files/write`, { path: filePath, content: text });
|
|
379
400
|
}
|
|
380
401
|
setUnsaved(false);
|
|
402
|
+
// The blame line table is keyed by line number, so an edit invalidates
|
|
403
|
+
// it; a save is the point where a fresh one can be had.
|
|
404
|
+
blame.refresh();
|
|
381
405
|
} catch { /* Silent — unsaved indicator persists */ }
|
|
382
406
|
},
|
|
383
|
-
[filePath, projectName, isExternalFile],
|
|
407
|
+
[filePath, projectName, isExternalFile, blame.refresh], // eslint-disable-line react-hooks/exhaustive-deps
|
|
384
408
|
);
|
|
385
409
|
|
|
386
410
|
function handleChange(value: string | undefined) {
|
|
@@ -452,6 +476,7 @@ export const CodeEditor = memo(function CodeEditor({ metadata, tabId }: CodeEdit
|
|
|
452
476
|
const handleEditorMount: OnMount = useCallback((editor, monaco) => {
|
|
453
477
|
editorRef.current = editor;
|
|
454
478
|
monacoInstanceRef.current = monaco;
|
|
479
|
+
setMounted({ editor, monaco });
|
|
455
480
|
if (lineNumber && lineNumber > 0) {
|
|
456
481
|
setTimeout(() => revealTarget(), 100);
|
|
457
482
|
}
|
|
@@ -466,6 +491,10 @@ export const CodeEditor = memo(function CodeEditor({ metadata, tabId }: CodeEdit
|
|
|
466
491
|
monaco.KeyMod.Alt | monaco.KeyCode.KeyZ,
|
|
467
492
|
() => useSettingsStore.getState().toggleWordWrap(),
|
|
468
493
|
);
|
|
494
|
+
editor.addCommand(
|
|
495
|
+
monaco.KeyMod.Alt | monaco.KeyCode.KeyB,
|
|
496
|
+
() => useSettingsStore.getState().toggleInlineBlame(),
|
|
497
|
+
);
|
|
469
498
|
monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({
|
|
470
499
|
noSemanticValidation: true, noSyntaxValidation: true, noSuggestionDiagnostics: true,
|
|
471
500
|
});
|
|
@@ -694,6 +723,9 @@ export const CodeEditor = memo(function CodeEditor({ metadata, tabId }: CodeEdit
|
|
|
694
723
|
onCsvModeChange={setCsvMode}
|
|
695
724
|
wordWrap={wordWrap}
|
|
696
725
|
onToggleWordWrap={toggleWordWrap}
|
|
726
|
+
inlineBlame={inlineBlame}
|
|
727
|
+
onToggleInlineBlame={canBlame ? toggleInlineBlame : undefined}
|
|
728
|
+
blameStale={blame.stale}
|
|
697
729
|
onRefresh={reloadFile}
|
|
698
730
|
refreshing={refreshing}
|
|
699
731
|
filePath={filePath}
|
|
@@ -5,7 +5,8 @@ import { useShallow } from "zustand/react/shallow";
|
|
|
5
5
|
import { useSettingsStore } from "@/stores/settings-store";
|
|
6
6
|
import { useMonacoTheme } from "@/lib/use-monaco-theme";
|
|
7
7
|
import { onHostResize } from "@/components/floating-window/pip/pip-resize-signal";
|
|
8
|
-
import { Loader2, FileCode, WrapText } from "lucide-react";
|
|
8
|
+
import { Loader2, FileCode, WrapText, UserRound } from "lucide-react";
|
|
9
|
+
import { useInlineBlame } from "@/hooks/use-inline-blame";
|
|
9
10
|
|
|
10
11
|
function getMonacoLanguage(filename: string): string {
|
|
11
12
|
const ext = filename.split(".").pop()?.toLowerCase() ?? "";
|
|
@@ -49,6 +50,20 @@ export function DiffViewer({ metadata }: DiffViewerProps) {
|
|
|
49
50
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
50
51
|
const diffEditorRef = useRef<import("monaco-editor").editor.IStandaloneDiffEditor | null>(null);
|
|
51
52
|
const [editorReady, setEditorReady] = useState(false);
|
|
53
|
+
// The two panes, as state rather than refs: the blame hooks have to re-run
|
|
54
|
+
// once the editor exists. `focused` is which pane gets annotated — only one,
|
|
55
|
+
// because both panes carry a cursor position and annotating both would leave a
|
|
56
|
+
// stray line-1 annotation in the idle pane.
|
|
57
|
+
//
|
|
58
|
+
// It starts on the modified side rather than nothing: that is the side the
|
|
59
|
+
// normal editor annotates, so turning blame on shows something immediately
|
|
60
|
+
// instead of waiting for a click nobody knows to make.
|
|
61
|
+
const [panes, setPanes] = useState<{
|
|
62
|
+
monaco: typeof import("monaco-editor");
|
|
63
|
+
original: import("monaco-editor").editor.ICodeEditor;
|
|
64
|
+
modified: import("monaco-editor").editor.ICodeEditor;
|
|
65
|
+
} | null>(null);
|
|
66
|
+
const [focused, setFocused] = useState<"original" | "modified">("modified");
|
|
52
67
|
const [containerHeight, setContainerHeight] = useState<number | undefined>();
|
|
53
68
|
|
|
54
69
|
useEffect(() => {
|
|
@@ -134,6 +149,42 @@ export function DiffViewer({ metadata }: DiffViewerProps) {
|
|
|
134
149
|
return langFile ? getMonacoLanguage(langFile) : "plaintext";
|
|
135
150
|
}, [filePath, file1, file2]);
|
|
136
151
|
|
|
152
|
+
const inlineBlame = useSettingsStore((s) => s.inlineBlame);
|
|
153
|
+
const toggleInlineBlame = useSettingsStore((s) => s.toggleInlineBlame);
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Blame is only honest on the full-file path.
|
|
157
|
+
*
|
|
158
|
+
* `file-full-diff` hands back both sides as complete files at real revisions,
|
|
159
|
+
* so a line number here is the line number git blamed. The other three paths
|
|
160
|
+
* cannot be blamed: inline content and a two-file compare are not a tracked
|
|
161
|
+
* path at a revision, and `parseDiff` rebuilds the file from hunks alone, so
|
|
162
|
+
* its line 40 is not the file's line 40 — annotating it would confidently
|
|
163
|
+
* name the wrong commit.
|
|
164
|
+
*/
|
|
165
|
+
const canBlame = Boolean(projectName && filePath && fullFileDiff);
|
|
166
|
+
|
|
167
|
+
// The left pane is the file at `ref1` (the route defaults to HEAD); the right
|
|
168
|
+
// is `ref2`, or the working tree when there is none.
|
|
169
|
+
useInlineBlame({
|
|
170
|
+
editor: panes?.original ?? null,
|
|
171
|
+
monaco: panes?.monaco ?? null,
|
|
172
|
+
projectName,
|
|
173
|
+
filePath: canBlame ? filePath : undefined,
|
|
174
|
+
rev: ref1 || "HEAD",
|
|
175
|
+
enabled: inlineBlame && canBlame && focused === "original",
|
|
176
|
+
readOnly: true,
|
|
177
|
+
});
|
|
178
|
+
useInlineBlame({
|
|
179
|
+
editor: panes?.modified ?? null,
|
|
180
|
+
monaco: panes?.monaco ?? null,
|
|
181
|
+
projectName,
|
|
182
|
+
filePath: canBlame ? filePath : undefined,
|
|
183
|
+
rev: ref2,
|
|
184
|
+
enabled: inlineBlame && canBlame && focused === "modified",
|
|
185
|
+
readOnly: true,
|
|
186
|
+
});
|
|
187
|
+
|
|
137
188
|
// Force inline on mobile (<768px) since side-by-side is too narrow
|
|
138
189
|
const isMobile = typeof window !== "undefined" && window.innerWidth < 768;
|
|
139
190
|
const renderSideBySide = !isMobile;
|
|
@@ -190,13 +241,26 @@ export function DiffViewer({ metadata }: DiffViewerProps) {
|
|
|
190
241
|
return (
|
|
191
242
|
<div className="flex flex-col h-full">
|
|
192
243
|
{/* Toolbar */}
|
|
193
|
-
{!isMobile && (
|
|
244
|
+
{(!isMobile || canBlame) && (
|
|
194
245
|
<div className="flex items-center justify-end gap-0.5 px-2 py-0.5 border-b border-border shrink-0">
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
246
|
+
{canBlame && (
|
|
247
|
+
<button type="button" onClick={toggleInlineBlame}
|
|
248
|
+
title="Inline blame (Alt+B) — who last touched the cursor's line. Click a pane to annotate that side."
|
|
249
|
+
className={`flex items-center justify-center rounded hover:bg-muted active:scale-95 transition-colors ${
|
|
250
|
+
isMobile ? "size-11" : "p-1"
|
|
251
|
+
} ${inlineBlame ? "bg-muted text-foreground" : ""}`}
|
|
252
|
+
>
|
|
253
|
+
<UserRound className="size-3.5" />
|
|
254
|
+
</button>
|
|
255
|
+
)}
|
|
256
|
+
{/* Word wrap is forced on below `md`, so its toggle would be a lie. */}
|
|
257
|
+
{!isMobile && (
|
|
258
|
+
<button type="button" onClick={toggleWordWrap} title="Toggle word wrap"
|
|
259
|
+
className={`p-1 rounded hover:bg-muted transition-colors ${wordWrap ? "bg-muted text-foreground" : ""}`}
|
|
260
|
+
>
|
|
261
|
+
<WrapText className="size-3.5" />
|
|
262
|
+
</button>
|
|
263
|
+
)}
|
|
200
264
|
</div>
|
|
201
265
|
)}
|
|
202
266
|
{/* Monaco DiffEditor */}
|
|
@@ -208,9 +272,20 @@ export function DiffViewer({ metadata }: DiffViewerProps) {
|
|
|
208
272
|
original={original}
|
|
209
273
|
modified={modified}
|
|
210
274
|
theme={monacoTheme}
|
|
211
|
-
onMount={(editor) => {
|
|
275
|
+
onMount={(editor, monaco) => {
|
|
212
276
|
diffEditorRef.current = editor;
|
|
213
277
|
setEditorReady(true);
|
|
278
|
+
const originalPane = editor.getOriginalEditor();
|
|
279
|
+
const modifiedPane = editor.getModifiedEditor();
|
|
280
|
+
setPanes({ monaco, original: originalPane, modified: modifiedPane });
|
|
281
|
+
originalPane.onDidFocusEditorText(() => setFocused("original"));
|
|
282
|
+
modifiedPane.onDidFocusEditorText(() => setFocused("modified"));
|
|
283
|
+
// Same shortcut as the normal editor. It goes on the diff editor,
|
|
284
|
+
// not the panes: only IStandaloneDiffEditor has addCommand.
|
|
285
|
+
editor.addCommand(
|
|
286
|
+
monaco.KeyMod.Alt | monaco.KeyCode.KeyB,
|
|
287
|
+
() => useSettingsStore.getState().toggleInlineBlame(),
|
|
288
|
+
);
|
|
214
289
|
}}
|
|
215
290
|
options={{
|
|
216
291
|
fontSize: isMobile ? 11 : 13,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Code, Eye, WrapText, Table, Download, RefreshCw } from "lucide-react";
|
|
1
|
+
import { Code, Eye, WrapText, Table, Download, RefreshCw, UserRound } from "lucide-react";
|
|
2
2
|
import { downloadFile } from "@/lib/file-download";
|
|
3
3
|
|
|
4
4
|
interface EditorToolbarProps {
|
|
@@ -9,6 +9,11 @@ interface EditorToolbarProps {
|
|
|
9
9
|
onCsvModeChange?: (mode: "table" | "raw") => void;
|
|
10
10
|
wordWrap: boolean;
|
|
11
11
|
onToggleWordWrap: () => void;
|
|
12
|
+
/** Omitted for buffers git cannot blame (untitled, inline preview). */
|
|
13
|
+
inlineBlame?: boolean;
|
|
14
|
+
onToggleInlineBlame?: () => void;
|
|
15
|
+
/** Blame is on but hidden because the buffer has unsaved edits. */
|
|
16
|
+
blameStale?: boolean;
|
|
12
17
|
onRefresh?: () => void;
|
|
13
18
|
refreshing?: boolean;
|
|
14
19
|
filePath?: string;
|
|
@@ -21,15 +26,18 @@ function ToolbarButton({
|
|
|
21
26
|
onClick,
|
|
22
27
|
icon: Icon,
|
|
23
28
|
label,
|
|
29
|
+
title,
|
|
24
30
|
}: {
|
|
25
31
|
active: boolean;
|
|
26
32
|
onClick: () => void;
|
|
27
33
|
icon: React.ComponentType<{ className?: string }>;
|
|
28
34
|
label: string;
|
|
35
|
+
title?: string;
|
|
29
36
|
}) {
|
|
30
37
|
return (
|
|
31
38
|
<button
|
|
32
39
|
type="button"
|
|
40
|
+
title={title}
|
|
33
41
|
onClick={onClick}
|
|
34
42
|
className={`flex items-center gap-1 px-2 py-1 rounded text-xs transition-colors ${
|
|
35
43
|
active ? "bg-muted text-foreground" : "text-muted-foreground hover:text-foreground"
|
|
@@ -49,6 +57,9 @@ export function EditorToolbar({
|
|
|
49
57
|
onCsvModeChange,
|
|
50
58
|
wordWrap,
|
|
51
59
|
onToggleWordWrap,
|
|
60
|
+
inlineBlame,
|
|
61
|
+
onToggleInlineBlame,
|
|
62
|
+
blameStale,
|
|
52
63
|
onRefresh,
|
|
53
64
|
refreshing,
|
|
54
65
|
filePath,
|
|
@@ -78,6 +89,19 @@ export function EditorToolbar({
|
|
|
78
89
|
icon={WrapText}
|
|
79
90
|
label="Wrap"
|
|
80
91
|
/>
|
|
92
|
+
{onToggleInlineBlame && (
|
|
93
|
+
<ToolbarButton
|
|
94
|
+
active={inlineBlame === true}
|
|
95
|
+
onClick={onToggleInlineBlame}
|
|
96
|
+
icon={UserRound}
|
|
97
|
+
label="Blame"
|
|
98
|
+
title={
|
|
99
|
+
inlineBlame && blameStale
|
|
100
|
+
? "Blame is hidden until this file is saved — the line numbers have moved"
|
|
101
|
+
: "Inline blame (Alt+B)"
|
|
102
|
+
}
|
|
103
|
+
/>
|
|
104
|
+
)}
|
|
81
105
|
{onRefresh && (
|
|
82
106
|
<button
|
|
83
107
|
type="button"
|