@hienlh/ppm 0.19.12 → 0.20.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (537) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/CLAUDE.md +8 -1
  3. package/assets/skills/ppm/SKILL.md +1 -1
  4. package/assets/skills/ppm/references/http-api.md +4 -1
  5. package/dist/web/assets/accounts-settings-section-_sG1AqtH.js +1 -0
  6. package/dist/web/assets/{ai-resource-editor-CqL-X2zu.js → ai-resource-editor-BLctP1Pr.js} +1 -1
  7. package/dist/web/assets/ai-settings-section-0hSDp7xx.js +1 -0
  8. package/dist/web/assets/{ai-settings-section-CcqokdzF.js → ai-settings-section-CTEU2uGa.js} +1 -1
  9. package/dist/web/assets/{angular-html-C8nsn2VC.js → angular-html-BtJLNcMw.js} +1 -1
  10. package/dist/web/assets/{angular-ts-Caw9nRCH.js → angular-ts-CQiu5F_W.js} +1 -1
  11. package/dist/web/assets/api-client-QfreGirP.js +1 -0
  12. package/dist/web/assets/api-settings-D18Q4Ocp.js +1 -0
  13. package/dist/web/assets/{api-themes-BoOJPqwM.js → api-themes-sSv-6OLk.js} +1 -1
  14. package/dist/web/assets/{apl-Dhu1LORF.js → apl-CKZvgs-w.js} +1 -1
  15. package/dist/web/assets/{appearance-settings-section-2hZBnpPS.js → appearance-settings-section-BkcoSV0E.js} +1 -1
  16. package/dist/web/assets/architecture-PBZL5I3N-DfR8k8Bc.js +1 -0
  17. package/dist/web/assets/{astro-DwkX9sLZ.js → astro-D0b8ssLD.js} +1 -1
  18. package/dist/web/assets/{audio-preview-CkGZO8Uy.js → audio-preview-B4E5_7TW.js} +1 -1
  19. package/dist/web/assets/{blade-D1fC9Osx.js → blade-DOIpINLw.js} +1 -1
  20. package/dist/web/assets/{bsl-pur-PRip.js → bsl-CJ7_JlQ8.js} +1 -1
  21. package/dist/web/assets/{button-2WCAS8dI.js → button-DX32ffge.js} +1 -1
  22. package/dist/web/assets/{cairo-DDJs9amR.js → cairo-5CpV2T1P.js} +1 -1
  23. package/dist/web/assets/chat-tab-CJi4uYb9.js +15 -0
  24. package/dist/web/assets/{cobol-Bo0usdoM.js → cobol-BreMVrtc.js} +1 -1
  25. package/dist/web/assets/code-editor-C4kKhamq.js +7 -0
  26. package/dist/web/assets/{coffee-DBBsbX1e.js → coffee-D7DoKh5b.js} +1 -1
  27. package/dist/web/assets/{conflict-editor-NqFAJP47.js → conflict-editor-BEULKwHk.js} +1 -1
  28. package/dist/web/assets/{context-menu-C13b1uT3.js → context-menu-CJL8L98s.js} +1 -1
  29. package/dist/web/assets/{cpp-6CTO78A-.js → cpp-BnWeWAoE.js} +1 -1
  30. package/dist/web/assets/{crystal-DAlvpAP7.js → crystal-D2pJbFUn.js} +1 -1
  31. package/dist/web/assets/{csv-preview-Dvriy18_.js → csv-preview-k3eM8nFf.js} +1 -1
  32. package/dist/web/assets/{data-grid-overlay-editor-iRrpAV0e.js → data-grid-overlay-editor-CbBkhhJa.js} +1 -1
  33. package/dist/web/assets/{database-viewer-Bs2RMm5a.js → database-viewer-Cslir1dr.js} +1 -1
  34. package/dist/web/assets/diff-viewer-DzAIvBbn.js +4 -0
  35. package/dist/web/assets/{docx-preview-Bgw838ry.js → docx-preview-Xy7SZ7Vf.js} +1 -1
  36. package/dist/web/assets/{edge-D0tZrpC7.js → edge-aqGMtbY3.js} +1 -1
  37. package/dist/web/assets/{edit-diff-preview-DUgmjNre.js → edit-diff-preview-xdDQvPhs.js} +2 -2
  38. package/dist/web/assets/{elixir-C1E6-o4G.js → elixir-CsYMBLc5.js} +1 -1
  39. package/dist/web/assets/{elm-BKyTfeQX.js → elm-BU8C2gFp.js} +1 -1
  40. package/dist/web/assets/{erb-Cdnr4BMI.js → erb-Z_E49ab8.js} +1 -1
  41. package/dist/web/assets/{erlang-N1237mFq.js → erlang-BRqOjXH9.js} +1 -1
  42. package/dist/web/assets/{esm-ClNnmD9t.js → esm-B-vXxHUd.js} +1 -1
  43. package/dist/web/assets/{explorer-body-DfWunSf9.js → explorer-body-D6H-4cz9.js} +2 -2
  44. package/dist/web/assets/explorer-window-content-C5W_JzCz.js +1 -0
  45. package/dist/web/assets/{extension-manager-section-Zc9yag2c.js → extension-manager-section-DwicI3CA.js} +1 -1
  46. package/dist/web/assets/{extension-webview-B5FayDPz.js → extension-webview-Z3HhC8Pd.js} +1 -1
  47. package/dist/web/assets/{files-settings-section-CxAtO3GL.js → files-settings-section-ByV7bRbB.js} +1 -1
  48. package/dist/web/assets/{fortran-fixed-form-D_f9tVv0.js → fortran-fixed-form-CWbxmDGR.js} +1 -1
  49. package/dist/web/assets/{fsharp-DEJmdhc2.js → fsharp-DKMYbq6I.js} +1 -1
  50. package/dist/web/assets/{gdresource-Dmmrx2gP.js → gdresource-B3yFau4V.js} +1 -1
  51. package/dist/web/assets/{general-settings-section-9DfKrBns.js → general-settings-section-nLksKHur.js} +1 -1
  52. package/dist/web/assets/{git-commit-qwJ867ig.js → git-commit-BW0p3Ekk.js} +1 -1
  53. package/dist/web/assets/{git-log-panel-BsmDZurR.js → git-log-panel-iUXltiTB.js} +1 -1
  54. package/dist/web/assets/{git-rebase-Dkv8Zw86.js → git-rebase-DrvmQCS7.js} +1 -1
  55. package/dist/web/assets/gitGraph-HDMCJU4V-CPd78aaT.js +1 -0
  56. package/dist/web/assets/{glide-data-grid-BuYwXIoS.js → glide-data-grid-C1QB4DHt.js} +6 -6
  57. package/dist/web/assets/{glimmer-js-CcrXn8lQ.js → glimmer-js-BY1XApJJ.js} +1 -1
  58. package/dist/web/assets/{glimmer-ts-CLab3lU7.js → glimmer-ts-DPZtxF8N.js} +1 -1
  59. package/dist/web/assets/{glsl-DXhkG9dU.js → glsl-DZ8G_Hvf.js} +1 -1
  60. package/dist/web/assets/{graphql-Cuqsx8r3.js → graphql-Ck06uCZy.js} +1 -1
  61. package/dist/web/assets/group-chat-tab-ezXKVVpg.js +1 -0
  62. package/dist/web/assets/{hack-C_CBVdBc.js → hack-pLogDwAs.js} +1 -1
  63. package/dist/web/assets/{haml-CBWfzZpv.js → haml-CBqeCQ2A.js} +1 -1
  64. package/dist/web/assets/{handlebars-4e8OR-VI.js → handlebars-B66JmVtp.js} +1 -1
  65. package/dist/web/assets/{html-C5X4urn_.js → html-Dk4wmfwC.js} +1 -1
  66. package/dist/web/assets/{html-derivative-nH-jXfsL.js → html-derivative-CJWqfN51.js} +1 -1
  67. package/dist/web/assets/{http-CQgaG7eu.js → http-CexhotTh.js} +1 -1
  68. package/dist/web/assets/{hurl-B8gidJhu.js → hurl-BScKFk3Z.js} +1 -1
  69. package/dist/web/assets/{hxml-BAK94GX8.js → hxml-DAle7vtu.js} +1 -1
  70. package/dist/web/assets/{image-preview-1kGj4W9R.js → image-preview-fuU4h2nr.js} +1 -1
  71. package/dist/web/assets/index-CJWbFurD.js +52 -0
  72. package/dist/web/assets/index-CoaVwAF6.css +2 -0
  73. package/dist/web/assets/info-3K5VOQVL-BciNds6R.js +1 -0
  74. package/dist/web/assets/{jinja-BuAjvj8M.js → jinja-yHpyD80t.js} +1 -1
  75. package/dist/web/assets/{jira-watcher-section-rJVz40wR.js → jira-watcher-section-DwARwskB.js} +1 -1
  76. package/dist/web/assets/{jison-Dhhs26-L.js → jison-BY6WLJVu.js} +1 -1
  77. package/dist/web/assets/{julia-gZavIyll.js → julia-Dd_iYQDT.js} +1 -1
  78. package/dist/web/assets/{just-DDHrFReO.js → just-BTuUKpxF.js} +1 -1
  79. package/dist/web/assets/keybindings-store-S7YLGd8B.js +1 -0
  80. package/dist/web/assets/{keyboard-shortcuts-section-Dv4e3M8h.js → keyboard-shortcuts-section-BoUDvdDy.js} +1 -1
  81. package/dist/web/assets/{latex-BdGZa8ov.js → latex-Dt3OUlMZ.js} +1 -1
  82. package/dist/web/assets/{liquid-urFfd9Fr.js → liquid-BxPJU2ix.js} +1 -1
  83. package/dist/web/assets/{lua-CcS6tnB5.js → lua-DEGIvn7d.js} +1 -1
  84. package/dist/web/assets/{markdown-renderer-CEloCR06.js → markdown-renderer-DKdfiSIX.js} +3 -3
  85. package/dist/web/assets/markdown-renderer-DYFmMC_8.js +1 -0
  86. package/dist/web/assets/{marko-CSQqk4oG.js → marko-BDxwBJur.js} +1 -1
  87. package/dist/web/assets/{mdc-CYIe80m_.js → mdc-B8aXPTg7.js} +1 -1
  88. package/dist/web/assets/{media-url-DBmHJRft.js → media-url-BMeBaB0q.js} +1 -1
  89. package/dist/web/assets/{mobile-bottom-sheet-B3uDlD79.js → mobile-bottom-sheet-CmySDB6I.js} +1 -1
  90. package/dist/web/assets/mobile-explorer-sheet-CMdqLCV-.js +1 -0
  91. package/dist/web/assets/{nextflow-C-W8vX8I.js → nextflow-DBVZnnT_.js} +1 -1
  92. package/dist/web/assets/{nginx-COGi5you.js → nginx-Crpyd-Do.js} +1 -1
  93. package/dist/web/assets/{nim-CPitYGel.js → nim-BSqTBYDj.js} +1 -1
  94. package/dist/web/assets/notification-store-DpSYFj36.js +1 -0
  95. package/dist/web/assets/{notifications-settings-section-B2GkfB0l.js → notifications-settings-section-CIf3-3xd.js} +1 -1
  96. package/dist/web/assets/{number-overlay-editor-C_eD-Bdr.js → number-overlay-editor-DRjTIcPH.js} +1 -1
  97. package/dist/web/assets/{overview-panel-Cm2HRtEz.js → overview-panel-DiiA3B_Z.js} +1 -1
  98. package/dist/web/assets/packet-RMMSAZCW-ClD3l2Bs.js +1 -0
  99. package/dist/web/assets/{panel-store-BRrnDO_i.js → panel-store-BmdcSBNb.js} +1 -1
  100. package/dist/web/assets/{pdf-preview-Dmz8jkZZ.js → pdf-preview-C-7IdkIK.js} +1 -1
  101. package/dist/web/assets/{perl-B0yW3hbb.js → perl-BU0kbB-K.js} +1 -1
  102. package/dist/web/assets/{php-BNMYN5Jp.js → php-D9n1ikHN.js} +1 -1
  103. package/dist/web/assets/pie-UPGHQEXC-PU6kD0rW.js +1 -0
  104. package/dist/web/assets/{postgres-viewer-BtZbfC3f.js → postgres-viewer-BYXFYlLg.js} +3 -3
  105. package/dist/web/assets/{ppmbot-settings-section-OYfVG5-l.js → ppmbot-settings-section-B9dXvQ6p.js} +1 -1
  106. package/dist/web/assets/{process-table-DRu3NyO_.js → process-table-B6NeTWt8.js} +1 -1
  107. package/dist/web/assets/{project-store-BuY7rxn_.js → project-store-wWZ6bjwq.js} +1 -1
  108. package/dist/web/assets/{proxy-settings-section-xMbrcyr8.js → proxy-settings-section-CUqq7pdU.js} +1 -1
  109. package/dist/web/assets/{pug-CP5fxsxD.js → pug-BOpaczDr.js} +1 -1
  110. package/dist/web/assets/{qml-CHGpQosY.js → qml-B-cQXhrg.js} +1 -1
  111. package/dist/web/assets/{query-audit-section-CC5Mf6qp.js → query-audit-section-C1TYrPIp.js} +1 -1
  112. package/dist/web/assets/radar-KQ55EAFF-4LzjL0Zt.js +1 -0
  113. package/dist/web/assets/{razor-Cfe0AYo4.js → razor-DFB0pNCh.js} +1 -1
  114. package/dist/web/assets/remote-desktop-mobile-sheet-DuKwKe5F.js +2 -0
  115. package/dist/web/assets/{remote-desktop-mobile-view-B4BSHIxU.js → remote-desktop-mobile-view-DxMTtsa6.js} +1 -1
  116. package/dist/web/assets/{remote-desktop-readiness-gate-C0d0PSX4.js → remote-desktop-readiness-gate-BwjfLWqg.js} +1 -1
  117. package/dist/web/assets/{remote-desktop-window-content-DjeCZzqq.js → remote-desktop-window-content-BIl4N5Hn.js} +1 -1
  118. package/dist/web/assets/{rst-DxSSGshb.js → rst-DAXedsgm.js} +1 -1
  119. package/dist/web/assets/{ruby-rbK5SsJa.js → ruby-BCO4polC.js} +1 -1
  120. package/dist/web/assets/{run-in-terminal-DHAvC1AG.js → run-in-terminal-wHKwAnWP.js} +1 -1
  121. package/dist/web/assets/{sas-DDM28fbb.js → sas-DfB5dcFC.js} +1 -1
  122. package/dist/web/assets/schedules-settings-section-C0qOb7GI.js +1 -0
  123. package/dist/web/assets/{scss-TDl-6ro4.js → scss-BknIVgn7.js} +1 -1
  124. package/dist/web/assets/{send-to-chat-BOyUWNKw.js → send-to-chat-BI7-fFpG.js} +1 -1
  125. package/dist/web/assets/{separator-BzGoTlR7.js → separator-W_hJcXPk.js} +1 -1
  126. package/dist/web/assets/{settings-body-CXTQwhbM.js → settings-body-qbrJXX_A.js} +2 -2
  127. package/dist/web/assets/settings-store-CzLnTR8p.js +2 -0
  128. package/dist/web/assets/settings-tab-BKRgYVxf.js +1 -0
  129. package/dist/web/assets/settings-window-content-DWsxJDu-.js +1 -0
  130. package/dist/web/assets/{shaderlab-CCvmDgGS.js → shaderlab-CPu31TpY.js} +1 -1
  131. package/dist/web/assets/{shellsession-BzKS9yKh.js → shellsession-CFX4c_TM.js} +1 -1
  132. package/dist/web/assets/{soy-QfO35nDh.js → soy-rz9hilgm.js} +1 -1
  133. package/dist/web/assets/{sparql-D29RDfp2.js → sparql-Bo8ZDeqE.js} +1 -1
  134. package/dist/web/assets/{sql-query-editor-B-VwfyTf.js → sql-query-editor-CsZOpCW3.js} +1 -1
  135. package/dist/web/assets/{sqlite-viewer-CpUhDEhN.js → sqlite-viewer-DUrnCaP7.js} +1 -1
  136. package/dist/web/assets/{stata-Be6j_SNv.js → stata-B5J-QMyE.js} +1 -1
  137. package/dist/web/assets/{streaming-store-9_9j70zt.js → streaming-store-9XVmAMfV.js} +1 -1
  138. package/dist/web/assets/{surrealql-CykFjgNG.js → surrealql-Caw1ADnG.js} +1 -1
  139. package/dist/web/assets/{svelte-CdBF8z4-.js → svelte-D3oMVcxz.js} +1 -1
  140. package/dist/web/assets/{system-monitor-body-DzWXd1wM.js → system-monitor-body-C5cMpg7I.js} +2 -2
  141. package/dist/web/assets/system-monitor-tab-C66ywbVZ.js +1 -0
  142. package/dist/web/assets/{system-monitor-window-content-DIIP3dff.js → system-monitor-window-content-CENaXYJ-.js} +1 -1
  143. package/dist/web/assets/{tab-host-window-content-CLdcz84K.js → tab-host-window-content-HViABGoS.js} +1 -1
  144. package/dist/web/assets/{tab-store-PZsP5ZwG.js → tab-store-1xYJb0gM.js} +1 -1
  145. package/dist/web/assets/team-member-sheet-CK6eyBRL.js +2 -0
  146. package/dist/web/assets/team-member-window-content-D6wxg37P.js +1 -0
  147. package/dist/web/assets/{templ-BVHE2CNb.js → templ-BAdNyIOL.js} +1 -1
  148. package/dist/web/assets/{terminal-tab-BeKLpaPM.js → terminal-tab-BmyTawmN.js} +2 -2
  149. package/dist/web/assets/{tex-C8XwrbBw.js → tex-IQ8mELOR.js} +1 -1
  150. package/dist/web/assets/tool-cards-y-XJcJHo.js +9 -0
  151. package/dist/web/assets/treemap-KZPCXAKY-DiumLNMg.js +1 -0
  152. package/dist/web/assets/{ts-tags-BIHk5AM0.js → ts-tags-D1DDO2jl.js} +1 -1
  153. package/dist/web/assets/{twig-YcE6Q2Ox.js → twig-C8Fd5RvV.js} +1 -1
  154. package/dist/web/assets/use-accounts-data-BbOb2QNP.js +1 -0
  155. package/dist/web/assets/{use-blob-url-De-ZtcpX.js → use-blob-url-CrwoZJUJ.js} +1 -1
  156. package/dist/web/assets/use-inline-blame-C1G-_NjC.js +8 -0
  157. package/dist/web/assets/{use-monaco-theme-Kj2OGDlm.js → use-monaco-theme-BkM5rMgL.js} +1 -1
  158. package/dist/web/assets/{use-open-team-member-DWhhWdmJ.js → use-open-team-member-CSUU7Z8N.js} +1 -1
  159. package/dist/web/assets/{use-remote-desktop-display-choice-8AkPpM_0.js → use-remote-desktop-display-choice-BT2r1td1.js} +1 -1
  160. package/dist/web/assets/{use-websocket-kR1omKO1.js → use-websocket-BvVTwMjI.js} +1 -1
  161. package/dist/web/assets/{vendor-mermaid-CKKtYuTV.js → vendor-mermaid-CfY1-PkA.js} +1 -1
  162. package/dist/web/assets/{video-preview-BdV7LjbZ.js → video-preview-BEaofjN7.js} +1 -1
  163. package/dist/web/assets/{vue-q7m01V0h.js → vue-EvccXlkP.js} +1 -1
  164. package/dist/web/assets/{vue-html-D1y9feOj.js → vue-html-Dg0xUJCh.js} +1 -1
  165. package/dist/web/assets/{vue-vine-DrIGs03v.js → vue-vine-BdrMg72k.js} +1 -1
  166. package/dist/web/assets/{window-pip-registry-CSLhJfpL.js → window-pip-registry-CrCYYTTf.js} +1 -1
  167. package/dist/web/assets/{window-store-DGKf9rPG.js → window-store-D0HbQ1pM.js} +1 -1
  168. package/dist/web/assets/ws-auth-D111jj32.js +1 -0
  169. package/dist/web/assets/{xml-oyvc1920.js → xml-MmDclzxK.js} +1 -1
  170. package/dist/web/assets/{xsl-BbIGdNPF.js → xsl-DQEFs6Dd.js} +1 -1
  171. package/dist/web/index.html +23 -23
  172. package/dist/web/sw.js +1 -1
  173. package/docs/extension-development-guide.md +47 -0
  174. package/docs/project-roadmap.md +3 -1
  175. package/package.json +2 -2
  176. package/packages/ext-git-graph/package.json +23 -5
  177. package/packages/ext-git-graph/src/blame-parser.test.ts +154 -0
  178. package/packages/ext-git-graph/src/blame-parser.ts +148 -0
  179. package/packages/ext-git-graph/src/blame-view.ts +389 -0
  180. package/packages/ext-git-graph/src/commit-search.test.ts +118 -0
  181. package/packages/ext-git-graph/src/commit-search.ts +102 -0
  182. package/packages/ext-git-graph/src/compare-args.test.ts +111 -0
  183. package/packages/ext-git-graph/src/compare-args.ts +109 -0
  184. package/packages/ext-git-graph/src/compare-view.ts +343 -0
  185. package/packages/ext-git-graph/src/extension-integration.test.ts +6 -3
  186. package/packages/ext-git-graph/src/extension.ts +202 -249
  187. package/packages/ext-git-graph/src/file-history-parser.test.ts +102 -0
  188. package/packages/ext-git-graph/src/file-history-parser.ts +89 -0
  189. package/packages/ext-git-graph/src/file-history-view.ts +386 -0
  190. package/packages/ext-git-graph/src/git-exec.ts +122 -0
  191. package/packages/ext-git-graph/src/git-state.ts +81 -0
  192. package/packages/ext-git-graph/src/panel-nav.ts +65 -0
  193. package/packages/ext-git-graph/src/panel-registry.ts +72 -0
  194. package/packages/ext-git-graph/src/ppm-api.ts +48 -0
  195. package/packages/ext-git-graph/src/rebase-todo.test.ts +196 -0
  196. package/packages/ext-git-graph/src/rebase-todo.ts +119 -0
  197. package/packages/ext-git-graph/src/rebase-view.ts +483 -0
  198. package/packages/ext-git-graph/src/reflog-parser.test.ts +93 -0
  199. package/packages/ext-git-graph/src/reflog-parser.ts +86 -0
  200. package/packages/ext-git-graph/src/reflog-view.ts +375 -0
  201. package/packages/ext-git-graph/src/register-view-command.ts +47 -0
  202. package/packages/ext-git-graph/src/submodule-parser.test.ts +79 -0
  203. package/packages/ext-git-graph/src/submodule-parser.ts +77 -0
  204. package/packages/ext-git-graph/src/types.ts +10 -1
  205. package/packages/ext-git-graph/src/webview-html.ts +438 -3
  206. package/packages/ext-git-graph/src/webview-shell.ts +236 -0
  207. package/src/providers/claude-agent-sdk.ts +7 -0
  208. package/src/providers/codex-app-server/codex-provider.ts +89 -1
  209. package/src/server/index.ts +1 -1
  210. package/src/server/routes/accounts.ts +25 -1
  211. package/src/server/routes/chat.ts +29 -1
  212. package/src/server/routes/codex-accounts.ts +41 -1
  213. package/src/server/routes/git.ts +97 -0
  214. package/src/server/routes/settings.ts +1 -0
  215. package/src/server/routes/teams.ts +14 -3
  216. package/src/services/account-selector.service.ts +125 -20
  217. package/src/services/account.service.ts +176 -54
  218. package/src/services/autostart-generator.ts +12 -1
  219. package/src/services/codex-account.service.ts +87 -13
  220. package/src/services/db.service.ts +58 -3
  221. package/src/services/extension.service.ts +55 -5
  222. package/src/services/git-blame/blame-porcelain.ts +89 -0
  223. package/src/services/git-blame/git-blame.service.ts +90 -0
  224. package/src/services/git-hunks/git-hunks.service.ts +264 -0
  225. package/src/services/git-hunks/unified-diff.ts +359 -0
  226. package/src/services/picked-account-binding.ts +57 -0
  227. package/src/services/team-member-activity/member-transcript-tail.ts +64 -0
  228. package/src/shared/blame.ts +79 -0
  229. package/src/web/components/chat/chat-history-bar.tsx +34 -5
  230. package/src/web/components/chat/chat-tab.tsx +120 -3
  231. package/src/web/components/chat/codex-usage-panel.tsx +132 -14
  232. package/src/web/components/chat/edit-diff-preview.tsx +14 -7
  233. package/src/web/components/chat/team-member-window-content.tsx +8 -39
  234. package/src/web/components/chat/usage-badge.tsx +176 -52
  235. package/src/web/components/editor/code-editor.tsx +33 -1
  236. package/src/web/components/editor/diff-viewer.tsx +83 -8
  237. package/src/web/components/editor/editor-toolbar.tsx +25 -1
  238. package/src/web/components/git/git-status-panel.tsx +46 -0
  239. package/src/web/components/git/hunk-selection.ts +132 -0
  240. package/src/web/components/git/hunk-stage-dialog.tsx +419 -0
  241. package/src/web/components/settings/accounts/account-bucket-row.tsx +19 -3
  242. package/src/web/components/settings/accounts/account-card.tsx +153 -33
  243. package/src/web/components/settings/accounts/account-hint.tsx +33 -0
  244. package/src/web/components/settings/accounts/account-usage-format.ts +26 -0
  245. package/src/web/components/settings/accounts/codex-accounts-section.tsx +24 -9
  246. package/src/web/components/settings/accounts/codex-usage-rows.tsx +2 -2
  247. package/src/web/components/settings/accounts/use-accounts-data.ts +32 -2
  248. package/src/web/components/settings/accounts/use-codex-accounts.ts +24 -2
  249. package/src/web/components/shared/markdown-code-block.tsx +10 -5
  250. package/src/web/hooks/use-chat.ts +27 -0
  251. package/src/web/hooks/use-inline-blame.ts +175 -0
  252. package/src/web/hooks/use-member-transcript-tail.ts +95 -0
  253. package/src/web/lib/api-settings.ts +24 -0
  254. package/src/web/stores/settings-store.ts +12 -0
  255. package/dist/web/assets/accounts-settings-section-Cd1Nh04o.js +0 -1
  256. package/dist/web/assets/ai-settings-section-OaYGT5OF.js +0 -1
  257. package/dist/web/assets/api-client-BogqsFna.js +0 -1
  258. package/dist/web/assets/api-settings-BVfemzZW.js +0 -1
  259. package/dist/web/assets/architecture-PBZL5I3N-BZBxtErN.js +0 -1
  260. package/dist/web/assets/chat-tab-Cc1W2pTu.js +0 -15
  261. package/dist/web/assets/code-editor-gLRyCPWE.js +0 -7
  262. package/dist/web/assets/diff-viewer-ix1Ko0xk.js +0 -4
  263. package/dist/web/assets/explorer-window-content-B7HmteDI.js +0 -1
  264. package/dist/web/assets/gitGraph-HDMCJU4V-BHxJbXfV.js +0 -1
  265. package/dist/web/assets/group-chat-tab-DmZ1vLk-.js +0 -1
  266. package/dist/web/assets/index-N-pnuwFP.css +0 -2
  267. package/dist/web/assets/index-yV5RgBDh.js +0 -52
  268. package/dist/web/assets/info-3K5VOQVL-DI4p3vbH.js +0 -1
  269. package/dist/web/assets/keybindings-store-88_Zkn5F.js +0 -1
  270. package/dist/web/assets/markdown-renderer-UhszMWSn.js +0 -1
  271. package/dist/web/assets/mobile-explorer-sheet-D9cqcU3X.js +0 -1
  272. package/dist/web/assets/notification-store-B6X6RYqQ.js +0 -1
  273. package/dist/web/assets/packet-RMMSAZCW-YNQWTi_-.js +0 -1
  274. package/dist/web/assets/pie-UPGHQEXC-E3Wiw76n.js +0 -1
  275. package/dist/web/assets/radar-KQ55EAFF-BPQJUKON.js +0 -1
  276. package/dist/web/assets/remote-desktop-mobile-sheet-DefltAPl.js +0 -2
  277. package/dist/web/assets/schedules-settings-section-D76Ob6tI.js +0 -1
  278. package/dist/web/assets/settings-store-DWUx3865.js +0 -2
  279. package/dist/web/assets/settings-tab-DToHQXfC.js +0 -1
  280. package/dist/web/assets/settings-window-content-B2rlq-Dv.js +0 -1
  281. package/dist/web/assets/system-monitor-tab-CuDpwAZU.js +0 -1
  282. package/dist/web/assets/team-member-sheet-DLrT_Kxo.js +0 -2
  283. package/dist/web/assets/team-member-window-content-S8cdLcg3.js +0 -1
  284. package/dist/web/assets/tool-cards-CLxrQlUs.js +0 -9
  285. package/dist/web/assets/treemap-KZPCXAKY-C2CvKBQR.js +0 -1
  286. package/dist/web/assets/use-accounts-data-snYPJED_.js +0 -1
  287. package/dist/web/assets/ws-auth-ZSZkAT6p.js +0 -1
  288. /package/dist/web/assets/{abap-Cq9nYQyd.js → abap-D-o-bvtI.js} +0 -0
  289. /package/dist/web/assets/{actionscript-3-DL8rUIy0.js → actionscript-3-D2tIzAm8.js} +0 -0
  290. /package/dist/web/assets/{ada-BY8TLuhY.js → ada-DxatBcxS.js} +0 -0
  291. /package/dist/web/assets/{andromeeda-CyVfHsS0.js → andromeeda-D0LK9YJ3.js} +0 -0
  292. /package/dist/web/assets/{apache-DTQUy73x.js → apache-B7uMPNiW.js} +0 -0
  293. /package/dist/web/assets/{apex-CiSZngrZ.js → apex-B7GiXrAr.js} +0 -0
  294. /package/dist/web/assets/{api-client-oY1tC3tM.js → api-client-CEYCBt_0.js} +0 -0
  295. /package/dist/web/assets/{applescript-CzuRDd3h.js → applescript-D_SIFkKe.js} +0 -0
  296. /package/dist/web/assets/{apply-theme-r8tKDrCR.js → apply-theme-BVXAUYoN.js} +0 -0
  297. /package/dist/web/assets/{ara-CSFV93kR.js → ara-C3iECgdg.js} +0 -0
  298. /package/dist/web/assets/{asciidoc-hOQNnXhz.js → asciidoc-gc8z_MMY.js} +0 -0
  299. /package/dist/web/assets/{asm-DdG53NGR.js → asm-27qeoPOI.js} +0 -0
  300. /package/dist/web/assets/{aurora-x-DJ7ORZG6.js → aurora-x-C5J9fWe1.js} +0 -0
  301. /package/dist/web/assets/{awk-B3FFpX_J.js → awk-B7GBzF92.js} +0 -0
  302. /package/dist/web/assets/{ayu-dark-D_P1CfTe.js → ayu-dark-XlnQhuLL.js} +0 -0
  303. /package/dist/web/assets/{ayu-light-Cz3YLUqH.js → ayu-light-DKLLilkU.js} +0 -0
  304. /package/dist/web/assets/{ayu-mirage-JcMq5tgJ.js → ayu-mirage-Dv-XOprJ.js} +0 -0
  305. /package/dist/web/assets/{ballerina-7Hs4Rfpk.js → ballerina-DACRT-sT.js} +0 -0
  306. /package/dist/web/assets/{bat-COgQdnFA.js → bat-BlQCq2XE.js} +0 -0
  307. /package/dist/web/assets/{beancount-BJ3CutBG.js → beancount-DYDztRI0.js} +0 -0
  308. /package/dist/web/assets/{berry-D7so2Cui.js → berry-BE5YKqvx.js} +0 -0
  309. /package/dist/web/assets/{bibtex-Ct3acHa1.js → bibtex-CAm7mgEm.js} +0 -0
  310. /package/dist/web/assets/{bicep-jXDwCQcm.js → bicep-BKgAbfVa.js} +0 -0
  311. /package/dist/web/assets/{bird2-CjLD2cna.js → bird2-yrBNbW1G.js} +0 -0
  312. /package/dist/web/assets/{c-CdgZojnL.js → c-txZFr4RM.js} +0 -0
  313. /package/dist/web/assets/{c3-DtpZYego.js → c3-D_DrzBUd.js} +0 -0
  314. /package/dist/web/assets/{cadence-CPLkixoh.js → cadence-CUFCEgG9.js} +0 -0
  315. /package/dist/web/assets/{can-open-in-ppm-BntPywsy.js → can-open-in-ppm-_yqa69dV.js} +0 -0
  316. /package/dist/web/assets/{catppuccin-frappe-Xt0_qJa9.js → catppuccin-frappe-Dn32MuhA.js} +0 -0
  317. /package/dist/web/assets/{catppuccin-latte-CJnbLec5.js → catppuccin-latte-Dewo7yDf.js} +0 -0
  318. /package/dist/web/assets/{catppuccin-macchiato-BPHP1wYB.js → catppuccin-macchiato-D8WOWZeo.js} +0 -0
  319. /package/dist/web/assets/{catppuccin-mocha-ssWJodGc.js → catppuccin-mocha-b0md48XM.js} +0 -0
  320. /package/dist/web/assets/{clarity-DB0408F6.js → clarity-Ct3UqLBj.js} +0 -0
  321. /package/dist/web/assets/{clojure-dpRPlAO4.js → clojure-CwP5aPEo.js} +0 -0
  322. /package/dist/web/assets/{cmake-r2VkeX9y.js → cmake-D5RF49e5.js} +0 -0
  323. /package/dist/web/assets/{codeowners-v5nQqHiB.js → codeowners-Cl5897L_.js} +0 -0
  324. /package/dist/web/assets/{codeql-BSSEhKRR.js → codeql-BKh1RCg0.js} +0 -0
  325. /package/dist/web/assets/{common-lisp-BXb2MMMA.js → common-lisp-BhHuD6t0.js} +0 -0
  326. /package/dist/web/assets/{coq-BjBNi73X.js → coq-Dco3qwD8.js} +0 -0
  327. /package/dist/web/assets/{csharp-CdPjZRbV.js → csharp-Dpjw9ZA9.js} +0 -0
  328. /package/dist/web/assets/{css-BuxbQOHL.js → css-CPe4cabH.js} +0 -0
  329. /package/dist/web/assets/{csv-Dj-eoZwH.js → csv-BAcTN8Kt.js} +0 -0
  330. /package/dist/web/assets/{csv-parser-CBbALsFU.js → csv-parser-DbxGKdMG.js} +0 -0
  331. /package/dist/web/assets/{cue-Dz73VksN.js → cue-Cr6y60TA.js} +0 -0
  332. /package/dist/web/assets/{cypher-CkVkDywZ.js → cypher-DWtqXXFP.js} +0 -0
  333. /package/dist/web/assets/{d-oU51Pbpf.js → d-BASiTZk4.js} +0 -0
  334. /package/dist/web/assets/{dark-plus-DcwvjjfO.js → dark-plus-56kmnrcl.js} +0 -0
  335. /package/dist/web/assets/{dart-DGvu8mcX.js → dart-C5mZnjoE.js} +0 -0
  336. /package/dist/web/assets/{data-grid-types-B6kUrF-M.js → data-grid-types-DeY60CLU.js} +0 -0
  337. /package/dist/web/assets/{dax-BmbjKQ90.js → dax-B4I2e7d8.js} +0 -0
  338. /package/dist/web/assets/{desktop-IRBSiFfh.js → desktop-DekGg0BT.js} +0 -0
  339. /package/dist/web/assets/{diff-Bdik2JR8.js → diff-CWnJBd73.js} +0 -0
  340. /package/dist/web/assets/{dist-Bv4zQpS1.js → dist-B0J2eYBz.js} +0 -0
  341. /package/dist/web/assets/{dist-C8YPBIOb.js → dist-BKOM_AoT.js} +0 -0
  342. /package/dist/web/assets/{docker-Bgdl0H8I.js → docker-DevchDxK.js} +0 -0
  343. /package/dist/web/assets/{dotenv-COe5NdnA.js → dotenv-DCvtdaGJ.js} +0 -0
  344. /package/dist/web/assets/{dracula-DjURaSAa.js → dracula-8U6Vkk3l.js} +0 -0
  345. /package/dist/web/assets/{dracula-soft-Do7d0bo-.js → dracula-soft-Bfeew5Wg.js} +0 -0
  346. /package/dist/web/assets/{dream-maker-DYwQvJIj.js → dream-maker-DDTJVuoy.js} +0 -0
  347. /package/dist/web/assets/{emacs-lisp-BXjlHyDW.js → emacs-lisp-CEHJqI4m.js} +0 -0
  348. /package/dist/web/assets/{everforest-dark-h8Q2loiX.js → everforest-dark-CHHLt7cq.js} +0 -0
  349. /package/dist/web/assets/{everforest-light-CdsJiL-9.js → everforest-light-TcTbK1sM.js} +0 -0
  350. /package/dist/web/assets/{fennel-D0CBtOvY.js → fennel-Bh4vsA1A.js} +0 -0
  351. /package/dist/web/assets/{fish-DzayfrMj.js → fish-D0T0fILG.js} +0 -0
  352. /package/dist/web/assets/{fluent-CatjkUfx.js → fluent-CgXDXdyy.js} +0 -0
  353. /package/dist/web/assets/{format-bytes-BulRSULX.js → format-bytes-Bc6SqF7M.js} +0 -0
  354. /package/dist/web/assets/{fortran-free-form-DM04ud0b.js → fortran-free-form-CUg3Xhz4.js} +0 -0
  355. /package/dist/web/assets/{gdscript-BLN5iZwf.js → gdscript-Cx056p_k.js} +0 -0
  356. /package/dist/web/assets/{gdshader-BK-1iZQs.js → gdshader-Dj1qP4I5.js} +0 -0
  357. /package/dist/web/assets/{genie-BGPOFWDN.js → genie-SBt15wn7.js} +0 -0
  358. /package/dist/web/assets/{gherkin-CdVOJ_JY.js → gherkin-bTJiHIrB.js} +0 -0
  359. /package/dist/web/assets/{github-dark-B_xW8TaY.js → github-dark-BOPhyugs.js} +0 -0
  360. /package/dist/web/assets/{github-dark-default-h_iKq7CX.js → github-dark-default-Cktqe6NF.js} +0 -0
  361. /package/dist/web/assets/{github-dark-dimmed-nKDYE62T.js → github-dark-dimmed-CeWfII_B.js} +0 -0
  362. /package/dist/web/assets/{github-dark-high-contrast-l8I771QT.js → github-dark-high-contrast-CTx8LpdV.js} +0 -0
  363. /package/dist/web/assets/{github-light-DNhjkg8Z.js → github-light-DVsO5Un1.js} +0 -0
  364. /package/dist/web/assets/{github-light-default-DQ06uS3g.js → github-light-default-BiyPxicr.js} +0 -0
  365. /package/dist/web/assets/{github-light-high-contrast-qc2sZCVN.js → github-light-high-contrast-BPlnke0a.js} +0 -0
  366. /package/dist/web/assets/{gleam-DPPatkNH.js → gleam-CWSxvD0C.js} +0 -0
  367. /package/dist/web/assets/{gn-BnzGQTy5.js → gn-D7NUVJuv.js} +0 -0
  368. /package/dist/web/assets/{gnuplot-Ct1leoo3.js → gnuplot-c5uXmJKc.js} +0 -0
  369. /package/dist/web/assets/{go-qysCdbrn.js → go-CRsHi4Cw.js} +0 -0
  370. /package/dist/web/assets/{groovy-BkWIdrRQ.js → groovy-DpV0RA5r.js} +0 -0
  371. /package/dist/web/assets/{gruvbox-dark-hard-L6Iiybbl.js → gruvbox-dark-hard-DD9fguUN.js} +0 -0
  372. /package/dist/web/assets/{gruvbox-dark-medium-DbD3yl0b.js → gruvbox-dark-medium-BvcwpYJ-.js} +0 -0
  373. /package/dist/web/assets/{gruvbox-dark-soft-Ban2cHK3.js → gruvbox-dark-soft-48dCxX9M.js} +0 -0
  374. /package/dist/web/assets/{gruvbox-light-hard-CK_SjS3e.js → gruvbox-light-hard-CjJ994Ip.js} +0 -0
  375. /package/dist/web/assets/{gruvbox-light-medium-Bjgvhc2w.js → gruvbox-light-medium-DQJQZ_rV.js} +0 -0
  376. /package/dist/web/assets/{gruvbox-light-soft-EWnyYGUu.js → gruvbox-light-soft-AqsC9mKw.js} +0 -0
  377. /package/dist/web/assets/{haskell-rT1N9nYr.js → haskell-B5V7yvmh.js} +0 -0
  378. /package/dist/web/assets/{haxe-Bbc91axo.js → haxe-LWTKfPKr.js} +0 -0
  379. /package/dist/web/assets/{hcl-D-3qJA4v.js → hcl-QmNZdAtI.js} +0 -0
  380. /package/dist/web/assets/{hjson-DWtewcyQ.js → hjson-CboJsAzK.js} +0 -0
  381. /package/dist/web/assets/{hlsl-C7t6hCrO.js → hlsl-Bd-DO_WL.js} +0 -0
  382. /package/dist/web/assets/{horizon-zJAGUQ3j.js → horizon-CU8Bi_ss.js} +0 -0
  383. /package/dist/web/assets/{horizon-bright-CRNyrJy8.js → horizon-bright-iSQ3_3jP.js} +0 -0
  384. /package/dist/web/assets/{houston-BFKa4W1-.js → houston-CTXv3Puz.js} +0 -0
  385. /package/dist/web/assets/{hy-W1hNjMBM.js → hy-Cd4hTidD.js} +0 -0
  386. /package/dist/web/assets/{image-gallery-BycaVWOu.js → image-gallery-J8h8IY0c.js} +0 -0
  387. /package/dist/web/assets/{imba-CPphaV-V.js → imba-BipCHEY2.js} +0 -0
  388. /package/dist/web/assets/{ini-CayaZ4vW.js → ini-Dd1KkMrB.js} +0 -0
  389. /package/dist/web/assets/{java-BPAE_9Ej.js → java-rGr0OQD8.js} +0 -0
  390. /package/dist/web/assets/{javascript-C5SKIVqe.js → javascript-DHhCH2TN.js} +0 -0
  391. /package/dist/web/assets/{json-C-0EUYmu.js → json-CMMLBCUf.js} +0 -0
  392. /package/dist/web/assets/{json5-BqVetEZL.js → json5-mPOwTOsB.js} +0 -0
  393. /package/dist/web/assets/{jsonc-DnjIzkUB.js → jsonc-Bie0E6D3.js} +0 -0
  394. /package/dist/web/assets/{jsonl-rbV-3EqO.js → jsonl-CpqHmtC1.js} +0 -0
  395. /package/dist/web/assets/{jsonnet-9hvXw4qr.js → jsonnet-D21Ms51e.js} +0 -0
  396. /package/dist/web/assets/{jssm-1ifXyIhu.js → jssm-BXnl1nur.js} +0 -0
  397. /package/dist/web/assets/{jsx-hPumywTY.js → jsx-Chnii_vB.js} +0 -0
  398. /package/dist/web/assets/{kanagawa-dragon-BCPjmoMO.js → kanagawa-dragon-DhxR0UjL.js} +0 -0
  399. /package/dist/web/assets/{kanagawa-lotus-DN2VNjy7.js → kanagawa-lotus-fupRg5wp.js} +0 -0
  400. /package/dist/web/assets/{kanagawa-wave-B8RZEsgc.js → kanagawa-wave-DSWbRS9M.js} +0 -0
  401. /package/dist/web/assets/{kdl-CkvjWQIa.js → kdl-DirGHqUy.js} +0 -0
  402. /package/dist/web/assets/{kotlin-D8cnRi5o.js → kotlin-DkhUVMDC.js} +0 -0
  403. /package/dist/web/assets/{kusto-CmeeBEJG.js → kusto-DU7OpovF.js} +0 -0
  404. /package/dist/web/assets/{laserwave--rtV8Mv9.js → laserwave-iLKb1ZCT.js} +0 -0
  405. /package/dist/web/assets/{lean-CS-Pyi42.js → lean-Cuvh_QAP.js} +0 -0
  406. /package/dist/web/assets/{less-Ddi_7qGm.js → less-B5tQZOdI.js} +0 -0
  407. /package/dist/web/assets/{lib-DIrxSyLb.js → lib-el9rA30D.js} +0 -0
  408. /package/dist/web/assets/{libesm-CYLrAeW3.js → libesm-CdttTIAd.js} +0 -0
  409. /package/dist/web/assets/{light-plus-CrEjpkkX.js → light-plus-DiHU2KL5.js} +0 -0
  410. /package/dist/web/assets/{llvm-Cm7ZXmAO.js → llvm-sCBTKCKN.js} +0 -0
  411. /package/dist/web/assets/{log-lW2sYxtp.js → log-DtyR34Ie.js} +0 -0
  412. /package/dist/web/assets/{logo-B-PiRb53.js → logo-CP91aQyG.js} +0 -0
  413. /package/dist/web/assets/{luau-8St0pC-R.js → luau-nfGo2ulP.js} +0 -0
  414. /package/dist/web/assets/{make-DbNdhH54.js → make-XbDn9GHe.js} +0 -0
  415. /package/dist/web/assets/{markdown-CYocYSGd.js → markdown-BB1_3xLr.js} +0 -0
  416. /package/dist/web/assets/{material-theme-CVBF1Cbi.js → material-theme-CIx_sxAy.js} +0 -0
  417. /package/dist/web/assets/{material-theme-darker-HFrlNZD8.js → material-theme-darker-DsQeueDD.js} +0 -0
  418. /package/dist/web/assets/{material-theme-lighter-BM-uz2ft.js → material-theme-lighter-x_lMi74j.js} +0 -0
  419. /package/dist/web/assets/{material-theme-ocean-PbvUez8m.js → material-theme-ocean-Bkgh25P8.js} +0 -0
  420. /package/dist/web/assets/{material-theme-palenight-B22Ck6qd.js → material-theme-palenight-CoOjdbti.js} +0 -0
  421. /package/dist/web/assets/{matlab-GFFNzucw.js → matlab-D6xZas_8.js} +0 -0
  422. /package/dist/web/assets/{mdx-BVnJzMDx.js → mdx-Cl5_YNoz.js} +0 -0
  423. /package/dist/web/assets/{mermaid-BlCmSTCM.js → mermaid-8Eow4jz_.js} +0 -0
  424. /package/dist/web/assets/{min-dark-B0PwlAfC.js → min-dark-D4DhjgRE.js} +0 -0
  425. /package/dist/web/assets/{min-light-C_H2ABKu.js → min-light-DzAckd0t.js} +0 -0
  426. /package/dist/web/assets/{mipsasm-C3rf2fo6.js → mipsasm-zI_zu0tj.js} +0 -0
  427. /package/dist/web/assets/{mojo-CDRuzM3o.js → mojo-DQnGGPHf.js} +0 -0
  428. /package/dist/web/assets/{monokai-KiebNy_Y.js → monokai-CrFoD2VD.js} +0 -0
  429. /package/dist/web/assets/{moonbit-CdVUHoWl.js → moonbit-DdphIGs6.js} +0 -0
  430. /package/dist/web/assets/{move-Dq9iKZRY.js → move-BUlkn0xW.js} +0 -0
  431. /package/dist/web/assets/{narrat-7y5L5sGM.js → narrat-CbOBdtCn.js} +0 -0
  432. /package/dist/web/assets/{nextflow-groovy-Cwcbkcrx.js → nextflow-groovy-CeVLUTuQ.js} +0 -0
  433. /package/dist/web/assets/{night-owl-TBgLPzj4.js → night-owl-Bm4LhUR1.js} +0 -0
  434. /package/dist/web/assets/{night-owl-light-Dehb9My2.js → night-owl-light-DtcBiMEf.js} +0 -0
  435. /package/dist/web/assets/{nix-BKj-gzoX.js → nix-DrdwQ70g.js} +0 -0
  436. /package/dist/web/assets/{nord-BF_r8PeX.js → nord-B0Head1v.js} +0 -0
  437. /package/dist/web/assets/{nushell-CeAjk9_E.js → nushell-CmUdltKn.js} +0 -0
  438. /package/dist/web/assets/{objective-c-Xl1OMcDp.js → objective-c-BGDR33XN.js} +0 -0
  439. /package/dist/web/assets/{objective-cpp-C4ZgzrRq.js → objective-cpp-Buc1Zhd0.js} +0 -0
  440. /package/dist/web/assets/{ocaml-CGj8IYTq.js → ocaml-xdzl9rRD.js} +0 -0
  441. /package/dist/web/assets/{odin-C4veqvW2.js → odin-DiUFngnd.js} +0 -0
  442. /package/dist/web/assets/{one-dark-pro-gAcWejsV.js → one-dark-pro-BURxmIp3.js} +0 -0
  443. /package/dist/web/assets/{one-light-B7E-UUXy.js → one-light-BMRMInAj.js} +0 -0
  444. /package/dist/web/assets/{openscad-C-wN9BEv.js → openscad-DAgLA5J3.js} +0 -0
  445. /package/dist/web/assets/{pascal-C8WBtxMs.js → pascal-CoDZxcRB.js} +0 -0
  446. /package/dist/web/assets/{pkl-BBfF4G2A.js → pkl-DzKM6BuM.js} +0 -0
  447. /package/dist/web/assets/{plastic-DFOAb89P.js → plastic-vF15LmNS.js} +0 -0
  448. /package/dist/web/assets/{plsql-BS2CEMMV.js → plsql-C1v07teA.js} +0 -0
  449. /package/dist/web/assets/{po-BKJDtPmy.js → po-CLLC8BuA.js} +0 -0
  450. /package/dist/web/assets/{poimandres-Cg3hL13e.js → poimandres-Uq0xysxM.js} +0 -0
  451. /package/dist/web/assets/{polar-B9ebiXUU.js → polar-2-luY1Pc.js} +0 -0
  452. /package/dist/web/assets/{portal-container-context-a399Hd9F.js → portal-container-context-DKymYOY-.js} +0 -0
  453. /package/dist/web/assets/{postcss-B5REwNY_.js → postcss-lbFhe0rz.js} +0 -0
  454. /package/dist/web/assets/{powerquery-stc0Gzkf.js → powerquery-RL8O9Wie.js} +0 -0
  455. /package/dist/web/assets/{powershell-jgd0ahJN.js → powershell-upFWhb-b.js} +0 -0
  456. /package/dist/web/assets/{prisma-CWuZjRhI.js → prisma-DBtYaWFk.js} +0 -0
  457. /package/dist/web/assets/{prolog-D5zNFE63.js → prolog-D8jSMykJ.js} +0 -0
  458. /package/dist/web/assets/{proto-B-6RrU7n.js → proto-BM4eadF7.js} +0 -0
  459. /package/dist/web/assets/{puppet-DONP8TDR.js → puppet-BQDQ3Snu.js} +0 -0
  460. /package/dist/web/assets/{purescript-DDJITf-A.js → purescript-Cj2aTX4s.js} +0 -0
  461. /package/dist/web/assets/{python-bmWy9j1-.js → python-BYpqq6u4.js} +0 -0
  462. /package/dist/web/assets/{qmldir-CKDwMt7X.js → qmldir-4uiTcwAu.js} +0 -0
  463. /package/dist/web/assets/{qss-DptQQzBn.js → qss-D5xB-Mqo.js} +0 -0
  464. /package/dist/web/assets/{r-BorKpJ8r.js → r-P6Ujy8rf.js} +0 -0
  465. /package/dist/web/assets/{racket-BaHpYVsZ.js → racket-COKgCFuM.js} +0 -0
  466. /package/dist/web/assets/{raku-DFJIV4k_.js → raku-z14Q9i7K.js} +0 -0
  467. /package/dist/web/assets/{react-X4jUntEf.js → react-D1Yg256j.js} +0 -0
  468. /package/dist/web/assets/{red-70oJGrkb.js → red-ObAvgvQ5.js} +0 -0
  469. /package/dist/web/assets/{reg-C4g235Dv.js → reg-Qv4f0JFA.js} +0 -0
  470. /package/dist/web/assets/{regexp-Cw1UVmti.js → regexp-CEOVGIzy.js} +0 -0
  471. /package/dist/web/assets/{rel-DeLhIz5L.js → rel-cWOyTUwd.js} +0 -0
  472. /package/dist/web/assets/{resolve-theme-b_yGlUFQ.js → resolve-theme-CMp5xxCW.js} +0 -0
  473. /package/dist/web/assets/{riscv-ChnTiBRK.js → riscv-BZDwx-lv.js} +0 -0
  474. /package/dist/web/assets/{ron-C5IWeqDn.js → ron-BpjosPS0.js} +0 -0
  475. /package/dist/web/assets/{rose-pine-DCR0ohYW.js → rose-pine-DhxfJWcm.js} +0 -0
  476. /package/dist/web/assets/{rose-pine-dawn-DvGZs1PR.js → rose-pine-dawn-BbGt9Wy_.js} +0 -0
  477. /package/dist/web/assets/{rose-pine-moon-BWACHjeS.js → rose-pine-moon-dj4Zxgik.js} +0 -0
  478. /package/dist/web/assets/{rosmsg-CQlIVazE.js → rosmsg-EciOaHo2.js} +0 -0
  479. /package/dist/web/assets/{rust-DGGVzWI8.js → rust-C_eH59O_.js} +0 -0
  480. /package/dist/web/assets/{sass-bj1C0rFI.js → sass-Dos3R5lL.js} +0 -0
  481. /package/dist/web/assets/{scala-MU1tLKDi.js → scala-8lVKsTvf.js} +0 -0
  482. /package/dist/web/assets/{scheme-CeGXtv7t.js → scheme-Bd3VdaZj.js} +0 -0
  483. /package/dist/web/assets/{sdbl-D0HLSGKp.js → sdbl-C4Yrjlgh.js} +0 -0
  484. /package/dist/web/assets/{shellscript-DkYiDvh1.js → shellscript-OFZbPKZH.js} +0 -0
  485. /package/dist/web/assets/{slack-dark-YqeHiGWH.js → slack-dark-aL6vlxZs.js} +0 -0
  486. /package/dist/web/assets/{slack-ochin-9Oe0DljJ.js → slack-ochin-BKt1E7sL.js} +0 -0
  487. /package/dist/web/assets/{smalltalk-CaaxedR7.js → smalltalk-D6qVBL1U.js} +0 -0
  488. /package/dist/web/assets/{snazzy-light-C8s2FHNC.js → snazzy-light-BrWerT0t.js} +0 -0
  489. /package/dist/web/assets/{solarized-dark-0ryY6oN3.js → solarized-dark-CzxTJgVQ.js} +0 -0
  490. /package/dist/web/assets/{solarized-light-bltgZeNh.js → solarized-light-BecaDAnG.js} +0 -0
  491. /package/dist/web/assets/{solidity-DOf5HmLZ.js → solidity-C9t7pZ84.js} +0 -0
  492. /package/dist/web/assets/{splunk-D1u4WHDW.js → splunk-CtxfFKtF.js} +0 -0
  493. /package/dist/web/assets/{sql-G8uQZljX.js → sql-7FE_Duz0.js} +0 -0
  494. /package/dist/web/assets/{ssh-config-rbXvly7Q.js → ssh-config-3Fr37ACC.js} +0 -0
  495. /package/dist/web/assets/{stylus-Cii1iADy.js → stylus-BP4mE1sO.js} +0 -0
  496. /package/dist/web/assets/{swift-DpIgonN4.js → swift-0uqTFouD.js} +0 -0
  497. /package/dist/web/assets/{synthwave-84-DjGLzU2b.js → synthwave-84-DQaNM3DI.js} +0 -0
  498. /package/dist/web/assets/{system-verilog-_Z0M0Hs1.js → system-verilog-DUIVrZHZ.js} +0 -0
  499. /package/dist/web/assets/{systemd-BhyDCh9P.js → systemd-CtNo_WIh.js} +0 -0
  500. /package/dist/web/assets/{talonscript-CNchgCRD.js → talonscript-B0HXaCVT.js} +0 -0
  501. /package/dist/web/assets/{tasl-m--W7Pyr.js → tasl-Cb7RAl11.js} +0 -0
  502. /package/dist/web/assets/{tcl-gY-M_2BC.js → tcl-B79aC6l5.js} +0 -0
  503. /package/dist/web/assets/{terraform-CB6CzMwo.js → terraform-75jJDjpW.js} +0 -0
  504. /package/dist/web/assets/{tokyo-night-COh9oRYi.js → tokyo-night-Cz9Uh9qb.js} +0 -0
  505. /package/dist/web/assets/{toml-Bg2bsY-v.js → toml-CD6y9A-z.js} +0 -0
  506. /package/dist/web/assets/{tsv-DceGDiJC.js → tsv-CXJMbBrb.js} +0 -0
  507. /package/dist/web/assets/{tsx-EccDYa7i.js → tsx-BuYN0I2t.js} +0 -0
  508. /package/dist/web/assets/{turtle-C_l2JhvX.js → turtle-DWH-Rjse.js} +0 -0
  509. /package/dist/web/assets/{typescript-C1ytmMcr.js → typescript-BPd-hsrR.js} +0 -0
  510. /package/dist/web/assets/{typespec-De2ztzG5.js → typespec-B0wvVykx.js} +0 -0
  511. /package/dist/web/assets/{typst-DNcS7gSr.js → typst-DWG2c48-.js} +0 -0
  512. /package/dist/web/assets/{use-is-mobile-CYW3FJFE.js → use-is-mobile-D1weanbj.js} +0 -0
  513. /package/dist/web/assets/{use-visual-viewport-D9O48HQt.js → use-visual-viewport-Bfl3Bxxb.js} +0 -0
  514. /package/dist/web/assets/{utils-C9OjCrou.js → utils-IrnQujwR.js} +0 -0
  515. /package/dist/web/assets/{v-DSMUsdMO.js → v-Ch6E4Egd.js} +0 -0
  516. /package/dist/web/assets/{vala-BEazLpkO.js → vala-CGVKwbPx.js} +0 -0
  517. /package/dist/web/assets/{vb-CV9pEJzW.js → vb-BIEbSxyT.js} +0 -0
  518. /package/dist/web/assets/{vendor-xterm-cuGQmUjN.js → vendor-xterm-YQlzIWv2.js} +0 -0
  519. /package/dist/web/assets/{verilog-DhXkLVMk.js → verilog-A-3pYPad.js} +0 -0
  520. /package/dist/web/assets/{vesper-n4IltYq-.js → vesper-CK1naZAV.js} +0 -0
  521. /package/dist/web/assets/{vhdl-CePjFbvs.js → vhdl-CVawmgMg.js} +0 -0
  522. /package/dist/web/assets/{viml-ChndFsm5.js → viml-CiWjaqBY.js} +0 -0
  523. /package/dist/web/assets/{vitesse-black-BytVdvhC.js → vitesse-black-PYXW4-BO.js} +0 -0
  524. /package/dist/web/assets/{vitesse-dark-83Wech0N.js → vitesse-dark-BU5s4AuG.js} +0 -0
  525. /package/dist/web/assets/{vitesse-light-Xg4wbeMO.js → vitesse-light-DgkIT-N7.js} +0 -0
  526. /package/dist/web/assets/{vyper-eW1NC3FG.js → vyper-D8cRDPp3.js} +0 -0
  527. /package/dist/web/assets/{wasm-BucxKwBZ.js → wasm-C02wlRbP.js} +0 -0
  528. /package/dist/web/assets/{wasm-Bhucqu1z.js → wasm-ClFJgzmT.js} +0 -0
  529. /package/dist/web/assets/{wenyan-BJFBuXuH.js → wenyan-B8plxIr8.js} +0 -0
  530. /package/dist/web/assets/{wgsl-6z4FUyMK.js → wgsl-CVujtm3V.js} +0 -0
  531. /package/dist/web/assets/{wikitext-BoDU6teJ.js → wikitext-CmcG9B69.js} +0 -0
  532. /package/dist/web/assets/{wit-DerMPpgt.js → wit--gl2Jium.js} +0 -0
  533. /package/dist/web/assets/{wolfram-D78v0Ap-.js → wolfram-6T_1D-NS.js} +0 -0
  534. /package/dist/web/assets/{x-YDu4m2Iq.js → x-CxoBA0po.js} +0 -0
  535. /package/dist/web/assets/{yaml-C4TIgIw4.js → yaml-CIVV9uA1.js} +0 -0
  536. /package/dist/web/assets/{zenscript-BydW-dLC.js → zenscript-CoT4yNdA.js} +0 -0
  537. /package/dist/web/assets/{zig-b9lTqL5E.js → zig-BWs2Qo3G.js} +0 -0
