vimd 0.5.3 → 0.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -19,6 +19,8 @@ export declare class FolderModeServer {
|
|
|
19
19
|
private _port;
|
|
20
20
|
private fileTree;
|
|
21
21
|
private renderedHtml;
|
|
22
|
+
private folderWatcher;
|
|
23
|
+
private treeUpdateTimer;
|
|
22
24
|
constructor(options: FolderModeOptions);
|
|
23
25
|
/**
|
|
24
26
|
* Get the actual port the server is running on
|
|
@@ -84,5 +86,17 @@ export declare class FolderModeServer {
|
|
|
84
86
|
* Count total files in tree
|
|
85
87
|
*/
|
|
86
88
|
private countFiles;
|
|
89
|
+
/**
|
|
90
|
+
* Start watching the folder for file changes
|
|
91
|
+
*/
|
|
92
|
+
private startFolderWatcher;
|
|
93
|
+
/**
|
|
94
|
+
* Handle file system changes (add, unlink, etc.)
|
|
95
|
+
*/
|
|
96
|
+
private handleFileSystemChange;
|
|
97
|
+
/**
|
|
98
|
+
* Update file tree and broadcast to all clients
|
|
99
|
+
*/
|
|
100
|
+
private updateFileTree;
|
|
87
101
|
}
|
|
88
102
|
//# sourceMappingURL=folder-mode-server.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"folder-mode-server.d.ts","sourceRoot":"","sources":["../../../src/core/folder-mode/folder-mode-server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"folder-mode-server.d.ts","sourceRoot":"","sources":["../../../src/core/folder-mode/folder-mode-server.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EACV,iBAAiB,EAEjB,aAAa,EAEd,MAAM,YAAY,CAAC;AAKpB;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;CACtB;AAeD;;GAEG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,UAAU,CAA4B;IAC9C,OAAO,CAAC,QAAQ,CAAyB;IACzC,OAAO,CAAC,OAAO,CAA0C;IACzD,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,OAAO,CAAoB;IACnC,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,QAAQ,CAAkB;IAClC,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,aAAa,CAA0B;IAC/C,OAAO,CAAC,eAAe,CAA+B;gBAE1C,OAAO,EAAE,iBAAiB;IAMtC;;OAEG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;OAEG;IACH,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,iBAAiB,CAAC;IAoGzC;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA2C3B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAuCxB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAU3B;;OAEG;YACW,gBAAgB;IAuF9B;;OAEG;YACW,WAAW;IASzB;;OAEG;IACH,OAAO,CAAC,YAAY;IAoBpB;;OAEG;IACH,OAAO,CAAC,WAAW;IAMnB;;OAEG;IACH,SAAS,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI;IASvC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAW3B;;OAEG;YACW,cAAc;IAkC5B;;OAEG;IACH,OAAO,CAAC,UAAU;IASlB;;OAEG;IACH,OAAO,CAAC,cAAc;IAYtB;;OAEG;IACH,OAAO,CAAC,UAAU;IAYlB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAuC1B;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAsB9B;;OAEG;YACW,cAAc;CAgB7B"}
|
|
@@ -3,6 +3,8 @@ import path from 'path';
|
|
|
3
3
|
import fs from 'fs-extra';
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
5
5
|
import polka from 'polka';
|
|
6
|
+
import sirv from 'sirv';
|
|
7
|
+
import chokidar from 'chokidar';
|
|
6
8
|
import { WebSocketServer as WSServer, WebSocket } from 'ws';
|
|
7
9
|
import { Logger } from '../../utils/logger.js';
|
|
8
10
|
import { SessionManager } from '../../utils/session-manager.js';
|
|
@@ -13,6 +15,10 @@ import { FileWatcher } from '../watcher.js';
|
|
|
13
15
|
import { FolderScanner } from './folder-scanner.js';
|
|
14
16
|
const __filename = fileURLToPath(import.meta.url);
|
|
15
17
|
const __dirname = path.dirname(__filename);
|
|
18
|
+
/**
|
|
19
|
+
* Supported extensions for tree updates
|
|
20
|
+
*/
|
|
21
|
+
const TREE_EXTENSIONS = ['.md', '.tex', '.latex'];
|
|
16
22
|
/**
|
|
17
23
|
* Folder mode server for multi-file preview
|
|
18
24
|
*/
|
|
@@ -23,6 +29,8 @@ export class FolderModeServer {
|
|
|
23
29
|
this.clients = new Map();
|
|
24
30
|
this.fileTree = [];
|
|
25
31
|
this.renderedHtml = null;
|
|
32
|
+
this.folderWatcher = null;
|
|
33
|
+
this.treeUpdateTimer = null;
|
|
26
34
|
this.options = options;
|
|
27
35
|
this._port = options.port;
|
|
28
36
|
this.scanner = new FolderScanner({ rootPath: options.rootPath });
|
|
@@ -54,6 +62,8 @@ export class FolderModeServer {
|
|
|
54
62
|
// Initial scan
|
|
55
63
|
this.fileTree = await this.scanner.scan();
|
|
56
64
|
Logger.info(`Found ${this.countFiles(this.fileTree)} files in folder`);
|
|
65
|
+
// Start watching for file changes
|
|
66
|
+
this.startFolderWatcher();
|
|
57
67
|
// Render template
|
|
58
68
|
this.renderedHtml = await this.renderTemplate();
|
|
59
69
|
// Create polka app
|
|
@@ -63,7 +73,28 @@ export class FolderModeServer {
|
|
|
63
73
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
64
74
|
res.end(JSON.stringify(this.getWrappedTree()));
|
|
65
75
|
});
|
|
66
|
-
//
|
|
76
|
+
// Static file server for images, CSS, JS, etc.
|
|
77
|
+
const serve = sirv(this.options.rootPath, {
|
|
78
|
+
dev: true, // Disable caching for development
|
|
79
|
+
});
|
|
80
|
+
// Middleware: serve static files except for markdown/latex/root
|
|
81
|
+
app.use((req, res, next) => {
|
|
82
|
+
const url = req.url || '/';
|
|
83
|
+
// Skip API routes
|
|
84
|
+
if (url.startsWith('/api/')) {
|
|
85
|
+
return next();
|
|
86
|
+
}
|
|
87
|
+
// Skip root and markdown/latex files (handled by folder mode)
|
|
88
|
+
if (url === '/' || url.endsWith('.md') || url.endsWith('.tex') || url.endsWith('.latex')) {
|
|
89
|
+
return next();
|
|
90
|
+
}
|
|
91
|
+
// Try to serve as static file
|
|
92
|
+
serve(req, res, () => {
|
|
93
|
+
// If file not found, fall back to folder mode HTML
|
|
94
|
+
next();
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
// Serve folder mode HTML for markdown/root routes
|
|
67
98
|
app.get('*', (req, res) => {
|
|
68
99
|
this.serveFolderModeHtml(req, res);
|
|
69
100
|
});
|
|
@@ -101,6 +132,16 @@ export class FolderModeServer {
|
|
|
101
132
|
* Stop the server
|
|
102
133
|
*/
|
|
103
134
|
async stop() {
|
|
135
|
+
// Stop folder watcher
|
|
136
|
+
if (this.folderWatcher) {
|
|
137
|
+
await this.folderWatcher.close();
|
|
138
|
+
this.folderWatcher = null;
|
|
139
|
+
}
|
|
140
|
+
// Clear tree update timer
|
|
141
|
+
if (this.treeUpdateTimer) {
|
|
142
|
+
clearTimeout(this.treeUpdateTimer);
|
|
143
|
+
this.treeUpdateTimer = null;
|
|
144
|
+
}
|
|
104
145
|
// Terminate all WebSocket clients
|
|
105
146
|
for (const [client] of this.clients) {
|
|
106
147
|
try {
|
|
@@ -386,4 +427,78 @@ export class FolderModeServer {
|
|
|
386
427
|
}
|
|
387
428
|
return count;
|
|
388
429
|
}
|
|
430
|
+
/**
|
|
431
|
+
* Start watching the folder for file changes
|
|
432
|
+
*/
|
|
433
|
+
startFolderWatcher() {
|
|
434
|
+
const watchOptions = {
|
|
435
|
+
ignored: [
|
|
436
|
+
'**/node_modules/**',
|
|
437
|
+
'**/.git/**',
|
|
438
|
+
'**/dist/**',
|
|
439
|
+
'**/build/**',
|
|
440
|
+
'**/vimd-preview-*.html',
|
|
441
|
+
],
|
|
442
|
+
persistent: true,
|
|
443
|
+
ignoreInitial: true,
|
|
444
|
+
depth: 99,
|
|
445
|
+
};
|
|
446
|
+
this.folderWatcher = chokidar.watch(this.options.rootPath, watchOptions);
|
|
447
|
+
// File added
|
|
448
|
+
this.folderWatcher.on('add', (filePath) => {
|
|
449
|
+
this.handleFileSystemChange(filePath, 'add');
|
|
450
|
+
});
|
|
451
|
+
// File removed
|
|
452
|
+
this.folderWatcher.on('unlink', (filePath) => {
|
|
453
|
+
this.handleFileSystemChange(filePath, 'unlink');
|
|
454
|
+
});
|
|
455
|
+
// Directory added
|
|
456
|
+
this.folderWatcher.on('addDir', (dirPath) => {
|
|
457
|
+
this.handleFileSystemChange(dirPath, 'addDir');
|
|
458
|
+
});
|
|
459
|
+
// Directory removed
|
|
460
|
+
this.folderWatcher.on('unlinkDir', (dirPath) => {
|
|
461
|
+
this.handleFileSystemChange(dirPath, 'unlinkDir');
|
|
462
|
+
});
|
|
463
|
+
Logger.info('Folder watcher started');
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Handle file system changes (add, unlink, etc.)
|
|
467
|
+
*/
|
|
468
|
+
handleFileSystemChange(filePath, eventType) {
|
|
469
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
470
|
+
// Only react to supported extensions or directory changes
|
|
471
|
+
const isDirectory = eventType.includes('Dir');
|
|
472
|
+
const isSupportedFile = TREE_EXTENSIONS.includes(ext);
|
|
473
|
+
if (!isDirectory && !isSupportedFile) {
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
// Debounce tree updates (300ms)
|
|
477
|
+
if (this.treeUpdateTimer) {
|
|
478
|
+
clearTimeout(this.treeUpdateTimer);
|
|
479
|
+
}
|
|
480
|
+
this.treeUpdateTimer = setTimeout(async () => {
|
|
481
|
+
await this.updateFileTree();
|
|
482
|
+
this.treeUpdateTimer = null;
|
|
483
|
+
}, 300);
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Update file tree and broadcast to all clients
|
|
487
|
+
*/
|
|
488
|
+
async updateFileTree() {
|
|
489
|
+
try {
|
|
490
|
+
const newTree = await this.scanner.scan();
|
|
491
|
+
const fileCount = this.countFiles(newTree);
|
|
492
|
+
// Only update if tree actually changed
|
|
493
|
+
if (JSON.stringify(newTree) !== JSON.stringify(this.fileTree)) {
|
|
494
|
+
this.fileTree = newTree;
|
|
495
|
+
this.broadcast({ type: 'tree', data: this.getWrappedTree() });
|
|
496
|
+
Logger.info(`File tree updated: ${fileCount} files`);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
catch (error) {
|
|
500
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
501
|
+
Logger.error(`Failed to update file tree: ${message}`);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
389
504
|
}
|