difit 4.0.6 → 5.0.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 (140) hide show
  1. package/README.ja.md +1 -2
  2. package/README.ko.md +1 -2
  3. package/README.md +6 -2
  4. package/README.zh.md +1 -2
  5. package/dist/cli/index.js +0 -35
  6. package/dist/cli/index.test.js +0 -264
  7. package/dist/cli/utils.d.ts +0 -2
  8. package/dist/cli/utils.js +1 -18
  9. package/dist/cli/utils.test.js +0 -16
  10. package/dist/client/assets/{arc-1g1LrDb3.js → arc-DbOd2jxV.js} +1 -1
  11. package/dist/client/assets/architecture-YZFGNWBL-0n2ZLuLj.js +1 -0
  12. package/dist/client/assets/{architectureDiagram-Q4EWVU46-D87-Rmwy.js → architectureDiagram-Q4EWVU46-BFXI_tUS.js} +1 -1
  13. package/dist/client/assets/{blockDiagram-DXYQGD6D-Cep-MIFv.js → blockDiagram-DXYQGD6D-BOEZDsFe.js} +1 -1
  14. package/dist/client/assets/{c4Diagram-AHTNJAMY-BQuH9Txx.js → c4Diagram-AHTNJAMY-BdIoPSTS.js} +1 -1
  15. package/dist/client/assets/channel-DPag0OPA.js +1 -0
  16. package/dist/client/assets/{chunk-2KRD3SAO-CpQQpmvx.js → chunk-2KRD3SAO-BaHSMZbk.js} +1 -1
  17. package/dist/client/assets/{chunk-336JU56O-Ddk9EzgO.js → chunk-336JU56O-BGfMTguc.js} +2 -2
  18. package/dist/client/assets/chunk-426QAEUC-Ct2OJ1s3.js +1 -0
  19. package/dist/client/assets/{chunk-4BX2VUAB-Ca-N0Wd9.js → chunk-4BX2VUAB-CNkIhTNe.js} +1 -1
  20. package/dist/client/assets/{chunk-4TB4RGXK-ZTWP_Onw.js → chunk-4TB4RGXK-wFMAN-Ug.js} +1 -1
  21. package/dist/client/assets/{chunk-55IACEB6-Dub40zHG.js → chunk-55IACEB6-J_10GkPG.js} +1 -1
  22. package/dist/client/assets/{chunk-5FUZZQ4R-Cgda0gtZ.js → chunk-5FUZZQ4R-0NY9W0kL.js} +1 -1
  23. package/dist/client/assets/{chunk-5PVQY5BW-D8JPH_tm.js → chunk-5PVQY5BW-nUEO-vub.js} +1 -1
  24. package/dist/client/assets/{chunk-67CJDMHE-U1KyLHzG.js → chunk-67CJDMHE-CpK_DCjw.js} +1 -1
  25. package/dist/client/assets/{chunk-7N4EOEYR-WzOy51nD.js → chunk-7N4EOEYR-CJfX2SRB.js} +1 -1
  26. package/dist/client/assets/{chunk-AA7GKIK3-DlWOj4lr.js → chunk-AA7GKIK3-DGHjDQX4.js} +1 -1
  27. package/dist/client/assets/{chunk-BSJP7CBP-CcZ0op08.js → chunk-BSJP7CBP-DiXS795w.js} +1 -1
  28. package/dist/client/assets/{chunk-CIAEETIT-qVSphnw5.js → chunk-CIAEETIT-DaOGL_z8.js} +1 -1
  29. package/dist/client/assets/{chunk-EDXVE4YY-76SPH4sf.js → chunk-EDXVE4YY-EPdmphYw.js} +1 -1
  30. package/dist/client/assets/{chunk-ENJZ2VHE-CKULNIzL.js → chunk-ENJZ2VHE-9x7-Qpzy.js} +1 -1
  31. package/dist/client/assets/{chunk-FMBD7UC4-CvDPP3mb.js → chunk-FMBD7UC4-Dgx4um1Q.js} +1 -1
  32. package/dist/client/assets/{chunk-FOC6F5B3-DceW0hWA.js → chunk-FOC6F5B3-BaVe0aqm.js} +1 -1
  33. package/dist/client/assets/{chunk-ICPOFSXX-ChGBNZMk.js → chunk-ICPOFSXX-C9l3r2YR.js} +1 -1
  34. package/dist/client/assets/{chunk-K5T4RW27-DBHdC4ln.js → chunk-K5T4RW27-Bv0dnd8C.js} +1 -1
  35. package/dist/client/assets/{chunk-KGLVRYIC-DRS7yiGQ.js → chunk-KGLVRYIC-BdTDi9hU.js} +1 -1
  36. package/dist/client/assets/{chunk-LIHQZDEY-KsE8dyJP.js → chunk-LIHQZDEY-BEYvjNoB.js} +1 -1
  37. package/dist/client/assets/{chunk-ORNJ4GCN-Dnp4oHRD.js → chunk-ORNJ4GCN-CHeeoEx_.js} +1 -1
  38. package/dist/client/assets/{chunk-OYMX7WX6-CciaotDu.js → chunk-OYMX7WX6-Bapbqtyc.js} +1 -1
  39. package/dist/client/assets/chunk-QZHKN3VN-CNqVW4m2.js +1 -0
  40. package/dist/client/assets/{chunk-U2HBQHQK-nbp7CjBP.js → chunk-U2HBQHQK-CpVfEbNc.js} +1 -1
  41. package/dist/client/assets/{chunk-X2U36JSP-Chs85loT.js → chunk-X2U36JSP-BAcfna0L.js} +1 -1
  42. package/dist/client/assets/{chunk-XPW4576I-VtI9b561.js → chunk-XPW4576I-D0wrDPCQ.js} +1 -1
  43. package/dist/client/assets/{chunk-YZCP3GAM-sBsewSoO.js → chunk-YZCP3GAM-D_AeoI3D.js} +1 -1
  44. package/dist/client/assets/{chunk-ZZ45TVLE-TMgeW_px.js → chunk-ZZ45TVLE-CwpZ5yb7.js} +1 -1
  45. package/dist/client/assets/classDiagram-6PBFFD2Q-aX3PLX0y.js +1 -0
  46. package/dist/client/assets/classDiagram-v2-HSJHXN6E-CshAcrwe.js +1 -0
  47. package/dist/client/assets/clone-J5cJUwR3.js +1 -0
  48. package/dist/client/assets/{cose-bilkent-S5V4N54A-5TzM3w9g.js → cose-bilkent-S5V4N54A-DJvkrscF.js} +1 -1
  49. package/dist/client/assets/{dagre-sb6WtN4K.js → dagre-BADyoi72.js} +1 -1
  50. package/dist/client/assets/{dagre-KV5264BT-xvyFOxd3.js → dagre-KV5264BT-BDTirpEd.js} +1 -1
  51. package/dist/client/assets/{diagram-5BDNPKRD-ChRpAe5p.js → diagram-5BDNPKRD-CJSn8eie.js} +1 -1
  52. package/dist/client/assets/{diagram-G4DWMVQ6-C_8BED4A.js → diagram-G4DWMVQ6-COzN0wAe.js} +1 -1
  53. package/dist/client/assets/{diagram-MMDJMWI5-BMwXEou2.js → diagram-MMDJMWI5-Bau__Yfk.js} +1 -1
  54. package/dist/client/assets/{diagram-TYMM5635-CeAkx82D.js → diagram-TYMM5635-DKIgp_-m.js} +1 -1
  55. package/dist/client/assets/{dist-CwC9dd2Z.js → dist-CL_sSZWx.js} +1 -1
  56. package/dist/client/assets/{erDiagram-SMLLAGMA-yGCTeXGt.js → erDiagram-SMLLAGMA-Iw0m0edr.js} +1 -1
  57. package/dist/client/assets/{flowDiagram-DWJPFMVM-CugkvbmM.js → flowDiagram-DWJPFMVM-CtXQs1rX.js} +1 -1
  58. package/dist/client/assets/{ganttDiagram-T4ZO3ILL-BXnlBFgK.js → ganttDiagram-T4ZO3ILL-B0341I_2.js} +1 -1
  59. package/dist/client/assets/gitGraph-7Q5UKJZL-NWgYXB7y.js +1 -0
  60. package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-B61aCwwu.js → gitGraphDiagram-UUTBAWPF-Yv3oicIa.js} +1 -1
  61. package/dist/client/assets/{graphlib-BMWKz3zT.js → graphlib-BLiSTux3.js} +1 -1
  62. package/dist/client/assets/index-C2jd9xpN.js +79 -0
  63. package/dist/client/assets/index-v-PY_jQI.css +2 -0
  64. package/dist/client/assets/info-OMHHGYJF-Bvc7BWzq.js +1 -0
  65. package/dist/client/assets/{infoDiagram-42DDH7IO-Bkh6nTL2.js → infoDiagram-42DDH7IO-Cfjrwz2v.js} +1 -1
  66. package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-D_fdVT6_.js → ishikawaDiagram-UXIWVN3A-Cc-jozqB.js} +1 -1
  67. package/dist/client/assets/{journeyDiagram-VCZTEJTY-DkXVokNF.js → journeyDiagram-VCZTEJTY-Bjx1sC-s.js} +1 -1
  68. package/dist/client/assets/{kanban-definition-6JOO6SKY-y8qq7qvL.js → kanban-definition-6JOO6SKY-CIkJYslO.js} +1 -1
  69. package/dist/client/assets/{line-B0LcTqNY.js → line-yxqmb99x.js} +1 -1
  70. package/dist/client/assets/{linear-CqIjr2qp.js → linear-DCAQGlgU.js} +1 -1
  71. package/dist/client/assets/{mermaid-parser.core-Du6QzpZO.js → mermaid-parser.core-B5ufZrv2.js} +2 -2
  72. package/dist/client/assets/{mermaid.core-CZBu-oKJ.js → mermaid.core-BFOnV1aK.js} +3 -3
  73. package/dist/client/assets/{mindmap-definition-QFDTVHPH-BJrRxSkM.js → mindmap-definition-QFDTVHPH-CPflWkUN.js} +1 -1
  74. package/dist/client/assets/packet-4T2RLAQJ-DNpLsp8v.js +1 -0
  75. package/dist/client/assets/pie-ZZUOXDRM-D8uToFEL.js +1 -0
  76. package/dist/client/assets/{pieDiagram-DEJITSTG-Debmhc0u.js → pieDiagram-DEJITSTG-Dl3gScVb.js} +1 -1
  77. package/dist/client/assets/{quadrantDiagram-34T5L4WZ-SE3g2BC9.js → quadrantDiagram-34T5L4WZ-38YyGeXU.js} +1 -1
  78. package/dist/client/assets/radar-PYXPWWZC-VrpLp1jL.js +1 -0
  79. package/dist/client/assets/{requirementDiagram-MS252O5E-1mv41puC.js → requirementDiagram-MS252O5E-CZW6zJs_.js} +1 -1
  80. package/dist/client/assets/{sankeyDiagram-XADWPNL6-CLjPRtOP.js → sankeyDiagram-XADWPNL6-D4r0GNlW.js} +1 -1
  81. package/dist/client/assets/{sequenceDiagram-FGHM5R23-Cs-P3AtR.js → sequenceDiagram-FGHM5R23-2xItvAxs.js} +1 -1
  82. package/dist/client/assets/{src-5XpQHeIJ.js → src-YK4ClFOB.js} +1 -1
  83. package/dist/client/assets/{stateDiagram-FHFEXIEX-CmB1fohY.js → stateDiagram-FHFEXIEX-lBViG7kM.js} +1 -1
  84. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-BcU-AM4S.js +1 -0
  85. package/dist/client/assets/{timeline-definition-GMOUNBTQ-BMUafJOI.js → timeline-definition-GMOUNBTQ-DmlaOXs4.js} +1 -1
  86. package/dist/client/assets/treeView-SZITEDCU-B1qc9ijP.js +1 -0
  87. package/dist/client/assets/treemap-W4RFUUIX-Dl7yWWae.js +1 -0
  88. package/dist/client/assets/{vennDiagram-DHZGUBPP-CpZ1Qhjz.js → vennDiagram-DHZGUBPP-BOLv0A8q.js} +1 -1
  89. package/dist/client/assets/wardley-RL74JXVD-CEXd-1Ht.js +1 -0
  90. package/dist/client/assets/{wardleyDiagram-NUSXRM2D-C-zH0lsd.js → wardleyDiagram-NUSXRM2D-B5lHUuTU.js} +1 -1
  91. package/dist/client/assets/{xychartDiagram-5P7HB3ND-SkLFuEHZ.js → xychartDiagram-5P7HB3ND-D8nLdJo8.js} +1 -1
  92. package/dist/client/index.html +2 -2
  93. package/dist/client/site-data/manifest.json +1 -0
  94. package/dist/client/site-data/snapshots/55f23a1...080c0e6.json +1 -0
  95. package/dist/client/site-data/snapshots/66ff7c6...e6977fe.json +1 -0
  96. package/dist/client/site-data/snapshots/7d40fd4...a72112f-comments.json +1 -0
  97. package/dist/client/site-data/snapshots/7d40fd4...a72112f.json +1 -0
  98. package/dist/types/diff.d.ts +11 -10
  99. package/dist/utils/commentFormatting.test.js +0 -52
  100. package/package.json +5 -6
  101. package/dist/cli/tuiDeprecation.d.ts +0 -3
  102. package/dist/cli/tuiDeprecation.js +0 -16
  103. package/dist/cli/tuiDeprecation.test.d.ts +0 -1
  104. package/dist/cli/tuiDeprecation.test.js +0 -16
  105. package/dist/client/assets/architecture-YZFGNWBL-MZfdAdY6.js +0 -1
  106. package/dist/client/assets/channel-CJqLEVLU.js +0 -1
  107. package/dist/client/assets/chunk-426QAEUC-2xhUznDE.js +0 -1
  108. package/dist/client/assets/chunk-QZHKN3VN-BiVE5u_E.js +0 -1
  109. package/dist/client/assets/classDiagram-6PBFFD2Q-CfyHazmg.js +0 -1
  110. package/dist/client/assets/classDiagram-v2-HSJHXN6E-D7Rb-bnu.js +0 -1
  111. package/dist/client/assets/clone-8xC1huEg.js +0 -1
  112. package/dist/client/assets/gitGraph-7Q5UKJZL-BeTWkPrd.js +0 -1
  113. package/dist/client/assets/index-D2Y8-unG.css +0 -2
  114. package/dist/client/assets/index-D9v_eYzS.js +0 -79
  115. package/dist/client/assets/info-OMHHGYJF-MUNR2tTt.js +0 -1
  116. package/dist/client/assets/packet-4T2RLAQJ-Ci-Uu57s.js +0 -1
  117. package/dist/client/assets/pie-ZZUOXDRM-pm57XGIg.js +0 -1
  118. package/dist/client/assets/radar-PYXPWWZC-CH-AuSDw.js +0 -1
  119. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-D6jsrR-f.js +0 -1
  120. package/dist/client/assets/treeView-SZITEDCU-BGsVMAdJ.js +0 -1
  121. package/dist/client/assets/treemap-W4RFUUIX-DXnhegXy.js +0 -1
  122. package/dist/client/assets/wardley-RL74JXVD-COd5nWj-.js +0 -1
  123. package/dist/server/git-diff-tui.d.ts +0 -2
  124. package/dist/server/git-diff-tui.js +0 -100
  125. package/dist/server/git-diff-tui.test.d.ts +0 -1
  126. package/dist/server/git-diff-tui.test.js +0 -76
  127. package/dist/tui/App.d.ts +0 -10
  128. package/dist/tui/App.js +0 -92
  129. package/dist/tui/components/DiffViewer.d.ts +0 -8
  130. package/dist/tui/components/DiffViewer.js +0 -88
  131. package/dist/tui/components/FileList.d.ts +0 -8
  132. package/dist/tui/components/FileList.js +0 -48
  133. package/dist/tui/components/SideBySideDiffViewer.d.ts +0 -9
  134. package/dist/tui/components/SideBySideDiffViewer.js +0 -240
  135. package/dist/tui/components/StatusBar.d.ts +0 -8
  136. package/dist/tui/components/StatusBar.js +0 -19
  137. package/dist/tui/utils/parseDiff.d.ts +0 -2
  138. package/dist/tui/utils/parseDiff.js +0 -67
  139. package/dist/utils/createId.test.d.ts +0 -1
  140. package/dist/utils/createId.test.js +0 -48
