@hienlh/ppm 0.19.12 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (535) hide show
  1. package/CHANGELOG.md +31 -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-BQyru31C.js +1 -0
  6. package/dist/web/assets/{ai-resource-editor-CqL-X2zu.js → ai-resource-editor-BgYIPGVP.js} +1 -1
  7. package/dist/web/assets/{ai-settings-section-CcqokdzF.js → ai-settings-section-Cywmtnps.js} +1 -1
  8. package/dist/web/assets/ai-settings-section-iBRCQvBR.js +1 -0
  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-mhu9T6Fk.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-Bs9OD6ss.js +15 -0
  24. package/dist/web/assets/{cobol-Bo0usdoM.js → cobol-BreMVrtc.js} +1 -1
  25. package/dist/web/assets/code-editor-Cv_pzHo7.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-BnWaMSEa.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-DG4YAabr.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-B-wA2qT-.js} +1 -1
  34. package/dist/web/assets/diff-viewer-DRtzdfXk.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-6tzdk7Pn.js} +1 -1
  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-uh8oJp6J.js} +2 -2
  44. package/dist/web/assets/explorer-window-content-DLn_4egF.js +1 -0
  45. package/dist/web/assets/{extension-manager-section-Zc9yag2c.js → extension-manager-section-C_ZGYvWf.js} +1 -1
  46. package/dist/web/assets/{extension-webview-B5FayDPz.js → extension-webview-DbFWxJpj.js} +1 -1
  47. package/dist/web/assets/{files-settings-section-CxAtO3GL.js → files-settings-section-BrUbJglA.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-B9ZPZVso.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-C0jnhmgC.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-CZVKIum-.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-DnPtmbPc.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-CHQAPZIX.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-Bjldyhru.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-DBVXZKp8.js +1 -0
  80. package/dist/web/assets/{keyboard-shortcuts-section-Dv4e3M8h.js → keyboard-shortcuts-section-D7jaCPrv.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-B4B5oxQS.js} +3 -3
  85. package/dist/web/assets/markdown-renderer-DwHh-jd_.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-JJxZYWkV.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-CCWNUHsS.js +1 -0
  95. package/dist/web/assets/{notifications-settings-section-B2GkfB0l.js → notifications-settings-section-DbbX7-Qp.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-DxNjWa1v.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-BScvk5ev.js} +3 -3
  105. package/dist/web/assets/{ppmbot-settings-section-OYfVG5-l.js → ppmbot-settings-section-jFA9Ncgo.js} +1 -1
  106. package/dist/web/assets/{process-table-DRu3NyO_.js → process-table-BexeYWQm.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-C_seKq56.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-BWXJmPDV.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-B--PeL5i.js +2 -0
  115. package/dist/web/assets/{remote-desktop-mobile-view-B4BSHIxU.js → remote-desktop-mobile-view-jgn8nmQa.js} +1 -1
  116. package/dist/web/assets/{remote-desktop-readiness-gate-C0d0PSX4.js → remote-desktop-readiness-gate-CjDth-W_.js} +1 -1
  117. package/dist/web/assets/{remote-desktop-window-content-DjeCZzqq.js → remote-desktop-window-content-CrHAUA03.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-DMFn3mYL.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-Dz2vPTEP.js} +2 -2
  127. package/dist/web/assets/settings-store-CzLnTR8p.js +2 -0
  128. package/dist/web/assets/settings-tab-B5QbsvS4.js +1 -0
  129. package/dist/web/assets/settings-window-content-iM2bPoX5.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-C0mGgLfX.js} +1 -1
  135. package/dist/web/assets/{sqlite-viewer-CpUhDEhN.js → sqlite-viewer-Bs3RKf1X.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-DvUmEdNe.js} +2 -2
  141. package/dist/web/assets/system-monitor-tab-CHZ5F_ZE.js +1 -0
  142. package/dist/web/assets/{system-monitor-window-content-DIIP3dff.js → system-monitor-window-content-DjzqoeCN.js} +1 -1
  143. package/dist/web/assets/{tab-host-window-content-CLdcz84K.js → tab-host-window-content-DKeAr5mD.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-Cl9ZB3dl.js +2 -0
  146. package/dist/web/assets/team-member-window-content-DfP7WdFD.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-J1Fqa_rm.js} +2 -2
  149. package/dist/web/assets/{tex-C8XwrbBw.js → tex-IQ8mELOR.js} +1 -1
  150. package/dist/web/assets/tool-cards-DQghSuKX.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-KWcMgRlc.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-aa4-wjKQ.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-B-TpQ-Ua.js} +1 -1
  160. package/dist/web/assets/{use-websocket-kR1omKO1.js → use-websocket-BBslmv3D.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-Bd0lcK3d.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/team-member-window-content.tsx +8 -39
  233. package/src/web/components/chat/usage-badge.tsx +176 -52
  234. package/src/web/components/editor/code-editor.tsx +33 -1
  235. package/src/web/components/editor/diff-viewer.tsx +83 -8
  236. package/src/web/components/editor/editor-toolbar.tsx +25 -1
  237. package/src/web/components/git/git-status-panel.tsx +46 -0
  238. package/src/web/components/git/hunk-selection.ts +132 -0
  239. package/src/web/components/git/hunk-stage-dialog.tsx +419 -0
  240. package/src/web/components/settings/accounts/account-bucket-row.tsx +19 -3
  241. package/src/web/components/settings/accounts/account-card.tsx +153 -33
  242. package/src/web/components/settings/accounts/account-hint.tsx +33 -0
  243. package/src/web/components/settings/accounts/account-usage-format.ts +26 -0
  244. package/src/web/components/settings/accounts/codex-accounts-section.tsx +24 -9
  245. package/src/web/components/settings/accounts/codex-usage-rows.tsx +2 -2
  246. package/src/web/components/settings/accounts/use-accounts-data.ts +32 -2
  247. package/src/web/components/settings/accounts/use-codex-accounts.ts +24 -2
  248. package/src/web/hooks/use-chat.ts +27 -0
  249. package/src/web/hooks/use-inline-blame.ts +175 -0
  250. package/src/web/hooks/use-member-transcript-tail.ts +95 -0
  251. package/src/web/lib/api-settings.ts +24 -0
  252. package/src/web/stores/settings-store.ts +12 -0
  253. package/dist/web/assets/accounts-settings-section-Cd1Nh04o.js +0 -1
  254. package/dist/web/assets/ai-settings-section-OaYGT5OF.js +0 -1
  255. package/dist/web/assets/api-client-BogqsFna.js +0 -1
  256. package/dist/web/assets/api-settings-BVfemzZW.js +0 -1
  257. package/dist/web/assets/architecture-PBZL5I3N-BZBxtErN.js +0 -1
  258. package/dist/web/assets/chat-tab-Cc1W2pTu.js +0 -15
  259. package/dist/web/assets/code-editor-gLRyCPWE.js +0 -7
  260. package/dist/web/assets/diff-viewer-ix1Ko0xk.js +0 -4
  261. package/dist/web/assets/explorer-window-content-B7HmteDI.js +0 -1
  262. package/dist/web/assets/gitGraph-HDMCJU4V-BHxJbXfV.js +0 -1
  263. package/dist/web/assets/group-chat-tab-DmZ1vLk-.js +0 -1
  264. package/dist/web/assets/index-N-pnuwFP.css +0 -2
  265. package/dist/web/assets/index-yV5RgBDh.js +0 -52
  266. package/dist/web/assets/info-3K5VOQVL-DI4p3vbH.js +0 -1
  267. package/dist/web/assets/keybindings-store-88_Zkn5F.js +0 -1
  268. package/dist/web/assets/markdown-renderer-UhszMWSn.js +0 -1
  269. package/dist/web/assets/mobile-explorer-sheet-D9cqcU3X.js +0 -1
  270. package/dist/web/assets/notification-store-B6X6RYqQ.js +0 -1
  271. package/dist/web/assets/packet-RMMSAZCW-YNQWTi_-.js +0 -1
  272. package/dist/web/assets/pie-UPGHQEXC-E3Wiw76n.js +0 -1
  273. package/dist/web/assets/radar-KQ55EAFF-BPQJUKON.js +0 -1
  274. package/dist/web/assets/remote-desktop-mobile-sheet-DefltAPl.js +0 -2
  275. package/dist/web/assets/schedules-settings-section-D76Ob6tI.js +0 -1
  276. package/dist/web/assets/settings-store-DWUx3865.js +0 -2
  277. package/dist/web/assets/settings-tab-DToHQXfC.js +0 -1
  278. package/dist/web/assets/settings-window-content-B2rlq-Dv.js +0 -1
  279. package/dist/web/assets/system-monitor-tab-CuDpwAZU.js +0 -1
  280. package/dist/web/assets/team-member-sheet-DLrT_Kxo.js +0 -2
  281. package/dist/web/assets/team-member-window-content-S8cdLcg3.js +0 -1
  282. package/dist/web/assets/tool-cards-CLxrQlUs.js +0 -9
  283. package/dist/web/assets/treemap-KZPCXAKY-C2CvKBQR.js +0 -1
  284. package/dist/web/assets/use-accounts-data-snYPJED_.js +0 -1
  285. package/dist/web/assets/ws-auth-ZSZkAT6p.js +0 -1
  286. /package/dist/web/assets/{abap-Cq9nYQyd.js → abap-D-o-bvtI.js} +0 -0
  287. /package/dist/web/assets/{actionscript-3-DL8rUIy0.js → actionscript-3-D2tIzAm8.js} +0 -0
  288. /package/dist/web/assets/{ada-BY8TLuhY.js → ada-DxatBcxS.js} +0 -0
  289. /package/dist/web/assets/{andromeeda-CyVfHsS0.js → andromeeda-D0LK9YJ3.js} +0 -0
  290. /package/dist/web/assets/{apache-DTQUy73x.js → apache-B7uMPNiW.js} +0 -0
  291. /package/dist/web/assets/{apex-CiSZngrZ.js → apex-B7GiXrAr.js} +0 -0
  292. /package/dist/web/assets/{api-client-oY1tC3tM.js → api-client-CEYCBt_0.js} +0 -0
  293. /package/dist/web/assets/{applescript-CzuRDd3h.js → applescript-D_SIFkKe.js} +0 -0
  294. /package/dist/web/assets/{apply-theme-r8tKDrCR.js → apply-theme-BVXAUYoN.js} +0 -0
  295. /package/dist/web/assets/{ara-CSFV93kR.js → ara-C3iECgdg.js} +0 -0
  296. /package/dist/web/assets/{asciidoc-hOQNnXhz.js → asciidoc-gc8z_MMY.js} +0 -0
  297. /package/dist/web/assets/{asm-DdG53NGR.js → asm-27qeoPOI.js} +0 -0
  298. /package/dist/web/assets/{aurora-x-DJ7ORZG6.js → aurora-x-C5J9fWe1.js} +0 -0
  299. /package/dist/web/assets/{awk-B3FFpX_J.js → awk-B7GBzF92.js} +0 -0
  300. /package/dist/web/assets/{ayu-dark-D_P1CfTe.js → ayu-dark-XlnQhuLL.js} +0 -0
  301. /package/dist/web/assets/{ayu-light-Cz3YLUqH.js → ayu-light-DKLLilkU.js} +0 -0
  302. /package/dist/web/assets/{ayu-mirage-JcMq5tgJ.js → ayu-mirage-Dv-XOprJ.js} +0 -0
  303. /package/dist/web/assets/{ballerina-7Hs4Rfpk.js → ballerina-DACRT-sT.js} +0 -0
  304. /package/dist/web/assets/{bat-COgQdnFA.js → bat-BlQCq2XE.js} +0 -0
  305. /package/dist/web/assets/{beancount-BJ3CutBG.js → beancount-DYDztRI0.js} +0 -0
  306. /package/dist/web/assets/{berry-D7so2Cui.js → berry-BE5YKqvx.js} +0 -0
  307. /package/dist/web/assets/{bibtex-Ct3acHa1.js → bibtex-CAm7mgEm.js} +0 -0
  308. /package/dist/web/assets/{bicep-jXDwCQcm.js → bicep-BKgAbfVa.js} +0 -0
  309. /package/dist/web/assets/{bird2-CjLD2cna.js → bird2-yrBNbW1G.js} +0 -0
  310. /package/dist/web/assets/{c-CdgZojnL.js → c-txZFr4RM.js} +0 -0
  311. /package/dist/web/assets/{c3-DtpZYego.js → c3-D_DrzBUd.js} +0 -0
  312. /package/dist/web/assets/{cadence-CPLkixoh.js → cadence-CUFCEgG9.js} +0 -0
  313. /package/dist/web/assets/{can-open-in-ppm-BntPywsy.js → can-open-in-ppm-_yqa69dV.js} +0 -0
  314. /package/dist/web/assets/{catppuccin-frappe-Xt0_qJa9.js → catppuccin-frappe-Dn32MuhA.js} +0 -0
  315. /package/dist/web/assets/{catppuccin-latte-CJnbLec5.js → catppuccin-latte-Dewo7yDf.js} +0 -0
  316. /package/dist/web/assets/{catppuccin-macchiato-BPHP1wYB.js → catppuccin-macchiato-D8WOWZeo.js} +0 -0
  317. /package/dist/web/assets/{catppuccin-mocha-ssWJodGc.js → catppuccin-mocha-b0md48XM.js} +0 -0
  318. /package/dist/web/assets/{clarity-DB0408F6.js → clarity-Ct3UqLBj.js} +0 -0
  319. /package/dist/web/assets/{clojure-dpRPlAO4.js → clojure-CwP5aPEo.js} +0 -0
  320. /package/dist/web/assets/{cmake-r2VkeX9y.js → cmake-D5RF49e5.js} +0 -0
  321. /package/dist/web/assets/{codeowners-v5nQqHiB.js → codeowners-Cl5897L_.js} +0 -0
  322. /package/dist/web/assets/{codeql-BSSEhKRR.js → codeql-BKh1RCg0.js} +0 -0
  323. /package/dist/web/assets/{common-lisp-BXb2MMMA.js → common-lisp-BhHuD6t0.js} +0 -0
  324. /package/dist/web/assets/{coq-BjBNi73X.js → coq-Dco3qwD8.js} +0 -0
  325. /package/dist/web/assets/{csharp-CdPjZRbV.js → csharp-Dpjw9ZA9.js} +0 -0
  326. /package/dist/web/assets/{css-BuxbQOHL.js → css-CPe4cabH.js} +0 -0
  327. /package/dist/web/assets/{csv-Dj-eoZwH.js → csv-BAcTN8Kt.js} +0 -0
  328. /package/dist/web/assets/{csv-parser-CBbALsFU.js → csv-parser-DbxGKdMG.js} +0 -0
  329. /package/dist/web/assets/{cue-Dz73VksN.js → cue-Cr6y60TA.js} +0 -0
  330. /package/dist/web/assets/{cypher-CkVkDywZ.js → cypher-DWtqXXFP.js} +0 -0
  331. /package/dist/web/assets/{d-oU51Pbpf.js → d-BASiTZk4.js} +0 -0
  332. /package/dist/web/assets/{dark-plus-DcwvjjfO.js → dark-plus-56kmnrcl.js} +0 -0
  333. /package/dist/web/assets/{dart-DGvu8mcX.js → dart-C5mZnjoE.js} +0 -0
  334. /package/dist/web/assets/{data-grid-types-B6kUrF-M.js → data-grid-types-DeY60CLU.js} +0 -0
  335. /package/dist/web/assets/{dax-BmbjKQ90.js → dax-B4I2e7d8.js} +0 -0
  336. /package/dist/web/assets/{desktop-IRBSiFfh.js → desktop-DekGg0BT.js} +0 -0
  337. /package/dist/web/assets/{diff-Bdik2JR8.js → diff-CWnJBd73.js} +0 -0
  338. /package/dist/web/assets/{dist-Bv4zQpS1.js → dist-B0J2eYBz.js} +0 -0
  339. /package/dist/web/assets/{dist-C8YPBIOb.js → dist-BKOM_AoT.js} +0 -0
  340. /package/dist/web/assets/{docker-Bgdl0H8I.js → docker-DevchDxK.js} +0 -0
  341. /package/dist/web/assets/{dotenv-COe5NdnA.js → dotenv-DCvtdaGJ.js} +0 -0
  342. /package/dist/web/assets/{dracula-DjURaSAa.js → dracula-8U6Vkk3l.js} +0 -0
  343. /package/dist/web/assets/{dracula-soft-Do7d0bo-.js → dracula-soft-Bfeew5Wg.js} +0 -0
  344. /package/dist/web/assets/{dream-maker-DYwQvJIj.js → dream-maker-DDTJVuoy.js} +0 -0
  345. /package/dist/web/assets/{emacs-lisp-BXjlHyDW.js → emacs-lisp-CEHJqI4m.js} +0 -0
  346. /package/dist/web/assets/{everforest-dark-h8Q2loiX.js → everforest-dark-CHHLt7cq.js} +0 -0
  347. /package/dist/web/assets/{everforest-light-CdsJiL-9.js → everforest-light-TcTbK1sM.js} +0 -0
  348. /package/dist/web/assets/{fennel-D0CBtOvY.js → fennel-Bh4vsA1A.js} +0 -0
  349. /package/dist/web/assets/{fish-DzayfrMj.js → fish-D0T0fILG.js} +0 -0
  350. /package/dist/web/assets/{fluent-CatjkUfx.js → fluent-CgXDXdyy.js} +0 -0
  351. /package/dist/web/assets/{format-bytes-BulRSULX.js → format-bytes-Bc6SqF7M.js} +0 -0
  352. /package/dist/web/assets/{fortran-free-form-DM04ud0b.js → fortran-free-form-CUg3Xhz4.js} +0 -0
  353. /package/dist/web/assets/{gdscript-BLN5iZwf.js → gdscript-Cx056p_k.js} +0 -0
  354. /package/dist/web/assets/{gdshader-BK-1iZQs.js → gdshader-Dj1qP4I5.js} +0 -0
  355. /package/dist/web/assets/{genie-BGPOFWDN.js → genie-SBt15wn7.js} +0 -0
  356. /package/dist/web/assets/{gherkin-CdVOJ_JY.js → gherkin-bTJiHIrB.js} +0 -0
  357. /package/dist/web/assets/{github-dark-B_xW8TaY.js → github-dark-BOPhyugs.js} +0 -0
  358. /package/dist/web/assets/{github-dark-default-h_iKq7CX.js → github-dark-default-Cktqe6NF.js} +0 -0
  359. /package/dist/web/assets/{github-dark-dimmed-nKDYE62T.js → github-dark-dimmed-CeWfII_B.js} +0 -0
  360. /package/dist/web/assets/{github-dark-high-contrast-l8I771QT.js → github-dark-high-contrast-CTx8LpdV.js} +0 -0
  361. /package/dist/web/assets/{github-light-DNhjkg8Z.js → github-light-DVsO5Un1.js} +0 -0
  362. /package/dist/web/assets/{github-light-default-DQ06uS3g.js → github-light-default-BiyPxicr.js} +0 -0
  363. /package/dist/web/assets/{github-light-high-contrast-qc2sZCVN.js → github-light-high-contrast-BPlnke0a.js} +0 -0
  364. /package/dist/web/assets/{gleam-DPPatkNH.js → gleam-CWSxvD0C.js} +0 -0
  365. /package/dist/web/assets/{gn-BnzGQTy5.js → gn-D7NUVJuv.js} +0 -0
  366. /package/dist/web/assets/{gnuplot-Ct1leoo3.js → gnuplot-c5uXmJKc.js} +0 -0
  367. /package/dist/web/assets/{go-qysCdbrn.js → go-CRsHi4Cw.js} +0 -0
  368. /package/dist/web/assets/{groovy-BkWIdrRQ.js → groovy-DpV0RA5r.js} +0 -0
  369. /package/dist/web/assets/{gruvbox-dark-hard-L6Iiybbl.js → gruvbox-dark-hard-DD9fguUN.js} +0 -0
  370. /package/dist/web/assets/{gruvbox-dark-medium-DbD3yl0b.js → gruvbox-dark-medium-BvcwpYJ-.js} +0 -0
  371. /package/dist/web/assets/{gruvbox-dark-soft-Ban2cHK3.js → gruvbox-dark-soft-48dCxX9M.js} +0 -0
  372. /package/dist/web/assets/{gruvbox-light-hard-CK_SjS3e.js → gruvbox-light-hard-CjJ994Ip.js} +0 -0
  373. /package/dist/web/assets/{gruvbox-light-medium-Bjgvhc2w.js → gruvbox-light-medium-DQJQZ_rV.js} +0 -0
  374. /package/dist/web/assets/{gruvbox-light-soft-EWnyYGUu.js → gruvbox-light-soft-AqsC9mKw.js} +0 -0
  375. /package/dist/web/assets/{haskell-rT1N9nYr.js → haskell-B5V7yvmh.js} +0 -0
  376. /package/dist/web/assets/{haxe-Bbc91axo.js → haxe-LWTKfPKr.js} +0 -0
  377. /package/dist/web/assets/{hcl-D-3qJA4v.js → hcl-QmNZdAtI.js} +0 -0
  378. /package/dist/web/assets/{hjson-DWtewcyQ.js → hjson-CboJsAzK.js} +0 -0
  379. /package/dist/web/assets/{hlsl-C7t6hCrO.js → hlsl-Bd-DO_WL.js} +0 -0
  380. /package/dist/web/assets/{horizon-zJAGUQ3j.js → horizon-CU8Bi_ss.js} +0 -0
  381. /package/dist/web/assets/{horizon-bright-CRNyrJy8.js → horizon-bright-iSQ3_3jP.js} +0 -0
  382. /package/dist/web/assets/{houston-BFKa4W1-.js → houston-CTXv3Puz.js} +0 -0
  383. /package/dist/web/assets/{hy-W1hNjMBM.js → hy-Cd4hTidD.js} +0 -0
  384. /package/dist/web/assets/{image-gallery-BycaVWOu.js → image-gallery-J8h8IY0c.js} +0 -0
  385. /package/dist/web/assets/{imba-CPphaV-V.js → imba-BipCHEY2.js} +0 -0
  386. /package/dist/web/assets/{ini-CayaZ4vW.js → ini-Dd1KkMrB.js} +0 -0
  387. /package/dist/web/assets/{java-BPAE_9Ej.js → java-rGr0OQD8.js} +0 -0
  388. /package/dist/web/assets/{javascript-C5SKIVqe.js → javascript-DHhCH2TN.js} +0 -0
  389. /package/dist/web/assets/{json-C-0EUYmu.js → json-CMMLBCUf.js} +0 -0
  390. /package/dist/web/assets/{json5-BqVetEZL.js → json5-mPOwTOsB.js} +0 -0
  391. /package/dist/web/assets/{jsonc-DnjIzkUB.js → jsonc-Bie0E6D3.js} +0 -0
  392. /package/dist/web/assets/{jsonl-rbV-3EqO.js → jsonl-CpqHmtC1.js} +0 -0
  393. /package/dist/web/assets/{jsonnet-9hvXw4qr.js → jsonnet-D21Ms51e.js} +0 -0
  394. /package/dist/web/assets/{jssm-1ifXyIhu.js → jssm-BXnl1nur.js} +0 -0
  395. /package/dist/web/assets/{jsx-hPumywTY.js → jsx-Chnii_vB.js} +0 -0
  396. /package/dist/web/assets/{kanagawa-dragon-BCPjmoMO.js → kanagawa-dragon-DhxR0UjL.js} +0 -0
  397. /package/dist/web/assets/{kanagawa-lotus-DN2VNjy7.js → kanagawa-lotus-fupRg5wp.js} +0 -0
  398. /package/dist/web/assets/{kanagawa-wave-B8RZEsgc.js → kanagawa-wave-DSWbRS9M.js} +0 -0
  399. /package/dist/web/assets/{kdl-CkvjWQIa.js → kdl-DirGHqUy.js} +0 -0
  400. /package/dist/web/assets/{kotlin-D8cnRi5o.js → kotlin-DkhUVMDC.js} +0 -0
  401. /package/dist/web/assets/{kusto-CmeeBEJG.js → kusto-DU7OpovF.js} +0 -0
  402. /package/dist/web/assets/{laserwave--rtV8Mv9.js → laserwave-iLKb1ZCT.js} +0 -0
  403. /package/dist/web/assets/{lean-CS-Pyi42.js → lean-Cuvh_QAP.js} +0 -0
  404. /package/dist/web/assets/{less-Ddi_7qGm.js → less-B5tQZOdI.js} +0 -0
  405. /package/dist/web/assets/{lib-DIrxSyLb.js → lib-el9rA30D.js} +0 -0
  406. /package/dist/web/assets/{libesm-CYLrAeW3.js → libesm-CdttTIAd.js} +0 -0
  407. /package/dist/web/assets/{light-plus-CrEjpkkX.js → light-plus-DiHU2KL5.js} +0 -0
  408. /package/dist/web/assets/{llvm-Cm7ZXmAO.js → llvm-sCBTKCKN.js} +0 -0
  409. /package/dist/web/assets/{log-lW2sYxtp.js → log-DtyR34Ie.js} +0 -0
  410. /package/dist/web/assets/{logo-B-PiRb53.js → logo-CP91aQyG.js} +0 -0
  411. /package/dist/web/assets/{luau-8St0pC-R.js → luau-nfGo2ulP.js} +0 -0
  412. /package/dist/web/assets/{make-DbNdhH54.js → make-XbDn9GHe.js} +0 -0
  413. /package/dist/web/assets/{markdown-CYocYSGd.js → markdown-BB1_3xLr.js} +0 -0
  414. /package/dist/web/assets/{material-theme-CVBF1Cbi.js → material-theme-CIx_sxAy.js} +0 -0
  415. /package/dist/web/assets/{material-theme-darker-HFrlNZD8.js → material-theme-darker-DsQeueDD.js} +0 -0
  416. /package/dist/web/assets/{material-theme-lighter-BM-uz2ft.js → material-theme-lighter-x_lMi74j.js} +0 -0
  417. /package/dist/web/assets/{material-theme-ocean-PbvUez8m.js → material-theme-ocean-Bkgh25P8.js} +0 -0
  418. /package/dist/web/assets/{material-theme-palenight-B22Ck6qd.js → material-theme-palenight-CoOjdbti.js} +0 -0
  419. /package/dist/web/assets/{matlab-GFFNzucw.js → matlab-D6xZas_8.js} +0 -0
  420. /package/dist/web/assets/{mdx-BVnJzMDx.js → mdx-Cl5_YNoz.js} +0 -0
  421. /package/dist/web/assets/{mermaid-BlCmSTCM.js → mermaid-8Eow4jz_.js} +0 -0
  422. /package/dist/web/assets/{min-dark-B0PwlAfC.js → min-dark-D4DhjgRE.js} +0 -0
  423. /package/dist/web/assets/{min-light-C_H2ABKu.js → min-light-DzAckd0t.js} +0 -0
  424. /package/dist/web/assets/{mipsasm-C3rf2fo6.js → mipsasm-zI_zu0tj.js} +0 -0
  425. /package/dist/web/assets/{mojo-CDRuzM3o.js → mojo-DQnGGPHf.js} +0 -0
  426. /package/dist/web/assets/{monokai-KiebNy_Y.js → monokai-CrFoD2VD.js} +0 -0
  427. /package/dist/web/assets/{moonbit-CdVUHoWl.js → moonbit-DdphIGs6.js} +0 -0
  428. /package/dist/web/assets/{move-Dq9iKZRY.js → move-BUlkn0xW.js} +0 -0
  429. /package/dist/web/assets/{narrat-7y5L5sGM.js → narrat-CbOBdtCn.js} +0 -0
  430. /package/dist/web/assets/{nextflow-groovy-Cwcbkcrx.js → nextflow-groovy-CeVLUTuQ.js} +0 -0
  431. /package/dist/web/assets/{night-owl-TBgLPzj4.js → night-owl-Bm4LhUR1.js} +0 -0
  432. /package/dist/web/assets/{night-owl-light-Dehb9My2.js → night-owl-light-DtcBiMEf.js} +0 -0
  433. /package/dist/web/assets/{nix-BKj-gzoX.js → nix-DrdwQ70g.js} +0 -0
  434. /package/dist/web/assets/{nord-BF_r8PeX.js → nord-B0Head1v.js} +0 -0
  435. /package/dist/web/assets/{nushell-CeAjk9_E.js → nushell-CmUdltKn.js} +0 -0
  436. /package/dist/web/assets/{objective-c-Xl1OMcDp.js → objective-c-BGDR33XN.js} +0 -0
  437. /package/dist/web/assets/{objective-cpp-C4ZgzrRq.js → objective-cpp-Buc1Zhd0.js} +0 -0
  438. /package/dist/web/assets/{ocaml-CGj8IYTq.js → ocaml-xdzl9rRD.js} +0 -0
  439. /package/dist/web/assets/{odin-C4veqvW2.js → odin-DiUFngnd.js} +0 -0
  440. /package/dist/web/assets/{one-dark-pro-gAcWejsV.js → one-dark-pro-BURxmIp3.js} +0 -0
  441. /package/dist/web/assets/{one-light-B7E-UUXy.js → one-light-BMRMInAj.js} +0 -0
  442. /package/dist/web/assets/{openscad-C-wN9BEv.js → openscad-DAgLA5J3.js} +0 -0
  443. /package/dist/web/assets/{pascal-C8WBtxMs.js → pascal-CoDZxcRB.js} +0 -0
  444. /package/dist/web/assets/{pkl-BBfF4G2A.js → pkl-DzKM6BuM.js} +0 -0
  445. /package/dist/web/assets/{plastic-DFOAb89P.js → plastic-vF15LmNS.js} +0 -0
  446. /package/dist/web/assets/{plsql-BS2CEMMV.js → plsql-C1v07teA.js} +0 -0
  447. /package/dist/web/assets/{po-BKJDtPmy.js → po-CLLC8BuA.js} +0 -0
  448. /package/dist/web/assets/{poimandres-Cg3hL13e.js → poimandres-Uq0xysxM.js} +0 -0
  449. /package/dist/web/assets/{polar-B9ebiXUU.js → polar-2-luY1Pc.js} +0 -0
  450. /package/dist/web/assets/{portal-container-context-a399Hd9F.js → portal-container-context-DKymYOY-.js} +0 -0
  451. /package/dist/web/assets/{postcss-B5REwNY_.js → postcss-lbFhe0rz.js} +0 -0
  452. /package/dist/web/assets/{powerquery-stc0Gzkf.js → powerquery-RL8O9Wie.js} +0 -0
  453. /package/dist/web/assets/{powershell-jgd0ahJN.js → powershell-upFWhb-b.js} +0 -0
  454. /package/dist/web/assets/{prisma-CWuZjRhI.js → prisma-DBtYaWFk.js} +0 -0
  455. /package/dist/web/assets/{prolog-D5zNFE63.js → prolog-D8jSMykJ.js} +0 -0
  456. /package/dist/web/assets/{proto-B-6RrU7n.js → proto-BM4eadF7.js} +0 -0
  457. /package/dist/web/assets/{puppet-DONP8TDR.js → puppet-BQDQ3Snu.js} +0 -0
  458. /package/dist/web/assets/{purescript-DDJITf-A.js → purescript-Cj2aTX4s.js} +0 -0
  459. /package/dist/web/assets/{python-bmWy9j1-.js → python-BYpqq6u4.js} +0 -0
  460. /package/dist/web/assets/{qmldir-CKDwMt7X.js → qmldir-4uiTcwAu.js} +0 -0
  461. /package/dist/web/assets/{qss-DptQQzBn.js → qss-D5xB-Mqo.js} +0 -0
  462. /package/dist/web/assets/{r-BorKpJ8r.js → r-P6Ujy8rf.js} +0 -0
  463. /package/dist/web/assets/{racket-BaHpYVsZ.js → racket-COKgCFuM.js} +0 -0
  464. /package/dist/web/assets/{raku-DFJIV4k_.js → raku-z14Q9i7K.js} +0 -0
  465. /package/dist/web/assets/{react-X4jUntEf.js → react-D1Yg256j.js} +0 -0
  466. /package/dist/web/assets/{red-70oJGrkb.js → red-ObAvgvQ5.js} +0 -0
  467. /package/dist/web/assets/{reg-C4g235Dv.js → reg-Qv4f0JFA.js} +0 -0
  468. /package/dist/web/assets/{regexp-Cw1UVmti.js → regexp-CEOVGIzy.js} +0 -0
  469. /package/dist/web/assets/{rel-DeLhIz5L.js → rel-cWOyTUwd.js} +0 -0
  470. /package/dist/web/assets/{resolve-theme-b_yGlUFQ.js → resolve-theme-CMp5xxCW.js} +0 -0
  471. /package/dist/web/assets/{riscv-ChnTiBRK.js → riscv-BZDwx-lv.js} +0 -0
  472. /package/dist/web/assets/{ron-C5IWeqDn.js → ron-BpjosPS0.js} +0 -0
  473. /package/dist/web/assets/{rose-pine-DCR0ohYW.js → rose-pine-DhxfJWcm.js} +0 -0
  474. /package/dist/web/assets/{rose-pine-dawn-DvGZs1PR.js → rose-pine-dawn-BbGt9Wy_.js} +0 -0
  475. /package/dist/web/assets/{rose-pine-moon-BWACHjeS.js → rose-pine-moon-dj4Zxgik.js} +0 -0
  476. /package/dist/web/assets/{rosmsg-CQlIVazE.js → rosmsg-EciOaHo2.js} +0 -0
  477. /package/dist/web/assets/{rust-DGGVzWI8.js → rust-C_eH59O_.js} +0 -0
  478. /package/dist/web/assets/{sass-bj1C0rFI.js → sass-Dos3R5lL.js} +0 -0
  479. /package/dist/web/assets/{scala-MU1tLKDi.js → scala-8lVKsTvf.js} +0 -0
  480. /package/dist/web/assets/{scheme-CeGXtv7t.js → scheme-Bd3VdaZj.js} +0 -0
  481. /package/dist/web/assets/{sdbl-D0HLSGKp.js → sdbl-C4Yrjlgh.js} +0 -0
  482. /package/dist/web/assets/{shellscript-DkYiDvh1.js → shellscript-OFZbPKZH.js} +0 -0
  483. /package/dist/web/assets/{slack-dark-YqeHiGWH.js → slack-dark-aL6vlxZs.js} +0 -0
  484. /package/dist/web/assets/{slack-ochin-9Oe0DljJ.js → slack-ochin-BKt1E7sL.js} +0 -0
  485. /package/dist/web/assets/{smalltalk-CaaxedR7.js → smalltalk-D6qVBL1U.js} +0 -0
  486. /package/dist/web/assets/{snazzy-light-C8s2FHNC.js → snazzy-light-BrWerT0t.js} +0 -0
  487. /package/dist/web/assets/{solarized-dark-0ryY6oN3.js → solarized-dark-CzxTJgVQ.js} +0 -0
  488. /package/dist/web/assets/{solarized-light-bltgZeNh.js → solarized-light-BecaDAnG.js} +0 -0
  489. /package/dist/web/assets/{solidity-DOf5HmLZ.js → solidity-C9t7pZ84.js} +0 -0
  490. /package/dist/web/assets/{splunk-D1u4WHDW.js → splunk-CtxfFKtF.js} +0 -0
  491. /package/dist/web/assets/{sql-G8uQZljX.js → sql-7FE_Duz0.js} +0 -0
  492. /package/dist/web/assets/{ssh-config-rbXvly7Q.js → ssh-config-3Fr37ACC.js} +0 -0
  493. /package/dist/web/assets/{stylus-Cii1iADy.js → stylus-BP4mE1sO.js} +0 -0
  494. /package/dist/web/assets/{swift-DpIgonN4.js → swift-0uqTFouD.js} +0 -0
  495. /package/dist/web/assets/{synthwave-84-DjGLzU2b.js → synthwave-84-DQaNM3DI.js} +0 -0
  496. /package/dist/web/assets/{system-verilog-_Z0M0Hs1.js → system-verilog-DUIVrZHZ.js} +0 -0
  497. /package/dist/web/assets/{systemd-BhyDCh9P.js → systemd-CtNo_WIh.js} +0 -0
  498. /package/dist/web/assets/{talonscript-CNchgCRD.js → talonscript-B0HXaCVT.js} +0 -0
  499. /package/dist/web/assets/{tasl-m--W7Pyr.js → tasl-Cb7RAl11.js} +0 -0
  500. /package/dist/web/assets/{tcl-gY-M_2BC.js → tcl-B79aC6l5.js} +0 -0
  501. /package/dist/web/assets/{terraform-CB6CzMwo.js → terraform-75jJDjpW.js} +0 -0
  502. /package/dist/web/assets/{tokyo-night-COh9oRYi.js → tokyo-night-Cz9Uh9qb.js} +0 -0
  503. /package/dist/web/assets/{toml-Bg2bsY-v.js → toml-CD6y9A-z.js} +0 -0
  504. /package/dist/web/assets/{tsv-DceGDiJC.js → tsv-CXJMbBrb.js} +0 -0
  505. /package/dist/web/assets/{tsx-EccDYa7i.js → tsx-BuYN0I2t.js} +0 -0
  506. /package/dist/web/assets/{turtle-C_l2JhvX.js → turtle-DWH-Rjse.js} +0 -0
  507. /package/dist/web/assets/{typescript-C1ytmMcr.js → typescript-BPd-hsrR.js} +0 -0
  508. /package/dist/web/assets/{typespec-De2ztzG5.js → typespec-B0wvVykx.js} +0 -0
  509. /package/dist/web/assets/{typst-DNcS7gSr.js → typst-DWG2c48-.js} +0 -0
  510. /package/dist/web/assets/{use-is-mobile-CYW3FJFE.js → use-is-mobile-D1weanbj.js} +0 -0
  511. /package/dist/web/assets/{use-visual-viewport-D9O48HQt.js → use-visual-viewport-Bfl3Bxxb.js} +0 -0
  512. /package/dist/web/assets/{utils-C9OjCrou.js → utils-IrnQujwR.js} +0 -0
  513. /package/dist/web/assets/{v-DSMUsdMO.js → v-Ch6E4Egd.js} +0 -0
  514. /package/dist/web/assets/{vala-BEazLpkO.js → vala-CGVKwbPx.js} +0 -0
  515. /package/dist/web/assets/{vb-CV9pEJzW.js → vb-BIEbSxyT.js} +0 -0
  516. /package/dist/web/assets/{vendor-xterm-cuGQmUjN.js → vendor-xterm-YQlzIWv2.js} +0 -0
  517. /package/dist/web/assets/{verilog-DhXkLVMk.js → verilog-A-3pYPad.js} +0 -0
  518. /package/dist/web/assets/{vesper-n4IltYq-.js → vesper-CK1naZAV.js} +0 -0
  519. /package/dist/web/assets/{vhdl-CePjFbvs.js → vhdl-CVawmgMg.js} +0 -0
  520. /package/dist/web/assets/{viml-ChndFsm5.js → viml-CiWjaqBY.js} +0 -0
  521. /package/dist/web/assets/{vitesse-black-BytVdvhC.js → vitesse-black-PYXW4-BO.js} +0 -0
  522. /package/dist/web/assets/{vitesse-dark-83Wech0N.js → vitesse-dark-BU5s4AuG.js} +0 -0
  523. /package/dist/web/assets/{vitesse-light-Xg4wbeMO.js → vitesse-light-DgkIT-N7.js} +0 -0
  524. /package/dist/web/assets/{vyper-eW1NC3FG.js → vyper-D8cRDPp3.js} +0 -0
  525. /package/dist/web/assets/{wasm-BucxKwBZ.js → wasm-C02wlRbP.js} +0 -0
  526. /package/dist/web/assets/{wasm-Bhucqu1z.js → wasm-ClFJgzmT.js} +0 -0
  527. /package/dist/web/assets/{wenyan-BJFBuXuH.js → wenyan-B8plxIr8.js} +0 -0
  528. /package/dist/web/assets/{wgsl-6z4FUyMK.js → wgsl-CVujtm3V.js} +0 -0
  529. /package/dist/web/assets/{wikitext-BoDU6teJ.js → wikitext-CmcG9B69.js} +0 -0
  530. /package/dist/web/assets/{wit-DerMPpgt.js → wit--gl2Jium.js} +0 -0
  531. /package/dist/web/assets/{wolfram-D78v0Ap-.js → wolfram-6T_1D-NS.js} +0 -0
  532. /package/dist/web/assets/{x-YDu4m2Iq.js → x-CxoBA0po.js} +0 -0
  533. /package/dist/web/assets/{yaml-C4TIgIw4.js → yaml-CIVV9uA1.js} +0 -0
  534. /package/dist/web/assets/{zenscript-BydW-dLC.js → zenscript-CoT4yNdA.js} +0 -0
  535. /package/dist/web/assets/{zig-b9lTqL5E.js → zig-BWs2Qo3G.js} +0 -0
@@ -3,43 +3,25 @@
3
3
  * Visualizes git commit history as an interactive graph in a webview.
4
4
  */
5
5
  import type { ExtensionContext } from "@ppm/vscode-compat";
6
- import type { SpawnResult } from "@ppm/vscode-compat/src/process.ts";
7
6
  import type { GitGraphSettings, WebviewToExt, Worktree } from "./types.ts";
8
7
  import { DEFAULT_SETTINGS } from "./types.ts";
9
8
  import { getWebviewHtml } from "./webview-html.ts";
10
-
11
- interface VscodeApi {
12
- commands: {
13
- registerCommand(command: string, callback: (...args: unknown[]) => unknown): { dispose(): void };
14
- };
15
- window: {
16
- showErrorMessage(message: string, ...items: string[]): Promise<string | undefined>;
17
- showInformationMessage(message: string, ...items: string[]): Promise<string | undefined>;
18
- openTab(tabType: string, title: string, projectId: string | null, metadata?: Record<string, unknown>): Promise<void>;
19
- switchProject(projectName: string): Promise<void>;
20
- createWebviewPanel(viewType: string, title: string, showOptions: unknown, options?: { projectPath?: string }): {
21
- webview: {
22
- html: string;
23
- onDidReceiveMessage: (listener: (msg: unknown) => void) => { dispose(): void };
24
- postMessage(message: unknown): Promise<boolean>;
25
- };
26
- onDidDispose: (listener: () => void) => { dispose(): void };
27
- dispose(): void;
28
- };
29
- };
30
- process: {
31
- spawn(cmd: string, args: string[], cwd: string, options?: { timeout?: number; env?: Record<string, string> }): Promise<SpawnResult>;
32
- };
33
- ViewColumn: { Active: number };
34
- }
35
-
36
- let baseUrl = "";
37
- let authToken = "";
38
-
39
- // One live panel per project. Opening git graph for another project must not
40
- // dispose or hijack an existing project's panel (multi-tab, multi-project usage).
41
- type WebviewPanel = ReturnType<VscodeApi["window"]["createWebviewPanel"]>;
42
- const panelsByProject = new Map<string, WebviewPanel>();
9
+ import type { VscodeApi } from "./git-exec.ts";
10
+ import {
11
+ assertSafeFilePaths, assertValidHash, assertValidRef, assertValidRemote, spawnGit,
12
+ } from "./git-exec.ts";
13
+ import { authHeaders, getBaseUrl, initPpmApi, resolveProjectName } from "./ppm-api.ts";
14
+ import { registerBlameView } from "./blame-view.ts";
15
+ import { registerFileHistoryView } from "./file-history-view.ts";
16
+ import { registerCompareView } from "./compare-view.ts";
17
+ import { registerRebaseView } from "./rebase-view.ts";
18
+ import { registerReflogView } from "./reflog-view.ts";
19
+ import { parseSubmoduleStatus } from "./submodule-parser.ts";
20
+ import { openPanel } from "./panel-registry.ts";
21
+ import { detectMergeState } from "./git-state.ts";
22
+ import { navigateToPanel } from "./panel-nav.ts";
23
+ import { registerViewCommand } from "./register-view-command.ts";
24
+ import { buildSearchArgs, isSearchMode, parseSearchResults } from "./commit-search.ts";
43
25
 
44
26
  function getSettings(context: ExtensionContext): GitGraphSettings {
45
27
  return { ...DEFAULT_SETTINGS, ...(context.globalState.get<Partial<GitGraphSettings>>("settings") || {}) };
@@ -60,28 +42,21 @@ async function saveSetting(context: ExtensionContext, key: string, value: unknow
60
42
  }
61
43
 
62
44
  export function activate(context: ExtensionContext, vscode: VscodeApi): void {
63
- baseUrl = (globalThis as any).__PPM_BASE_URL__ || "";
64
- authToken = (globalThis as any).__PPM_AUTH_TOKEN__ || "";
65
-
66
- context.subscriptions.push(
67
- vscode.commands.registerCommand("git-graph.view", async (...args: unknown[]) => {
68
- const projectPath = args[0] as string | undefined;
69
- console.log(`[ext-git-graph] git-graph.view command received, projectPath=${projectPath ?? "(none)"}`);
70
- const resolvedPath = projectPath || await resolveProjectPath();
71
- if (!resolvedPath) {
72
- console.warn("[ext-git-graph] no project path resolved");
73
- await vscode.window.showErrorMessage("Git Graph: No project selected. Open a project first, then try again.");
74
- return;
75
- }
76
- try {
77
- await openGitGraph(vscode, context, resolvedPath);
78
- console.log("[ext-git-graph] webview panel created");
79
- } catch (e) {
80
- console.error("[ext-git-graph] openGitGraph failed:", e);
81
- throw e;
82
- }
83
- }),
84
- );
45
+ initPpmApi();
46
+
47
+ registerViewCommand({
48
+ context,
49
+ vscode,
50
+ command: "git-graph.view",
51
+ label: "Git Graph",
52
+ open: (projectPath) => openGitGraph(vscode, context, projectPath),
53
+ });
54
+
55
+ registerBlameView(context, vscode);
56
+ registerFileHistoryView(context, vscode);
57
+ registerCompareView(context, vscode);
58
+ registerRebaseView(context, vscode);
59
+ registerReflogView(context, vscode);
85
60
 
86
61
  console.log("[ext-git-graph] activated");
87
62
  }
@@ -90,75 +65,29 @@ export function deactivate(): void {
90
65
  console.log("[ext-git-graph] deactivated");
91
66
  }
92
67
 
93
- /** Build fetch options with auth header when token is available */
94
- function authHeaders(): RequestInit {
95
- return authToken ? { headers: { Authorization: `Bearer ${authToken}` } } : {};
96
- }
97
-
98
- /** Resolve project path from PPM API as fallback */
99
- async function resolveProjectPath(): Promise<string | null> {
100
- try {
101
- const res = await fetch(`${baseUrl}/api/projects`, authHeaders());
102
- const json = await res.json() as { ok: boolean; data?: { name: string; path: string }[] };
103
- if (!json.ok || !json.data || json.data.length === 0) return null;
104
- // Single project — safe to auto-select
105
- if (json.data.length === 1) return json.data[0].path;
106
- // Multiple projects — cannot guess which is active, return null
107
- return null;
108
- } catch {}
109
- return null;
110
- }
111
-
112
- /** Resolve project name from path via PPM API */
113
- async function resolveProjectName(projectPath: string): Promise<string> {
114
- try {
115
- const res = await fetch(`${baseUrl}/api/projects`, authHeaders());
116
- const json = await res.json() as { ok: boolean; data?: { name: string; path: string }[] };
117
- if (json.ok && json.data) {
118
- const match = json.data.find((p) => p.path === projectPath);
119
- if (match) return match.name;
120
- }
121
- } catch {}
122
- // Fallback to directory name
123
- return projectPath.split(/[\\/]/).filter(Boolean).pop() || "project";
124
- }
125
-
126
- /** Spawn git and return result */
127
- async function spawnGit(
128
- vscode: VscodeApi,
129
- args: string[],
130
- cwd: string,
131
- timeout = 30_000,
132
- ): Promise<SpawnResult> {
133
- return vscode.process.spawn("git", args, cwd, {
134
- timeout,
135
- env: { GIT_TERMINAL_PROMPT: "0" },
136
- });
137
- }
138
-
139
68
  function openGitGraph(
140
69
  vscode: VscodeApi,
141
70
  context: ExtensionContext,
142
71
  projectPath: string,
143
72
  ): void {
144
- // Dispose stale panel for THIS project only, to avoid browser/server desync.
145
- // On page reload the browser's WS close message can be lost, leaving the map
146
- // referencing a panel the browser no longer knows about. Other projects'
147
- // panels stay alive.
148
- panelsByProject.get(projectPath)?.dispose(); // fires onDidDispose → clears map entry & timers
149
-
150
73
  const dirName = projectPath.split(/[\\/]/).filter(Boolean).pop() || "Git Graph";
151
- const panel = vscode.window.createWebviewPanel(
152
- "git-graph.view",
153
- `Git Graph: ${dirName}`,
154
- vscode.ViewColumn.Active,
155
- { projectPath },
156
- );
157
- panelsByProject.set(projectPath, panel);
158
74
 
159
- panel.webview.html = getWebviewHtml();
75
+ // Declared before openPanel so the message handler can reach the panel it is
76
+ // attached to without threading it through every handler signature.
77
+ let uncommittedPollTimer: ReturnType<typeof setInterval> | undefined;
78
+ let disposed = false;
160
79
 
161
- const msgDisposable = panel.webview.onDidReceiveMessage(async (raw: unknown) => {
80
+ const panel = openPanel({
81
+ vscode,
82
+ viewType: "git-graph.view",
83
+ title: `Git Graph: ${dirName}`,
84
+ projectPath,
85
+ html: getWebviewHtml(),
86
+ onDispose: () => {
87
+ disposed = true;
88
+ if (uncommittedPollTimer) clearInterval(uncommittedPollTimer);
89
+ },
90
+ onMessage: async (raw: unknown) => {
162
91
  const msg = raw as WebviewToExt;
163
92
  // Panel is bound to its project for life — reopening a project recreates
164
93
  // the panel, so the closure path is always current.
@@ -171,6 +100,7 @@ function openGitGraph(
171
100
  handleUncommittedStatus(vscode, panel, pp); // fire-and-forget
172
101
  handleWorktrees(vscode, panel, pp); // fire-and-forget
173
102
  handleStashes(vscode, panel, pp); // fire-and-forget
103
+ handleSubmodules(vscode, panel, pp); // fire-and-forget
174
104
  break;
175
105
  case "requestRepoInfo":
176
106
  await handleRepoInfo(vscode, panel, pp);
@@ -281,6 +211,103 @@ function openGitGraph(
281
211
  case "requestStashes":
282
212
  await handleStashes(vscode, panel, pp);
283
213
  break;
214
+ case "requestSubmodules":
215
+ await handleSubmodules(vscode, panel, pp);
216
+ break;
217
+ case "updateSubmodule": {
218
+ // A path from the webview, so it gets the same treatment as any other.
219
+ // `--` keeps a path that starts with a dash out of the option list.
220
+ const subPath = String(msg.path || "");
221
+ assertSafeFilePaths([subPath], pp);
222
+ const updateRes = await spawnGit(
223
+ vscode,
224
+ ["submodule", "update", "--init", "--recursive", "--", subPath],
225
+ pp,
226
+ 180_000,
227
+ );
228
+ if (updateRes.exitCode !== 0) {
229
+ throw new Error(updateRes.stderr.trim() || "git could not update that submodule.");
230
+ }
231
+ await handleSubmodules(vscode, panel, pp);
232
+ break;
233
+ }
234
+ case "openSubmodule": {
235
+ const subPath = String(msg.path || "");
236
+ assertSafeFilePaths([subPath], pp);
237
+ await openProjectAt(vscode, `${pp}/${subPath}`, "submodule");
238
+ break;
239
+ }
240
+ case "searchCommits": {
241
+ if (!isSearchMode(msg.mode)) throw new Error(`Unknown search mode: "${msg.mode}"`);
242
+ const searchArgs = buildSearchArgs({ mode: msg.mode, text: msg.text }, 200, pp);
243
+ const searchRes = await spawnGit(vscode, searchArgs, pp, 60_000);
244
+ if (searchRes.exitCode !== 0) {
245
+ throw new Error(searchRes.stderr.trim() || "Search failed.");
246
+ }
247
+ await panel.webview.postMessage({
248
+ command: "loadSearchResults",
249
+ data: { mode: msg.mode, text: msg.text, hits: parseSearchResults(searchRes.stdout) },
250
+ });
251
+ break;
252
+ }
253
+ case "openBlame": {
254
+ assertSafeFilePaths([msg.filePath], pp);
255
+ const fileName = msg.filePath.split(/[\\/]/).pop() || msg.filePath;
256
+ await navigateToPanel({
257
+ vscode,
258
+ context,
259
+ viewType: "git-graph.blame",
260
+ title: `Blame: ${fileName}`,
261
+ projectPath: pp,
262
+ target: { filePath: msg.filePath, rev: msg.hash ? assertValidHash(msg.hash) : undefined },
263
+ });
264
+ break;
265
+ }
266
+ case "openFileHistory": {
267
+ assertSafeFilePaths([msg.filePath], pp);
268
+ const fileName = msg.filePath.split(/[\\/]/).pop() || msg.filePath;
269
+ await navigateToPanel({
270
+ vscode,
271
+ context,
272
+ viewType: "git-graph.fileHistory",
273
+ title: `History: ${fileName}`,
274
+ projectPath: pp,
275
+ target: { filePath: msg.filePath },
276
+ });
277
+ break;
278
+ }
279
+ case "openCompare":
280
+ await navigateToPanel({
281
+ vscode,
282
+ context,
283
+ viewType: "git-graph.compare",
284
+ title: "Compare",
285
+ projectPath: pp,
286
+ target: {
287
+ ...(msg.ref1 ? { ref1: assertValidRef(msg.ref1, "ref1") } : {}),
288
+ ...(msg.ref2 ? { ref2: assertValidRef(msg.ref2, "ref2") } : {}),
289
+ },
290
+ });
291
+ break;
292
+ case "openReflog":
293
+ await navigateToPanel({
294
+ vscode,
295
+ context,
296
+ viewType: "git-graph.reflog",
297
+ title: "Reflog",
298
+ projectPath: pp,
299
+ });
300
+ break;
301
+ case "openInteractiveRebase":
302
+ await navigateToPanel({
303
+ vscode,
304
+ context,
305
+ viewType: "git-graph.interactiveRebase",
306
+ title: "Interactive Rebase",
307
+ projectPath: pp,
308
+ target: msg.base ? { base: assertValidHash(msg.base) } : undefined,
309
+ });
310
+ break;
284
311
  case "addWorktree": {
285
312
  const addArgs = ["worktree", "add"];
286
313
  if (msg.newBranch) {
@@ -316,41 +343,9 @@ function openGitGraph(
316
343
  if (pruneResult.exitCode === 0) await handleWorktrees(vscode, panel, pp);
317
344
  break;
318
345
  }
319
- case "openWorktree": {
320
- // Find project matching worktree path and switch to it
321
- try {
322
- const res = await fetch(`${baseUrl}/api/projects`, authHeaders());
323
- const json = await res.json() as { ok: boolean; data?: { name: string; path: string }[] };
324
- const match = json.data?.find((p) => p.path === msg.path);
325
- if (match) {
326
- await vscode.window.switchProject(match.name);
327
- } else {
328
- // Worktree not registered — offer to add it as a project
329
- const dirName = msg.path.split(/[\\/]/).filter(Boolean).pop() || "worktree";
330
- const answer = await vscode.window.showInformationMessage(
331
- `Worktree "${dirName}" is not registered as a project. Add it?`,
332
- "Yes, add project", "Cancel",
333
- );
334
- if (answer === "Yes, add project") {
335
- const addRes = await fetch(`${baseUrl}/api/projects`, {
336
- method: "POST",
337
- headers: { "Content-Type": "application/json", ...(authToken ? { Authorization: `Bearer ${authToken}` } : {}) },
338
- body: JSON.stringify({ path: msg.path, name: dirName }),
339
- });
340
- const addJson = await addRes.json() as { ok: boolean; data?: { name: string } };
341
- if (addJson.ok) {
342
- const name = addJson.data?.name || dirName;
343
- await vscode.window.switchProject(name);
344
- } else {
345
- await vscode.window.showErrorMessage("Failed to add project");
346
- }
347
- }
348
- }
349
- } catch {
350
- await vscode.window.showErrorMessage("Failed to look up projects");
351
- }
346
+ case "openWorktree":
347
+ await openProjectAt(vscode, msg.path, "Worktree");
352
348
  break;
353
- }
354
349
  case "openConflictFile": {
355
350
  assertSafeFilePaths([msg.filePath], pp);
356
351
  const projectName = await resolveProjectName(pp);
@@ -370,24 +365,13 @@ function openGitGraph(
370
365
  const errMsg = e instanceof Error ? e.message : String(e);
371
366
  await panel.webview.postMessage({ command: "error", message: errMsg });
372
367
  }
368
+ },
373
369
  });
374
370
 
375
- context.subscriptions.push(msgDisposable);
376
-
377
371
  // Poll uncommitted changes every 5 seconds
378
- let disposed = false;
379
- const uncommittedPollTimer = setInterval(() => {
372
+ uncommittedPollTimer = setInterval(() => {
380
373
  if (!disposed) handleUncommittedStatus(vscode, panel, projectPath);
381
374
  }, 5_000);
382
-
383
- panel.onDidDispose(() => {
384
- disposed = true;
385
- // Only clear the map entry if it still points at this panel — a recreate
386
- // for the same project may have already replaced it
387
- if (panelsByProject.get(projectPath) === panel) panelsByProject.delete(projectPath);
388
- clearInterval(uncommittedPollTimer);
389
- msgDisposable.dispose();
390
- });
391
375
  }
392
376
 
393
377
 
@@ -526,54 +510,63 @@ async function handleUncommittedStatus(
526
510
  }
527
511
  }
528
512
 
529
- async function detectMergeState(
530
- vscode: VscodeApi,
531
- projectPath: string,
532
- ): Promise<import("./types.ts").MergeState | undefined> {
533
- // Resolve the actual GIT_DIR (handles worktrees where .git is a pointer file)
534
- const gitDirResult = await spawnGit(vscode, ["rev-parse", "--git-dir"], projectPath, 2000);
535
- if (gitDirResult.exitCode !== 0) return undefined;
536
- let gitDir = gitDirResult.stdout.trim();
537
- // Make absolute if relative
538
- if (!gitDir.startsWith("/")) gitDir = `${projectPath}/${gitDir}`;
539
-
540
- // Check rebase-merge (interactive rebase)
541
- const rebaseMergeDir = `${gitDir}/rebase-merge`;
542
- const checkRebase = await vscode.process.spawn("test", ["-d", rebaseMergeDir], projectPath, { timeout: 2000 });
543
- if (checkRebase.exitCode === 0) {
544
- const [numResult, endResult, msgResult] = await Promise.all([
545
- vscode.process.spawn("cat", [`${rebaseMergeDir}/msgnum`], projectPath, { timeout: 2000 }),
546
- vscode.process.spawn("cat", [`${rebaseMergeDir}/end`], projectPath, { timeout: 2000 }),
547
- vscode.process.spawn("cat", [`${rebaseMergeDir}/message`], projectPath, { timeout: 2000 }),
548
- ]);
549
- const current = numResult.stdout.trim();
550
- const total = endResult.stdout.trim();
551
- return {
552
- type: "rebase",
553
- progress: current && total ? `${current}/${total}` : undefined,
554
- message: msgResult.stdout.trim().split("\n")[0] || undefined,
555
- };
556
- }
557
-
558
- // Check rebase-apply (non-interactive rebase / am)
559
- const checkRebaseApply = await vscode.process.spawn("test", ["-d", `${gitDir}/rebase-apply`], projectPath, { timeout: 2000 });
560
- if (checkRebaseApply.exitCode === 0) {
561
- return { type: "rebase" };
562
- }
563
-
564
- // Check merge
565
- const checkMerge = await vscode.process.spawn("test", ["-f", `${gitDir}/MERGE_HEAD`], projectPath, { timeout: 2000 });
566
- if (checkMerge.exitCode === 0) {
567
- return { type: "merge" };
568
- }
513
+ /**
514
+ * Switch PPM to the project living at `path`, offering to register it first.
515
+ *
516
+ * A worktree or a submodule is a git repository PPM may never have been told
517
+ * about; without this, opening one would silently do nothing.
518
+ */
519
+ async function openProjectAt(vscode: VscodeApi, path: string, kind: string): Promise<void> {
520
+ try {
521
+ const res = await fetch(`${getBaseUrl()}/api/projects`, authHeaders());
522
+ const json = await res.json() as { ok: boolean; data?: { name: string; path: string }[] };
523
+ const match = json.data?.find((p) => p.path === path);
524
+ if (match) {
525
+ await vscode.window.switchProject(match.name);
526
+ return;
527
+ }
569
528
 
570
- // Check cherry-pick
571
- const checkCherry = await vscode.process.spawn("test", ["-f", `${gitDir}/CHERRY_PICK_HEAD`], projectPath, { timeout: 2000 });
572
- if (checkCherry.exitCode === 0) {
573
- return { type: "cherry-pick" };
529
+ const dirName = path.split(/[\\/]/).filter(Boolean).pop() || kind.toLowerCase();
530
+ const answer = await vscode.window.showInformationMessage(
531
+ `${kind} "${dirName}" is not registered as a project. Add it?`,
532
+ "Yes, add project", "Cancel",
533
+ );
534
+ if (answer !== "Yes, add project") return;
535
+
536
+ const authInit = authHeaders() as { headers?: Record<string, string> };
537
+ const addRes = await fetch(`${getBaseUrl()}/api/projects`, {
538
+ method: "POST",
539
+ headers: { "Content-Type": "application/json", ...(authInit.headers ?? {}) },
540
+ body: JSON.stringify({ path, name: dirName }),
541
+ });
542
+ const addJson = await addRes.json() as { ok: boolean; data?: { name: string } };
543
+ if (addJson.ok) {
544
+ await vscode.window.switchProject(addJson.data?.name || dirName);
545
+ } else {
546
+ await vscode.window.showErrorMessage("Failed to add project");
547
+ }
548
+ } catch {
549
+ await vscode.window.showErrorMessage("Failed to look up projects");
574
550
  }
551
+ }
575
552
 
576
- return undefined;
553
+ /**
554
+ * The repository's submodules and how far each has drifted.
555
+ *
556
+ * A repository with no submodules is the overwhelming majority, and git exits
557
+ * non-zero for one reason or another in several of the edge cases, so a failure
558
+ * here reports an empty list rather than an error the user cannot act on.
559
+ */
560
+ async function handleSubmodules(
561
+ vscode: VscodeApi,
562
+ panel: ReturnType<VscodeApi["window"]["createWebviewPanel"]>,
563
+ projectPath: string,
564
+ ): Promise<void> {
565
+ const result = await spawnGit(vscode, ["submodule", "status", "--recursive"], projectPath, 30_000);
566
+ await panel.webview.postMessage({
567
+ command: "loadSubmodules",
568
+ data: result.exitCode === 0 ? parseSubmoduleStatus(result.stdout) : [],
569
+ });
577
570
  }
578
571
 
579
572
  async function handleWorktrees(
@@ -804,46 +797,6 @@ function parseCommitDetail(stdout: string): import("./types.ts").CommitDetail {
804
797
  return { hash, parents, author, authorEmail, authorDate, committer, committerEmail, commitDate, message, fileChanges };
805
798
  }
806
799
 
807
- // --- Input validation for git actions ---
808
-
809
- function assertValidHash(value: unknown): string {
810
- const s = String(value || "");
811
- if (s === "HEAD") return s;
812
- if (!/^[0-9a-f]{4,40}$/i.test(s)) throw new Error(`Invalid commit hash: "${s}"`);
813
- return s;
814
- }
815
-
816
- function assertValidRef(value: unknown, label: string): string {
817
- const s = String(value || "");
818
- if (!s || /[\x00-\x1f\x7f~^:?*[\]\\]/.test(s) || s.startsWith("-") || s.includes("..")) {
819
- throw new Error(`Invalid git ref for ${label}: "${s}"`);
820
- }
821
- return s;
822
- }
823
-
824
- function assertValidRemote(value: unknown): string {
825
- const s = String(value || "");
826
- if (!s || /[\x00-\x1f\x7f]/.test(s) || s.startsWith("-")) {
827
- throw new Error(`Invalid remote name: "${s}"`);
828
- }
829
- return s;
830
- }
831
-
832
- /** Validate file paths are relative and don't escape the project root */
833
- function assertSafeFilePaths(files: string[], projectPath: string): void {
834
- const { resolve, normalize } = require("path");
835
- const root = normalize(projectPath) + "/";
836
- for (const f of files) {
837
- if (!f || f.startsWith("-") || f.startsWith("/") || /[\x00-\x1f\x7f]/.test(f)) {
838
- throw new Error(`Invalid file path: "${f}"`);
839
- }
840
- const resolved = normalize(resolve(projectPath, f));
841
- if (!resolved.startsWith(root) && resolved !== normalize(projectPath)) {
842
- throw new Error(`File path escapes project root: "${f}"`);
843
- }
844
- }
845
- }
846
-
847
800
  function buildGitActionArgs(action: string, args: Record<string, unknown>): string[] {
848
801
  const VALID_RESET_MODES = ["soft", "mixed", "hard"];
849
802
 
@@ -0,0 +1,102 @@
1
+ import { describe, it, expect } from "bun:test";
2
+ import {
3
+ FIELD_SEP, RECORD_SEP, buildLineRangeArg, parseFileHistory,
4
+ } from "./file-history-parser.ts";
5
+
6
+ function record(fields: string[], statusLines: string[] = []): string {
7
+ return RECORD_SEP + fields.join(FIELD_SEP) + (statusLines.length ? "\n" + statusLines.join("\n") : "");
8
+ }
9
+
10
+ const A = "a".repeat(40);
11
+ const B = "b".repeat(40);
12
+
13
+ describe("parseFileHistory", () => {
14
+ it("reads one commit with its file status", () => {
15
+ const stdout = record([A, "parent1", "Mai", "mai@example.com", "1700000000", "tidy up"], ["M\tsrc/app.ts"]);
16
+
17
+ const entries = parseFileHistory(stdout);
18
+
19
+ expect(entries).toHaveLength(1);
20
+ expect(entries[0]).toMatchObject({
21
+ hash: A,
22
+ parents: ["parent1"],
23
+ author: "Mai",
24
+ authorEmail: "mai@example.com",
25
+ authorDate: 1700000000,
26
+ subject: "tidy up",
27
+ status: "M",
28
+ path: "src/app.ts",
29
+ });
30
+ });
31
+
32
+ it("keeps both sides of a rename", () => {
33
+ const stdout = record([A, "p", "Mai", "m@e.com", "1", "move it"], ["R096\tsrc/old.ts\tsrc/new.ts"]);
34
+
35
+ const entries = parseFileHistory(stdout);
36
+
37
+ expect(entries[0]!.status).toBe("R");
38
+ expect(entries[0]!.oldPath).toBe("src/old.ts");
39
+ expect(entries[0]!.path).toBe("src/new.ts");
40
+ });
41
+
42
+ it("treats a copy like a rename", () => {
43
+ const stdout = record([A, "p", "Mai", "m@e.com", "1", "copy"], ["C100\tsrc/a.ts\tsrc/b.ts"]);
44
+
45
+ expect(parseFileHistory(stdout)[0]).toMatchObject({
46
+ status: "C", oldPath: "src/a.ts", path: "src/b.ts",
47
+ });
48
+ });
49
+
50
+ it("survives a merge commit that reports no file status", () => {
51
+ const stdout = record([A, "p1 p2", "Mai", "m@e.com", "1", "merge branch"]);
52
+
53
+ const entries = parseFileHistory(stdout);
54
+
55
+ expect(entries).toHaveLength(1);
56
+ expect(entries[0]!.parents).toEqual(["p1", "p2"]);
57
+ expect(entries[0]!.path).toBe("");
58
+ });
59
+
60
+ it("keeps a subject containing a tab or a pipe intact", () => {
61
+ // The reason the format uses \x1f rather than a printable separator.
62
+ const subject = "fix: a|b\tc";
63
+ const stdout = record([A, "p", "Mai", "m@e.com", "1", subject], ["M\tsrc/app.ts"]);
64
+
65
+ expect(parseFileHistory(stdout)[0]!.subject).toBe(subject);
66
+ });
67
+
68
+ it("reads several commits in one go", () => {
69
+ const stdout = [
70
+ record([A, "p", "Mai", "m@e.com", "2", "second"], ["M\tsrc/app.ts"]),
71
+ record([B, "", "Nam", "nam@e.com", "1", "first"], ["A\tsrc/app.ts"]),
72
+ ].join("");
73
+
74
+ const entries = parseFileHistory(stdout);
75
+
76
+ expect(entries.map((e) => e.hash)).toEqual([A, B]);
77
+ expect(entries[1]!.status).toBe("A");
78
+ expect(entries[1]!.parents).toEqual([]);
79
+ });
80
+
81
+ it("ignores a record missing fields rather than emitting a broken entry", () => {
82
+ expect(parseFileHistory(RECORD_SEP + ["only", "two"].join(FIELD_SEP))).toEqual([]);
83
+ });
84
+
85
+ it("returns nothing for empty output", () => {
86
+ expect(parseFileHistory("")).toEqual([]);
87
+ });
88
+ });
89
+
90
+ describe("buildLineRangeArg", () => {
91
+ it("builds the -L argument", () => {
92
+ expect(buildLineRangeArg(10, 20, "src/app.ts")).toBe("-L10,20:src/app.ts");
93
+ });
94
+
95
+ it("orders an inverted selection so git still accepts it", () => {
96
+ expect(buildLineRangeArg(20, 10, "src/app.ts")).toBe("-L10,20:src/app.ts");
97
+ });
98
+
99
+ it("handles a single-line range", () => {
100
+ expect(buildLineRangeArg(7, 7, "a.ts")).toBe("-L7,7:a.ts");
101
+ });
102
+ });