difit 4.0.7 → 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-DX2p9X2Y.js → arc-DbOd2jxV.js} +1 -1
- package/dist/client/assets/architecture-YZFGNWBL-0n2ZLuLj.js +1 -0
- package/dist/client/assets/{architectureDiagram-Q4EWVU46-FixTWViB.js → architectureDiagram-Q4EWVU46-BFXI_tUS.js} +1 -1
- package/dist/client/assets/{blockDiagram-DXYQGD6D-CUAMgGr9.js → blockDiagram-DXYQGD6D-BOEZDsFe.js} +1 -1
- package/dist/client/assets/{c4Diagram-AHTNJAMY-BM_HNNZe.js → c4Diagram-AHTNJAMY-BdIoPSTS.js} +1 -1
- package/dist/client/assets/channel-DPag0OPA.js +1 -0
- package/dist/client/assets/{chunk-2KRD3SAO-DeT59g2K.js → chunk-2KRD3SAO-BaHSMZbk.js} +1 -1
- package/dist/client/assets/{chunk-336JU56O-CaGvJA86.js → chunk-336JU56O-BGfMTguc.js} +2 -2
- package/dist/client/assets/chunk-426QAEUC-Ct2OJ1s3.js +1 -0
- package/dist/client/assets/{chunk-4BX2VUAB-D9mNDl5f.js → chunk-4BX2VUAB-CNkIhTNe.js} +1 -1
- package/dist/client/assets/{chunk-4TB4RGXK-Df3b4HEG.js → chunk-4TB4RGXK-wFMAN-Ug.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-dCWLe_n4.js → chunk-55IACEB6-J_10GkPG.js} +1 -1
- package/dist/client/assets/{chunk-5FUZZQ4R-EScvXcSN.js → chunk-5FUZZQ4R-0NY9W0kL.js} +1 -1
- package/dist/client/assets/{chunk-5PVQY5BW-ail-oj89.js → chunk-5PVQY5BW-nUEO-vub.js} +1 -1
- package/dist/client/assets/{chunk-67CJDMHE-CHeCIL1u.js → chunk-67CJDMHE-CpK_DCjw.js} +1 -1
- package/dist/client/assets/{chunk-7N4EOEYR-P0tNRVMZ.js → chunk-7N4EOEYR-CJfX2SRB.js} +1 -1
- package/dist/client/assets/{chunk-AA7GKIK3-DloBHWSo.js → chunk-AA7GKIK3-DGHjDQX4.js} +1 -1
- package/dist/client/assets/{chunk-BSJP7CBP-CGLThsR8.js → chunk-BSJP7CBP-DiXS795w.js} +1 -1
- package/dist/client/assets/{chunk-CIAEETIT-rCt2IEMp.js → chunk-CIAEETIT-DaOGL_z8.js} +1 -1
- package/dist/client/assets/{chunk-EDXVE4YY-DIJEIKIq.js → chunk-EDXVE4YY-EPdmphYw.js} +1 -1
- package/dist/client/assets/{chunk-ENJZ2VHE-CdrdxFfV.js → chunk-ENJZ2VHE-9x7-Qpzy.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-BH_GgR9u.js → chunk-FMBD7UC4-Dgx4um1Q.js} +1 -1
- package/dist/client/assets/{chunk-FOC6F5B3-D71VljSN.js → chunk-FOC6F5B3-BaVe0aqm.js} +1 -1
- package/dist/client/assets/{chunk-ICPOFSXX-2vcQKuhB.js → chunk-ICPOFSXX-C9l3r2YR.js} +1 -1
- package/dist/client/assets/{chunk-K5T4RW27-BWIFd7pZ.js → chunk-K5T4RW27-Bv0dnd8C.js} +1 -1
- package/dist/client/assets/{chunk-KGLVRYIC-Ck8I8tdt.js → chunk-KGLVRYIC-BdTDi9hU.js} +1 -1
- package/dist/client/assets/{chunk-LIHQZDEY-Cc7TtI-w.js → chunk-LIHQZDEY-BEYvjNoB.js} +1 -1
- package/dist/client/assets/{chunk-ORNJ4GCN-BMSqiphc.js → chunk-ORNJ4GCN-CHeeoEx_.js} +1 -1
- package/dist/client/assets/{chunk-OYMX7WX6-B5faFb53.js → chunk-OYMX7WX6-Bapbqtyc.js} +1 -1
- package/dist/client/assets/chunk-QZHKN3VN-CNqVW4m2.js +1 -0
- package/dist/client/assets/{chunk-U2HBQHQK-BILTfRyq.js → chunk-U2HBQHQK-CpVfEbNc.js} +1 -1
- package/dist/client/assets/{chunk-X2U36JSP-D4-56gWx.js → chunk-X2U36JSP-BAcfna0L.js} +1 -1
- package/dist/client/assets/{chunk-XPW4576I-SxB401Zg.js → chunk-XPW4576I-D0wrDPCQ.js} +1 -1
- package/dist/client/assets/{chunk-YZCP3GAM-CWXUVxFj.js → chunk-YZCP3GAM-D_AeoI3D.js} +1 -1
- package/dist/client/assets/{chunk-ZZ45TVLE-CXjZua4f.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-YToNpueF.js → cose-bilkent-S5V4N54A-DJvkrscF.js} +1 -1
- package/dist/client/assets/{dagre-tvaMpP4D.js → dagre-BADyoi72.js} +1 -1
- package/dist/client/assets/{dagre-KV5264BT-QFYoTa0z.js → dagre-KV5264BT-BDTirpEd.js} +1 -1
- package/dist/client/assets/{diagram-5BDNPKRD-DM0NNmEN.js → diagram-5BDNPKRD-CJSn8eie.js} +1 -1
- package/dist/client/assets/{diagram-G4DWMVQ6-TiLkMmwt.js → diagram-G4DWMVQ6-COzN0wAe.js} +1 -1
- package/dist/client/assets/{diagram-MMDJMWI5-DM1ykqrB.js → diagram-MMDJMWI5-Bau__Yfk.js} +1 -1
- package/dist/client/assets/{diagram-TYMM5635-BEOLX1wr.js → diagram-TYMM5635-DKIgp_-m.js} +1 -1
- package/dist/client/assets/{dist-CCBhd9az.js → dist-CL_sSZWx.js} +1 -1
- package/dist/client/assets/{erDiagram-SMLLAGMA-DZcjZq6z.js → erDiagram-SMLLAGMA-Iw0m0edr.js} +1 -1
- package/dist/client/assets/{flowDiagram-DWJPFMVM-B1AVT9es.js → flowDiagram-DWJPFMVM-CtXQs1rX.js} +1 -1
- package/dist/client/assets/{ganttDiagram-T4ZO3ILL-BCEXws9V.js → ganttDiagram-T4ZO3ILL-B0341I_2.js} +1 -1
- package/dist/client/assets/gitGraph-7Q5UKJZL-NWgYXB7y.js +1 -0
- package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-CVznBDOl.js → gitGraphDiagram-UUTBAWPF-Yv3oicIa.js} +1 -1
- package/dist/client/assets/{graphlib-C4fWcyt1.js → graphlib-BLiSTux3.js} +1 -1
- package/dist/client/assets/{index-6LShOAAb.js → index-C2jd9xpN.js} +6 -6
- package/dist/client/assets/{index-C16wNcPQ.css → index-v-PY_jQI.css} +1 -1
- package/dist/client/assets/info-OMHHGYJF-Bvc7BWzq.js +1 -0
- package/dist/client/assets/{infoDiagram-42DDH7IO-D8Oxr-KJ.js → infoDiagram-42DDH7IO-Cfjrwz2v.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-BE9KniVE.js → ishikawaDiagram-UXIWVN3A-Cc-jozqB.js} +1 -1
- package/dist/client/assets/{journeyDiagram-VCZTEJTY-B3lGcz06.js → journeyDiagram-VCZTEJTY-Bjx1sC-s.js} +1 -1
- package/dist/client/assets/{kanban-definition-6JOO6SKY-Bs1QdB0j.js → kanban-definition-6JOO6SKY-CIkJYslO.js} +1 -1
- package/dist/client/assets/{line-CO4-KhEq.js → line-yxqmb99x.js} +1 -1
- package/dist/client/assets/{linear-CnaJKs0I.js → linear-DCAQGlgU.js} +1 -1
- package/dist/client/assets/{mermaid-parser.core-CravK6bS.js → mermaid-parser.core-B5ufZrv2.js} +2 -2
- package/dist/client/assets/{mermaid.core-DTh9KJvF.js → mermaid.core-BFOnV1aK.js} +3 -3
- package/dist/client/assets/{mindmap-definition-QFDTVHPH-D2xU2hfX.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-CRX6y4IQ.js → pieDiagram-DEJITSTG-Dl3gScVb.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-34T5L4WZ-K2HFp8O8.js → quadrantDiagram-34T5L4WZ-38YyGeXU.js} +1 -1
- package/dist/client/assets/radar-PYXPWWZC-VrpLp1jL.js +1 -0
- package/dist/client/assets/{requirementDiagram-MS252O5E-C-8AW0uI.js → requirementDiagram-MS252O5E-CZW6zJs_.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-XADWPNL6-Bv-_ZFS5.js → sankeyDiagram-XADWPNL6-D4r0GNlW.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-FGHM5R23-Bk4QYIPk.js → sequenceDiagram-FGHM5R23-2xItvAxs.js} +1 -1
- package/dist/client/assets/{src-XMuEuFcU.js → src-YK4ClFOB.js} +1 -1
- package/dist/client/assets/{stateDiagram-FHFEXIEX-CI1G7zGC.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-CnXv8xHg.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-M5x471Ar.js → vennDiagram-DHZGUBPP-BOLv0A8q.js} +1 -1
- package/dist/client/assets/wardley-RL74JXVD-CEXd-1Ht.js +1 -0
- package/dist/client/assets/{wardleyDiagram-NUSXRM2D-BG99uPNN.js → wardleyDiagram-NUSXRM2D-B5lHUuTU.js} +1 -1
- package/dist/client/assets/{xychartDiagram-5P7HB3ND-DO7Upr9G.js → xychartDiagram-5P7HB3ND-D8nLdJo8.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/types/diff.d.ts +0 -10
- 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/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
|
@@ -10,10 +10,6 @@ describe('commentFormatting', () => {
|
|
|
10
10
|
const result = formatCommentPrompt('src/utils/validation.ts', [100, 120], 'Extract validation logic');
|
|
11
11
|
expect(result).toBe('src/utils/validation.ts:L100-L120\nExtract validation logic');
|
|
12
12
|
});
|
|
13
|
-
it('should handle line range as array correctly', () => {
|
|
14
|
-
const result = formatCommentPrompt('src/api/client.ts', [75, 80], 'Add retry logic');
|
|
15
|
-
expect(result).toBe('src/api/client.ts:L75-L80\nAdd retry logic');
|
|
16
|
-
});
|
|
17
13
|
it('should handle multi-line comment body', () => {
|
|
18
14
|
const body = 'This is a comment\nwith multiple lines\nof text';
|
|
19
15
|
const result = formatCommentPrompt('src/index.ts', 10, body);
|
|
@@ -23,14 +19,6 @@ describe('commentFormatting', () => {
|
|
|
23
19
|
const result = formatCommentPrompt('src/components/@shared/Button.tsx', 15, 'Update styles');
|
|
24
20
|
expect(result).toBe('src/components/@shared/Button.tsx:L15\nUpdate styles');
|
|
25
21
|
});
|
|
26
|
-
it('should handle undefined file path gracefully', () => {
|
|
27
|
-
const result = formatCommentPrompt(undefined, 10, 'Comment body');
|
|
28
|
-
expect(result).toBe('<unknown file>:L10\nComment body');
|
|
29
|
-
});
|
|
30
|
-
it('should handle null file path gracefully', () => {
|
|
31
|
-
const result = formatCommentPrompt(null, 10, 'Comment body');
|
|
32
|
-
expect(result).toBe('<unknown file>:L10\nComment body');
|
|
33
|
-
});
|
|
34
22
|
it('should handle empty string file path', () => {
|
|
35
23
|
const result = formatCommentPrompt('', 10, 'Comment body');
|
|
36
24
|
expect(result).toBe('<unknown file>:L10\nComment body');
|
|
@@ -104,26 +92,6 @@ const newCode = 42;
|
|
|
104
92
|
const result = formatAllCommentsPrompt(comments);
|
|
105
93
|
expect(result).toBe('src/App.tsx:L10\nFirst comment\n=====\nsrc/utils/helper.ts:L20-L25\nSecond comment');
|
|
106
94
|
});
|
|
107
|
-
it('should handle comments with line ranges', () => {
|
|
108
|
-
const comments = [
|
|
109
|
-
{
|
|
110
|
-
id: '1',
|
|
111
|
-
file: 'src/components/Form.tsx',
|
|
112
|
-
line: [50, 75],
|
|
113
|
-
body: 'Refactor form validation',
|
|
114
|
-
timestamp: '2024-01-01T00:00:00Z',
|
|
115
|
-
},
|
|
116
|
-
{
|
|
117
|
-
id: '2',
|
|
118
|
-
file: 'src/api/endpoints.ts',
|
|
119
|
-
line: [100, 150],
|
|
120
|
-
body: 'Add error handling',
|
|
121
|
-
timestamp: '2024-01-01T00:01:00Z',
|
|
122
|
-
},
|
|
123
|
-
];
|
|
124
|
-
const result = formatAllCommentsPrompt(comments);
|
|
125
|
-
expect(result).toBe('src/components/Form.tsx:L50-L75\nRefactor form validation\n=====\nsrc/api/endpoints.ts:L100-L150\nAdd error handling');
|
|
126
|
-
});
|
|
127
95
|
it('should preserve comment order', () => {
|
|
128
96
|
const comments = [
|
|
129
97
|
{
|
|
@@ -226,25 +194,5 @@ const newCode = 42;
|
|
|
226
194
|
expect(result).toContain('This is a complex issue that\nspans multiple lines\nand needs attention');
|
|
227
195
|
expect(result).toContain('Total comments: 1');
|
|
228
196
|
});
|
|
229
|
-
it('should format output with correct structure', () => {
|
|
230
|
-
const comments = [
|
|
231
|
-
{
|
|
232
|
-
id: '1',
|
|
233
|
-
file: 'test.ts',
|
|
234
|
-
line: 1,
|
|
235
|
-
body: 'Test comment',
|
|
236
|
-
timestamp: '2024-01-01T00:00:00Z',
|
|
237
|
-
},
|
|
238
|
-
];
|
|
239
|
-
const result = formatCommentsOutput(comments);
|
|
240
|
-
const lines = result.split('\n');
|
|
241
|
-
expect(lines[0]).toBe('');
|
|
242
|
-
expect(lines[1]).toBe('📝 Comments from review session:');
|
|
243
|
-
expect(lines[2]).toBe('='.repeat(50));
|
|
244
|
-
expect(lines[3]).toBe('test.ts:L1');
|
|
245
|
-
expect(lines[4]).toBe('Test comment');
|
|
246
|
-
expect(lines[5]).toBe('='.repeat(50));
|
|
247
|
-
expect(lines[6]).toBe('Total comments: 1');
|
|
248
|
-
});
|
|
249
197
|
});
|
|
250
198
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "difit",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0",
|
|
4
4
|
"description": "A lightweight command-line tool that spins up a local web server to display Git commit diffs in a GitHub-like Files changed view",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -67,7 +67,6 @@
|
|
|
67
67
|
"commander": "^14.0.0",
|
|
68
68
|
"diff": "^9.0.0",
|
|
69
69
|
"express": "^5.1.0",
|
|
70
|
-
"ink": "^7.0.0",
|
|
71
70
|
"lucide-react": "^1.0.0",
|
|
72
71
|
"mermaid": "^11.13.0",
|
|
73
72
|
"open": "^11.0.0",
|
|
@@ -96,9 +95,9 @@
|
|
|
96
95
|
"happy-dom": "^20.0.0",
|
|
97
96
|
"knip": "^6.0.0",
|
|
98
97
|
"lefthook": "^2.0.0",
|
|
99
|
-
"oxfmt": "^0.
|
|
98
|
+
"oxfmt": "^0.47.0",
|
|
100
99
|
"oxlint": "^1.49.0",
|
|
101
|
-
"oxlint-tsgolint": "^0.
|
|
100
|
+
"oxlint-tsgolint": "^0.22.0",
|
|
102
101
|
"playwright": "^1.54.1",
|
|
103
102
|
"postcss": "^8.5.6",
|
|
104
103
|
"tailwindcss": "^4.1.11",
|
|
@@ -110,5 +109,5 @@
|
|
|
110
109
|
"engines": {
|
|
111
110
|
"node": ">=21.0.0"
|
|
112
111
|
},
|
|
113
|
-
"packageManager": "pnpm@
|
|
112
|
+
"packageManager": "pnpm@11.0.0"
|
|
114
113
|
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { waitForEnter } from './utils.js';
|
|
2
|
-
const TUI_DEPRECATION_ISSUES_URL = 'https://github.com/yoshiko-pg/difit/issues';
|
|
3
|
-
export const TUI_DEPRECATION_NOTICE_LINES = [
|
|
4
|
-
`⚠️ TUI mode will be removed in the next major release because ongoing implementation of core features is difficult. If you have feedback, please open an issue: ${TUI_DEPRECATION_ISSUES_URL}`,
|
|
5
|
-
`⚠️ tuiモードは継続的な主要機能の実装が難しいため次回のメジャーリリースで削除予定です。もしご意見がある場合はissueの起票をお願いします。 ${TUI_DEPRECATION_ISSUES_URL}`,
|
|
6
|
-
];
|
|
7
|
-
export const TUI_DEPRECATION_PROMPT = [
|
|
8
|
-
'Press Enter to start TUI mode.',
|
|
9
|
-
'Enterを押すとtuiモードを起動します。',
|
|
10
|
-
].join('\n');
|
|
11
|
-
export async function warnAboutTuiDeprecation(waitForEnterFn = waitForEnter) {
|
|
12
|
-
for (const line of TUI_DEPRECATION_NOTICE_LINES) {
|
|
13
|
-
console.warn(line);
|
|
14
|
-
}
|
|
15
|
-
await waitForEnterFn(`\n${TUI_DEPRECATION_PROMPT}\n`);
|
|
16
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { TUI_DEPRECATION_NOTICE_LINES, TUI_DEPRECATION_PROMPT, warnAboutTuiDeprecation, } from './tuiDeprecation.js';
|
|
3
|
-
describe('TUI deprecation warning', () => {
|
|
4
|
-
afterEach(() => {
|
|
5
|
-
vi.restoreAllMocks();
|
|
6
|
-
});
|
|
7
|
-
it('shows bilingual warnings and waits for Enter before continuing', async () => {
|
|
8
|
-
const consoleWarn = vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
9
|
-
const waitForEnter = vi.fn().mockResolvedValue(undefined);
|
|
10
|
-
await warnAboutTuiDeprecation(waitForEnter);
|
|
11
|
-
expect(consoleWarn).toHaveBeenNthCalledWith(1, TUI_DEPRECATION_NOTICE_LINES[0]);
|
|
12
|
-
expect(consoleWarn).toHaveBeenNthCalledWith(2, TUI_DEPRECATION_NOTICE_LINES[1]);
|
|
13
|
-
expect(waitForEnter).toHaveBeenCalledWith(`\n${TUI_DEPRECATION_PROMPT}\n`);
|
|
14
|
-
expect(consoleWarn.mock.invocationCallOrder[1]).toBeLessThan(waitForEnter.mock.invocationCallOrder[0]);
|
|
15
|
-
});
|
|
16
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import"./chunk-K5T4RW27-BWIFd7pZ.js";import{n as e}from"./chunk-7N4EOEYR-P0tNRVMZ.js";export{e as createArchitectureServices};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{it as e,rt as t}from"./chunk-ICPOFSXX-2vcQKuhB.js";var n=(n,r)=>e.lang.round(t.parse(n)[r]);export{n as t};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{h as e,p as t}from"./src-XMuEuFcU.js";import{b as n}from"./chunk-ICPOFSXX-2vcQKuhB.js";var r=e(e=>{let{securityLevel:r}=n(),i=t(`body`);return r===`sandbox`&&(i=t((t(`#i${e}`).node()?.contentDocument??document).body)),i.select(`#${e}`)},`selectSvgElement`);export{r as t};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{h as e}from"./src-XMuEuFcU.js";var t=class{constructor(e){this.init=e,this.records=this.init()}static{e(this,`ImperativeState`)}reset(){this.records=this.init()}};export{t};
|
|
@@ -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-FMBD7UC4-BH_GgR9u.js";import"./chunk-BSJP7CBP-CGLThsR8.js";import"./chunk-ZZ45TVLE-CXjZua4f.js";import"./chunk-YZCP3GAM-CWXUVxFj.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-4TB4RGXK-Df3b4HEG.js";var a={parser:n,get db(){return new i},renderer:r,styles:t,init:e(e=>{e.class||={},e.class.arrowMarkerAbsolute=e.arrowMarkerAbsolute},`init`)};export{a as diagram};
|
|
@@ -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-FMBD7UC4-BH_GgR9u.js";import"./chunk-BSJP7CBP-CGLThsR8.js";import"./chunk-ZZ45TVLE-CXjZua4f.js";import"./chunk-YZCP3GAM-CWXUVxFj.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-4TB4RGXK-Df3b4HEG.js";var a={parser:n,get db(){return new i},renderer:r,styles:t,init:e(e=>{e.class||={},e.class.arrowMarkerAbsolute=e.arrowMarkerAbsolute},`init`)};export{a as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{i as e}from"./graphlib-C4fWcyt1.js";var t=4;function n(n){return e(n,t)}export{n as t};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import"./chunk-K5T4RW27-BWIFd7pZ.js";import{n as e}from"./chunk-67CJDMHE-CHeCIL1u.js";export{e as createGitGraphServices};
|
|
@@ -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;
|