honeytree 1.2.0 → 1.2.1
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 +42 -52
- package/bin/honeydew.js +72 -9
- package/package.json +6 -3
- package/src/ascii-tree.js +21 -0
- package/src/auth.js +83 -0
- package/src/components/ForestApp.js +404 -0
- package/src/components/ForestScene.js +26 -0
- package/src/components/LiveTree.js +14 -0
- package/src/components/StatsBar.js +70 -0
- package/src/components/TreeInfoPopup.js +28 -0
- package/src/components/UnlockCelebration.js +40 -0
- package/src/growth.js +32 -0
- package/src/init.js +48 -2
- package/src/plant-real.js +129 -0
- package/src/plant.js +39 -17
- package/src/renderer.js +85 -12
- package/src/rewards.js +119 -0
- package/src/session.js +34 -0
- package/src/sprites.js +70 -5
- package/src/stdin.js +28 -0
- package/src/sync.js +44 -0
- package/src/transcript.js +62 -0
- package/src/turn.js +54 -0
- package/src/varieties.js +30 -0
- package/src/viewer2d.js +24 -0
- package/src/camera.js +0 -54
- package/src/diffactions.js +0 -32
- package/src/diffpanel.js +0 -83
- package/src/diffparser.js +0 -44
- package/src/diffwatch.js +0 -52
- package/src/markdown.js +0 -77
- package/src/pointcloud.js +0 -193
- package/src/renderer3d.js +0 -135
- package/src/scanner.js +0 -102
- package/src/viewer.js +0 -461
package/src/viewer.js
DELETED
|
@@ -1,461 +0,0 @@
|
|
|
1
|
-
import chalk from "chalk";
|
|
2
|
-
import { scanCodebase } from "./scanner.js";
|
|
3
|
-
import { generateForestCloud, generateGroundPlane } from "./pointcloud.js";
|
|
4
|
-
import { createCamera, rotatePoint, projectPoint, clampElevation, clampAzimuth } from "./camera.js";
|
|
5
|
-
import { createFrameBuffer, rasterize, renderBufferToString, renderTopBar, renderStatusBar } from "./renderer3d.js";
|
|
6
|
-
import { getChangedFiles, getFileDiff, watchForChanges } from "./diffwatch.js";
|
|
7
|
-
import { parseDiff } from "./diffparser.js";
|
|
8
|
-
import { createDiffPanel, renderDiffPanel } from "./diffpanel.js";
|
|
9
|
-
import { stageHunk, revertHunk } from "./diffactions.js";
|
|
10
|
-
|
|
11
|
-
export function createForestWatcher(filePath, onChange) {
|
|
12
|
-
try {
|
|
13
|
-
const stat = fs.statSync(filePath);
|
|
14
|
-
if (!stat.isFile()) return null;
|
|
15
|
-
|
|
16
|
-
const watcher = fs.watch(filePath, onChange);
|
|
17
|
-
watcher.on("error", () => {
|
|
18
|
-
try { watcher.close(); } catch {}
|
|
19
|
-
});
|
|
20
|
-
return watcher;
|
|
21
|
-
} catch {
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function writeAnsi(code) {
|
|
27
|
-
process.stdout.write(code);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function clearScreen() {
|
|
31
|
-
writeAnsi("\x1b[2J\x1b[H");
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function hideCursor() {
|
|
35
|
-
writeAnsi("\x1b[?25l");
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function showCursor() {
|
|
39
|
-
writeAnsi("\x1b[?25h");
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function moveHome() {
|
|
43
|
-
writeAnsi("\x1b[H");
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function enableMouse() {
|
|
47
|
-
writeAnsi("\x1b[?1000h");
|
|
48
|
-
writeAnsi("\x1b[?1002h");
|
|
49
|
-
writeAnsi("\x1b[?1006h");
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function disableMouse() {
|
|
53
|
-
writeAnsi("\x1b[?1006l");
|
|
54
|
-
writeAnsi("\x1b[?1002l");
|
|
55
|
-
writeAnsi("\x1b[?1000l");
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function enableMouseMotion() {
|
|
59
|
-
writeAnsi("\x1b[?1003h");
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function disableMouseMotion() {
|
|
63
|
-
writeAnsi("\x1b[?1003l");
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export async function viewer(targetDir) {
|
|
67
|
-
const dir = targetDir || process.cwd();
|
|
68
|
-
|
|
69
|
-
process.stdout.write("Scanning codebase...\n");
|
|
70
|
-
const files = scanCodebase(dir);
|
|
71
|
-
|
|
72
|
-
if (files.length === 0) {
|
|
73
|
-
console.error("No source files found in", dir);
|
|
74
|
-
process.exit(1);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const { points: treePoints, filePaths } = generateForestCloud(files);
|
|
78
|
-
const groundRadius = Math.max(25, Math.sqrt(files.length) * 7);
|
|
79
|
-
const groundPoints = generateGroundPlane(groundRadius);
|
|
80
|
-
const allPoints = [...treePoints, ...groundPoints];
|
|
81
|
-
|
|
82
|
-
const camera = createCamera();
|
|
83
|
-
let mouseDown = false;
|
|
84
|
-
let lastMouseX = 0;
|
|
85
|
-
let lastMouseY = 0;
|
|
86
|
-
let hoveredFile = "";
|
|
87
|
-
let hoveredModified = false;
|
|
88
|
-
let needsRedraw = true;
|
|
89
|
-
|
|
90
|
-
let changedFiles = new Set();
|
|
91
|
-
let diffPanel = null;
|
|
92
|
-
let diffMode = false;
|
|
93
|
-
let polling = false;
|
|
94
|
-
let watcher = null;
|
|
95
|
-
|
|
96
|
-
let screenWidth = process.stdout.columns || 80;
|
|
97
|
-
let screenHeight = (process.stdout.rows || 24) - 2;
|
|
98
|
-
|
|
99
|
-
function renderFrame() {
|
|
100
|
-
screenWidth = process.stdout.columns || 80;
|
|
101
|
-
screenHeight = (process.stdout.rows || 24) - 2;
|
|
102
|
-
|
|
103
|
-
const forestWidth = diffPanel ? Math.floor(screenWidth * 0.4) : screenWidth;
|
|
104
|
-
const buf = createFrameBuffer(forestWidth, screenHeight);
|
|
105
|
-
|
|
106
|
-
const projected = [];
|
|
107
|
-
for (const p of allPoints) {
|
|
108
|
-
const [rx, ry, rz] = rotatePoint(p.x, p.y, p.z, camera.azimuth, camera.elevation);
|
|
109
|
-
const proj = projectPoint(rx, ry, rz, forestWidth, screenHeight, camera.distance);
|
|
110
|
-
if (proj.visible) {
|
|
111
|
-
projected.push({
|
|
112
|
-
...proj,
|
|
113
|
-
color: p.color,
|
|
114
|
-
fileIndex: p.fileIndex,
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
rasterize(buf, projected);
|
|
120
|
-
|
|
121
|
-
const changedIndices = diffMode
|
|
122
|
-
? new Set(files.map((f, i) => f.changed ? i : -1).filter(i => i >= 0))
|
|
123
|
-
: null;
|
|
124
|
-
|
|
125
|
-
moveHome();
|
|
126
|
-
process.stdout.write(renderTopBar(hoveredFile, screenWidth, hoveredModified));
|
|
127
|
-
process.stdout.write("\n");
|
|
128
|
-
|
|
129
|
-
const forestLines = renderBufferToString(buf, undefined, changedIndices).split("\n");
|
|
130
|
-
|
|
131
|
-
if (diffPanel) {
|
|
132
|
-
const panelWidth = screenWidth - forestWidth - 1;
|
|
133
|
-
const panelLines = renderDiffPanel(diffPanel, panelWidth, screenHeight);
|
|
134
|
-
const border = chalk.hex("#555555")("│");
|
|
135
|
-
|
|
136
|
-
for (let y = 0; y < screenHeight; y++) {
|
|
137
|
-
const fLine = forestLines[y] || "";
|
|
138
|
-
const pLine = panelLines[y] || "";
|
|
139
|
-
process.stdout.write(fLine + border + pLine);
|
|
140
|
-
if (y < screenHeight - 1) process.stdout.write("\n");
|
|
141
|
-
}
|
|
142
|
-
} else {
|
|
143
|
-
process.stdout.write(forestLines.join("\n"));
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
process.stdout.write("\n");
|
|
147
|
-
|
|
148
|
-
const changedCount = changedFiles.size;
|
|
149
|
-
const changeText = changedCount > 0 ? `${changedCount} files changed | ` : "";
|
|
150
|
-
process.stdout.write(renderStatusBar(files.length, screenWidth, changeText));
|
|
151
|
-
|
|
152
|
-
return buf;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function regeneratePoints() {
|
|
156
|
-
const { points: newTreePoints, filePaths: newPaths } = generateForestCloud(files);
|
|
157
|
-
const newGround = generateGroundPlane(groundRadius);
|
|
158
|
-
allPoints.length = 0;
|
|
159
|
-
allPoints.push(...newTreePoints, ...newGround);
|
|
160
|
-
filePaths.length = 0;
|
|
161
|
-
filePaths.push(...newPaths);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
async function pollChanges() {
|
|
165
|
-
if (polling) return;
|
|
166
|
-
polling = true;
|
|
167
|
-
try {
|
|
168
|
-
const newChanged = await getChangedFiles(dir);
|
|
169
|
-
const changed = newChanged.size !== changedFiles.size ||
|
|
170
|
-
[...newChanged].some(f => !changedFiles.has(f));
|
|
171
|
-
changedFiles = newChanged;
|
|
172
|
-
|
|
173
|
-
for (const file of files) {
|
|
174
|
-
file.changed = changedFiles.has(file.relativePath);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
if (changed) {
|
|
178
|
-
regeneratePoints();
|
|
179
|
-
needsRedraw = true;
|
|
180
|
-
redraw();
|
|
181
|
-
}
|
|
182
|
-
} catch {
|
|
183
|
-
// ignore poll errors
|
|
184
|
-
}
|
|
185
|
-
polling = false;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
function checkAllResolved() {
|
|
189
|
-
if (!diffPanel) return;
|
|
190
|
-
const allDone = diffPanel.hunkStatus.every(s => s !== "pending");
|
|
191
|
-
if (!allDone) return;
|
|
192
|
-
|
|
193
|
-
for (let i = 0; i < diffPanel.hunks.length; i++) {
|
|
194
|
-
if (diffPanel.hunkStatus[i] === "accepted") {
|
|
195
|
-
stageHunk(dir, diffPanel.filePath, diffPanel.hunks[i]);
|
|
196
|
-
} else if (diffPanel.hunkStatus[i] === "rejected") {
|
|
197
|
-
revertHunk(dir, diffPanel.filePath, diffPanel.hunks[i]);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
diffPanel = null;
|
|
202
|
-
clearScreen();
|
|
203
|
-
pollChanges();
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
hideCursor();
|
|
207
|
-
clearScreen();
|
|
208
|
-
enableMouse();
|
|
209
|
-
enableMouseMotion();
|
|
210
|
-
|
|
211
|
-
// Render first frame before starting change detection
|
|
212
|
-
let currentBuf = renderFrame();
|
|
213
|
-
needsRedraw = false;
|
|
214
|
-
|
|
215
|
-
// Start async change detection after first render
|
|
216
|
-
pollChanges();
|
|
217
|
-
|
|
218
|
-
// Watch filesystem for instant change detection
|
|
219
|
-
watcher = watchForChanges(dir, () => pollChanges());
|
|
220
|
-
|
|
221
|
-
// Fallback poll every 3 seconds in case fs.watch misses events
|
|
222
|
-
const pollTimer = setInterval(() => pollChanges(), 3000);
|
|
223
|
-
|
|
224
|
-
function redraw() {
|
|
225
|
-
if (!needsRedraw) return;
|
|
226
|
-
needsRedraw = false;
|
|
227
|
-
try {
|
|
228
|
-
currentBuf = renderFrame();
|
|
229
|
-
} catch {
|
|
230
|
-
// swallow render errors to prevent terminal corruption
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
function parseMouseEvent(data) {
|
|
235
|
-
const str = data.toString();
|
|
236
|
-
const match = str.match(/\x1b\[<(\d+);(\d+);(\d+)([Mm])/);
|
|
237
|
-
if (!match) return null;
|
|
238
|
-
|
|
239
|
-
const button = parseInt(match[1]);
|
|
240
|
-
const x = parseInt(match[2]) - 1;
|
|
241
|
-
const y = parseInt(match[3]) - 1;
|
|
242
|
-
const released = match[4] === "m";
|
|
243
|
-
|
|
244
|
-
return { button, x, y, released };
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
const cleanup = () => {
|
|
248
|
-
clearInterval(pollTimer);
|
|
249
|
-
if (watcher) watcher.close();
|
|
250
|
-
disableMouseMotion();
|
|
251
|
-
disableMouse();
|
|
252
|
-
showCursor();
|
|
253
|
-
clearScreen();
|
|
254
|
-
console.log(`Scanned ${files.length} files in ${dir}`);
|
|
255
|
-
process.exit(0);
|
|
256
|
-
};
|
|
257
|
-
|
|
258
|
-
process.on("SIGINT", cleanup);
|
|
259
|
-
process.on("SIGTERM", cleanup);
|
|
260
|
-
process.stdout.on("resize", () => {
|
|
261
|
-
needsRedraw = true;
|
|
262
|
-
clearScreen();
|
|
263
|
-
redraw();
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
if (process.stdin.isTTY) {
|
|
267
|
-
process.stdin.setRawMode(true);
|
|
268
|
-
process.stdin.resume();
|
|
269
|
-
process.stdin.on("data", (data) => {
|
|
270
|
-
const key = data.toString();
|
|
271
|
-
|
|
272
|
-
if (key === "\x03" || key === "q") {
|
|
273
|
-
cleanup();
|
|
274
|
-
return;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
// Panel keybindings (when panel is open)
|
|
278
|
-
if (diffPanel) {
|
|
279
|
-
if (key === "\x1b" || key === "\x1b\x1b") {
|
|
280
|
-
diffPanel = null;
|
|
281
|
-
needsRedraw = true;
|
|
282
|
-
clearScreen();
|
|
283
|
-
redraw();
|
|
284
|
-
return;
|
|
285
|
-
}
|
|
286
|
-
if (key === "j" || key === "\x1b[B") {
|
|
287
|
-
diffPanel.currentHunk = Math.min(diffPanel.currentHunk + 1, diffPanel.hunks.length - 1);
|
|
288
|
-
needsRedraw = true;
|
|
289
|
-
redraw();
|
|
290
|
-
return;
|
|
291
|
-
}
|
|
292
|
-
if (key === "k" || key === "\x1b[A") {
|
|
293
|
-
diffPanel.currentHunk = Math.max(diffPanel.currentHunk - 1, 0);
|
|
294
|
-
needsRedraw = true;
|
|
295
|
-
redraw();
|
|
296
|
-
return;
|
|
297
|
-
}
|
|
298
|
-
if (key === "a") {
|
|
299
|
-
diffPanel.hunkStatus[diffPanel.currentHunk] = "accepted";
|
|
300
|
-
const next = diffPanel.hunkStatus.findIndex((s, i) => i > diffPanel.currentHunk && s === "pending");
|
|
301
|
-
if (next >= 0) diffPanel.currentHunk = next;
|
|
302
|
-
checkAllResolved();
|
|
303
|
-
needsRedraw = true;
|
|
304
|
-
redraw();
|
|
305
|
-
return;
|
|
306
|
-
}
|
|
307
|
-
if (key === "r") {
|
|
308
|
-
diffPanel.hunkStatus[diffPanel.currentHunk] = "rejected";
|
|
309
|
-
const next = diffPanel.hunkStatus.findIndex((s, i) => i > diffPanel.currentHunk && s === "pending");
|
|
310
|
-
if (next >= 0) diffPanel.currentHunk = next;
|
|
311
|
-
checkAllResolved();
|
|
312
|
-
needsRedraw = true;
|
|
313
|
-
redraw();
|
|
314
|
-
return;
|
|
315
|
-
}
|
|
316
|
-
if (key === "A") {
|
|
317
|
-
for (let i = 0; i < diffPanel.hunkStatus.length; i++) {
|
|
318
|
-
if (diffPanel.hunkStatus[i] === "pending") diffPanel.hunkStatus[i] = "accepted";
|
|
319
|
-
}
|
|
320
|
-
checkAllResolved();
|
|
321
|
-
needsRedraw = true;
|
|
322
|
-
redraw();
|
|
323
|
-
return;
|
|
324
|
-
}
|
|
325
|
-
if (key === "R") {
|
|
326
|
-
for (let i = 0; i < diffPanel.hunkStatus.length; i++) {
|
|
327
|
-
if (diffPanel.hunkStatus[i] === "pending") diffPanel.hunkStatus[i] = "rejected";
|
|
328
|
-
}
|
|
329
|
-
checkAllResolved();
|
|
330
|
-
needsRedraw = true;
|
|
331
|
-
redraw();
|
|
332
|
-
return;
|
|
333
|
-
}
|
|
334
|
-
return;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
if (key === "d") {
|
|
338
|
-
diffMode = !diffMode;
|
|
339
|
-
needsRedraw = true;
|
|
340
|
-
redraw();
|
|
341
|
-
return;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
if (key === "r") {
|
|
345
|
-
process.stdout.write("\x1b[H");
|
|
346
|
-
process.stdout.write("Rescanning...");
|
|
347
|
-
const newFiles = scanCodebase(dir);
|
|
348
|
-
files.length = 0;
|
|
349
|
-
files.push(...newFiles);
|
|
350
|
-
regeneratePoints();
|
|
351
|
-
needsRedraw = true;
|
|
352
|
-
clearScreen();
|
|
353
|
-
redraw();
|
|
354
|
-
return;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
if (key === "\x1b[D") {
|
|
358
|
-
camera.azimuth = clampAzimuth(camera.azimuth - 5);
|
|
359
|
-
needsRedraw = true;
|
|
360
|
-
redraw();
|
|
361
|
-
return;
|
|
362
|
-
}
|
|
363
|
-
if (key === "\x1b[C") {
|
|
364
|
-
camera.azimuth = clampAzimuth(camera.azimuth + 5);
|
|
365
|
-
needsRedraw = true;
|
|
366
|
-
redraw();
|
|
367
|
-
return;
|
|
368
|
-
}
|
|
369
|
-
if (key === "\x1b[A") {
|
|
370
|
-
camera.elevation = clampElevation(camera.elevation + 5);
|
|
371
|
-
needsRedraw = true;
|
|
372
|
-
redraw();
|
|
373
|
-
return;
|
|
374
|
-
}
|
|
375
|
-
if (key === "\x1b[B") {
|
|
376
|
-
camera.elevation = clampElevation(camera.elevation - 5);
|
|
377
|
-
needsRedraw = true;
|
|
378
|
-
redraw();
|
|
379
|
-
return;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
if (key === "+" || key === "=") {
|
|
383
|
-
camera.distance = Math.max(10, camera.distance - 5);
|
|
384
|
-
needsRedraw = true;
|
|
385
|
-
redraw();
|
|
386
|
-
return;
|
|
387
|
-
}
|
|
388
|
-
if (key === "-" || key === "_") {
|
|
389
|
-
camera.distance = Math.min(120, camera.distance + 5);
|
|
390
|
-
needsRedraw = true;
|
|
391
|
-
redraw();
|
|
392
|
-
return;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
const mouse = parseMouseEvent(data);
|
|
396
|
-
if (!mouse) return;
|
|
397
|
-
|
|
398
|
-
if (mouse.button === 0 && !mouse.released) {
|
|
399
|
-
mouseDown = true;
|
|
400
|
-
lastMouseX = mouse.x;
|
|
401
|
-
lastMouseY = mouse.y;
|
|
402
|
-
return;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
if (mouse.released && mouse.button === 0) {
|
|
406
|
-
const sy = mouse.y - 1;
|
|
407
|
-
const sx = mouse.x;
|
|
408
|
-
if (!diffPanel && currentBuf && sy >= 0 && sy < currentBuf.height && sx >= 0 && sx < currentBuf.width) {
|
|
409
|
-
const fi = currentBuf.fileIndices[sy][sx];
|
|
410
|
-
if (fi >= 0 && files[fi] && files[fi].changed) {
|
|
411
|
-
const filePath = filePaths[fi];
|
|
412
|
-
getFileDiff(dir, filePath).then((diff) => {
|
|
413
|
-
if (diff) {
|
|
414
|
-
const hunks = parseDiff(diff);
|
|
415
|
-
if (hunks.length > 0) {
|
|
416
|
-
diffPanel = createDiffPanel(filePath, hunks);
|
|
417
|
-
needsRedraw = true;
|
|
418
|
-
clearScreen();
|
|
419
|
-
redraw();
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
});
|
|
423
|
-
mouseDown = false;
|
|
424
|
-
return;
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
mouseDown = false;
|
|
428
|
-
return;
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
if (mouse.button === 32 && mouseDown) {
|
|
432
|
-
const dx = mouse.x - lastMouseX;
|
|
433
|
-
const dy = mouse.y - lastMouseY;
|
|
434
|
-
lastMouseX = mouse.x;
|
|
435
|
-
lastMouseY = mouse.y;
|
|
436
|
-
|
|
437
|
-
camera.azimuth = clampAzimuth(camera.azimuth + dx * 0.8);
|
|
438
|
-
camera.elevation = clampElevation(camera.elevation - dy * 0.8);
|
|
439
|
-
needsRedraw = true;
|
|
440
|
-
redraw();
|
|
441
|
-
return;
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
if (mouse.button === 35 || (mouse.button >= 32 && mouse.released === false && !mouseDown)) {
|
|
445
|
-
const sy = mouse.y - 1;
|
|
446
|
-
const sx = mouse.x;
|
|
447
|
-
if (currentBuf && sy >= 0 && sy < currentBuf.height && sx >= 0 && sx < currentBuf.width) {
|
|
448
|
-
const fi = currentBuf.fileIndices[sy][sx];
|
|
449
|
-
const newHover = fi >= 0 ? filePaths[fi] : "";
|
|
450
|
-
const isModified = fi >= 0 && files[fi] && files[fi].changed;
|
|
451
|
-
if (newHover !== hoveredFile) {
|
|
452
|
-
hoveredFile = newHover;
|
|
453
|
-
hoveredModified = isModified;
|
|
454
|
-
writeAnsi(`\x1b[1;1H`);
|
|
455
|
-
process.stdout.write(renderTopBar(hoveredFile, screenWidth, hoveredModified));
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
});
|
|
460
|
-
}
|
|
461
|
-
}
|