@hienlh/ppm 0.9.85 → 0.9.87

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 (243) hide show
  1. package/260415-0932-git-graph-stash-rebase-conflicts/reports/code-reviewer-260415-1020-stash-rebase-conflicts.md +288 -0
  2. package/260415-0932-git-graph-stash-rebase-conflicts/reports/tester-260415-1020-build-check.md +117 -0
  3. package/260415-1150-ext-silent-failure-debugging/reports/code-reviewer-260415-1159-ext-error-reporting-review.md +205 -0
  4. package/260415-1150-ext-silent-failure-debugging/reports/docs-manager-260415-1206-ext-error-reporting.md +99 -0
  5. package/260415-1150-ext-silent-failure-debugging/reports/tester-260415-1159-extension-error-reporting.md +174 -0
  6. package/CHANGELOG.md +24 -0
  7. package/dist/web/assets/{_basePickBy-D-bUmjma.js → _basePickBy-Bj0dI1ei.js} +1 -1
  8. package/dist/web/assets/{_baseUniq-BnXXIfRB.js → _baseUniq-CyzdZeQH.js} +1 -1
  9. package/dist/web/assets/ai-settings-section-Bo9lCaTd.js +1 -0
  10. package/dist/web/assets/{api-settings-Qi2xRiHa.js → api-settings-CUxg9RE5.js} +1 -1
  11. package/dist/web/assets/{arc-DB9vXGzd.js → arc-CxgHJ7Z4.js} +1 -1
  12. package/dist/web/assets/architecture-PBZL5I3N-DDFO_NKq.js +1 -0
  13. package/dist/web/assets/{architectureDiagram-2XIMDMQ5-BBV25747.js → architectureDiagram-2XIMDMQ5-D16OotsC.js} +1 -1
  14. package/dist/web/assets/arrow-up-I9-21gkR.js +1 -0
  15. package/dist/web/assets/{blockDiagram-WCTKOSBZ-BOTnY2Lq.js → blockDiagram-WCTKOSBZ-Ct57Wtfk.js} +1 -1
  16. package/dist/web/assets/{c4Diagram-IC4MRINW-D7QAUdHD.js → c4Diagram-IC4MRINW-BIymcNsg.js} +1 -1
  17. package/dist/web/assets/channel-wumTB1if.js +1 -0
  18. package/dist/web/assets/chat-tab-R4gKsnxD.js +10 -0
  19. package/dist/web/assets/chevron-right-DY_wImxB.js +1 -0
  20. package/dist/web/assets/{chunk-4BX2VUAB-BnOVw77D.js → chunk-4BX2VUAB-CENmY7Kw.js} +1 -1
  21. package/dist/web/assets/{chunk-55IACEB6-BftA8DxR.js → chunk-55IACEB6-DhZGI1l3.js} +1 -1
  22. package/dist/web/assets/{chunk-7E7YKBS2-B0vnP8v3.js → chunk-7E7YKBS2-DZcnC7Ow.js} +1 -1
  23. package/dist/web/assets/{chunk-7R4GIKGN-Czlaj26D.js → chunk-7R4GIKGN-y8bfHEy-.js} +2 -2
  24. package/dist/web/assets/{chunk-C72U2L5F-DpEbDtMo.js → chunk-C72U2L5F-BHPkfQj2.js} +1 -1
  25. package/dist/web/assets/{chunk-EGIJ26TM-BWXe6lkx.js → chunk-EGIJ26TM-nant2LXl.js} +1 -1
  26. package/dist/web/assets/{chunk-FMBD7UC4-DspqhPfk.js → chunk-FMBD7UC4-Bog4cpN-.js} +1 -1
  27. package/dist/web/assets/{chunk-GEFDOKGD-D6HHRbYk.js → chunk-GEFDOKGD-86LFbsAC.js} +1 -1
  28. package/dist/web/assets/chunk-GLR3WWYH-Re-5eSlQ.js +2 -0
  29. package/dist/web/assets/chunk-HHEYEP7N-C45i5G_3.js +1 -0
  30. package/dist/web/assets/{chunk-JSJVCQXG-BC8wnMwf.js → chunk-JSJVCQXG-23eG9mgt.js} +1 -1
  31. package/dist/web/assets/{chunk-KX2RTZJC-D3VDtyvX.js → chunk-KX2RTZJC-CHj8TnTB.js} +1 -1
  32. package/dist/web/assets/{chunk-KYZI473N-Z-NBw_HS.js → chunk-KYZI473N-gqRLpJ4w.js} +1 -1
  33. package/dist/web/assets/{chunk-L3YUKLVL--RGkEh__.js → chunk-L3YUKLVL-DnSMmNFC.js} +1 -1
  34. package/dist/web/assets/{chunk-MX3YWQON-2B76t_Kx.js → chunk-MX3YWQON-B6g1ZH9X.js} +1 -1
  35. package/dist/web/assets/{chunk-NQ4KR5QH-BekY3tEi.js → chunk-NQ4KR5QH-DX32345Y.js} +1 -1
  36. package/dist/web/assets/{chunk-O4XLMI2P-2CJLfx_1.js → chunk-O4XLMI2P-Vp_V4P-b.js} +1 -1
  37. package/dist/web/assets/{chunk-OZEHJAEY-sug_L09P.js → chunk-OZEHJAEY-lKq2SWjA.js} +1 -1
  38. package/dist/web/assets/{chunk-PQ6SQG4A-_fwPRLQy.js → chunk-PQ6SQG4A-Bik13fTV.js} +1 -1
  39. package/dist/web/assets/{chunk-PU5JKC2W-BUaTFJVQ.js → chunk-PU5JKC2W-DD95Rx35.js} +1 -1
  40. package/dist/web/assets/chunk-QZHKN3VN-N3VXx1VH.js +1 -0
  41. package/dist/web/assets/{chunk-R5LLSJPH-C37xW0vj.js → chunk-R5LLSJPH-dRhXRnrb.js} +1 -1
  42. package/dist/web/assets/{chunk-WL4C6EOR-CCkt_MT6.js → chunk-WL4C6EOR-B1iIvLOG.js} +1 -1
  43. package/dist/web/assets/{chunk-XIRO2GV7-Dz2LBq7Y.js → chunk-XIRO2GV7-DZBoNl1_.js} +1 -1
  44. package/dist/web/assets/{chunk-XPW4576I-DenTbBuj.js → chunk-XPW4576I-CgLyyW03.js} +1 -1
  45. package/dist/web/assets/{chunk-XZSTWKYB-Dbp1nUSQ.js → chunk-XZSTWKYB-DjV8xl5A.js} +1 -1
  46. package/dist/web/assets/{chunk-YBOYWFTD-3OTKowjE.js → chunk-YBOYWFTD-D_ILLe6_.js} +1 -1
  47. package/dist/web/assets/classDiagram-VBA2DB6C-mr-Cb1me.js +1 -0
  48. package/dist/web/assets/classDiagram-v2-RAHNMMFH-BKe8_uda.js +1 -0
  49. package/dist/web/assets/clone--z5KLAuR.js +1 -0
  50. package/dist/web/assets/code-editor-Br0vzTOy.js +8 -0
  51. package/dist/web/assets/columns-2-IeETSfON.js +1 -0
  52. package/dist/web/assets/conflict-editor-BPgCjnNz.js +19 -0
  53. package/dist/web/assets/{cose-bilkent-S5V4N54A-MbmGZnt0.js → cose-bilkent-S5V4N54A-BGNPFv3x.js} +1 -1
  54. package/dist/web/assets/{csv-preview-uZ_7b8I7.js → csv-preview-BZRICDP0.js} +2 -2
  55. package/dist/web/assets/{dagre-CPhI6v-K.js → dagre-CkhlMHnx.js} +1 -1
  56. package/dist/web/assets/{dagre-KLK3FWXG-CmSE-oNj.js → dagre-KLK3FWXG-Cnp996VG.js} +1 -1
  57. package/dist/web/assets/database-CgTomMxt.js +1 -0
  58. package/dist/web/assets/{database-viewer-5xljX0JI.js → database-viewer-DaUoQ-oR.js} +2 -2
  59. package/dist/web/assets/{diagram-E7M64L7V-B5XG3ZT7.js → diagram-E7M64L7V-BZF0tSOr.js} +1 -1
  60. package/dist/web/assets/{diagram-IFDJBPK2-BsP248aX.js → diagram-IFDJBPK2-nUcO8sN8.js} +1 -1
  61. package/dist/web/assets/{diagram-P4PSJMXO-Cna3408N.js → diagram-P4PSJMXO-CW0eCkwC.js} +1 -1
  62. package/dist/web/assets/diff-viewer-BzvK3gAE.js +4 -0
  63. package/dist/web/assets/dist-CM0oD8tQ.js +1 -0
  64. package/dist/web/assets/{erDiagram-INFDFZHY-B7SgktiR.js → erDiagram-INFDFZHY-DSkriYZ9.js} +1 -1
  65. package/dist/web/assets/extension-webview-CGepEw-b.js +3 -0
  66. package/dist/web/assets/{flowDiagram-PKNHOUZH-FOYZZ1OB.js → flowDiagram-PKNHOUZH-CFYAfZBx.js} +1 -1
  67. package/dist/web/assets/{ganttDiagram-A5KZAMGK-CnHVYh9v.js → ganttDiagram-A5KZAMGK-KSn4XAU4.js} +1 -1
  68. package/dist/web/assets/gitGraph-HDMCJU4V-OkvBPi6H.js +1 -0
  69. package/dist/web/assets/{gitGraphDiagram-K3NZZRJ6-0G9XxZay.js → gitGraphDiagram-K3NZZRJ6-BMgjjVys.js} +1 -1
  70. package/dist/web/assets/{graphlib-CNiBwlg_.js → graphlib-BWe1iK_s.js} +1 -1
  71. package/dist/web/assets/index-CKsEzQ4f.js +26 -0
  72. package/dist/web/assets/index-Chf0otez.css +2 -0
  73. package/dist/web/assets/info-3K5VOQVL-BDU2_bYD.js +1 -0
  74. package/dist/web/assets/infoDiagram-LFFYTUFH-Diq4Cyc3.js +2 -0
  75. package/dist/web/assets/input-BHj0veau.js +45 -0
  76. package/dist/web/assets/{isEmpty-CcCb5n2-.js → isEmpty-BfLnxq-B.js} +1 -1
  77. package/dist/web/assets/{ishikawaDiagram-PHBUUO56-D4QCzh5J.js → ishikawaDiagram-PHBUUO56-CiVEvp8o.js} +1 -1
  78. package/dist/web/assets/{journeyDiagram-4ABVD52K-CnHYNfKW.js → journeyDiagram-4ABVD52K-CG_v5Aho.js} +1 -1
  79. package/dist/web/assets/jsx-runtime-BRW_vwa9.js +1 -0
  80. package/dist/web/assets/{kanban-definition-K7BYSVSG-Bh_g3EVu.js → kanban-definition-K7BYSVSG-miB0-_Zq.js} +1 -1
  81. package/dist/web/assets/keybindings-store-D5zgHod8.js +1 -0
  82. package/dist/web/assets/{line-6d3eBADm.js → line-CSuSrJ9J.js} +1 -1
  83. package/dist/web/assets/{linear-cA_2lQy7.js → linear-DFN_MPsw.js} +1 -1
  84. package/dist/web/assets/{markdown-renderer-CZ07F7T6.js → markdown-renderer-DSYnGywb.js} +6 -6
  85. package/dist/web/assets/{mermaid-parser.core-C3kd7JXM.js → mermaid-parser.core-CFdP1Z5_.js} +2 -2
  86. package/dist/web/assets/{mindmap-definition-YRQLILUH-CYiUwhr_.js → mindmap-definition-YRQLILUH-pYPWwASE.js} +1 -1
  87. package/dist/web/assets/{ordinal-XHK5vIzZ.js → ordinal-DpFn432U.js} +1 -1
  88. package/dist/web/assets/packet-RMMSAZCW-BwpIpYB3.js +1 -0
  89. package/dist/web/assets/pie-UPGHQEXC-BPgAfmes.js +1 -0
  90. package/dist/web/assets/{pieDiagram-SKSYHLDU-D0S7jeZA.js → pieDiagram-SKSYHLDU-Dovdlvhu.js} +1 -1
  91. package/dist/web/assets/plus-DQGIb4mQ.js +1 -0
  92. package/dist/web/assets/port-forwarding-tab-vmqDKmk2.js +1 -0
  93. package/dist/web/assets/{postgres-viewer-RldlAO_m.js → postgres-viewer-0lIAosrr.js} +3 -3
  94. package/dist/web/assets/{quadrantDiagram-337W2JSQ-0hNP63hW.js → quadrantDiagram-337W2JSQ-TXe6cU_F.js} +1 -1
  95. package/dist/web/assets/radar-KQ55EAFF-TqxBkWx-.js +1 -0
  96. package/dist/web/assets/refresh-cw-Clk8fdUD.js +1 -0
  97. package/dist/web/assets/{requirementDiagram-Z7DCOOCP-BVnmqFbL.js → requirementDiagram-Z7DCOOCP-CuiiuGS9.js} +1 -1
  98. package/dist/web/assets/{sankeyDiagram-WA2Y5GQK-DVkYdCJb.js → sankeyDiagram-WA2Y5GQK-BbRmhv0t.js} +1 -1
  99. package/dist/web/assets/scroll-area-BpXCNme3.js +1 -0
  100. package/dist/web/assets/{sequenceDiagram-2WXFIKYE-B80s7sOg.js → sequenceDiagram-2WXFIKYE-B2D8IQDb.js} +1 -1
  101. package/dist/web/assets/settings-tab-CMnv1fce.js +1 -0
  102. package/dist/web/assets/{sql-query-editor-CjZ7Z6XL.js → sql-query-editor-Bc2hAwqT.js} +1 -1
  103. package/dist/web/assets/sqlite-viewer-B60MS2Dy.js +1 -0
  104. package/dist/web/assets/square-vBdqj0bF.js +1 -0
  105. package/dist/web/assets/{stateDiagram-RAJIS63D-BPLXgXRR.js → stateDiagram-RAJIS63D-ylr4HxPu.js} +1 -1
  106. package/dist/web/assets/stateDiagram-v2-FVOUBMTO-D6zvxf3M.js +1 -0
  107. package/dist/web/assets/table-Bi27fEaN.js +1 -0
  108. package/dist/web/assets/{terminal-tab-DjzD8GLn.js → terminal-tab-CCJoLstH.js} +2 -2
  109. package/dist/web/assets/text-wrap-D_OmSzhp.js +1 -0
  110. package/dist/web/assets/{timeline-definition-YZTLITO2-fa_51u1X.js → timeline-definition-YZTLITO2-pMv1grvM.js} +1 -1
  111. package/dist/web/assets/trash-2-CNuB-htI.js +1 -0
  112. package/dist/web/assets/treemap-KZPCXAKY-Kck06FKU.js +1 -0
  113. package/dist/web/assets/{use-monaco-theme-D9XFxQuU.js → use-monaco-theme-BJK48EmK.js} +1 -1
  114. package/dist/web/assets/{vennDiagram-LZ73GAT5-kX4jJn6W.js → vennDiagram-LZ73GAT5-C-rkIUbo.js} +1 -1
  115. package/dist/web/assets/x-Dw3TjeY_.js +1 -0
  116. package/dist/web/assets/{xychartDiagram-JWTSCODW-Bzm5lZBs.js → xychartDiagram-JWTSCODW-CtpjAakO.js} +1 -1
  117. package/dist/web/index.html +18 -22
  118. package/dist/web/sw.js +1 -1
  119. package/docs/codebase-summary.md +169 -13
  120. package/docs/extension-development-guide.md +98 -1
  121. package/docs/journals/260414-1400-ext-git-graph-port-complete.md +147 -0
  122. package/docs/journals/260414-1452-git-graph-faithful-port.md +144 -0
  123. package/docs/journals/260414-1810-git-graph-ui-improvements-complete.md +261 -0
  124. package/docs/journals/260414-2001-bundled-extensions.md +219 -0
  125. package/docs/project-changelog.md +123 -21
  126. package/docs/project-roadmap.md +4 -2
  127. package/docs/system-architecture.md +77 -6
  128. package/package.json +1 -1
  129. package/packages/ext-git-graph/package.json +30 -0
  130. package/packages/ext-git-graph/src/extension-integration.test.ts +230 -0
  131. package/packages/ext-git-graph/src/extension-parsers.test.ts +193 -0
  132. package/packages/ext-git-graph/src/extension.ts +921 -0
  133. package/packages/ext-git-graph/src/git-log-parser.test.ts +271 -0
  134. package/packages/ext-git-graph/src/git-log-parser.ts +38 -0
  135. package/packages/ext-git-graph/src/types.ts +192 -0
  136. package/packages/ext-git-graph/src/webview-html.test.ts +142 -0
  137. package/packages/ext-git-graph/src/webview-html.ts +2417 -0
  138. package/packages/vscode-compat/src/index.ts +4 -0
  139. package/packages/vscode-compat/src/process.ts +25 -0
  140. package/packages/vscode-compat/src/window.ts +10 -0
  141. package/src/cli/commands/ext-cmd.ts +3 -1
  142. package/src/server/ws/extensions.ts +34 -4
  143. package/src/services/contribution-registry.ts +14 -1
  144. package/src/services/extension-host-worker.ts +12 -3
  145. package/src/services/extension-manifest.ts +18 -1
  146. package/src/services/extension-rpc-handlers.ts +68 -2
  147. package/src/services/extension.service.ts +63 -9
  148. package/src/types/extension-messages.ts +3 -1
  149. package/src/types/extension.ts +8 -0
  150. package/src/web/components/editor/code-editor.tsx +16 -4
  151. package/src/web/components/editor/conflict-editor.tsx +368 -0
  152. package/src/web/components/extensions/extension-webview.tsx +153 -12
  153. package/src/web/components/layout/command-palette.tsx +41 -17
  154. package/src/web/components/layout/editor-panel.tsx +16 -4
  155. package/src/web/components/layout/mobile-nav.tsx +6 -5
  156. package/src/web/components/layout/tab-bar.tsx +3 -3
  157. package/src/web/components/layout/tab-content.tsx +17 -5
  158. package/src/web/components/settings/keyboard-shortcuts-section.tsx +46 -1
  159. package/src/web/hooks/use-extension-ws.ts +30 -4
  160. package/src/web/hooks/use-global-keybindings.ts +24 -2
  161. package/src/web/hooks/use-url-sync.ts +8 -3
  162. package/src/web/stores/extension-store.ts +8 -0
  163. package/src/web/stores/keybindings-store.ts +2 -3
  164. package/src/web/stores/panel-store.ts +2 -2
  165. package/src/web/stores/panel-utils.ts +6 -2
  166. package/src/web/stores/tab-store.ts +3 -2
  167. package/dist/web/assets/ai-settings-section-D6d-RmR6.js +0 -1
  168. package/dist/web/assets/architecture-PBZL5I3N-DpVzOETR.js +0 -1
  169. package/dist/web/assets/arrow-up-BigIMx-e.js +0 -1
  170. package/dist/web/assets/channel-Cgy1thYT.js +0 -1
  171. package/dist/web/assets/chat-tab-DXBb9Y3U.js +0 -10
  172. package/dist/web/assets/check-ePA3ZvK4.js +0 -1
  173. package/dist/web/assets/chevron-down-EQA06nR-.js +0 -1
  174. package/dist/web/assets/chevron-right-CXzzT44u.js +0 -1
  175. package/dist/web/assets/chunk-GLR3WWYH-CxUl1sdz.js +0 -2
  176. package/dist/web/assets/chunk-HHEYEP7N-DN7ebS2Y.js +0 -1
  177. package/dist/web/assets/chunk-QZHKN3VN-C4La7oLj.js +0 -1
  178. package/dist/web/assets/classDiagram-VBA2DB6C-C3IyfqG-.js +0 -1
  179. package/dist/web/assets/classDiagram-v2-RAHNMMFH-Dcvhz2pb.js +0 -1
  180. package/dist/web/assets/clone--C7Tby8z.js +0 -1
  181. package/dist/web/assets/code-editor-Cr7JrBKC.js +0 -8
  182. package/dist/web/assets/columns-2-BZ9uqssV.js +0 -1
  183. package/dist/web/assets/createLucideIcon-PuMiQgHl.js +0 -1
  184. package/dist/web/assets/database-D1ToEV9d.js +0 -1
  185. package/dist/web/assets/diff-viewer-BBr6e_gb.js +0 -4
  186. package/dist/web/assets/dist-KUoHa6tg.js +0 -1
  187. package/dist/web/assets/extension-webview-B0klBip8.js +0 -3
  188. package/dist/web/assets/eye-CNcBU6Tx.js +0 -1
  189. package/dist/web/assets/git-graph-CDiwGa0g.js +0 -1
  190. package/dist/web/assets/gitGraph-HDMCJU4V-DcPyMEIJ.js +0 -1
  191. package/dist/web/assets/index-CkaCzNgO.css +0 -2
  192. package/dist/web/assets/index-Ic5uTu20.js +0 -26
  193. package/dist/web/assets/info-3K5VOQVL-Dw4O15cw.js +0 -1
  194. package/dist/web/assets/infoDiagram-LFFYTUFH-DFhmsucr.js +0 -2
  195. package/dist/web/assets/input-CcbTF6ih.js +0 -45
  196. package/dist/web/assets/jsx-runtime-R_NjdZtX.js +0 -1
  197. package/dist/web/assets/keybindings-store-CxE6BlG2.js +0 -1
  198. package/dist/web/assets/packet-RMMSAZCW-o3LmdL8H.js +0 -1
  199. package/dist/web/assets/pie-UPGHQEXC-BjNP0M3B.js +0 -1
  200. package/dist/web/assets/plus-Iso5r9vD.js +0 -1
  201. package/dist/web/assets/port-forwarding-tab-BPuSc6pI.js +0 -1
  202. package/dist/web/assets/radar-KQ55EAFF-gDgOiaME.js +0 -1
  203. package/dist/web/assets/refresh-cw-BgQzFNaG.js +0 -1
  204. package/dist/web/assets/scroll-area-i4EZlOl_.js +0 -1
  205. package/dist/web/assets/settings-tab-BzSSN2BQ.js +0 -1
  206. package/dist/web/assets/sqlite-viewer-CoyZOM_Y.js +0 -1
  207. package/dist/web/assets/square-pfn_LYYy.js +0 -1
  208. package/dist/web/assets/stateDiagram-v2-FVOUBMTO-DksQJ7es.js +0 -1
  209. package/dist/web/assets/table-CHv2x_qg.js +0 -1
  210. package/dist/web/assets/tag-Bb_UFXt0.js +0 -1
  211. package/dist/web/assets/text-wrap-D8BbQYTx.js +0 -1
  212. package/dist/web/assets/trash-2-DYCa06CV.js +0 -1
  213. package/dist/web/assets/treemap-KZPCXAKY-DwFqAvnj.js +0 -1
  214. package/dist/web/assets/x-BXecj-16.js +0 -1
  215. package/src/web/components/git/git-graph-branch-label.tsx +0 -124
  216. package/src/web/components/git/git-graph-constants.ts +0 -185
  217. package/src/web/components/git/git-graph-detail.tsx +0 -107
  218. package/src/web/components/git/git-graph-dialog.tsx +0 -72
  219. package/src/web/components/git/git-graph-row.tsx +0 -167
  220. package/src/web/components/git/git-graph-settings-dialog.tsx +0 -104
  221. package/src/web/components/git/git-graph-svg.tsx +0 -54
  222. package/src/web/components/git/git-graph-toolbar.tsx +0 -195
  223. package/src/web/components/git/git-graph.tsx +0 -193
  224. package/src/web/components/git/use-column-resize.ts +0 -33
  225. package/src/web/components/git/use-git-graph.ts +0 -201
  226. /package/dist/web/assets/{api-client-wQbeUyeh.js → api-client-BvxmRZUi.js} +0 -0
  227. /package/dist/web/assets/{array-X0JlPOfd.js → array-BFDiaBgf.js} +0 -0
  228. /package/dist/web/assets/{csv-parser-CElqio6o.js → csv-parser-i7fjqP2H.js} +0 -0
  229. /package/dist/web/assets/{cytoscape.esm-BfIOPvwt.js → cytoscape.esm-C8i2jUzT.js} +0 -0
  230. /package/dist/web/assets/{defaultLocale-B6RGN4id.js → defaultLocale-ZeknFqNe.js} +0 -0
  231. /package/dist/web/assets/{dist-CK1enexV.js → dist-DZmJeHOA.js} +0 -0
  232. /package/dist/web/assets/{init-BmUWJJHz.js → init-0VJVrkRJ.js} +0 -0
  233. /package/dist/web/assets/{isArrayLikeObject-BrCM-iA1.js → isArrayLikeObject-ClzWCpcm.js} +0 -0
  234. /package/dist/web/assets/{katex-xQS_6bNb.js → katex-DR0kdMDv.js} +0 -0
  235. /package/dist/web/assets/{lib-CfWBrYll.js → lib-DSLzfeW0.js} +0 -0
  236. /package/dist/web/assets/{math-CpLFzrfV.js → math-CRc16Nj6.js} +0 -0
  237. /package/dist/web/assets/{path-CoPyR7c2.js → path-INs8XTPH.js} +0 -0
  238. /package/dist/web/assets/{preload-helper-CH6UZRzu.js → preload-helper-mr3rCizq.js} +0 -0
  239. /package/dist/web/assets/{react-j5zqhEum.js → react-0tkk-ztn.js} +0 -0
  240. /package/dist/web/assets/{rough.esm-D5NinLFK.js → rough.esm-eLccZ4OJ.js} +0 -0
  241. /package/dist/web/assets/{sql-completion-provider-D0xutVaK.js → sql-completion-provider-B8uUWWej.js} +0 -0
  242. /package/dist/web/assets/{src-j04igtQ5.js → src-CqyWLlNZ.js} +0 -0
  243. /package/dist/web/assets/{utils-CSCvNZxE.js → utils-DX8jb5qv.js} +0 -0
