diffwatch 1.0.1 → 1.0.3

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/dist/index.js CHANGED
@@ -70,8 +70,29 @@ function main() {
70
70
  border: { type: 'line' },
71
71
  tags: false,
72
72
  });
73
+ const searchBox = blessed.box({
74
+ top: 'center',
75
+ left: 'center',
76
+ width: '50%',
77
+ height: 3,
78
+ label: ' Search ',
79
+ border: { type: 'line' },
80
+ style: { border: { fg: 'yellow' } },
81
+ hidden: true,
82
+ });
83
+ const searchInput = blessed.textbox({
84
+ parent: searchBox,
85
+ top: 0,
86
+ left: 0,
87
+ width: '100%-2',
88
+ height: 1,
89
+ keys: true,
90
+ inputOnFocus: true,
91
+ style: { fg: 'white', bg: 'black' },
92
+ });
73
93
  screen.append(fileList);
74
94
  screen.append(diffView);
95
+ screen.append(searchBox);
75
96
  const updateBorders = () => {
76
97
  fileList.style.border.fg = screen.focused === fileList ? 'yellow' : 'white';
77
98
  diffView.style.border.fg = screen.focused === diffView ? 'yellow' : 'white';
@@ -80,6 +101,7 @@ function main() {
80
101
  let currentFiles = [];
81
102
  let lastSelectedPath = null;
82
103
  let diffUpdateTimeout = null;
104
+ let currentSearchTerm = '';
83
105
  const scheduleDiffUpdate = () => {
84
106
  if (diffUpdateTimeout)
85
107
  clearTimeout(diffUpdateTimeout);
@@ -107,17 +129,33 @@ function main() {
107
129
  const selectedIndex = fileList.selected;
108
130
  const selectedFile = currentFiles[selectedIndex];
109
131
  if (selectedFile) {
110
- const diff = yield gitHandler.getDiff(selectedFile.path);
111
- const formattedDiff = (0, diff_formatter_1.formatDiffWithDiff2Html)(diff);
112
- const newLabel = ` Diff (${selectedFile.path}) `;
132
+ let content = '';
133
+ let label = ` Diff (${selectedFile.path}) `;
134
+ if (selectedFile.status !== 'unchanged' && selectedFile.status !== 'deleted') {
135
+ const diff = yield gitHandler.getDiff(selectedFile.path);
136
+ content = (0, diff_formatter_1.formatDiffWithDiff2Html)(diff, currentSearchTerm);
137
+ }
138
+ if (!content && selectedFile.status !== 'deleted') {
139
+ content = yield gitHandler.getFileContent(selectedFile.path);
140
+ // Highlight search term in full content
141
+ if (currentSearchTerm) {
142
+ const regex = new RegExp(`(${currentSearchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
143
+ content = content.replace(regex, `\x1b[43m\x1b[30m$1\x1b[0m`);
144
+ }
145
+ label = ` File (${selectedFile.path}) `;
146
+ }
147
+ else if (!content && selectedFile.status === 'deleted') {
148
+ content = chalk_1.default.red('File was deleted.');
149
+ label = ` Diff (${selectedFile.path}) `;
150
+ }
113
151
  const currentContent = diffView.content;
114
152
  const currentLabel = diffView.label;
115
153
  // Only update if content or label changed to reduce flickering
116
- if (formattedDiff !== currentContent || newLabel !== currentLabel) {
154
+ if (content !== currentContent || label !== currentLabel) {
117
155
  const savedScroll = diffView.scrollTop;
118
156
  const isNewFile = selectedFile.path !== lastSelectedPath;
119
- diffView.setContent(formattedDiff);
120
- diffView.setLabel(newLabel);
157
+ diffView.setContent(content);
158
+ diffView.setLabel(label);
121
159
  if (isNewFile) {
122
160
  diffView.scrollTo(0);
123
161
  }
@@ -143,9 +181,14 @@ function main() {
143
181
  var _a;
144
182
  // Preserve selected file path and scroll positions
145
183
  const selectedPath = (_a = currentFiles[fileList.selected]) === null || _a === void 0 ? void 0 : _a.path;
146
- const fileListScroll = fileList.scroll;
147
184
  const diffScroll = diffView.scrollTop;
148
- const files = yield gitHandler.getStatus();
185
+ let files;
186
+ if (currentSearchTerm) {
187
+ files = yield gitHandler.searchFiles(currentSearchTerm);
188
+ }
189
+ else {
190
+ files = yield gitHandler.getStatus();
191
+ }
149
192
  currentFiles = files;
150
193
  const items = files.map(f => {
151
194
  let color = '{white-fg}';
@@ -157,10 +200,13 @@ function main() {
157
200
  color = '{blue-fg}';
158
201
  else if (f.status === 'unstaged')
159
202
  color = '{white-fg}';
203
+ else if (f.status === 'unchanged')
204
+ color = '{grey-fg}';
160
205
  return `${color}${f.path}{/}`;
161
206
  });
162
207
  fileList.setItems(items);
163
- fileList.setLabel(` Files (${files.length}) `);
208
+ const labelTitle = currentSearchTerm ? ` Files (${files.length}) - Searching: "${currentSearchTerm}" ` : ` Files (${files.length}) `;
209
+ fileList.setLabel(labelTitle);
164
210
  if (items.length > 0) {
165
211
  // Restore selection by path if possible
166
212
  const newSelectedIndex = selectedPath ? currentFiles.findIndex(f => f.path === selectedPath) : -1;
@@ -173,11 +219,17 @@ function main() {
173
219
  yield updateDiff();
174
220
  }
175
221
  else {
176
- diffView.setContent('No changes detected.');
222
+ diffView.setContent(currentSearchTerm ? `No files match "${currentSearchTerm}".` : 'No changes detected.');
177
223
  diffView.setLabel(' Diff () ');
178
224
  }
179
- // Restore scroll positions
180
- fileList.scroll = fileListScroll;
225
+ // Restore scroll positions if reasonably possible (reset if list changed drastically)
226
+ // Actually, if we filter, the scroll position might be invalid.
227
+ // Ideally we keep it 0 if it was 0 or just let the select() call handle scrolling to the item.
228
+ // The previous implementation blindly restored scrollTop.
229
+ // If the list shrunk, select() should have brought it into view.
230
+ // We only explicitly restore if items.length > 0
231
+ // But setting scroll to previous value might be wrong if the list is now shorter.
232
+ // Safe to only restore diffView scroll as it depends on content, fileList is handled by select.
181
233
  diffView.scrollTop = diffScroll;
182
234
  screen.render();
183
235
  });
@@ -188,8 +240,34 @@ function main() {
188
240
  scheduleDiffUpdate();
189
241
  });
190
242
  screen.key(['escape', 'q', 'C-c'], () => {
191
- screen.destroy();
192
- process.exit(0);
243
+ if (!searchBox.hidden) {
244
+ searchBox.hide();
245
+ screen.render();
246
+ fileList.focus();
247
+ }
248
+ else {
249
+ screen.destroy();
250
+ process.exit(0);
251
+ }
252
+ });
253
+ screen.key(['s'], () => {
254
+ searchBox.show();
255
+ searchBox.setFront();
256
+ searchInput.setValue(currentSearchTerm);
257
+ searchInput.focus();
258
+ screen.render();
259
+ });
260
+ searchInput.on('submit', (value) => __awaiter(this, void 0, void 0, function* () {
261
+ currentSearchTerm = (value || '').trim();
262
+ searchBox.hide();
263
+ fileList.focus();
264
+ // Force immediate update
265
+ yield updateFileList();
266
+ }));
267
+ searchInput.on('cancel', () => {
268
+ searchBox.hide();
269
+ fileList.focus();
270
+ screen.render();
193
271
  });
194
272
  screen.key(['tab'], () => {
195
273
  if (screen.focused === fileList) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- export declare function formatDiffWithDiff2Html(diffString: string): string;
1
+ export declare function formatDiffWithDiff2Html(diffString: string, searchTerm?: string): string;
@@ -36,9 +36,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.formatDiffWithDiff2Html = formatDiffWithDiff2Html;
37
37
  const cheerio = __importStar(require("cheerio"));
38
38
  const Diff2Html = require('diff2html');
39
- function formatDiffWithDiff2Html(diffString) {
39
+ function formatDiffWithDiff2Html(diffString, searchTerm) {
40
40
  if (!diffString || diffString.trim() === '') {
41
- return 'No changes detected.';
41
+ return '';
42
42
  }
43
43
  try {
44
44
  const html = Diff2Html.html(diffString, {
@@ -69,7 +69,15 @@ function formatDiffWithDiff2Html(diffString) {
69
69
  else {
70
70
  content = $lineWrapper.text().trim();
71
71
  }
72
- const fullLine = prefix + content;
72
+ let fullLine = prefix + content;
73
+ // Highlight search term if present
74
+ if (searchTerm && fullLine.toLowerCase().includes(searchTerm.toLowerCase())) {
75
+ const regex = new RegExp(`(${searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
76
+ // We need to know the base color to restore it.
77
+ // Added: 32, Deleted: 31, Normal: 37
78
+ const baseColor = isAdded ? '32' : (isDeleted ? '31' : '37');
79
+ fullLine = fullLine.replace(regex, `\x1b[43m\x1b[30m$1\x1b[0m\x1b[${baseColor}m`);
80
+ }
73
81
  if (isAdded) {
74
82
  blessedText += `\x1b[32m${fullLine}\x1b[0m\n`;
75
83
  }
@@ -85,7 +93,7 @@ function formatDiffWithDiff2Html(diffString) {
85
93
  blessedText += `\x1b[36m${hunkText}\x1b[0m\n`;
86
94
  }
87
95
  });
88
- return blessedText.trim() || 'No changes detected.';
96
+ return blessedText.trim();
89
97
  }
90
98
  catch (error) {
91
99
  return `Error formatting diff: ${error}`;
@@ -1 +1 @@
1
- {"version":3,"file":"diff-formatter.js","sourceRoot":"","sources":["../../src/utils/diff-formatter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"diff-formatter.js","sourceRoot":"","sources":["../../src/utils/diff-formatter.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  export interface FileStatus {
2
2
  path: string;
3
- status: 'modified' | 'added' | 'deleted' | 'unstaged' | 'unknown';
3
+ status: 'modified' | 'added' | 'deleted' | 'unstaged' | 'unknown' | 'unchanged';
4
4
  mtime?: Date;
5
5
  }
6
6
  export declare class GitHandler {
@@ -8,5 +8,7 @@ export declare class GitHandler {
8
8
  constructor(workingDir?: string);
9
9
  isRepo(): Promise<boolean>;
10
10
  getStatus(): Promise<FileStatus[]>;
11
+ searchFiles(term: string): Promise<FileStatus[]>;
11
12
  getDiff(filePath: string): Promise<string>;
13
+ getFileContent(filePath: string): Promise<string>;
12
14
  }
package/dist/utils/git.js CHANGED
@@ -74,6 +74,63 @@ class GitHandler {
74
74
  });
75
75
  });
76
76
  }
77
+ searchFiles(term) {
78
+ return __awaiter(this, void 0, void 0, function* () {
79
+ if (!term || !term.trim())
80
+ return [];
81
+ try {
82
+ // Use git grep to find files containing the term
83
+ // -i: ignore case
84
+ // -l: list filenames only
85
+ // -F: interpret pattern as a fixed string
86
+ // --untracked: include untracked files
87
+ const result = yield this.git.raw(['grep', '-i', '-l', '-F', '--untracked', term.trim()]);
88
+ const matchedPaths = [...new Set(result.split('\n').map(p => p.trim()).filter(p => p !== ''))];
89
+ if (matchedPaths.length === 0)
90
+ return [];
91
+ // Get current status to identify which matched files are changed
92
+ const changedFiles = yield this.getStatus();
93
+ const changedMap = new Map();
94
+ changedFiles.forEach(f => changedMap.set(f.path, f));
95
+ const finalResults = [];
96
+ for (const path of matchedPaths) {
97
+ if (changedMap.has(path)) {
98
+ finalResults.push(changedMap.get(path));
99
+ }
100
+ else {
101
+ // It's an unchanged file
102
+ let mtime = new Date(0);
103
+ try {
104
+ const stat = yield promises_1.default.stat(path);
105
+ mtime = stat.mtime;
106
+ }
107
+ catch (_a) { }
108
+ finalResults.push({
109
+ path,
110
+ status: 'unchanged',
111
+ mtime
112
+ });
113
+ }
114
+ }
115
+ // Sort results by mtime descending
116
+ return finalResults.sort((a, b) => {
117
+ const mtimeA = a.mtime || new Date(0);
118
+ const mtimeB = b.mtime || new Date(0);
119
+ const timeDiff = mtimeB.getTime() - mtimeA.getTime();
120
+ if (timeDiff !== 0)
121
+ return timeDiff;
122
+ return a.path.localeCompare(b.path);
123
+ });
124
+ }
125
+ catch (error) {
126
+ // git grep returns exit code 1 if no matches are found, which simple-git might throw as an error
127
+ if (error.message && (error.message.includes('exit code 1') || error.exitCode === 1)) {
128
+ return [];
129
+ }
130
+ throw error;
131
+ }
132
+ });
133
+ }
77
134
  getDiff(filePath) {
78
135
  return __awaiter(this, void 0, void 0, function* () {
79
136
  try {
@@ -84,13 +141,23 @@ class GitHandler {
84
141
  });
85
142
  }
86
143
  const diff = yield this.git.diff(['HEAD', '--', filePath]);
87
- return diff || 'No changes or file is new.';
144
+ return diff || '';
88
145
  }
89
146
  catch (error) {
90
147
  return `Error getting diff: ${error}`;
91
148
  }
92
149
  });
93
150
  }
151
+ getFileContent(filePath) {
152
+ return __awaiter(this, void 0, void 0, function* () {
153
+ try {
154
+ return yield promises_1.default.readFile(filePath, 'utf8');
155
+ }
156
+ catch (error) {
157
+ return `Error reading file: ${error}`;
158
+ }
159
+ });
160
+ }
94
161
  }
95
162
  exports.GitHandler = GitHandler;
96
163
  //# sourceMappingURL=git.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/utils/git.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/utils/git.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "diffwatch",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "An app for watching git repository file changes.",
5
5
  "author": "Sarfraz Ahmed <sarfraznawaz2005@gmail.com>",
6
6
  "main": "dist/index.js",
package/src/index.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
- const blessed = require('neo-neo-blessed');
3
- import chalk from 'chalk';
4
- import { spawn } from 'child_process';
5
- import { GitHandler, FileStatus } from './utils/git';
6
- import { formatDiffWithDiff2Html } from './utils/diff-formatter';
2
+ const blessed = require('neo-neo-blessed');
3
+ import chalk from 'chalk';
4
+ import { spawn } from 'child_process';
5
+ import { GitHandler, FileStatus } from './utils/git';
6
+ import { formatDiffWithDiff2Html } from './utils/diff-formatter';
7
7
 
8
8
  async function main() {
9
9
  const gitHandler = new GitHandler();
@@ -61,8 +61,31 @@ async function main() {
61
61
  tags: false,
62
62
  });
63
63
 
64
+ const searchBox = blessed.box({
65
+ top: 'center',
66
+ left: 'center',
67
+ width: '50%',
68
+ height: 3,
69
+ label: ' Search ',
70
+ border: { type: 'line' },
71
+ style: { border: { fg: 'yellow' } },
72
+ hidden: true,
73
+ });
74
+
75
+ const searchInput = blessed.textbox({
76
+ parent: searchBox,
77
+ top: 0,
78
+ left: 0,
79
+ width: '100%-2',
80
+ height: 1,
81
+ keys: true,
82
+ inputOnFocus: true,
83
+ style: { fg: 'white', bg: 'black' },
84
+ });
85
+
64
86
  screen.append(fileList);
65
87
  screen.append(diffView);
88
+ screen.append(searchBox);
66
89
 
67
90
  const updateBorders = () => {
68
91
  fileList.style.border.fg = screen.focused === fileList ? 'yellow' : 'white';
@@ -70,125 +93,188 @@ async function main() {
70
93
  screen.render();
71
94
  };
72
95
 
73
- let currentFiles: FileStatus[] = [];
74
- let lastSelectedPath: string | null = null;
75
- let diffUpdateTimeout: NodeJS.Timeout | null = null;
76
-
77
- const scheduleDiffUpdate = () => {
78
- if (diffUpdateTimeout) clearTimeout(diffUpdateTimeout);
79
- diffUpdateTimeout = setTimeout(async () => {
80
- await updateDiff();
81
- }, 150); // 150ms debounce
82
- };
83
-
84
- const openInEditor = (filePath: string) => {
85
- try {
86
- if (process.platform === 'win32') {
87
- // On Windows, use 'start' to open with default program
88
- spawn('cmd', ['/c', 'start', '', filePath], { stdio: 'ignore', detached: true }).unref();
89
- } else {
90
- // On Unix-like systems, try EDITOR, fallback to xdg-open
91
- const editor = process.env.EDITOR || process.env.VISUAL || 'xdg-open';
92
- spawn(editor, [filePath], { stdio: 'ignore', detached: true }).unref();
93
- }
94
- } catch (error) {
95
- console.error(`Failed to open ${filePath}: ${error}`);
96
- }
96
+ let currentFiles: FileStatus[] = [];
97
+ let lastSelectedPath: string | null = null;
98
+ let diffUpdateTimeout: NodeJS.Timeout | null = null;
99
+ let currentSearchTerm: string = '';
100
+
101
+ const scheduleDiffUpdate = () => {
102
+ if (diffUpdateTimeout) clearTimeout(diffUpdateTimeout);
103
+ diffUpdateTimeout = setTimeout(async () => {
104
+ await updateDiff();
105
+ }, 150); // 150ms debounce
106
+ };
107
+
108
+ const openInEditor = (filePath: string) => {
109
+ try {
110
+ if (process.platform === 'win32') {
111
+ // On Windows, use 'start' to open with default program
112
+ spawn('cmd', ['/c', 'start', '', filePath], { stdio: 'ignore', detached: true }).unref();
113
+ } else {
114
+ // On Unix-like systems, try EDITOR, fallback to xdg-open
115
+ const editor = process.env.EDITOR || process.env.VISUAL || 'xdg-open';
116
+ spawn(editor, [filePath], { stdio: 'ignore', detached: true }).unref();
117
+ }
118
+ } catch (error) {
119
+ console.error(`Failed to open ${filePath}: ${error}`);
120
+ }
121
+ };
122
+
123
+ const updateDiff = async () => {
124
+ const selectedIndex = fileList.selected;
125
+ const selectedFile = currentFiles[selectedIndex];
126
+ if (selectedFile) {
127
+ let content = '';
128
+ let label = ` Diff (${selectedFile.path}) `;
129
+
130
+ if (selectedFile.status !== 'unchanged' && selectedFile.status !== 'deleted') {
131
+ const diff = await gitHandler.getDiff(selectedFile.path);
132
+ content = formatDiffWithDiff2Html(diff, currentSearchTerm);
133
+ }
134
+
135
+ if (!content && selectedFile.status !== 'deleted') {
136
+ content = await gitHandler.getFileContent(selectedFile.path);
137
+ // Highlight search term in full content
138
+ if (currentSearchTerm) {
139
+ const regex = new RegExp(`(${currentSearchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
140
+ content = content.replace(regex, `\x1b[43m\x1b[30m$1\x1b[0m`);
141
+ }
142
+ label = ` File (${selectedFile.path}) `;
143
+ } else if (!content && selectedFile.status === 'deleted') {
144
+ content = chalk.red('File was deleted.');
145
+ label = ` Diff (${selectedFile.path}) `;
146
+ }
147
+
148
+ const currentContent = diffView.content;
149
+ const currentLabel = diffView.label;
150
+
151
+ // Only update if content or label changed to reduce flickering
152
+ if (content !== currentContent || label !== currentLabel) {
153
+ const savedScroll = diffView.scrollTop;
154
+ const isNewFile = selectedFile.path !== lastSelectedPath;
155
+
156
+ diffView.setContent(content);
157
+ diffView.setLabel(label);
158
+
159
+ if (isNewFile) {
160
+ diffView.scrollTo(0);
161
+ } else {
162
+ diffView.scrollTop = savedScroll;
163
+ }
164
+ }
165
+ lastSelectedPath = selectedFile.path;
166
+ } else {
167
+ const newContent = 'Select a file to view diff.';
168
+ const newLabel = ' Diff () ';
169
+ if (diffView.content !== newContent || diffView.label !== newLabel) {
170
+ diffView.setContent(newContent);
171
+ diffView.setLabel(newLabel);
172
+ diffView.scrollTo(0);
173
+ }
174
+ lastSelectedPath = null;
175
+ }
176
+ screen.render();
177
+ };
178
+
179
+ const updateFileList = async () => {
180
+ // Preserve selected file path and scroll positions
181
+ const selectedPath = currentFiles[fileList.selected]?.path;
182
+ const diffScroll = diffView.scrollTop;
183
+
184
+ let files: FileStatus[];
185
+
186
+ if (currentSearchTerm) {
187
+ files = await gitHandler.searchFiles(currentSearchTerm);
188
+ } else {
189
+ files = await gitHandler.getStatus();
190
+ }
191
+
192
+ currentFiles = files;
193
+
194
+ const items = files.map(f => {
195
+ let color = '{white-fg}';
196
+ if (f.status === 'added') color = '{green-fg}';
197
+ else if (f.status === 'deleted') color = '{red-fg}';
198
+ else if (f.status === 'modified') color = '{blue-fg}';
199
+ else if (f.status === 'unstaged') color = '{white-fg}';
200
+ else if (f.status === 'unchanged') color = '{grey-fg}';
201
+
202
+ return `${color}${f.path}{/}`;
203
+ });
204
+
205
+ fileList.setItems(items);
206
+
207
+ const labelTitle = currentSearchTerm ? ` Files (${files.length}) - Searching: "${currentSearchTerm}" ` : ` Files (${files.length}) `;
208
+ fileList.setLabel(labelTitle);
209
+
210
+ if (items.length > 0) {
211
+ // Restore selection by path if possible
212
+ const newSelectedIndex = selectedPath ? currentFiles.findIndex(f => f.path === selectedPath) : -1;
213
+ fileList.select(newSelectedIndex >= 0 ? newSelectedIndex : 0);
214
+ // Cancel any pending diff update and update immediately
215
+ if (diffUpdateTimeout) {
216
+ clearTimeout(diffUpdateTimeout);
217
+ diffUpdateTimeout = null;
218
+ }
219
+ await updateDiff();
220
+ } else {
221
+ diffView.setContent(currentSearchTerm ? `No files match "${currentSearchTerm}".` : 'No changes detected.');
222
+ diffView.setLabel(' Diff () ');
223
+ }
224
+
225
+ // Restore scroll positions if reasonably possible (reset if list changed drastically)
226
+ // Actually, if we filter, the scroll position might be invalid.
227
+ // Ideally we keep it 0 if it was 0 or just let the select() call handle scrolling to the item.
228
+ // The previous implementation blindly restored scrollTop.
229
+ // If the list shrunk, select() should have brought it into view.
230
+ // We only explicitly restore if items.length > 0
231
+ // But setting scroll to previous value might be wrong if the list is now shorter.
232
+ // Safe to only restore diffView scroll as it depends on content, fileList is handled by select.
233
+
234
+ diffView.scrollTop = diffScroll;
235
+
236
+ screen.render();
97
237
  };
98
238
 
99
- const updateDiff = async () => {
100
- const selectedIndex = fileList.selected;
101
- const selectedFile = currentFiles[selectedIndex];
102
- if (selectedFile) {
103
- const diff = await gitHandler.getDiff(selectedFile.path);
104
- const formattedDiff = formatDiffWithDiff2Html(diff);
105
- const newLabel = ` Diff (${selectedFile.path}) `;
106
- const currentContent = diffView.content;
107
- const currentLabel = diffView.label;
108
-
109
- // Only update if content or label changed to reduce flickering
110
- if (formattedDiff !== currentContent || newLabel !== currentLabel) {
111
- const savedScroll = diffView.scrollTop;
112
- const isNewFile = selectedFile.path !== lastSelectedPath;
113
-
114
- diffView.setContent(formattedDiff);
115
- diffView.setLabel(newLabel);
116
-
117
- if (isNewFile) {
118
- diffView.scrollTo(0);
119
- } else {
120
- diffView.scrollTop = savedScroll;
121
- }
122
- }
123
- lastSelectedPath = selectedFile.path;
124
- } else {
125
- const newContent = 'Select a file to view diff.';
126
- const newLabel = ' Diff () ';
127
- if (diffView.content !== newContent || diffView.label !== newLabel) {
128
- diffView.setContent(newContent);
129
- diffView.setLabel(newLabel);
130
- diffView.scrollTo(0);
131
- }
132
- lastSelectedPath = null;
133
- }
134
- screen.render();
135
- };
136
-
137
- const updateFileList = async () => {
138
- // Preserve selected file path and scroll positions
139
- const selectedPath = currentFiles[fileList.selected]?.path;
140
- const fileListScroll = fileList.scroll;
141
- const diffScroll = diffView.scrollTop;
142
-
143
- const files = await gitHandler.getStatus();
144
- currentFiles = files;
145
-
146
- const items = files.map(f => {
147
- let color = '{white-fg}';
148
- if (f.status === 'added') color = '{green-fg}';
149
- else if (f.status === 'deleted') color = '{red-fg}';
150
- else if (f.status === 'modified') color = '{blue-fg}';
151
- else if (f.status === 'unstaged') color = '{white-fg}';
152
-
153
- return `${color}${f.path}{/}`;
154
- });
155
-
156
- fileList.setItems(items);
157
- fileList.setLabel(` Files (${files.length}) `);
158
-
159
- if (items.length > 0) {
160
- // Restore selection by path if possible
161
- const newSelectedIndex = selectedPath ? currentFiles.findIndex(f => f.path === selectedPath) : -1;
162
- fileList.select(newSelectedIndex >= 0 ? newSelectedIndex : 0);
163
- // Cancel any pending diff update and update immediately
164
- if (diffUpdateTimeout) {
165
- clearTimeout(diffUpdateTimeout);
166
- diffUpdateTimeout = null;
167
- }
168
- await updateDiff();
169
- } else {
170
- diffView.setContent('No changes detected.');
171
- diffView.setLabel(' Diff () ');
172
- }
173
-
174
- // Restore scroll positions
175
- fileList.scroll = fileListScroll;
176
- diffView.scrollTop = diffScroll;
177
-
178
- screen.render();
179
- };
180
-
181
- fileList.on('select item', () => {
182
- scheduleDiffUpdate();
183
- });
184
-
185
- fileList.key(['up', 'down'], () => {
186
- scheduleDiffUpdate();
187
- });
239
+ fileList.on('select item', () => {
240
+ scheduleDiffUpdate();
241
+ });
242
+
243
+ fileList.key(['up', 'down'], () => {
244
+ scheduleDiffUpdate();
245
+ });
188
246
 
189
247
  screen.key(['escape', 'q', 'C-c'], () => {
190
- screen.destroy();
191
- process.exit(0);
248
+ if (!searchBox.hidden) {
249
+ searchBox.hide();
250
+ screen.render();
251
+ fileList.focus();
252
+ } else {
253
+ screen.destroy();
254
+ process.exit(0);
255
+ }
256
+ });
257
+
258
+ screen.key(['s'], () => {
259
+ searchBox.show();
260
+ searchBox.setFront();
261
+ searchInput.setValue(currentSearchTerm);
262
+ searchInput.focus();
263
+ screen.render();
264
+ });
265
+
266
+ searchInput.on('submit', async (value: string) => {
267
+ currentSearchTerm = (value || '').trim();
268
+ searchBox.hide();
269
+ fileList.focus();
270
+ // Force immediate update
271
+ await updateFileList();
272
+ });
273
+
274
+ searchInput.on('cancel', () => {
275
+ searchBox.hide();
276
+ fileList.focus();
277
+ screen.render();
192
278
  });
193
279
 
194
280
  screen.key(['tab'], () => {
@@ -200,23 +286,23 @@ async function main() {
200
286
  updateBorders();
201
287
  });
202
288
 
203
- screen.key(['left'], () => {
204
- fileList.focus();
205
- updateBorders();
206
- });
207
-
208
- screen.key(['right'], () => {
209
- diffView.focus();
210
- updateBorders();
211
- });
212
-
213
- screen.key(['enter'], () => {
214
- const selectedIndex = fileList.selected;
215
- const selectedFile = currentFiles[selectedIndex];
216
- if (selectedFile) {
217
- openInEditor(selectedFile.path);
218
- }
219
- });
289
+ screen.key(['left'], () => {
290
+ fileList.focus();
291
+ updateBorders();
292
+ });
293
+
294
+ screen.key(['right'], () => {
295
+ diffView.focus();
296
+ updateBorders();
297
+ });
298
+
299
+ screen.key(['enter'], () => {
300
+ const selectedIndex = fileList.selected;
301
+ const selectedFile = currentFiles[selectedIndex];
302
+ if (selectedFile) {
303
+ openInEditor(selectedFile.path);
304
+ }
305
+ });
220
306
 
221
307
  setInterval(async () => {
222
308
  await updateFileList();
@@ -1,9 +1,9 @@
1
1
  import * as cheerio from 'cheerio';
2
2
  const Diff2Html = require('diff2html');
3
3
 
4
- export function formatDiffWithDiff2Html(diffString: string): string {
4
+ export function formatDiffWithDiff2Html(diffString: string, searchTerm?: string): string {
5
5
  if (!diffString || diffString.trim() === '') {
6
- return 'No changes detected.';
6
+ return '';
7
7
  }
8
8
 
9
9
  try {
@@ -41,7 +41,16 @@ export function formatDiffWithDiff2Html(diffString: string): string {
41
41
  content = $lineWrapper.text().trim();
42
42
  }
43
43
 
44
- const fullLine = prefix + content;
44
+ let fullLine = prefix + content;
45
+
46
+ // Highlight search term if present
47
+ if (searchTerm && fullLine.toLowerCase().includes(searchTerm.toLowerCase())) {
48
+ const regex = new RegExp(`(${searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
49
+ // We need to know the base color to restore it.
50
+ // Added: 32, Deleted: 31, Normal: 37
51
+ const baseColor = isAdded ? '32' : (isDeleted ? '31' : '37');
52
+ fullLine = fullLine.replace(regex, `\x1b[43m\x1b[30m$1\x1b[0m\x1b[${baseColor}m`);
53
+ }
45
54
 
46
55
  if (isAdded) {
47
56
  blessedText += `\x1b[32m${fullLine}\x1b[0m\n`;
@@ -58,7 +67,7 @@ export function formatDiffWithDiff2Html(diffString: string): string {
58
67
  }
59
68
  });
60
69
 
61
- return blessedText.trim() || 'No changes detected.';
70
+ return blessedText.trim();
62
71
  } catch (error) {
63
72
  return `Error formatting diff: ${error}`;
64
73
  }
package/src/utils/git.ts CHANGED
@@ -3,7 +3,7 @@ import fs from 'fs/promises';
3
3
 
4
4
  export interface FileStatus {
5
5
  path: string;
6
- status: 'modified' | 'added' | 'deleted' | 'unstaged' | 'unknown';
6
+ status: 'modified' | 'added' | 'deleted' | 'unstaged' | 'unknown' | 'unchanged';
7
7
  mtime?: Date;
8
8
  }
9
9
 
@@ -73,6 +73,63 @@ export class GitHandler {
73
73
  });
74
74
  }
75
75
 
76
+ async searchFiles(term: string): Promise<FileStatus[]> {
77
+ if (!term || !term.trim()) return [];
78
+
79
+ try {
80
+ // Use git grep to find files containing the term
81
+ // -i: ignore case
82
+ // -l: list filenames only
83
+ // -F: interpret pattern as a fixed string
84
+ // --untracked: include untracked files
85
+ const result = await this.git.raw(['grep', '-i', '-l', '-F', '--untracked', term.trim()]);
86
+ const matchedPaths = [...new Set(result.split('\n').map(p => p.trim()).filter(p => p !== ''))];
87
+
88
+ if (matchedPaths.length === 0) return [];
89
+
90
+ // Get current status to identify which matched files are changed
91
+ const changedFiles = await this.getStatus();
92
+ const changedMap = new Map<string, FileStatus>();
93
+ changedFiles.forEach(f => changedMap.set(f.path, f));
94
+
95
+ const finalResults: FileStatus[] = [];
96
+
97
+ for (const path of matchedPaths) {
98
+ if (changedMap.has(path)) {
99
+ finalResults.push(changedMap.get(path)!);
100
+ } else {
101
+ // It's an unchanged file
102
+ let mtime = new Date(0);
103
+ try {
104
+ const stat = await fs.stat(path);
105
+ mtime = stat.mtime;
106
+ } catch {}
107
+
108
+ finalResults.push({
109
+ path,
110
+ status: 'unchanged',
111
+ mtime
112
+ });
113
+ }
114
+ }
115
+
116
+ // Sort results by mtime descending
117
+ return finalResults.sort((a, b) => {
118
+ const mtimeA = a.mtime || new Date(0);
119
+ const mtimeB = b.mtime || new Date(0);
120
+ const timeDiff = mtimeB.getTime() - mtimeA.getTime();
121
+ if (timeDiff !== 0) return timeDiff;
122
+ return a.path.localeCompare(b.path);
123
+ });
124
+ } catch (error: any) {
125
+ // git grep returns exit code 1 if no matches are found, which simple-git might throw as an error
126
+ if (error.message && (error.message.includes('exit code 1') || error.exitCode === 1)) {
127
+ return [];
128
+ }
129
+ throw error;
130
+ }
131
+ }
132
+
76
133
  async getDiff(filePath: string): Promise<string> {
77
134
  try {
78
135
  const isUntracked = (await this.git.status()).not_added.includes(filePath);
@@ -84,9 +141,17 @@ export class GitHandler {
84
141
  }
85
142
 
86
143
  const diff = await this.git.diff(['HEAD', '--', filePath]);
87
- return diff || 'No changes or file is new.';
144
+ return diff || '';
88
145
  } catch (error) {
89
146
  return `Error getting diff: ${error}`;
90
147
  }
91
148
  }
149
+
150
+ async getFileContent(filePath: string): Promise<string> {
151
+ try {
152
+ return await fs.readFile(filePath, 'utf8');
153
+ } catch (error) {
154
+ return `Error reading file: ${error}`;
155
+ }
156
+ }
92
157
  }
package/tests/git.test.ts CHANGED
@@ -3,11 +3,12 @@ jest.mock('simple-git', () => ({
3
3
  }));
4
4
  jest.mock('fs/promises', () => ({
5
5
  stat: jest.fn(),
6
+ readFile: jest.fn(),
6
7
  }));
7
8
 
8
9
  import { simpleGit } from 'simple-git';
9
10
  import fs from 'fs/promises';
10
- import { GitHandler } from '../src/utils/git';
11
+ import { GitHandler, FileStatus } from '../src/utils/git';
11
12
 
12
13
  describe('GitHandler', () => {
13
14
  let gitHandler: GitHandler;
@@ -22,6 +23,7 @@ describe('GitHandler', () => {
22
23
  };
23
24
  (simpleGit as jest.Mock).mockReturnValue(mockGit);
24
25
  (fs.stat as jest.Mock).mockResolvedValue({ mtime: new Date() });
26
+ (fs.readFile as jest.Mock).mockResolvedValue('');
25
27
  gitHandler = new GitHandler();
26
28
  });
27
29
 
@@ -62,6 +64,13 @@ describe('GitHandler', () => {
62
64
  expect(diff).toBe('+ added line\n- removed line');
63
65
  });
64
66
 
67
+ it('should return empty string if no diff', async () => {
68
+ mockGit.status.mockResolvedValue({ not_added: [] });
69
+ mockGit.diff.mockResolvedValue('');
70
+ const diff = await gitHandler.getDiff('file1.ts');
71
+ expect(diff).toBe('');
72
+ });
73
+
65
74
  it('should sort files by last modified descending then filename', async () => {
66
75
  // Mock fs.stat to return different mtimes
67
76
  const mockStat = jest.fn();
@@ -98,4 +107,42 @@ describe('GitHandler', () => {
98
107
  'a-file.ts', // oldest
99
108
  ]);
100
109
  });
110
+
111
+ it('should search files correctly', async () => {
112
+ mockGit.raw = jest.fn().mockResolvedValue('match.ts\nunchanged.ts');
113
+ mockGit.status.mockResolvedValue({
114
+ modified: ['match.ts'],
115
+ deleted: [],
116
+ created: [],
117
+ not_added: [],
118
+ renamed: [],
119
+ });
120
+
121
+ const results = await gitHandler.searchFiles('match');
122
+
123
+ expect(results).toHaveLength(2);
124
+ expect(results).toEqual(expect.arrayContaining([
125
+ expect.objectContaining({ path: 'match.ts', status: 'modified' }),
126
+ expect.objectContaining({ path: 'unchanged.ts', status: 'unchanged' }),
127
+ ]));
128
+ expect(mockGit.raw).toHaveBeenCalledWith(['grep', '-i', '-l', '-F', '--untracked', 'match']);
129
+ });
130
+
131
+ it('should deduplicate search results', async () => {
132
+ // Return duplicate paths
133
+ mockGit.raw = jest.fn().mockResolvedValue('match.ts\nmatch.ts\nother.ts\nother.ts');
134
+ mockGit.status.mockResolvedValue({
135
+ modified: [],
136
+ deleted: [],
137
+ created: [],
138
+ not_added: [],
139
+ renamed: [],
140
+ });
141
+
142
+ const results = await gitHandler.searchFiles('term');
143
+
144
+ expect(results).toHaveLength(2);
145
+ const paths = results.map(r => r.path).sort();
146
+ expect(paths).toEqual(['match.ts', 'other.ts']);
147
+ });
101
148
  });