@@ -0,0 +1,236 @@
1
+ /**
2
+ * Shared chrome for the panels added in v0.2.0 (blame, file history, compare,
3
+ * interactive rebase).
4
+ *
5
+ * The graph panel keeps its own self-contained HTML — it predates this shell and
6
+ * its CSS is tuned to the graph canvas. This shell only carries what the four
7
+ * newer panels have in common: the theme tokens, base typography, a toolbar, and
8
+ * the `acquireVsCodeApi` bridge. Webviews are sandboxed with `allow-scripts`
9
+ * only, so everything is inlined.
10
+ */
11
+
12
+ /** Theme tokens, kept in sync with the graph panel's palette. */
13
+ export const SHELL_CSS = `
14
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
15
+ :root {
16
+ --bg: #ffffff; --surface: #f4f4f5; --text: #09090b; --subtext: #71717a; --subtle: #a1a1aa;
17
+ --border: #e4e4e7; --border2: #d4d4d8; --blue: #3b82f6; --red: #ef4444; --green: #22c55e;
18
+ --yellow: #eab308; --purple: #8b5cf6; --orange: #f97316;
19
+ --surface-hover: #f4f4f5; --selected: #eff6ff;
20
+ }
21
+ @media (prefers-color-scheme: dark) {
22
+ :root {
23
+ --bg: #09090b; --surface: #18181b; --text: #fafafa; --subtext: #a1a1aa; --subtle: #52525b;
24
+ --border: #27272a; --border2: #3f3f46; --selected: #1e293b; --surface-hover: #27272a;
25
+ }
26
+ }
27
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: var(--bg); color: var(--text); font-size: 12px; overflow: hidden; height: 100vh; display: flex; flex-direction: column; }
28
+ #app { display: flex; flex-direction: column; height: 100vh; min-height: 0; }
29
+ code, .mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
30
+
31
+ #toolbar { display: flex; justify-content: space-between; align-items: center; gap: 8px; padding: 4px 10px; border-bottom: 1px solid var(--border); background: var(--surface); flex-shrink: 0; }
32
+ .toolbar-left, .toolbar-right { display: flex; align-items: center; gap: 4px; min-width: 0; }
33
+ .toolbar-title { font-weight: 600; font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
34
+ .toolbar-sub { color: var(--subtext); font-size: 11px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
35
+
36
+ button { background: transparent; color: var(--text); border: 1px solid var(--border); border-radius: 5px; padding: 3px 8px; font-size: 11px; cursor: pointer; min-width: 24px; min-height: 24px; transition: background 0.15s, border-color 0.15s; }
37
+ button:hover { background: var(--surface-hover); border-color: var(--border2); }
38
+ button:active { background: var(--surface); }
39
+ button:disabled { opacity: 0.5; cursor: default; }
40
+ button.primary { background: var(--blue); border-color: var(--blue); color: #fff; }
41
+ button.danger { border-color: var(--red); color: var(--red); }
42
+ select, input[type=text], input[type=search] { background: var(--bg); color: var(--text); border: 1px solid var(--border2); border-radius: 4px; padding: 3px 6px; font-size: 11px; outline: none; }
43
+ select:focus, input:focus { border-color: var(--blue); }
44
+
45
+ .scroll { flex: 1; overflow: auto; min-height: 0; }
46
+ .empty { display: flex; align-items: center; justify-content: center; height: 100%; color: var(--subtext); font-size: 12px; padding: 24px; text-align: center; }
47
+ .spinner { width: 16px; height: 16px; border: 2px solid var(--border2); border-top-color: var(--blue); border-radius: 50%; animation: spin 0.7s linear infinite; }
48
+ @keyframes spin { to { transform: rotate(360deg); } }
49
+ .banner { padding: 6px 10px; font-size: 11px; border-bottom: 1px solid var(--border); background: rgba(234,179,8,0.12); color: var(--text); display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
50
+ .banner.error { background: rgba(239,68,68,0.12); }
51
+ .hidden { display: none !important; }
52
+
53
+ /* Author chip — initials only. Rendering a real avatar would mean sending the
54
+ committer's email to a third-party service (gravatar), which a self-hosted
55
+ tool must not do silently. */
56
+ .avatar { width: 18px; height: 18px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; font-size: 8px; font-weight: 700; color: #fff; flex-shrink: 0; letter-spacing: -0.2px; }
57
+
58
+ /* Touch devices get taller rows — matches the graph panel's coarse-pointer rule */
59
+ @media (pointer: coarse) {
60
+ button { min-height: 32px; padding: 5px 10px; }
61
+ .row { min-height: 40px; }
62
+ }
63
+ `;
64
+
65
+ /** Deterministic author colour + initials, shared by every panel. */
66
+ export const AVATAR_JS = `
67
+ const AVATAR_COLORS = ['#3b82f6','#8b5cf6','#ec4899','#f97316','#22c55e','#14b8a6','#eab308','#ef4444','#6366f1','#0ea5e9'];
68
+ function authorInitials(name) {
69
+ const parts = String(name || '?').trim().split(/\\s+/).filter(Boolean);
70
+ if (!parts.length) return '?';
71
+ if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();
72
+ return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
73
+ }
74
+ function authorColor(key) {
75
+ const s = String(key || '');
76
+ let h = 0;
77
+ for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) | 0;
78
+ return AVATAR_COLORS[Math.abs(h) % AVATAR_COLORS.length];
79
+ }
80
+ function avatarHtml(name, email) {
81
+ return '<span class="avatar" style="background:' + authorColor(email || name) + '">' + escHtml(authorInitials(name)) + '</span>';
82
+ }
83
+ `;
84
+
85
+ /** HTML escaping + relative time, needed by every panel's renderer. */
86
+ export const SHELL_JS = `
87
+ const vscode = acquireVsCodeApi();
88
+ function escHtml(s) {
89
+ return String(s == null ? '' : s)
90
+ .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
91
+ .replace(/"/g, '&quot;').replace(/'/g, '&#39;');
92
+ }
93
+ function relTime(ts) {
94
+ const diff = Math.floor(Date.now() / 1000) - Number(ts);
95
+ if (!Number.isFinite(diff)) return '';
96
+ if (diff < 60) return 'just now';
97
+ if (diff < 3600) return Math.floor(diff / 60) + 'm ago';
98
+ if (diff < 86400) return Math.floor(diff / 3600) + 'h ago';
99
+ if (diff < 2592000) return Math.floor(diff / 86400) + 'd ago';
100
+ if (diff < 31536000) return Math.floor(diff / 2592000) + 'mo ago';
101
+ return Math.floor(diff / 31536000) + 'y ago';
102
+ }
103
+ function showError(message) {
104
+ const el = document.getElementById('banner');
105
+ if (!el) return;
106
+ el.textContent = message;
107
+ el.classList.remove('hidden');
108
+ el.classList.add('error');
109
+ }
110
+ function clearError() {
111
+ const el = document.getElementById('banner');
112
+ if (el) el.classList.add('hidden');
113
+ }
114
+ ${AVATAR_JS}
115
+ `;
116
+
117
+ /**
118
+ * Tracked-file picker, shared by the blame and file-history panels.
119
+ *
120
+ * The extension holds the full `git ls-files` list and filters it per keystroke,
121
+ * so the webview never receives a large repo's entire path list. The host panel
122
+ * supplies `onFileChosen` and forwards `loadFiles` to `applyFileList`.
123
+ */
124
+ export const FILE_PICKER_CSS = `
125
+ .picker { position: absolute; top: 34px; left: 8px; right: 8px; max-width: 620px; z-index: 40; background: var(--surface); border: 1px solid var(--border2); border-radius: 8px; box-shadow: 0 8px 24px rgba(0,0,0,0.25); display: flex; flex-direction: column; max-height: 60vh; }
126
+ .picker input { margin: 8px; }
127
+ .picker-list { overflow: auto; min-height: 0; }
128
+ .picker-item { padding: 6px 10px; cursor: pointer; font-family: ui-monospace, monospace; font-size: 11px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
129
+ .picker-item:hover, .picker-item.active { background: var(--selected); }
130
+ .picker-foot { padding: 6px 10px; border-top: 1px solid var(--border); color: var(--subtext); font-size: 10px; }
131
+ @media (pointer: coarse) { .picker-item { padding: 10px; } }
132
+ `;
133
+
134
+ export const FILE_PICKER_HTML = `
135
+ <div id="picker" class="picker hidden">
136
+ <input id="picker-input" type="search" placeholder="Filter tracked files…" autocomplete="off" />
137
+ <div id="picker-list" class="picker-list"></div>
138
+ <div class="picker-foot" id="picker-foot"></div>
139
+ </div>`;
140
+
141
+ export const FILE_PICKER_JS = `
142
+ const picker = {
143
+ root: document.getElementById('picker'),
144
+ input: document.getElementById('picker-input'),
145
+ list: document.getElementById('picker-list'),
146
+ foot: document.getElementById('picker-foot'),
147
+ files: [],
148
+ idx: 0,
149
+ timer: undefined,
150
+ };
151
+
152
+ function openFilePicker() {
153
+ picker.root.classList.remove('hidden');
154
+ picker.input.value = '';
155
+ picker.input.focus();
156
+ vscode.postMessage({ command: 'requestFiles', query: '' });
157
+ }
158
+ function closeFilePicker() { picker.root.classList.add('hidden'); }
159
+ function isFilePickerOpen() { return !picker.root.classList.contains('hidden'); }
160
+
161
+ function applyFileList(data) {
162
+ picker.files = data.files || [];
163
+ picker.idx = 0;
164
+ renderFilePicker();
165
+ picker.foot.textContent = picker.files.length + ' of ' + data.total + ' tracked files';
166
+ }
167
+
168
+ function renderFilePicker() {
169
+ picker.list.innerHTML = picker.files.map((f, i) =>
170
+ '<div class="picker-item' + (i === picker.idx ? ' active' : '') + '" data-file="' + escHtml(f) + '">' + escHtml(f) + '</div>'
171
+ ).join('') || '<div class="picker-foot">No tracked file matches.</div>';
172
+ }
173
+
174
+ picker.input.addEventListener('input', () => {
175
+ clearTimeout(picker.timer);
176
+ picker.timer = setTimeout(() => {
177
+ vscode.postMessage({ command: 'requestFiles', query: picker.input.value });
178
+ }, 120);
179
+ });
180
+
181
+ picker.input.addEventListener('keydown', (e) => {
182
+ if (e.key === 'Escape') { closeFilePicker(); return; }
183
+ if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
184
+ e.preventDefault();
185
+ picker.idx = Math.max(0, Math.min(picker.files.length - 1, picker.idx + (e.key === 'ArrowDown' ? 1 : -1)));
186
+ renderFilePicker();
187
+ const active = picker.list.querySelector('.picker-item.active');
188
+ if (active) active.scrollIntoView({ block: 'nearest' });
189
+ return;
190
+ }
191
+ if (e.key === 'Enter') {
192
+ const file = picker.files[picker.idx];
193
+ if (file) { closeFilePicker(); onFileChosen(file); }
194
+ }
195
+ });
196
+
197
+ picker.list.addEventListener('click', (e) => {
198
+ const item = e.target.closest('.picker-item');
199
+ if (item) { closeFilePicker(); onFileChosen(item.dataset.file); }
200
+ });
201
+ `;
202
+
203
+ export interface ShellOptions {
204
+ /** Toolbar markup (left/right groups). */
205
+ toolbar: string;
206
+ /** Panel body markup, placed inside `#app` below the toolbar. */
207
+ body: string;
208
+ /** Panel-specific CSS appended after the shell CSS. */
209
+ css?: string;
210
+ /** Panel-specific JS appended after the shell JS. */
211
+ script: string;
212
+ }
213
+
214
+ export function shellHtml(options: ShellOptions): string {
215
+ return `<!DOCTYPE html>
216
+ <html>
217
+ <head>
218
+ <meta charset="utf-8">
219
+ <style>
220
+ ${SHELL_CSS}
221
+ ${options.css ?? ""}
222
+ </style>
223
+ </head>
224
+ <body>
225
+ <div id="app">
226
+ <div id="toolbar">${options.toolbar}</div>
227
+ <div id="banner" class="banner hidden"></div>
228
+ ${options.body}
229
+ </div>
230
+ <script>
231
+ ${SHELL_JS}
232
+ ${options.script}
233
+ </script>
234
+ </body>
235
+ </html>`;
236
+ }
@@ -812,6 +812,13 @@ export class ClaudeAgentSdkProvider implements AIProvider {
812
812
  if (oldCount != null) { this.messageCount.set(newId, oldCount); this.messageCount.delete(sessionId); }
813
813
  const oldStream = this.streamingSessions.get(sessionId);
814
814
  if (oldStream) { this.streamingSessions.set(newId, oldStream); this.streamingSessions.delete(sessionId); }
815
+ // The account binding lives in the database under the old id, and the lookup below
816
+ // runs after this block — so without moving it, a session that migrates silently
817
+ // loses whichever account was chosen for it and gets re-routed to another one. That
818
+ // costs a full prompt-cache write, and when the choice was the user's it also
819
+ // quietly overrides them.
820
+ const boundAccount = getSessionAccount(sessionId);
821
+ if (boundAccount) accountSelector.bindSession(newId, boundAccount);
815
822
  yield { type: "session_migrated" as const, oldSessionId: sessionId, newSessionId: newId };
816
823
  sessionId = newId;
817
824
  }
