diffwatch 1.0.1 → 1.0.2

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,29 @@ 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') {
135
+ content = yield gitHandler.getFileContent(selectedFile.path);
136
+ // Highlight search term in full content
137
+ if (currentSearchTerm) {
138
+ const regex = new RegExp(`(${currentSearchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
139
+ content = content.replace(regex, `\x1b[43m\x1b[30m$1\x1b[0m`);
140
+ }
141
+ label = ` File (${selectedFile.path}) `;
142
+ }
143
+ else {
144
+ const diff = yield gitHandler.getDiff(selectedFile.path);
145
+ content = (0, diff_formatter_1.formatDiffWithDiff2Html)(diff, currentSearchTerm);
146
+ }
113
147
  const currentContent = diffView.content;
114
148
  const currentLabel = diffView.label;
115
149
  // Only update if content or label changed to reduce flickering
116
- if (formattedDiff !== currentContent || newLabel !== currentLabel) {
150
+ if (content !== currentContent || label !== currentLabel) {
117
151
  const savedScroll = diffView.scrollTop;
118
152
  const isNewFile = selectedFile.path !== lastSelectedPath;
119
- diffView.setContent(formattedDiff);
120
- diffView.setLabel(newLabel);
153
+ diffView.setContent(content);
154
+ diffView.setLabel(label);
121
155
  if (isNewFile) {
122
156
  diffView.scrollTo(0);
123
157
  }
@@ -143,9 +177,14 @@ function main() {
143
177
  var _a;
144
178
  // Preserve selected file path and scroll positions
145
179
  const selectedPath = (_a = currentFiles[fileList.selected]) === null || _a === void 0 ? void 0 : _a.path;
146
- const fileListScroll = fileList.scroll;
147
180
  const diffScroll = diffView.scrollTop;
148
- const files = yield gitHandler.getStatus();
181
+ let files;
182
+ if (currentSearchTerm) {
183
+ files = yield gitHandler.searchFiles(currentSearchTerm);
184
+ }
185
+ else {
186
+ files = yield gitHandler.getStatus();
187
+ }
149
188
  currentFiles = files;
150
189
  const items = files.map(f => {
151
190
  let color = '{white-fg}';
@@ -157,10 +196,13 @@ function main() {
157
196
  color = '{blue-fg}';
158
197
  else if (f.status === 'unstaged')
159
198
  color = '{white-fg}';
199
+ else if (f.status === 'unchanged')
200
+ color = '{grey-fg}';
160
201
  return `${color}${f.path}{/}`;
161
202
  });
162
203
  fileList.setItems(items);
163
- fileList.setLabel(` Files (${files.length}) `);
204
+ const labelTitle = currentSearchTerm ? ` Files (${files.length}) - Searching: "${currentSearchTerm}" ` : ` Files (${files.length}) `;
205
+ fileList.setLabel(labelTitle);
164
206
  if (items.length > 0) {
165
207
  // Restore selection by path if possible
166
208
  const newSelectedIndex = selectedPath ? currentFiles.findIndex(f => f.path === selectedPath) : -1;
@@ -173,11 +215,17 @@ function main() {
173
215
  yield updateDiff();
174
216
  }
175
217
  else {
176
- diffView.setContent('No changes detected.');
218
+ diffView.setContent(currentSearchTerm ? `No files match "${currentSearchTerm}".` : 'No changes detected.');
177
219
  diffView.setLabel(' Diff () ');
178
220
  }
179
- // Restore scroll positions
180
- fileList.scroll = fileListScroll;
221
+ // Restore scroll positions if reasonably possible (reset if list changed drastically)
222
+ // Actually, if we filter, the scroll position might be invalid.
223
+ // Ideally we keep it 0 if it was 0 or just let the select() call handle scrolling to the item.
224
+ // The previous implementation blindly restored scrollTop.
225
+ // If the list shrunk, select() should have brought it into view.
226
+ // We only explicitly restore if items.length > 0
227
+ // But setting scroll to previous value might be wrong if the list is now shorter.
228
+ // Safe to only restore diffView scroll as it depends on content, fileList is handled by select.
181
229
  diffView.scrollTop = diffScroll;
182
230
  screen.render();
183
231
  });
@@ -188,8 +236,34 @@ function main() {
188
236
  scheduleDiffUpdate();
189
237
  });
190
238
  screen.key(['escape', 'q', 'C-c'], () => {
191
- screen.destroy();
192
- process.exit(0);
239
+ if (!searchBox.hidden) {
240
+ searchBox.hide();
241
+ screen.render();
242
+ fileList.focus();
243
+ }
244
+ else {
245
+ screen.destroy();
246
+ process.exit(0);
247
+ }
248
+ });
249
+ screen.key(['s'], () => {
250
+ searchBox.show();
251
+ searchBox.setFront();
252
+ searchInput.setValue(currentSearchTerm);
253
+ searchInput.focus();
254
+ screen.render();
255
+ });
256
+ searchInput.on('submit', (value) => __awaiter(this, void 0, void 0, function* () {
257
+ currentSearchTerm = (value || '').trim();
258
+ searchBox.hide();
259
+ fileList.focus();
260
+ // Force immediate update
261
+ yield updateFileList();
262
+ }));
263
+ searchInput.on('cancel', () => {
264
+ searchBox.hide();
265
+ fileList.focus();
266
+ screen.render();
193
267
  });
194
268
  screen.key(['tab'], () => {
195
269
  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,7 +36,7 @@ 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
41
  return 'No changes detected.';
42
42
  }
@@ -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
  }
@@ -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 {
@@ -91,6 +148,16 @@ class GitHandler {
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.2",
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,183 @@ 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') {
131
+ content = await gitHandler.getFileContent(selectedFile.path);
132
+ // Highlight search term in full content
133
+ if (currentSearchTerm) {
134
+ const regex = new RegExp(`(${currentSearchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
135
+ content = content.replace(regex, `\x1b[43m\x1b[30m$1\x1b[0m`);
136
+ }
137
+ label = ` File (${selectedFile.path}) `;
138
+ } else {
139
+ const diff = await gitHandler.getDiff(selectedFile.path);
140
+ content = formatDiffWithDiff2Html(diff, currentSearchTerm);
141
+ }
142
+
143
+ const currentContent = diffView.content;
144
+ const currentLabel = diffView.label;
145
+
146
+ // Only update if content or label changed to reduce flickering
147
+ if (content !== currentContent || label !== currentLabel) {
148
+ const savedScroll = diffView.scrollTop;
149
+ const isNewFile = selectedFile.path !== lastSelectedPath;
150
+
151
+ diffView.setContent(content);
152
+ diffView.setLabel(label);
153
+
154
+ if (isNewFile) {
155
+ diffView.scrollTo(0);
156
+ } else {
157
+ diffView.scrollTop = savedScroll;
158
+ }
159
+ }
160
+ lastSelectedPath = selectedFile.path;
161
+ } else {
162
+ const newContent = 'Select a file to view diff.';
163
+ const newLabel = ' Diff () ';
164
+ if (diffView.content !== newContent || diffView.label !== newLabel) {
165
+ diffView.setContent(newContent);
166
+ diffView.setLabel(newLabel);
167
+ diffView.scrollTo(0);
168
+ }
169
+ lastSelectedPath = null;
170
+ }
171
+ screen.render();
97
172
  };
98
173
 
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
- });
174
+ const updateFileList = async () => {
175
+ // Preserve selected file path and scroll positions
176
+ const selectedPath = currentFiles[fileList.selected]?.path;
177
+ const diffScroll = diffView.scrollTop;
178
+
179
+ let files: FileStatus[];
180
+
181
+ if (currentSearchTerm) {
182
+ files = await gitHandler.searchFiles(currentSearchTerm);
183
+ } else {
184
+ files = await gitHandler.getStatus();
185
+ }
186
+
187
+ currentFiles = files;
188
+
189
+ const items = files.map(f => {
190
+ let color = '{white-fg}';
191
+ if (f.status === 'added') color = '{green-fg}';
192
+ else if (f.status === 'deleted') color = '{red-fg}';
193
+ else if (f.status === 'modified') color = '{blue-fg}';
194
+ else if (f.status === 'unstaged') color = '{white-fg}';
195
+ else if (f.status === 'unchanged') color = '{grey-fg}';
196
+
197
+ return `${color}${f.path}{/}`;
198
+ });
199
+
200
+ fileList.setItems(items);
201
+
202
+ const labelTitle = currentSearchTerm ? ` Files (${files.length}) - Searching: "${currentSearchTerm}" ` : ` Files (${files.length}) `;
203
+ fileList.setLabel(labelTitle);
204
+
205
+ if (items.length > 0) {
206
+ // Restore selection by path if possible
207
+ const newSelectedIndex = selectedPath ? currentFiles.findIndex(f => f.path === selectedPath) : -1;
208
+ fileList.select(newSelectedIndex >= 0 ? newSelectedIndex : 0);
209
+ // Cancel any pending diff update and update immediately
210
+ if (diffUpdateTimeout) {
211
+ clearTimeout(diffUpdateTimeout);
212
+ diffUpdateTimeout = null;
213
+ }
214
+ await updateDiff();
215
+ } else {
216
+ diffView.setContent(currentSearchTerm ? `No files match "${currentSearchTerm}".` : 'No changes detected.');
217
+ diffView.setLabel(' Diff () ');
218
+ }
219
+
220
+ // Restore scroll positions if reasonably possible (reset if list changed drastically)
221
+ // Actually, if we filter, the scroll position might be invalid.
222
+ // Ideally we keep it 0 if it was 0 or just let the select() call handle scrolling to the item.
223
+ // The previous implementation blindly restored scrollTop.
224
+ // If the list shrunk, select() should have brought it into view.
225
+ // We only explicitly restore if items.length > 0
226
+ // But setting scroll to previous value might be wrong if the list is now shorter.
227
+ // Safe to only restore diffView scroll as it depends on content, fileList is handled by select.
228
+
229
+ diffView.scrollTop = diffScroll;
230
+
231
+ screen.render();
232
+ };
233
+
234
+ fileList.on('select item', () => {
235
+ scheduleDiffUpdate();
236
+ });
237
+
238
+ fileList.key(['up', 'down'], () => {
239
+ scheduleDiffUpdate();
240
+ });
188
241
 
189
242
  screen.key(['escape', 'q', 'C-c'], () => {
190
- screen.destroy();
191
- process.exit(0);
243
+ if (!searchBox.hidden) {
244
+ searchBox.hide();
245
+ screen.render();
246
+ fileList.focus();
247
+ } else {
248
+ screen.destroy();
249
+ process.exit(0);
250
+ }
251
+ });
252
+
253
+ screen.key(['s'], () => {
254
+ searchBox.show();
255
+ searchBox.setFront();
256
+ searchInput.setValue(currentSearchTerm);
257
+ searchInput.focus();
258
+ screen.render();
259
+ });
260
+
261
+ searchInput.on('submit', async (value: string) => {
262
+ currentSearchTerm = (value || '').trim();
263
+ searchBox.hide();
264
+ fileList.focus();
265
+ // Force immediate update
266
+ await updateFileList();
267
+ });
268
+
269
+ searchInput.on('cancel', () => {
270
+ searchBox.hide();
271
+ fileList.focus();
272
+ screen.render();
192
273
  });
193
274
 
194
275
  screen.key(['tab'], () => {
@@ -200,23 +281,23 @@ async function main() {
200
281
  updateBorders();
201
282
  });
202
283
 
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
- });
284
+ screen.key(['left'], () => {
285
+ fileList.focus();
286
+ updateBorders();
287
+ });
288
+
289
+ screen.key(['right'], () => {
290
+ diffView.focus();
291
+ updateBorders();
292
+ });
293
+
294
+ screen.key(['enter'], () => {
295
+ const selectedIndex = fileList.selected;
296
+ const selectedFile = currentFiles[selectedIndex];
297
+ if (selectedFile) {
298
+ openInEditor(selectedFile.path);
299
+ }
300
+ });
220
301
 