@@ -1,195 +0,0 @@
1
- import { useState, useRef, useEffect } from "react";
2
- import {
3
- RefreshCw,
4
- Download,
5
- Search,
6
- Settings,
7
- X,
8
- ChevronDown,
9
- Check,
10
- } from "lucide-react";
11
- import { Button } from "@/components/ui/button";
12
- import { Input } from "@/components/ui/input";
13
- import type { GitBranch } from "../../../types/git";
14
-
15
- interface GitGraphToolbarProps {
16
- branches: GitBranch[];
17
- branchFilter: string;
18
- onBranchFilterChange: (value: string) => void;
19
- searchQuery: string;
20
- onSearchQueryChange: (value: string) => void;
21
- showSearch: boolean;
22
- onToggleSearch: () => void;
23
- onFetch: () => void;
24
- onRefresh: () => void;
25
- onOpenSettings?: () => void;
26
- loading: boolean;
27
- acting: boolean;
28
- projectName?: string;
29
- }
30
-
31
- export function GitGraphToolbar({
32
- branches,
33
- branchFilter,
34
- onBranchFilterChange,
35
- searchQuery,
36
- onSearchQueryChange,
37
- showSearch,
38
- onToggleSearch,
39
- onFetch,
40
- onRefresh,
41
- onOpenSettings,
42
- loading,
43
- acting,
44
- projectName,
45
- }: GitGraphToolbarProps) {
46
- const localBranches = branches.filter((b) => !b.remote);
47
- const [dropdownOpen, setDropdownOpen] = useState(false);
48
- const [branchSearch, setBranchSearch] = useState("");
49
- const dropdownRef = useRef<HTMLDivElement>(null);
50
-
51
- // Close dropdown on outside click
52
- useEffect(() => {
53
- if (!dropdownOpen) return;
54
- const handler = (e: MouseEvent) => {
55
- if (dropdownRef.current && !dropdownRef.current.contains(e.target as Node)) {
56
- setDropdownOpen(false);
57
- setBranchSearch("");
58
- }
59
- };
60
- document.addEventListener("mousedown", handler);
61
- return () => document.removeEventListener("mousedown", handler);
62
- }, [dropdownOpen]);
63
-
64
- const filteredBranches = branchSearch
65
- ? localBranches.filter((b) =>
66
- b.name.toLowerCase().includes(branchSearch.toLowerCase()),
67
- )
68
- : localBranches;
69
-
70
- const selectedLabel =
71
- branchFilter === "__all__"
72
- ? "Show All"
73
- : localBranches.find((b) => b.name === branchFilter)?.name ?? "Show All";
74
-
75
- return (
76
- <div className="border-b bg-background">
77
- <div className="flex items-center gap-1.5 px-2 py-1.5">
78
- {/* Repo (single for now) */}
79
- <div className="flex items-center gap-1 text-xs text-muted-foreground shrink-0">
80
- <span className="font-semibold">Repo:</span>
81
- <span className="font-medium text-foreground truncate max-w-[120px]">
82
- {projectName ?? "—"}
83
- </span>
84
- </div>
85
-
86
- <div className="w-px h-4 bg-border mx-1" />
87
-
88
- {/* Branch filter — custom searchable dropdown */}
89
- <div className="relative shrink-0" ref={dropdownRef}>
90
- <button
91
- type="button"
92
- className="flex items-center gap-1 h-6 px-2 text-xs border rounded-md bg-transparent hover:bg-muted/50"
93
- onClick={() => { setDropdownOpen((o) => !o); setBranchSearch(""); }}
94
- >
95
- <span className="font-semibold text-muted-foreground">Branches:</span>
96
- <span className="max-w-[100px] truncate">{selectedLabel}</span>
97
- <ChevronDown className="size-3 opacity-50" />
98
- </button>
99
- {dropdownOpen && (
100
- <div className="absolute top-full left-0 mt-1 z-50 w-[220px] rounded-md border bg-popover shadow-md">
101
- <div className="p-1.5">
102
- <Input
103
- className="h-6 text-xs px-2"
104
- placeholder="Filter branches..."
105
- value={branchSearch}
106
- onChange={(e) => setBranchSearch(e.target.value)}
107
- autoFocus
108
- />
109
- </div>
110
- <div className="max-h-[200px] overflow-y-auto p-1">
111
- <BranchDropdownItem
112
- label="Show All"
113
- selected={branchFilter === "__all__"}
114
- onClick={() => { onBranchFilterChange("__all__"); setDropdownOpen(false); }}
115
- />
116
- {filteredBranches.map((b) => (
117
- <BranchDropdownItem
118
- key={b.name}
119
- label={b.name}
120
- current={b.current}
121
- selected={branchFilter === b.name}
122
- onClick={() => { onBranchFilterChange(b.name); setDropdownOpen(false); }}
123
- />
124
- ))}
125
- {filteredBranches.length === 0 && branchSearch && (
126
- <div className="px-2 py-1.5 text-xs text-muted-foreground">No branches found</div>
127
- )}
128
- </div>
129
- </div>
130
- )}
131
- </div>
132
-
133
- <div className="flex-1" />
134
-
135
- {/* Search input (toggled) */}
136
- {showSearch && (
137
- <div className="flex items-center gap-1">
138
- <Input
139
- className="h-6 text-xs w-[160px] px-2"
140
- placeholder="Search commits..."
141
- value={searchQuery}
142
- onChange={(e) => onSearchQueryChange(e.target.value)}
143
- autoFocus
144
- />
145
- <Button variant="ghost" size="icon-xs" onClick={onToggleSearch}>
146
- <X className="size-3" />
147
- </Button>
148
- </div>
149
- )}
150
-
151
- {/* Action buttons */}
152
- {!showSearch && (
153
- <Button variant="ghost" size="icon-xs" onClick={onToggleSearch} title="Find">
154
- <Search className="size-3.5" />
155
- </Button>
156
- )}
157
- <Button variant="ghost" size="icon-xs" onClick={onOpenSettings} title="Settings">
158
- <Settings className="size-3.5" />
159
- </Button>
160
- <Button variant="ghost" size="icon-xs" onClick={onFetch} disabled={acting} title="Fetch">
161
- <Download className="size-3.5" />
162
- </Button>
163
- <Button variant="ghost" size="icon-xs" onClick={onRefresh} disabled={acting} title="Refresh">
164
- <RefreshCw className={`size-3.5 ${loading ? "animate-spin" : ""}`} />
165
- </Button>
166
- </div>
167
- </div>
168
- );
169
- }
170
-
171
- function BranchDropdownItem({
172
- label,
173
- selected,
174
- current,
175
- onClick,
176
- }: {
177
- label: string;
178
- selected: boolean;
179
- current?: boolean;
180
- onClick: () => void;
181
- }) {
182
- return (
183
- <button
184
- type="button"
185
- className="flex items-center gap-2 w-full px-2 py-1 text-xs rounded-sm hover:bg-accent hover:text-accent-foreground text-left"
186
- onClick={onClick}
187
- >
188
- <Check className={`size-3 shrink-0 ${selected ? "opacity-100" : "opacity-0"}`} />
189
- <span className="truncate flex-1">{label}</span>
190
- {current && (
191
- <span className="text-[10px] text-muted-foreground italic">current</span>
192
- )}
193
- </button>
194
- );
195
- }
@@ -1,193 +0,0 @@
1
- import { useState, useRef, useCallback } from "react";
2
- import { Loader2 } from "lucide-react";
3
- import { Button } from "@/components/ui/button";
4
- import { LANE_WIDTH, ROW_HEIGHT } from "./git-graph-constants";
5
- import { useGitGraph } from "./use-git-graph";
6
- import { useColumnResize } from "./use-column-resize";
7
- import { GitGraphToolbar } from "./git-graph-toolbar";
8
- import { GitGraphSvg } from "./git-graph-svg";
9
- import { GitGraphRow } from "./git-graph-row";
10
- import { GitGraphDetail } from "./git-graph-detail";
11
- import { GitGraphDialog } from "./git-graph-dialog";
12
- import { GitGraphSettingsDialog } from "./git-graph-settings-dialog";
13
-
14
- interface GitGraphProps {
15
- metadata?: Record<string, unknown>;
16
- }
17
-
18
- export function GitGraph({ metadata }: GitGraphProps) {
19
- const projectName = metadata?.projectName as string | undefined;
20
- const g = useGitGraph(projectName);
21
- const [dialogState, setDialogState] = useState<{
22
- type: "branch" | "tag" | null;
23
- hash?: string;
24
- }>({ type: null });
25
- const [showSettings, setShowSettings] = useState(false);
26
-
27
- // Resizable graph column — use ref to avoid stale closure
28
- const isMobile = typeof window !== "undefined" && window.innerWidth < 768;
29
- const defaultGraphW = (isMobile ? 6 : 10) * LANE_WIDTH + LANE_WIDTH;
30
- const [graphColWidth, setGraphColWidth] = useState(defaultGraphW);
31
- const graphWidthRef = useRef(defaultGraphW);
32
- graphWidthRef.current = graphColWidth;
33
- const graphDragging = useRef(false);
34
-
35
- const startGraphResize = (startX: number) => {
36
- graphDragging.current = true;
37
- const startW = graphWidthRef.current;
38
- const onMove = (ev: MouseEvent | TouchEvent) => {
39
- if (!graphDragging.current) return;
40
- const cx = "touches" in ev ? ev.touches[0]!.clientX : ev.clientX;
41
- setGraphColWidth(Math.max(40, startW + cx - startX));
42
- };
43
- const onUp = () => {
44
- graphDragging.current = false;
45
- window.removeEventListener("mousemove", onMove);
46
- window.removeEventListener("mouseup", onUp);
47
- window.removeEventListener("touchmove", onMove);
48
- window.removeEventListener("touchend", onUp);
49
- };
50
- window.addEventListener("mousemove", onMove);
51
- window.addEventListener("mouseup", onUp);
52
- window.addEventListener("touchmove", onMove, { passive: false });
53
- window.addEventListener("touchend", onUp);
54
- };
55
-
56
- // Resizable table columns (Date, Author, Commit)
57
- const { widths: colW, startResize } = useColumnResize({ date: 80, author: 120, commit: 70 });
58
-
59
- // Infinite scroll — ref-based to avoid stale closure
60
- const loadMoreRef = useRef(g.loadMore);
61
- loadMoreRef.current = g.loadMore;
62
- const handleScroll = useCallback((e: React.UIEvent<HTMLDivElement>) => {
63
- const el = e.currentTarget;
64
- if (el.scrollHeight - el.scrollTop - el.clientHeight < 200) {
65
- loadMoreRef.current();
66
- }
67
- }, []);
68
-
69
- if (!projectName) return <EmptyState msg="No project selected." />;
70
- if (g.loading && !g.data) return <LoadingState />;
71
- if (g.error && !g.data) return <ErrorState error={g.error} onRetry={g.fetchGraph} />;
72
-
73
- return (
74
- <div className="flex flex-col h-full">
75
- <GitGraphToolbar
76
- branches={g.data?.branches ?? []} branchFilter={g.branchFilter}
77
- onBranchFilterChange={g.setBranchFilter} searchQuery={g.searchQuery}
78
- onSearchQueryChange={g.setSearchQuery} showSearch={g.showSearch}
79
- onToggleSearch={() => g.setShowSearch(!g.showSearch)}
80
- onFetch={g.fetchFromRemotes} onRefresh={g.fetchGraph}
81
- onOpenSettings={() => setShowSettings(true)}
82
- loading={g.loading} acting={g.acting} projectName={projectName}
83
- />
84
- {g.error && <div className="px-3 py-1.5 text-xs text-destructive bg-destructive/10">{g.error}</div>}
85
-
86
- <div className="flex-1 overflow-auto" onScroll={handleScroll}>
87
- <div className="flex min-w-max md:min-w-0">
88
- {/* Graph SVG column — overflow-hidden clips lanes beyond column width */}
89
- <div className="sticky left-0 z-10 shrink-0 bg-background relative overflow-hidden" style={{ width: `${graphColWidth}px` }}>
90
- <div className="text-[11px] font-semibold text-muted-foreground px-2 border-b bg-background sticky top-0 z-20"
91
- style={{ height: `${ROW_HEIGHT}px`, lineHeight: `${ROW_HEIGHT}px` }}>Graph</div>
92
- <GitGraphSvg commits={g.filteredCommits} laneMap={g.filteredLanes.laneMap}
93
- svgPaths={g.svgPaths} width={(g.filteredLanes.maxLane + 2) * LANE_WIDTH} height={g.svgHeight} headHash={g.headHash} />
94
- <div className="absolute top-0 right-0 w-1.5 h-full cursor-col-resize hover:bg-primary/30"
95
- onMouseDown={(e) => { e.preventDefault(); startGraphResize(e.clientX); }}
96
- onTouchStart={(e) => startGraphResize(e.touches[0]!.clientX)} />
97
- </div>
98
-
99
- {/* Commit table */}
100
- <div className="flex-1 min-w-[400px]">
101
- <table className="w-full border-collapse text-xs" style={{ tableLayout: "fixed" }}>
102
- <colgroup>
103
- <col />
104
- <col style={{ width: `${colW.date}px` }} />
105
- <col style={{ width: `${colW.author}px` }} />
106
- <col style={{ width: `${colW.commit}px` }} />
107
- </colgroup>
108
- <thead className="sticky top-0 z-10 bg-background">
109
- <tr className="border-b text-[11px] font-semibold text-muted-foreground" style={{ height: `${ROW_HEIGHT}px` }}>
110
- <th className="text-left px-2 font-semibold">Description</th>
111
- <ResizableTh label="Date" colKey="date" onStartResize={startResize} />
112
- <ResizableTh label="Author" colKey="author" onStartResize={startResize} />
113
- <th className="text-left px-2 font-semibold">Commit</th>
114
- </tr>
115
- </thead>
116
- <tbody>
117
- {g.filteredCommits.map((commit) => (
118
- <GitGraphRow key={commit.hash} commit={commit}
119
- lane={g.filteredLanes.laneMap.get(commit.hash) ?? 0}
120
- isSelected={g.selectedCommit?.hash === commit.hash}
121
- isHead={commit.hash === g.headHash}
122
- labels={g.commitLabels.get(commit.hash) ?? []}
123
- currentBranch={g.currentBranch}
124
- onSelect={() => g.selectCommit(commit)}
125
- onCheckout={g.handleCheckout} onCherryPick={g.handleCherryPick}
126
- onRevert={g.handleRevert} onMerge={g.handleMerge}
127
- onDeleteBranch={g.handleDeleteBranch} onPushBranch={g.handlePushBranch}
128
- onCreatePr={g.handleCreatePr}
129
- onOpenCreateBranch={(h) => setDialogState({ type: "branch", hash: h })}
130
- onOpenCreateTag={(h) => setDialogState({ type: "tag", hash: h })}
131
- onOpenDiff={() => g.openDiffForCommit(commit)}
132
- onCopyHash={() => g.copyHash(commit.hash)} />
133
- ))}
134
- </tbody>
135
- </table>
136
- {g.loadingMore && (
137
- <div className="flex items-center justify-center gap-2 py-3 text-xs text-muted-foreground">
138
- <Loader2 className="size-3.5 animate-spin" /> Loading more commits...
139
- </div>
140
- )}
141
- {!g.hasMore && g.data && g.data.commits.length > 0 && (
142
- <div className="text-center py-2 text-xs text-muted-foreground">
143
- {g.data.commits.length} commits loaded
144
- </div>
145
- )}
146
- </div>
147
- </div>
148
- </div>
149
-
150
- {g.selectedCommit && projectName && (
151
- <GitGraphDetail commit={g.selectedCommit} files={g.commitFiles}
152
- loadingDetail={g.loadingDetail} projectName={projectName}
153
- onClose={() => g.setSelectedCommit(null)} copyHash={g.copyHash} />
154
- )}
155
- <GitGraphDialog type={dialogState.type} hash={dialogState.hash}
156
- onClose={() => setDialogState({ type: null })}
157
- onCreateBranch={g.handleCreateBranch} onCreateTag={g.handleCreateTag} />
158
- <GitGraphSettingsDialog open={showSettings} onClose={() => setShowSettings(false)}
159
- projectName={projectName} branches={g.data?.branches ?? []} />
160
- </div>
161
- );
162
- }
163
-
164
- function ResizableTh({ label, colKey, onStartResize }: {
165
- label: string; colKey: string; onStartResize: (key: string, x: number) => void;
166
- }) {
167
- return (
168
- <th className="text-left px-2 font-semibold relative">
169
- {label}
170
- <div className="absolute top-0 right-0 w-1.5 h-full cursor-col-resize hover:bg-primary/30"
171
- onMouseDown={(e) => { e.preventDefault(); onStartResize(colKey, e.clientX); }}
172
- onTouchStart={(e) => onStartResize(colKey, e.touches[0]!.clientX)} />
173
- </th>
174
- );
175
- }
176
-
177
- function EmptyState({ msg }: { msg: string }) {
178
- return <div className="flex items-center justify-center h-full text-muted-foreground text-sm">{msg}</div>;
179
- }
180
- function LoadingState() {
181
- return (
182
- <div className="flex items-center justify-center h-full gap-2 text-muted-foreground">
183
- <Loader2 className="size-5 animate-spin" /><span className="text-sm">Loading git graph...</span>
184
- </div>
185
- );
186
- }
187
- function ErrorState({ error, onRetry }: { error: string; onRetry: () => void }) {
188
- return (
189
- <div className="flex flex-col items-center justify-center h-full gap-2 text-destructive text-sm">
190
- <p>{error}</p><Button variant="outline" size="sm" onClick={onRetry}>Retry</Button>
191
- </div>
192
- );
193
- }
@@ -1,33 +0,0 @@
1
- import { useState, useRef } from "react";
2
-
3
- /** Generic column resize hook — uses refs to avoid stale closures */
4
- export function useColumnResize(initialWidths: Record<string, number>) {
5
- const [widths, setWidths] = useState(initialWidths);
6
- const widthsRef = useRef(initialWidths);
7
- widthsRef.current = widths;
8
- const dragging = useRef(false);
9
-
10
- const startResize = (colKey: string, startX: number) => {
11
- dragging.current = true;
12
- const startW = widthsRef.current[colKey] ?? 80;
13
- const onMove = (ev: MouseEvent | TouchEvent) => {
14
- if (!dragging.current) return;
15
- const clientX = "touches" in ev ? ev.touches[0]!.clientX : ev.clientX;
16
- const newW = Math.max(40, startW + clientX - startX);
17
- setWidths((prev) => ({ ...prev, [colKey]: newW }));
18
- };
19
- const onUp = () => {
20
- dragging.current = false;
21
- window.removeEventListener("mousemove", onMove);
22
- window.removeEventListener("mouseup", onUp);
23
- window.removeEventListener("touchmove", onMove);
24
- window.removeEventListener("touchend", onUp);
25
- };
26
- window.addEventListener("mousemove", onMove);
27
- window.addEventListener("mouseup", onUp);
28
- window.addEventListener("touchmove", onMove, { passive: false });
29
- window.addEventListener("touchend", onUp);
30
- };
31
-
32
- return { widths, startResize };
33
- }
@@ -1,201 +0,0 @@
1
- import { useEffect, useState, useCallback, useMemo, useRef } from "react";
2
- import { api, projectUrl } from "@/lib/api-client";
3
- import { useTabStore } from "@/stores/tab-store";
4
- import type { GitGraphData, GitCommit } from "../../../types/git";
5
- import { buildCommitLabels, computeLanes, computeSvgPaths, ROW_HEIGHT } from "./git-graph-constants";
6
-
7
- const PAGE_SIZE = 200;
8
-
9
- export function useGitGraph(projectName: string | undefined) {
10
- const [data, setData] = useState<GitGraphData | null>(null);
11
- const [loading, setLoading] = useState(true);
12
- const [loadingMore, setLoadingMore] = useState(false);
13
- const [hasMore, setHasMore] = useState(true);
14
- const [error, setError] = useState<string | null>(null);
15
- const [acting, setActing] = useState(false);
16
- const [selectedCommit, setSelectedCommit] = useState<GitCommit | null>(null);
17
- const [commitFiles, setCommitFiles] = useState<
18
- Array<{ path: string; additions: number; deletions: number }>
19
- >([]);
20
- const [loadingDetail, setLoadingDetail] = useState(false);
21
- const [branchFilter, setBranchFilter] = useState("__all__");
22
- const [searchQuery, setSearchQuery] = useState("");
23
- const [showSearch, setShowSearch] = useState(false);
24
- const { openTab } = useTabStore();
25
- const loadedCountRef = useRef(0);
26
-
27
- const fetchGraph = useCallback(async () => {
28
- if (!projectName) return;
29
- try {
30
- setLoading(true);
31
- // Fetch at least PAGE_SIZE, but if we've loaded more via pagination, re-fetch all
32
- const count = Math.max(PAGE_SIZE, loadedCountRef.current);
33
- const result = await api.get<GitGraphData>(
34
- `${projectUrl(projectName)}/git/graph?max=${count}`,
35
- );
36
- setData(result);
37
- loadedCountRef.current = result.commits.length;
38
- setHasMore(result.commits.length >= count);
39
- setError(null);
40
- } catch (e) {
41
- setError(e instanceof Error ? e.message : "Failed to fetch graph");
42
- } finally {
43
- setLoading(false);
44
- }
45
- }, [projectName]);
46
-
47
- const loadMore = useCallback(async () => {
48
- if (!projectName || loadingMore || !hasMore) return;
49
- try {
50
- setLoadingMore(true);
51
- const skip = loadedCountRef.current;
52
- const result = await api.get<GitGraphData>(
53
- `${projectUrl(projectName)}/git/graph?max=${PAGE_SIZE}&skip=${skip}`,
54
- );
55
- if (result.commits.length === 0) {
56
- setHasMore(false);
57
- return;
58
- }
59
- setData((prev) => {
60
- if (!prev) return result;
61
- // Deduplicate by hash
62
- const existing = new Set(prev.commits.map((c) => c.hash));
63
- const newCommits = result.commits.filter((c) => !existing.has(c.hash));
64
- // Merge branches: keep existing + add new remote-only branches
65
- const existingBranches = new Set(prev.branches.map((b) => b.name));
66
- const newBranches = result.branches.filter((b) => !existingBranches.has(b.name));
67
- return {
68
- commits: [...prev.commits, ...newCommits],
69
- branches: [...prev.branches, ...newBranches],
70
- head: prev.head,
71
- };
72
- });
73
- loadedCountRef.current = skip + result.commits.length;
74
- setHasMore(result.commits.length >= PAGE_SIZE);
75
- } catch (e) {
76
- setError(e instanceof Error ? e.message : "Failed to load more");
77
- } finally {
78
- setLoadingMore(false);
79
- }
80
- }, [projectName, loadingMore, hasMore]);
81
-
82
- useEffect(() => {
83
- fetchGraph();
84
- const interval = setInterval(fetchGraph, 10000);
85
- return () => clearInterval(interval);
86
- }, [fetchGraph]);
87
-
88
- const gitAction = async (path: string, body: Record<string, unknown>) => {
89
- if (!projectName) return;
90
- setActing(true);
91
- try { await api.post(`${projectUrl(projectName)}${path}`, body); await fetchGraph(); }
92
- catch (e) { setError(e instanceof Error ? e.message : "Action failed"); }
93
- finally { setActing(false); }
94
- };
95
-
96
- const fetchFromRemotes = () => gitAction("/git/fetch", {});
97
- const handleCheckout = (ref: string) => gitAction("/git/checkout", { ref });
98
- const handleCherryPick = (hash: string) => gitAction("/git/cherry-pick", { hash });
99
- const handleRevert = (hash: string) => gitAction("/git/revert", { hash });
100
- const handleMerge = (source: string) => gitAction("/git/merge", { source });
101
- const handleDeleteBranch = (name: string) => gitAction("/git/branch/delete", { name });
102
- const handlePushBranch = (branch: string) => gitAction("/git/push", { branch });
103
- const handleCreateTag = (name: string, hash?: string) => gitAction("/git/tag", { name, hash });
104
- const copyHash = (hash: string) => navigator.clipboard.writeText(hash);
105
-
106
- const handleCreateBranch = async (name: string, from: string) => {
107
- const exists = data?.branches.some((b) => b.name === name || b.name.endsWith(`/${name}`));
108
- if (exists) {
109
- if (!window.confirm(`Branch "${name}" already exists.\nDelete and recreate from this commit?`)) return;
110
- await gitAction("/git/branch/delete", { name });
111
- }
112
- await gitAction("/git/branch/create", { name, from });
113
- };
114
-
115
- const handleCreatePr = async (branch: string) => {
116
- if (!projectName) return;
117
- try {
118
- const r = await api.get<{ url: string | null }>(`${projectUrl(projectName)}/git/pr-url?branch=${encodeURIComponent(branch)}`);
119
- if (r.url) window.open(r.url, "_blank");
120
- } catch { /* silent */ }
121
- };
122
-
123
- const selectCommit = async (commit: GitCommit) => {
124
- if (selectedCommit?.hash === commit.hash) { setSelectedCommit(null); return; }
125
- setSelectedCommit(commit);
126
- setLoadingDetail(true);
127
- try {
128
- const parent = commit.parents[0] ?? "";
129
- const ref1Param = parent ? `ref1=${encodeURIComponent(parent)}&` : "";
130
- const files = await api.get<Array<{ path: string; additions: number; deletions: number }>>(
131
- `${projectUrl(projectName!)}/git/diff-stat?${ref1Param}ref2=${encodeURIComponent(commit.hash)}`);
132
- setCommitFiles(Array.isArray(files) ? files : []);
133
- } catch { setCommitFiles([]); }
134
- finally { setLoadingDetail(false); }
135
- };
136
-
137
- const openDiffForCommit = (commit: GitCommit) => openTab({
138
- type: "git-diff", title: `Diff ${commit.abbreviatedHash}`, closable: true,
139
- metadata: { projectName, ref1: commit.parents[0] ?? undefined, ref2: commit.hash },
140
- projectId: projectName ?? null,
141
- });
142
-
143
- // --- Computed ---
144
- const commitLabels = useMemo(() => buildCommitLabels(data), [data]);
145
- const currentBranch = data?.branches.find((b) => b.current);
146
- const headHash = data?.head ?? "";
147
-
148
- const filteredCommits = useMemo(() => {
149
- if (!data) return [];
150
- let commits = data.commits;
151
- if (branchFilter !== "__all__") {
152
- const branch = data.branches.find((b) => b.name === branchFilter);
153
- if (branch) {
154
- const reachable = new Set<string>();
155
- const queue = [branch.commitHash];
156
- while (queue.length > 0) {
157
- const hash = queue.pop()!;
158
- if (reachable.has(hash)) continue;
159
- reachable.add(hash);
160
- const c = data.commits.find((cm) => cm.hash === hash);
161
- if (c) queue.push(...c.parents);
162
- }
163
- commits = commits.filter((c) => reachable.has(c.hash));
164
- }
165
- }
166
- if (searchQuery.trim()) {
167
- const q = searchQuery.toLowerCase();
168
- commits = commits.filter(
169
- (c) =>
170
- c.subject.toLowerCase().includes(q) ||
171
- c.authorName.toLowerCase().includes(q) ||
172
- c.abbreviatedHash.includes(q) ||
173
- c.hash.includes(q),
174
- );
175
- }
176
- return commits;
177
- }, [data, branchFilter, searchQuery]);
178
-
179
- const filteredData = useMemo(() => (data ? { ...data, commits: filteredCommits } : null), [data, filteredCommits]);
180
- const filteredLanes = useMemo(() => computeLanes(filteredData), [filteredData]);
181
- const svgHeight = filteredCommits.length * ROW_HEIGHT + ROW_HEIGHT * 2; // extra padding for unloaded-parent lines
182
- const svgPaths = useMemo(
183
- () => computeSvgPaths(filteredData, filteredLanes.laneMap, filteredLanes.unloadedParentLanes, svgHeight),
184
- [filteredData, filteredLanes.laneMap, filteredLanes.unloadedParentLanes, svgHeight]);
185
-
186
- return {
187
- data, loading, loadingMore, hasMore, error, acting,
188
- selectedCommit, setSelectedCommit,
189
- commitFiles, loadingDetail,
190
- branchFilter, setBranchFilter,
191
- searchQuery, setSearchQuery,
192
- showSearch, setShowSearch,
193
- fetchGraph, fetchFromRemotes, loadMore,
194
- handleCheckout, handleCherryPick, handleRevert,
195
- handleMerge, handleDeleteBranch, handlePushBranch,
196
- handleCreateBranch, handleCreateTag, handleCreatePr,
197
- copyHash, selectCommit, openDiffForCommit,
198
- commitLabels, currentBranch, headHash,
199
- filteredCommits, filteredLanes, svgHeight, svgPaths,
200
- };
201
- }