@@ -106,6 +106,26 @@ interface LiveSession {
106
106
  compactRequested?: boolean;
107
107
  /** Token counts from the most recent usage notification, attached to `done`. */
108
108
  lastUsage?: import("../../shared/turn-usage.ts").TurnUsage;
109
+ /** A turn is running on the thread. Codex accepts exactly one at a time: a
110
+ * second `turn/start` resolves with the ALREADY-RUNNING turn (same id,
111
+ * status `inProgress`) and silently discards the new input — no error, so
112
+ * nothing downstream can notice. Follow-ups therefore wait in `pendingTurns`
113
+ * and are sent when the running turn reports `turn/completed`. */
114
+ turnInFlight?: boolean;
115
+ /** Id of the running turn — `turn/interrupt` needs it, `threadId` alone is rejected. */
116
+ activeTurnId?: string | null;
117
+ /** Interrupt asked for before `turn/started` named the turn; fired on arrival. */
118
+ interruptRequested?: boolean;
119
+ /** Follow-ups waiting for the current turn, in the order they will be sent. */
120
+ pendingTurns: QueuedTurn[];
121
+ }
122
+
123
+ /** A follow-up held back because a turn was already running. */
124
+ interface QueuedTurn {
125
+ message: string;
126
+ opts?: SendMessageOpts;
127
+ /** `later` sinks to the end of the queue; `now`/`next` go in front of it. */
128
+ priority: "now" | "next" | "later";
109
129
  }