221
302
  setInterval(async () => {
222
303
  await updateFileList();
@@ -1,7 +1,7 @@
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
6
  return 'No changes detected.';
7
7
  }
@@ -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`;
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);
@@ -89,4 +146,12 @@ export class GitHandler {
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
 
@@ -98,4 +100,42 @@ describe('GitHandler', () => {
98
100
  'a-file.ts', // oldest
99
101
  ]);
100
102
  });
103
+
104
+ it('should search files correctly', async () => {
105
+ mockGit.raw = jest.fn().mockResolvedValue('match.ts\nunchanged.ts');
106
+ mockGit.status.mockResolvedValue({
107
+ modified: ['match.ts'],
108
+ deleted: [],
109
+ created: [],
110
+ not_added: [],
111
+ renamed: [],
112
+ });
113
+
114
+ const results = await gitHandler.searchFiles('match');
115
+
116
+ expect(results).toHaveLength(2);
117
+ expect(results).toEqual(expect.arrayContaining([
118
+ expect.objectContaining({ path: 'match.ts', status: 'modified' }),
119
+ expect.objectContaining({ path: 'unchanged.ts', status: 'unchanged' }),
120
+ ]));
121
+ expect(mockGit.raw).toHaveBeenCalledWith(['grep', '-i', '-l', '-F', '--untracked', 'match']);
122
+ });
123
+
124
+ it('should deduplicate search results', async () => {
125
+ // Return duplicate paths
126
+ mockGit.raw = jest.fn().mockResolvedValue('match.ts\nmatch.ts\nother.ts\nother.ts');
127
+ mockGit.status.mockResolvedValue({
128
+ modified: [],
129
+ deleted: [],
130
+ created: [],
131
+ not_added: [],
132
+ renamed: [],
133
+ });
134
+
135
+ const results = await gitHandler.searchFiles('term');
136
+
137
+ expect(results).toHaveLength(2);
138
+ const paths = results.map(r => r.path).sort();
139
+ expect(paths).toEqual(['match.ts', 'other.ts']);
140
+ });
101
141
  });