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.
- package/README.ja.md +1 -2
- package/README.ko.md +1 -2
- package/README.md +6 -2
- package/README.zh.md +1 -2
- package/dist/cli/index.js +0 -35
- package/dist/cli/index.test.js +0 -264
- 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-1g1LrDb3.js → arc-DbOd2jxV.js} +1 -1
- package/dist/client/assets/architecture-YZFGNWBL-0n2ZLuLj.js +1 -0
- package/dist/client/assets/{architectureDiagram-Q4EWVU46-D87-Rmwy.js → architectureDiagram-Q4EWVU46-BFXI_tUS.js} +1 -1
- package/dist/client/assets/{blockDiagram-DXYQGD6D-Cep-MIFv.js → blockDiagram-DXYQGD6D-BOEZDsFe.js} +1 -1
- package/dist/client/assets/{c4Diagram-AHTNJAMY-BQuH9Txx.js → c4Diagram-AHTNJAMY-BdIoPSTS.js} +1 -1
- package/dist/client/assets/channel-DPag0OPA.js +1 -0
- package/dist/client/assets/{chunk-2KRD3SAO-CpQQpmvx.js → chunk-2KRD3SAO-BaHSMZbk.js} +1 -1
- package/dist/client/assets/{chunk-336JU56O-Ddk9EzgO.js → chunk-336JU56O-BGfMTguc.js} +2 -2
- package/dist/client/assets/chunk-426QAEUC-Ct2OJ1s3.js +1 -0
- package/dist/client/assets/{chunk-4BX2VUAB-Ca-N0Wd9.js → chunk-4BX2VUAB-CNkIhTNe.js} +1 -1
- package/dist/client/assets/{chunk-4TB4RGXK-ZTWP_Onw.js → chunk-4TB4RGXK-wFMAN-Ug.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-Dub40zHG.js → chunk-55IACEB6-J_10GkPG.js} +1 -1
- package/dist/client/assets/{chunk-5FUZZQ4R-Cgda0gtZ.js → chunk-5FUZZQ4R-0NY9W0kL.js} +1 -1
- package/dist/client/assets/{chunk-5PVQY5BW-D8JPH_tm.js → chunk-5PVQY5BW-nUEO-vub.js} +1 -1
- package/dist/client/assets/{chunk-67CJDMHE-U1KyLHzG.js → chunk-67CJDMHE-CpK_DCjw.js} +1 -1
- package/dist/client/assets/{chunk-7N4EOEYR-WzOy51nD.js → chunk-7N4EOEYR-CJfX2SRB.js} +1 -1
- package/dist/client/assets/{chunk-AA7GKIK3-DlWOj4lr.js → chunk-AA7GKIK3-DGHjDQX4.js} +1 -1
- package/dist/client/assets/{chunk-BSJP7CBP-CcZ0op08.js → chunk-BSJP7CBP-DiXS795w.js} +1 -1
- package/dist/client/assets/{chunk-CIAEETIT-qVSphnw5.js → chunk-CIAEETIT-DaOGL_z8.js} +1 -1
- package/dist/client/assets/{chunk-EDXVE4YY-76SPH4sf.js → chunk-EDXVE4YY-EPdmphYw.js} +1 -1
- package/dist/client/assets/{chunk-ENJZ2VHE-CKULNIzL.js → chunk-ENJZ2VHE-9x7-Qpzy.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-CvDPP3mb.js → chunk-FMBD7UC4-Dgx4um1Q.js} +1 -1
- package/dist/client/assets/{chunk-FOC6F5B3-DceW0hWA.js → chunk-FOC6F5B3-BaVe0aqm.js} +1 -1
- package/dist/client/assets/{chunk-ICPOFSXX-ChGBNZMk.js → chunk-ICPOFSXX-C9l3r2YR.js} +1 -1
- package/dist/client/assets/{chunk-K5T4RW27-DBHdC4ln.js → chunk-K5T4RW27-Bv0dnd8C.js} +1 -1
- package/dist/client/assets/{chunk-KGLVRYIC-DRS7yiGQ.js → chunk-KGLVRYIC-BdTDi9hU.js} +1 -1
- package/dist/client/assets/{chunk-LIHQZDEY-KsE8dyJP.js → chunk-LIHQZDEY-BEYvjNoB.js} +1 -1
- package/dist/client/assets/{chunk-ORNJ4GCN-Dnp4oHRD.js → chunk-ORNJ4GCN-CHeeoEx_.js} +1 -1
- package/dist/client/assets/{chunk-OYMX7WX6-CciaotDu.js → chunk-OYMX7WX6-Bapbqtyc.js} +1 -1
- package/dist/client/assets/chunk-QZHKN3VN-CNqVW4m2.js +1 -0
- package/dist/client/assets/{chunk-U2HBQHQK-nbp7CjBP.js → chunk-U2HBQHQK-CpVfEbNc.js} +1 -1
- package/dist/client/assets/{chunk-X2U36JSP-Chs85loT.js → chunk-X2U36JSP-BAcfna0L.js} +1 -1
- package/dist/client/assets/{chunk-XPW4576I-VtI9b561.js → chunk-XPW4576I-D0wrDPCQ.js} +1 -1
- package/dist/client/assets/{chunk-YZCP3GAM-sBsewSoO.js → chunk-YZCP3GAM-D_AeoI3D.js} +1 -1
- package/dist/client/assets/{chunk-ZZ45TVLE-TMgeW_px.js → chunk-ZZ45TVLE-CwpZ5yb7.js} +1 -1
- package/dist/client/assets/classDiagram-6PBFFD2Q-aX3PLX0y.js +1 -0
- package/dist/client/assets/classDiagram-v2-HSJHXN6E-CshAcrwe.js +1 -0
- package/dist/client/assets/clone-J5cJUwR3.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-5TzM3w9g.js → cose-bilkent-S5V4N54A-DJvkrscF.js} +1 -1
- package/dist/client/assets/{dagre-sb6WtN4K.js → dagre-BADyoi72.js} +1 -1
- package/dist/client/assets/{dagre-KV5264BT-xvyFOxd3.js → dagre-KV5264BT-BDTirpEd.js} +1 -1
- package/dist/client/assets/{diagram-5BDNPKRD-ChRpAe5p.js → diagram-5BDNPKRD-CJSn8eie.js} +1 -1
- package/dist/client/assets/{diagram-G4DWMVQ6-C_8BED4A.js → diagram-G4DWMVQ6-COzN0wAe.js} +1 -1
- package/dist/client/assets/{diagram-MMDJMWI5-BMwXEou2.js → diagram-MMDJMWI5-Bau__Yfk.js} +1 -1
- package/dist/client/assets/{diagram-TYMM5635-CeAkx82D.js → diagram-TYMM5635-DKIgp_-m.js} +1 -1
- package/dist/client/assets/{dist-CwC9dd2Z.js → dist-CL_sSZWx.js} +1 -1
- package/dist/client/assets/{erDiagram-SMLLAGMA-yGCTeXGt.js → erDiagram-SMLLAGMA-Iw0m0edr.js} +1 -1
- package/dist/client/assets/{flowDiagram-DWJPFMVM-CugkvbmM.js → flowDiagram-DWJPFMVM-CtXQs1rX.js} +1 -1
- package/dist/client/assets/{ganttDiagram-T4ZO3ILL-BXnlBFgK.js → ganttDiagram-T4ZO3ILL-B0341I_2.js} +1 -1
- package/dist/client/assets/gitGraph-7Q5UKJZL-NWgYXB7y.js +1 -0
- package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-B61aCwwu.js → gitGraphDiagram-UUTBAWPF-Yv3oicIa.js} +1 -1
- package/dist/client/assets/{graphlib-BMWKz3zT.js → graphlib-BLiSTux3.js} +1 -1
- package/dist/client/assets/index-C2jd9xpN.js +79 -0
- package/dist/client/assets/index-v-PY_jQI.css +2 -0
- package/dist/client/assets/info-OMHHGYJF-Bvc7BWzq.js +1 -0
- package/dist/client/assets/{infoDiagram-42DDH7IO-Bkh6nTL2.js → infoDiagram-42DDH7IO-Cfjrwz2v.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-D_fdVT6_.js → ishikawaDiagram-UXIWVN3A-Cc-jozqB.js} +1 -1
- package/dist/client/assets/{journeyDiagram-VCZTEJTY-DkXVokNF.js → journeyDiagram-VCZTEJTY-Bjx1sC-s.js} +1 -1
- package/dist/client/assets/{kanban-definition-6JOO6SKY-y8qq7qvL.js → kanban-definition-6JOO6SKY-CIkJYslO.js} +1 -1
- package/dist/client/assets/{line-B0LcTqNY.js → line-yxqmb99x.js} +1 -1
- package/dist/client/assets/{linear-CqIjr2qp.js → linear-DCAQGlgU.js} +1 -1
- package/dist/client/assets/{mermaid-parser.core-Du6QzpZO.js → mermaid-parser.core-B5ufZrv2.js} +2 -2
- package/dist/client/assets/{mermaid.core-CZBu-oKJ.js → mermaid.core-BFOnV1aK.js} +3 -3
- package/dist/client/assets/{mindmap-definition-QFDTVHPH-BJrRxSkM.js → mindmap-definition-QFDTVHPH-CPflWkUN.js} +1 -1
- package/dist/client/assets/packet-4T2RLAQJ-DNpLsp8v.js +1 -0
- package/dist/client/assets/pie-ZZUOXDRM-D8uToFEL.js +1 -0
- package/dist/client/assets/{pieDiagram-DEJITSTG-Debmhc0u.js → pieDiagram-DEJITSTG-Dl3gScVb.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-34T5L4WZ-SE3g2BC9.js → quadrantDiagram-34T5L4WZ-38YyGeXU.js} +1 -1
- package/dist/client/assets/radar-PYXPWWZC-VrpLp1jL.js +1 -0
- package/dist/client/assets/{requirementDiagram-MS252O5E-1mv41puC.js → requirementDiagram-MS252O5E-CZW6zJs_.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-XADWPNL6-CLjPRtOP.js → sankeyDiagram-XADWPNL6-D4r0GNlW.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-FGHM5R23-Cs-P3AtR.js → sequenceDiagram-FGHM5R23-2xItvAxs.js} +1 -1
- package/dist/client/assets/{src-5XpQHeIJ.js → src-YK4ClFOB.js} +1 -1
- package/dist/client/assets/{stateDiagram-FHFEXIEX-CmB1fohY.js → stateDiagram-FHFEXIEX-lBViG7kM.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-BcU-AM4S.js +1 -0
- package/dist/client/assets/{timeline-definition-GMOUNBTQ-BMUafJOI.js → timeline-definition-GMOUNBTQ-DmlaOXs4.js} +1 -1
- package/dist/client/assets/treeView-SZITEDCU-B1qc9ijP.js +1 -0
- package/dist/client/assets/treemap-W4RFUUIX-Dl7yWWae.js +1 -0
- package/dist/client/assets/{vennDiagram-DHZGUBPP-CpZ1Qhjz.js → vennDiagram-DHZGUBPP-BOLv0A8q.js} +1 -1
- package/dist/client/assets/wardley-RL74JXVD-CEXd-1Ht.js +1 -0
- package/dist/client/assets/{wardleyDiagram-NUSXRM2D-C-zH0lsd.js → wardleyDiagram-NUSXRM2D-B5lHUuTU.js} +1 -1
- package/dist/client/assets/{xychartDiagram-5P7HB3ND-SkLFuEHZ.js → xychartDiagram-5P7HB3ND-D8nLdJo8.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/client/site-data/manifest.json +1 -0
- package/dist/client/site-data/snapshots/55f23a1...080c0e6.json +1 -0
- package/dist/client/site-data/snapshots/66ff7c6...e6977fe.json +1 -0
- package/dist/client/site-data/snapshots/7d40fd4...a72112f-comments.json +1 -0
- package/dist/client/site-data/snapshots/7d40fd4...a72112f.json +1 -0
- package/dist/types/diff.d.ts +11 -10
- package/dist/utils/commentFormatting.test.js +0 -52
- package/package.json +5 -6
- 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-MZfdAdY6.js +0 -1
- package/dist/client/assets/channel-CJqLEVLU.js +0 -1
- package/dist/client/assets/chunk-426QAEUC-2xhUznDE.js +0 -1
- package/dist/client/assets/chunk-QZHKN3VN-BiVE5u_E.js +0 -1
- package/dist/client/assets/classDiagram-6PBFFD2Q-CfyHazmg.js +0 -1
- package/dist/client/assets/classDiagram-v2-HSJHXN6E-D7Rb-bnu.js +0 -1
- package/dist/client/assets/clone-8xC1huEg.js +0 -1
- package/dist/client/assets/gitGraph-7Q5UKJZL-BeTWkPrd.js +0 -1
- package/dist/client/assets/index-D2Y8-unG.css +0 -2
- package/dist/client/assets/index-D9v_eYzS.js +0 -79
- package/dist/client/assets/info-OMHHGYJF-MUNR2tTt.js +0 -1
- package/dist/client/assets/packet-4T2RLAQJ-Ci-Uu57s.js +0 -1
- package/dist/client/assets/pie-ZZUOXDRM-pm57XGIg.js +0 -1
- package/dist/client/assets/radar-PYXPWWZC-CH-AuSDw.js +0 -1
- package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-D6jsrR-f.js +0 -1
- package/dist/client/assets/treeView-SZITEDCU-BGsVMAdJ.js +0 -1
- package/dist/client/assets/treemap-W4RFUUIX-DXnhegXy.js +0 -1
- package/dist/client/assets/wardley-RL74JXVD-COd5nWj-.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-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,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;
|