110
130
 
111
131
  interface EventChannel {
@@ -310,6 +330,15 @@ export class CodexAppServerProvider implements AIProvider {
310
330
 
311
331
  private startTurn(live: LiveSession, message: string, opts?: SendMessageOpts): void {
312
332
  if (!live.threadId) return;
333
+ // One turn at a time — anything sent now would be dropped without a trace.
334
+ // `now` additionally cuts the running turn short so this one answers next.
335
+ if (live.turnInFlight) {
336
+ const priority = opts?.priority ?? "next";
337
+ this.enqueueTurn(live, { message, opts, priority });
338
+ if (priority === "now") this.interruptActiveTurn(live);
339
+ return;
340
+ }
341
+ live.turnInFlight = true;
313
342
  // `/compact` → trigger codex's real compaction (thread/compact/start), not a text turn.
314
343
  // Completion is surfaced on the `thread/compacted` notification (handleNotification).
315
344
  if (message.trim() === "/compact") {
@@ -318,6 +347,7 @@ export class CodexAppServerProvider implements AIProvider {
318
347
  live.compactRequested = false;
319
348
  if (!live.client.isClosed) live.channel.push({ type: "error", message: redactTruncate((err as Error)?.message ?? String(err), 256) });
320
349
  live.channel.push({ type: "done", sessionId: live.threadId ?? "", resultSubtype: "error_during_execution" });
350
+ this.endTurn(live);
321
351
  });
322
352
  return;
323
353
  }
@@ -335,11 +365,55 @@ export class CodexAppServerProvider implements AIProvider {
335
365
  threadId: live.threadId,
336
366
  input,
337
367
  ...(turnModel ? { model: turnModel } : {}),
368
+ }).then((res) => {
369
+ // Backup for the `turn/started` notification, which is what normally names
370
+ // the turn — an interrupt arriving in between has nothing to address.
371
+ const id = (res as { turn?: { id?: string } })?.turn?.id;
372
+ if (id && !live.activeTurnId) this.setActiveTurn(live, id);
338
373
  }).catch((err) => {
339
374
  if (!live.client.isClosed) live.channel.push({ type: "error", message: redactTruncate((err as Error)?.message ?? String(err), 256) });
375
+ // No turn is running, so nothing will report `turn/completed` — release the
376
+ // queue here or every later follow-up waits on a turn that never existed.
377
+ this.endTurn(live);
340
378
  });
341
379
  }
