@hienlh/ppm 0.19.2 → 0.19.4
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 +27 -0
- package/CLAUDE.md +1 -0
- package/assets/skills/ppm/SKILL.md +1 -1
- package/assets/skills/ppm/references/cli-reference.md +4 -4
- package/assets/skills/ppm/references/http-api.md +3 -2
- package/dist/web/assets/accounts-settings-section-CCFQV-RB.js +1 -0
- package/dist/web/assets/ai-resource-editor-BTWds-aC.js +1 -0
- package/dist/web/assets/ai-settings-section-CmoBsSpY.js +1 -0
- package/dist/web/assets/ai-settings-section-DjbYj4wK.js +1 -0
- package/dist/web/assets/{angular-html-BlcrLjUx.js → angular-html-BuHNQIbQ.js} +1 -1
- package/dist/web/assets/{angular-ts-BUjtceAJ.js → angular-ts-DHRWoLc-.js} +1 -1
- package/dist/web/assets/api-client-BogqsFna.js +1 -0
- package/dist/web/assets/api-settings-CTMdz_VF.js +1 -0
- package/dist/web/assets/{api-themes-YsZgf6Xj.js → api-themes-Cknp3Q-P.js} +1 -1
- package/dist/web/assets/{apl-B3g0v1pk.js → apl-C1VSWVei.js} +1 -1
- package/dist/web/assets/appearance-settings-section-vLmakLtA.js +1 -0
- package/dist/web/assets/architecture-PBZL5I3N-Ca5IpdzP.js +1 -0
- package/dist/web/assets/arrow-left-CyFcsyzD.js +1 -0
- package/dist/web/assets/{astro-FRnyn7cH.js → astro-RX-rChsZ.js} +1 -1
- package/dist/web/assets/{audio-preview-DWTRm4HC.js → audio-preview-CCd1jdje.js} +1 -1
- package/dist/web/assets/{blade-NLdOqUKw.js → blade-CZ6bxMbK.js} +1 -1
- package/dist/web/assets/{pencil-BlIIg6RH.js → brain-KDa9jFRq.js} +1 -1
- package/dist/web/assets/{bsl-CenHZfaU.js → bsl-BhgCXLWk.js} +1 -1
- package/dist/web/assets/{button-Cq8yixjf.js → button-BZtEJcUv.js} +1 -1
- package/dist/web/assets/{cairo-B_WR0aiQ.js → cairo-BrwygPkn.js} +1 -1
- package/dist/web/assets/calendar-clock-C1asT-gk.js +1 -0
- package/dist/web/assets/chat-tab-CLRlS8Jc.js +15 -0
- package/dist/web/assets/check-DN75Ucvz.js +1 -0
- package/dist/web/assets/chevron-up-8PbsNiJp.js +1 -0
- package/dist/web/assets/{cobol-DOynhWYj.js → cobol-C5TGW1Ir.js} +1 -1
- package/dist/web/assets/code-editor-CCwVu5F5.js +7 -0
- package/dist/web/assets/{coffee-Bn6dbw4P.js → coffee-CFgKlmgI.js} +1 -1
- package/dist/web/assets/{conflict-editor-Cgb-z12D.js → conflict-editor-CvWYcbwD.js} +1 -1
- package/dist/web/assets/context-menu-C13b1uT3.js +1 -0
- package/dist/web/assets/{cpp-CoZR9t8r.js → cpp-BhG5q3-d.js} +1 -1
- package/dist/web/assets/{crystal-BSheONSg.js → crystal-DvuI7_tY.js} +1 -1
- package/dist/web/assets/{csv-preview-BZMHfFH3.js → csv-preview-DzuDuyE_.js} +1 -1
- package/dist/web/assets/{data-grid-overlay-editor-gwPoVSCU.js → data-grid-overlay-editor-DP1xkog4.js} +1 -1
- package/dist/web/assets/database-viewer-D4XGEDHm.js +1 -0
- package/dist/web/assets/diff-viewer-B5N1X61R.js +4 -0
- package/dist/web/assets/{docx-preview-DqVDOMmv.js → docx-preview-CWIDVNZn.js} +1 -1
- package/dist/web/assets/{edge-DTtoslQu.js → edge-BSIEsmlI.js} +1 -1
- package/dist/web/assets/{edit-diff-preview-CXSbSUBX.js → edit-diff-preview-DeHb85DN.js} +1 -1
- package/dist/web/assets/{elixir-CIBCZ3Zi.js → elixir-BF-lrGXM.js} +1 -1
- package/dist/web/assets/{elm-BLmb7K1H.js → elm-8mCsJOeN.js} +1 -1
- package/dist/web/assets/{erb-BBY8ApRd.js → erb-Dw5PUVKa.js} +1 -1
- package/dist/web/assets/{erlang-D5Zb5_P0.js → erlang-BvjBIA6O.js} +1 -1
- package/dist/web/assets/{esm-6F8v1jLL.js → esm-Bt9OwSnU.js} +1 -1
- package/dist/web/assets/explorer-body-BxLLjthF.js +2 -0
- package/dist/web/assets/explorer-window-content-CL1Cipj3.js +1 -0
- package/dist/web/assets/extension-manager-section-BpyGq15A.js +1 -0
- package/dist/web/assets/{extension-webview-BSh6KGd7.js → extension-webview-BM8FXhuN.js} +1 -1
- package/dist/web/assets/{plus-BR0qewJI.js → eye-gWwaF4Lg.js} +1 -1
- package/dist/web/assets/eye-off-GjXVuAOn.js +1 -0
- package/dist/web/assets/files-settings-section-B0_aR1nL.js +1 -0
- package/dist/web/assets/flask-conical-tp8gyZGh.js +1 -0
- package/dist/web/assets/{fortran-fixed-form-CBdwkEUJ.js → fortran-fixed-form-CRjUJe9K.js} +1 -1
- package/dist/web/assets/{fsharp-AT4XdVYH.js → fsharp-CjUIHIHp.js} +1 -1
- package/dist/web/assets/{gdresource-D852fWBR.js → gdresource-x_NUQFrZ.js} +1 -1
- package/dist/web/assets/general-settings-section-C1EJzjhU.js +1 -0
- package/dist/web/assets/{git-commit-DWfWAyxy.js → git-commit-BOpxSbns.js} +1 -1
- package/dist/web/assets/git-log-panel-D1yGTUE_.js +1 -0
- package/dist/web/assets/{git-rebase-C6opk8gr.js → git-rebase-BW4xlK7k.js} +1 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-BloZfB0Q.js +1 -0
- package/dist/web/assets/{glide-data-grid-CICFjJon.js → glide-data-grid-Cu0STKML.js} +12 -12
- package/dist/web/assets/{glimmer-js-DLi5mWYO.js → glimmer-js-CT9jrmFj.js} +1 -1
- package/dist/web/assets/{glimmer-ts-D-scFX30.js → glimmer-ts-CjQT1lxK.js} +1 -1
- package/dist/web/assets/{glsl-dbQzF4iA.js → glsl-DsFJvtg5.js} +1 -1
- package/dist/web/assets/{graphql-D1m5Wen2.js → graphql-Dhlnmkn_.js} +1 -1
- package/dist/web/assets/group-chat-tab-BfcvkRX5.js +1 -0
- package/dist/web/assets/{hack-CqYmpO7u.js → hack-BKHbsJ7u.js} +1 -1
- package/dist/web/assets/{haml-BXzKWEag.js → haml-C1Ih25Y2.js} +1 -1
- package/dist/web/assets/{handlebars-DXnrtHtq.js → handlebars-CaFitvmC.js} +1 -1
- package/dist/web/assets/{html-DhgQJ-xL.js → html-BwpKIE6V.js} +1 -1
- package/dist/web/assets/{html-derivative-xZRft-YE.js → html-derivative-uuTEquZU.js} +1 -1
- package/dist/web/assets/{http-B53mSj14.js → http-Cv4Pg5cR.js} +1 -1
- package/dist/web/assets/{hurl-CsomVy04.js → hurl-DubkI6TG.js} +1 -1
- package/dist/web/assets/{hxml-LffjWwKA.js → hxml-B9OVZFAB.js} +1 -1
- package/dist/web/assets/{image-preview-CNA6XMNS.js → image-preview-DRbZ0FA2.js} +1 -1
- package/dist/web/assets/index-B1bzKRjJ.js +52 -0
- package/dist/web/assets/index-nP3uxXvR.css +2 -0
- package/dist/web/assets/info-3K5VOQVL-c0W8wp5o.js +1 -0
- package/dist/web/assets/{jinja-CCHURl7V.js → jinja-BHb1jyl6.js} +1 -1
- package/dist/web/assets/jira-watcher-section-DZh-Z2MK.js +1 -0
- package/dist/web/assets/{jison-7qBd8dnR.js → jison-CtpfXDzp.js} +1 -1
- package/dist/web/assets/{julia-BY9pnTcy.js → julia-TYjgpazU.js} +1 -1
- package/dist/web/assets/{just-D7wRSNRr.js → just-D27bzZuW.js} +1 -1
- package/dist/web/assets/key-round-CQpDrDxg.js +1 -0
- package/dist/web/assets/keybindings-store-CFwgcnpZ.js +1 -0
- package/dist/web/assets/keyboard-shortcuts-section-JBtcJGM3.js +1 -0
- package/dist/web/assets/{latex-RgsCae55.js → latex-D_MzopTn.js} +1 -1
- package/dist/web/assets/{liquid-CnDFzvOZ.js → liquid-BG_FG0Pe.js} +1 -1
- package/dist/web/assets/{lua-0lj5W8sm.js → lua-Bl29oC2L.js} +1 -1
- package/dist/web/assets/{markdown-renderer-dbLIvU2M.js → markdown-renderer-Bo8GgGXl.js} +3 -3
- package/dist/web/assets/markdown-renderer-DY60UZNW.js +1 -0
- package/dist/web/assets/{marko-CQgqAhbV.js → marko-D-HI7dbm.js} +1 -1
- package/dist/web/assets/{mdc-BkIcTiIy.js → mdc-y444JQTQ.js} +1 -1
- package/dist/web/assets/{media-url-DDciwvLU.js → media-url-VzecHk9g.js} +1 -1
- package/dist/web/assets/{mobile-bottom-sheet-pjcHKorA.js → mobile-bottom-sheet-BD5b7KJF.js} +1 -1
- package/dist/web/assets/mobile-explorer-sheet-C59rPw7Z.js +1 -0
- package/dist/web/assets/{nextflow-DvIW68DS.js → nextflow-C5sKcldC.js} +1 -1
- package/dist/web/assets/{nginx-O-hft0gG.js → nginx-bXnEC3wm.js} +1 -1
- package/dist/web/assets/{nim-BxrR4HwQ.js → nim-pevhf4WD.js} +1 -1
- package/dist/web/assets/notification-store-Bd6L0K_Q.js +1 -0
- package/dist/web/assets/notifications-settings-section-ARQlF-6E.js +1 -0
- package/dist/web/assets/{number-overlay-editor-BoLRYuZT.js → number-overlay-editor-B7WINIW4.js} +1 -1
- package/dist/web/assets/{overview-panel-l3g1gT5e.js → overview-panel-mhN0qw6Q.js} +1 -1
- package/dist/web/assets/packet-RMMSAZCW-D_BoHr_M.js +1 -0
- package/dist/web/assets/panel-store-BRrnDO_i.js +1 -0
- package/dist/web/assets/pdf-preview-CRShdbBS.js +1 -0
- package/dist/web/assets/{perl-Da-uNqmL.js → perl-B1KUtMnW.js} +1 -1
- package/dist/web/assets/{php-JMu45mEx.js → php-BGmVZBAJ.js} +1 -1
- package/dist/web/assets/pie-UPGHQEXC-BaLfuEf8.js +1 -0
- package/dist/web/assets/{postgres-viewer-DstZwUai.js → postgres-viewer-BNT-4yYl.js} +3 -3
- package/dist/web/assets/power-CriQkeK5.js +1 -0
- package/dist/web/assets/ppmbot-settings-section-Bq4YAcFW.js +1 -0
- package/dist/web/assets/{process-table-kmQ2-8qJ.js → process-table-CjiXW0rg.js} +1 -1
- package/dist/web/assets/{project-store-DTUCS65m.js → project-store-BuY7rxn_.js} +1 -1
- package/dist/web/assets/proxy-settings-section-wRbiyQiA.js +3 -0
- package/dist/web/assets/{pug-CLzyQhia.js → pug-BfN8CpEH.js} +1 -1
- package/dist/web/assets/{qml-BHXfGB1l.js → qml-D6fRdLZt.js} +1 -1
- package/dist/web/assets/query-audit-section-C5RmhRSu.js +1 -0
- package/dist/web/assets/radar-KQ55EAFF-DBIZEaIU.js +1 -0
- package/dist/web/assets/{razor-BqetJLN3.js → razor-fg-9AScR.js} +1 -1
- package/dist/web/assets/remote-desktop-mobile-sheet-BF-75_03.js +2 -0
- package/dist/web/assets/remote-desktop-mobile-view-C9KR9I9G.js +1 -0
- package/dist/web/assets/remote-desktop-readiness-gate-BnKJicB2.js +1 -0
- package/dist/web/assets/remote-desktop-window-content-awXsNw_R.js +1 -0
- package/dist/web/assets/{rst-iQdJZ0BP.js → rst-BZgx076b.js} +1 -1
- package/dist/web/assets/{ruby-CNwzZGdH.js → ruby-iUkWDcLo.js} +1 -1
- package/dist/web/assets/{run-in-terminal-CBXmVGWa.js → run-in-terminal-bKRwuO2z.js} +1 -1
- package/dist/web/assets/{sas-BpBIT2eR.js → sas-CLXO0lZ6.js} +1 -1
- package/dist/web/assets/schedules-settings-section-BOecKJzl.js +1 -0
- package/dist/web/assets/{scss-D1MotuEp.js → scss-DQqteGaG.js} +1 -1
- package/dist/web/assets/send-Bb0jtcR6.js +1 -0
- package/dist/web/assets/{send-to-chat-DJAdlvJ8.js → send-to-chat-S8LCNX7V.js} +1 -1
- package/dist/web/assets/separator-DqGg3f9c.js +1 -0
- package/dist/web/assets/settings-body-DGi5DXY6.js +2 -0
- package/dist/web/assets/settings-store-Brjd0QnO.js +2 -0
- package/dist/web/assets/settings-tab-GM9zTRul.js +1 -0
- package/dist/web/assets/settings-window-content-Dvhxo4_H.js +1 -0
- package/dist/web/assets/{shaderlab-BNqmcPQi.js → shaderlab-CeMTqagR.js} +1 -1
- package/dist/web/assets/{shellsession-BBLOVOO-.js → shellsession-BAkquZAV.js} +1 -1
- package/dist/web/assets/{soy-BpTGo9uy.js → soy-Bxm9Ez55.js} +1 -1
- package/dist/web/assets/{sparql-D7A3yKob.js → sparql-C1phwdLc.js} +1 -1
- package/dist/web/assets/sql-query-editor-BmkGFzcL.js +1 -0
- package/dist/web/assets/sqlite-viewer-DbziVQXQ.js +1 -0
- package/dist/web/assets/{stata-B7N56ptS.js → stata-DGNMQdEb.js} +1 -1
- package/dist/web/assets/{surrealql-CTmnD-lv.js → surrealql-ByVrV320.js} +1 -1
- package/dist/web/assets/{svelte-BppwY9TR.js → svelte-CNWjIMhl.js} +1 -1
- package/dist/web/assets/system-monitor-body-CbHW5aYm.js +2 -0
- package/dist/web/assets/system-monitor-tab-C7EZLMIo.js +1 -0
- package/dist/web/assets/{system-monitor-window-content-B9Y98UW6.js → system-monitor-window-content-C_w4w81e.js} +1 -1
- package/dist/web/assets/{tab-host-window-content-C1j8zr68.js → tab-host-window-content-BSP1SEXS.js} +1 -1
- package/dist/web/assets/{tab-store-DzYSsw5a.js → tab-store-PZsP5ZwG.js} +1 -1
- package/dist/web/assets/team-member-sheet-C9N0iA1-.js +2 -0
- package/dist/web/assets/team-member-window-content-CM2QFv-s.js +1 -0
- package/dist/web/assets/{templ-CvTMKKra.js → templ-Cf0gQL4I.js} +1 -1
- package/dist/web/assets/{terminal-tab-B9I_HlES.js → terminal-tab-jWYELm8y.js} +3 -3
- package/dist/web/assets/{tex--c4POEUE.js → tex-BTIqPw_R.js} +1 -1
- package/dist/web/assets/tool-cards-jefdOWVJ.js +9 -0
- package/dist/web/assets/treemap-KZPCXAKY-DFiG9UYW.js +1 -0
- package/dist/web/assets/{ts-tags--kx7D-Hw.js → ts-tags-B8dN_Yf6.js} +1 -1
- package/dist/web/assets/{twig-UG9uMHGd.js → twig-BgemonbJ.js} +1 -1
- package/dist/web/assets/use-accounts-data-MhMl0Y4Z.js +1 -0
- package/dist/web/assets/{use-blob-url-Bdxopwwc.js → use-blob-url-Gfi69PtP.js} +1 -1
- package/dist/web/assets/use-monaco-theme-BbAd7DXr.js +1 -0
- package/dist/web/assets/{use-open-team-member-D9EOB8-i.js → use-open-team-member-CruUTqzD.js} +1 -1
- package/dist/web/assets/use-remote-desktop-display-choice-DlfMIWq3.js +1 -0
- package/dist/web/assets/{use-websocket-xOOwBdwP.js → use-websocket-Bq8X_SUI.js} +1 -1
- package/dist/web/assets/{vendor-mermaid-0Nt-ew76.js → vendor-mermaid-BKWscKt0.js} +1 -1
- package/dist/web/assets/video-preview-BGh3B0Lu.js +1 -0
- package/dist/web/assets/{vue-jjTomlbt.js → vue-D3D4lbXz.js} +1 -1
- package/dist/web/assets/{vue-html-8KEW6-I9.js → vue-html-DYEQFAiK.js} +1 -1
- package/dist/web/assets/{vue-vine-C5tohCkU.js → vue-vine-D0OPNscN.js} +1 -1
- package/dist/web/assets/{window-pip-registry-FdZdGF7h.js → window-pip-registry-BEN0QNSW.js} +1 -1
- package/dist/web/assets/window-store-DGKf9rPG.js +1 -0
- package/dist/web/assets/ws-auth-Dawx17PM.js +1 -0
- package/dist/web/assets/{xml-ByFFosqL.js → xml-DHaWgpya.js} +1 -1
- package/dist/web/assets/{xsl-CZUwiLkj.js → xsl-DVRN8JsA.js} +1 -1
- package/dist/web/index.html +32 -55
- package/dist/web/sw.js +1 -1
- package/docs/codebase-summary.md +9 -5
- package/docs/lessons-learned.md +11 -0
- package/package.json +2 -2
- package/packages/vscode-compat/node_modules/.bin/tsc.bunx +0 -0
- package/packages/vscode-compat/node_modules/.bin/tsc.exe +0 -0
- package/packages/vscode-compat/node_modules/.bin/tsserver.bunx +0 -0
- package/packages/vscode-compat/node_modules/.bin/tsserver.exe +0 -0
- package/src/providers/codex-app-server/codex-event-mapper.ts +70 -5
- package/src/providers/codex-app-server/codex-history.ts +24 -0
- package/src/providers/codex-app-server/codex-jsonrpc-client.ts +39 -2
- package/src/providers/codex-app-server/codex-protocol.ts +24 -0
- package/src/providers/codex-app-server/codex-provider.ts +159 -17
- package/src/providers/codex-app-server/codex-rollout-items.ts +149 -0
- package/src/providers/codex-app-server/codex-skill-parser.ts +63 -0
- package/src/providers/codex-app-server/codex-usage-fetch.ts +22 -20
- package/src/providers/codex-app-server/codex-usage-source.ts +37 -0
- package/src/server/routes/chat.ts +67 -9
- package/src/server/routes/codex-accounts.ts +9 -5
- package/src/server/ws/chat.ts +46 -1
- package/src/services/claude-usage-source.ts +40 -0
- package/src/services/claude-usage.service.ts +77 -146
- package/src/services/codex-account-login.ts +123 -55
- package/src/services/codex-account.service.ts +27 -4
- package/src/services/db.service.ts +161 -20
- package/src/services/edge-forwarder.ts +191 -191
- package/src/services/fs-credential-path-guard.ts +35 -8
- package/src/services/fs-path-guard.service.ts +1 -0
- package/src/services/postgres.service.ts +144 -72
- package/src/services/provider-usage/index.ts +63 -0
- package/src/services/provider-usage/register-usage-sources.ts +18 -0
- package/src/services/provider-usage/usage-memory-cache.ts +71 -0
- package/src/services/provider-usage/usage-registry.ts +157 -0
- package/src/services/provider-usage/usage-scheduler.ts +117 -0
- package/src/services/provider-usage/usage-snapshot-store.ts +126 -0
- package/src/services/provider-usage/usage-source.ts +82 -0
- package/src/services/proxy-agent-anthropic-bridge.ts +93 -0
- package/src/services/proxy-agent-bridge.ts +100 -0
- package/src/services/proxy-agent-turn.ts +161 -0
- package/src/services/proxy-anthropic-format.ts +157 -0
- package/src/services/proxy-openai-format.ts +148 -0
- package/src/services/slash-discovery/builtin-commands.ts +13 -0
- package/src/services/slash-discovery/codex-skill-items.ts +57 -0
- package/src/services/slash-discovery/provider-skill-sigil.ts +19 -0
- package/src/services/slash-discovery/types.ts +19 -0
- package/src/services/windows-process-tree.ts +351 -351
- package/src/types/chat.ts +7 -1
- package/src/web/app.tsx +1 -7
- package/src/web/components/chat/chat-history-bar.tsx +10 -1
- package/src/web/components/chat/chat-tab.tsx +17 -1
- package/src/web/components/chat/message-input.tsx +11 -4
- package/src/web/components/chat/message-list.tsx +14 -0
- package/src/web/components/chat/slash-command-picker.tsx +22 -3
- package/src/web/components/chat/tool-cards.tsx +47 -4
- package/src/web/components/chat/usage-badge.tsx +59 -426
- package/src/web/components/floating-window/window-content-registry.ts +2 -0
- package/src/web/components/floating-window/window-store-types.ts +1 -1
- package/src/web/components/layout/command-palette.tsx +2 -2
- package/src/web/components/layout/mobile-drawer.tsx +5 -4
- package/src/web/components/layout/mobile-nav.tsx +5 -1
- package/src/web/components/layout/nav-section-rail.tsx +2 -1
- package/src/web/components/layout/project-bottom-sheet.tsx +3 -9
- package/src/web/components/layout/sidebar.tsx +0 -2
- package/src/web/components/layout/tab-pop-out-menu-item.tsx +3 -2
- package/src/web/components/settings/accounts/account-add-dialog.tsx +155 -0
- package/src/web/components/settings/accounts/account-backup-format.ts +19 -0
- package/src/web/components/settings/accounts/account-bucket-row.tsx +55 -0
- package/src/web/components/settings/accounts/account-card.tsx +151 -0
- package/src/web/components/settings/accounts/account-delete-confirm.tsx +62 -0
- package/src/web/components/settings/accounts/account-export-dialog.tsx +154 -0
- package/src/web/components/settings/accounts/account-export-simulation.tsx +196 -0
- package/src/web/components/settings/accounts/account-import-dialog.tsx +72 -0
- package/src/web/components/settings/accounts/account-profile-panel.tsx +52 -0
- package/src/web/components/{chat → settings/accounts}/account-rotation-settings.tsx +1 -1
- package/src/web/components/settings/accounts/account-token-test-dialog.tsx +147 -0
- package/src/web/components/settings/accounts/account-usage-format.ts +92 -0
- package/src/web/components/{chat/usage-pattern-chart.tsx → settings/accounts/account-usage-pattern-chart.tsx} +1 -1
- package/src/web/components/settings/accounts/accounts-pane-header.tsx +113 -0
- package/src/web/components/settings/accounts/accounts-settings-section.tsx +80 -0
- package/src/web/components/settings/accounts/claude-accounts-section.tsx +169 -0
- package/src/web/components/settings/accounts/codex-accounts-section.tsx +144 -0
- package/src/web/components/settings/accounts/codex-add-account-dialog.tsx +130 -0
- package/src/web/components/settings/accounts/codex-backup-dialog.tsx +101 -0
- package/src/web/components/settings/accounts/codex-rotation-dialog.tsx +65 -0
- package/src/web/components/settings/accounts/use-accounts-data.ts +81 -0
- package/src/web/components/settings/accounts/use-codex-accounts.ts +151 -0
- package/src/web/components/settings/ai-settings-section.tsx +14 -4
- package/src/web/components/settings/appearance-settings-section.tsx +125 -0
- package/src/web/components/settings/general-settings-section.tsx +87 -0
- package/src/web/components/settings/jira-watcher-section.tsx +34 -0
- package/src/web/components/settings/notifications-settings-section.tsx +37 -0
- package/src/web/components/settings/open-settings.ts +42 -0
- package/src/web/components/settings/resolve-open-settings-action.ts +45 -0
- package/src/web/components/settings/settings-body.tsx +110 -0
- package/src/web/components/settings/settings-categories.ts +102 -0
- package/src/web/components/settings/settings-category-list.tsx +56 -0
- package/src/web/components/settings/settings-category-rail.tsx +62 -0
- package/src/web/components/settings/settings-section-content.tsx +43 -0
- package/src/web/components/settings/settings-tab.tsx +15 -329
- package/src/web/components/settings/settings-window-content.tsx +24 -0
- package/src/web/hooks/use-chat.ts +38 -1
- package/src/web/hooks/use-global-keybindings.ts +3 -4
- package/src/web/hooks/use-url-sync.ts +30 -0
- package/src/web/hooks/use-usage.ts +23 -11
- package/src/web/lib/sidebar-tabs/tab-registry.ts +9 -9
- package/src/web/lib/slash-items-cache.ts +27 -7
- package/src/web/stores/panel-utils.ts +11 -0
- package/src/web/stores/settings-store.ts +4 -2
- package/src/web/stores/window-panel-actions.ts +1 -3
- package/src/web/stores/window-panel-persistence.ts +9 -4
- package/dist/web/assets/adaptive-context-menu-BbuRVTLn.js +0 -1
- package/dist/web/assets/ai-resource-editor-BCCOUzox.js +0 -1
- package/dist/web/assets/ai-settings-section-BEM1z7kp.js +0 -1
- package/dist/web/assets/api-client-D1CePgQ4.js +0 -1
- package/dist/web/assets/api-settings-CNtKkHqW.js +0 -1
- package/dist/web/assets/api-settings-Cubuqjsu.js +0 -1
- package/dist/web/assets/architecture-PBZL5I3N-6konx-J1.js +0 -1
- package/dist/web/assets/chat-tab-CmXGsJGc.js +0 -15
- package/dist/web/assets/clipboard-B-gwJ8DF.js +0 -1
- package/dist/web/assets/code-editor-DtHXzNZ1.js +0 -7
- package/dist/web/assets/context-menu-DfWZAxQy.js +0 -1
- package/dist/web/assets/database-viewer-GVaQVMdb.js +0 -1
- package/dist/web/assets/dialog-BSfuiTkJ.js +0 -1
- package/dist/web/assets/diff-viewer-BG5_fH1E.js +0 -4
- package/dist/web/assets/explorer-body-BPFnjxYj.js +0 -2
- package/dist/web/assets/explorer-window-content-Da1T9uM4.js +0 -1
- package/dist/web/assets/extension-store-Bhu--fPX.js +0 -1
- package/dist/web/assets/external-link-B6Hl0U4X.js +0 -1
- package/dist/web/assets/eye-off-CUdzgmX8.js +0 -1
- package/dist/web/assets/file-store-6HW2Lfl4.js +0 -1
- package/dist/web/assets/git-log-panel-DNC9fV0M.js +0 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-ikUwsekd.js +0 -1
- package/dist/web/assets/group-chat-tab-DbUkcOaJ.js +0 -1
- package/dist/web/assets/index-DTqbMiVc.js +0 -52
- package/dist/web/assets/index-SFFv2-VM.css +0 -2
- package/dist/web/assets/info-3K5VOQVL-BAgmboBp.js +0 -1
- package/dist/web/assets/input-D0ol5jW0.js +0 -1
- package/dist/web/assets/keybindings-store-BGkB72gM.js +0 -1
- package/dist/web/assets/keybindings-store-Bq6VWl5X.js +0 -1
- package/dist/web/assets/lock-CNxUGpTk.js +0 -1
- package/dist/web/assets/markdown-renderer-DEAVpo0C.js +0 -1
- package/dist/web/assets/mobile-explorer-sheet-CD_kl-jA.js +0 -1
- package/dist/web/assets/monitor-smartphone-DLZ6Q05s.js +0 -1
- package/dist/web/assets/notification-store-Dgbsw9p9.js +0 -1
- package/dist/web/assets/packet-RMMSAZCW-RVlbbDRS.js +0 -1
- package/dist/web/assets/panel-store-UOV8wrfR.js +0 -1
- package/dist/web/assets/pdf-preview-B2CJ_EKv.js +0 -1
- package/dist/web/assets/pie-UPGHQEXC-ZWb4izpk.js +0 -1
- package/dist/web/assets/play-DpEEBJTT.js +0 -1
- package/dist/web/assets/radar-KQ55EAFF-bsP5x1Bx.js +0 -1
- package/dist/web/assets/refresh-cw-BQU5g2CY.js +0 -1
- package/dist/web/assets/remote-desktop-mobile-sheet-KKTRNjOL.js +0 -2
- package/dist/web/assets/remote-desktop-mobile-view-C1ST4-Fm.js +0 -1
- package/dist/web/assets/remote-desktop-readiness-gate-BH3BE5wA.js +0 -1
- package/dist/web/assets/remote-desktop-window-content-BaZJQt_N.js +0 -1
- package/dist/web/assets/rotate-ccw-CpjRo90J.js +0 -1
- package/dist/web/assets/scroll-area-lc0ZHORr.js +0 -1
- package/dist/web/assets/send-C_sLFt56.js +0 -1
- package/dist/web/assets/settings-store-zbPkG7o-.js +0 -2
- package/dist/web/assets/settings-tab-Cn-AqUP_.js +0 -1
- package/dist/web/assets/settings-tab-xyRpt2gq.js +0 -3
- package/dist/web/assets/shallow-CmPMAPte.js +0 -1
- package/dist/web/assets/sql-query-editor-B86NiZHI.js +0 -1
- package/dist/web/assets/sqlite-viewer-DB775F6C.js +0 -1
- package/dist/web/assets/switch-BmjKoIpR.js +0 -1
- package/dist/web/assets/system-monitor-body-BDxHQvy9.js +0 -2
- package/dist/web/assets/system-monitor-tab-BoTHLx6B.js +0 -1
- package/dist/web/assets/team-member-sheet-Cxz6h5Ne.js +0 -2
- package/dist/web/assets/team-member-window-content-CUSuRHCB.js +0 -1
- package/dist/web/assets/tool-cards-D3KSzd2K.js +0 -8
- package/dist/web/assets/trash-2-By4VMbC4.js +0 -1
- package/dist/web/assets/treemap-KZPCXAKY-Dh1oxNdQ.js +0 -1
- package/dist/web/assets/triangle-alert-CUTURFbG.js +0 -1
- package/dist/web/assets/upload-BNlX8D07.js +0 -1
- package/dist/web/assets/use-monaco-theme-CVCye2kq.js +0 -1
- package/dist/web/assets/use-remote-desktop-display-choice-neUqyE3p.js +0 -1
- package/dist/web/assets/video-preview-DvpgBDhM.js +0 -1
- package/dist/web/assets/window-store-D0x_fxr0.js +0 -1
- package/src/web/components/chat/account-dialogs.tsx +0 -384
- package/src/web/components/settings/accounts-settings-section.tsx +0 -1090
- package/src/web/components/settings/codex-accounts-section.tsx +0 -198
- package/src/web/components/settings/telegram-settings-section.tsx +0 -113
- /package/dist/web/assets/{abap-C5DFF0s3.js → abap-h3QyH8MW.js} +0 -0
- /package/dist/web/assets/{actionscript-3-B0qyRNbc.js → actionscript-3-mkxkq-FN.js} +0 -0
- /package/dist/web/assets/{ada-bAJzplug.js → ada-DhISK_EH.js} +0 -0
- /package/dist/web/assets/{andromeeda-DUCpISy6.js → andromeeda-DnlWWesX.js} +0 -0
- /package/dist/web/assets/{apache-BhAr84Z_.js → apache-P5AK3sfA.js} +0 -0
- /package/dist/web/assets/{apex-CRv8YnNq.js → apex-CJUPehg2.js} +0 -0
- /package/dist/web/assets/{api-client-BzWM7XPR.js → api-client-oY1tC3tM.js} +0 -0
- /package/dist/web/assets/{applescript-CD-Cw8bt.js → applescript-Dx6GGbay.js} +0 -0
- /package/dist/web/assets/{apply-theme-DJ794zSQ.js → apply-theme-DxTXOAcV.js} +0 -0
- /package/dist/web/assets/{ara-BUzeQYoG.js → ara-95Kx3qKb.js} +0 -0
- /package/dist/web/assets/{asciidoc-CT2hTg-v.js → asciidoc-Dp0RbIej.js} +0 -0
- /package/dist/web/assets/{asm-CAu35JeY.js → asm-C82coUjU.js} +0 -0
- /package/dist/web/assets/{aurora-x-BeFJoIA1.js → aurora-x-BFc3zeop.js} +0 -0
- /package/dist/web/assets/{awk-Vau-DrGN.js → awk-ChjC0icr.js} +0 -0
- /package/dist/web/assets/{ayu-dark-CLe-OTsQ.js → ayu-dark-DYfUf_PB.js} +0 -0
- /package/dist/web/assets/{ayu-light-CJbQfu4M.js → ayu-light-Bh6O0plO.js} +0 -0
- /package/dist/web/assets/{ayu-mirage-GWaKwdrx.js → ayu-mirage-DlNhHTGq.js} +0 -0
- /package/dist/web/assets/{ballerina-DWu0USI5.js → ballerina-Ceoll3Sk.js} +0 -0
- /package/dist/web/assets/{bat-_07Huw7E.js → bat-C7QS6Lfq.js} +0 -0
- /package/dist/web/assets/{beancount-EcvogrcF.js → beancount-5NMEAZgs.js} +0 -0
- /package/dist/web/assets/{berry-BBB87SY7.js → berry-BBL83Vbk.js} +0 -0
- /package/dist/web/assets/{bibtex-DbueS87A.js → bibtex-L7rPkPSG.js} +0 -0
- /package/dist/web/assets/{bicep-tcmXnUo4.js → bicep-BOwHIINh.js} +0 -0
- /package/dist/web/assets/{bird2-B2qwilIR.js → bird2-CWLsMom2.js} +0 -0
- /package/dist/web/assets/{c-Z58oivSt.js → c-CD4Ku5Cr.js} +0 -0
- /package/dist/web/assets/{c3-U40cGT9p.js → c3-CpMl-w3G.js} +0 -0
- /package/dist/web/assets/{cadence-BFcD-LyM.js → cadence-CvgF6ehN.js} +0 -0
- /package/dist/web/assets/{can-open-in-ppm-CdCK2c2a.js → can-open-in-ppm-j3jh7fUo.js} +0 -0
- /package/dist/web/assets/{catppuccin-frappe-RkC9AX6J.js → catppuccin-frappe-eo_dTm3F.js} +0 -0
- /package/dist/web/assets/{catppuccin-latte-Bsi4l-nO.js → catppuccin-latte-DoiMCU_7.js} +0 -0
- /package/dist/web/assets/{catppuccin-macchiato-B8JIdTW-.js → catppuccin-macchiato-UQxsK2Vh.js} +0 -0
- /package/dist/web/assets/{catppuccin-mocha-oH8wwrDj.js → catppuccin-mocha-J0i-8RNy.js} +0 -0
- /package/dist/web/assets/{clarity-C3WNeRCR.js → clarity-DXYM8-SA.js} +0 -0
- /package/dist/web/assets/{clojure-CFoAz7u9.js → clojure-DzamsL69.js} +0 -0
- /package/dist/web/assets/{cmake-Bcsqp9yR.js → cmake-CR6RP_T_.js} +0 -0
- /package/dist/web/assets/{codeowners-DC2Z7heP.js → codeowners-D1s6-IyY.js} +0 -0
- /package/dist/web/assets/{codeql-BfG68Al5.js → codeql-DAqNWMkS.js} +0 -0
- /package/dist/web/assets/{common-lisp-Z5P-lKQe.js → common-lisp-8e-KtitB.js} +0 -0
- /package/dist/web/assets/{coq-BJslqrnv.js → coq-UEq0WUcO.js} +0 -0
- /package/dist/web/assets/{csharp-DhhZTOTs.js → csharp-wxJ0TuCm.js} +0 -0
- /package/dist/web/assets/{css-DiFIpMfE.js → css-Cerub-yB.js} +0 -0
- /package/dist/web/assets/{csv-DViLbHxI.js → csv-Bh9_WD_h.js} +0 -0
- /package/dist/web/assets/{csv-parser-B2v8MoUy.js → csv-parser-BPIDt6ni.js} +0 -0
- /package/dist/web/assets/{cue-CoG3mg2I.js → cue-rzM1ztqU.js} +0 -0
- /package/dist/web/assets/{cypher-jTxC8NAB.js → cypher-DGaTQ8m0.js} +0 -0
- /package/dist/web/assets/{d-6hKIUAbU.js → d-DbkZGuhB.js} +0 -0
- /package/dist/web/assets/{dark-plus-D_-np2Sr.js → dark-plus-BO2bzZcA.js} +0 -0
- /package/dist/web/assets/{dart-CVpxLpVn.js → dart-BDhCoqVg.js} +0 -0
- /package/dist/web/assets/{data-grid-types-Dq_H1IPa.js → data-grid-types-CdAg-4W1.js} +0 -0
- /package/dist/web/assets/{dax-BPqJ8qIY.js → dax-Bh03stWh.js} +0 -0
- /package/dist/web/assets/{desktop-u_y5_Hr5.js → desktop-BOdr_fwq.js} +0 -0
- /package/dist/web/assets/{diff-Duj1XMrd.js → diff-Bb_gTsS2.js} +0 -0
- /package/dist/web/assets/{dist-CEZ4plgH.js → dist-BUMDu0av.js} +0 -0
- /package/dist/web/assets/{dist-aDSxTR_v.js → dist-DonmFWG8.js} +0 -0
- /package/dist/web/assets/{docker-8UGSCc7N.js → docker-BDsTF5ce.js} +0 -0
- /package/dist/web/assets/{dotenv-DVnj8kyR.js → dotenv-CbCWp9Ti.js} +0 -0
- /package/dist/web/assets/{dracula-D0cEmSg7.js → dracula-Cd1fxvoX.js} +0 -0
- /package/dist/web/assets/{dracula-soft-DfGS5Yfr.js → dracula-soft-B7eXkfXG.js} +0 -0
- /package/dist/web/assets/{dream-maker-qG8kMGTF.js → dream-maker-XlzuS21R.js} +0 -0
- /package/dist/web/assets/{emacs-lisp-iVqyMLyT.js → emacs-lisp-CXV1Ki_7.js} +0 -0
- /package/dist/web/assets/{everforest-dark-Cruygd5U.js → everforest-dark-DuqR60Dg.js} +0 -0
- /package/dist/web/assets/{everforest-light-BiJa8RHL.js → everforest-light-q4BPnenD.js} +0 -0
- /package/dist/web/assets/{fennel-H6XSEIoz.js → fennel-B-e0TX2Q.js} +0 -0
- /package/dist/web/assets/{fish-ho_7CxX6.js → fish-Bzb6-TtF.js} +0 -0
- /package/dist/web/assets/{fluent-1l5ssJTF.js → fluent-B-szH2-f.js} +0 -0
- /package/dist/web/assets/{folder-symlink-Dx1hOk8X.js → folder-symlink-fQRoG_rV.js} +0 -0
- /package/dist/web/assets/{format-bytes-gQw5wzqa.js → format-bytes-CKXk9k2d.js} +0 -0
- /package/dist/web/assets/{fortran-free-form-Bo1x233A.js → fortran-free-form-OS6SGYC0.js} +0 -0
- /package/dist/web/assets/{gdscript-DffFMp_k.js → gdscript-FYq4OP2d.js} +0 -0
- /package/dist/web/assets/{gdshader-CvnpAiAF.js → gdshader-BpG6P8rw.js} +0 -0
- /package/dist/web/assets/{genie-2eOvAEzW.js → genie-BS9RLAna.js} +0 -0
- /package/dist/web/assets/{gherkin-DL-5OUlV.js → gherkin-B_9FdtGs.js} +0 -0
- /package/dist/web/assets/{github-dark-DrTZprgG.js → github-dark-cfaIlnBV.js} +0 -0
- /package/dist/web/assets/{github-dark-default-_mDH8IMP.js → github-dark-default-B2Wv65He.js} +0 -0
- /package/dist/web/assets/{github-dark-dimmed-C48hJuLn.js → github-dark-dimmed-CtM9X76s.js} +0 -0
- /package/dist/web/assets/{github-dark-high-contrast-BQ-zGGNe.js → github-dark-high-contrast-DLHyk0tG.js} +0 -0
- /package/dist/web/assets/{github-light-BYc90Yx8.js → github-light-DWEI10iG.js} +0 -0
- /package/dist/web/assets/{github-light-default-Daecj4J5.js → github-light-default-DcJkK1YN.js} +0 -0
- /package/dist/web/assets/{github-light-high-contrast-DoafCbsU.js → github-light-high-contrast-Bcx9iKDt.js} +0 -0
- /package/dist/web/assets/{gleam-hUWWjmnp.js → gleam-DVpiM6b2.js} +0 -0
- /package/dist/web/assets/{gn-7PJ3R70o.js → gn-B80DNc0i.js} +0 -0
- /package/dist/web/assets/{gnuplot-V-Pxck7b.js → gnuplot-D9i3MaT-.js} +0 -0
- /package/dist/web/assets/{go-ARF6DprD.js → go-CwCBf_Pt.js} +0 -0
- /package/dist/web/assets/{groovy-CKGBIPOz.js → groovy-D7mEfwmW.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-hard-Cm9wqdI3.js → gruvbox-dark-hard-C4R4MOoH.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-medium-Cmsd6dG8.js → gruvbox-dark-medium-CZnFC0s8.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-soft-BBGlGGCz.js → gruvbox-dark-soft-DFcJlEBl.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-hard-BCR7KmqG.js → gruvbox-light-hard-B6IF_1L-.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-medium-CP32bga8.js → gruvbox-light-medium-C19AZJv2.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-soft-qjxk8ps6.js → gruvbox-light-soft-BAnpDm9B.js} +0 -0
- /package/dist/web/assets/{haskell-C5DzwGRa.js → haskell-CsXSYXRG.js} +0 -0
- /package/dist/web/assets/{haxe-C8f_jCVv.js → haxe-B1uyiI9t.js} +0 -0
- /package/dist/web/assets/{hcl-DetJkf6E.js → hcl-DlYKn5q9.js} +0 -0
- /package/dist/web/assets/{hjson-DeZhwIdo.js → hjson-C_0VMGJJ.js} +0 -0
- /package/dist/web/assets/{hlsl-SMIDLRyX.js → hlsl-zt2PEzbg.js} +0 -0
- /package/dist/web/assets/{horizon-BMAVDxMx.js → horizon-6wQfyOK0.js} +0 -0
- /package/dist/web/assets/{horizon-bright-3BaV_VGD.js → horizon-bright-CqI2qTim.js} +0 -0
- /package/dist/web/assets/{houston-BWBbEPvb.js → houston-BljpHDoG.js} +0 -0
- /package/dist/web/assets/{hy-DcsmE-Nm.js → hy-XzuL55dw.js} +0 -0
- /package/dist/web/assets/{image-gallery-Bqb-wjNw.js → image-gallery-Cj8nd4xW.js} +0 -0
- /package/dist/web/assets/{imba-CnkgnYMf.js → imba-CqhhjBrP.js} +0 -0
- /package/dist/web/assets/{ini-BnvTdau7.js → ini-CiFDi6cY.js} +0 -0
- /package/dist/web/assets/{java-BLFACXat.js → java-OsQgTL5-.js} +0 -0
- /package/dist/web/assets/{javascript-BKcFWENk.js → javascript-qWAOScxl.js} +0 -0
- /package/dist/web/assets/{json-3AJtJADf.js → json-RiAKsaD1.js} +0 -0
- /package/dist/web/assets/{json5-DLyDVlwe.js → json5-BeCnS3H2.js} +0 -0
- /package/dist/web/assets/{jsonc-B5DgF1jW.js → jsonc-CbTW1Mka.js} +0 -0
- /package/dist/web/assets/{jsonl-CV_O71a2.js → jsonl-BLSi_669.js} +0 -0
- /package/dist/web/assets/{jsonnet-DZfPLgYz.js → jsonnet-C3xAggou.js} +0 -0
- /package/dist/web/assets/{jssm-Cr1zYOsY.js → jssm-CLdiagC9.js} +0 -0
- /package/dist/web/assets/{jsx-VN-JS2_q.js → jsx-lYE1lb50.js} +0 -0
- /package/dist/web/assets/{kanagawa-dragon-D9CcT3xg.js → kanagawa-dragon-Cd1vcrpD.js} +0 -0
- /package/dist/web/assets/{kanagawa-lotus-DLXsB8fr.js → kanagawa-lotus-Dw3QKtk9.js} +0 -0
- /package/dist/web/assets/{kanagawa-wave-weDs3Wjz.js → kanagawa-wave-DIq5Zv5G.js} +0 -0
- /package/dist/web/assets/{kdl-0XF56jnT.js → kdl-BfDV0825.js} +0 -0
- /package/dist/web/assets/{keyboard-DBBaoQSu.js → keyboard-DiOYrs6C.js} +0 -0
- /package/dist/web/assets/{kotlin-BPkH9vzk.js → kotlin-DONxR4wD.js} +0 -0
- /package/dist/web/assets/{kusto-DMVpADb9.js → kusto-BDP63Fh5.js} +0 -0
- /package/dist/web/assets/{laserwave-Cl6T2cGP.js → laserwave-BeBrWApL.js} +0 -0
- /package/dist/web/assets/{lean-BvwzQGl1.js → lean-DOZc0N1A.js} +0 -0
- /package/dist/web/assets/{less-DI_03xkC.js → less-C3H9NlDv.js} +0 -0
- /package/dist/web/assets/{lib-v9x_qXgI.js → lib-xrSJ4EZX.js} +0 -0
- /package/dist/web/assets/{libesm-dcbw4_18.js → libesm-DLBE59ul.js} +0 -0
- /package/dist/web/assets/{light-plus-hIiIsQN_.js → light-plus-C_kTL608.js} +0 -0
- /package/dist/web/assets/{llvm-B5hjjSWc.js → llvm-Dsp27U3P.js} +0 -0
- /package/dist/web/assets/{loader-circle-DouKgIXy.js → loader-circle-DPMrnLQ2.js} +0 -0
- /package/dist/web/assets/{log-D28raQiE.js → log-BywQaTCy.js} +0 -0
- /package/dist/web/assets/{logo-6Cdd4ASb.js → logo-BirJgciO.js} +0 -0
- /package/dist/web/assets/{luau-CK9nKpOX.js → luau-Btrz5kJB.js} +0 -0
- /package/dist/web/assets/{make-CVnxKCB6.js → make-Kl7nFQrS.js} +0 -0
- /package/dist/web/assets/{markdown-B9mMQzD5.js → markdown-qsC4P8S-.js} +0 -0
- /package/dist/web/assets/{material-theme-BZSs2o0j.js → material-theme-CC__ui3p.js} +0 -0
- /package/dist/web/assets/{material-theme-darker-BeAQBzQ_.js → material-theme-darker-D4FxG2FZ.js} +0 -0
- /package/dist/web/assets/{material-theme-lighter-BAe0IXcs.js → material-theme-lighter-zGqiWcxn.js} +0 -0
- /package/dist/web/assets/{material-theme-ocean-CiPIf0Np.js → material-theme-ocean-CZIdTTB1.js} +0 -0
- /package/dist/web/assets/{material-theme-palenight-DJBb4_XN.js → material-theme-palenight-CEIs0GcK.js} +0 -0
- /package/dist/web/assets/{matlab-CY0dbp_s.js → matlab-Cf0aUjN9.js} +0 -0
- /package/dist/web/assets/{mdx-DU9zbF7h.js → mdx-CIsXjH86.js} +0 -0
- /package/dist/web/assets/{mermaid-UmmjGwdh.js → mermaid-B06WO9Qc.js} +0 -0
- /package/dist/web/assets/{min-dark-C59GkzOm.js → min-dark-z8fyp0FT.js} +0 -0
- /package/dist/web/assets/{min-light-B9QglrdQ.js → min-light-Dr-qesbW.js} +0 -0
- /package/dist/web/assets/{mipsasm-Dc6Jsr_D.js → mipsasm-CWHzYLcE.js} +0 -0
- /package/dist/web/assets/{mojo-D5ij-nGW.js → mojo-D9zXrKmv.js} +0 -0
- /package/dist/web/assets/{monitor-CxPV_fQI.js → monitor-C835ucWH.js} +0 -0
- /package/dist/web/assets/{monokai-QAtE_yCP.js → monokai-4b-AKOpX.js} +0 -0
- /package/dist/web/assets/{moonbit-BMtIdiTL.js → moonbit-Dl4dhOZJ.js} +0 -0
- /package/dist/web/assets/{move-BB_jMHYZ.js → move-DTYKJmXy.js} +0 -0
- /package/dist/web/assets/{narrat-BvvoKGVn.js → narrat-DnvXOS-o.js} +0 -0
- /package/dist/web/assets/{nextflow-groovy-CqxtAsnF.js → nextflow-groovy-Di8QnEXg.js} +0 -0
- /package/dist/web/assets/{night-owl-CLoE9kjt.js → night-owl-Czh0yq-H.js} +0 -0
- /package/dist/web/assets/{night-owl-light-BwIFT5Xd.js → night-owl-light-D_OpZqOR.js} +0 -0
- /package/dist/web/assets/{nix-BKlnVT9r.js → nix-2MvcjpK3.js} +0 -0
- /package/dist/web/assets/{nord-B7R1KRAK.js → nord-CG_r4cyk.js} +0 -0
- /package/dist/web/assets/{nushell-CzUme-NJ.js → nushell-DINskMcU.js} +0 -0
- /package/dist/web/assets/{objective-c-D-UKKiOc.js → objective-c-Dv7BIicR.js} +0 -0
- /package/dist/web/assets/{objective-cpp-BucWn4Fx.js → objective-cpp-CpqsCxPb.js} +0 -0
- /package/dist/web/assets/{ocaml-EJCNXMGo.js → ocaml-DwTsYSmW.js} +0 -0
- /package/dist/web/assets/{odin-CEjnRU_3.js → odin-G-4O4k4t.js} +0 -0
- /package/dist/web/assets/{one-dark-pro-Bv469NX5.js → one-dark-pro-13Pv5SjI.js} +0 -0
- /package/dist/web/assets/{one-light-BlzO5RWI.js → one-light-Bi2e86mS.js} +0 -0
- /package/dist/web/assets/{openscad-Czl6alLw.js → openscad-D0WsOTij.js} +0 -0
- /package/dist/web/assets/{pascal-BI9fLvG4.js → pascal-DmDStuXr.js} +0 -0
- /package/dist/web/assets/{pkl-ChQLsyjU.js → pkl-Bl6Qjtuo.js} +0 -0
- /package/dist/web/assets/{plastic-DGL3mtdL.js → plastic-UeSVSjTI.js} +0 -0
- /package/dist/web/assets/{plsql-BJDjqcWp.js → plsql-DreFC689.js} +0 -0
- /package/dist/web/assets/{po-DK5O3OpF.js → po-ZIjccxsX.js} +0 -0
- /package/dist/web/assets/{poimandres-CnuqZT4T.js → poimandres-CvQHJPrI.js} +0 -0
- /package/dist/web/assets/{polar-DCsUxSsQ.js → polar-BrMB0Kgh.js} +0 -0
- /package/dist/web/assets/{portal-container-context-D-495m5C.js → portal-container-context-a399Hd9F.js} +0 -0
- /package/dist/web/assets/{postcss-8UjdFmis.js → postcss-pgUsil1T.js} +0 -0
- /package/dist/web/assets/{powerquery-CAp0CGQj.js → powerquery-CTsZkCg_.js} +0 -0
- /package/dist/web/assets/{powershell-CbBsKZJP.js → powershell-CcwVuBAp.js} +0 -0
- /package/dist/web/assets/{prisma-BnNpXCJN.js → prisma-spjnAI5W.js} +0 -0
- /package/dist/web/assets/{prolog-CC-l9E8Y.js → prolog-ChCkZpDO.js} +0 -0
- /package/dist/web/assets/{proto-CV_1k2rL.js → proto-wVGqK4gB.js} +0 -0
- /package/dist/web/assets/{puppet-2D8HU2bv.js → puppet-CED7iwPQ.js} +0 -0
- /package/dist/web/assets/{purescript-BEJAORgU.js → purescript-u9lGiQKV.js} +0 -0
- /package/dist/web/assets/{python-BBdbb7nn.js → python-DeqTx6ef.js} +0 -0
- /package/dist/web/assets/{qmldir-D5ZYI4Ie.js → qmldir-BH1uoPrX.js} +0 -0
- /package/dist/web/assets/{qss-BOJVGyXE.js → qss-CUVKA_13.js} +0 -0
- /package/dist/web/assets/{r-B5dlEtkj.js → r-BqdPzqYH.js} +0 -0
- /package/dist/web/assets/{racket-BUAEygJ6.js → racket-C2eTgPN3.js} +0 -0
- /package/dist/web/assets/{raku-BG0Ss0ID.js → raku-uAay1sWx.js} +0 -0
- /package/dist/web/assets/{react-B549sK_T.js → react-X4jUntEf.js} +0 -0
- /package/dist/web/assets/{red-C5yyQbtG.js → red-DTZP1ZfC.js} +0 -0
- /package/dist/web/assets/{reg-DHoBWtLf.js → reg-Cdk3YsxY.js} +0 -0
- /package/dist/web/assets/{regexp-DDtJrlKv.js → regexp-Bpa2aS7K.js} +0 -0
- /package/dist/web/assets/{rel-CR6WJDRD.js → rel-bNsb1-3L.js} +0 -0
- /package/dist/web/assets/{resolve-theme-4fexHWl6.js → resolve-theme-DQk_YcsT.js} +0 -0
- /package/dist/web/assets/{riscv-2V9tC-nx.js → riscv-DSaubByW.js} +0 -0
- /package/dist/web/assets/{ron-QLTmqGUw.js → ron-DbQzYM6i.js} +0 -0
- /package/dist/web/assets/{rose-pine-be0__X3A.js → rose-pine-C7RsprTu.js} +0 -0
- /package/dist/web/assets/{rose-pine-dawn-n_WQJwsW.js → rose-pine-dawn-VP976YzL.js} +0 -0
- /package/dist/web/assets/{rose-pine-moon-t2aLtzgB.js → rose-pine-moon-XHZNhOqd.js} +0 -0
- /package/dist/web/assets/{rosmsg-Ce1JD8vt.js → rosmsg-BVQlx0Lj.js} +0 -0
- /package/dist/web/assets/{rust-5fHH-ZoW.js → rust-VHaQ3rh3.js} +0 -0
- /package/dist/web/assets/{sass-DPmGFiOJ.js → sass-CO-kMkwD.js} +0 -0
- /package/dist/web/assets/{save-DYLiVzU8.js → save-CipGrG1k.js} +0 -0
- /package/dist/web/assets/{scala-BRMVFbZF.js → scala-cY0GXkiz.js} +0 -0
- /package/dist/web/assets/{scheme-BMSJWevF.js → scheme-D4LNZE0X.js} +0 -0
- /package/dist/web/assets/{sdbl-DTZ-pEHI.js → sdbl-Bh2Fd2cn.js} +0 -0
- /package/dist/web/assets/{shellscript-6-9yg5Cz.js → shellscript-KKclmCnt.js} +0 -0
- /package/dist/web/assets/{shield-check-CUI36aSc.js → shield-check-BsG3LV8i.js} +0 -0
- /package/dist/web/assets/{shield-off-GdkkVhb3.js → shield-off-7jKWpJBD.js} +0 -0
- /package/dist/web/assets/{slack-dark-DKJgiR7e.js → slack-dark-D6YQ-fqf.js} +0 -0
- /package/dist/web/assets/{slack-ochin-WjehVgyK.js → slack-ochin-CDf66CDj.js} +0 -0
- /package/dist/web/assets/{smalltalk-BChf4SN1.js → smalltalk-D2OH_QIl.js} +0 -0
- /package/dist/web/assets/{snazzy-light-C1WAwOx6.js → snazzy-light-CQeF8G1K.js} +0 -0
- /package/dist/web/assets/{solarized-dark-BxREZrGx.js → solarized-dark-LTPf1iLZ.js} +0 -0
- /package/dist/web/assets/{solarized-light-DG29B1pR.js → solarized-light-Ose1n88k.js} +0 -0
- /package/dist/web/assets/{solidity-bDbtGwdR.js → solidity-lFuOMHki.js} +0 -0
- /package/dist/web/assets/{splunk-Ceqx5njI.js → splunk-Bov-sB5S.js} +0 -0
- /package/dist/web/assets/{sql-OwMEKbz8.js → sql-BKyZ6wmk.js} +0 -0
- /package/dist/web/assets/{ssh-config-WAR3lu2k.js → ssh-config-CqJoMGMb.js} +0 -0
- /package/dist/web/assets/{stylus-Cjmoz3d8.js → stylus-Bf5pWRLI.js} +0 -0
- /package/dist/web/assets/{swift-S_ZSBG1q.js → swift-D5d1hn_u.js} +0 -0
- /package/dist/web/assets/{synthwave-84-DiUeWMSh.js → synthwave-84-B4c6YAgO.js} +0 -0
- /package/dist/web/assets/{system-verilog-RRjqPjUV.js → system-verilog-D7fXyDsI.js} +0 -0
- /package/dist/web/assets/{systemd-BD3SkXoU.js → systemd-oNagzlBK.js} +0 -0
- /package/dist/web/assets/{table-D5Q4ap4W.js → table-CRJsmfeg.js} +0 -0
- /package/dist/web/assets/{talonscript-DWefISOm.js → talonscript-C6Nqm9Dm.js} +0 -0
- /package/dist/web/assets/{tasl-DCjSS-09.js → tasl-CaWy-cBX.js} +0 -0
- /package/dist/web/assets/{tcl-DTYqT_66.js → tcl-C435P5Mq.js} +0 -0
- /package/dist/web/assets/{terraform-CkbP5QnR.js → terraform-CVDxqdPf.js} +0 -0
- /package/dist/web/assets/{text-wrap-DBvQD3kc.js → text-wrap-oiekm5SB.js} +0 -0
- /package/dist/web/assets/{tokyo-night-qVSqO9xy.js → tokyo-night-Uhs17tQ3.js} +0 -0
- /package/dist/web/assets/{toml-D9F5Osqs.js → toml-j6fPOJ6P.js} +0 -0
- /package/dist/web/assets/{tsv-C5tpHoxm.js → tsv-h78O6xrL.js} +0 -0
- /package/dist/web/assets/{tsx-CMWkGtGm.js → tsx-Bol-vCrQ.js} +0 -0
- /package/dist/web/assets/{turtle-Cn4z3xOi.js → turtle-Dw8eFf04.js} +0 -0
- /package/dist/web/assets/{typescript-DSyAEWsn.js → typescript-Dtz7DPci.js} +0 -0
- /package/dist/web/assets/{typespec-B93Qap7y.js → typespec-BIOQ7dMP.js} +0 -0
- /package/dist/web/assets/{typst-DtlOmFlX.js → typst-CoaGMjzu.js} +0 -0
- /package/dist/web/assets/{use-is-mobile-B9UZNwSm.js → use-is-mobile-CYW3FJFE.js} +0 -0
- /package/dist/web/assets/{use-visual-viewport-Sfb6CZ80.js → use-visual-viewport-rnvuooQZ.js} +0 -0
- /package/dist/web/assets/{utils-QHe_Q6UF.js → utils-C9OjCrou.js} +0 -0
- /package/dist/web/assets/{v-Dfzf-szS.js → v-BoqFdpJ7.js} +0 -0
- /package/dist/web/assets/{vala-CyiQZQ9f.js → vala-COBpNnCC.js} +0 -0
- /package/dist/web/assets/{vb-C7DGSldd.js → vb-DxI86znh.js} +0 -0
- /package/dist/web/assets/{vendor-xterm-BHKF_Nsf.js → vendor-xterm-BaMOj65g.js} +0 -0
- /package/dist/web/assets/{verilog-kF-hClY2.js → verilog-DcTAamuQ.js} +0 -0
- /package/dist/web/assets/{vesper-BmKcADuz.js → vesper-DO298JSv.js} +0 -0
- /package/dist/web/assets/{vhdl-CDnb16KJ.js → vhdl-Dq-bDVSN.js} +0 -0
- /package/dist/web/assets/{viml-B8cs6Ady.js → viml-zHTRWuQh.js} +0 -0
- /package/dist/web/assets/{vitesse-black-BR6o9Gto.js → vitesse-black-jlYMJCBf.js} +0 -0
- /package/dist/web/assets/{vitesse-dark-Dye14-KB.js → vitesse-dark-DS0sUlSZ.js} +0 -0
- /package/dist/web/assets/{vitesse-light-BLpswIxf.js → vitesse-light-HSeZ2YhG.js} +0 -0
- /package/dist/web/assets/{vyper-DU51Z97W.js → vyper-BQfKVO3M.js} +0 -0
- /package/dist/web/assets/{wasm-ChtccNfv.js → wasm-B1xp9bLp.js} +0 -0
- /package/dist/web/assets/{wasm-CVqlJAR2.js → wasm-B6mCuI8h.js} +0 -0
- /package/dist/web/assets/{wenyan-DhCa3hCn.js → wenyan-CfpS05sP.js} +0 -0
- /package/dist/web/assets/{wgsl-Bj1Ec4vv.js → wgsl-14GDsWRD.js} +0 -0
- /package/dist/web/assets/{wikitext-C90DHNa0.js → wikitext-Ccbq5EVX.js} +0 -0
- /package/dist/web/assets/{wit-DUgbOD7Z.js → wit-C19hb3R5.js} +0 -0
- /package/dist/web/assets/{wolfram-BgwUqD1I.js → wolfram-B-ZSwGwP.js} +0 -0
- /package/dist/web/assets/{x-DiOUR_mp.js → x-YDu4m2Iq.js} +0 -0
- /package/dist/web/assets/{yaml-DEQulj-8.js → yaml-mw_JUeio.js} +0 -0
- /package/dist/web/assets/{zenscript-C7Jp7TgT.js → zenscript-WbzDRjvF.js} +0 -0
- /package/dist/web/assets/{zig-9x4qXYLg.js → zig-F0vvImTJ.js} +0 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formatting and colour rules for account usage, shared by the Settings pane and the chat
|
|
3
|
+
* usage chip.
|
|
4
|
+
*
|
|
5
|
+
* Extracted so both presentations agree: a bucket that reads 91% red in one place must not
|
|
6
|
+
* read amber in the other, and "expires in" must not drift between them either.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { LimitBucket } from "../../../../types/chat";
|
|
10
|
+
import type { AccountInfo } from "../../../lib/api-settings";
|
|
11
|
+
|
|
12
|
+
/** Text colour for a utilisation percentage. Thresholds are shared, not per-caller. */
|
|
13
|
+
export function pctColor(pct: number): string {
|
|
14
|
+
if (pct >= 90) return "text-error";
|
|
15
|
+
if (pct >= 70) return "text-warning";
|
|
16
|
+
return "text-success";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Fill colour for a utilisation bar — same thresholds as `pctColor`. */
|
|
20
|
+
export function barColor(pct: number): string {
|
|
21
|
+
if (pct >= 90) return "bg-error";
|
|
22
|
+
if (pct >= 70) return "bg-warning";
|
|
23
|
+
return "bg-success";
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* "Resets in" for a bucket, from whichever of the three shapes the server sent.
|
|
28
|
+
* Returns null when the bucket carries no reset information at all.
|
|
29
|
+
*/
|
|
30
|
+
export function formatResetTime(bucket?: LimitBucket): string | null {
|
|
31
|
+
if (!bucket) return null;
|
|
32
|
+
let totalMins: number | null = null;
|
|
33
|
+
if (bucket.resetsInMinutes != null) {
|
|
34
|
+
totalMins = bucket.resetsInMinutes;
|
|
35
|
+
} else if (bucket.resetsInHours != null) {
|
|
36
|
+
totalMins = Math.round(bucket.resetsInHours * 60);
|
|
37
|
+
} else if (bucket.resetsAt) {
|
|
38
|
+
const diff = new Date(bucket.resetsAt).getTime() - Date.now();
|
|
39
|
+
totalMins = diff > 0 ? Math.ceil(diff / 60_000) : 0;
|
|
40
|
+
}
|
|
41
|
+
if (totalMins == null) return null;
|
|
42
|
+
if (totalMins <= 0) return "now";
|
|
43
|
+
const d = Math.floor(totalMins / 1440);
|
|
44
|
+
const h = Math.floor((totalMins % 1440) / 60);
|
|
45
|
+
const m = totalMins % 60;
|
|
46
|
+
if (d > 0) return m > 0 ? `${d}d ${h}h ${m}m` : h > 0 ? `${d}d ${h}h` : `${d}d`;
|
|
47
|
+
if (h > 0) return m > 0 ? `${h}h ${m}m` : `${h}h`;
|
|
48
|
+
return `${m}m`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Time until a token expires. Takes milliseconds. */
|
|
52
|
+
export function formatExpiry(expiresAtMs: number): string {
|
|
53
|
+
const diff = expiresAtMs - Date.now();
|
|
54
|
+
if (diff <= 0) return "expired";
|
|
55
|
+
const mins = Math.ceil(diff / 60_000);
|
|
56
|
+
const h = Math.floor(mins / 60);
|
|
57
|
+
const d = Math.floor(h / 24);
|
|
58
|
+
if (d > 0) return `${d}d ${h % 24}h`;
|
|
59
|
+
if (h > 0) return `${h}h ${mins % 60}m`;
|
|
60
|
+
return `${mins}m`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Human-readable token status.
|
|
65
|
+
*
|
|
66
|
+
* An expired token with a refresh token is only a warning, not an error: the server renews
|
|
67
|
+
* it on the next call. Without one it is genuinely dead, which is why the two cases differ.
|
|
68
|
+
*/
|
|
69
|
+
export function tokenStatus(info?: AccountInfo): { label: string; tip: string; color: string } {
|
|
70
|
+
if (!info) return { label: "unknown", tip: "No account info available", color: "text-text-subtle" };
|
|
71
|
+
if (!info.expiresAt) return { label: "key", tip: "API key (no expiry)", color: "text-text-subtle" };
|
|
72
|
+
const expired = info.expiresAt * 1000 < Date.now(); // expiresAt is seconds
|
|
73
|
+
if (expired && info.hasRefreshToken) return { label: "expired", tip: "Token expired but has refresh token — will auto-renew", color: "text-warning" };
|
|
74
|
+
if (expired) return { label: "expired", tip: "Token expired, no refresh token", color: "text-error" };
|
|
75
|
+
if (info.hasRefreshToken) return { label: "long-lived", tip: "OAuth token with refresh — long-lived", color: "text-success" };
|
|
76
|
+
return { label: "temp", tip: "Temporary token without refresh — will expire", color: "text-warning" };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Relative age of a timestamp, for "last updated" labels. */
|
|
80
|
+
export function formatLastUpdated(ts: number | null | undefined): string | null {
|
|
81
|
+
if (!ts) return null;
|
|
82
|
+
const secs = Math.round((Date.now() - ts) / 1000);
|
|
83
|
+
if (secs < 5) return "just now";
|
|
84
|
+
if (secs < 60) return `${secs}s ago`;
|
|
85
|
+
const mins = Math.floor(secs / 60);
|
|
86
|
+
if (mins < 60) return `${mins}m ago`;
|
|
87
|
+
const hrs = Math.floor(mins / 60);
|
|
88
|
+
const remainMins = mins % 60;
|
|
89
|
+
if (hrs < 24) return remainMins > 0 ? `${hrs}h ${remainMins}m ago` : `${hrs}h ago`;
|
|
90
|
+
const days = Math.floor(hrs / 24);
|
|
91
|
+
return `${days}d ago`;
|
|
92
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useState, useEffect, useMemo } from "react";
|
|
2
2
|
import { Loader2 } from "lucide-react";
|
|
3
|
-
import { getUsageHistory, type UsageSnapshot } from "
|
|
3
|
+
import { getUsageHistory, type UsageSnapshot } from "../../../lib/api-settings";
|
|
4
4
|
|
|
5
5
|
const DAY_LABELS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
|
|
6
6
|
const HOUR_LABELS = Array.from({ length: 24 }, (_, i) => i);
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The top of an accounts pane: what this provider is, a refresh, and its action buttons.
|
|
3
|
+
*
|
|
4
|
+
* Shared by every provider's pane so the sub-tabs read as one screen. They used to be two
|
|
5
|
+
* different designs — one on shadcn buttons with dialogs, the other on raw buttons with
|
|
6
|
+
* forms inline down the page — and switching tabs looked like switching apps.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { RefreshCw, X } from "lucide-react";
|
|
10
|
+
import { Button } from "@/components/ui/button";
|
|
11
|
+
|
|
12
|
+
export function AccountsPaneHeader({ description, onRefresh, refreshing, disabled, actions }: {
|
|
13
|
+
/** One line on what these accounts are for. */
|
|
14
|
+
description: React.ReactNode;
|
|
15
|
+
onRefresh: () => void;
|
|
16
|
+
refreshing?: boolean;
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
/** The pane's action buttons, in a wrapping row. */
|
|
19
|
+
actions: React.ReactNode;
|
|
20
|
+
}) {
|
|
21
|
+
return (
|
|
22
|
+
<section className="space-y-2">
|
|
23
|
+
<div className="flex items-start justify-between gap-3">
|
|
24
|
+
<p className="text-xs text-muted-foreground">{description}</p>
|
|
25
|
+
<Button
|
|
26
|
+
variant="ghost"
|
|
27
|
+
size="icon"
|
|
28
|
+
className="size-8 shrink-0 cursor-pointer"
|
|
29
|
+
onClick={onRefresh}
|
|
30
|
+
disabled={disabled || refreshing}
|
|
31
|
+
title="Refresh"
|
|
32
|
+
aria-label="Refresh accounts"
|
|
33
|
+
>
|
|
34
|
+
<RefreshCw className={`size-4 ${refreshing ? "animate-spin" : ""}`} />
|
|
35
|
+
</Button>
|
|
36
|
+
</div>
|
|
37
|
+
<div className="flex flex-wrap gap-2">{actions}</div>
|
|
38
|
+
</section>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* An inline result message.
|
|
44
|
+
*
|
|
45
|
+
* One neutral style, dismissible, for both success and failure: a red banner for "export
|
|
46
|
+
* failed" next to a green one for "backup downloaded" made the pane look like a form with
|
|
47
|
+
* validation errors, and the two providers had picked different colours for the same events.
|
|
48
|
+
*/
|
|
49
|
+
export function AccountsPaneMessage({ message, onDismiss }: {
|
|
50
|
+
message: string | null;
|
|
51
|
+
onDismiss: () => void;
|
|
52
|
+
}) {
|
|
53
|
+
if (!message) return null;
|
|
54
|
+
return (
|
|
55
|
+
<div className="flex items-start gap-2 text-xs p-2 rounded bg-muted" role="status">
|
|
56
|
+
<span className="flex-1">{message}</span>
|
|
57
|
+
<button
|
|
58
|
+
onClick={onDismiss}
|
|
59
|
+
className="shrink-0 text-muted-foreground hover:text-foreground cursor-pointer"
|
|
60
|
+
aria-label="Dismiss message"
|
|
61
|
+
>
|
|
62
|
+
<X className="size-3.5" />
|
|
63
|
+
</button>
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* The row accounts are laid out in: a sideways scroller, one card per account.
|
|
70
|
+
*
|
|
71
|
+
* Horizontal everywhere — the chat panel and both settings panes — because the point of the
|
|
72
|
+
* list is comparing accounts, and stacked vertically you scroll past one to reach the next.
|
|
73
|
+
* Shared so the three callers cannot end up scrolling in different directions.
|
|
74
|
+
*/
|
|
75
|
+
export function AccountCardRow({ children }: { children: React.ReactNode }) {
|
|
76
|
+
return (
|
|
77
|
+
// Negative margin then padding: cards scroll edge to edge while still clearing the
|
|
78
|
+
// pane's own padding at rest. Snap so a swipe lands on a card rather than between two.
|
|
79
|
+
<div className="flex gap-2 overflow-x-auto pb-1 -mx-1 px-1 snap-x snap-mandatory scrollbar-thin">
|
|
80
|
+
{children}
|
|
81
|
+
</div>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* The card one account is drawn in.
|
|
87
|
+
*
|
|
88
|
+
* Shared so a Claude row and a Codex row are the same object on screen even though what goes
|
|
89
|
+
* inside them differs.
|
|
90
|
+
*/
|
|
91
|
+
export function AccountCardShell({ active, flash, dense, children, ...rest }: {
|
|
92
|
+
active?: boolean;
|
|
93
|
+
/** Brief highlight when this account's numbers just changed. */
|
|
94
|
+
flash?: boolean;
|
|
95
|
+
/** Tighter padding, for a card that has to fit a fixed narrow width. */
|
|
96
|
+
dense?: boolean;
|
|
97
|
+
children: React.ReactNode;
|
|
98
|
+
} & React.HTMLAttributes<HTMLDivElement>) {
|
|
99
|
+
return (
|
|
100
|
+
<div
|
|
101
|
+
{...rest}
|
|
102
|
+
className={[
|
|
103
|
+
"rounded-md border transition-colors duration-500",
|
|
104
|
+
dense ? "p-2.5 space-y-1.5" : "p-3 space-y-2",
|
|
105
|
+
flash ? "bg-primary/10 border-primary/40" : "",
|
|
106
|
+
active ? "border-primary/30 bg-primary/5" : "border-border/50",
|
|
107
|
+
rest.className ?? "",
|
|
108
|
+
].filter(Boolean).join(" ")}
|
|
109
|
+
>
|
|
110
|
+
{children}
|
|
111
|
+
</div>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Accounts pane: every provider's sign-ins in one place, one sub-tab each.
|
|
3
|
+
*
|
|
4
|
+
* Claude and Codex accounts have nothing in common underneath — separate endpoints
|
|
5
|
+
* (`/api/accounts/*` vs `/api/codex-accounts/*`), separate login flows (paste-a-code OAuth
|
|
6
|
+
* vs device code), separate rotation models (per-account settings vs a global strategy). So
|
|
7
|
+
* they stay separate panes rather than being forced into one list; what they share is the
|
|
8
|
+
* question the user came here to answer, which is "which accounts does PPM have?".
|
|
9
|
+
*
|
|
10
|
+
* A provider's tab only appears when that provider is configured. Only Claude ships in the
|
|
11
|
+
* default config, so a fresh install would otherwise offer a Codex tab for something the
|
|
12
|
+
* user never set up.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { useEffect, useState } from "react";
|
|
16
|
+
import { Loader2 } from "lucide-react";
|
|
17
|
+
import { ProviderBadge } from "@/components/chat/provider-selector";
|
|
18
|
+
import { getAISettings } from "../../../lib/api-settings";
|
|
19
|
+
import { ClaudeAccountsSection } from "./claude-accounts-section";
|
|
20
|
+
import { CodexAccountsSection } from "./codex-accounts-section";
|
|
21
|
+
|
|
22
|
+
/** Providers with an account manager, in tab order. Others have no credentials to manage. */
|
|
23
|
+
const PROVIDER_TABS = [
|
|
24
|
+
{ id: "claude", label: "Claude" },
|
|
25
|
+
{ id: "codex", label: "Codex" },
|
|
26
|
+
] as const;
|
|
27
|
+
|
|
28
|
+
type ProviderTabId = (typeof PROVIDER_TABS)[number]["id"];
|
|
29
|
+
|
|
30
|
+
export function AccountsSettingsSection() {
|
|
31
|
+
// Claude is always present, so it is a safe first paint while the provider list loads —
|
|
32
|
+
// no spinner for the common case, and no tab bar flash when Codex turns out to be absent.
|
|
33
|
+
const [configured, setConfigured] = useState<Set<string> | null>(null);
|
|
34
|
+
const [active, setActive] = useState<ProviderTabId>("claude");
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
let cancelled = false;
|
|
38
|
+
getAISettings()
|
|
39
|
+
.then((s) => { if (!cancelled) setConfigured(new Set(Object.keys(s.providers ?? {}))); })
|
|
40
|
+
// A failed lookup must not hide Claude accounts — fall back to Claude only.
|
|
41
|
+
.catch(() => { if (!cancelled) setConfigured(new Set(["claude"])); });
|
|
42
|
+
return () => { cancelled = true; };
|
|
43
|
+
}, []);
|
|
44
|
+
|
|
45
|
+
const tabs = PROVIDER_TABS.filter((t) => t.id === "claude" || configured?.has(t.id));
|
|
46
|
+
const showTabBar = tabs.length > 1;
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<div className="space-y-4" data-testid="accounts-pane" data-provider={active}>
|
|
50
|
+
{showTabBar && (
|
|
51
|
+
<div className="flex gap-1 border-b border-border/50">
|
|
52
|
+
{tabs.map((tab) => (
|
|
53
|
+
<button
|
|
54
|
+
key={tab.id}
|
|
55
|
+
onClick={() => setActive(tab.id)}
|
|
56
|
+
data-testid={`accounts-tab-${tab.id}`}
|
|
57
|
+
aria-current={active === tab.id ? "page" : undefined}
|
|
58
|
+
className={`flex items-center gap-1.5 px-3 py-2 text-xs rounded-t transition-colors cursor-pointer ${
|
|
59
|
+
active === tab.id
|
|
60
|
+
? "text-primary border-b-2 border-primary font-medium"
|
|
61
|
+
: "text-text-subtle hover:text-text-secondary"
|
|
62
|
+
}`}
|
|
63
|
+
>
|
|
64
|
+
<ProviderBadge providerId={tab.id} />
|
|
65
|
+
{tab.label}
|
|
66
|
+
</button>
|
|
67
|
+
))}
|
|
68
|
+
</div>
|
|
69
|
+
)}
|
|
70
|
+
|
|
71
|
+
{configured === null && !showTabBar && (
|
|
72
|
+
<div className="flex items-center gap-2 text-xs text-muted-foreground">
|
|
73
|
+
<Loader2 className="size-3 animate-spin" /> Checking configured providers...
|
|
74
|
+
</div>
|
|
75
|
+
)}
|
|
76
|
+
|
|
77
|
+
{active === "claude" ? <ClaudeAccountsSection /> : <CodexAccountsSection />}
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude accounts: added, removed, enabled, exported, imported and rotated here.
|
|
3
|
+
*
|
|
4
|
+
* It used to live behind the chat usage chip, where a 350px-tall strip had to carry an
|
|
5
|
+
* account list, four dialogs and a delete confirmation. The chip keeps what it is good at —
|
|
6
|
+
* showing the current session's usage — and management moved here, so there is a single
|
|
7
|
+
* place that can add or remove an account rather than two that can drift.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { useState } from "react";
|
|
11
|
+
import { Download, KeyRound, Loader2, Plus, Settings, Upload, FlaskConical } from "lucide-react";
|
|
12
|
+
import { Button } from "@/components/ui/button";
|
|
13
|
+
import { AccountCardRow, AccountsPaneHeader, AccountsPaneMessage } from "./accounts-pane-header";
|
|
14
|
+
import { patchAccount, deleteAccount, type OAuthProfileData } from "../../../lib/api-settings";
|
|
15
|
+
import { AccountProfilePanel } from "./account-profile-panel";
|
|
16
|
+
import { AccountDeleteConfirm } from "./account-delete-confirm";
|
|
17
|
+
import { AccountCard } from "./account-card";
|
|
18
|
+
import { AddAccountDialog } from "./account-add-dialog";
|
|
19
|
+
import { ExportAccountsDialog } from "./account-export-dialog";
|
|
20
|
+
import { ImportAccountsDialog } from "./account-import-dialog";
|
|
21
|
+
import { AccountRotationSettings } from "./account-rotation-settings";
|
|
22
|
+
import { AccountTokenTestDialog } from "./account-token-test-dialog";
|
|
23
|
+
import { useAccountsData } from "./use-accounts-data";
|
|
24
|
+
import { formatLastUpdated } from "./account-usage-format";
|
|
25
|
+
|
|
26
|
+
export function ClaudeAccountsSection() {
|
|
27
|
+
const { usages, accounts, activeAccountId, initialLoading, refreshing, flashIds, reload } = useAccountsData();
|
|
28
|
+
|
|
29
|
+
const [message, setMessage] = useState<string | null>(null);
|
|
30
|
+
const [togglingId, setTogglingId] = useState<string | null>(null);
|
|
31
|
+
const [deleteTarget, setDeleteTarget] = useState<{ id: string; display: string } | null>(null);
|
|
32
|
+
const [profileView, setProfileView] = useState<{ profile: OAuthProfileData; accountId: string } | null>(null);
|
|
33
|
+
const [exportPreselect, setExportPreselect] = useState<string | null>(null);
|
|
34
|
+
const [dialog, setDialog] = useState<"add" | "export" | "import" | "rotation" | "token-test" | null>(null);
|
|
35
|
+
|
|
36
|
+
function handleSuccess(msg?: string) {
|
|
37
|
+
void reload();
|
|
38
|
+
if (msg) setMessage(msg);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function handleToggle(id: string, status: string) {
|
|
42
|
+
// Enabling a parked account makes the server prove its token first, which is a network
|
|
43
|
+
// round trip that can come back 400 — and `patchAccount` throws on that. Without the
|
|
44
|
+
// catch, the message the server took care to write reaches nobody and the switch just
|
|
45
|
+
// snaps back. The pending flag is because that trip can take most of a minute.
|
|
46
|
+
setTogglingId(id);
|
|
47
|
+
try {
|
|
48
|
+
await patchAccount(id, { status: status === "disabled" ? "active" : "disabled" });
|
|
49
|
+
} catch (e) {
|
|
50
|
+
setMessage((e as Error).message);
|
|
51
|
+
}
|
|
52
|
+
setTogglingId(null);
|
|
53
|
+
void reload();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function confirmDelete() {
|
|
57
|
+
if (!deleteTarget) return;
|
|
58
|
+
try {
|
|
59
|
+
await deleteAccount(deleteTarget.id);
|
|
60
|
+
setMessage(`Account "${deleteTarget.display}" removed.`);
|
|
61
|
+
void reload();
|
|
62
|
+
} catch (e) {
|
|
63
|
+
setMessage(`Failed to remove: ${(e as Error).message}`);
|
|
64
|
+
}
|
|
65
|
+
setDeleteTarget(null);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const accountMap = new Map(accounts.map((a) => [a.id, a]));
|
|
69
|
+
const lastFetched = usages[0]?.usage.lastFetchedAt;
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<div className="space-y-4">
|
|
73
|
+
<AccountsPaneHeader
|
|
74
|
+
description="Connect multiple Claude accounts. PPM rotates between them automatically to avoid rate limits."
|
|
75
|
+
onRefresh={() => void reload()}
|
|
76
|
+
refreshing={refreshing}
|
|
77
|
+
disabled={initialLoading}
|
|
78
|
+
actions={<>
|
|
79
|
+
<Button size="sm" className="cursor-pointer gap-1.5" onClick={() => setDialog("add")}>
|
|
80
|
+
<Plus className="size-4" /> Add account
|
|
81
|
+
</Button>
|
|
82
|
+
<Button size="sm" variant="outline" className="cursor-pointer gap-1.5" onClick={() => { setExportPreselect(null); setDialog("export"); }}>
|
|
83
|
+
<Download className="size-4" /> Export
|
|
84
|
+
</Button>
|
|
85
|
+
<Button size="sm" variant="outline" className="cursor-pointer gap-1.5" onClick={() => setDialog("import")}>
|
|
86
|
+
<Upload className="size-4" /> Import
|
|
87
|
+
</Button>
|
|
88
|
+
<Button size="sm" variant="outline" className="cursor-pointer gap-1.5" onClick={() => setDialog("rotation")}>
|
|
89
|
+
<Settings className="size-4" /> Rotation
|
|
90
|
+
</Button>
|
|
91
|
+
<Button size="sm" variant="outline" className="cursor-pointer gap-1.5" onClick={() => setDialog("token-test")}>
|
|
92
|
+
<FlaskConical className="size-4" /> Token test
|
|
93
|
+
</Button>
|
|
94
|
+
</>}
|
|
95
|
+
/>
|
|
96
|
+
|
|
97
|
+
<AccountsPaneMessage message={message} onDismiss={() => setMessage(null)} />
|
|
98
|
+
|
|
99
|
+
<section className="space-y-2">
|
|
100
|
+
<div className="flex items-center justify-between">
|
|
101
|
+
<h3 className="text-sm font-medium">Accounts</h3>
|
|
102
|
+
{lastFetched && (
|
|
103
|
+
<span className="text-[10px] text-muted-foreground">
|
|
104
|
+
usage {formatLastUpdated(new Date(lastFetched).getTime())}
|
|
105
|
+
</span>
|
|
106
|
+
)}
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
{initialLoading ? (
|
|
110
|
+
<div className="flex items-center gap-2 py-6 text-xs text-muted-foreground">
|
|
111
|
+
<Loader2 className="size-4 animate-spin" /> Loading accounts...
|
|
112
|
+
</div>
|
|
113
|
+
) : usages.length === 0 ? (
|
|
114
|
+
<div className="flex flex-col items-center gap-2 py-8 text-center">
|
|
115
|
+
<KeyRound className="size-5 text-muted-foreground" />
|
|
116
|
+
<p className="text-sm text-muted-foreground">No accounts connected yet.</p>
|
|
117
|
+
<p className="text-xs text-muted-foreground">Add one to start using PPM.</p>
|
|
118
|
+
</div>
|
|
119
|
+
) : (
|
|
120
|
+
<AccountCardRow>
|
|
121
|
+
{usages.map((entry) => (
|
|
122
|
+
<AccountCard
|
|
123
|
+
layout="strip"
|
|
124
|
+
key={entry.accountId}
|
|
125
|
+
entry={entry}
|
|
126
|
+
isActive={entry.accountId === activeAccountId}
|
|
127
|
+
accountInfo={accountMap.get(entry.accountId)}
|
|
128
|
+
onToggle={handleToggle}
|
|
129
|
+
toggling={togglingId === entry.accountId}
|
|
130
|
+
onDelete={(id, display) => setDeleteTarget({ id, display })}
|
|
131
|
+
onExport={(id) => { setExportPreselect(id); setDialog("export"); }}
|
|
132
|
+
onViewProfile={(profile, accountId) => setProfileView({ profile, accountId })}
|
|
133
|
+
flash={flashIds.has(entry.accountId)}
|
|
134
|
+
/>
|
|
135
|
+
))}
|
|
136
|
+
</AccountCardRow>
|
|
137
|
+
)}
|
|
138
|
+
</section>
|
|
139
|
+
|
|
140
|
+
{profileView && (
|
|
141
|
+
<AccountProfilePanel
|
|
142
|
+
profile={profileView.profile}
|
|
143
|
+
accountId={profileView.accountId}
|
|
144
|
+
onClose={() => setProfileView(null)}
|
|
145
|
+
/>
|
|
146
|
+
)}
|
|
147
|
+
|
|
148
|
+
{deleteTarget && (
|
|
149
|
+
<AccountDeleteConfirm
|
|
150
|
+
display={deleteTarget.display}
|
|
151
|
+
onConfirm={() => void confirmDelete()}
|
|
152
|
+
onCancel={() => setDeleteTarget(null)}
|
|
153
|
+
/>
|
|
154
|
+
)}
|
|
155
|
+
|
|
156
|
+
<AddAccountDialog open={dialog === "add"} onOpenChange={(v) => setDialog(v ? "add" : null)} onSuccess={handleSuccess} />
|
|
157
|
+
<ExportAccountsDialog
|
|
158
|
+
open={dialog === "export"}
|
|
159
|
+
onOpenChange={(v) => { setDialog(v ? "export" : null); if (!v) setExportPreselect(null); }}
|
|
160
|
+
accounts={accounts}
|
|
161
|
+
preselectId={exportPreselect}
|
|
162
|
+
onMessage={setMessage}
|
|
163
|
+
/>
|
|
164
|
+
<ImportAccountsDialog open={dialog === "import"} onOpenChange={(v) => setDialog(v ? "import" : null)} onSuccess={handleSuccess} />
|
|
165
|
+
<AccountRotationSettings open={dialog === "rotation"} onOpenChange={(v) => setDialog(v ? "rotation" : null)} />
|
|
166
|
+
<AccountTokenTestDialog open={dialog === "token-test"} onOpenChange={(v) => setDialog(v ? "token-test" : null)} accounts={accounts} />
|
|
167
|
+
</div>
|
|
168
|
+
);
|
|
169
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex accounts pane.
|
|
3
|
+
*
|
|
4
|
+
* Same shape as the Claude pane — shared header, shared message strip, shared account card,
|
|
5
|
+
* shared usage bar, and every action behind a dialog — because the two sit next to each other
|
|
6
|
+
* as sub-tabs and used to look like two different products.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { useState } from "react";
|
|
10
|
+
import { Download, KeyRound, Loader2, Plus, Settings, Trash2, Upload } from "lucide-react";
|
|
11
|
+
import { Button } from "@/components/ui/button";
|
|
12
|
+
import { AccountCardRow, AccountCardShell, AccountsPaneHeader, AccountsPaneMessage } from "./accounts-pane-header";
|
|
13
|
+
import { AccountUsageBar } from "./account-bucket-row";
|
|
14
|
+
import { CodexAddAccountDialog } from "./codex-add-account-dialog";
|
|
15
|
+
import { CodexBackupDialog } from "./codex-backup-dialog";
|
|
16
|
+
import { CodexRotationDialog } from "./codex-rotation-dialog";
|
|
17
|
+
import { useCodexAccounts } from "./use-codex-accounts";
|
|
18
|
+
|
|
19
|
+
/** Usage arrives as a 0-1 fraction; the shared bar wants whole percent. */
|
|
20
|
+
function toPct(v?: number): number | null { return v != null ? Math.round(v * 100) : null; }
|
|
21
|
+
|
|
22
|
+
/** Codex multi-account management, separate from Claude accounts because codex auth is owned
|
|
23
|
+
* by the app-server per CODEX_HOME. Added by API key or ChatGPT device code. */
|
|
24
|
+
export function CodexAccountsSection() {
|
|
25
|
+
const [dialog, setDialog] = useState<"add" | "export" | "import" | "rotation" | null>(null);
|
|
26
|
+
const c = useCodexAccounts(() => setDialog(null));
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<div className="space-y-4">
|
|
30
|
+
<AccountsPaneHeader
|
|
31
|
+
description={<>Each Codex account keeps its own login (<code>CODEX_HOME</code>). One is picked per chat by the rotation setting.</>}
|
|
32
|
+
onRefresh={() => void c.load()}
|
|
33
|
+
refreshing={c.loading}
|
|
34
|
+
actions={<>
|
|
35
|
+
<Button size="sm" className="cursor-pointer gap-1.5" onClick={() => setDialog("add")}>
|
|
36
|
+
<Plus className="size-4" /> Add account
|
|
37
|
+
</Button>
|
|
38
|
+
<Button size="sm" variant="outline" className="cursor-pointer gap-1.5" onClick={() => setDialog("export")}>
|
|
39
|
+
<Download className="size-4" /> Export
|
|
40
|
+
</Button>
|
|
41
|
+
<Button size="sm" variant="outline" className="cursor-pointer gap-1.5" onClick={() => setDialog("import")}>
|
|
42
|
+
<Upload className="size-4" /> Import
|
|
43
|
+
</Button>
|
|
44
|
+
<Button size="sm" variant="outline" className="cursor-pointer gap-1.5" onClick={() => setDialog("rotation")}>
|
|
45
|
+
<Settings className="size-4" /> Rotation
|
|
46
|
+
</Button>
|
|
47
|
+
</>}
|
|
48
|
+
/>
|
|
49
|
+
|
|
50
|
+
{/* Errors show here too, so a failure that happened behind a dialog is not lost when
|
|
51
|
+
the dialog closes. */}
|
|
52
|
+
<AccountsPaneMessage message={c.msg ?? c.err} onDismiss={() => { c.setMsg(null); c.setErr(null); }} />
|
|
53
|
+
|
|
54
|
+
<section className="space-y-2">
|
|
55
|
+
<div className="flex items-center justify-between">
|
|
56
|
+
<h3 className="text-sm font-medium">Codex Accounts</h3>
|
|
57
|
+
<span className="text-[10px] text-muted-foreground capitalize">{c.strategy.replace("-", " ")}</span>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
{c.loading && c.accounts.length === 0 ? (
|
|
61
|
+
<div className="flex items-center gap-2 py-6 text-xs text-muted-foreground">
|
|
62
|
+
<Loader2 className="size-4 animate-spin" /> Loading accounts...
|
|
63
|
+
</div>
|
|
64
|
+
) : c.accounts.length === 0 ? (
|
|
65
|
+
<div className="flex flex-col items-center gap-2 py-8 text-center">
|
|
66
|
+
<KeyRound className="size-5 text-muted-foreground" />
|
|
67
|
+
<p className="text-sm text-muted-foreground">No Codex accounts yet.</p>
|
|
68
|
+
<p className="text-xs text-muted-foreground">
|
|
69
|
+
Chats use your default <code>~/.codex</code> login until you add one.
|
|
70
|
+
</p>
|
|
71
|
+
</div>
|
|
72
|
+
) : (
|
|
73
|
+
<AccountCardRow>
|
|
74
|
+
{c.accounts.map((a) => {
|
|
75
|
+
const u = c.usages[a.id] ?? {};
|
|
76
|
+
return (
|
|
77
|
+
<AccountCardShell
|
|
78
|
+
key={a.id}
|
|
79
|
+
dense
|
|
80
|
+
// Matches the Claude card that also carries a control beside the name.
|
|
81
|
+
className="min-w-[300px] shrink-0 snap-start"
|
|
82
|
+
data-testid="account-card"
|
|
83
|
+
data-account-id={a.id}
|
|
84
|
+
>
|
|
85
|
+
<div className="flex items-center gap-2">
|
|
86
|
+
<span className="text-sm font-medium truncate flex-1 min-w-0">{a.label}</span>
|
|
87
|
+
<span className="text-[10px] uppercase tracking-wide text-text-subtle border border-border rounded px-1 shrink-0">
|
|
88
|
+
{a.type}
|
|
89
|
+
</span>
|
|
90
|
+
{a.planType && <span className="text-[10px] text-text-subtle shrink-0">{a.planType}</span>}
|
|
91
|
+
<button
|
|
92
|
+
type="button"
|
|
93
|
+
onClick={() => void c.remove(a.id)}
|
|
94
|
+
title="Remove account"
|
|
95
|
+
aria-label="Remove account"
|
|
96
|
+
className="p-2 rounded cursor-pointer text-text-subtle hover:text-error hover:bg-surface-elevated transition-colors shrink-0"
|
|
97
|
+
>
|
|
98
|
+
<Trash2 className="size-4" />
|
|
99
|
+
</button>
|
|
100
|
+
</div>
|
|
101
|
+
<div className="space-y-2">
|
|
102
|
+
<AccountUsageBar label="5-Hour Session" pct={toPct(u.fiveHour)} />
|
|
103
|
+
<AccountUsageBar label="Weekly" pct={toPct(u.sevenDay)} />
|
|
104
|
+
</div>
|
|
105
|
+
</AccountCardShell>
|
|
106
|
+
);
|
|
107
|
+
})}
|
|
108
|
+
</AccountCardRow>
|
|
109
|
+
)}
|
|
110
|
+
</section>
|
|
111
|
+
|
|
112
|
+
<CodexAddAccountDialog
|
|
113
|
+
open={dialog === "add"}
|
|
114
|
+
onOpenChange={(v) => setDialog(v ? "add" : null)}
|
|
115
|
+
label={c.label}
|
|
116
|
+
onLabelChange={c.setLabel}
|
|
117
|
+
apiKey={c.apiKey}
|
|
118
|
+
onApiKeyChange={c.setApiKey}
|
|
119
|
+
adding={c.adding}
|
|
120
|
+
onAddApiKey={() => void c.addApiKey()}
|
|
121
|
+
deviceWaiting={c.deviceWaiting}
|
|
122
|
+
onStartDevice={() => void c.startDevice()}
|
|
123
|
+
device={c.device}
|
|
124
|
+
error={c.err}
|
|
125
|
+
/>
|
|
126
|
+
<CodexBackupDialog
|
|
127
|
+
mode={dialog === "export" ? "export" : dialog === "import" ? "import" : null}
|
|
128
|
+
onOpenChange={(v) => { if (!v) setDialog(null); }}
|
|
129
|
+
password={c.backupPassword}
|
|
130
|
+
onPasswordChange={c.setBackupPassword}
|
|
131
|
+
busy={c.exporting || c.importing}
|
|
132
|
+
onExport={() => void c.doExport()}
|
|
133
|
+
onImport={(f) => void c.doImport(f)}
|
|
134
|
+
error={c.err}
|
|
135
|
+
/>
|
|
136
|
+
<CodexRotationDialog
|
|
137
|
+
open={dialog === "rotation"}
|
|
138
|
+
onOpenChange={(v) => setDialog(v ? "rotation" : null)}
|
|
139
|
+
strategy={c.strategy}
|
|
140
|
+
onChange={(v) => void c.changeStrategy(v)}
|
|
141
|
+
/>
|
|
142
|
+
</div>
|
|
143
|
+
);
|
|
144
|
+
}
|