difit 4.0.7 → 5.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +1 -3
- package/README.ko.md +1 -3
- package/README.md +6 -3
- package/README.zh.md +1 -3
- package/dist/cli/index.js +0 -39
- package/dist/cli/index.test.js +0 -374
- package/dist/cli/utils.d.ts +0 -2
- package/dist/cli/utils.js +1 -18
- package/dist/cli/utils.test.js +0 -16
- package/dist/client/assets/{arc-DX2p9X2Y.js → arc-47JvE2o4.js} +1 -1
- package/dist/client/assets/architecture-YZFGNWBL-CAofxL11.js +1 -0
- package/dist/client/assets/{architectureDiagram-Q4EWVU46-FixTWViB.js → architectureDiagram-Q4EWVU46-PEhI9x48.js} +1 -1
- package/dist/client/assets/{blockDiagram-DXYQGD6D-CUAMgGr9.js → blockDiagram-DXYQGD6D-SUrPMV8E.js} +1 -1
- package/dist/client/assets/{c4Diagram-AHTNJAMY-BM_HNNZe.js → c4Diagram-AHTNJAMY-BJ9pq9B3.js} +1 -1
- package/dist/client/assets/channel-DXpjokeU.js +1 -0
- package/dist/client/assets/{chunk-2KRD3SAO-DeT59g2K.js → chunk-2KRD3SAO-2O6hV_yw.js} +1 -1
- package/dist/client/assets/{chunk-336JU56O-CaGvJA86.js → chunk-336JU56O-C0NxME-U.js} +2 -2
- package/dist/client/assets/chunk-426QAEUC-DO73G5YJ.js +1 -0
- package/dist/client/assets/{chunk-4BX2VUAB-D9mNDl5f.js → chunk-4BX2VUAB-PjGa5t5G.js} +1 -1
- package/dist/client/assets/{chunk-4TB4RGXK-Df3b4HEG.js → chunk-4TB4RGXK-DKCtDLTf.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-dCWLe_n4.js → chunk-55IACEB6-BUBhWZ5_.js} +1 -1
- package/dist/client/assets/{chunk-5FUZZQ4R-EScvXcSN.js → chunk-5FUZZQ4R-D52DoV-0.js} +1 -1
- package/dist/client/assets/{chunk-5PVQY5BW-ail-oj89.js → chunk-5PVQY5BW-Cpp0JjTq.js} +1 -1
- package/dist/client/assets/{chunk-67CJDMHE-CHeCIL1u.js → chunk-67CJDMHE-DFAAAXYj.js} +1 -1
- package/dist/client/assets/{chunk-7N4EOEYR-P0tNRVMZ.js → chunk-7N4EOEYR-DeLrWd_x.js} +1 -1
- package/dist/client/assets/{chunk-AA7GKIK3-DloBHWSo.js → chunk-AA7GKIK3-Pb9IU0o9.js} +1 -1
- package/dist/client/assets/{chunk-BSJP7CBP-CGLThsR8.js → chunk-BSJP7CBP-DvFe6BQg.js} +1 -1
- package/dist/client/assets/{chunk-CIAEETIT-rCt2IEMp.js → chunk-CIAEETIT-AytpwHlv.js} +1 -1
- package/dist/client/assets/{chunk-EDXVE4YY-DIJEIKIq.js → chunk-EDXVE4YY-DSprT5LQ.js} +1 -1
- package/dist/client/assets/{chunk-ENJZ2VHE-CdrdxFfV.js → chunk-ENJZ2VHE-DsQnPoSa.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-BH_GgR9u.js → chunk-FMBD7UC4-CRzu5RUs.js} +1 -1
- package/dist/client/assets/{chunk-FOC6F5B3-D71VljSN.js → chunk-FOC6F5B3-GWMv90go.js} +1 -1
- package/dist/client/assets/{chunk-ICPOFSXX-2vcQKuhB.js → chunk-ICPOFSXX-LaB5P0SR.js} +1 -1
- package/dist/client/assets/{chunk-K5T4RW27-BWIFd7pZ.js → chunk-K5T4RW27-S036x5p5.js} +1 -1
- package/dist/client/assets/{chunk-KGLVRYIC-Ck8I8tdt.js → chunk-KGLVRYIC-DDzbiNrj.js} +1 -1
- package/dist/client/assets/{chunk-LIHQZDEY-Cc7TtI-w.js → chunk-LIHQZDEY-BoUwuY9Y.js} +1 -1
- package/dist/client/assets/{chunk-ORNJ4GCN-BMSqiphc.js → chunk-ORNJ4GCN-CopGHq93.js} +1 -1
- package/dist/client/assets/{chunk-OYMX7WX6-B5faFb53.js → chunk-OYMX7WX6-Do8-v0_P.js} +1 -1
- package/dist/client/assets/chunk-QZHKN3VN-BdzTLtlM.js +1 -0
- package/dist/client/assets/{chunk-U2HBQHQK-BILTfRyq.js → chunk-U2HBQHQK-CuugOH8t.js} +1 -1
- package/dist/client/assets/{chunk-X2U36JSP-D4-56gWx.js → chunk-X2U36JSP-ZX9AmJVa.js} +1 -1
- package/dist/client/assets/{chunk-XPW4576I-SxB401Zg.js → chunk-XPW4576I-DwqcIpF8.js} +1 -1
- package/dist/client/assets/{chunk-YZCP3GAM-CWXUVxFj.js → chunk-YZCP3GAM-w3hvGtGQ.js} +1 -1
- package/dist/client/assets/{chunk-ZZ45TVLE-CXjZua4f.js → chunk-ZZ45TVLE-CX35ksjr.js} +1 -1
- package/dist/client/assets/classDiagram-6PBFFD2Q-C9JPZQtl.js +1 -0
- package/dist/client/assets/classDiagram-v2-HSJHXN6E-BgDR8rt-.js +1 -0
- package/dist/client/assets/clone-BygUtDF6.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-YToNpueF.js → cose-bilkent-S5V4N54A-DXfb_bs_.js} +1 -1
- package/dist/client/assets/{dagre-tvaMpP4D.js → dagre-BYBAH6EF.js} +1 -1
- package/dist/client/assets/{dagre-KV5264BT-QFYoTa0z.js → dagre-KV5264BT-Bj9WMi27.js} +1 -1
- package/dist/client/assets/{diagram-5BDNPKRD-DM0NNmEN.js → diagram-5BDNPKRD-DHnS0s39.js} +1 -1
- package/dist/client/assets/{diagram-G4DWMVQ6-TiLkMmwt.js → diagram-G4DWMVQ6-DdKcPnFw.js} +1 -1
- package/dist/client/assets/{diagram-MMDJMWI5-DM1ykqrB.js → diagram-MMDJMWI5-DfdNeC0p.js} +1 -1
- package/dist/client/assets/{diagram-TYMM5635-BEOLX1wr.js → diagram-TYMM5635-DwieVXZu.js} +1 -1
- package/dist/client/assets/{dist-CCBhd9az.js → dist-COv0rtkK.js} +1 -1
- package/dist/client/assets/{erDiagram-SMLLAGMA-DZcjZq6z.js → erDiagram-SMLLAGMA--nNfIX9m.js} +1 -1
- package/dist/client/assets/{flowDiagram-DWJPFMVM-B1AVT9es.js → flowDiagram-DWJPFMVM-BFk7GDSn.js} +1 -1
- package/dist/client/assets/{ganttDiagram-T4ZO3ILL-BCEXws9V.js → ganttDiagram-T4ZO3ILL-DlVKHIi_.js} +1 -1
- package/dist/client/assets/gitGraph-7Q5UKJZL-4fOTOOzG.js +1 -0
- package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-CVznBDOl.js → gitGraphDiagram-UUTBAWPF-ww35jb2y.js} +1 -1
- package/dist/client/assets/{graphlib-C4fWcyt1.js → graphlib-cvGsNSEN.js} +1 -1
- package/dist/client/assets/index-CrvsDrrA.js +79 -0
- package/dist/client/assets/{index-C16wNcPQ.css → index-v-PY_jQI.css} +1 -1
- package/dist/client/assets/info-OMHHGYJF-BcwRhjJb.js +1 -0
- package/dist/client/assets/{infoDiagram-42DDH7IO-D8Oxr-KJ.js → infoDiagram-42DDH7IO-CbZg29-f.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-BE9KniVE.js → ishikawaDiagram-UXIWVN3A-DbmLYe6W.js} +1 -1
- package/dist/client/assets/{journeyDiagram-VCZTEJTY-B3lGcz06.js → journeyDiagram-VCZTEJTY-m6lofB3D.js} +1 -1
- package/dist/client/assets/{kanban-definition-6JOO6SKY-Bs1QdB0j.js → kanban-definition-6JOO6SKY-zNyvW0z9.js} +1 -1
- package/dist/client/assets/{line-CO4-KhEq.js → line-DYCPrtIt.js} +1 -1
- package/dist/client/assets/{linear-CnaJKs0I.js → linear-BAKLx75z.js} +1 -1
- package/dist/client/assets/{mermaid-parser.core-CravK6bS.js → mermaid-parser.core-BGGMv1j6.js} +2 -2
- package/dist/client/assets/{mermaid.core-DTh9KJvF.js → mermaid.core-CMGQEZKx.js} +3 -3
- package/dist/client/assets/{mindmap-definition-QFDTVHPH-D2xU2hfX.js → mindmap-definition-QFDTVHPH-4Qsf3Hkp.js} +1 -1
- package/dist/client/assets/packet-4T2RLAQJ-LdDEUu30.js +1 -0
- package/dist/client/assets/pie-ZZUOXDRM-B-ezG4xa.js +1 -0
- package/dist/client/assets/{pieDiagram-DEJITSTG-CRX6y4IQ.js → pieDiagram-DEJITSTG-Cnlop37j.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-34T5L4WZ-K2HFp8O8.js → quadrantDiagram-34T5L4WZ-CLFb7OC8.js} +1 -1
- package/dist/client/assets/radar-PYXPWWZC-D66LzHfH.js +1 -0
- package/dist/client/assets/{requirementDiagram-MS252O5E-C-8AW0uI.js → requirementDiagram-MS252O5E-CFMEftHL.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-XADWPNL6-Bv-_ZFS5.js → sankeyDiagram-XADWPNL6-Bz_Rsg7k.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-FGHM5R23-Bk4QYIPk.js → sequenceDiagram-FGHM5R23-C_UnEheO.js} +1 -1
- package/dist/client/assets/{src-XMuEuFcU.js → src-D4dXpp2L.js} +1 -1
- package/dist/client/assets/{stateDiagram-FHFEXIEX-CI1G7zGC.js → stateDiagram-FHFEXIEX-D5jyXLVB.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-CTyQGCYc.js +1 -0
- package/dist/client/assets/{timeline-definition-GMOUNBTQ-CnXv8xHg.js → timeline-definition-GMOUNBTQ-BtgnceD7.js} +1 -1
- package/dist/client/assets/treeView-SZITEDCU-BsI_6Qwy.js +1 -0
- package/dist/client/assets/treemap-W4RFUUIX-CzaM5y4Y.js +1 -0
- package/dist/client/assets/{vennDiagram-DHZGUBPP-M5x471Ar.js → vennDiagram-DHZGUBPP-DWtzk4wH.js} +1 -1
- package/dist/client/assets/wardley-RL74JXVD-C1IHEcDw.js +1 -0
- package/dist/client/assets/{wardleyDiagram-NUSXRM2D-BG99uPNN.js → wardleyDiagram-NUSXRM2D-D88r6BkP.js} +1 -1
- package/dist/client/assets/{xychartDiagram-5P7HB3ND-DO7Upr9G.js → xychartDiagram-5P7HB3ND-DkXj8P_J.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/server/server.d.ts +0 -1
- package/dist/server/server.js +0 -3
- package/dist/server/server.test.js +0 -37
- package/dist/types/diff.d.ts +0 -12
- package/dist/utils/commentFormatting.test.js +0 -52
- package/package.json +4 -5
- package/dist/cli/tuiDeprecation.d.ts +0 -3
- package/dist/cli/tuiDeprecation.js +0 -16
- package/dist/cli/tuiDeprecation.test.d.ts +0 -1
- package/dist/cli/tuiDeprecation.test.js +0 -16
- package/dist/client/assets/architecture-YZFGNWBL-2zVtKbnG.js +0 -1
- package/dist/client/assets/channel-B_ddQhpW.js +0 -1
- package/dist/client/assets/chunk-426QAEUC-CMTCMPn4.js +0 -1
- package/dist/client/assets/chunk-QZHKN3VN-B-G9G-FB.js +0 -1
- package/dist/client/assets/classDiagram-6PBFFD2Q-DnUQ2iGN.js +0 -1
- package/dist/client/assets/classDiagram-v2-HSJHXN6E-Dwp5vuOB.js +0 -1
- package/dist/client/assets/clone-aWrl-obY.js +0 -1
- package/dist/client/assets/gitGraph-7Q5UKJZL-BE3Mcr-v.js +0 -1
- package/dist/client/assets/index-6LShOAAb.js +0 -79
- package/dist/client/assets/info-OMHHGYJF-CBpXVhw-.js +0 -1
- package/dist/client/assets/packet-4T2RLAQJ-abaJ3V5T.js +0 -1
- package/dist/client/assets/pie-ZZUOXDRM-B12dpA7V.js +0 -1
- package/dist/client/assets/radar-PYXPWWZC-BbBaJJN8.js +0 -1
- package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-DQ0U-oto.js +0 -1
- package/dist/client/assets/treeView-SZITEDCU-CM0rCBUc.js +0 -1
- package/dist/client/assets/treemap-W4RFUUIX-CXoNE_rL.js +0 -1
- package/dist/client/assets/wardley-RL74JXVD-B_EtnvOk.js +0 -1
- package/dist/server/git-diff-tui.d.ts +0 -2
- package/dist/server/git-diff-tui.js +0 -100
- package/dist/server/git-diff-tui.test.d.ts +0 -1
- package/dist/server/git-diff-tui.test.js +0 -76
- package/dist/tui/App.d.ts +0 -10
- package/dist/tui/App.js +0 -92
- package/dist/tui/components/DiffViewer.d.ts +0 -8
- package/dist/tui/components/DiffViewer.js +0 -88
- package/dist/tui/components/FileList.d.ts +0 -8
- package/dist/tui/components/FileList.js +0 -48
- package/dist/tui/components/SideBySideDiffViewer.d.ts +0 -9
- package/dist/tui/components/SideBySideDiffViewer.js +0 -240
- package/dist/tui/components/StatusBar.d.ts +0 -8
- package/dist/tui/components/StatusBar.js +0 -19
- package/dist/tui/utils/parseDiff.d.ts +0 -2
- package/dist/tui/utils/parseDiff.js +0 -67
- package/dist/utils/createId.test.d.ts +0 -1
- package/dist/utils/createId.test.js +0 -48
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import"./chunk-K5T4RW27-BWIFd7pZ.js";import{n as e}from"./chunk-KGLVRYIC-Ck8I8tdt.js";export{e as createInfoServices};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import"./chunk-K5T4RW27-BWIFd7pZ.js";import{n as e}from"./chunk-FOC6F5B3-D71VljSN.js";export{e as createPacketServices};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import"./chunk-K5T4RW27-BWIFd7pZ.js";import{n as e}from"./chunk-AA7GKIK3-DloBHWSo.js";export{e as createPieServices};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import"./chunk-K5T4RW27-BWIFd7pZ.js";import{n as e}from"./chunk-2KRD3SAO-DeT59g2K.js";export{e as createRadarServices};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{h as e}from"./src-XMuEuFcU.js";import"./chunk-ICPOFSXX-2vcQKuhB.js";import"./chunk-5PVQY5BW-ail-oj89.js";import"./chunk-U2HBQHQK-BILTfRyq.js";import"./chunk-BSJP7CBP-CGLThsR8.js";import"./chunk-ZZ45TVLE-CXjZua4f.js";import"./chunk-55IACEB6-dCWLe_n4.js";import"./chunk-EDXVE4YY-DIJEIKIq.js";import"./chunk-X2U36JSP-D4-56gWx.js";import"./chunk-5FUZZQ4R-EScvXcSN.js";import"./chunk-ENJZ2VHE-CdrdxFfV.js";import"./chunk-336JU56O-CaGvJA86.js";import{i as t,n,r,t as i}from"./chunk-OYMX7WX6-B5faFb53.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-BWIFd7pZ.js";import{n as e}from"./chunk-ORNJ4GCN-BMSqiphc.js";export{e as createTreeViewServices};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import"./chunk-K5T4RW27-BWIFd7pZ.js";import{n as e}from"./chunk-LIHQZDEY-Cc7TtI-w.js";export{e as createTreemapServices};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import"./chunk-K5T4RW27-BWIFd7pZ.js";import{n as e}from"./chunk-CIAEETIT-rCt2IEMp.js";export{e as createWardleyServices};
|
|
@@ -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,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,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;
|