342
380
 
381
+ /** Insert a follow-up: `later` sinks behind everything, `now`/`next` queue ahead of it. */
382
+ private enqueueTurn(live: LiveSession, item: QueuedTurn): void {
383
+ if (item.priority === "later") {
384
+ live.pendingTurns.push(item);
385
+ return;
386
+ }
387
+ const firstLater = live.pendingTurns.findIndex((q) => q.priority === "later");
388
+ if (firstLater === -1) live.pendingTurns.push(item);
389
+ else live.pendingTurns.splice(firstLater, 0, item);
390
+ }
391
+
392
+ /** Cut the running turn short. Falls back to waiting it out when the interrupt
393
+ * cannot be sent — the follow-up stays queued either way, never dropped. */
394
+ private interruptActiveTurn(live: LiveSession): void {
395
+ if (!live.activeTurnId) { live.interruptRequested = true; return; }
396
+ live.client.request("turn/interrupt", { threadId: live.threadId, turnId: live.activeTurnId })
397
+ .catch(() => { /* turn already ending — the queue drains on turn/completed */ });
398
+ }
399
+
400
+ private setActiveTurn(live: LiveSession, turnId: string): void {
401
+ live.activeTurnId = turnId;
402
+ if (live.interruptRequested) {
403
+ live.interruptRequested = false;
404
+ this.interruptActiveTurn(live);
405
+ }
406
+ }
407
+
408
+ /** Running turn is over: send the next follow-up, if one is waiting. */
409
+ private endTurn(live: LiveSession): void {
410
+ live.turnInFlight = false;
411
+ live.activeTurnId = null;
412
+ live.interruptRequested = false;
413
+ const next = live.pendingTurns.shift();
414
+ if (next) this.startTurn(live, next.message, next.opts);
415
+ }
416
+
343
417
  private async connect(sessionId: string, opts?: SendMessageOpts): Promise<LiveSession> {
344
418
  const meta = this.sessions.get(sessionId);
345
419
  const cwd = meta?.projectPath || getSessionProjectPath(sessionId) || process.cwd();
@@ -352,6 +426,7 @@ export class CodexAppServerProvider implements AIProvider {
352
426
  client, threadId: null, cwd, channel, permission, model,
353
427
  pendingApprovals: new Map(), answeredCodexIds: new Set(),
354
428
  history: [], transcript: [], currentAssistant: "", currentEvents: [],
429
+ pendingTurns: [],
355
430
  };
356
431
  this.live.set(sessionId, live);
357
432
 
@@ -434,6 +509,14 @@ export class CodexAppServerProvider implements AIProvider {
434
509
  }
435
510
  live.currentAssistant = "";
436
511
  live.currentEvents = [];
512
+ this.endTurn(live);
513
+ }
514
+ if (notif.method === "turn/started") {
515
+ // Codex is the authority on whether the thread is busy — take the flag from
516
+ // it rather than only from what this side believes it started.
517
+ live.turnInFlight = true;
518
+ const id = (notif.params as { turn?: { id?: string } })?.turn?.id;
519
+ if (id) this.setActiveTurn(live, id);
437
520
  }
