@ycniuqton/devlens 0.1.7 → 0.1.9
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 +11 -2
- package/dist/routes/browser.js +3 -1
- package/dist/routes/browser.js.map +1 -1
- package/dist/routes/settings.d.ts +1 -0
- package/dist/routes/settings.js +53 -0
- package/dist/routes/settings.js.map +1 -0
- package/dist/server.js +20 -3
- package/dist/server.js.map +1 -1
- package/dist/services/files.d.ts +1 -1
- package/dist/services/files.js +4 -3
- package/dist/services/files.js.map +1 -1
- package/dist/services/git.js +1 -0
- package/dist/services/git.js.map +1 -1
- package/dist/services/settings.d.ts +13 -0
- package/dist/services/settings.js +104 -0
- package/dist/services/settings.js.map +1 -0
- package/dist/services/watcher.d.ts +2 -2
- package/dist/services/watcher.js +8 -8
- package/dist/services/watcher.js.map +1 -1
- package/dist/types/index.d.ts +2 -1
- package/package.json +2 -2
- package/public/css/style.css +214 -1
- package/public/index.html +37 -4
- package/public/js/app.js +1 -1
- package/public/js/diff.js +44 -19
- package/public/js/history.js +91 -17
- package/public/js/settings.js +85 -0
package/README.md
CHANGED
|
@@ -21,15 +21,24 @@ Devlens gives you a web-based UI to monitor file changes, manage Claude Code tas
|
|
|
21
21
|
|
|
22
22
|
## Installation
|
|
23
23
|
|
|
24
|
-
###
|
|
24
|
+
### One-liner (install + init in current project)
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install @ycniuqton/devlens@latest && npx devlens init
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Run this in any project directory — installs the latest version locally and sets up Devlens hooks for that project. Re-run anytime to upgrade.
|
|
31
|
+
|
|
32
|
+
### Global install
|
|
25
33
|
|
|
26
34
|
```bash
|
|
27
35
|
npm install -g @ycniuqton/devlens
|
|
36
|
+
devlens init
|
|
28
37
|
```
|
|
29
38
|
|
|
30
39
|
### Without installing — use `npx`
|
|
31
40
|
|
|
32
|
-
> **Note:** the package is scoped
|
|
41
|
+
> **Note:** the package is scoped. Plain `npx devlens` will install an unrelated package with the same name. Use the `--package` form:
|
|
33
42
|
|
|
34
43
|
```bash
|
|
35
44
|
npx --package=@ycniuqton/devlens -- devlens init
|
package/dist/routes/browser.js
CHANGED
|
@@ -42,8 +42,10 @@ exports.browserRouter.get('/commit/:hash', async (req, res) => {
|
|
|
42
42
|
// GET /api/browser/files?path=... — list directory contents
|
|
43
43
|
exports.browserRouter.get('/files', (req, res) => {
|
|
44
44
|
const projectDir = req.app.locals.projectDir;
|
|
45
|
+
const settings = req.app.locals.settingsService;
|
|
45
46
|
const relPath = req.query.path || '';
|
|
46
|
-
const
|
|
47
|
+
const ignoreSet = settings ? settings.getIgnoreNameSet() : undefined;
|
|
48
|
+
const entries = (0, files_1.listDirectory)(projectDir, relPath, ignoreSet);
|
|
47
49
|
res.json({ path: relPath, entries });
|
|
48
50
|
});
|
|
49
51
|
// GET /api/browser/file?path=... — read file content
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser.js","sourceRoot":"","sources":["../../src/routes/browser.ts"],"names":[],"mappings":";;;AAAA,qCAAoD;AAEpD,6CAA4D;AAE/C,QAAA,aAAa,GAAG,IAAA,gBAAM,GAAE,CAAC;AAEtC,gDAAgD;AAChD,qBAAa,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;IACjE,MAAM,GAAG,GAAe,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC;IAClD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,gBAAgB,EAAE,CAAC;QAC5C,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IACvB,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;IAC/C,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,uEAAuE;AACvE,qBAAa,CAAC,GAAG,CAAC,UAAU,EAAE,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;IAClE,MAAM,GAAG,GAAe,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC;IAClD,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,KAAe,CAAC,IAAI,EAAE,CAAC;QACxD,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACpC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChB,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;IAC/C,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,qEAAqE;AACrE,qBAAa,CAAC,GAAG,CAAC,eAAe,EAAE,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;IACvE,MAAM,GAAG,GAAe,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC;IAClD,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACxD,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACtD,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;IAC/C,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,4DAA4D;AAC5D,qBAAa,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,GAAY,EAAE,GAAa,EAAE,EAAE;IAC1D,MAAM,UAAU,GAAW,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC;IACrD,MAAM,OAAO,GAAI,GAAG,CAAC,KAAK,CAAC,IAAe,IAAI,EAAE,CAAC;IACjD,MAAM,OAAO,GAAG,IAAA,qBAAa,EAAC,UAAU,EAAE,OAAO,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"browser.js","sourceRoot":"","sources":["../../src/routes/browser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const settingsRouter: import("express-serve-static-core").Router;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.settingsRouter = void 0;
|
|
4
|
+
const express_1 = require("express");
|
|
5
|
+
exports.settingsRouter = (0, express_1.Router)();
|
|
6
|
+
const PRESET_PATTERNS = [
|
|
7
|
+
'node_modules',
|
|
8
|
+
'.git',
|
|
9
|
+
'.devlens',
|
|
10
|
+
'dist',
|
|
11
|
+
'build',
|
|
12
|
+
'.next',
|
|
13
|
+
'.nuxt',
|
|
14
|
+
'.cache',
|
|
15
|
+
'.turbo',
|
|
16
|
+
'_bmad',
|
|
17
|
+
'venv',
|
|
18
|
+
'.venv',
|
|
19
|
+
'env',
|
|
20
|
+
'__pycache__',
|
|
21
|
+
'.pytest_cache',
|
|
22
|
+
'target',
|
|
23
|
+
'vendor',
|
|
24
|
+
'coverage',
|
|
25
|
+
'.idea',
|
|
26
|
+
'.vscode',
|
|
27
|
+
];
|
|
28
|
+
// GET /api/settings — current settings + presets
|
|
29
|
+
exports.settingsRouter.get('/', (req, res) => {
|
|
30
|
+
const settings = req.app.locals.settingsService;
|
|
31
|
+
res.json({
|
|
32
|
+
ignorePatterns: settings.getIgnorePatterns(),
|
|
33
|
+
presets: PRESET_PATTERNS,
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
// PUT /api/settings — replace settings
|
|
37
|
+
exports.settingsRouter.put('/', (req, res) => {
|
|
38
|
+
const settings = req.app.locals.settingsService;
|
|
39
|
+
const broadcast = req.app.locals.broadcast;
|
|
40
|
+
const reloadWatcher = req.app.locals.reloadWatcher;
|
|
41
|
+
const { ignorePatterns } = req.body || {};
|
|
42
|
+
if (!Array.isArray(ignorePatterns)) {
|
|
43
|
+
return res.status(400).json({ error: 'ignorePatterns must be an array' });
|
|
44
|
+
}
|
|
45
|
+
const updated = settings.updateSettings({ ignorePatterns });
|
|
46
|
+
// Restart the watcher with new patterns
|
|
47
|
+
if (reloadWatcher)
|
|
48
|
+
reloadWatcher();
|
|
49
|
+
if (broadcast)
|
|
50
|
+
broadcast({ type: 'settings-update', payload: updated });
|
|
51
|
+
res.json(updated);
|
|
52
|
+
});
|
|
53
|
+
//# sourceMappingURL=settings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings.js","sourceRoot":"","sources":["../../src/routes/settings.ts"],"names":[],"mappings":";;;AAAA,qCAAoD;AAIvC,QAAA,cAAc,GAAG,IAAA,gBAAM,GAAE,CAAC;AAEvC,MAAM,eAAe,GAAG;IACtB,cAAc;IACd,MAAM;IACN,UAAU;IACV,MAAM;IACN,OAAO;IACP,OAAO;IACP,OAAO;IACP,QAAQ;IACR,QAAQ;IACR,OAAO;IACP,MAAM;IACN,OAAO;IACP,KAAK;IACL,aAAa;IACb,eAAe;IACf,QAAQ;IACR,QAAQ;IACR,UAAU;IACV,OAAO;IACP,SAAS;CACV,CAAC;AAEF,iDAAiD;AACjD,sBAAc,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAY,EAAE,GAAa,EAAE,EAAE;IACtD,MAAM,QAAQ,GAAoB,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,eAAe,CAAC;IACjE,GAAG,CAAC,IAAI,CAAC;QACP,cAAc,EAAE,QAAQ,CAAC,iBAAiB,EAAE;QAC5C,OAAO,EAAE,eAAe;KACzB,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,uCAAuC;AACvC,sBAAc,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAY,EAAE,GAAa,EAAE,EAAE;IACtD,MAAM,QAAQ,GAAoB,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,eAAe,CAAC;IACjE,MAAM,SAAS,GAA6B,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC;IACrE,MAAM,aAAa,GAA6B,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC;IAE7E,MAAM,EAAE,cAAc,EAAE,GAAG,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC;IAC1C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,CAAC;QACnC,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,iCAAiC,EAAE,CAAC,CAAC;IAC5E,CAAC;IAED,MAAM,OAAO,GAAG,QAAQ,CAAC,cAAc,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC;IAE5D,wCAAwC;IACxC,IAAI,aAAa;QAAE,aAAa,EAAE,CAAC;IAEnC,IAAI,SAAS;QAAE,SAAS,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,OAAO,EAAS,CAAC,CAAC;IAC/E,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AACpB,CAAC,CAAC,CAAC"}
|
package/dist/server.js
CHANGED
|
@@ -13,11 +13,13 @@ const git_1 = require("./services/git");
|
|
|
13
13
|
const watcher_1 = require("./services/watcher");
|
|
14
14
|
const taskStore_1 = require("./services/taskStore");
|
|
15
15
|
const rules_1 = require("./services/rules");
|
|
16
|
+
const settings_1 = require("./services/settings");
|
|
16
17
|
const diff_1 = require("./routes/diff");
|
|
17
18
|
const tasks_1 = require("./routes/tasks");
|
|
18
19
|
const integrations_1 = require("./routes/integrations");
|
|
19
20
|
const rules_2 = require("./routes/rules");
|
|
20
21
|
const browser_1 = require("./routes/browser");
|
|
22
|
+
const settings_2 = require("./routes/settings");
|
|
21
23
|
function createServer(options) {
|
|
22
24
|
const app = (0, express_1.default)();
|
|
23
25
|
const httpServer = http_1.default.createServer(app);
|
|
@@ -28,11 +30,13 @@ function createServer(options) {
|
|
|
28
30
|
const gitService = (0, git_1.createGitService)(options.projectDir);
|
|
29
31
|
const taskStore = (0, taskStore_1.createTaskStore)(options.projectDir);
|
|
30
32
|
const rulesService = (0, rules_1.createRulesService)(options.projectDir);
|
|
33
|
+
const settingsService = (0, settings_1.createSettingsService)(options.projectDir);
|
|
31
34
|
rulesService.ensureDefault();
|
|
32
35
|
// Attach to app.locals for route access
|
|
33
36
|
app.locals.gitService = gitService;
|
|
34
37
|
app.locals.taskStore = taskStore;
|
|
35
38
|
app.locals.rulesService = rulesService;
|
|
39
|
+
app.locals.settingsService = settingsService;
|
|
36
40
|
app.locals.projectDir = options.projectDir;
|
|
37
41
|
app.locals.port = options.port;
|
|
38
42
|
// API routes
|
|
@@ -48,6 +52,7 @@ function createServer(options) {
|
|
|
48
52
|
app.use('/api/integrations', integrations_1.integrationsRouter);
|
|
49
53
|
app.use('/api/rules', rules_2.rulesRouter);
|
|
50
54
|
app.use('/api/browser', browser_1.browserRouter);
|
|
55
|
+
app.use('/api/settings', settings_2.settingsRouter);
|
|
51
56
|
// Static files
|
|
52
57
|
const publicDir = path_1.default.resolve(__dirname, '../public');
|
|
53
58
|
app.use(express_1.default.static(publicDir));
|
|
@@ -64,8 +69,9 @@ function createServer(options) {
|
|
|
64
69
|
}
|
|
65
70
|
});
|
|
66
71
|
}
|
|
67
|
-
// File watcher -> WebSocket broadcast
|
|
68
|
-
|
|
72
|
+
// File watcher -> WebSocket broadcast (rebuildable when settings change)
|
|
73
|
+
let watcher = null;
|
|
74
|
+
const onWatcherChange = async () => {
|
|
69
75
|
try {
|
|
70
76
|
const diff = await gitService.getDiff();
|
|
71
77
|
const status = await gitService.getStatus();
|
|
@@ -75,7 +81,18 @@ function createServer(options) {
|
|
|
75
81
|
catch {
|
|
76
82
|
// Git service may fail if not a git repo
|
|
77
83
|
}
|
|
78
|
-
}
|
|
84
|
+
};
|
|
85
|
+
function buildWatcher() {
|
|
86
|
+
watcher = (0, watcher_1.createWatcher)(options.projectDir, onWatcherChange, settingsService.getChokidarIgnoreGlobs());
|
|
87
|
+
}
|
|
88
|
+
buildWatcher();
|
|
89
|
+
function reloadWatcher() {
|
|
90
|
+
if (watcher) {
|
|
91
|
+
watcher.close().catch(() => { });
|
|
92
|
+
}
|
|
93
|
+
buildWatcher();
|
|
94
|
+
}
|
|
95
|
+
app.locals.reloadWatcher = reloadWatcher;
|
|
79
96
|
// Watch rules.md for external changes
|
|
80
97
|
const rulesPath = path_1.default.join(options.projectDir, '.devlens', 'rules.md');
|
|
81
98
|
const rulesWatcher = chokidar_1.default.watch(rulesPath, { ignoreInitial: true });
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/dist/services/files.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export interface FileEntry {
|
|
|
4
4
|
type: 'file' | 'dir';
|
|
5
5
|
size?: number;
|
|
6
6
|
}
|
|
7
|
-
export declare function listDirectory(projectDir: string, relPath: string): FileEntry[];
|
|
7
|
+
export declare function listDirectory(projectDir: string, relPath: string, ignoreSet?: Set<string>): FileEntry[];
|
|
8
8
|
export declare function readFile(projectDir: string, relPath: string, maxBytes?: number): {
|
|
9
9
|
content: string;
|
|
10
10
|
truncated: boolean;
|
package/dist/services/files.js
CHANGED
|
@@ -7,13 +7,13 @@ exports.listDirectory = listDirectory;
|
|
|
7
7
|
exports.readFile = readFile;
|
|
8
8
|
const fs_1 = __importDefault(require("fs"));
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
|
-
const
|
|
10
|
+
const FALLBACK_IGNORE = new Set(['.git', 'node_modules', '.devlens', 'dist', '.next', '.nuxt', '.cache']);
|
|
11
11
|
function isSafePath(projectDir, target) {
|
|
12
12
|
const abs = path_1.default.resolve(projectDir, target);
|
|
13
13
|
const root = path_1.default.resolve(projectDir);
|
|
14
14
|
return abs === root || abs.startsWith(root + path_1.default.sep);
|
|
15
15
|
}
|
|
16
|
-
function listDirectory(projectDir, relPath) {
|
|
16
|
+
function listDirectory(projectDir, relPath, ignoreSet) {
|
|
17
17
|
if (!isSafePath(projectDir, relPath))
|
|
18
18
|
return [];
|
|
19
19
|
const abs = path_1.default.resolve(projectDir, relPath);
|
|
@@ -21,8 +21,9 @@ function listDirectory(projectDir, relPath) {
|
|
|
21
21
|
return [];
|
|
22
22
|
const entries = fs_1.default.readdirSync(abs, { withFileTypes: true });
|
|
23
23
|
const result = [];
|
|
24
|
+
const ignored = ignoreSet || FALLBACK_IGNORE;
|
|
24
25
|
for (const entry of entries) {
|
|
25
|
-
if (
|
|
26
|
+
if (ignored.has(entry.name))
|
|
26
27
|
continue;
|
|
27
28
|
if (entry.name.startsWith('.') && entry.name !== '.gitignore' && entry.name !== '.env.example')
|
|
28
29
|
continue;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/services/files.ts"],"names":[],"mappings":";;;;;AAkBA,
|
|
1
|
+
{"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/services/files.ts"],"names":[],"mappings":"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"}
|
package/dist/services/git.js
CHANGED
package/dist/services/git.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/services/git.ts"],"names":[],"mappings":";;;;;AAeA,
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/services/git.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface DevlensSettings {
|
|
2
|
+
ignorePatterns: string[];
|
|
3
|
+
}
|
|
4
|
+
export interface SettingsService {
|
|
5
|
+
getSettings(): DevlensSettings;
|
|
6
|
+
updateSettings(input: Partial<DevlensSettings>): DevlensSettings;
|
|
7
|
+
getIgnorePatterns(): string[];
|
|
8
|
+
/** Convert ignore patterns to chokidar-compatible globs */
|
|
9
|
+
getChokidarIgnoreGlobs(): (string | RegExp)[];
|
|
10
|
+
/** Convert ignore patterns to a Set of names for the file explorer */
|
|
11
|
+
getIgnoreNameSet(): Set<string>;
|
|
12
|
+
}
|
|
13
|
+
export declare function createSettingsService(projectDir: string): SettingsService;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createSettingsService = createSettingsService;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
// Common heavy/noisy directories that should be ignored by default
|
|
10
|
+
const DEFAULT_IGNORE_PATTERNS = [
|
|
11
|
+
'node_modules',
|
|
12
|
+
'.git',
|
|
13
|
+
'.devlens',
|
|
14
|
+
'dist',
|
|
15
|
+
'build',
|
|
16
|
+
'.next',
|
|
17
|
+
'.nuxt',
|
|
18
|
+
'.cache',
|
|
19
|
+
'.turbo',
|
|
20
|
+
'_bmad',
|
|
21
|
+
'venv',
|
|
22
|
+
'.venv',
|
|
23
|
+
'env',
|
|
24
|
+
'__pycache__',
|
|
25
|
+
'.pytest_cache',
|
|
26
|
+
'target', // Rust / Java
|
|
27
|
+
'vendor', // Go / PHP
|
|
28
|
+
'coverage',
|
|
29
|
+
'.idea',
|
|
30
|
+
'.vscode',
|
|
31
|
+
];
|
|
32
|
+
function createSettingsService(projectDir) {
|
|
33
|
+
const devlensDir = path_1.default.join(projectDir, '.devlens');
|
|
34
|
+
const settingsFile = path_1.default.join(devlensDir, 'settings.json');
|
|
35
|
+
function ensureFile() {
|
|
36
|
+
if (!fs_1.default.existsSync(devlensDir)) {
|
|
37
|
+
fs_1.default.mkdirSync(devlensDir, { recursive: true });
|
|
38
|
+
}
|
|
39
|
+
if (!fs_1.default.existsSync(settingsFile)) {
|
|
40
|
+
const initial = { ignorePatterns: DEFAULT_IGNORE_PATTERNS };
|
|
41
|
+
fs_1.default.writeFileSync(settingsFile, JSON.stringify(initial, null, 2));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function load() {
|
|
45
|
+
ensureFile();
|
|
46
|
+
try {
|
|
47
|
+
const data = JSON.parse(fs_1.default.readFileSync(settingsFile, 'utf-8'));
|
|
48
|
+
return {
|
|
49
|
+
ignorePatterns: Array.isArray(data.ignorePatterns) ? data.ignorePatterns : DEFAULT_IGNORE_PATTERNS,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return { ignorePatterns: DEFAULT_IGNORE_PATTERNS };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function save(settings) {
|
|
57
|
+
ensureFile();
|
|
58
|
+
fs_1.default.writeFileSync(settingsFile, JSON.stringify(settings, null, 2));
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
getSettings() {
|
|
62
|
+
return load();
|
|
63
|
+
},
|
|
64
|
+
updateSettings(input) {
|
|
65
|
+
const current = load();
|
|
66
|
+
const merged = {
|
|
67
|
+
ignorePatterns: input.ignorePatterns ?? current.ignorePatterns,
|
|
68
|
+
};
|
|
69
|
+
// Dedupe + trim + drop empties
|
|
70
|
+
merged.ignorePatterns = Array.from(new Set(merged.ignorePatterns.map(p => (p || '').trim()).filter(Boolean)));
|
|
71
|
+
save(merged);
|
|
72
|
+
return merged;
|
|
73
|
+
},
|
|
74
|
+
getIgnorePatterns() {
|
|
75
|
+
return load().ignorePatterns;
|
|
76
|
+
},
|
|
77
|
+
getChokidarIgnoreGlobs() {
|
|
78
|
+
const patterns = load().ignorePatterns;
|
|
79
|
+
// Always ignore dotfiles
|
|
80
|
+
const result = [/(^|[\/\\])\../];
|
|
81
|
+
for (const p of patterns) {
|
|
82
|
+
// If pattern contains glob chars, use as-is. Otherwise treat as a directory or filename.
|
|
83
|
+
if (p.includes('*') || p.includes('?') || p.includes('[')) {
|
|
84
|
+
result.push(p);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
// Match anywhere in tree: ignore the dir/file at any depth
|
|
88
|
+
result.push(`**/${p}/**`, `**/${p}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return result;
|
|
92
|
+
},
|
|
93
|
+
getIgnoreNameSet() {
|
|
94
|
+
const patterns = load().ignorePatterns;
|
|
95
|
+
const set = new Set();
|
|
96
|
+
for (const p of patterns) {
|
|
97
|
+
if (!p.includes('*') && !p.includes('/'))
|
|
98
|
+
set.add(p);
|
|
99
|
+
}
|
|
100
|
+
return set;
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=settings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings.js","sourceRoot":"","sources":["../../src/services/settings.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
export declare function createWatcher(projectDir: string, onChange: () => void):
|
|
1
|
+
import { FSWatcher } from 'chokidar';
|
|
2
|
+
export declare function createWatcher(projectDir: string, onChange: () => void, ignored?: (string | RegExp)[]): FSWatcher;
|
package/dist/services/watcher.js
CHANGED
|
@@ -5,22 +5,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.createWatcher = createWatcher;
|
|
7
7
|
const chokidar_1 = __importDefault(require("chokidar"));
|
|
8
|
-
function createWatcher(projectDir, onChange
|
|
8
|
+
function createWatcher(projectDir, onChange, ignored = [
|
|
9
|
+
/(^|[\/\\])\../,
|
|
10
|
+
'**/node_modules/**',
|
|
11
|
+
'**/.git/**',
|
|
12
|
+
'**/.devlens/**',
|
|
13
|
+
]) {
|
|
9
14
|
let debounceTimer = null;
|
|
10
15
|
const watcher = chokidar_1.default.watch(projectDir, {
|
|
11
|
-
ignored
|
|
12
|
-
/(^|[\/\\])\../, // dotfiles
|
|
13
|
-
'**/node_modules/**',
|
|
14
|
-
'**/.git/**',
|
|
15
|
-
'**/.devlens/**',
|
|
16
|
-
],
|
|
16
|
+
ignored,
|
|
17
17
|
persistent: true,
|
|
18
18
|
ignoreInitial: true,
|
|
19
19
|
});
|
|
20
20
|
const debouncedOnChange = () => {
|
|
21
21
|
if (debounceTimer)
|
|
22
22
|
clearTimeout(debounceTimer);
|
|
23
|
-
debounceTimer = setTimeout(onChange,
|
|
23
|
+
debounceTimer = setTimeout(onChange, 500);
|
|
24
24
|
};
|
|
25
25
|
watcher.on('change', debouncedOnChange);
|
|
26
26
|
watcher.on('add', debouncedOnChange);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"watcher.js","sourceRoot":"","sources":["../../src/services/watcher.ts"],"names":[],"mappings":";;;;;AAEA,
|
|
1
|
+
{"version":3,"file":"watcher.js","sourceRoot":"","sources":["../../src/services/watcher.ts"],"names":[],"mappings":";;;;;AAEA,sCA4BC;AA9BD,wDAA+C;AAE/C,SAAgB,aAAa,CAC3B,UAAkB,EAClB,QAAoB,EACpB,UAA+B;IAC7B,eAAe;IACf,oBAAoB;IACpB,YAAY;IACZ,gBAAgB;CACjB;IAED,IAAI,aAAa,GAA0B,IAAI,CAAC;IAEhD,MAAM,OAAO,GAAG,kBAAQ,CAAC,KAAK,CAAC,UAAU,EAAE;QACzC,OAAO;QACP,UAAU,EAAE,IAAI;QAChB,aAAa,EAAE,IAAI;KACpB,CAAC,CAAC;IAEH,MAAM,iBAAiB,GAAG,GAAG,EAAE;QAC7B,IAAI,aAAa;YAAE,YAAY,CAAC,aAAa,CAAC,CAAC;QAC/C,aAAa,GAAG,UAAU,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC5C,CAAC,CAAC;IAEF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,iBAAiB,CAAC,CAAC;IACxC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,CAAC,CAAC;IACrC,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,iBAAiB,CAAC,CAAC;IAExC,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export interface FileStatus {
|
|
|
12
12
|
export interface LogEntry {
|
|
13
13
|
hash: string;
|
|
14
14
|
message: string;
|
|
15
|
+
body?: string;
|
|
15
16
|
author: string;
|
|
16
17
|
date: string;
|
|
17
18
|
}
|
|
@@ -82,6 +83,6 @@ export interface DevlensConfig {
|
|
|
82
83
|
};
|
|
83
84
|
}
|
|
84
85
|
export interface WsMessage {
|
|
85
|
-
type: 'file-changed' | 'diff-update' | 'status-update' | 'task-update' | 'todo-update' | 'claude-tasks-update' | 'rules-update' | 'commit-approval-update';
|
|
86
|
+
type: 'file-changed' | 'diff-update' | 'status-update' | 'task-update' | 'todo-update' | 'claude-tasks-update' | 'rules-update' | 'commit-approval-update' | 'settings-update';
|
|
86
87
|
payload: unknown;
|
|
87
88
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ycniuqton/devlens",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
},
|
|
30
30
|
"homepage": "https://github.com/ycniuqton/Devlens#readme",
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@ycniuqton/devlens": "^0.1.
|
|
32
|
+
"@ycniuqton/devlens": "^0.1.7",
|
|
33
33
|
"better-sqlite3": "^12.8.0",
|
|
34
34
|
"chokidar": "^3.6.0",
|
|
35
35
|
"commander": "^12.0.0",
|
package/public/css/style.css
CHANGED
|
@@ -1781,9 +1781,16 @@ body {
|
|
|
1781
1781
|
}
|
|
1782
1782
|
|
|
1783
1783
|
.commit-header-content {
|
|
1784
|
+
display: flex;
|
|
1785
|
+
flex-direction: column;
|
|
1786
|
+
gap: var(--sp-3);
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
.commit-header-top {
|
|
1784
1790
|
display: flex;
|
|
1785
1791
|
align-items: center;
|
|
1786
1792
|
gap: var(--sp-3);
|
|
1793
|
+
flex-wrap: wrap;
|
|
1787
1794
|
}
|
|
1788
1795
|
|
|
1789
1796
|
.commit-header-hash {
|
|
@@ -1796,15 +1803,112 @@ body {
|
|
|
1796
1803
|
border-radius: var(--radius-sm);
|
|
1797
1804
|
}
|
|
1798
1805
|
|
|
1806
|
+
.commit-header-meta {
|
|
1807
|
+
display: flex;
|
|
1808
|
+
align-items: center;
|
|
1809
|
+
gap: var(--sp-2);
|
|
1810
|
+
font-size: var(--text-xs);
|
|
1811
|
+
color: var(--color-text-muted);
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1799
1814
|
.commit-header-files {
|
|
1800
1815
|
font-size: var(--text-xs);
|
|
1801
1816
|
color: var(--color-text-muted);
|
|
1802
1817
|
}
|
|
1803
1818
|
|
|
1819
|
+
.commit-header-message {
|
|
1820
|
+
background: var(--color-bg);
|
|
1821
|
+
border: 1px solid var(--color-border-subtle);
|
|
1822
|
+
border-radius: var(--radius-md);
|
|
1823
|
+
padding: var(--sp-3) var(--sp-4);
|
|
1824
|
+
max-height: 240px;
|
|
1825
|
+
overflow-y: auto;
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
.commit-header-subject {
|
|
1829
|
+
font-size: var(--text-base);
|
|
1830
|
+
font-weight: 600;
|
|
1831
|
+
color: var(--color-text);
|
|
1832
|
+
word-break: break-word;
|
|
1833
|
+
}
|
|
1834
|
+
|
|
1835
|
+
.commit-header-body {
|
|
1836
|
+
margin: var(--sp-2) 0 0;
|
|
1837
|
+
font-family: var(--font-mono);
|
|
1838
|
+
font-size: var(--text-xs);
|
|
1839
|
+
color: var(--color-text-secondary);
|
|
1840
|
+
white-space: pre-wrap;
|
|
1841
|
+
word-break: break-word;
|
|
1842
|
+
line-height: var(--leading-normal);
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1804
1845
|
.commit-header-actions {
|
|
1805
|
-
margin-left: auto;
|
|
1806
1846
|
display: flex;
|
|
1807
1847
|
gap: var(--sp-2);
|
|
1848
|
+
flex-wrap: wrap;
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
/* Hover tooltip on commit list items */
|
|
1852
|
+
.commit-item { position: relative; }
|
|
1853
|
+
|
|
1854
|
+
.commit-tooltip {
|
|
1855
|
+
position: absolute;
|
|
1856
|
+
left: calc(100% + 12px);
|
|
1857
|
+
top: 50%;
|
|
1858
|
+
transform: translateY(-50%);
|
|
1859
|
+
background: var(--color-surface-2);
|
|
1860
|
+
border: 1px solid var(--color-border);
|
|
1861
|
+
border-radius: var(--radius-md);
|
|
1862
|
+
padding: var(--sp-3) var(--sp-4);
|
|
1863
|
+
min-width: 320px;
|
|
1864
|
+
max-width: 480px;
|
|
1865
|
+
box-shadow: var(--shadow-lg);
|
|
1866
|
+
opacity: 0;
|
|
1867
|
+
pointer-events: none;
|
|
1868
|
+
transform-origin: left center;
|
|
1869
|
+
transform: translateY(-50%) scale(0.95);
|
|
1870
|
+
transition: opacity var(--duration-fast) var(--ease), transform var(--duration-fast) var(--ease);
|
|
1871
|
+
z-index: 50;
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
.commit-item:hover .commit-tooltip {
|
|
1875
|
+
opacity: 1;
|
|
1876
|
+
transform: translateY(-50%) scale(1);
|
|
1877
|
+
transition-delay: 250ms;
|
|
1878
|
+
}
|
|
1879
|
+
|
|
1880
|
+
.commit-tooltip::before {
|
|
1881
|
+
content: '';
|
|
1882
|
+
position: absolute;
|
|
1883
|
+
left: -6px;
|
|
1884
|
+
top: 50%;
|
|
1885
|
+
transform: translateY(-50%);
|
|
1886
|
+
width: 0;
|
|
1887
|
+
height: 0;
|
|
1888
|
+
border-top: 6px solid transparent;
|
|
1889
|
+
border-bottom: 6px solid transparent;
|
|
1890
|
+
border-right: 6px solid var(--color-border);
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
.commit-tooltip-message {
|
|
1894
|
+
font-family: var(--font-mono);
|
|
1895
|
+
font-size: var(--text-xs);
|
|
1896
|
+
color: var(--color-text);
|
|
1897
|
+
white-space: pre-wrap;
|
|
1898
|
+
word-break: break-word;
|
|
1899
|
+
line-height: var(--leading-normal);
|
|
1900
|
+
max-height: 240px;
|
|
1901
|
+
overflow-y: auto;
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
.commit-tooltip-meta {
|
|
1905
|
+
display: flex;
|
|
1906
|
+
gap: var(--sp-2);
|
|
1907
|
+
margin-top: var(--sp-2);
|
|
1908
|
+
padding-top: var(--sp-2);
|
|
1909
|
+
border-top: 1px solid var(--color-border-subtle);
|
|
1910
|
+
font-size: 10px;
|
|
1911
|
+
color: var(--color-text-muted);
|
|
1808
1912
|
}
|
|
1809
1913
|
|
|
1810
1914
|
.commit-detail-diff {
|
|
@@ -1849,8 +1953,48 @@ body {
|
|
|
1849
1953
|
overflow: hidden;
|
|
1850
1954
|
text-overflow: ellipsis;
|
|
1851
1955
|
white-space: nowrap;
|
|
1956
|
+
flex: 1;
|
|
1957
|
+
min-width: 0;
|
|
1852
1958
|
}
|
|
1853
1959
|
|
|
1960
|
+
/* File status icon (left of filename) */
|
|
1961
|
+
.commit-file-status-icon {
|
|
1962
|
+
display: inline-flex;
|
|
1963
|
+
align-items: center;
|
|
1964
|
+
justify-content: center;
|
|
1965
|
+
width: 20px;
|
|
1966
|
+
height: 20px;
|
|
1967
|
+
border-radius: var(--radius-sm);
|
|
1968
|
+
flex-shrink: 0;
|
|
1969
|
+
}
|
|
1970
|
+
|
|
1971
|
+
.commit-file-status-icon.added { color: var(--color-success); background: var(--color-success-subtle); }
|
|
1972
|
+
.commit-file-status-icon.modified { color: var(--color-warning); background: var(--color-warning-subtle); }
|
|
1973
|
+
.commit-file-status-icon.deleted { color: var(--color-danger); background: var(--color-danger-subtle); }
|
|
1974
|
+
.commit-file-status-icon.renamed { color: var(--color-purple); background: var(--color-purple-subtle); }
|
|
1975
|
+
|
|
1976
|
+
/* File status text tag (right side) */
|
|
1977
|
+
.commit-file-status-tag {
|
|
1978
|
+
font-size: 10px;
|
|
1979
|
+
font-weight: 700;
|
|
1980
|
+
letter-spacing: 0.06em;
|
|
1981
|
+
padding: 2px 8px;
|
|
1982
|
+
border-radius: var(--radius-sm);
|
|
1983
|
+
flex-shrink: 0;
|
|
1984
|
+
}
|
|
1985
|
+
|
|
1986
|
+
.commit-file-status-tag.added { color: var(--color-success); background: var(--color-success-subtle); }
|
|
1987
|
+
.commit-file-status-tag.modified { color: var(--color-warning); background: var(--color-warning-subtle); }
|
|
1988
|
+
.commit-file-status-tag.deleted { color: var(--color-danger); background: var(--color-danger-subtle); }
|
|
1989
|
+
.commit-file-status-tag.renamed { color: var(--color-purple); background: var(--color-purple-subtle); }
|
|
1990
|
+
|
|
1991
|
+
/* Filename color hints based on status */
|
|
1992
|
+
.commit-file-header.file-status-deleted .commit-file-name {
|
|
1993
|
+
text-decoration: line-through;
|
|
1994
|
+
color: var(--color-text-muted);
|
|
1995
|
+
}
|
|
1996
|
+
.commit-file-header.file-status-added .commit-file-name { color: var(--color-success); }
|
|
1997
|
+
|
|
1854
1998
|
.commit-file-body {
|
|
1855
1999
|
display: none;
|
|
1856
2000
|
overflow-x: auto;
|
|
@@ -2011,6 +2155,75 @@ body {
|
|
|
2011
2155
|
font-weight: 500;
|
|
2012
2156
|
}
|
|
2013
2157
|
|
|
2158
|
+
/* ============================================================
|
|
2159
|
+
Settings Tab
|
|
2160
|
+
============================================================ */
|
|
2161
|
+
.settings-container { max-width: 800px; width: 100%; }
|
|
2162
|
+
|
|
2163
|
+
.settings-section {
|
|
2164
|
+
background: var(--color-surface);
|
|
2165
|
+
border: 1px solid var(--color-border);
|
|
2166
|
+
border-radius: var(--radius-lg);
|
|
2167
|
+
padding: var(--sp-5) var(--sp-6);
|
|
2168
|
+
}
|
|
2169
|
+
|
|
2170
|
+
.settings-section h2 {
|
|
2171
|
+
font-size: var(--text-md);
|
|
2172
|
+
font-weight: 600;
|
|
2173
|
+
margin-bottom: var(--sp-2);
|
|
2174
|
+
}
|
|
2175
|
+
|
|
2176
|
+
.settings-subheader {
|
|
2177
|
+
font-size: var(--text-xs);
|
|
2178
|
+
font-weight: 700;
|
|
2179
|
+
text-transform: uppercase;
|
|
2180
|
+
letter-spacing: 0.08em;
|
|
2181
|
+
color: var(--color-text-muted);
|
|
2182
|
+
margin: var(--sp-5) 0 var(--sp-3);
|
|
2183
|
+
}
|
|
2184
|
+
|
|
2185
|
+
.settings-presets {
|
|
2186
|
+
display: grid;
|
|
2187
|
+
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
|
2188
|
+
gap: var(--sp-2);
|
|
2189
|
+
}
|
|
2190
|
+
|
|
2191
|
+
.preset-checkbox {
|
|
2192
|
+
display: flex;
|
|
2193
|
+
align-items: center;
|
|
2194
|
+
gap: var(--sp-2);
|
|
2195
|
+
padding: var(--sp-2) var(--sp-3);
|
|
2196
|
+
background: var(--color-bg);
|
|
2197
|
+
border: 1px solid var(--color-border-subtle);
|
|
2198
|
+
border-radius: var(--radius-sm);
|
|
2199
|
+
cursor: pointer;
|
|
2200
|
+
transition: all var(--duration-fast) var(--ease);
|
|
2201
|
+
font-size: var(--text-sm);
|
|
2202
|
+
}
|
|
2203
|
+
|
|
2204
|
+
.preset-checkbox:hover { border-color: var(--color-text-muted); }
|
|
2205
|
+
.preset-checkbox input { cursor: pointer; }
|
|
2206
|
+
.preset-name { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--color-text); }
|
|
2207
|
+
|
|
2208
|
+
.settings-textarea {
|
|
2209
|
+
width: 100%;
|
|
2210
|
+
background: var(--color-bg);
|
|
2211
|
+
border: 1px solid var(--color-border);
|
|
2212
|
+
border-radius: var(--radius-md);
|
|
2213
|
+
color: var(--color-text);
|
|
2214
|
+
font-family: var(--font-mono);
|
|
2215
|
+
font-size: var(--text-sm);
|
|
2216
|
+
padding: var(--sp-3) var(--sp-4);
|
|
2217
|
+
resize: vertical;
|
|
2218
|
+
min-height: 120px;
|
|
2219
|
+
}
|
|
2220
|
+
|
|
2221
|
+
.settings-textarea:focus {
|
|
2222
|
+
outline: none;
|
|
2223
|
+
border-color: var(--color-primary);
|
|
2224
|
+
box-shadow: 0 0 0 3px var(--color-primary-subtle);
|
|
2225
|
+
}
|
|
2226
|
+
|
|
2014
2227
|
/* ============================================================
|
|
2015
2228
|
Responsive
|
|
2016
2229
|
============================================================ */
|
package/public/index.html
CHANGED
|
@@ -63,6 +63,12 @@
|
|
|
63
63
|
</svg>
|
|
64
64
|
<span>Integrations</span>
|
|
65
65
|
</button>
|
|
66
|
+
<button class="nav-item" data-tab="settings" aria-label="Settings">
|
|
67
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
68
|
+
<circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/>
|
|
69
|
+
</svg>
|
|
70
|
+
<span>Settings</span>
|
|
71
|
+
</button>
|
|
66
72
|
</nav>
|
|
67
73
|
|
|
68
74
|
<div class="sidebar-footer">
|
|
@@ -88,11 +94,11 @@
|
|
|
88
94
|
</div>
|
|
89
95
|
<div class="divider-v"></div>
|
|
90
96
|
<div class="btn-group">
|
|
91
|
-
<button class="btn btn-ghost" data-view="side-by-side">
|
|
97
|
+
<button class="btn btn-ghost active" data-view="side-by-side">
|
|
92
98
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="8" height="18" rx="1"/><rect x="13" y="3" width="8" height="18" rx="1"/></svg>
|
|
93
99
|
Split
|
|
94
100
|
</button>
|
|
95
|
-
<button class="btn btn-ghost
|
|
101
|
+
<button class="btn btn-ghost" data-view="line-by-line">
|
|
96
102
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="1"/><line x1="3" y1="12" x2="21" y2="12"/></svg>
|
|
97
103
|
Unified
|
|
98
104
|
</button>
|
|
@@ -118,10 +124,10 @@
|
|
|
118
124
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="18 15 12 9 6 15"/></svg>
|
|
119
125
|
</button>
|
|
120
126
|
<div class="file-list-divider"></div>
|
|
121
|
-
<button class="btn-icon btn-icon-sm" id="file-view-tree" title="Folder view" aria-label="Folder view">
|
|
127
|
+
<button class="btn-icon btn-icon-sm active" id="file-view-tree" title="Folder view" aria-label="Folder view">
|
|
122
128
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg>
|
|
123
129
|
</button>
|
|
124
|
-
<button class="btn-icon btn-icon-sm
|
|
130
|
+
<button class="btn-icon btn-icon-sm" id="file-view-flat" title="Flat list" aria-label="Flat list">
|
|
125
131
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/></svg>
|
|
126
132
|
</button>
|
|
127
133
|
<span class="file-count" id="file-count">0</span>
|
|
@@ -378,6 +384,32 @@
|
|
|
378
384
|
|
|
379
385
|
</div>
|
|
380
386
|
</section>
|
|
387
|
+
|
|
388
|
+
<!-- Settings View -->
|
|
389
|
+
<section id="settings-view" class="tab-content">
|
|
390
|
+
<header class="view-header">
|
|
391
|
+
<h1>Settings</h1>
|
|
392
|
+
<div class="header-actions">
|
|
393
|
+
<button class="btn btn-primary" id="save-settings-btn">Save Settings</button>
|
|
394
|
+
</div>
|
|
395
|
+
</header>
|
|
396
|
+
|
|
397
|
+
<div class="settings-container">
|
|
398
|
+
<div class="settings-section">
|
|
399
|
+
<h2>Ignored Patterns</h2>
|
|
400
|
+
<p class="section-desc">Patterns ignored by the file watcher and the file explorer. Reduces lag for projects with large dependency folders. Changes apply immediately on save.</p>
|
|
401
|
+
|
|
402
|
+
<h3 class="settings-subheader">Common presets</h3>
|
|
403
|
+
<div id="settings-presets" class="settings-presets">
|
|
404
|
+
<p class="panel-empty">Loading...</p>
|
|
405
|
+
</div>
|
|
406
|
+
|
|
407
|
+
<h3 class="settings-subheader">Custom patterns</h3>
|
|
408
|
+
<p class="section-desc">One pattern per line. Plain names match anywhere in the tree (e.g. <code>node_modules</code>). Globs supported (e.g. <code>**/*.log</code>).</p>
|
|
409
|
+
<textarea id="settings-custom" class="settings-textarea" rows="6" placeholder="my-cache **/*.log"></textarea>
|
|
410
|
+
</div>
|
|
411
|
+
</div>
|
|
412
|
+
</section>
|
|
381
413
|
</main>
|
|
382
414
|
|
|
383
415
|
<!-- Task Modal -->
|
|
@@ -482,6 +514,7 @@
|
|
|
482
514
|
<script src="/js/rules.js"></script>
|
|
483
515
|
<script src="/js/browser.js"></script>
|
|
484
516
|
<script src="/js/history.js"></script>
|
|
517
|
+
<script src="/js/settings.js"></script>
|
|
485
518
|
<script src="/js/integrations.js"></script>
|
|
486
519
|
</body>
|
|
487
520
|
</html>
|
package/public/js/app.js
CHANGED
|
@@ -40,7 +40,7 @@ window.addEventListener('popstate', () => {
|
|
|
40
40
|
// Load initial tab from URL — redirect / to /diff
|
|
41
41
|
(function() {
|
|
42
42
|
const path = location.pathname.replace('/', '');
|
|
43
|
-
const tab = ['diff', 'tasks', 'browser', 'history', 'rules', 'integrations'].includes(path) ? path : 'diff';
|
|
43
|
+
const tab = ['diff', 'tasks', 'browser', 'history', 'rules', 'integrations', 'settings'].includes(path) ? path : 'diff';
|
|
44
44
|
if (!path || path === '') {
|
|
45
45
|
history.replaceState(null, '', '/diff');
|
|
46
46
|
}
|
package/public/js/diff.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// Diff viewer
|
|
2
2
|
var currentFilter = 'all';
|
|
3
|
-
var currentViewMode = '
|
|
4
|
-
var fileListMode = localStorage.getItem('devlens-file-view') || '
|
|
3
|
+
var currentViewMode = localStorage.getItem('devlens-diff-view') || 'side-by-side';
|
|
4
|
+
var fileListMode = localStorage.getItem('devlens-file-view') || 'tree';
|
|
5
5
|
var diffFiles = [];
|
|
6
6
|
var currentFiles = [];
|
|
7
7
|
|
|
8
|
-
// Restore file list
|
|
9
|
-
(function
|
|
8
|
+
// Restore view mode + file list mode from localStorage
|
|
9
|
+
(function restoreDiffSettings() {
|
|
10
10
|
const flatBtn = document.getElementById('file-view-flat');
|
|
11
11
|
const treeBtn = document.getElementById('file-view-tree');
|
|
12
12
|
if (fileListMode === 'tree') {
|
|
@@ -16,6 +16,12 @@ var currentFiles = [];
|
|
|
16
16
|
flatBtn?.classList.add('active');
|
|
17
17
|
treeBtn?.classList.remove('active');
|
|
18
18
|
}
|
|
19
|
+
|
|
20
|
+
// Restore active button for diff view mode (split / unified)
|
|
21
|
+
document.querySelectorAll('[data-view]').forEach(b => {
|
|
22
|
+
if (b.dataset.view === currentViewMode) b.classList.add('active');
|
|
23
|
+
else b.classList.remove('active');
|
|
24
|
+
});
|
|
19
25
|
})();
|
|
20
26
|
|
|
21
27
|
// Filter and view toggle
|
|
@@ -34,6 +40,7 @@ document.querySelector('.header-actions')?.addEventListener('click', (e) => {
|
|
|
34
40
|
document.querySelectorAll('[data-view]').forEach(b => b.classList.remove('active'));
|
|
35
41
|
btn.classList.add('active');
|
|
36
42
|
currentViewMode = btn.dataset.view;
|
|
43
|
+
localStorage.setItem('devlens-diff-view', currentViewMode);
|
|
37
44
|
renderAllFiles();
|
|
38
45
|
}
|
|
39
46
|
});
|
|
@@ -56,11 +63,12 @@ document.getElementById('file-list-items').addEventListener('click', (e) => {
|
|
|
56
63
|
document.querySelectorAll('#file-list-items li').forEach(l => l.classList.remove('selected'));
|
|
57
64
|
li.classList.add('selected');
|
|
58
65
|
|
|
59
|
-
// Collapse all, expand only the clicked file
|
|
66
|
+
// Collapse all, expand only the clicked file (lazy-render its body)
|
|
60
67
|
const sections = document.querySelectorAll('.diff-file-section');
|
|
61
68
|
for (const section of sections) {
|
|
62
69
|
if (section.dataset.file === fileName) {
|
|
63
70
|
section.classList.add('expanded');
|
|
71
|
+
renderFileBodyIfNeeded(section);
|
|
64
72
|
} else {
|
|
65
73
|
section.classList.remove('expanded');
|
|
66
74
|
}
|
|
@@ -198,21 +206,12 @@ function renderAllFiles() {
|
|
|
198
206
|
return;
|
|
199
207
|
}
|
|
200
208
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
// First file expanded, rest collapsed
|
|
209
|
+
// Render only headers — diffs are computed lazily on expand
|
|
210
|
+
// diffFiles[i].diff holds the raw unified diff string for this file
|
|
204
211
|
container.innerHTML = diffFiles.map((file, i) => {
|
|
205
|
-
const diffHtml = Diff2Html.html(file.diff, {
|
|
206
|
-
drawFileList: false,
|
|
207
|
-
matching: 'lines',
|
|
208
|
-
outputFormat: outputFormat,
|
|
209
|
-
colorScheme: 'dark',
|
|
210
|
-
});
|
|
211
|
-
|
|
212
212
|
const shortName = file.name.split('/').pop();
|
|
213
|
-
|
|
214
213
|
return `
|
|
215
|
-
<div class="diff-file-section ${i === 0 ? 'expanded' : ''}" data-file="${file.name}">
|
|
214
|
+
<div class="diff-file-section ${i === 0 ? 'expanded' : ''}" data-file="${file.name}" data-idx="${i}">
|
|
216
215
|
<div class="diff-file-header" onclick="toggleFileSection(this)">
|
|
217
216
|
<svg class="chevron" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
218
217
|
<polyline points="9 18 15 12 9 6"/>
|
|
@@ -220,16 +219,42 @@ function renderAllFiles() {
|
|
|
220
219
|
<span class="diff-file-name">${file.name}</span>
|
|
221
220
|
<span class="diff-file-badge">${shortName}</span>
|
|
222
221
|
</div>
|
|
223
|
-
<div class="diff-file-body"
|
|
222
|
+
<div class="diff-file-body" data-rendered="false"></div>
|
|
224
223
|
</div>
|
|
225
224
|
`;
|
|
226
225
|
}).join('');
|
|
226
|
+
|
|
227
|
+
// Pre-render only the first (expanded by default) file
|
|
228
|
+
const first = container.querySelector('.diff-file-section.expanded');
|
|
229
|
+
if (first) renderFileBodyIfNeeded(first);
|
|
227
230
|
}
|
|
228
231
|
|
|
229
|
-
//
|
|
232
|
+
// Render the diff HTML for a file section if not already done
|
|
233
|
+
function renderFileBodyIfNeeded(section) {
|
|
234
|
+
const body = section.querySelector('.diff-file-body');
|
|
235
|
+
if (!body || body.dataset.rendered === 'true') return;
|
|
236
|
+
|
|
237
|
+
const idx = parseInt(section.dataset.idx, 10);
|
|
238
|
+
const file = diffFiles[idx];
|
|
239
|
+
if (!file) return;
|
|
240
|
+
|
|
241
|
+
const outputFormat = currentViewMode === 'side-by-side' ? 'side-by-side' : 'line-by-line';
|
|
242
|
+
body.innerHTML = Diff2Html.html(file.diff, {
|
|
243
|
+
drawFileList: false,
|
|
244
|
+
matching: 'lines',
|
|
245
|
+
outputFormat: outputFormat,
|
|
246
|
+
colorScheme: 'dark',
|
|
247
|
+
});
|
|
248
|
+
body.dataset.rendered = 'true';
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Chevron click — toggle just this file, lazy-render on first expand
|
|
230
252
|
function toggleFileSection(headerEl) {
|
|
231
253
|
const section = headerEl.closest('.diff-file-section');
|
|
232
254
|
section.classList.toggle('expanded');
|
|
255
|
+
if (section.classList.contains('expanded')) {
|
|
256
|
+
renderFileBodyIfNeeded(section);
|
|
257
|
+
}
|
|
233
258
|
}
|
|
234
259
|
|
|
235
260
|
var STATUS_LABELS = { modified: 'M', added: 'A', deleted: 'D', untracked: 'U', renamed: 'R' };
|
package/public/js/history.js
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
var historyLoaded = false;
|
|
4
4
|
var historySelectedHash = null;
|
|
5
|
-
var historyViewMode = localStorage.getItem('devlens-history-view') || '
|
|
5
|
+
var historyViewMode = localStorage.getItem('devlens-history-view') || 'side-by-side';
|
|
6
6
|
var historyCachedData = null; // last loaded commit { hash, files, diff }
|
|
7
|
+
var historyCommits = []; // last loaded commit list (for full message lookup)
|
|
7
8
|
|
|
8
9
|
async function loadBranchInfo() {
|
|
9
10
|
try {
|
|
@@ -20,6 +21,7 @@ async function loadCommits() {
|
|
|
20
21
|
try {
|
|
21
22
|
const res = await fetch('/api/browser/commits?limit=100');
|
|
22
23
|
const commits = await res.json();
|
|
24
|
+
historyCommits = commits;
|
|
23
25
|
document.getElementById('commit-count').textContent = commits.length;
|
|
24
26
|
|
|
25
27
|
if (!commits.length) {
|
|
@@ -27,7 +29,9 @@ async function loadCommits() {
|
|
|
27
29
|
return;
|
|
28
30
|
}
|
|
29
31
|
|
|
30
|
-
list.innerHTML = commits.map(c =>
|
|
32
|
+
list.innerHTML = commits.map(c => {
|
|
33
|
+
const fullMessage = c.body ? `${c.message}\n\n${c.body}` : c.message;
|
|
34
|
+
return `
|
|
31
35
|
<li class="commit-item" data-hash="${c.hash}">
|
|
32
36
|
<div class="commit-marker"></div>
|
|
33
37
|
<div class="commit-content">
|
|
@@ -38,8 +42,16 @@ async function loadCommits() {
|
|
|
38
42
|
<span class="commit-date">${formatRelativeDate(c.date)}</span>
|
|
39
43
|
</div>
|
|
40
44
|
</div>
|
|
41
|
-
|
|
42
|
-
|
|
45
|
+
<div class="commit-tooltip">
|
|
46
|
+
<div class="commit-tooltip-message">${escapeHtmlHistory(fullMessage)}</div>
|
|
47
|
+
<div class="commit-tooltip-meta">
|
|
48
|
+
<span>${escapeHtmlHistory(c.author)}</span>
|
|
49
|
+
<span>·</span>
|
|
50
|
+
<span>${new Date(c.date).toLocaleString()}</span>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
</li>`;
|
|
54
|
+
}).join('');
|
|
43
55
|
|
|
44
56
|
list.querySelectorAll('.commit-item').forEach(item => {
|
|
45
57
|
item.addEventListener('click', () => {
|
|
@@ -99,11 +111,26 @@ function renderCommitDetails(hash, data) {
|
|
|
99
111
|
if (!headerEl || !diffEl) return;
|
|
100
112
|
|
|
101
113
|
const isUnified = historyViewMode === 'line-by-line';
|
|
114
|
+
const commit = historyCommits.find(c => c.hash === hash) || {};
|
|
115
|
+
const subject = commit.message || '';
|
|
116
|
+
const body = commit.body || '';
|
|
117
|
+
const author = commit.author || '';
|
|
118
|
+
const date = commit.date ? new Date(commit.date).toLocaleString() : '';
|
|
102
119
|
|
|
103
120
|
headerEl.innerHTML = `
|
|
104
121
|
<div class="commit-header-content">
|
|
105
|
-
<div class="commit-header-
|
|
106
|
-
|
|
122
|
+
<div class="commit-header-top">
|
|
123
|
+
<div class="commit-header-hash">${hash}</div>
|
|
124
|
+
<div class="commit-header-meta">
|
|
125
|
+
${author ? `<span>${escapeHtmlHistory(author)}</span>` : ''}
|
|
126
|
+
${date ? `<span>·</span><span>${date}</span>` : ''}
|
|
127
|
+
<span>·</span><span>${data.files.length} file(s)</span>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
<div class="commit-header-message">
|
|
131
|
+
<div class="commit-header-subject">${escapeHtmlHistory(subject)}</div>
|
|
132
|
+
${body ? `<pre class="commit-header-body">${escapeHtmlHistory(body)}</pre>` : ''}
|
|
133
|
+
</div>
|
|
107
134
|
<div class="commit-header-actions">
|
|
108
135
|
<div class="btn-group">
|
|
109
136
|
<button class="btn btn-ghost btn-sm ${!isUnified ? 'active' : ''}" id="history-view-split" title="Side by side">
|
|
@@ -133,33 +160,58 @@ function renderCommitDetails(hash, data) {
|
|
|
133
160
|
}
|
|
134
161
|
|
|
135
162
|
const files = historySplitDiffByFile(data.diff);
|
|
136
|
-
|
|
137
|
-
|
|
163
|
+
|
|
164
|
+
// Build a path → status map from data.files (e.g. "added", "modified", "deleted", "renamed")
|
|
165
|
+
const statusByPath = {};
|
|
166
|
+
for (const f of (data.files || [])) statusByPath[f.path] = f.status;
|
|
167
|
+
|
|
168
|
+
// Stash raw diffs on a closure-accessible array indexed by data-idx
|
|
169
|
+
const renderCommitFileBody = (section) => {
|
|
170
|
+
const body = section.querySelector('.commit-file-body');
|
|
171
|
+
if (!body || body.dataset.rendered === 'true') return;
|
|
172
|
+
const idx = parseInt(section.dataset.idx, 10);
|
|
173
|
+
const f = files[idx];
|
|
174
|
+
if (!f) return;
|
|
175
|
+
body.innerHTML = Diff2Html.html(f.diff, {
|
|
138
176
|
drawFileList: false,
|
|
139
177
|
matching: 'lines',
|
|
140
178
|
outputFormat: historyViewMode,
|
|
141
179
|
colorScheme: 'dark',
|
|
142
180
|
});
|
|
181
|
+
body.dataset.rendered = 'true';
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
diffEl.innerHTML = files.map((file, i) => {
|
|
185
|
+
const status = statusByPath[file.name] || 'modified';
|
|
143
186
|
return `
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
</
|
|
149
|
-
<
|
|
187
|
+
<div class="commit-file-section" data-file="${escapeAttrHistory(file.name)}" data-idx="${i}">
|
|
188
|
+
<div class="commit-file-header file-status-${status}">
|
|
189
|
+
<svg class="commit-file-chevron" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"/></svg>
|
|
190
|
+
<span class="commit-file-status-icon ${status}" title="${status}">${commitFileStatusIcon(status)}</span>
|
|
191
|
+
<span class="commit-file-name">${escapeHtmlHistory(file.name)}</span>
|
|
192
|
+
<span class="commit-file-status-tag ${status}">${commitFileStatusLabel(status)}</span>
|
|
150
193
|
</div>
|
|
194
|
+
<div class="commit-file-body" data-rendered="false"></div>
|
|
195
|
+
</div>
|
|
151
196
|
`;
|
|
152
197
|
}).join('');
|
|
153
198
|
|
|
154
|
-
// Wire
|
|
199
|
+
// Wire collapse/expand with lazy render on first expand
|
|
155
200
|
diffEl.querySelectorAll('.commit-file-header').forEach(h => {
|
|
156
201
|
h.addEventListener('click', () => {
|
|
157
|
-
h.closest('.commit-file-section')
|
|
202
|
+
const section = h.closest('.commit-file-section');
|
|
203
|
+
section.classList.toggle('expanded');
|
|
204
|
+
if (section.classList.contains('expanded')) {
|
|
205
|
+
renderCommitFileBody(section);
|
|
206
|
+
}
|
|
158
207
|
});
|
|
159
208
|
});
|
|
160
209
|
|
|
161
210
|
document.getElementById('commit-expand-all')?.addEventListener('click', () => {
|
|
162
|
-
diffEl.querySelectorAll('.commit-file-section').forEach(s =>
|
|
211
|
+
diffEl.querySelectorAll('.commit-file-section').forEach(s => {
|
|
212
|
+
s.classList.add('expanded');
|
|
213
|
+
renderCommitFileBody(s);
|
|
214
|
+
});
|
|
163
215
|
});
|
|
164
216
|
document.getElementById('commit-collapse-all')?.addEventListener('click', () => {
|
|
165
217
|
diffEl.querySelectorAll('.commit-file-section').forEach(s => s.classList.remove('expanded'));
|
|
@@ -182,6 +234,28 @@ function escapeAttrHistory(str) {
|
|
|
182
234
|
return String(str).replace(/"/g, '"');
|
|
183
235
|
}
|
|
184
236
|
|
|
237
|
+
function commitFileStatusIcon(status) {
|
|
238
|
+
// SVG icons for each git status
|
|
239
|
+
const svg = (path) => `<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">${path}</svg>`;
|
|
240
|
+
switch (status) {
|
|
241
|
+
case 'added': return svg('<line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/>'); // +
|
|
242
|
+
case 'deleted': return svg('<line x1="5" y1="12" x2="19" y2="12"/>'); // —
|
|
243
|
+
case 'renamed': return svg('<polyline points="17 1 21 5 17 9"/><path d="M3 11V9a4 4 0 0 1 4-4h14"/><polyline points="7 23 3 19 7 15"/><path d="M21 13v2a4 4 0 0 1-4 4H3"/>'); // ↻
|
|
244
|
+
case 'modified':
|
|
245
|
+
default: return svg('<circle cx="12" cy="12" r="3"/>'); // •
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function commitFileStatusLabel(status) {
|
|
250
|
+
switch (status) {
|
|
251
|
+
case 'added': return 'NEW';
|
|
252
|
+
case 'deleted': return 'DEL';
|
|
253
|
+
case 'renamed': return 'REN';
|
|
254
|
+
case 'modified': return 'MOD';
|
|
255
|
+
default: return status.toUpperCase();
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
185
259
|
function escapeHtmlHistory(str) {
|
|
186
260
|
if (str == null) return '';
|
|
187
261
|
const div = document.createElement('div');
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// Settings tab — manage .devlens/settings.json (ignore patterns)
|
|
2
|
+
|
|
3
|
+
var settingsLoaded = false;
|
|
4
|
+
|
|
5
|
+
async function loadSettings() {
|
|
6
|
+
try {
|
|
7
|
+
const res = await fetch('/api/settings');
|
|
8
|
+
const data = await res.json();
|
|
9
|
+
renderSettings(data);
|
|
10
|
+
settingsLoaded = true;
|
|
11
|
+
} catch {
|
|
12
|
+
showToast('Failed to load settings', 'error');
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function renderSettings(data) {
|
|
17
|
+
const presetsEl = document.getElementById('settings-presets');
|
|
18
|
+
const customEl = document.getElementById('settings-custom');
|
|
19
|
+
if (!presetsEl || !customEl) return;
|
|
20
|
+
|
|
21
|
+
const active = new Set(data.ignorePatterns || []);
|
|
22
|
+
const presets = data.presets || [];
|
|
23
|
+
|
|
24
|
+
// Render preset checkboxes
|
|
25
|
+
presetsEl.innerHTML = presets.map(p => `
|
|
26
|
+
<label class="preset-checkbox">
|
|
27
|
+
<input type="checkbox" data-preset="${escapeAttrSettings(p)}" ${active.has(p) ? 'checked' : ''}>
|
|
28
|
+
<span class="preset-name">${escapeHtmlSettings(p)}</span>
|
|
29
|
+
</label>
|
|
30
|
+
`).join('');
|
|
31
|
+
|
|
32
|
+
// Custom = active patterns NOT in presets
|
|
33
|
+
const customPatterns = (data.ignorePatterns || []).filter(p => !presets.includes(p));
|
|
34
|
+
customEl.value = customPatterns.join('\n');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function collectCurrentPatterns() {
|
|
38
|
+
const checked = Array.from(document.querySelectorAll('#settings-presets input[type="checkbox"]:checked'))
|
|
39
|
+
.map(cb => cb.dataset.preset);
|
|
40
|
+
const custom = (document.getElementById('settings-custom')?.value || '')
|
|
41
|
+
.split('\n')
|
|
42
|
+
.map(l => l.trim())
|
|
43
|
+
.filter(Boolean);
|
|
44
|
+
// Dedupe
|
|
45
|
+
return Array.from(new Set([...checked, ...custom]));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function saveSettings() {
|
|
49
|
+
const patterns = collectCurrentPatterns();
|
|
50
|
+
try {
|
|
51
|
+
const res = await fetch('/api/settings', {
|
|
52
|
+
method: 'PUT',
|
|
53
|
+
headers: { 'Content-Type': 'application/json' },
|
|
54
|
+
body: JSON.stringify({ ignorePatterns: patterns }),
|
|
55
|
+
});
|
|
56
|
+
if (!res.ok) {
|
|
57
|
+
showToast('Failed to save settings', 'error');
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
showToast('Settings saved — watcher restarted', 'success');
|
|
61
|
+
} catch {
|
|
62
|
+
showToast('Failed to save settings', 'error');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function escapeHtmlSettings(str) {
|
|
67
|
+
if (str == null) return '';
|
|
68
|
+
const div = document.createElement('div');
|
|
69
|
+
div.textContent = str;
|
|
70
|
+
return div.innerHTML;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function escapeAttrSettings(str) {
|
|
74
|
+
return String(str).replace(/"/g, '"');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
document.getElementById('save-settings-btn')?.addEventListener('click', saveSettings);
|
|
78
|
+
|
|
79
|
+
document.querySelector('[data-tab="settings"]')?.addEventListener('click', () => {
|
|
80
|
+
if (!settingsLoaded) loadSettings();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
if (location.pathname === '/settings') {
|
|
84
|
+
loadSettings();
|
|
85
|
+
}
|