@@ -1 +0,0 @@
1
- import"./chunk-K5T4RW27-DBHdC4ln.js";import{n as e}from"./chunk-KGLVRYIC-DRS7yiGQ.js";export{e as createInfoServices};
@@ -1 +0,0 @@
1
- import"./chunk-K5T4RW27-DBHdC4ln.js";import{n as e}from"./chunk-FOC6F5B3-DceW0hWA.js";export{e as createPacketServices};
@@ -1 +0,0 @@
1
- import"./chunk-K5T4RW27-DBHdC4ln.js";import{n as e}from"./chunk-AA7GKIK3-DlWOj4lr.js";export{e as createPieServices};
@@ -1 +0,0 @@
1
- import"./chunk-K5T4RW27-DBHdC4ln.js";import{n as e}from"./chunk-2KRD3SAO-CpQQpmvx.js";export{e as createRadarServices};
@@ -1 +0,0 @@
1
- import{h as e}from"./src-5XpQHeIJ.js";import"./chunk-ICPOFSXX-ChGBNZMk.js";import"./chunk-5PVQY5BW-D8JPH_tm.js";import"./chunk-U2HBQHQK-nbp7CjBP.js";import"./chunk-BSJP7CBP-CcZ0op08.js";import"./chunk-ZZ45TVLE-TMgeW_px.js";import"./chunk-55IACEB6-Dub40zHG.js";import"./chunk-EDXVE4YY-76SPH4sf.js";import"./chunk-X2U36JSP-Chs85loT.js";import"./chunk-5FUZZQ4R-Cgda0gtZ.js";import"./chunk-ENJZ2VHE-CKULNIzL.js";import"./chunk-336JU56O-Ddk9EzgO.js";import{i as t,n,r,t as i}from"./chunk-OYMX7WX6-CciaotDu.js";var a={parser:n,get db(){return new i(2)},renderer:r,styles:t,init:e(e=>{e.state||={},e.state.arrowMarkerAbsolute=e.arrowMarkerAbsolute},`init`)};export{a as diagram};
@@ -1 +0,0 @@
1
- import"./chunk-K5T4RW27-DBHdC4ln.js";import{n as e}from"./chunk-ORNJ4GCN-Dnp4oHRD.js";export{e as createTreeViewServices};
@@ -1 +0,0 @@
1
- import"./chunk-K5T4RW27-DBHdC4ln.js";import{n as e}from"./chunk-LIHQZDEY-KsE8dyJP.js";export{e as createTreemapServices};
@@ -1 +0,0 @@
1
- import"./chunk-K5T4RW27-DBHdC4ln.js";import{n as e}from"./chunk-CIAEETIT-qVSphnw5.js";export{e as createWardleyServices};
@@ -1,2 +0,0 @@
1
- import type { DiffSelection, FileDiff } from '../types/diff.js';
2
- export declare function loadGitDiff(selection: DiffSelection, repoPath?: string, contextLines?: number): Promise<FileDiff[]>;
@@ -1,100 +0,0 @@
1
- import simpleGit from 'simple-git';
2
- import { validateDiffArguments } from '../cli/utils.js';
3
- import { getMergeBaseTargetRef, normalizeBaseMode } from '../utils/diffSelection.js';
4
- export async function loadGitDiff(selection, repoPath, contextLines) {
5
- const { targetCommitish, baseCommitish } = selection;
6
- // Validate arguments
7
- const validation = validateDiffArguments(targetCommitish, baseCommitish);
8
- if (!validation.valid) {
9
- throw new Error(validation.error);
10
- }
11
- const git = simpleGit(repoPath);
12
- const effectiveBaseCommitish = normalizeBaseMode(selection.baseMode) === 'merge-base'
13
- ? (await git.raw(['merge-base', getMergeBaseTargetRef(targetCommitish), baseCommitish])).trim()
14
- : baseCommitish;
15
- let diff;
16
- // Handle target special chars (base is always a regular commit)
17
- if (targetCommitish === 'working') {
18
- // Show unstaged changes (working vs staged)
19
- diff = await git.diff(['--name-status']);
20
- }
21
- else if (targetCommitish === 'staged') {
22
- // Show staged changes against base commit
23
- diff = await git.diff(['--cached', effectiveBaseCommitish, '--name-status']);
24
- }
25
- else if (targetCommitish === '.') {
26
- // Show all uncommitted changes against base commit
27
- diff = await git.diff([effectiveBaseCommitish, '--name-status']);
28
- }
29
- else {
30
- // Both are regular commits: standard commit-to-commit comparison
31
- diff = await git.diff([effectiveBaseCommitish, targetCommitish, '--name-status']);
32
- if (!diff.trim()) {
33
- // Try without parent (for initial commit)
34
- const diffInitial = await git.diff([targetCommitish, '--name-status']);
35
- if (!diffInitial.trim()) {
36
- throw new Error('No changes found in this commit');
37
- }
38
- diff = diffInitial;
39
- }
40
- }
41
- const fileChanges = diff
42
- .split('\n')
43
- .filter((line) => line.trim())
44
- .map((line) => {
45
- const [status, ...pathParts] = line.split('\t');
46
- const path = pathParts.join('\t');
47
- return { status, path };
48
- });
49
- const contextArgs = contextLines !== undefined ? [`-U${contextLines}`] : [];
50
- // Get diff for each file individually
51
- const fileDiffs = await Promise.all(fileChanges.map(async ({ status, path }) => {
52
- let fileDiff = '';
53
- // Handle individual file diffs (base is always a regular commit)
54
- if (targetCommitish === 'working') {
55
- // Show unstaged changes (working vs staged)
56
- fileDiff = await git.diff([...contextArgs, '--', path]);
57
- }
58
- else if (targetCommitish === 'staged') {
59
- // Show staged changes against base commit
60
- fileDiff = await git.diff(['--cached', effectiveBaseCommitish, ...contextArgs, '--', path]);
61
- }
62
- else if (targetCommitish === '.') {
63
- // Show all uncommitted changes against base commit
64
- fileDiff = await git.diff([effectiveBaseCommitish, ...contextArgs, '--', path]);
65
- }
66
- else {
67
- try {
68
- // Both are regular commits: standard commit-to-commit comparison
69
- fileDiff = await git.diff([
70
- effectiveBaseCommitish,
71
- targetCommitish,
72
- ...contextArgs,
73
- '--',
74
- path,
75
- ]);
76
- }
77
- catch {
78
- // For new files or if parent doesn't exist
79
- fileDiff = await git.diff([targetCommitish, ...contextArgs, '--', path]);
80
- }
81
- }
82
- const lines = fileDiff.split('\n');
83
- let additions = 0;
84
- let deletions = 0;
85
- lines.forEach((line) => {
86
- if (line.startsWith('+') && !line.startsWith('+++'))
87
- additions++;
88
- if (line.startsWith('-') && !line.startsWith('---'))
89
- deletions++;
90
- });
91
- return {
92
- path,
93
- status: status,
94
- diff: fileDiff,
95
- additions,
96
- deletions,
97
- };
98
- }));
99
- return fileDiffs;
100
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,76 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from 'vitest';
2
- import { loadGitDiff } from './git-diff-tui.js';
3
- const mockDiff = vi.hoisted(() => vi.fn());
4
- const mockRaw = vi.hoisted(() => vi.fn());
5
- const mockSimpleGit = vi.hoisted(() => vi.fn(() => ({ diff: mockDiff, raw: mockRaw })));
6
- vi.mock('simple-git', () => ({
7
- default: mockSimpleGit,
8
- }));
9
- describe('loadGitDiff', () => {
10
- beforeEach(() => {
11
- mockDiff.mockReset();
12
- mockRaw.mockReset();
13
- mockSimpleGit.mockClear();
14
- });
15
- it.each([
16
- {
17
- name: 'working tree diffs',
18
- targetCommitish: 'working',
19
- baseCommitish: 'staged',
20
- expectedListArgs: ['--name-status'],
21
- expectedFileArgs: ['-U5', '--', 'src/file.ts'],
22
- },
23
- {
24
- name: 'staged diffs',
25
- targetCommitish: 'staged',
26
- baseCommitish: 'HEAD',
27
- expectedListArgs: ['--cached', 'HEAD', '--name-status'],
28
- expectedFileArgs: ['--cached', 'HEAD', '-U5', '--', 'src/file.ts'],
29
- },
30
- {
31
- name: 'working tree against a base commit',
32
- targetCommitish: '.',
33
- baseCommitish: 'HEAD',
34
- expectedListArgs: ['HEAD', '--name-status'],
35
- expectedFileArgs: ['HEAD', '-U5', '--', 'src/file.ts'],
36
- },
37
- {
38
- name: 'commit comparisons',
39
- targetCommitish: 'HEAD',
40
- baseCommitish: 'HEAD^',
41
- expectedListArgs: ['HEAD^', 'HEAD', '--name-status'],
42
- expectedFileArgs: ['HEAD^', 'HEAD', '-U5', '--', 'src/file.ts'],
43
- },
44
- ])('passes context lines for $name', async ({ targetCommitish, baseCommitish, expectedListArgs, expectedFileArgs }) => {
45
- mockDiff
46
- .mockResolvedValueOnce('M\tsrc/file.ts')
47
- .mockResolvedValueOnce('@@ -1 +1 @@\n-old line\n+new line\n');
48
- const result = await loadGitDiff({ targetCommitish, baseCommitish }, '/repo', 5);
49
- expect(mockSimpleGit).toHaveBeenCalledWith('/repo');
50
- expect(mockDiff).toHaveBeenNthCalledWith(1, expectedListArgs);
51
- expect(mockDiff).toHaveBeenNthCalledWith(2, expectedFileArgs);
52
- expect(result).toEqual([
53
- {
54
- path: 'src/file.ts',
55
- status: 'M',
56
- diff: '@@ -1 +1 @@\n-old line\n+new line\n',
57
- additions: 1,
58
- deletions: 1,
59
- },
60
- ]);
61
- });
62
- it('uses merge-base for merge-base selections', async () => {
63
- mockRaw.mockResolvedValue('mergebase123\n');
64
- mockDiff
65
- .mockResolvedValueOnce('M\tsrc/file.ts')
66
- .mockResolvedValueOnce('@@ -1 +1 @@\n-old line\n+new line\n');
67
- await loadGitDiff({
68
- targetCommitish: '.',
69
- baseCommitish: 'origin/main',
70
- baseMode: 'merge-base',
71
- }, '/repo', 5);
72
- expect(mockRaw).toHaveBeenCalledWith(['merge-base', 'HEAD', 'origin/main']);
73
- expect(mockDiff).toHaveBeenNthCalledWith(1, ['mergebase123', '--name-status']);
74
- expect(mockDiff).toHaveBeenNthCalledWith(2, ['mergebase123', '-U5', '--', 'src/file.ts']);
75
- });
76
- });
package/dist/tui/App.d.ts DELETED
@@ -1,10 +0,0 @@
1
- import React from 'react';
2
- import { type DiffSelection } from '../types/diff.js';
3
- interface AppProps {
4
- selection: DiffSelection;
5
- mode?: string;
6
- repoPath?: string;
7
- contextLines?: number;
8
- }
9
- declare const App: React.FC<AppProps>;
10
- export default App;
package/dist/tui/App.js DELETED
@@ -1,92 +0,0 @@
1
- import { Box, Text, useApp, useInput } from 'ink';
2
- import React, { useState, useEffect } from 'react';
3
- import { loadGitDiff } from '../server/git-diff-tui.js';
4
- import { normalizeDiffViewMode } from '../utils/diffMode.js';
5
- import DiffViewer from './components/DiffViewer.js';
6
- import FileList from './components/FileList.js';
7
- import SideBySideDiffViewer from './components/SideBySideDiffViewer.js';
8
- import StatusBar from './components/StatusBar.js';
9
- const App = ({ selection, mode, repoPath, contextLines }) => {
10
- const { targetCommitish, baseCommitish } = selection;
11
- const [files, setFiles] = useState([]);
12
- const [selectedFileIndex, setSelectedFileIndex] = useState(0);
13
- const [loading, setLoading] = useState(true);
14
- const [error, setError] = useState(null);
15
- const [viewMode, setViewMode] = useState(normalizeDiffViewMode(mode));
16
- const { exit } = useApp();
17
- const loadDiff = async () => {
18
- setLoading(true);
19
- setError(null);
20
- try {
21
- const fileDiffs = await loadGitDiff(selection, repoPath, contextLines);
22
- setFiles(fileDiffs);
23
- setLoading(false);
24
- }
25
- catch (err) {
26
- setError(err instanceof Error ? err.message : 'Unknown error');
27
- setLoading(false);
28
- }
29
- };
30
- useEffect(() => {
31
- void loadDiff();
32
- // oxlint-disable-next-line react/exhaustive-deps
33
- }, [baseCommitish, targetCommitish]);
34
- useInput((input, key) => {
35
- if (input === 'q' || (key.ctrl && input === 'c')) {
36
- exit();
37
- }
38
- // Reload on 'r' key
39
- if (input === 'r') {
40
- void loadDiff();
41
- return;
42
- }
43
- if (viewMode === 'list') {
44
- if (key.upArrow || input === 'k') {
45
- setSelectedFileIndex((prev) => Math.max(0, prev - 1));
46
- }
47
- if (key.downArrow || input === 'j') {
48
- setSelectedFileIndex((prev) => Math.min(files.length - 1, prev + 1));
49
- }
50
- if (key.return || input === ' ') {
51
- setViewMode('split');
52
- }
53
- if (input === 'd') {
54
- setViewMode('unified');
55
- }
56
- }
57
- else if (key.escape || input === 'b') {
58
- setViewMode('list');
59
- }
60
- }, { isActive: true });
61
- if (loading) {
62
- return React.createElement(Text, null,
63
- "Loading diff for ",
64
- targetCommitish,
65
- "...");
66
- }
67
- if (error) {
68
- return React.createElement(Text, { color: "red" },
69
- "Error: ",
70
- error);
71
- }
72
- if (files.length === 0) {
73
- return (React.createElement(Box, { flexDirection: "column" },
74
- React.createElement(StatusBar, { commitish: targetCommitish, totalFiles: 0, currentMode: "list" }),
75
- React.createElement(Box, { marginTop: 1 },
76
- React.createElement(Text, { color: "yellow" },
77
- "No changes found for ",
78
- targetCommitish)),
79
- React.createElement(Box, { marginTop: 1 },
80
- React.createElement(Text, { dimColor: true }, "Press 'q' to quit"))));
81
- }
82
- return (React.createElement(Box, { flexDirection: "column", height: process.stdout.rows },
83
- React.createElement(StatusBar, { commitish: targetCommitish, totalFiles: files.length, currentMode: viewMode }),
84
- React.createElement(Box, { flexGrow: 1, flexDirection: "column" }, viewMode === 'list' ? (React.createElement(FileList, { files: files, selectedIndex: selectedFileIndex })) : viewMode === 'split' ? (React.createElement(SideBySideDiffViewer, { files: files, initialFileIndex: selectedFileIndex, onBack: () => setViewMode('list') })) : (React.createElement(DiffViewer, { files: files, initialFileIndex: selectedFileIndex }))),
85
- React.createElement(Box, { borderStyle: "single", paddingX: 1 },
86
- React.createElement(Text, { dimColor: true }, viewMode === 'list'
87
- ? '↑/↓ or j/k: navigate | Enter/Space: split | d: unified diff | r: reload | q: quit'
88
- : viewMode === 'split'
89
- ? 'Tab: next file | Shift+Tab: prev | ↑/↓ or j/k: scroll | ESC/b: list | r: reload | q: quit'
90
- : 'Tab: next | Shift+Tab: prev | ↑/↓ or j/k: scroll | ESC/b: list | r: reload | q: quit'))));
91
- };
92
- export default App;
@@ -1,8 +0,0 @@
1
- import React from 'react';
2
- import { type FileDiff } from '../../types/diff.js';
3
- interface DiffViewerProps {
4
- files: FileDiff[];
5
- initialFileIndex: number;
6
- }
7
- declare const DiffViewer: React.FC<DiffViewerProps>;
8
- export default DiffViewer;
@@ -1,88 +0,0 @@
1
- import { Box, Text, useInput, useApp } from 'ink';
2
- import React, { useState } from 'react';
3
- const DiffViewer = ({ files, initialFileIndex }) => {
4
- const [currentFileIndex, setCurrentFileIndex] = useState(initialFileIndex);
5
- const [scrollOffset, setScrollOffset] = useState(0);
6
- const file = files[currentFileIndex];
7
- const lines = file.diff.split('\n');
8
- const viewportHeight = Math.max(10, (process.stdout.rows || 24) - 7); // StatusBar(3) + footer(3) + margin(1)
9
- const maxScroll = Math.max(0, lines.length - viewportHeight);
10
- const { exit } = useApp();
11
- useInput((input, key) => {
12
- if (input === 'q' || (key.ctrl && input === 'c')) {
13
- exit();
14
- return;
15
- }
16
- if (key.upArrow || input === 'k') {
17
- setScrollOffset((prev) => Math.max(0, prev - 1));
18
- }
19
- if (key.downArrow || input === 'j') {
20
- setScrollOffset((prev) => Math.min(maxScroll, prev + 1));
21
- }
22
- if (key.pageUp) {
23
- setScrollOffset((prev) => Math.max(0, prev - viewportHeight));
24
- }
25
- if (key.pageDown) {
26
- setScrollOffset((prev) => Math.min(maxScroll, prev + viewportHeight));
27
- }
28
- // Navigate between files
29
- if (key.tab && !key.shift) {
30
- // Next file (loop to first when at end)
31
- setCurrentFileIndex((currentFileIndex + 1) % files.length);
32
- setScrollOffset(0);
33
- }
34
- if (key.tab && key.shift) {
35
- // Previous file (loop to last when at start)
36
- setCurrentFileIndex((currentFileIndex - 1 + files.length) % files.length);
37
- setScrollOffset(0);
38
- }
39
- }, { isActive: true });
40
- const visibleLines = lines.slice(scrollOffset, scrollOffset + viewportHeight);
41
- const getLineColor = (line) => {
42
- if (line.startsWith('+') && !line.startsWith('+++'))
43
- return 'green';
44
- if (line.startsWith('-') && !line.startsWith('---'))
45
- return 'red';
46
- if (line.startsWith('@@'))
47
- return 'cyan';
48
- if (line.startsWith('diff --git'))
49
- return 'yellow';
50
- return undefined;
51
- };
52
- return (React.createElement(Box, { flexDirection: "column", flexGrow: 1 },
53
- React.createElement(Box, { marginBottom: 1, flexDirection: "column" },
54
- React.createElement(Box, null,
55
- React.createElement(Text, { bold: true },
56
- file.path,
57
- " (",
58
- currentFileIndex + 1,
59
- "/",
60
- files.length,
61
- ")"),
62
- React.createElement(Text, { dimColor: true },
63
- ' ',
64
- "- ",
65
- file.additions,
66
- " additions, ",
67
- file.deletions,
68
- " deletions")),
69
- React.createElement(Box, null,
70
- React.createElement(Text, { dimColor: true },
71
- currentFileIndex > 0 && `← ${files[currentFileIndex - 1].path}`,
72
- currentFileIndex > 0 && currentFileIndex < files.length - 1 && ' | ',
73
- currentFileIndex < files.length - 1 && `${files[currentFileIndex + 1].path} →`))),
74
- React.createElement(Box, { flexGrow: 1, flexDirection: "column", borderStyle: "single", paddingX: 1 }, visibleLines.map((line, index) => (React.createElement(Text, { key: `line-${scrollOffset + index}`, color: getLineColor(line) }, line || ' ')))),
75
- React.createElement(Box, { marginTop: 1, justifyContent: "space-between" },
76
- React.createElement(Text, { dimColor: true },
77
- "Lines ",
78
- scrollOffset + 1,
79
- "-",
80
- Math.min(scrollOffset + viewportHeight, lines.length),
81
- " of",
82
- ' ',
83
- lines.length,
84
- scrollOffset + viewportHeight < lines.length &&
85
- ` (${lines.length - scrollOffset - viewportHeight} more)`),
86
- React.createElement(Text, { dimColor: true }, "Tab: next file | Shift+Tab: prev file"))));
87
- };
88
- export default DiffViewer;
@@ -1,8 +0,0 @@
1
- import React from 'react';
2
- import { type FileDiff } from '../../types/diff.js';
3
- interface FileListProps {
4
- files: FileDiff[];
5
- selectedIndex: number;
6
- }
7
- declare const FileList: React.FC<FileListProps>;
8
- export default FileList;
@@ -1,48 +0,0 @@
1
- import { Box, Text } from 'ink';
2
- import React from 'react';
3
- const FileList = ({ files, selectedIndex }) => {
4
- const getStatusColor = (status) => {
5
- switch (status) {
6
- case 'A':
7
- return 'green';
8
- case 'M':
9
- return 'yellow';
10
- case 'D':
11
- return 'red';
12
- default:
13
- return 'white';
14
- }
15
- };
16
- const getStatusLabel = (status) => {
17
- switch (status) {
18
- case 'A':
19
- return '[+]';
20
- case 'M':
21
- return '[M]';
22
- case 'D':
23
- return '[-]';
24
- default:
25
- return '[?]';
26
- }
27
- };
28
- return (React.createElement(Box, { flexDirection: "column" },
29
- React.createElement(Box, { marginBottom: 1 },
30
- React.createElement(Text, { bold: true },
31
- "Changed Files (",
32
- files.length,
33
- ")")),
34
- files.map((file, index) => (React.createElement(Box, { key: `${file.path}-${index}` },
35
- React.createElement(Text, { color: index === selectedIndex ? 'cyan' : undefined, backgroundColor: index === selectedIndex ? 'gray' : undefined },
36
- index === selectedIndex ? '▶ ' : ' ',
37
- React.createElement(Text, { color: getStatusColor(file.status) }, getStatusLabel(file.status)),
38
- ' ',
39
- file.path,
40
- ' ',
41
- React.createElement(Text, { dimColor: true },
42
- "(+",
43
- file.additions,
44
- " -",
45
- file.deletions,
46
- ")")))))));
47
- };
48
- export default FileList;
@@ -1,9 +0,0 @@
1
- import React from 'react';
2
- import { type FileDiff } from '../../types/diff.js';
3
- interface SideBySideDiffViewerProps {
4
- files: FileDiff[];
5
- initialFileIndex: number;
6
- onBack: () => void;
7
- }
8
- declare const SideBySideDiffViewer: React.FC<SideBySideDiffViewerProps>;
9
- export default SideBySideDiffViewer;