438
521
  // Manual /compact finished — codex signals via the contextCompaction item
439
522
  // (the thread/compacted notification is deprecated). Surface the compact-summary
@@ -724,7 +807,12 @@ export class CodexAppServerProvider implements AIProvider {
724
807
 
725
808
  // Managed accounts are assigned on first send; don't show an unrelated
726
809
  // ambient login while that assignment is pending or the binding is missing.
727
- if (accountId || listCodexAccounts().length > 0) return {};
810
+ //
811
+ // Disabled ones do not count towards "managed accounts exist": with every one switched
812
+ // off the selector returns null and chats really do run on the ambient ~/.codex login,
813
+ // so that is the login whose usage belongs here.
814
+ const selectable = listCodexAccounts().filter((a) => a.status !== "disabled");
815
+ if (accountId || selectable.length > 0) return {};
728
816
  return getOrFetchUsage(this.id, AMBIENT_ACCOUNT_KEY);
729
817
  }
730
818
  }
@@ -952,7 +952,7 @@ if (process.argv.includes("__serve__")) {
952
952
  });
953
953
 
954
954
  // Start background account token refresh in daemon child
955
- import("../services/account.service.ts").then(({ accountService }) => accountService.startAutoRefresh()).catch(() => {});
955
+ import("../services/account.service.ts").then(({ accountService }) => accountService.startAccountMaintenance()).catch(() => {});
956
956
 
