diffwatch 2.0.5 → 2.0.7
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.md +0 -35
- package/package.json +1 -1
- package/src/components/DiffViewer.tsx +19 -6
- package/src/components/FileList.tsx +32 -2
- package/src/version.ts +1 -1
package/README.md
CHANGED
|
@@ -8,15 +8,12 @@ A TUI app for watching git repository file changes with diffs.
|
|
|
8
8
|
|
|
9
9
|
- **File Status Tracking**: Shows all changed files in a Git repository with their status (modified, new, deleted, renamed, etc.)
|
|
10
10
|
- **Visual Diff Viewer**: Displays differences between file versions with syntax highlighting and line-by-line comparison
|
|
11
|
-
- **Interactive Terminal Interface**: Built with React-like components for terminal UI using the @opentui library
|
|
12
|
-
- **Real-time Updates**: Automatically polls for changes in the repository every 2 seconds
|
|
13
11
|
- **File Operations**:
|
|
14
12
|
- Revert changes to files
|
|
15
13
|
- Delete files safely (with trash/recycle bin support)
|
|
16
14
|
- Open files in the default editor
|
|
17
15
|
- **Search Functionality**: Allows searching for files by content within the repository
|
|
18
16
|
- **Commit History Browser**: View commit history with hash, message, author, and date
|
|
19
|
-
- **Keyboard Navigation**: Full keyboard controls for navigating files and diffs
|
|
20
17
|
|
|
21
18
|
## Installation
|
|
22
19
|
|
|
@@ -62,8 +59,6 @@ bun install
|
|
|
62
59
|
|
|
63
60
|
## Usage
|
|
64
61
|
|
|
65
|
-
### Basic Usage
|
|
66
|
-
|
|
67
62
|
To watch the current git repository:
|
|
68
63
|
```bash
|
|
69
64
|
diffwatch
|
|
@@ -97,16 +92,6 @@ Watch a specific project:
|
|
|
97
92
|
diffwatch --path ~/projects/my-app
|
|
98
93
|
```
|
|
99
94
|
|
|
100
|
-
Check version:
|
|
101
|
-
```bash
|
|
102
|
-
diffwatch --version
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
Show help:
|
|
106
|
-
```bash
|
|
107
|
-
diffwatch --help
|
|
108
|
-
```
|
|
109
|
-
|
|
110
95
|
## Keyboard Shortcuts
|
|
111
96
|
|
|
112
97
|
- `↑` / `↓` - Navigate through the file list
|
|
@@ -119,26 +104,6 @@ diffwatch --help
|
|
|
119
104
|
- `Q` - Quit the application
|
|
120
105
|
- `Esc` - Exit search mode or close dialogs
|
|
121
106
|
|
|
122
|
-
## Development
|
|
123
|
-
|
|
124
|
-
### Prerequisites
|
|
125
|
-
|
|
126
|
-
- [Bun](https://bun.sh/) - Fast JavaScript runtime and package manager
|
|
127
|
-
- Node.js 18+ (for running published package)
|
|
128
|
-
|
|
129
|
-
### Setup
|
|
130
|
-
|
|
131
|
-
1. Clone the repository:
|
|
132
|
-
```bash
|
|
133
|
-
git clone https://github.com/sarfraznawaz2005/diffwatch.git
|
|
134
|
-
cd diffwatch
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
2. Install dependencies:
|
|
138
|
-
```bash
|
|
139
|
-
bun install
|
|
140
|
-
```
|
|
141
|
-
|
|
142
107
|
### Running the App in Development
|
|
143
108
|
|
|
144
109
|
```bash
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useEffect, useState, useMemo, useRef } from 'react';
|
|
2
|
-
import { useKeyboard } from '@opentui/react';
|
|
2
|
+
import { useKeyboard, useRenderer } from '@opentui/react';
|
|
3
3
|
import { getRawDiff } from '../utils/git';
|
|
4
4
|
import * as Diff2Html from 'diff2html';
|
|
5
5
|
import * as fsPromises from 'fs/promises';
|
|
@@ -22,6 +22,7 @@ export function DiffViewer({ filename, focused, searchQuery, status, repoPath }:
|
|
|
22
22
|
const [isBinary, setIsBinary] = useState(false);
|
|
23
23
|
const scrollRef = useRef<any>(null);
|
|
24
24
|
const pollingIntervalRef = useRef<NodeJS.Timeout | null>(null);
|
|
25
|
+
const renderer = useRenderer();
|
|
25
26
|
|
|
26
27
|
const loadContent = async (showLoading = true) => {
|
|
27
28
|
if (!filename) {
|
|
@@ -115,9 +116,19 @@ const diffData = useMemo(() => {
|
|
|
115
116
|
return fileContent.split('\n');
|
|
116
117
|
}, [fileContent]);
|
|
117
118
|
|
|
118
|
-
//
|
|
119
|
-
|
|
119
|
+
// Calculate the max line width based on available width in the diff viewer
|
|
120
|
+
// The diff viewer takes up 67% of the total width, and we need to account for:
|
|
121
|
+
// - Line numbers (6 chars: " 1234: ")
|
|
122
|
+
// - Prefix (+/-/space) (1 char)
|
|
123
|
+
// - Borders (2 chars: left and right)
|
|
124
|
+
// - Some padding (1 char)
|
|
125
|
+
// Total overhead: 6 (line numbers) + 1 (prefix) + 2 (borders) = 9 chars
|
|
126
|
+
const calculateMaxLineWidth = (): number => {
|
|
127
|
+
const estimatedWidth = Math.floor((renderer.width * 0.67) - 9); // 9 chars for line numbers, prefix, and borders
|
|
128
|
+
return Math.max(20, estimatedWidth); // minimum width of 20
|
|
129
|
+
};
|
|
120
130
|
|
|
131
|
+
// Soft wrap lines based on available width
|
|
121
132
|
const wrapLine = (text: string, maxLength: number): string[] => {
|
|
122
133
|
if (text.length <= maxLength) return [text];
|
|
123
134
|
|
|
@@ -145,11 +156,13 @@ const diffData = useMemo(() => {
|
|
|
145
156
|
return lines;
|
|
146
157
|
};
|
|
147
158
|
|
|
159
|
+
const maxLineWidth = calculateMaxLineWidth();
|
|
160
|
+
|
|
148
161
|
return (
|
|
149
162
|
<box
|
|
150
163
|
border
|
|
151
164
|
title={` Diff (${filename || 'None'}) `}
|
|
152
|
-
width="
|
|
165
|
+
width="67%"
|
|
153
166
|
borderColor={focused ? 'yellow' : 'grey'}
|
|
154
167
|
flexDirection="column"
|
|
155
168
|
>
|
|
@@ -162,7 +175,7 @@ const diffData = useMemo(() => {
|
|
|
162
175
|
) : isNewFile ? (
|
|
163
176
|
<scrollbox flexGrow={1} ref={scrollRef}>
|
|
164
177
|
{contentLines.flatMap((line, i) => {
|
|
165
|
-
const wrappedLines = wrapLine(line,
|
|
178
|
+
const wrappedLines = wrapLine(line, maxLineWidth);
|
|
166
179
|
|
|
167
180
|
return wrappedLines.map((wrappedLine, wrapIdx) => {
|
|
168
181
|
const isFirstLine = wrapIdx === 0;
|
|
@@ -214,7 +227,7 @@ const diffData = useMemo(() => {
|
|
|
214
227
|
const lnText = ln ? `${String(ln).padStart(4)}: ` : ' ';
|
|
215
228
|
|
|
216
229
|
// Wrap content if it's too long
|
|
217
|
-
const wrappedContent = wrapLine(content,
|
|
230
|
+
const wrappedContent = wrapLine(content, maxLineWidth);
|
|
218
231
|
|
|
219
232
|
return (
|
|
220
233
|
<>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useRef, useEffect } from 'react';
|
|
2
|
+
import { useRenderer } from '@opentui/react';
|
|
2
3
|
import { type FileStatus } from '../utils/git';
|
|
3
4
|
|
|
4
5
|
interface FileListProps {
|
|
@@ -12,6 +13,7 @@ interface FileListProps {
|
|
|
12
13
|
|
|
13
14
|
export function FileList({ files, selectedIndex, focused, searchQuery, onSelect, onScroll }: FileListProps) {
|
|
14
15
|
const scrollRef = useRef<any>(null);
|
|
16
|
+
const renderer = useRenderer();
|
|
15
17
|
|
|
16
18
|
useEffect(() => {
|
|
17
19
|
if (scrollRef.current) {
|
|
@@ -26,6 +28,32 @@ export function FileList({ files, selectedIndex, focused, searchQuery, onSelect,
|
|
|
26
28
|
}
|
|
27
29
|
}, [selectedIndex]);
|
|
28
30
|
|
|
31
|
+
// Calculate the max length for file paths based on available width
|
|
32
|
+
// Account for the symbol, space, and padding
|
|
33
|
+
const calculateMaxPathLength = (): number => {
|
|
34
|
+
// Get the width of the container (33% of total width)
|
|
35
|
+
// Since we can't directly measure the rendered width, we'll estimate based on a percentage
|
|
36
|
+
// considering that the container is 33% of the total width
|
|
37
|
+
// and we need to account for the symbol (1 char) + space (1 char) before the filename
|
|
38
|
+
// Also account for borders (left and right = 2 chars total)
|
|
39
|
+
// Total overhead: 1 (symbol) + 1 (space after symbol) + 2 (borders) = 4 chars
|
|
40
|
+
const estimatedWidth = Math.floor((renderer.width * 0.33) - 4); // 4 chars for symbol + space + borders
|
|
41
|
+
return Math.max(10, estimatedWidth); // minimum length of 10
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const truncatePath = (path: string, maxLength: number): string => {
|
|
45
|
+
if (path.length <= maxLength) {
|
|
46
|
+
return path;
|
|
47
|
+
}
|
|
48
|
+
if (maxLength <= 3) {
|
|
49
|
+
return '.'.repeat(maxLength);
|
|
50
|
+
}
|
|
51
|
+
// Truncate the path and add ellipsis (...) at the end
|
|
52
|
+
return path.substring(0, maxLength - 3) + '...';
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const maxPathLength = calculateMaxPathLength();
|
|
56
|
+
|
|
29
57
|
const title = searchQuery
|
|
30
58
|
? ` Files (${files.length}) - "${searchQuery}" `
|
|
31
59
|
: ` Files (${files.length}) `;
|
|
@@ -34,7 +62,7 @@ export function FileList({ files, selectedIndex, focused, searchQuery, onSelect,
|
|
|
34
62
|
<box
|
|
35
63
|
border
|
|
36
64
|
title={title}
|
|
37
|
-
width="
|
|
65
|
+
width="33%"
|
|
38
66
|
height="100%"
|
|
39
67
|
borderColor={focused ? 'yellow' : 'grey'}
|
|
40
68
|
flexDirection="column"
|
|
@@ -74,6 +102,8 @@ export function FileList({ files, selectedIndex, focused, searchQuery, onSelect,
|
|
|
74
102
|
};
|
|
75
103
|
const symbol = symbolMap[file.status] || '?';
|
|
76
104
|
|
|
105
|
+
const truncatedPath = truncatePath(file.path, maxPathLength);
|
|
106
|
+
|
|
77
107
|
return (
|
|
78
108
|
<box
|
|
79
109
|
key={file.path}
|
|
@@ -84,7 +114,7 @@ export function FileList({ files, selectedIndex, focused, searchQuery, onSelect,
|
|
|
84
114
|
<text
|
|
85
115
|
fg={isSelected ? 'black' : color}
|
|
86
116
|
>
|
|
87
|
-
{` ${symbol} ${
|
|
117
|
+
{` ${symbol} ${truncatedPath}`}
|
|
88
118
|
</text>
|
|
89
119
|
</box>
|
|
90
120
|
);
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const BUILD_VERSION = "2.0.
|
|
1
|
+
export const BUILD_VERSION = "2.0.7";
|