difit 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/cli/index.js +0 -0
  2. package/dist/client/assets/index-W2UC55JC.css +1 -0
  3. package/dist/client/assets/index-hiGBtmpa.js +142 -0
  4. package/dist/client/index.html +2 -2
  5. package/dist/server/comment-store.js +85 -8
  6. package/dist/server/git-diff.js +3 -1
  7. package/dist/server/server.js +10 -0
  8. package/package.json +3 -2
  9. package/dist/cli/index.d.ts +0 -2
  10. package/dist/cli/index.test.d.ts +0 -1
  11. package/dist/cli/index.test.js +0 -676
  12. package/dist/cli/utils.d.ts +0 -1
  13. package/dist/cli/utils.test.d.ts +0 -1
  14. package/dist/cli/utils.test.js +0 -214
  15. package/dist/client/assets/index-B79lDGO8.js +0 -53
  16. package/dist/client/assets/index-CSJzfcU-.css +0 -1
  17. package/dist/client/assets/prism-css-Bpx-unsJ.js +0 -1
  18. package/dist/client/assets/prism-json-xwnKirkR.js +0 -1
  19. package/dist/client/assets/prism-typescript-B2PMeEx1.js +0 -1
  20. package/dist/server/comment-store.d.ts +0 -13
  21. package/dist/server/git-diff-tui.d.ts +0 -2
  22. package/dist/server/git-diff-tui.js +0 -95
  23. package/dist/server/git-diff.d.ts +0 -10
  24. package/dist/server/git-diff.test.d.ts +0 -1
  25. package/dist/server/git-diff.test.js +0 -292
  26. package/dist/server/server.d.ts +0 -11
  27. package/dist/server/server.test.d.ts +0 -1
  28. package/dist/server/server.test.js +0 -382
  29. package/dist/tui/App.d.ts +0 -8
  30. package/dist/tui/App.js +0 -92
  31. package/dist/tui/App.test.d.ts +0 -1
  32. package/dist/tui/App.test.js +0 -31
  33. package/dist/tui/components/DiffViewer.d.ts +0 -9
  34. package/dist/tui/components/DiffViewer.js +0 -88
  35. package/dist/tui/components/FileList.d.ts +0 -8
  36. package/dist/tui/components/FileList.js +0 -48
  37. package/dist/tui/components/SideBySideDiffViewer.d.ts +0 -9
  38. package/dist/tui/components/SideBySideDiffViewer.js +0 -237
  39. package/dist/tui/components/StatusBar.d.ts +0 -8
  40. package/dist/tui/components/StatusBar.js +0 -23
  41. package/dist/tui/utils/parseDiff.d.ts +0 -2
  42. package/dist/tui/utils/parseDiff.js +0 -68
  43. package/dist/types/diff.d.ts +0 -33
  44. package/dist/utils/fileUtils.d.ts +0 -12
  45. package/dist/utils/fileUtils.js +0 -21