957
957
  // Start background usage limit polling (every 5 min)
958
958
  import("../services/claude-usage.service.ts").then(({ startUsagePolling }) => startUsagePolling()).catch(() => {});
@@ -32,6 +32,9 @@ accountsRoutes.get("/", (c) => {
32
32
  const accounts = accountService.list().map((acc) => ({
33
33
  ...acc,
34
34
  hasRefreshToken: accountService.hasRefreshToken(acc.id),
35
+ // When this account's sign-in stops being valid, so the card can warn instead of
36
+ // waiting for the turn that fails. Null where PPM never recorded the grant.
37
+ grantExpiresAt: accountService.grantExpiresAt(acc),
35
38
  }));
36
39
  return c.json(ok(accounts));
37
40
  });
@@ -44,6 +47,25 @@ accountsRoutes.get("/active", (c) => {
44
47
  return c.json(ok(account));
45
48
  });
46
49
 
50
+ /**
51
+ * POST /api/accounts/pick — claim the account that will serve a new chat tab.
52
+ *
53
+ * POST, not GET, because this consumes a pick: round-robin advances its cursor, so asking
54
+ * twice is meant to hand back two different accounts. That is the point — a tab asks once
55
+ * when it opens, keeps the answer for as long as it lives, and several tabs opened in a row
56
+ * spread across the pool instead of piling onto whichever account happens to be next.
57
+ *
58
+ * Unlike `/active`, which only previews, the answer here is the one the first message will
59
+ * actually run on: the client sends the id back when it creates the session, and it becomes
60
+ * that session's binding. Null means nothing is usable right now, and the caller must say so
61
+ * rather than showing a stale name.
62
+ */
63
+ accountsRoutes.post("/pick", (c) => {
64
+ const picked = accountSelector.next();
65
+ if (!picked) return c.json(ok(null));
66
+ return c.json(ok({ id: picked.id, label: picked.label ?? picked.email ?? null }));
67
+ });
68
+
47
69
  /** GET /api/accounts/settings */
