@hienlh/ppm 0.19.1 → 0.19.3

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.
Files changed (681) hide show
  1. package/CHANGELOG.md +29 -1
  2. package/CLAUDE.md +1 -0
  3. package/assets/skills/ppm/SKILL.md +1 -1
  4. package/assets/skills/ppm/references/cli-reference.md +4 -4
  5. package/assets/skills/ppm/references/http-api.md +3 -2
  6. package/dist/web/assets/accounts-settings-section-LtSVxNNp.js +1 -0
  7. package/dist/web/assets/ai-resource-editor-BqFT1EUB.js +1 -0
  8. package/dist/web/assets/ai-settings-section-CY6spbEx.js +1 -0
  9. package/dist/web/assets/ai-settings-section-Po3K8j-g.js +1 -0
  10. package/dist/web/assets/{angular-html-VzxSzTCy.js → angular-html-BuHNQIbQ.js} +1 -1
  11. package/dist/web/assets/{angular-ts-CmJ4bmzM.js → angular-ts-DHRWoLc-.js} +1 -1
  12. package/dist/web/assets/api-client-BogqsFna.js +1 -0
  13. package/dist/web/assets/{api-client-qRWu6_s5.js → api-client-oY1tC3tM.js} +1 -1
  14. package/dist/web/assets/api-settings-CTMdz_VF.js +1 -0
  15. package/dist/web/assets/{api-themes-DsAp8m7m.js → api-themes-Cknp3Q-P.js} +1 -1
  16. package/dist/web/assets/{apl-do17bnVx.js → apl-C1VSWVei.js} +1 -1
  17. package/dist/web/assets/appearance-settings-section-L9Z-nej_.js +1 -0
  18. package/dist/web/assets/architecture-PBZL5I3N-Ca5IpdzP.js +1 -0
  19. package/dist/web/assets/arrow-left-CyFcsyzD.js +1 -0
  20. package/dist/web/assets/{astro-ClY0e0OA.js → astro-RX-rChsZ.js} +1 -1
  21. package/dist/web/assets/{audio-preview-Fw935Mok.js → audio-preview-CCd1jdje.js} +1 -1
  22. package/dist/web/assets/{blade-BXSdXDGq.js → blade-CZ6bxMbK.js} +1 -1
  23. package/dist/web/assets/{bot-CWZAuloY.js → bot-D6sx11y0.js} +1 -1
  24. package/dist/web/assets/{pencil-CHKLOgLs.js → brain-KDa9jFRq.js} +1 -1
  25. package/dist/web/assets/{bsl-MxsMKDUZ.js → bsl-BhgCXLWk.js} +1 -1
  26. package/dist/web/assets/{button-CrwgGd1y.js → button-BZtEJcUv.js} +1 -1
  27. package/dist/web/assets/{cairo-4CIrzX_p.js → cairo-BrwygPkn.js} +1 -1
  28. package/dist/web/assets/calendar-clock-C1asT-gk.js +1 -0
  29. package/dist/web/assets/chat-tab-BIO-VhWT.js +15 -0
  30. package/dist/web/assets/check-DN75Ucvz.js +1 -0
  31. package/dist/web/assets/{chevron-down-D7CbB5f6.js → chevron-down-Dp0rpHb8.js} +1 -1
  32. package/dist/web/assets/chevron-right-De-Wz5Lz.js +1 -0
  33. package/dist/web/assets/chevron-up-8PbsNiJp.js +1 -0
  34. package/dist/web/assets/circle-BCy0AUCo.js +1 -0
  35. package/dist/web/assets/{circle-question-mark-BBzFZrNh.js → circle-question-mark-CzR7YRc-.js} +1 -1
  36. package/dist/web/assets/clock-C1CQUQPZ.js +1 -0
  37. package/dist/web/assets/{cobol-phPOE89d.js → cobol-C5TGW1Ir.js} +1 -1
  38. package/dist/web/assets/code-DJgYrFzm.js +1 -0
  39. package/dist/web/assets/code-editor-B9lVjElM.js +7 -0
  40. package/dist/web/assets/{coffee-Cjuukfz3.js → coffee-CFgKlmgI.js} +1 -1
  41. package/dist/web/assets/{conflict-editor-Ni0PPsKu.js → conflict-editor-BoRPwL3L.js} +1 -1
  42. package/dist/web/assets/context-menu-C13b1uT3.js +1 -0
  43. package/dist/web/assets/{copy-CbPdNp7E.js → copy-BewHj66E.js} +1 -1
  44. package/dist/web/assets/{cpp-CT7tGBC3.js → cpp-BhG5q3-d.js} +1 -1
  45. package/dist/web/assets/{createLucideIcon-ConhwJnb.js → createLucideIcon-D8pyde1A.js} +1 -1
  46. package/dist/web/assets/{crystal-D8U1Qnqh.js → crystal-DvuI7_tY.js} +1 -1
  47. package/dist/web/assets/{csv-preview-Coc2oed1.js → csv-preview-BpoJqZQi.js} +1 -1
  48. package/dist/web/assets/{data-grid-overlay-editor-B8WQkZDB.js → data-grid-overlay-editor-DP1xkog4.js} +1 -1
  49. package/dist/web/assets/{data-grid-types-KpjaKAci.js → data-grid-types-CdAg-4W1.js} +1 -1
  50. package/dist/web/assets/{database-BqyeGogo.js → database-Y3ESDK4-.js} +1 -1
  51. package/dist/web/assets/database-viewer-I-9cER1L.js +1 -0
  52. package/dist/web/assets/diff-viewer-_xHZsxvr.js +4 -0
  53. package/dist/web/assets/{dist-C2_dKAa_.js → dist-DonmFWG8.js} +1 -1
  54. package/dist/web/assets/{docx-preview-76KDw1eE.js → docx-preview-CWIDVNZn.js} +1 -1
  55. package/dist/web/assets/{download-DAF7SXcf.js → download-DG3_9xXy.js} +1 -1
  56. package/dist/web/assets/{edge-Db4ze6bw.js → edge-BSIEsmlI.js} +1 -1
  57. package/dist/web/assets/{edit-diff-preview-DyC_-4My.js → edit-diff-preview-DeHb85DN.js} +1 -1
  58. package/dist/web/assets/{elixir-RZPZ0472.js → elixir-BF-lrGXM.js} +1 -1
  59. package/dist/web/assets/{elm-DNZcQt_7.js → elm-8mCsJOeN.js} +1 -1
  60. package/dist/web/assets/{erb-DRONsZXs.js → erb-Dw5PUVKa.js} +1 -1
  61. package/dist/web/assets/{erlang-DeG0-QwJ.js → erlang-BvjBIA6O.js} +1 -1
  62. package/dist/web/assets/{esm-CZy-sSoK.js → esm-Bt9OwSnU.js} +1 -1
  63. package/dist/web/assets/explorer-body-DapwsOnp.js +2 -0
  64. package/dist/web/assets/explorer-window-content-DD6Z15pm.js +1 -0
  65. package/dist/web/assets/extension-manager-section-Cu9D9Udt.js +1 -0
  66. package/dist/web/assets/{extension-webview-BW5GuBnH.js → extension-webview-Diz2OzVq.js} +1 -1
  67. package/dist/web/assets/eye-gWwaF4Lg.js +1 -0
  68. package/dist/web/assets/eye-off-GjXVuAOn.js +1 -0
  69. package/dist/web/assets/{file-exclamation-point-Bpg-zDPP.js → file-exclamation-point-JzhTU2TX.js} +1 -1
  70. package/dist/web/assets/files-settings-section-B1e7BgKQ.js +1 -0
  71. package/dist/web/assets/flask-conical-tp8gyZGh.js +1 -0
  72. package/dist/web/assets/{folder-symlink-LD-mr54V.js → folder-symlink-fQRoG_rV.js} +1 -1
  73. package/dist/web/assets/{fortran-fixed-form-CK5VRJNd.js → fortran-fixed-form-CRjUJe9K.js} +1 -1
  74. package/dist/web/assets/{fsharp-TVVgzHWp.js → fsharp-CjUIHIHp.js} +1 -1
  75. package/dist/web/assets/{gdresource-DiUcyewI.js → gdresource-x_NUQFrZ.js} +1 -1
  76. package/dist/web/assets/general-settings-section-DWdHHRDy.js +1 -0
  77. package/dist/web/assets/{git-commit-CfOruFrg.js → git-commit-BOpxSbns.js} +1 -1
  78. package/dist/web/assets/git-log-panel-CMMsvU64.js +1 -0
  79. package/dist/web/assets/{git-rebase-Bxyr24oc.js → git-rebase-BW4xlK7k.js} +1 -1
  80. package/dist/web/assets/gitGraph-HDMCJU4V-BloZfB0Q.js +1 -0
  81. package/dist/web/assets/glide-data-grid-pKQuyYFj.js +141 -0
  82. package/dist/web/assets/{glimmer-js-CT5aJxz_.js → glimmer-js-CT9jrmFj.js} +1 -1
  83. package/dist/web/assets/{glimmer-ts-CRjP9Yey.js → glimmer-ts-CjQT1lxK.js} +1 -1
  84. package/dist/web/assets/{glsl-BX2--oI_.js → glsl-DsFJvtg5.js} +1 -1
  85. package/dist/web/assets/{graphql-BIQsvBxQ.js → graphql-Dhlnmkn_.js} +1 -1
  86. package/dist/web/assets/group-chat-tab-U5u0Loii.js +1 -0
  87. package/dist/web/assets/{hack-CTWk0k1m.js → hack-BKHbsJ7u.js} +1 -1
  88. package/dist/web/assets/{haml-DGh27hXc.js → haml-C1Ih25Y2.js} +1 -1
  89. package/dist/web/assets/{hand-DKSxDa5c.js → hand-x0ejq3fS.js} +1 -1
  90. package/dist/web/assets/{handlebars-BJL1jNrG.js → handlebars-CaFitvmC.js} +1 -1
  91. package/dist/web/assets/{html-DALyDNqU.js → html-BwpKIE6V.js} +1 -1
  92. package/dist/web/assets/{html-derivative-B4XcU57e.js → html-derivative-uuTEquZU.js} +1 -1
  93. package/dist/web/assets/{http-DfMo7nNu.js → http-Cv4Pg5cR.js} +1 -1
  94. package/dist/web/assets/{hurl-CGc_n2Qf.js → hurl-DubkI6TG.js} +1 -1
  95. package/dist/web/assets/{hxml-40zZ1t4x.js → hxml-B9OVZFAB.js} +1 -1
  96. package/dist/web/assets/{image-preview-Cp3XB8UO.js → image-preview-DRbZ0FA2.js} +1 -1
  97. package/dist/web/assets/index--KWVW-Ga.js +52 -0
  98. package/dist/web/assets/index-CvGx7wBQ.css +2 -0
  99. package/dist/web/assets/info-3K5VOQVL-c0W8wp5o.js +1 -0
  100. package/dist/web/assets/{jinja-D5zuIn9u.js → jinja-BHb1jyl6.js} +1 -1
  101. package/dist/web/assets/jira-watcher-section-uA5oyB8s.js +1 -0
  102. package/dist/web/assets/{jison-Dxut9QwA.js → jison-CtpfXDzp.js} +1 -1
  103. package/dist/web/assets/{julia-Cu85tTPo.js → julia-TYjgpazU.js} +1 -1
  104. package/dist/web/assets/{just-CctM4uLa.js → just-D27bzZuW.js} +1 -1
  105. package/dist/web/assets/key-round-CQpDrDxg.js +1 -0
  106. package/dist/web/assets/keybindings-store-Ccf0etMJ.js +1 -0
  107. package/dist/web/assets/{keyboard-AdwmYDSu.js → keyboard-DiOYrs6C.js} +1 -1
  108. package/dist/web/assets/keyboard-shortcuts-section-B7IxJHiI.js +1 -0
  109. package/dist/web/assets/{latex-kpwS_CC7.js → latex-D_MzopTn.js} +1 -1
  110. package/dist/web/assets/{lib-Ca7ai8xa.js → lib-xrSJ4EZX.js} +1 -1
  111. package/dist/web/assets/{liquid-B8Rr0xue.js → liquid-BG_FG0Pe.js} +1 -1
  112. package/dist/web/assets/loader-circle-DPMrnLQ2.js +1 -0
  113. package/dist/web/assets/{lua-C_3gixwM.js → lua-Bl29oC2L.js} +1 -1
  114. package/dist/web/assets/markdown-renderer-CY1epGRc.js +1 -0
  115. package/dist/web/assets/markdown-renderer-DDcIGQEd.js +3 -0
  116. package/dist/web/assets/{marko-B28rvNL-.js → marko-D-HI7dbm.js} +1 -1
  117. package/dist/web/assets/{mdc-DQ6-STFO.js → mdc-y444JQTQ.js} +1 -1
  118. package/dist/web/assets/{media-url-BjvbfTTl.js → media-url-VzecHk9g.js} +1 -1
  119. package/dist/web/assets/{mobile-bottom-sheet-DXn5g2LB.js → mobile-bottom-sheet-BD5b7KJF.js} +1 -1
  120. package/dist/web/assets/mobile-explorer-sheet-CqkwbCRl.js +1 -0
  121. package/dist/web/assets/{monitor-CXI_XpWJ.js → monitor-C835ucWH.js} +1 -1
  122. package/dist/web/assets/{nextflow-B1xq2O8L.js → nextflow-C5sKcldC.js} +1 -1
  123. package/dist/web/assets/{nginx-xsLxRGxO.js → nginx-bXnEC3wm.js} +1 -1
  124. package/dist/web/assets/{nim-wqcek3WO.js → nim-pevhf4WD.js} +1 -1
  125. package/dist/web/assets/notification-store-BNSYk3E7.js +1 -0
  126. package/dist/web/assets/notifications-settings-section-v_628h4B.js +1 -0
  127. package/dist/web/assets/{number-overlay-editor-1c4sgZJt.js → number-overlay-editor-B7WINIW4.js} +1 -1
  128. package/dist/web/assets/{overview-panel-Cr2xsHc6.js → overview-panel-mhN0qw6Q.js} +1 -1
  129. package/dist/web/assets/packet-RMMSAZCW-D_BoHr_M.js +1 -0
  130. package/dist/web/assets/panel-store-BRrnDO_i.js +1 -0
  131. package/dist/web/assets/pdf-preview-CI3q1MnH.js +1 -0
  132. package/dist/web/assets/{perl-BH54gN9q.js → perl-B1KUtMnW.js} +1 -1
  133. package/dist/web/assets/{php-Br5ZrEZm.js → php-BGmVZBAJ.js} +1 -1
  134. package/dist/web/assets/pie-UPGHQEXC-BaLfuEf8.js +1 -0
  135. package/dist/web/assets/portal-container-context-a399Hd9F.js +1 -0
  136. package/dist/web/assets/{postgres-viewer-BJPr_FYL.js → postgres-viewer-jaR0FYJ_.js} +3 -3
  137. package/dist/web/assets/power-CriQkeK5.js +1 -0
  138. package/dist/web/assets/ppmbot-settings-section-BET4VmKb.js +1 -0
  139. package/dist/web/assets/{process-table-BqTcAJ-h.js → process-table-D1r-Gupi.js} +1 -1
  140. package/dist/web/assets/{project-store-KHjFTCxf.js → project-store-BuY7rxn_.js} +1 -1
  141. package/dist/web/assets/proxy-settings-section-0DvNh1B-.js +3 -0
  142. package/dist/web/assets/{pug-8VCubZ3H.js → pug-BfN8CpEH.js} +1 -1
  143. package/dist/web/assets/{qml-D8AF1RZ6.js → qml-D6fRdLZt.js} +1 -1
  144. package/dist/web/assets/query-audit-section-BA5rT6Sh.js +1 -0
  145. package/dist/web/assets/radar-KQ55EAFF-DBIZEaIU.js +1 -0
  146. package/dist/web/assets/{razor-6v042IJW.js → razor-fg-9AScR.js} +1 -1
  147. package/dist/web/assets/{react-DHs6efqI.js → react-X4jUntEf.js} +1 -1
  148. package/dist/web/assets/remote-desktop-mobile-sheet-CPa3XElX.js +2 -0
  149. package/dist/web/assets/remote-desktop-mobile-view-De_a4skd.js +1 -0
  150. package/dist/web/assets/remote-desktop-readiness-gate-CPrDIv2d.js +1 -0
  151. package/dist/web/assets/remote-desktop-window-content-CG1StfCE.js +1 -0
  152. package/dist/web/assets/{rst-mTGFo4AQ.js → rst-BZgx076b.js} +1 -1
  153. package/dist/web/assets/{ruby-BBaOP636.js → ruby-iUkWDcLo.js} +1 -1
  154. package/dist/web/assets/{run-in-terminal-Zl5iIx-B.js → run-in-terminal-bKRwuO2z.js} +1 -1
  155. package/dist/web/assets/{sas-D5QEkIH2.js → sas-CLXO0lZ6.js} +1 -1
  156. package/dist/web/assets/{save-Dxq-Zepc.js → save-CipGrG1k.js} +1 -1
  157. package/dist/web/assets/schedules-settings-section-B2f7V7Xr.js +1 -0
  158. package/dist/web/assets/{scss-C5Mu6y2u.js → scss-DQqteGaG.js} +1 -1
  159. package/dist/web/assets/send-Bb0jtcR6.js +1 -0
  160. package/dist/web/assets/{send-to-chat-Cv7ikFUC.js → send-to-chat-S8LCNX7V.js} +1 -1
  161. package/dist/web/assets/separator-DqGg3f9c.js +1 -0
  162. package/dist/web/assets/settings-body-omv04jbb.js +2 -0
  163. package/dist/web/assets/settings-store-Brjd0QnO.js +2 -0
  164. package/dist/web/assets/settings-tab-NdnBTqyj.js +1 -0
  165. package/dist/web/assets/settings-window-content-Chou1JGa.js +1 -0
  166. package/dist/web/assets/{shaderlab-mLnpMpN3.js → shaderlab-CeMTqagR.js} +1 -1
  167. package/dist/web/assets/{shellsession-qdcDfUuk.js → shellsession-BAkquZAV.js} +1 -1
  168. package/dist/web/assets/{shield-check-CH7F1P7m.js → shield-check-BsG3LV8i.js} +1 -1
  169. package/dist/web/assets/{shield-off-C9HiF0-6.js → shield-off-7jKWpJBD.js} +1 -1
  170. package/dist/web/assets/{soy-CGGf1G43.js → soy-Bxm9Ez55.js} +1 -1
  171. package/dist/web/assets/{sparkles-BnuLH6Ck.js → sparkles-Dh-_J2Rd.js} +1 -1
  172. package/dist/web/assets/{sparql-tQHB50VD.js → sparql-C1phwdLc.js} +1 -1
  173. package/dist/web/assets/sql-query-editor-Dk9y3jqy.js +1 -0
  174. package/dist/web/assets/sqlite-viewer-4xFhkLYy.js +1 -0
  175. package/dist/web/assets/{stata-DVQNLMcR.js → stata-DGNMQdEb.js} +1 -1
  176. package/dist/web/assets/{surrealql-lu576tBi.js → surrealql-ByVrV320.js} +1 -1
  177. package/dist/web/assets/{svelte-CSCrOBjT.js → svelte-CNWjIMhl.js} +1 -1
  178. package/dist/web/assets/system-monitor-body-B_1iMVOI.js +2 -0
  179. package/dist/web/assets/system-monitor-tab-B2j9mWBW.js +1 -0
  180. package/dist/web/assets/system-monitor-window-content-BOjq53uU.js +1 -0
  181. package/dist/web/assets/{tab-host-window-content-C5kYP0-z.js → tab-host-window-content-CW_dlYlW.js} +1 -1
  182. package/dist/web/assets/{tab-store-DS3YlUwR.js → tab-store-PZsP5ZwG.js} +1 -1
  183. package/dist/web/assets/{table-BXDyImqk.js → table-CRJsmfeg.js} +1 -1
  184. package/dist/web/assets/team-member-sheet-CkR4qO7V.js +2 -0
  185. package/dist/web/assets/team-member-window-content-CmF5ia3n.js +1 -0
  186. package/dist/web/assets/{templ-CPt8AS4Q.js → templ-Cf0gQL4I.js} +1 -1
  187. package/dist/web/assets/{terminal-tab-8bKQ3CWR.js → terminal-tab-HXZkicIc.js} +3 -3
  188. package/dist/web/assets/{tex-BrTByT7i.js → tex-BTIqPw_R.js} +1 -1
  189. package/dist/web/assets/{text-wrap-CzMjB6GD.js → text-wrap-oiekm5SB.js} +1 -1
  190. package/dist/web/assets/tool-cards-jA1BTLZj.js +9 -0
  191. package/dist/web/assets/treemap-KZPCXAKY-DFiG9UYW.js +1 -0
  192. package/dist/web/assets/{ts-tags-DSqEZ7A8.js → ts-tags-B8dN_Yf6.js} +1 -1
  193. package/dist/web/assets/{twig-DW9qc1bf.js → twig-BgemonbJ.js} +1 -1
  194. package/dist/web/assets/use-accounts-data-BI_5qNvr.js +1 -0
  195. package/dist/web/assets/{use-blob-url-DKbV7PM_.js → use-blob-url-Gfi69PtP.js} +1 -1
  196. package/dist/web/assets/{use-coarse-long-press-DGyC48NF.js → use-coarse-long-press-BmI_GVW5.js} +1 -1
  197. package/dist/web/assets/{use-is-mobile-ClE-UcPF.js → use-is-mobile-CYW3FJFE.js} +1 -1
  198. package/dist/web/assets/use-monaco-theme-BcQqKVbE.js +1 -0
  199. package/dist/web/assets/use-open-team-member-CruUTqzD.js +1 -0
  200. package/dist/web/assets/use-remote-desktop-display-choice-DY5BWRTo.js +1 -0
  201. package/dist/web/assets/{use-visual-viewport-CCX21c9w.js → use-visual-viewport-rnvuooQZ.js} +1 -1
  202. package/dist/web/assets/{use-websocket-CWpG7PWN.js → use-websocket-DetaY8OI.js} +1 -1
  203. package/dist/web/assets/vendor-markdown-Ls5AcU91.js +291 -0
  204. package/dist/web/assets/{vendor-mermaid-0Nt-ew76.js → vendor-mermaid-BKWscKt0.js} +1 -1
  205. package/dist/web/assets/{vendor-ui-CXJtBULr.js → vendor-ui-CaeeFO0B.js} +1 -1
  206. package/dist/web/assets/video-preview-C0uH2228.js +1 -0
  207. package/dist/web/assets/{vue-CJNDL1g-.js → vue-D3D4lbXz.js} +1 -1
  208. package/dist/web/assets/{vue-html-CG5BKtq1.js → vue-html-DYEQFAiK.js} +1 -1
  209. package/dist/web/assets/{vue-vine-Dd5HZ7T5.js → vue-vine-D0OPNscN.js} +1 -1
  210. package/dist/web/assets/{window-pip-registry-9H6Y-HqR.js → window-pip-registry-BEN0QNSW.js} +1 -1
  211. package/dist/web/assets/window-store-DGKf9rPG.js +1 -0
  212. package/dist/web/assets/ws-auth-Dawx17PM.js +1 -0
  213. package/dist/web/assets/x-YDu4m2Iq.js +1 -0
  214. package/dist/web/assets/{xml-DTOm5niz.js → xml-DHaWgpya.js} +1 -1
  215. package/dist/web/assets/{xsl-BD_kBC04.js → xsl-DVRN8JsA.js} +1 -1
  216. package/dist/web/index.html +42 -65
  217. package/dist/web/monacoeditorwork/css.worker.bundle.js +122 -122
  218. package/dist/web/monacoeditorwork/editor.worker.bundle.js +78 -78
  219. package/dist/web/monacoeditorwork/html.worker.bundle.js +110 -110
  220. package/dist/web/monacoeditorwork/json.worker.bundle.js +108 -108
  221. package/dist/web/monacoeditorwork/ts.worker.bundle.js +81 -81
  222. package/dist/web/sw.js +1 -1
  223. package/docs/codebase-summary.md +9 -5
  224. package/docs/lessons-learned.md +11 -0
  225. package/package.json +2 -2
  226. package/packages/vscode-compat/node_modules/.bin/tsc.bunx +0 -0
  227. package/packages/vscode-compat/node_modules/.bin/tsc.exe +0 -0
  228. package/packages/vscode-compat/node_modules/.bin/tsserver.bunx +0 -0
  229. package/packages/vscode-compat/node_modules/.bin/tsserver.exe +0 -0
  230. package/scripts/check-ppm-dir-usage.sh +0 -0
  231. package/scripts/install.sh +0 -0
  232. package/scripts/release.sh +0 -0
  233. package/src/providers/claude-agent-sdk.ts +51 -43
  234. package/src/providers/codex-app-server/codex-event-mapper.ts +70 -5
  235. package/src/providers/codex-app-server/codex-history.ts +24 -0
  236. package/src/providers/codex-app-server/codex-jsonrpc-client.ts +39 -2
  237. package/src/providers/codex-app-server/codex-protocol.ts +24 -0
  238. package/src/providers/codex-app-server/codex-provider.ts +166 -18
  239. package/src/providers/codex-app-server/codex-rollout-items.ts +149 -0
  240. package/src/providers/codex-app-server/codex-skill-parser.ts +63 -0
  241. package/src/providers/codex-app-server/codex-usage-fetch.ts +22 -20
  242. package/src/providers/codex-app-server/codex-usage-source.ts +37 -0
  243. package/src/server/routes/chat.ts +95 -12
  244. package/src/server/routes/codex-accounts.ts +9 -5
  245. package/src/server/ws/chat.ts +55 -5
  246. package/src/services/claude-usage-source.ts +40 -0
  247. package/src/services/claude-usage.service.ts +77 -146
  248. package/src/services/codex-account-login.ts +123 -55
  249. package/src/services/codex-account.service.ts +27 -4
  250. package/src/services/db.service.ts +161 -20
  251. package/src/services/edge-forwarder.ts +191 -191
  252. package/src/services/fs-credential-path-guard.ts +35 -8
  253. package/src/services/fs-path-guard.service.ts +1 -0
  254. package/src/services/named-tunnel/named-tunnel-retry.ts +26 -0
  255. package/src/services/named-tunnel/network-ready.ts +63 -0
  256. package/src/services/postgres.service.ts +144 -72
  257. package/src/services/provider-usage/index.ts +63 -0
  258. package/src/services/provider-usage/register-usage-sources.ts +18 -0
  259. package/src/services/provider-usage/usage-memory-cache.ts +71 -0
  260. package/src/services/provider-usage/usage-registry.ts +157 -0
  261. package/src/services/provider-usage/usage-scheduler.ts +117 -0
  262. package/src/services/provider-usage/usage-snapshot-store.ts +126 -0
  263. package/src/services/provider-usage/usage-source.ts +82 -0
  264. package/src/services/slash-discovery/builtin-commands.ts +13 -0
  265. package/src/services/slash-discovery/codex-skill-items.ts +57 -0
  266. package/src/services/slash-discovery/provider-skill-sigil.ts +19 -0
  267. package/src/services/slash-discovery/types.ts +19 -0
  268. package/src/services/supervisor.ts +98 -0
  269. package/src/services/transcript-images.ts +14 -7
  270. package/src/services/upgrade.service.ts +7 -2
  271. package/src/services/windows-process-tree.ts +351 -351
  272. package/src/shared/image-limits.ts +37 -0
  273. package/src/types/api.ts +3 -1
  274. package/src/types/chat.ts +13 -1
  275. package/src/types/extension-messages.ts +2 -0
  276. package/src/web/app.tsx +1 -7
  277. package/src/web/components/chat/attachment-chips.tsx +10 -0
  278. package/src/web/components/chat/chat-history-bar.tsx +10 -1
  279. package/src/web/components/chat/chat-tab.tsx +54 -14
  280. package/src/web/components/chat/message-input.tsx +113 -15
  281. package/src/web/components/chat/message-list.tsx +14 -0
  282. package/src/web/components/chat/session-debug-dialog.tsx +2 -1
  283. package/src/web/components/chat/slash-command-picker.tsx +22 -3
  284. package/src/web/components/chat/tool-cards.tsx +47 -4
  285. package/src/web/components/chat/usage-badge.tsx +43 -447
  286. package/src/web/components/chat/user-message-parse.ts +33 -11
  287. package/src/web/components/database/database-viewer.tsx +34 -11
  288. package/src/web/components/database/extract-query-table.ts +24 -0
  289. package/src/web/components/database/glide-data-grid.tsx +11 -4
  290. package/src/web/components/database/glide-grid-types.ts +4 -0
  291. package/src/web/components/database/split-sql-statements.ts +116 -0
  292. package/src/web/components/database/sql-completion-provider.ts +1 -26
  293. package/src/web/components/database/sql-query-editor.tsx +2 -1
  294. package/src/web/components/database/use-database.ts +16 -1
  295. package/src/web/components/database/use-glide-cell-content.ts +11 -6
  296. package/src/web/components/editor/code-editor.tsx +30 -56
  297. package/src/web/components/floating-window/window-content-registry.ts +2 -0
  298. package/src/web/components/floating-window/window-store-types.ts +1 -1
  299. package/src/web/components/layout/command-palette.tsx +2 -2
  300. package/src/web/components/layout/mobile-drawer.tsx +5 -4
  301. package/src/web/components/layout/mobile-nav.tsx +5 -1
  302. package/src/web/components/layout/nav-section-rail.tsx +2 -1
  303. package/src/web/components/layout/project-bottom-sheet.tsx +3 -9
  304. package/src/web/components/layout/sidebar.tsx +0 -2
  305. package/src/web/components/layout/tab-pop-out-menu-item.tsx +3 -2
  306. package/src/web/components/settings/accounts/account-add-dialog.tsx +155 -0
  307. package/src/web/components/settings/accounts/account-backup-format.ts +19 -0
  308. package/src/web/components/settings/accounts/account-bucket-row.tsx +55 -0
  309. package/src/web/components/settings/accounts/account-card.tsx +127 -0
  310. package/src/web/components/settings/accounts/account-delete-confirm.tsx +62 -0
  311. package/src/web/components/settings/accounts/account-export-dialog.tsx +154 -0
  312. package/src/web/components/settings/accounts/account-export-simulation.tsx +196 -0
  313. package/src/web/components/settings/accounts/account-import-dialog.tsx +72 -0
  314. package/src/web/components/settings/accounts/account-profile-panel.tsx +52 -0
  315. package/src/web/components/{chat → settings/accounts}/account-rotation-settings.tsx +1 -1
  316. package/src/web/components/settings/accounts/account-token-test-dialog.tsx +147 -0
  317. package/src/web/components/settings/accounts/account-usage-format.ts +92 -0
  318. package/src/web/components/{chat/usage-pattern-chart.tsx → settings/accounts/account-usage-pattern-chart.tsx} +1 -1
  319. package/src/web/components/settings/accounts/accounts-pane-header.tsx +93 -0
  320. package/src/web/components/settings/accounts/accounts-settings-section.tsx +80 -0
  321. package/src/web/components/settings/accounts/claude-accounts-section.tsx +168 -0
  322. package/src/web/components/settings/accounts/codex-accounts-section.tsx +137 -0
  323. package/src/web/components/settings/accounts/codex-add-account-dialog.tsx +130 -0
  324. package/src/web/components/settings/accounts/codex-backup-dialog.tsx +101 -0
  325. package/src/web/components/settings/accounts/codex-rotation-dialog.tsx +65 -0
  326. package/src/web/components/settings/accounts/use-accounts-data.ts +81 -0
  327. package/src/web/components/settings/accounts/use-codex-accounts.ts +151 -0
  328. package/src/web/components/settings/ai-settings-section.tsx +14 -4
  329. package/src/web/components/settings/appearance-settings-section.tsx +125 -0
  330. package/src/web/components/settings/general-settings-section.tsx +87 -0
  331. package/src/web/components/settings/jira-watcher-section.tsx +34 -0
  332. package/src/web/components/settings/notifications-settings-section.tsx +37 -0
  333. package/src/web/components/settings/open-settings.ts +42 -0
  334. package/src/web/components/settings/resolve-open-settings-action.ts +45 -0
  335. package/src/web/components/settings/settings-body.tsx +110 -0
  336. package/src/web/components/settings/settings-categories.ts +102 -0
  337. package/src/web/components/settings/settings-category-list.tsx +56 -0
  338. package/src/web/components/settings/settings-category-rail.tsx +62 -0
  339. package/src/web/components/settings/settings-section-content.tsx +43 -0
  340. package/src/web/components/settings/settings-tab.tsx +15 -329
  341. package/src/web/components/settings/settings-window-content.tsx +24 -0
  342. package/src/web/hooks/use-chat.ts +44 -4
  343. package/src/web/hooks/use-global-keybindings.ts +3 -4
  344. package/src/web/hooks/use-url-sync.ts +30 -0
  345. package/src/web/hooks/use-usage.ts +23 -11
  346. package/src/web/lib/attachment-marker-split.ts +44 -0
  347. package/src/web/lib/image-animation.ts +51 -0
  348. package/src/web/lib/image-resize-limits.ts +47 -0
  349. package/src/web/lib/image-resize.ts +133 -0
  350. package/src/web/lib/sidebar-tabs/tab-registry.ts +9 -9
  351. package/src/web/lib/slash-items-cache.ts +27 -7
  352. package/src/web/stores/panel-utils.ts +11 -0
  353. package/src/web/stores/settings-store.ts +4 -2
  354. package/src/web/stores/window-panel-actions.ts +1 -3
  355. package/src/web/stores/window-panel-persistence.ts +9 -4
  356. package/dist/web/assets/adaptive-context-menu-JfuUSqbe.js +0 -1
  357. package/dist/web/assets/ai-resource-editor-MfdJKVM5.js +0 -1
  358. package/dist/web/assets/ai-settings-section-CJE7jFsj.js +0 -1
  359. package/dist/web/assets/api-client-DLWwuUkJ.js +0 -1
  360. package/dist/web/assets/api-settings-BFuaZrNC.js +0 -1
  361. package/dist/web/assets/api-settings-CPg4yGvS.js +0 -1
  362. package/dist/web/assets/architecture-PBZL5I3N-6konx-J1.js +0 -1
  363. package/dist/web/assets/chat-tab-bSOVoC5N.js +0 -15
  364. package/dist/web/assets/chevron-right-D7Pn5agw.js +0 -1
  365. package/dist/web/assets/circle-4g2coECH.js +0 -1
  366. package/dist/web/assets/clipboard-B-gwJ8DF.js +0 -1
  367. package/dist/web/assets/clock-DbiNiwCG.js +0 -1
  368. package/dist/web/assets/code-DdKXqtf_.js +0 -1
  369. package/dist/web/assets/code-editor-COTa37oe.js +0 -10
  370. package/dist/web/assets/context-menu-BEAa1qhd.js +0 -1
  371. package/dist/web/assets/database-viewer-Dta7lSSk.js +0 -1
  372. package/dist/web/assets/dialog-DrL3WEXd.js +0 -1
  373. package/dist/web/assets/diff-viewer-CT0lmDc9.js +0 -4
  374. package/dist/web/assets/explorer-body-BGjR2Gbt.js +0 -2
  375. package/dist/web/assets/explorer-window-content-DAhHh5EQ.js +0 -1
  376. package/dist/web/assets/extension-store-ClQpnxQr.js +0 -1
  377. package/dist/web/assets/external-link-ojDHcQxQ.js +0 -1
  378. package/dist/web/assets/eye-off-Rq1n2-oe.js +0 -1
  379. package/dist/web/assets/file-store-28e44KP3.js +0 -1
  380. package/dist/web/assets/git-log-panel-D1-YXZ0O.js +0 -1
  381. package/dist/web/assets/gitGraph-HDMCJU4V-ikUwsekd.js +0 -1
  382. package/dist/web/assets/glide-data-grid-BSsqxe2v.js +0 -138
  383. package/dist/web/assets/group-chat-tab-CMtzoaFc.js +0 -1
  384. package/dist/web/assets/index-BkHlMfPD.js +0 -52
  385. package/dist/web/assets/index-SFFv2-VM.css +0 -2
  386. package/dist/web/assets/info-3K5VOQVL-BAgmboBp.js +0 -1
  387. package/dist/web/assets/input-DTmI80l2.js +0 -1
  388. package/dist/web/assets/keybindings-store-Bi0q9x4Q.js +0 -1
  389. package/dist/web/assets/keybindings-store-_KfovSz7.js +0 -1
  390. package/dist/web/assets/loader-circle-DD65e9-7.js +0 -1
  391. package/dist/web/assets/lock-BEtt_eTO.js +0 -1
  392. package/dist/web/assets/markdown-renderer-DroTmKbJ.js +0 -1
  393. package/dist/web/assets/markdown-renderer-ntDi3Tx-.js +0 -3
  394. package/dist/web/assets/mobile-explorer-sheet-BP6iU4ma.js +0 -1
  395. package/dist/web/assets/monitor-smartphone-C1wUPOSg.js +0 -1
  396. package/dist/web/assets/notification-store-II5Kw9rD.js +0 -1
  397. package/dist/web/assets/packet-RMMSAZCW-RVlbbDRS.js +0 -1
  398. package/dist/web/assets/panel-store-Dv28vz3L.js +0 -1
  399. package/dist/web/assets/pdf-preview-DKeIIJTv.js +0 -1
  400. package/dist/web/assets/pie-UPGHQEXC-ZWb4izpk.js +0 -1
  401. package/dist/web/assets/play-CPbVzGgq.js +0 -1
  402. package/dist/web/assets/plus-BxqD2FIZ.js +0 -1
  403. package/dist/web/assets/portal-container-context-BmG4--I-.js +0 -1
  404. package/dist/web/assets/radar-KQ55EAFF-bsP5x1Bx.js +0 -1
  405. package/dist/web/assets/refresh-cw-BALzw7L3.js +0 -1
  406. package/dist/web/assets/remote-desktop-mobile-sheet-CYZUjYTO.js +0 -2
  407. package/dist/web/assets/remote-desktop-mobile-view-DaoB49k4.js +0 -1
  408. package/dist/web/assets/remote-desktop-readiness-gate--LJgsuoj.js +0 -1
  409. package/dist/web/assets/remote-desktop-window-content-Cihx4PNZ.js +0 -1
  410. package/dist/web/assets/rotate-ccw-CuvNBM80.js +0 -1
  411. package/dist/web/assets/scroll-area-B5yhPpZo.js +0 -1
  412. package/dist/web/assets/send-Cits8q0l.js +0 -1
  413. package/dist/web/assets/settings-store-Dp4i8zeA.js +0 -2
  414. package/dist/web/assets/settings-tab-BtMvvKAF.js +0 -3
  415. package/dist/web/assets/settings-tab-UPPycGRF.js +0 -1
  416. package/dist/web/assets/shallow-BBJ_qoEF.js +0 -1
  417. package/dist/web/assets/sql-query-editor-DZqnYIB7.js +0 -1
  418. package/dist/web/assets/sqlite-viewer-DXutos0e.js +0 -1
  419. package/dist/web/assets/switch-BPol3BFC.js +0 -1
  420. package/dist/web/assets/system-monitor-body-Ct8_GX_f.js +0 -2
  421. package/dist/web/assets/system-monitor-tab-DC_EerdI.js +0 -1
  422. package/dist/web/assets/system-monitor-window-content-D25DMNpw.js +0 -1
  423. package/dist/web/assets/team-member-sheet-DvmioeQL.js +0 -2
  424. package/dist/web/assets/team-member-window-content-BJ_XFw8-.js +0 -1
  425. package/dist/web/assets/tool-cards-pIu_cmEL.js +0 -8
  426. package/dist/web/assets/trash-2-BjShZ7if.js +0 -1
  427. package/dist/web/assets/treemap-KZPCXAKY-Dh1oxNdQ.js +0 -1
  428. package/dist/web/assets/triangle-alert-qQ6Plydp.js +0 -1
  429. package/dist/web/assets/upload-7Zs6oaK5.js +0 -1
  430. package/dist/web/assets/use-monaco-theme-DaxAk3gB.js +0 -1
  431. package/dist/web/assets/use-open-team-member-CopUuDHG.js +0 -1
  432. package/dist/web/assets/use-remote-desktop-display-choice-BUA_1eaH.js +0 -1
  433. package/dist/web/assets/vendor-markdown-CKTlmXbz.js +0 -291
  434. package/dist/web/assets/video-preview-C2_fdunE.js +0 -1
  435. package/dist/web/assets/window-store-CNzkeUyO.js +0 -1
  436. package/dist/web/assets/x-Dzu9ecti.js +0 -1
  437. package/src/web/components/chat/account-dialogs.tsx +0 -384
  438. package/src/web/components/settings/accounts-settings-section.tsx +0 -1090
  439. package/src/web/components/settings/codex-accounts-section.tsx +0 -198
  440. package/src/web/components/settings/telegram-settings-section.tsx +0 -113
  441. /package/dist/web/assets/{abap-Cjj2olMr.js → abap-h3QyH8MW.js} +0 -0
  442. /package/dist/web/assets/{actionscript-3-DCkW7kWC.js → actionscript-3-mkxkq-FN.js} +0 -0
  443. /package/dist/web/assets/{ada-AEWvZXuX.js → ada-DhISK_EH.js} +0 -0
  444. /package/dist/web/assets/{andromeeda-D7cSpME6.js → andromeeda-DnlWWesX.js} +0 -0
  445. /package/dist/web/assets/{apache-DjvX0RSe.js → apache-P5AK3sfA.js} +0 -0
  446. /package/dist/web/assets/{apex-70xIZ0pH.js → apex-CJUPehg2.js} +0 -0
  447. /package/dist/web/assets/{applescript-C3qKqxmA.js → applescript-Dx6GGbay.js} +0 -0
  448. /package/dist/web/assets/{apply-theme-DJ794zSQ.js → apply-theme-DxTXOAcV.js} +0 -0
  449. /package/dist/web/assets/{ara-iGt2zixk.js → ara-95Kx3qKb.js} +0 -0
  450. /package/dist/web/assets/{asciidoc-Bs2t-06W.js → asciidoc-Dp0RbIej.js} +0 -0
  451. /package/dist/web/assets/{asm-B-T0eqhE.js → asm-C82coUjU.js} +0 -0
  452. /package/dist/web/assets/{aurora-x-D3h_9M3E.js → aurora-x-BFc3zeop.js} +0 -0
  453. /package/dist/web/assets/{awk-CZwfwpPi.js → awk-ChjC0icr.js} +0 -0
  454. /package/dist/web/assets/{ayu-dark-DulkglEA.js → ayu-dark-DYfUf_PB.js} +0 -0
  455. /package/dist/web/assets/{ayu-light-DyNQiRpn.js → ayu-light-Bh6O0plO.js} +0 -0
  456. /package/dist/web/assets/{ayu-mirage-CcEt4xkI.js → ayu-mirage-DlNhHTGq.js} +0 -0
  457. /package/dist/web/assets/{ballerina-OhQPjyHS.js → ballerina-Ceoll3Sk.js} +0 -0
  458. /package/dist/web/assets/{bat-DqKcJTUn.js → bat-C7QS6Lfq.js} +0 -0
  459. /package/dist/web/assets/{beancount-BdXlMi8T.js → beancount-5NMEAZgs.js} +0 -0
  460. /package/dist/web/assets/{berry-DvgY6BkY.js → berry-BBL83Vbk.js} +0 -0
  461. /package/dist/web/assets/{bibtex-BSRJq0yM.js → bibtex-L7rPkPSG.js} +0 -0
  462. /package/dist/web/assets/{bicep-DpAdumKt.js → bicep-BOwHIINh.js} +0 -0
  463. /package/dist/web/assets/{bird2-rlbtp8di.js → bird2-CWLsMom2.js} +0 -0
  464. /package/dist/web/assets/{c-AjOLBXIs.js → c-CD4Ku5Cr.js} +0 -0
  465. /package/dist/web/assets/{c3-DqIXz4Ia.js → c3-CpMl-w3G.js} +0 -0
  466. /package/dist/web/assets/{cadence-DAJtUpOE.js → cadence-CvgF6ehN.js} +0 -0
  467. /package/dist/web/assets/{can-open-in-ppm-CdCK2c2a.js → can-open-in-ppm-j3jh7fUo.js} +0 -0
  468. /package/dist/web/assets/{catppuccin-frappe-DUPoeZfb.js → catppuccin-frappe-eo_dTm3F.js} +0 -0
  469. /package/dist/web/assets/{catppuccin-latte-CK3U68Ql.js → catppuccin-latte-DoiMCU_7.js} +0 -0
  470. /package/dist/web/assets/{catppuccin-macchiato-BzteLnIX.js → catppuccin-macchiato-UQxsK2Vh.js} +0 -0
  471. /package/dist/web/assets/{catppuccin-mocha-ozLd0aBR.js → catppuccin-mocha-J0i-8RNy.js} +0 -0
  472. /package/dist/web/assets/{clarity-6KqEoBfz.js → clarity-DXYM8-SA.js} +0 -0
  473. /package/dist/web/assets/{clojure-B_uBL5C7.js → clojure-DzamsL69.js} +0 -0
  474. /package/dist/web/assets/{cmake-B3nxBNmj.js → cmake-CR6RP_T_.js} +0 -0
  475. /package/dist/web/assets/{codeowners-DkS-51at.js → codeowners-D1s6-IyY.js} +0 -0
  476. /package/dist/web/assets/{codeql-rpr1z_mR.js → codeql-DAqNWMkS.js} +0 -0
  477. /package/dist/web/assets/{common-lisp-BfNbv_W6.js → common-lisp-8e-KtitB.js} +0 -0
  478. /package/dist/web/assets/{coq-EAW-1nw0.js → coq-UEq0WUcO.js} +0 -0
  479. /package/dist/web/assets/{csharp-BE3Gn5bq.js → csharp-wxJ0TuCm.js} +0 -0
  480. /package/dist/web/assets/{css-Cr27CfPY.js → css-Cerub-yB.js} +0 -0
  481. /package/dist/web/assets/{csv-B5VtaPtH.js → csv-Bh9_WD_h.js} +0 -0
  482. /package/dist/web/assets/{csv-parser-B2v8MoUy.js → csv-parser-BPIDt6ni.js} +0 -0
  483. /package/dist/web/assets/{cue-jm6lYNrx.js → cue-rzM1ztqU.js} +0 -0
  484. /package/dist/web/assets/{cypher-DXkvK-pn.js → cypher-DGaTQ8m0.js} +0 -0
  485. /package/dist/web/assets/{d-CgG1_yzd.js → d-DbkZGuhB.js} +0 -0
  486. /package/dist/web/assets/{dark-plus-CALzcIVB.js → dark-plus-BO2bzZcA.js} +0 -0
  487. /package/dist/web/assets/{dart-BEVoLlWg.js → dart-BDhCoqVg.js} +0 -0
  488. /package/dist/web/assets/{dax-H0BW3Q_P.js → dax-Bh03stWh.js} +0 -0
  489. /package/dist/web/assets/{desktop-BtYq1l_0.js → desktop-BOdr_fwq.js} +0 -0
  490. /package/dist/web/assets/{diff-CnBYdfTg.js → diff-Bb_gTsS2.js} +0 -0
  491. /package/dist/web/assets/{dist-CJJKpgR7.js → dist-BUMDu0av.js} +0 -0
  492. /package/dist/web/assets/{docker-COBitpld.js → docker-BDsTF5ce.js} +0 -0
  493. /package/dist/web/assets/{dotenv-CG0x1m7c.js → dotenv-CbCWp9Ti.js} +0 -0
  494. /package/dist/web/assets/{dracula-CLCi47X-.js → dracula-Cd1fxvoX.js} +0 -0
  495. /package/dist/web/assets/{dracula-soft-BMv5cCAV.js → dracula-soft-B7eXkfXG.js} +0 -0
  496. /package/dist/web/assets/{dream-maker-1Bd4OSww.js → dream-maker-XlzuS21R.js} +0 -0
  497. /package/dist/web/assets/{emacs-lisp-B-2zKLn-.js → emacs-lisp-CXV1Ki_7.js} +0 -0
  498. /package/dist/web/assets/{everforest-dark-BGVynCbV.js → everforest-dark-DuqR60Dg.js} +0 -0
  499. /package/dist/web/assets/{everforest-light--rn3R7Cm.js → everforest-light-q4BPnenD.js} +0 -0
  500. /package/dist/web/assets/{fennel-SWg9Us_u.js → fennel-B-e0TX2Q.js} +0 -0
  501. /package/dist/web/assets/{fish-B04oqgKk.js → fish-Bzb6-TtF.js} +0 -0
  502. /package/dist/web/assets/{fluent-BefzAGMN.js → fluent-B-szH2-f.js} +0 -0
  503. /package/dist/web/assets/{format-bytes-gQw5wzqa.js → format-bytes-CKXk9k2d.js} +0 -0
  504. /package/dist/web/assets/{fortran-free-form-alw7jMxb.js → fortran-free-form-OS6SGYC0.js} +0 -0
  505. /package/dist/web/assets/{gdscript-BAyhvjCx.js → gdscript-FYq4OP2d.js} +0 -0
  506. /package/dist/web/assets/{gdshader-Crl09X-B.js → gdshader-BpG6P8rw.js} +0 -0
  507. /package/dist/web/assets/{genie-BSIGRHu7.js → genie-BS9RLAna.js} +0 -0
  508. /package/dist/web/assets/{gherkin-DJhSnSWH.js → gherkin-B_9FdtGs.js} +0 -0
  509. /package/dist/web/assets/{github-dark-r8C6qwug.js → github-dark-cfaIlnBV.js} +0 -0
  510. /package/dist/web/assets/{github-dark-default-FVtXjcm8.js → github-dark-default-B2Wv65He.js} +0 -0
  511. /package/dist/web/assets/{github-dark-dimmed-CzrUPgSb.js → github-dark-dimmed-CtM9X76s.js} +0 -0
  512. /package/dist/web/assets/{github-dark-high-contrast-1s9rF5EB.js → github-dark-high-contrast-DLHyk0tG.js} +0 -0
  513. /package/dist/web/assets/{github-light-BvIWCkl7.js → github-light-DWEI10iG.js} +0 -0
  514. /package/dist/web/assets/{github-light-default-CVdhzrxV.js → github-light-default-DcJkK1YN.js} +0 -0
  515. /package/dist/web/assets/{github-light-high-contrast-CBAf6hjF.js → github-light-high-contrast-Bcx9iKDt.js} +0 -0
  516. /package/dist/web/assets/{gleam-DvL1q7aa.js → gleam-DVpiM6b2.js} +0 -0
  517. /package/dist/web/assets/{gn-BG5NvwAf.js → gn-B80DNc0i.js} +0 -0
  518. /package/dist/web/assets/{gnuplot-BARnM-zG.js → gnuplot-D9i3MaT-.js} +0 -0
  519. /package/dist/web/assets/{go-DXvjjq4q.js → go-CwCBf_Pt.js} +0 -0
  520. /package/dist/web/assets/{groovy-CRylSKhr.js → groovy-D7mEfwmW.js} +0 -0
  521. /package/dist/web/assets/{gruvbox-dark-hard-BVPjnqm7.js → gruvbox-dark-hard-C4R4MOoH.js} +0 -0
  522. /package/dist/web/assets/{gruvbox-dark-medium-U_PDgq0a.js → gruvbox-dark-medium-CZnFC0s8.js} +0 -0
  523. /package/dist/web/assets/{gruvbox-dark-soft-CkzNtpbr.js → gruvbox-dark-soft-DFcJlEBl.js} +0 -0
  524. /package/dist/web/assets/{gruvbox-light-hard-DfMmR2G8.js → gruvbox-light-hard-B6IF_1L-.js} +0 -0
  525. /package/dist/web/assets/{gruvbox-light-medium-BGCak-dd.js → gruvbox-light-medium-C19AZJv2.js} +0 -0
  526. /package/dist/web/assets/{gruvbox-light-soft-DnpM3fkN.js → gruvbox-light-soft-BAnpDm9B.js} +0 -0
  527. /package/dist/web/assets/{haskell-DXgGIDD_.js → haskell-CsXSYXRG.js} +0 -0
  528. /package/dist/web/assets/{haxe-B7qlTpRz.js → haxe-B1uyiI9t.js} +0 -0
  529. /package/dist/web/assets/{hcl-ADWfVe_Q.js → hcl-DlYKn5q9.js} +0 -0
  530. /package/dist/web/assets/{hjson-BaM9gLbq.js → hjson-C_0VMGJJ.js} +0 -0
  531. /package/dist/web/assets/{hlsl-iqaRA4MG.js → hlsl-zt2PEzbg.js} +0 -0
  532. /package/dist/web/assets/{horizon-BPpTnhB9.js → horizon-6wQfyOK0.js} +0 -0
  533. /package/dist/web/assets/{horizon-bright-yBLg3QbE.js → horizon-bright-CqI2qTim.js} +0 -0
  534. /package/dist/web/assets/{houston-eFOp0ySL.js → houston-BljpHDoG.js} +0 -0
  535. /package/dist/web/assets/{hy-BALeMn3i.js → hy-XzuL55dw.js} +0 -0
  536. /package/dist/web/assets/{image-gallery-Bqb-wjNw.js → image-gallery-Cj8nd4xW.js} +0 -0
  537. /package/dist/web/assets/{imba-CdGUm_GD.js → imba-CqhhjBrP.js} +0 -0
  538. /package/dist/web/assets/{ini-DG7imsqe.js → ini-CiFDi6cY.js} +0 -0
  539. /package/dist/web/assets/{java-D5GTT474.js → java-OsQgTL5-.js} +0 -0
  540. /package/dist/web/assets/{javascript-V7WwJvRm.js → javascript-qWAOScxl.js} +0 -0
  541. /package/dist/web/assets/{json-B9FhwAgR.js → json-RiAKsaD1.js} +0 -0
  542. /package/dist/web/assets/{json5--Abk7MUy.js → json5-BeCnS3H2.js} +0 -0
  543. /package/dist/web/assets/{jsonc-C8jb-Z6x.js → jsonc-CbTW1Mka.js} +0 -0
  544. /package/dist/web/assets/{jsonl-CaJNxfkm.js → jsonl-BLSi_669.js} +0 -0
  545. /package/dist/web/assets/{jsonnet-CPrKMSCU.js → jsonnet-C3xAggou.js} +0 -0
  546. /package/dist/web/assets/{jssm-DEu-9_uo.js → jssm-CLdiagC9.js} +0 -0
  547. /package/dist/web/assets/{jsx-MfAsCWDW.js → jsx-lYE1lb50.js} +0 -0
  548. /package/dist/web/assets/{kanagawa-dragon-DnbcAduF.js → kanagawa-dragon-Cd1vcrpD.js} +0 -0
  549. /package/dist/web/assets/{kanagawa-lotus-yxsY3ew8.js → kanagawa-lotus-Dw3QKtk9.js} +0 -0
  550. /package/dist/web/assets/{kanagawa-wave-C2RyvZaM.js → kanagawa-wave-DIq5Zv5G.js} +0 -0
  551. /package/dist/web/assets/{kdl-CvssmvcU.js → kdl-BfDV0825.js} +0 -0
  552. /package/dist/web/assets/{kotlin-jufO49mU.js → kotlin-DONxR4wD.js} +0 -0
  553. /package/dist/web/assets/{kusto-BAFnylR7.js → kusto-BDP63Fh5.js} +0 -0
  554. /package/dist/web/assets/{laserwave-BgxFC4Ge.js → laserwave-BeBrWApL.js} +0 -0
  555. /package/dist/web/assets/{lean-Bgt24V1D.js → lean-DOZc0N1A.js} +0 -0
  556. /package/dist/web/assets/{less-Bk_UaqsM.js → less-C3H9NlDv.js} +0 -0
  557. /package/dist/web/assets/{libesm-dcbw4_18.js → libesm-DLBE59ul.js} +0 -0
  558. /package/dist/web/assets/{light-plus-nMxFfInN.js → light-plus-C_kTL608.js} +0 -0
  559. /package/dist/web/assets/{llvm-3_PASDOO.js → llvm-Dsp27U3P.js} +0 -0
  560. /package/dist/web/assets/{log-CKm0CxB1.js → log-BywQaTCy.js} +0 -0
  561. /package/dist/web/assets/{logo-DFP1_5U6.js → logo-BirJgciO.js} +0 -0
  562. /package/dist/web/assets/{luau-BcZnoyh3.js → luau-Btrz5kJB.js} +0 -0
  563. /package/dist/web/assets/{make-CpNSwgwT.js → make-Kl7nFQrS.js} +0 -0
  564. /package/dist/web/assets/{markdown-DhU3Djpv.js → markdown-qsC4P8S-.js} +0 -0
  565. /package/dist/web/assets/{material-theme-DD5L3OqF.js → material-theme-CC__ui3p.js} +0 -0
  566. /package/dist/web/assets/{material-theme-darker-CG2_tD3R.js → material-theme-darker-D4FxG2FZ.js} +0 -0
  567. /package/dist/web/assets/{material-theme-lighter-CzNkUc9Y.js → material-theme-lighter-zGqiWcxn.js} +0 -0
  568. /package/dist/web/assets/{material-theme-ocean-BtLiAaF_.js → material-theme-ocean-CZIdTTB1.js} +0 -0
  569. /package/dist/web/assets/{material-theme-palenight-Brvjz035.js → material-theme-palenight-CEIs0GcK.js} +0 -0
  570. /package/dist/web/assets/{matlab-CCY0KtMD.js → matlab-Cf0aUjN9.js} +0 -0
  571. /package/dist/web/assets/{mdx-CH6YY0aS.js → mdx-CIsXjH86.js} +0 -0
  572. /package/dist/web/assets/{mermaid-Bqh3gFI0.js → mermaid-B06WO9Qc.js} +0 -0
  573. /package/dist/web/assets/{min-dark-BUwfTzI6.js → min-dark-z8fyp0FT.js} +0 -0
  574. /package/dist/web/assets/{min-light-oLanXCOk.js → min-light-Dr-qesbW.js} +0 -0
  575. /package/dist/web/assets/{mipsasm-CTh3hPWK.js → mipsasm-CWHzYLcE.js} +0 -0
  576. /package/dist/web/assets/{mojo-DXdszZKa.js → mojo-D9zXrKmv.js} +0 -0
  577. /package/dist/web/assets/{monokai-TNE_tRxc.js → monokai-4b-AKOpX.js} +0 -0
  578. /package/dist/web/assets/{moonbit-jrk-Gx1X.js → moonbit-Dl4dhOZJ.js} +0 -0
  579. /package/dist/web/assets/{move-DItn61bH.js → move-DTYKJmXy.js} +0 -0
  580. /package/dist/web/assets/{narrat-3kB4dkh0.js → narrat-DnvXOS-o.js} +0 -0
  581. /package/dist/web/assets/{nextflow-groovy-CSKtwETr.js → nextflow-groovy-Di8QnEXg.js} +0 -0
  582. /package/dist/web/assets/{night-owl-Dm91nSIq.js → night-owl-Czh0yq-H.js} +0 -0
  583. /package/dist/web/assets/{night-owl-light-riwrcPRg.js → night-owl-light-D_OpZqOR.js} +0 -0
  584. /package/dist/web/assets/{nix-sLgl9zGF.js → nix-2MvcjpK3.js} +0 -0
  585. /package/dist/web/assets/{nord-TWkJPI6M.js → nord-CG_r4cyk.js} +0 -0
  586. /package/dist/web/assets/{nushell-CDZi4-Gf.js → nushell-DINskMcU.js} +0 -0
  587. /package/dist/web/assets/{objective-c-DW9PboNp.js → objective-c-Dv7BIicR.js} +0 -0
  588. /package/dist/web/assets/{objective-cpp-2XCCX6q1.js → objective-cpp-CpqsCxPb.js} +0 -0
  589. /package/dist/web/assets/{ocaml-CxPdnjrh.js → ocaml-DwTsYSmW.js} +0 -0
  590. /package/dist/web/assets/{odin-BJjlKpkM.js → odin-G-4O4k4t.js} +0 -0
  591. /package/dist/web/assets/{one-dark-pro-K1KOmBZ7.js → one-dark-pro-13Pv5SjI.js} +0 -0
  592. /package/dist/web/assets/{one-light-DQB-vUyZ.js → one-light-Bi2e86mS.js} +0 -0
  593. /package/dist/web/assets/{openscad-DCoLwn4j.js → openscad-D0WsOTij.js} +0 -0
  594. /package/dist/web/assets/{pascal-z2ktbbWp.js → pascal-DmDStuXr.js} +0 -0
  595. /package/dist/web/assets/{pkl-Dyq4-v6z.js → pkl-Bl6Qjtuo.js} +0 -0
  596. /package/dist/web/assets/{plastic-C4Rq5PnD.js → plastic-UeSVSjTI.js} +0 -0
  597. /package/dist/web/assets/{plsql-BCIjO63y.js → plsql-DreFC689.js} +0 -0
  598. /package/dist/web/assets/{po-_kXpd_0n.js → po-ZIjccxsX.js} +0 -0
  599. /package/dist/web/assets/{poimandres-C9XggjB3.js → poimandres-CvQHJPrI.js} +0 -0
  600. /package/dist/web/assets/{polar-DV_1L04z.js → polar-BrMB0Kgh.js} +0 -0
  601. /package/dist/web/assets/{postcss-BuqS91mQ.js → postcss-pgUsil1T.js} +0 -0
  602. /package/dist/web/assets/{powerquery-DK13B2Dc.js → powerquery-CTsZkCg_.js} +0 -0
  603. /package/dist/web/assets/{powershell-tkuhhtuP.js → powershell-CcwVuBAp.js} +0 -0
  604. /package/dist/web/assets/{prisma-WNXfBtpr.js → prisma-spjnAI5W.js} +0 -0
  605. /package/dist/web/assets/{prolog-BzEdNKfO.js → prolog-ChCkZpDO.js} +0 -0
  606. /package/dist/web/assets/{proto-B5meVsrA.js → proto-wVGqK4gB.js} +0 -0
  607. /package/dist/web/assets/{puppet-C7h_p4r2.js → puppet-CED7iwPQ.js} +0 -0
  608. /package/dist/web/assets/{purescript-qH1KxAs6.js → purescript-u9lGiQKV.js} +0 -0
  609. /package/dist/web/assets/{python-DeTIYd5V.js → python-DeqTx6ef.js} +0 -0
  610. /package/dist/web/assets/{qmldir-4Rm5AvD3.js → qmldir-BH1uoPrX.js} +0 -0
  611. /package/dist/web/assets/{qss-YRbeNtr0.js → qss-CUVKA_13.js} +0 -0
  612. /package/dist/web/assets/{r-DHwHK0Jh.js → r-BqdPzqYH.js} +0 -0
  613. /package/dist/web/assets/{racket-gKn4SFLQ.js → racket-C2eTgPN3.js} +0 -0
  614. /package/dist/web/assets/{raku-DWTmAj3A.js → raku-uAay1sWx.js} +0 -0
  615. /package/dist/web/assets/{red-BFuHRZFS.js → red-DTZP1ZfC.js} +0 -0
  616. /package/dist/web/assets/{reg-DYoOF5tH.js → reg-Cdk3YsxY.js} +0 -0
  617. /package/dist/web/assets/{regexp-6rXQHD2-.js → regexp-Bpa2aS7K.js} +0 -0
  618. /package/dist/web/assets/{rel-Z_tTruCU.js → rel-bNsb1-3L.js} +0 -0
  619. /package/dist/web/assets/{resolve-theme-4fexHWl6.js → resolve-theme-DQk_YcsT.js} +0 -0
  620. /package/dist/web/assets/{riscv-DfTfKuvU.js → riscv-DSaubByW.js} +0 -0
  621. /package/dist/web/assets/{ron-CsUtW8gQ.js → ron-DbQzYM6i.js} +0 -0
  622. /package/dist/web/assets/{rose-pine-Dng_vCo6.js → rose-pine-C7RsprTu.js} +0 -0
  623. /package/dist/web/assets/{rose-pine-dawn-Ds88Nezf.js → rose-pine-dawn-VP976YzL.js} +0 -0
  624. /package/dist/web/assets/{rose-pine-moon-DWpzrqNz.js → rose-pine-moon-XHZNhOqd.js} +0 -0
  625. /package/dist/web/assets/{rosmsg-h2sO84oD.js → rosmsg-BVQlx0Lj.js} +0 -0
  626. /package/dist/web/assets/{rust-Dx86yfCi.js → rust-VHaQ3rh3.js} +0 -0
  627. /package/dist/web/assets/{sass-BwwVhdT9.js → sass-CO-kMkwD.js} +0 -0
  628. /package/dist/web/assets/{scala-WJqNEN-w.js → scala-cY0GXkiz.js} +0 -0
  629. /package/dist/web/assets/{scheme-uwj-t1Rp.js → scheme-D4LNZE0X.js} +0 -0
  630. /package/dist/web/assets/{sdbl-BYy5hgEz.js → sdbl-Bh2Fd2cn.js} +0 -0
  631. /package/dist/web/assets/{shellscript-DWTW_w2K.js → shellscript-KKclmCnt.js} +0 -0
  632. /package/dist/web/assets/{slack-dark-BvhGq5vQ.js → slack-dark-D6YQ-fqf.js} +0 -0
  633. /package/dist/web/assets/{slack-ochin-C9g0ZRFr.js → slack-ochin-CDf66CDj.js} +0 -0
  634. /package/dist/web/assets/{smalltalk-ZEiZ6i7U.js → smalltalk-D2OH_QIl.js} +0 -0
  635. /package/dist/web/assets/{snazzy-light-29OA24q5.js → snazzy-light-CQeF8G1K.js} +0 -0
  636. /package/dist/web/assets/{solarized-dark-Bnq9IOn_.js → solarized-dark-LTPf1iLZ.js} +0 -0
  637. /package/dist/web/assets/{solarized-light-CYdI7T5g.js → solarized-light-Ose1n88k.js} +0 -0
  638. /package/dist/web/assets/{solidity-DcuIDB-_.js → solidity-lFuOMHki.js} +0 -0
  639. /package/dist/web/assets/{splunk-D5ONE91g.js → splunk-Bov-sB5S.js} +0 -0
  640. /package/dist/web/assets/{sql-DIwJJDU-.js → sql-BKyZ6wmk.js} +0 -0
  641. /package/dist/web/assets/{ssh-config-D8717tLI.js → ssh-config-CqJoMGMb.js} +0 -0
  642. /package/dist/web/assets/{stylus-Cm1lO4Y6.js → stylus-Bf5pWRLI.js} +0 -0
  643. /package/dist/web/assets/{swift-X8oWpSeU.js → swift-D5d1hn_u.js} +0 -0
  644. /package/dist/web/assets/{synthwave-84-DF-t1UKY.js → synthwave-84-B4c6YAgO.js} +0 -0
  645. /package/dist/web/assets/{system-verilog-JYyHFpPf.js → system-verilog-D7fXyDsI.js} +0 -0
  646. /package/dist/web/assets/{systemd-Da-5Ma_c.js → systemd-oNagzlBK.js} +0 -0
  647. /package/dist/web/assets/{talonscript-CXraVPho.js → talonscript-C6Nqm9Dm.js} +0 -0
  648. /package/dist/web/assets/{tasl-BCmWezH9.js → tasl-CaWy-cBX.js} +0 -0
  649. /package/dist/web/assets/{tcl-BTufzfbf.js → tcl-C435P5Mq.js} +0 -0
  650. /package/dist/web/assets/{terraform-CtUA9c8W.js → terraform-CVDxqdPf.js} +0 -0
  651. /package/dist/web/assets/{tokyo-night-BCThYXmz.js → tokyo-night-Uhs17tQ3.js} +0 -0
  652. /package/dist/web/assets/{toml-D5YlDr6Y.js → toml-j6fPOJ6P.js} +0 -0
  653. /package/dist/web/assets/{tsv-CBB-P_Sl.js → tsv-h78O6xrL.js} +0 -0
  654. /package/dist/web/assets/{tsx-D5B4juDp.js → tsx-Bol-vCrQ.js} +0 -0
  655. /package/dist/web/assets/{turtle-D8PCeJ-v.js → turtle-Dw8eFf04.js} +0 -0
  656. /package/dist/web/assets/{typescript-CMPnTawp.js → typescript-Dtz7DPci.js} +0 -0
  657. /package/dist/web/assets/{typespec-CHxldxIW.js → typespec-BIOQ7dMP.js} +0 -0
  658. /package/dist/web/assets/{typst-Q8CqX5OR.js → typst-CoaGMjzu.js} +0 -0
  659. /package/dist/web/assets/{utils-QHe_Q6UF.js → utils-C9OjCrou.js} +0 -0
  660. /package/dist/web/assets/{v-Bf2hKAMu.js → v-BoqFdpJ7.js} +0 -0
  661. /package/dist/web/assets/{vala-Cbete-UE.js → vala-COBpNnCC.js} +0 -0
  662. /package/dist/web/assets/{vb-CA-xWAcX.js → vb-DxI86znh.js} +0 -0
  663. /package/dist/web/assets/{vendor-xterm-BHKF_Nsf.js → vendor-xterm-BaMOj65g.js} +0 -0
  664. /package/dist/web/assets/{verilog-B3wb8CfT.js → verilog-DcTAamuQ.js} +0 -0
  665. /package/dist/web/assets/{vesper-D6r3VJq_.js → vesper-DO298JSv.js} +0 -0
  666. /package/dist/web/assets/{vhdl-C42S_797.js → vhdl-Dq-bDVSN.js} +0 -0
  667. /package/dist/web/assets/{viml-BePrIAs8.js → viml-zHTRWuQh.js} +0 -0
  668. /package/dist/web/assets/{vitesse-black-DRqn61Ck.js → vitesse-black-jlYMJCBf.js} +0 -0
  669. /package/dist/web/assets/{vitesse-dark-39_8UE-w.js → vitesse-dark-DS0sUlSZ.js} +0 -0
  670. /package/dist/web/assets/{vitesse-light-B1jwrxDm.js → vitesse-light-HSeZ2YhG.js} +0 -0
  671. /package/dist/web/assets/{vyper-tDJv3imF.js → vyper-BQfKVO3M.js} +0 -0
  672. /package/dist/web/assets/{wasm-ChtccNfv.js → wasm-B1xp9bLp.js} +0 -0
  673. /package/dist/web/assets/{wasm-DR7eskZH.js → wasm-B6mCuI8h.js} +0 -0
  674. /package/dist/web/assets/{wenyan-CezVfqN3.js → wenyan-CfpS05sP.js} +0 -0
  675. /package/dist/web/assets/{wgsl-CwOcIiIG.js → wgsl-14GDsWRD.js} +0 -0
  676. /package/dist/web/assets/{wikitext-C5coomYc.js → wikitext-Ccbq5EVX.js} +0 -0
  677. /package/dist/web/assets/{wit-C9yyX6-U.js → wit-C19hb3R5.js} +0 -0
  678. /package/dist/web/assets/{wolfram-BWi0rMAa.js → wolfram-B-ZSwGwP.js} +0 -0
  679. /package/dist/web/assets/{yaml-CGXhKUtT.js → yaml-mw_JUeio.js} +0 -0
  680. /package/dist/web/assets/{zenscript-Do7WLoJh.js → zenscript-WbzDRjvF.js} +0 -0
  681. /package/dist/web/assets/{zig-Cr80Qrru.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 "../../lib/api-settings";
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,93 @@
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 card one account is drawn in.
70
+ *
71
+ * Shared so a Claude row and a Codex row are the same object on screen even though what goes
72
+ * inside them differs.
73
+ */
74
+ export function AccountCardShell({ active, flash, children, ...rest }: {
75
+ active?: boolean;
76
+ /** Brief highlight when this account's numbers just changed. */
77
+ flash?: boolean;
78
+ children: React.ReactNode;
79
+ } & React.HTMLAttributes<HTMLDivElement>) {
80
+ return (
81
+ <div
82
+ {...rest}
83
+ className={[
84
+ "rounded-md border p-3 space-y-2 transition-colors duration-500",
85
+ flash ? "bg-primary/10 border-primary/40" : "",
86
+ active ? "border-primary/30 bg-primary/5" : "border-border/50",
87
+ rest.className ?? "",
88
+ ].filter(Boolean).join(" ")}
89
+ >
90
+ {children}
91
+ </div>
92
+ );
93
+ }
@@ -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,168 @@
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 { 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
+ <div className="space-y-2">
121
+ {usages.map((entry) => (
122
+ <AccountCard
123
+ key={entry.accountId}
124
+ entry={entry}
125
+ isActive={entry.accountId === activeAccountId}
126
+ accountInfo={accountMap.get(entry.accountId)}
127
+ onToggle={handleToggle}
128
+ toggling={togglingId === entry.accountId}
129
+ onDelete={(id, display) => setDeleteTarget({ id, display })}
130
+ onExport={(id) => { setExportPreselect(id); setDialog("export"); }}
131
+ onViewProfile={(profile, accountId) => setProfileView({ profile, accountId })}
132
+ flash={flashIds.has(entry.accountId)}
133
+ />
134
+ ))}
135
+ </div>
136
+ )}
137
+ </section>
138
+
139
+ {profileView && (
140
+ <AccountProfilePanel
141
+ profile={profileView.profile}
142
+ accountId={profileView.accountId}
143
+ onClose={() => setProfileView(null)}
144
+ />
145
+ )}
146
+
147
+ {deleteTarget && (
148
+ <AccountDeleteConfirm
149
+ display={deleteTarget.display}
150
+ onConfirm={() => void confirmDelete()}
151
+ onCancel={() => setDeleteTarget(null)}
152
+ />
153
+ )}
154
+
155
+ <AddAccountDialog open={dialog === "add"} onOpenChange={(v) => setDialog(v ? "add" : null)} onSuccess={handleSuccess} />
156
+ <ExportAccountsDialog
157
+ open={dialog === "export"}
158
+ onOpenChange={(v) => { setDialog(v ? "export" : null); if (!v) setExportPreselect(null); }}
159
+ accounts={accounts}
160
+ preselectId={exportPreselect}
161
+ onMessage={setMessage}
162
+ />
163
+ <ImportAccountsDialog open={dialog === "import"} onOpenChange={(v) => setDialog(v ? "import" : null)} onSuccess={handleSuccess} />
164
+ <AccountRotationSettings open={dialog === "rotation"} onOpenChange={(v) => setDialog(v ? "rotation" : null)} />
165
+ <AccountTokenTestDialog open={dialog === "token-test"} onOpenChange={(v) => setDialog(v ? "token-test" : null)} accounts={accounts} />
166
+ </div>
167
+ );
168
+ }
@@ -0,0 +1,137 @@
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 { 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
+ <div className="space-y-2">
74
+ {c.accounts.map((a) => {
75
+ const u = c.usages[a.id] ?? {};
76
+ return (
77
+ <AccountCardShell key={a.id} data-testid="account-card" data-account-id={a.id}>
78
+ <div className="flex items-center gap-2">
79
+ <span className="text-sm font-medium truncate flex-1 min-w-0">{a.label}</span>
80
+ <span className="text-[10px] uppercase tracking-wide text-text-subtle border border-border rounded px-1 shrink-0">
81
+ {a.type}
82
+ </span>
83
+ {a.planType && <span className="text-[10px] text-text-subtle shrink-0">{a.planType}</span>}
84
+ <button
85
+ type="button"
86
+ onClick={() => void c.remove(a.id)}
87
+ title="Remove account"
88
+ aria-label="Remove account"
89
+ className="p-2 rounded cursor-pointer text-text-subtle hover:text-error hover:bg-surface-elevated transition-colors shrink-0"
90
+ >
91
+ <Trash2 className="size-4" />
92
+ </button>
93
+ </div>
94
+ <div className="space-y-2">
95
+ <AccountUsageBar label="5-Hour Session" pct={toPct(u.fiveHour)} />
96
+ <AccountUsageBar label="Weekly" pct={toPct(u.sevenDay)} />
97
+ </div>
98
+ </AccountCardShell>
99
+ );
100
+ })}
101
+ </div>
102
+ )}
103
+ </section>
104
+
105
+ <CodexAddAccountDialog
106
+ open={dialog === "add"}
107
+ onOpenChange={(v) => setDialog(v ? "add" : null)}
108
+ label={c.label}
109
+ onLabelChange={c.setLabel}
110
+ apiKey={c.apiKey}
111
+ onApiKeyChange={c.setApiKey}
112
+ adding={c.adding}
113
+ onAddApiKey={() => void c.addApiKey()}
114
+ deviceWaiting={c.deviceWaiting}
115
+ onStartDevice={() => void c.startDevice()}
116
+ device={c.device}
117
+ error={c.err}
118
+ />
119
+ <CodexBackupDialog
120
+ mode={dialog === "export" ? "export" : dialog === "import" ? "import" : null}
121
+ onOpenChange={(v) => { if (!v) setDialog(null); }}
122
+ password={c.backupPassword}
123
+ onPasswordChange={c.setBackupPassword}
124
+ busy={c.exporting || c.importing}
125
+ onExport={() => void c.doExport()}
126
+ onImport={(f) => void c.doImport(f)}
127
+ error={c.err}
128
+ />
129
+ <CodexRotationDialog
130
+ open={dialog === "rotation"}
131
+ onOpenChange={(v) => setDialog(v ? "rotation" : null)}
132
+ strategy={c.strategy}
133
+ onChange={(v) => void c.changeStrategy(v)}
134
+ />
135
+ </div>
136
+ );
137
+ }