@@ -1,48 +0,0 @@
1
- import React from 'react';
2
- import { Box, Text } from 'ink';
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 { 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;
@@ -1,237 +0,0 @@
1
- import React, { useState } from 'react';
2
- import { Box, Text, useInput, useApp } from 'ink';
3
- import { parseDiff } from '../utils/parseDiff.js';
4
- const SideBySideDiffViewer = ({ files, initialFileIndex, onBack, }) => {
5
- const [currentFileIndex, setCurrentFileIndex] = useState(initialFileIndex);
6
- const [scrollOffset, setScrollOffset] = useState(0);
7
- const currentFile = files[currentFileIndex];
8
- if (!currentFile || files.length === 0) {
9
- return (React.createElement(Box, { flexDirection: "column", flexGrow: 1 },
10
- React.createElement(Text, { color: "yellow" }, "No files to display"),
11
- React.createElement(Box, { marginTop: 1 },
12
- React.createElement(Text, { dimColor: true }, "Press ESC or 'b' to go back"))));
13
- }
14
- const parsedDiff = parseDiff(currentFile.diff);
15
- // Calculate total lines for current file
16
- const allLines = [];
17
- parsedDiff.chunks.forEach((chunk) => {
18
- // Add chunk header
19
- allLines.push({
20
- old: chunk.header,
21
- new: chunk.header,
22
- type: 'header',
23
- });
24
- let oldIdx = 0;
25
- let newIdx = 0;
26
- while (oldIdx < chunk.lines.length || newIdx < chunk.lines.length) {
27
- const oldLine = chunk.lines[oldIdx];
28
- const newLine = chunk.lines[newIdx];
29
- if (oldLine?.type === 'remove' && newLine?.type === 'add') {
30
- // Same line modified - show side by side
31
- allLines.push({
32
- old: oldLine.content,
33
- new: newLine.content,
34
- oldNum: oldLine.oldLineNumber,
35
- newNum: newLine.newLineNumber,
36
- type: 'modified',
37
- });
38
- oldIdx++;
39
- newIdx++;
40
- }
41
- else if (oldLine?.type === 'remove') {
42
- // Line removed
43
- allLines.push({
44
- old: oldLine.content,
45
- oldNum: oldLine.oldLineNumber,
46
- type: 'remove',
47
- });
48
- oldIdx++;
49
- }
50
- else if (newLine?.type === 'add') {
51
- // Line added
52
- allLines.push({
53
- new: newLine.content,
54
- newNum: newLine.newLineNumber,
55
- type: 'add',
56
- });
57
- newIdx++;
58
- }
59
- else if (oldLine?.type === 'context') {
60
- // Unchanged line
61
- allLines.push({
62
- old: oldLine.content,
63
- new: oldLine.content,
64
- oldNum: oldLine.oldLineNumber,
65
- newNum: oldLine.newLineNumber,
66
- type: 'context',
67
- });
68
- oldIdx++;
69
- newIdx++;
70
- }
71
- else {
72
- oldIdx++;
73
- newIdx++;
74
- }
75
- }
76
- });
77
- const viewportHeight = Math.max(10, (process.stdout.rows || 24) - 10); // StatusBar(3) + file nav(3) + footer(3) + margin(1)
78
- const maxScroll = Math.max(0, allLines.length - viewportHeight);
79
- const { exit } = useApp();
80
- useInput((input, key) => {
81
- if (input === 'q' || (key.ctrl && input === 'c')) {
82
- exit();
83
- return;
84
- }
85
- if (key.escape || input === 'b') {
86
- onBack();
87
- return;
88
- }
89
- // Scroll within file
90
- if (key.upArrow || input === 'k') {
91
- setScrollOffset((prev) => Math.max(0, prev - 1));
92
- }
93
- if (key.downArrow || input === 'j') {
94
- setScrollOffset((prev) => Math.min(maxScroll, prev + 1));
95
- }
96
- if (key.pageUp) {
97
- setScrollOffset((prev) => Math.max(0, prev - viewportHeight));
98
- }
99
- if (key.pageDown) {
100
- setScrollOffset((prev) => Math.min(maxScroll, prev + viewportHeight));
101
- }
102
- // Navigate between files
103
- if (key.tab && !key.shift) {
104
- // Next file (loop to first when at end)
105
- setCurrentFileIndex((currentFileIndex + 1) % files.length);
106
- setScrollOffset(0);
107
- }
108
- if (key.tab && key.shift) {
109
- // Previous file (loop to last when at start)
110
- setCurrentFileIndex((currentFileIndex - 1 + files.length) % files.length);
111
- setScrollOffset(0);
112
- }
113
- }, { isActive: true });
114
- const visibleLines = allLines.slice(scrollOffset, scrollOffset + viewportHeight);
115
- const terminalWidth = process.stdout.columns || 80;
116
- const columnWidth = Math.floor((terminalWidth - 6) / 2); // 6 for borders and separators
117
- const getLineColor = (type) => {
118
- switch (type) {
119
- case 'add':
120
- return 'green';
121
- case 'remove':
122
- return 'red';
123
- case 'modified':
124
- return undefined; // Will be handled separately for each side
125
- case 'header':
126
- return 'cyan';
127
- default:
128
- return undefined;
129
- }
130
- };
131
- const truncateLine = (line, width) => {
132
- if (line.length <= width)
133
- return line.padEnd(width);
134
- return line.substring(0, width - 1) + '…';
135
- };
136
- return (React.createElement(Box, { flexDirection: "column", flexGrow: 1 },
137
- React.createElement(Box, { marginBottom: 1, flexDirection: "column" },
138
- React.createElement(Box, null,
139
- React.createElement(Text, { bold: true },
140
- currentFile.path,
141
- " (",
142
- currentFileIndex + 1,
143
- "/",
144
- files.length,
145
- ")"),
146
- React.createElement(Text, { dimColor: true },
147
- ' ',
148
- "- ",
149
- currentFile.additions,
150
- " additions, ",
151
- currentFile.deletions,
152
- " deletions")),
153
- React.createElement(Box, { height: 2, overflow: "hidden", flexDirection: "column" }, (() => {
154
- const terminalWidth = process.stdout.columns || 80;
155
- const maxWidth = terminalWidth - 4; // Leave some margin
156
- // Generate file list with current file highlighted
157
- const fileItems = [];
158
- // Add files before current
159
- for (let i = Math.max(0, currentFileIndex - 2); i < currentFileIndex; i++) {
160
- fileItems.push({
161
- text: files[i].path,
162
- isActive: false,
163
- });
164
- }
165
- // Add current file
166
- fileItems.push({
167
- text: `[${files[currentFileIndex].path}]`,
168
- isActive: true,
169
- });
170
- // Add files after current
171
- for (let i = currentFileIndex + 1; i < Math.min(files.length, currentFileIndex + 3); i++) {
172
- fileItems.push({
173
- text: files[i].path,
174
- isActive: false,
175
- });
176
- }
177
- // Build lines (max 2 lines)
178
- const lines = [[]];
179
- let currentLineWidth = 0;
180
- for (const item of fileItems) {
181
- const itemWidth = item.text.length + 3; // Include separator
182
- if (currentLineWidth + itemWidth > maxWidth && lines.length < 2) {
183
- lines.push([]);
184
- currentLineWidth = 0;
185
- }
186
- if (lines.length <= 2) {
187
- lines[lines.length - 1].push(item);
188
- currentLineWidth += itemWidth;
189
- }
190
- }
191
- return lines.map((line, lineIndex) => (React.createElement(Box, { key: lineIndex }, line.map((item, itemIndex) => (React.createElement(React.Fragment, { key: itemIndex },
192
- itemIndex > 0 && React.createElement(Text, { dimColor: true }, " | "),
193
- React.createElement(Text, { color: item.isActive ? 'cyan' : undefined, dimColor: !item.isActive }, item.text)))))));
194
- })())),
195
- React.createElement(Box, { borderStyle: "single", flexDirection: "column", flexGrow: 1 },
196
- React.createElement(Box, { borderStyle: "single", borderTop: false, borderLeft: false, borderRight: false },
197
- React.createElement(Box, { width: columnWidth },
198
- React.createElement(Text, { dimColor: true }, " \u2502 "),
199
- React.createElement(Text, { bold: true }, "Old")),
200
- React.createElement(Text, { dimColor: true }, " \u2503 "),
201
- React.createElement(Box, { width: columnWidth },
202
- React.createElement(Text, { dimColor: true }, " \u2502 "),
203
- React.createElement(Text, { bold: true }, "New"))),
204
- React.createElement(Box, { flexDirection: "column", flexGrow: 1 }, visibleLines.map((line, index) => (React.createElement(Box, { key: `line-${scrollOffset + index}` },
205
- React.createElement(Box, { width: columnWidth },
206
- React.createElement(Text, { dimColor: true }, line.oldNum ? String(line.oldNum).padStart(4) : ' '),
207
- React.createElement(Text, { dimColor: true }, " \u2502 "),
208
- React.createElement(Text, { color: line.type === 'remove' || line.type === 'modified' ? 'red' : undefined, dimColor: line.type === 'header' }, line.type === 'remove' || line.type === 'modified' ? '- ' : ' '),
209
- React.createElement(Text, { color: line.type === 'remove' || line.type === 'modified'
210
- ? 'red'
211
- : getLineColor(line.type) }, line.old
212
- ? truncateLine(line.old, columnWidth - 10)
213
- : ' '.repeat(columnWidth - 10))),
214
- React.createElement(Text, { dimColor: true }, " \u2503 "),
215
- React.createElement(Box, { width: columnWidth },
216
- React.createElement(Text, { dimColor: true }, line.newNum ? String(line.newNum).padStart(4) : ' '),
217
- React.createElement(Text, { dimColor: true }, " \u2502 "),
218
- React.createElement(Text, { color: line.type === 'add' || line.type === 'modified' ? 'green' : undefined, dimColor: line.type === 'header' }, line.type === 'add' || line.type === 'modified' ? '+ ' : ' '),
219
- React.createElement(Text, { color: line.type === 'add' || line.type === 'modified'
220
- ? 'green'
221
- : getLineColor(line.type) }, line.new
222
- ? truncateLine(line.new, columnWidth - 10)
223
- : ' '.repeat(columnWidth - 10)))))))),
224
- React.createElement(Box, { marginTop: 1, justifyContent: "space-between" },
225
- React.createElement(Text, { dimColor: true },
226
- "Lines ",
227
- scrollOffset + 1,
228
- "-",
229
- Math.min(scrollOffset + viewportHeight, allLines.length),
230
- " of",
231
- ' ',
232
- allLines.length,
233
- scrollOffset + viewportHeight < allLines.length &&
234
- ` (${allLines.length - scrollOffset - viewportHeight} more)`),
235
- React.createElement(Text, { dimColor: true }, "Tab: next file | Shift+Tab: prev file | \u2191\u2193/jk: scroll | ESC/b: back"))));
236
- };
237
- export default SideBySideDiffViewer;
@@ -1,8 +0,0 @@
1
- import React from 'react';
2
- interface StatusBarProps {
3
- commitish: string;
4
- totalFiles: number;
5
- currentMode: 'list' | 'inline' | 'side-by-side';
6
- }
7
- declare const StatusBar: React.FC<StatusBarProps>;
8
- export default StatusBar;
@@ -1,23 +0,0 @@
1
- import React from 'react';
2
- import { Box, Text } from 'ink';
3
- const StatusBar = ({ commitish, totalFiles, currentMode }) => {
4
- return (React.createElement(Box, { borderStyle: "round", paddingX: 1, marginBottom: 1 },
5
- React.createElement(Box, { flexGrow: 1 },
6
- React.createElement(Text, { bold: true, color: "cyan" }, "\uD83D\uDCCB ReviewIt TUI"),
7
- React.createElement(Text, null, " | "),
8
- React.createElement(Text, { color: "yellow" }, commitish),
9
- React.createElement(Text, null, " | "),
10
- React.createElement(Text, null,
11
- totalFiles,
12
- " files changed")),
13
- React.createElement(Box, null,
14
- React.createElement(Text, { dimColor: true },
15
- "[",
16
- currentMode === 'list'
17
- ? 'File List'
18
- : currentMode === 'side-by-side'
19
- ? 'Side-by-Side'
20
- : 'Inline Diff',
21
- "]"))));
22
- };
23
- export default StatusBar;
@@ -1,2 +0,0 @@
1
- import { ParsedDiff } from '../../types/diff.js';
2
- export declare function parseDiff(diffText: string): ParsedDiff;
@@ -1,68 +0,0 @@
1
- export function parseDiff(diffText) {
2
- const lines = diffText.split('\n');
3
- const chunks = [];
4
- let currentChunk = null;
5
- let oldLineNumber = 0;
6
- let newLineNumber = 0;
7
- for (let i = 0; i < lines.length; i++) {
8
- const line = lines[i];
9
- // Skip file headers
10
- if (line.startsWith('diff --git') ||
11
- line.startsWith('index ') ||
12
- line.startsWith('---') ||
13
- line.startsWith('+++')) {
14
- continue;
15
- }
16
- // Chunk header
17
- if (line.startsWith('@@')) {
18
- const match = line.match(/@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@/);
19
- if (match) {
20
- const [, oldStart, oldLinesStr, newStart, newLinesStr] = match;
21
- currentChunk = {
22
- header: line,
23
- oldStart: parseInt(oldStart),
24
- oldLines: parseInt(oldLinesStr || '1'),
25
- newStart: parseInt(newStart),
26
- newLines: parseInt(newLinesStr || '1'),
27
- lines: [],
28
- };
29
- chunks.push(currentChunk);
30
- oldLineNumber = currentChunk.oldStart - 1;
31
- newLineNumber = currentChunk.newStart - 1;
32
- }
33
- continue;
34
- }
35
- // Skip if no current chunk
36
- if (!currentChunk)
37
- continue;
38
- // Parse diff lines
39
- if (line.startsWith('+')) {
40
- newLineNumber++;
41
- currentChunk.lines.push({
42
- type: 'add',
43
- content: line.substring(1),
44
- newLineNumber: newLineNumber,
45
- });
46
- }
47
- else if (line.startsWith('-')) {
48
- oldLineNumber++;
49
- currentChunk.lines.push({
50
- type: 'remove',
51
- content: line.substring(1),
52
- oldLineNumber: oldLineNumber,
53
- });
54
- }
55
- else {
56
- // Context line
57
- oldLineNumber++;
58
- newLineNumber++;
59
- currentChunk.lines.push({
60
- type: 'context',
61
- content: line.substring(1),
62
- oldLineNumber: oldLineNumber,
63
- newLineNumber: newLineNumber,
64
- });
65
- }
66
- }
67
- return { chunks };
68
- }
@@ -1,33 +0,0 @@
1
- export interface DiffFile {
2
- path: string;
3
- oldPath?: string;
4
- status: 'modified' | 'added' | 'deleted' | 'renamed';
5
- additions: number;
6
- deletions: number;
7
- chunks: DiffChunk[];
8
- }
9
- export interface DiffChunk {
10
- header: string;
11
- oldStart: number;
12
- oldLines: number;
13
- newStart: number;
14
- newLines: number;
15
- lines: DiffLine[];
16
- }
17
- export interface DiffLine {
18
- type: 'add' | 'delete' | 'normal' | 'hunk';
19
- content: string;
20
- oldLineNumber?: number;
21
- newLineNumber?: number;
22
- }
23
- export interface DiffResponse {
24
- commit: string;
25
- files: DiffFile[];
26
- }
27
- export interface Comment {
28
- id: string;
29
- file: string;
30
- line: number;
31
- body: string;
32
- timestamp: string;
33
- }
@@ -1,12 +0,0 @@
1
- /**
2
- * Get file extension from filename or filepath
3
- * @param filename - The filename or filepath
4
- * @returns The file extension in lowercase, or null if no extension
5
- */
6
- export declare function getFileExtension(filename: string): string | null;
7
- /**
8
- * Get filename from filepath
9
- * @param filepath - The filepath
10
- * @returns The filename
11
- */
12
- export declare function getFileName(filepath: string): string;
@@ -1,21 +0,0 @@
1
- /**
2
- * Get file extension from filename or filepath
3
- * @param filename - The filename or filepath
4
- * @returns The file extension in lowercase, or null if no extension
5
- */
6
- export function getFileExtension(filename) {
7
- if (!filename)
8
- return null;
9
- const extension = filename.split('.').pop()?.toLowerCase();
10
- return extension || null;
11
- }
12
- /**
13
- * Get filename from filepath
14
- * @param filepath - The filepath
15
- * @returns The filename
16
- */
17
- export function getFileName(filepath) {
18
- if (!filepath)
19
- return '';
20
- return filepath.split('/').pop() || '';
21
- }