48
70
  accountsRoutes.get("/settings", (c) => {
49
71
  return c.json(ok({
@@ -389,7 +411,9 @@ accountsRoutes.patch("/:id", async (c) => {
389
411
  // Only for an account that has a refresh token: a temporary one has nothing to prove,
390
412
  // and setEnabled()'s own guard says something more useful than a refresh error would.
391
413
  if (wasParked && accountService.hasRefreshToken(id)) {
392
- const proof = await accountService.ensureFreshTokenChecked(id);
414
+ // Enabling is a deliberate gesture, so it re-tests a grant PPM has already
415
+ // written off — the check exists to catch a rejection, not to trust a stored one.
416
+ const proof = await accountService.ensureFreshTokenChecked(id, { retryRejected: true });
393
417
  if (!proof.account) {
394
418
  // Worth splitting: invalid_grant means the token is gone and only a fresh sign-in
395
419
  // brings it back, while a network drop or a 429 is worth trying again in a minute.
@@ -13,6 +13,7 @@ import { ensureSdkCommands, invalidateSdkCommands } from "../../services/slash-d
13
13
  import { upsertSlashRecent, getSlashRecents, setSessionClearedFrom, listTurnUsage, getSessionAccount, getSessionProvider, resolveMigratedSession } from "../../services/db.service.ts";
14
14
  import type { TurnUsage } from "../../shared/turn-usage.ts";
15
15
  import { getCachedUsage, refreshUsageNow } from "../../services/claude-usage.service.ts";
16
+ import { bindPickedAccount, bindRefusalReason } from "../../services/picked-account-binding.ts";
16
17
  import { getSessionLog } from "../../services/session-log.service.ts";
17
18
  import { parseJsonlTranscript, validateJsonlPath } from "../../services/jsonl-transcript-parser.ts";
18
19
  import { aggregateTasks } from "../../services/task-status-aggregator.ts";
@@ -361,13 +362,19 @@ chatRoutes.post("/sessions", async (c) => {
361
362
  try {
362
363
  const projectName = c.get("projectName");
363
364
  const projectPath = c.get("projectPath");
364
- const body = await c.req.json<{ providerId?: string; title?: string; clearedFrom?: string }>();
365
+ const body = await c.req.json<{ providerId?: string; title?: string; clearedFrom?: string; accountId?: string }>();
365
366
  const session = await chatService.createSession(body.providerId, {
366
367
  projectName,
367
368
  projectPath,
368
369
  title: body.title,
369
370
  });
370
371
  if (body.clearedFrom) setSessionClearedFrom(session.id, body.clearedFrom);
372
+ // The tab claimed an account when it opened and showed its name; honour that here so
373
+ // the first message runs on the account the user was actually looking at. Advisory,
374
+ // never authoritative: bindPickedAccount re-checks the id against the server's own
375
+ // pool and simply declines an id it does not recognise, because this arrives from a
376
+ // client and selecting a token by client-supplied id is not something to allow.
377
+ if (body.accountId) bindPickedAccount(session.id, session.providerId, body.accountId);
371
378
  // Auto-assign default tag if project has one
372
379
  const defaultTagId = getProjectDefaultTagId(projectPath);
373
380
  if (defaultTagId) setSessionTag(session.id, defaultTagId, projectPath);
@@ -473,6 +480,27 @@ chatRoutes.patch("/sessions/:id", async (c) => {
473
480
  }
474
481
  });
475
482
 
483
+ /**
484
+ * PUT /chat/sessions/:id/account — move a live session onto an account the user picked.
485
+ *
486
+ * Unlike the same choice made at session creation, this one answers: the user is looking at
487
+ * a panel and pressed something, so a silently ignored pick would read as a broken button.
488
+ *
489
+ * Worth knowing before calling: the prompt cache is scoped per account, so moving a session
490
+ * mid-conversation re-sends its whole transcript as a cache write rather than a read. On a
491
+ * long session that is a real cost, not a formality.
492
+ */
493
+ chatRoutes.put("/sessions/:id/account", async (c) => {
494
+ const sessionId = c.req.param("id");
495
+ const body = await c.req.json<{ accountId?: string }>().catch(() => ({} as { accountId?: string }));
496
+ if (!body.accountId) return c.json(err("accountId is required"), 400);
497
+ const providerId = getSessionProvider(sessionId) ?? "claude";
498
+ if (!bindPickedAccount(sessionId, providerId, body.accountId)) {
499
+ return c.json(err(bindRefusalReason(providerId, body.accountId)), 400);
500
+ }
501
+ return c.json(ok({ accountId: body.accountId }));
502
+ });
503
+
476
504
  /** PUT /chat/sessions/:id/pin — pin a session */
477
505
  chatRoutes.put("/sessions/:id/pin", (c) => {
478
506
  try {
@@ -1,6 +1,6 @@
1
1
  import { Hono } from "hono";
2
2
  import { ok, err } from "../../types/api.ts";
3
- import { listCodexAccounts, removeCodexAccount, getAllCodexUsages, getCodexStrategy, setCodexStrategy, type CodexStrategy } from "../../services/codex-account.service.ts";
3
+ import { listCodexAccounts, removeCodexAccount, getAllCodexUsages, getCodexStrategy, setCodexStrategy, selectCodexAccount, setCodexAccountStatus, type CodexStrategy } from "../../services/codex-account.service.ts";
4
4
  import { addApiKeyAccount, startDeviceLogin, getDeviceLoginStatus, cancelDeviceLogin } from "../../services/codex-account-login.ts";
5
5
  import { exportCodexEncrypted, importCodexEncrypted } from "../../services/codex-account-portability.ts";
6
6
 
@@ -12,6 +12,46 @@ codexAccountsRoutes.get("/", (c) => c.json(ok({ accounts: listCodexAccounts(), s
12
12
  /** Per-account quota map { [accountId]: UsageInfo }. */
13
13
  codexAccountsRoutes.get("/usage", async (c) => c.json(ok(await getAllCodexUsages())));
14
14
 
15
+ /**
16
+ * POST /api/codex-accounts/pick — claim the account that will serve a new chat tab.
17
+ *
18
+ * Mirrors the Claude side: a consuming pick, so round-robin advances and consecutive tabs
19
+ * land on different accounts. Usage is read once here and handed to the selector, which is
20
+ * what lets it skip accounts with no five-hour room left — the synchronous `peekCodexAccount`
21
+ * has no way to reach that data, which is why the toolbar could never name an account before
22
+ * the first turn under round-robin.
23
+ *
24
+ * Null when no account is managed: chats then run on the ambient ~/.codex login, which has
25
+ * no id to bind and nothing to choose between.
26
+ */
27
+ codexAccountsRoutes.post("/pick", async (c) => {
28
+ if (listCodexAccounts().length === 0) return c.json(ok(null));
29
+ const usages = await getAllCodexUsages();
30
+ // A failed usage fetch yields {} → +Infinity, which the selector reads as "unknown", not
31
+ // as "capped": an account we could not measure stays a candidate.
32
+ const picked = selectCodexAccount({ usageOf: (id) => usages[id]?.fiveHour ?? Number.POSITIVE_INFINITY });
33
+ if (!picked) return c.json(ok(null));
34
+ return c.json(ok({ id: picked.id, label: picked.label }));
35
+ });
36
+
37
+ /**
38
+ * PATCH /api/codex-accounts/:id — switch an account on or off.
39
+ *
40
+ * Mirrors the shape of the Claude route so the two panels can share one control, but not its
41
+ * pre-flight token check: that exists for Anthropic OAuth refresh tokens, which have nothing
42
+ * to do with how a Codex account authenticates. Nothing to prove here means this answers
43
+ * immediately instead of taking the better part of a minute.
44
+ */
45
+ codexAccountsRoutes.patch("/:id", async (c) => {
46
+ const body = await c.req.json<{ status?: string }>().catch(() => ({} as { status?: string }));
47
+ if (body.status !== "active" && body.status !== "disabled") {
48
+ return c.json(err("status must be active or disabled"), 400);
49
+ }
50
+ const updated = setCodexAccountStatus(c.req.param("id"), body.status);
51
+ if (!updated) return c.json(err("Account not found"), 404);
52
+ return c.json(ok(updated));
53
+ });
54
+
15
55
  /** Set the selection strategy. */
16
56
  codexAccountsRoutes.put("/strategy", async (c) => {
17
57
  const body = await c.req.json<{ strategy?: CodexStrategy }>().catch(() => ({} as { strategy?: CodexStrategy }));