reslop 0.2.6 → 0.2.7
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/CHANGELOG.md +11 -1
- package/lib/ansi.js +42 -2
- package/lib/api.js +1 -1
- package/lib/cli.js +21 -1
- package/lib/dashboard/model.js +28 -3
- package/lib/dashboard/npm.js +116 -19
- package/lib/files.js +13 -7
- package/lib/find.js +61 -0
- package/lib/git-deps.js +1 -0
- package/lib/git-worktree.js +22 -4
- package/lib/keys.js +26 -1
- package/lib/render/chrome.js +257 -79
- package/lib/render/dash-blocks.js +297 -46
- package/lib/render/dash-table.js +6 -1
- package/lib/render/files.js +23 -4
- package/lib/render/frame.js +4 -0
- package/lib/render/notes.js +13 -2
- package/lib/render/packages.js +93 -0
- package/lib/render/primitives.js +13 -6
- package/lib/render/repos.js +226 -0
- package/lib/render/search.js +25 -0
- package/lib/render/tiles.js +107 -12
- package/lib/review.js +5 -4
- package/lib/session/actions.js +136 -30
- package/lib/session/branches.js +3 -5
- package/lib/session/commit.js +31 -4
- package/lib/session/compose.js +19 -4
- package/lib/session/dashboard.js +160 -15
- package/lib/session/dispatch.js +38 -4
- package/lib/session/files-pane.js +17 -2
- package/lib/session/find.js +94 -0
- package/lib/session/lifecycle.js +27 -0
- package/lib/session/navigation.js +2 -0
- package/lib/session/packages.js +143 -0
- package/lib/session/pointer.js +14 -0
- package/lib/session/tasks.js +3 -3
- package/lib/session/view.js +15 -2
- package/lib/session/watch.js +5 -0
- package/lib/session/workspace.js +455 -0
- package/lib/session.js +14 -0
- package/lib/workspace.js +166 -0
- package/package.json +1 -1
package/lib/session.js
CHANGED
|
@@ -36,6 +36,8 @@ const branches = require('./session/branches.js');
|
|
|
36
36
|
const { BranchController } = branches;
|
|
37
37
|
const npm = require('./session/npm.js');
|
|
38
38
|
const { NpmController } = npm;
|
|
39
|
+
const packages = require('./session/packages.js');
|
|
40
|
+
const { PackageController } = packages;
|
|
39
41
|
const accessors = require('./session/accessors.js');
|
|
40
42
|
const { bindAccessors, bindGetters } = accessors;
|
|
41
43
|
const view = require('./session/view.js');
|
|
@@ -46,6 +48,8 @@ const dispatch = require('./session/dispatch.js');
|
|
|
46
48
|
const { dispatchAction, confirmChoice, handleEvent } = dispatch;
|
|
47
49
|
const dashboard = require('./session/dashboard.js');
|
|
48
50
|
const { Dashboard } = dashboard;
|
|
51
|
+
const workspace = require('./session/workspace.js');
|
|
52
|
+
const { Workspace } = workspace;
|
|
49
53
|
|
|
50
54
|
const PROGRESS_MS = 160;
|
|
51
55
|
|
|
@@ -98,6 +102,8 @@ const assignOptions = (session, options) => {
|
|
|
98
102
|
session.auditMap = options.auditMap;
|
|
99
103
|
session.updateOpts = options.update;
|
|
100
104
|
session.dashboardHome = options.startPane === 'dashboard';
|
|
105
|
+
session.workspaceRoot = options.workspace ? options.workspace.root : '';
|
|
106
|
+
session.workspaceInfo = options.workspace ?? null;
|
|
101
107
|
};
|
|
102
108
|
|
|
103
109
|
const assignParts = (session, options) => {
|
|
@@ -115,7 +121,9 @@ const assignParts = (session, options) => {
|
|
|
115
121
|
session.commits = new CommitController(session);
|
|
116
122
|
session.gitBranches = new BranchController(session);
|
|
117
123
|
session.npm = new NpmController(session);
|
|
124
|
+
session.packages = new PackageController(session);
|
|
118
125
|
session.dashboard = new Dashboard(session);
|
|
126
|
+
session.workspace = new Workspace(session);
|
|
119
127
|
session.lifecycle = new Lifecycle(session);
|
|
120
128
|
};
|
|
121
129
|
|
|
@@ -160,6 +168,7 @@ class Session {
|
|
|
160
168
|
this.commits.reset();
|
|
161
169
|
this.npm.reset();
|
|
162
170
|
this.dashboard.reset();
|
|
171
|
+
this.workspace.reset();
|
|
163
172
|
this.top = this.cwd;
|
|
164
173
|
this.branch = '';
|
|
165
174
|
this.status = '';
|
|
@@ -234,6 +243,7 @@ class Session {
|
|
|
234
243
|
this.term.paint(frame, size, composing ? frame.cursor : null);
|
|
235
244
|
this.scheduleAutosave();
|
|
236
245
|
this.dashboard.afterDraw();
|
|
246
|
+
this.workspace.afterDraw();
|
|
237
247
|
}
|
|
238
248
|
|
|
239
249
|
scheduleAutosave() {
|
|
@@ -321,6 +331,7 @@ class Session {
|
|
|
321
331
|
if (result.kind === 'commit') this.commits.finishGitCommit();
|
|
322
332
|
if (result.kind === 'branch') this.gitBranches.finishCreateBranch();
|
|
323
333
|
if (result.kind === 'npm') this.npm.finishEdit();
|
|
334
|
+
if (result.kind === 'package') this.packages.finishAdd();
|
|
324
335
|
}
|
|
325
336
|
|
|
326
337
|
selectTemplate(index) {
|
|
@@ -385,6 +396,7 @@ class Session {
|
|
|
385
396
|
this.uiOpen = true;
|
|
386
397
|
this.term.enter();
|
|
387
398
|
try {
|
|
399
|
+
if (this.nav.pane === 'repos') this.workspace.start();
|
|
388
400
|
this.openLoad();
|
|
389
401
|
this.openUpdate();
|
|
390
402
|
this.draw();
|
|
@@ -412,6 +424,7 @@ class Session {
|
|
|
412
424
|
settled = true;
|
|
413
425
|
this.lifecycle.stopWatch();
|
|
414
426
|
this.dashboard.stop();
|
|
427
|
+
this.workspace.stop();
|
|
415
428
|
this.term.stopListening();
|
|
416
429
|
this.progress.clear();
|
|
417
430
|
this.term.clearTimers();
|
|
@@ -431,6 +444,7 @@ class Session {
|
|
|
431
444
|
this.term.startListening({ onData, onResize: () => this.draw() });
|
|
432
445
|
this.lifecycle.startWatch();
|
|
433
446
|
if (this.dashboardHome) this.dashboard.start();
|
|
447
|
+
if (this.nav.pane === 'repos') this.workspace.start();
|
|
434
448
|
});
|
|
435
449
|
}
|
|
436
450
|
}
|
package/lib/workspace.js
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
|
|
6
|
+
const files = require('./files.js');
|
|
7
|
+
const { REVIEW_DIR } = files;
|
|
8
|
+
const review = require('./review.js');
|
|
9
|
+
const { listReviewNames, latestReviewName, loadReview, noteCounts } = review;
|
|
10
|
+
const worktree = require('./git-worktree.js');
|
|
11
|
+
const { runGitAsync, countUntrackedAdded } = worktree;
|
|
12
|
+
const model = require('./dashboard/model.js');
|
|
13
|
+
const { runName } = model;
|
|
14
|
+
const runs = require('./runs.js');
|
|
15
|
+
const { readRuns } = runs;
|
|
16
|
+
|
|
17
|
+
const emptyDiff = () => ({
|
|
18
|
+
stagedAdded: 0,
|
|
19
|
+
unstagedAdded: 0,
|
|
20
|
+
stagedRemoved: 0,
|
|
21
|
+
unstagedRemoved: 0,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const isRepoDir = (dir) => {
|
|
25
|
+
try {
|
|
26
|
+
fs.accessSync(path.join(dir, '.git'));
|
|
27
|
+
return true;
|
|
28
|
+
} catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const listChildRepos = (dir) => {
|
|
34
|
+
let entries;
|
|
35
|
+
try {
|
|
36
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
37
|
+
} catch {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
const repos = [];
|
|
41
|
+
for (const entry of entries) {
|
|
42
|
+
if (!entry.isDirectory() || entry.name.startsWith('.')) continue;
|
|
43
|
+
const full = path.join(dir, entry.name);
|
|
44
|
+
if (!isRepoDir(full)) continue;
|
|
45
|
+
repos.push({ name: entry.name, dir: full });
|
|
46
|
+
}
|
|
47
|
+
repos.sort((left, right) => left.name.localeCompare(right.name, 'en'));
|
|
48
|
+
return repos;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const workspaceAt = (dir) => {
|
|
52
|
+
let stat;
|
|
53
|
+
try {
|
|
54
|
+
stat = fs.statSync(dir);
|
|
55
|
+
} catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
if (!stat.isDirectory() || isRepoDir(dir)) return null;
|
|
59
|
+
const repos = listChildRepos(dir);
|
|
60
|
+
if (!repos.length) return null;
|
|
61
|
+
return { root: path.resolve(dir), repos };
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const workspaceFromArgs = (cwd, paths) => {
|
|
65
|
+
if (paths.length > 1) return null;
|
|
66
|
+
const dir = paths.length ? path.resolve(cwd, paths[0]) : cwd;
|
|
67
|
+
if (paths.length) {
|
|
68
|
+
try {
|
|
69
|
+
if (!fs.statSync(dir).isDirectory()) return null;
|
|
70
|
+
} catch {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return workspaceAt(dir);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const sumNumstat = (text) => {
|
|
78
|
+
let added = 0;
|
|
79
|
+
let removed = 0;
|
|
80
|
+
for (const line of `${text ?? ''}`.split('\n')) {
|
|
81
|
+
if (!line) continue;
|
|
82
|
+
const parts = line.split('\t');
|
|
83
|
+
const add = Number(parts[0]);
|
|
84
|
+
const del = Number(parts[1]);
|
|
85
|
+
if (Number.isFinite(add)) added += add;
|
|
86
|
+
if (Number.isFinite(del)) removed += del;
|
|
87
|
+
}
|
|
88
|
+
return { added, removed };
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const readNumstat = async (dir, cached) => {
|
|
92
|
+
const args = cached
|
|
93
|
+
? ['diff', '--cached', '--numstat']
|
|
94
|
+
: ['diff', '--numstat'];
|
|
95
|
+
try {
|
|
96
|
+
const result = await runGitAsync(args, dir);
|
|
97
|
+
if (result.status !== 0) return { added: 0, removed: 0 };
|
|
98
|
+
return sumNumstat(result.stdout);
|
|
99
|
+
} catch {
|
|
100
|
+
return { added: 0, removed: 0 };
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const readDiffStat = async (dir) => {
|
|
105
|
+
const staged = await readNumstat(dir, true);
|
|
106
|
+
const open = await readNumstat(dir, false);
|
|
107
|
+
const untracked = await countUntrackedAdded(dir);
|
|
108
|
+
return {
|
|
109
|
+
stagedAdded: staged.added,
|
|
110
|
+
unstagedAdded: open.added + untracked,
|
|
111
|
+
stagedRemoved: staged.removed,
|
|
112
|
+
unstagedRemoved: open.removed,
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const countCommits = async (dir) => {
|
|
117
|
+
try {
|
|
118
|
+
const result = await runGitAsync(['rev-list', '--count', 'HEAD'], dir);
|
|
119
|
+
if (result.status !== 0) return 0;
|
|
120
|
+
const value = parseInt(result.stdout.trim(), 10);
|
|
121
|
+
return Number.isFinite(value) ? value : 0;
|
|
122
|
+
} catch {
|
|
123
|
+
return 0;
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const taskStats = (dir) => {
|
|
128
|
+
const latest = latestReviewName(listReviewNames(dir));
|
|
129
|
+
if (!latest) return { done: 0, total: 0 };
|
|
130
|
+
try {
|
|
131
|
+
const file = path.join(dir, REVIEW_DIR, latest);
|
|
132
|
+
const counts = noteCounts(loadReview(file, []));
|
|
133
|
+
return { done: counts.tasksDone, total: counts.tasks };
|
|
134
|
+
} catch {
|
|
135
|
+
return { done: 0, total: 0 };
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const pickRun = (dir) => {
|
|
140
|
+
const list = readRuns(dir);
|
|
141
|
+
const live = list.find((row) => row.status === 'running');
|
|
142
|
+
const record = live ?? list[0] ?? null;
|
|
143
|
+
if (!record) return null;
|
|
144
|
+
const progress = record.progress ?? {};
|
|
145
|
+
return {
|
|
146
|
+
name: runName(record),
|
|
147
|
+
status: record.status,
|
|
148
|
+
done: progress.done ?? 0,
|
|
149
|
+
failed: progress.failed ?? 0,
|
|
150
|
+
expected: progress.expected ?? 0,
|
|
151
|
+
result: record.result ?? null,
|
|
152
|
+
};
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
module.exports = {
|
|
156
|
+
emptyDiff,
|
|
157
|
+
isRepoDir,
|
|
158
|
+
listChildRepos,
|
|
159
|
+
workspaceAt,
|
|
160
|
+
workspaceFromArgs,
|
|
161
|
+
sumNumstat,
|
|
162
|
+
readDiffStat,
|
|
163
|
+
countCommits,
|
|
164
|
+
taskStats,
|
|
165
|
+
pickRun,
|
|
166
|
+
};
|