reslop 0.2.6 → 0.3.0
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 +27 -1
- package/README.md +16 -14
- package/bin/reslop.js +8 -5
- package/lib/agents/agents.js +504 -0
- package/lib/agents/allow.js +71 -0
- package/lib/agents/allow.json +16 -0
- package/lib/agents/models.js +460 -0
- package/lib/api.js +15 -12
- package/lib/cli.js +61 -66
- package/lib/{files.js → common/files.js} +51 -52
- package/lib/common/find.js +61 -0
- package/lib/common/format.js +101 -0
- package/lib/{utilities.js → common/process.js} +7 -95
- package/lib/common/tasks.json +27 -0
- package/lib/common/utilities.js +112 -0
- package/lib/dashboard/git.js +9 -16
- package/lib/dashboard/model.js +117 -51
- package/lib/dashboard/npm.js +133 -32
- package/lib/dashboard/tree.js +27 -30
- package/lib/{session → dashboard}/watch.js +41 -31
- package/lib/dashboard/workspace.js +151 -0
- package/lib/{deps.js → deps/deps.js} +31 -45
- package/lib/{deps-diff.js → deps/diff.js} +35 -25
- package/lib/{manifest.js → deps/manifest.js} +47 -30
- package/lib/diff/align.js +1 -2
- package/lib/diff/diff.js +2 -2
- package/lib/diff/display.js +29 -5
- package/lib/diff/inline.js +10 -14
- package/lib/diff/items.js +6 -4
- package/lib/diff/patch.js +39 -3
- package/lib/diff/unit.js +45 -16
- package/lib/{git-branches.js → git/branches.js} +53 -48
- package/lib/{git-deps.js → git/deps.js} +123 -95
- package/lib/git/git.js +71 -0
- package/lib/git/worktree.js +475 -0
- package/lib/highlight/bash.js +1 -2
- package/lib/highlight/csv.js +1 -2
- package/lib/highlight/dot.js +1 -2
- package/lib/{highlight.js → highlight/highlight.js} +42 -11
- package/lib/highlight/html.js +20 -18
- package/lib/highlight/json.js +1 -2
- package/lib/highlight/log.js +1 -2
- package/lib/highlight/txt.js +1 -2
- package/lib/{session → input}/actions.js +240 -71
- package/lib/{editor.js → input/editor.js} +7 -8
- package/lib/{keys.js → input/keys.js} +36 -39
- package/lib/input/line-edit.js +77 -0
- package/lib/input/menu-pick.js +70 -0
- package/lib/render/agents.js +640 -0
- package/lib/render/branches.js +4 -5
- package/lib/render/commits.js +54 -64
- package/lib/render/{dash-activity.js → dashboard/activity.js} +25 -40
- package/lib/render/dashboard/blocks.js +490 -0
- package/lib/render/{dashboard.js → dashboard/dashboard.js} +25 -22
- package/lib/render/diff.js +166 -45
- package/lib/render/field.js +131 -0
- package/lib/render/files.js +22 -8
- package/lib/render/footer.js +383 -0
- package/lib/render/frame.js +127 -25
- package/lib/render/header.js +176 -0
- package/lib/render/log.js +319 -0
- package/lib/render/menu.js +60 -0
- package/lib/render/notes.js +29 -13
- package/lib/render/npm.js +322 -196
- package/lib/render/packages.js +89 -0
- package/lib/render/primitives.js +109 -16
- package/lib/render/render.js +7 -11
- package/lib/render/repos.js +214 -0
- package/lib/render/status.js +370 -0
- package/lib/render/status.json +85 -0
- package/lib/render/{dash-table.js → table.js} +19 -61
- package/lib/render/tiles.js +203 -47
- package/lib/render/unit.js +32 -13
- package/lib/{report-model.js → report/model.js} +4 -6
- package/lib/{report-parse.js → report/parse.js} +9 -14
- package/lib/{report-render.js → report/render.js} +46 -14
- package/lib/{report-run.js → report/run.js} +42 -30
- package/lib/{report-test.js → report/test.js} +56 -55
- package/lib/{report-tools.js → report/tools.js} +24 -33
- package/lib/review/agent-instructions.md +9 -0
- package/lib/review/format.js +426 -0
- package/lib/{review-merge.js → review/merge.js} +9 -8
- package/lib/review/review.js +190 -0
- package/lib/review/store.js +281 -0
- package/lib/review/templates.js +83 -0
- package/lib/runs/commands.js +14 -0
- package/lib/runs/logs.js +299 -0
- package/lib/runs/output.js +174 -0
- package/lib/runs/process.js +113 -0
- package/lib/{runs.js → runs/runs.js} +84 -36
- package/lib/runs/scripts.js +150 -0
- package/lib/session/agents/agents.js +958 -0
- package/lib/session/agents/job.js +320 -0
- package/lib/session/agents/jobs.js +355 -0
- package/lib/session/agents/options.js +153 -0
- package/lib/session/agents/plans.js +83 -0
- package/lib/session/branches.js +36 -44
- package/lib/session/change-actions.js +34 -100
- package/lib/session/commit.js +122 -56
- package/lib/session/compose.js +197 -146
- package/lib/session/dashboard.js +183 -37
- package/lib/session/dispatch.js +120 -22
- package/lib/session/files-pane.js +60 -24
- package/lib/session/find.js +81 -0
- package/lib/session/import.js +82 -0
- package/lib/session/items.js +8 -17
- package/lib/session/lifecycle.js +96 -66
- package/lib/session/load.js +2 -4
- package/lib/session/navigation.js +8 -3
- package/lib/session/npm/history.js +249 -0
- package/lib/session/npm/npm.js +815 -0
- package/lib/session/npm/packages.js +150 -0
- package/lib/session/ops.js +125 -34
- package/lib/session/pointer.js +216 -10
- package/lib/session/prefs.js +112 -0
- package/lib/session/reads.js +109 -0
- package/lib/session/review.js +28 -10
- package/lib/session/run-panel.js +40 -0
- package/lib/{session.js → session/session.js} +166 -59
- package/lib/session/tasks.js +321 -66
- package/lib/session/templates.js +1 -1
- package/lib/session/terminal.js +94 -12
- package/lib/session/unit.js +35 -30
- package/lib/session/update.js +194 -6
- package/lib/session/view.js +72 -59
- package/lib/session/workspace.js +416 -0
- package/lib/{capabilities.js → source/capabilities.js} +3 -12
- package/lib/{github.js → source/github.js} +102 -32
- package/lib/{gitlab.js → source/gitlab.js} +87 -10
- package/lib/{remote.js → source/remote.js} +144 -40
- package/lib/source/review-threads.graphql +20 -0
- package/lib/source/source.js +74 -0
- package/lib/term/ansi.js +396 -0
- package/lib/{clipboard.js → term/clipboard.js} +3 -6
- package/lib/{select.js → term/select.js} +2 -2
- package/lib/term/themes/dark.json +122 -0
- package/lib/term/themes/light.json +122 -0
- package/lib/term/wide.txt +21 -0
- package/lib/{wrap.js → term/wrap.js} +4 -4
- package/package.json +2 -2
- package/lib/ansi.js +0 -505
- package/lib/detect.js +0 -34
- package/lib/diff/edit.js +0 -33
- package/lib/git-worktree.js +0 -437
- package/lib/git.js +0 -79
- package/lib/npm-commands.js +0 -522
- package/lib/render/chrome.js +0 -628
- package/lib/render/dash-blocks.js +0 -245
- package/lib/render/present.js +0 -19
- package/lib/render/report.js +0 -43
- package/lib/review.js +0 -744
- package/lib/session/accessors.js +0 -26
- package/lib/session/edit-click.js +0 -57
- package/lib/session/npm.js +0 -452
- package/lib/session/progress.js +0 -41
- package/lib/source.js +0 -36
- package/lib/update.js +0 -192
- /package/lib/{git-reword-todo.js → git/reword-todo.js} +0 -0
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const {
|
|
6
|
-
const {
|
|
7
|
-
const
|
|
8
|
-
const {
|
|
9
|
-
const
|
|
10
|
-
const actions = require('./actions.js');
|
|
11
|
-
const { LIST_PANES } = actions;
|
|
12
|
-
const { setFileScope, openUnitFile, moveUnitBlock } = unit;
|
|
3
|
+
const { clamp } = require('../common/utilities.js');
|
|
4
|
+
const { THEME_NAMES, setTheme, themeName } = require('../term/ansi.js');
|
|
5
|
+
const { isTasksEntry, TASKS_FILE } = require('../common/files.js');
|
|
6
|
+
const { logViewRows } = require('../render/log.js');
|
|
7
|
+
const { setFileScope, openUnitFile, moveUnitBlock } = require('./unit.js');
|
|
8
|
+
const { LIST_PANES } = require('../input/actions.js');
|
|
9
|
+
const { saveUiPrefs, LAYOUT_LABEL } = require('./prefs.js');
|
|
13
10
|
|
|
14
11
|
const LAYOUTS = ['unified', 'mixed', 'side'];
|
|
15
12
|
|
|
@@ -31,11 +28,8 @@ const syncFileCursor = (ui) => {
|
|
|
31
28
|
|
|
32
29
|
const showFiles = (ui) => {
|
|
33
30
|
const nav = ui.nav;
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
nav.pane === 'branches' ||
|
|
37
|
-
nav.pane === 'commits' ||
|
|
38
|
-
nav.pane === 'npm';
|
|
31
|
+
const listed = nav.pane !== 'files' && LIST_PANES.includes(nav.pane);
|
|
32
|
+
const held = nav.tasksOpen === true || listed;
|
|
39
33
|
nav.pane = 'files';
|
|
40
34
|
nav.resetListScroll('files');
|
|
41
35
|
nav.clearSelection();
|
|
@@ -66,15 +60,26 @@ const showDashboard = (ui) => {
|
|
|
66
60
|
ui.dashboard.enter();
|
|
67
61
|
};
|
|
68
62
|
|
|
69
|
-
const HOME_CHILDREN = ['files', 'branches', 'commits', 'npm'];
|
|
70
|
-
|
|
71
63
|
const returnsHome = (ui) => {
|
|
72
64
|
if (!ui.dashboardHome) return false;
|
|
73
65
|
const { pane, tasksOpen } = ui.nav;
|
|
74
|
-
return
|
|
66
|
+
return LIST_PANES.includes(pane) || tasksOpen === true;
|
|
75
67
|
};
|
|
76
68
|
|
|
77
69
|
const onEscape = (ui) => {
|
|
70
|
+
if (ui.nav.pane === 'agents' && ui.agents.pick) {
|
|
71
|
+
return void ui.agents.closePick();
|
|
72
|
+
}
|
|
73
|
+
if (ui.nav.pane === 'agents' && ui.agents.viewing) {
|
|
74
|
+
return void ui.agents.closeView();
|
|
75
|
+
}
|
|
76
|
+
if (ui.nav.pane === 'npm' && ui.npm.viewing) {
|
|
77
|
+
return void ui.npm.closeView();
|
|
78
|
+
}
|
|
79
|
+
if (ui.nav.pane === 'repos') return void ui.onQuit();
|
|
80
|
+
if (ui.nav.pane === 'dashboard' && ui.workspaceRoot) {
|
|
81
|
+
return void ui.workspace.show();
|
|
82
|
+
}
|
|
78
83
|
if (ui.nav.pane === 'dashboard') return void ui.onQuit();
|
|
79
84
|
const commitDiff = ui.nav.pane === 'diff' && viewingCommit(ui);
|
|
80
85
|
if (returnsHome(ui) || (ui.dashboardHome && commitDiff)) {
|
|
@@ -101,11 +106,13 @@ const onFiles = (ui) => {
|
|
|
101
106
|
};
|
|
102
107
|
|
|
103
108
|
const onOpenFile = (ui) => {
|
|
109
|
+
if (ui.nav.pane === 'packages') return;
|
|
104
110
|
if (ui.nav.pane === 'branches') {
|
|
105
111
|
return void ui.gitBranches.onCheckoutBranch();
|
|
106
112
|
}
|
|
107
113
|
if (ui.nav.pane === 'commits') return void ui.commits.onShowDiff();
|
|
108
114
|
if (ui.nav.pane === 'npm') return void ui.npm.run();
|
|
115
|
+
if (ui.nav.pane === 'agents') return void ui.agents.start();
|
|
109
116
|
if (ui.nav.pane !== 'files') {
|
|
110
117
|
if (ui.nav.tasksOpen) ui.composer.tasks.editFocusedTask();
|
|
111
118
|
return;
|
|
@@ -138,7 +145,7 @@ const onFileMove = (ui, delta) => {
|
|
|
138
145
|
}
|
|
139
146
|
const last = list.length - 1;
|
|
140
147
|
const moved = ui.nav.fileCursor + delta;
|
|
141
|
-
const next =
|
|
148
|
+
const next = clamp(moved, 0, last);
|
|
142
149
|
if (next === ui.nav.fileCursor) return;
|
|
143
150
|
ui.nav.fileCursor = next;
|
|
144
151
|
const entry = list[next];
|
|
@@ -185,7 +192,8 @@ const scrollStep = (ui, fraction) => {
|
|
|
185
192
|
const fallback = Math.max(1, rows - 4);
|
|
186
193
|
const bodyH = frame?.bodyH ?? fallback;
|
|
187
194
|
const viewing = ui.nav.pane === 'npm' && ui.npm.viewing;
|
|
188
|
-
const
|
|
195
|
+
const agentLog = ui.nav.pane === 'agents' && ui.agents.viewing;
|
|
196
|
+
const viewRows = viewing || agentLog ? logViewRows(bodyH) : bodyH;
|
|
189
197
|
const height = Math.max(1, viewRows);
|
|
190
198
|
const span = Math.floor(height * Math.abs(fraction));
|
|
191
199
|
const n = Math.max(1, span);
|
|
@@ -194,6 +202,10 @@ const scrollStep = (ui, fraction) => {
|
|
|
194
202
|
};
|
|
195
203
|
|
|
196
204
|
const onScroll = (ui, delta) => {
|
|
205
|
+
if (ui.composer.tasks.planOpen && ui.nav.pane === 'tasks') {
|
|
206
|
+
const step = delta < 0 ? -1 : 1;
|
|
207
|
+
return void ui.composer.tasks.movePlan(step);
|
|
208
|
+
}
|
|
197
209
|
if (ui.nav.pane === 'dashboard') return;
|
|
198
210
|
if (ui.nav.pane === 'files') return void onFileMove(ui, delta);
|
|
199
211
|
if (ui.nav.pane === 'branches') {
|
|
@@ -203,6 +215,16 @@ const onScroll = (ui, delta) => {
|
|
|
203
215
|
return void ui.commits.onCommitMove(delta);
|
|
204
216
|
}
|
|
205
217
|
if (ui.nav.pane === 'npm') return void ui.npm.move(delta);
|
|
218
|
+
if (ui.nav.pane === 'agents') return void ui.agents.move(delta);
|
|
219
|
+
if (ui.nav.pane === 'packages') {
|
|
220
|
+
const count = ui.dashboard.packageCount();
|
|
221
|
+
const nav = ui.nav;
|
|
222
|
+
const next = nav.packagesCursor + delta;
|
|
223
|
+
const last = Math.max(0, count - 1);
|
|
224
|
+
nav.packagesCursor = clamp(next, 0, last);
|
|
225
|
+
ui.status = '';
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
206
228
|
if (ui.nav.tasksOpen && ui.composer.tasks.taskRowCount() > 1) {
|
|
207
229
|
return void ui.composer.tasks.moveTaskFocus(delta);
|
|
208
230
|
}
|
|
@@ -238,10 +260,22 @@ const onLayout = (ui) => {
|
|
|
238
260
|
const i = LAYOUTS.indexOf(ui.layout);
|
|
239
261
|
const next = (i < 0 ? 0 : i + 1) % LAYOUTS.length;
|
|
240
262
|
ui.layout = LAYOUTS[next];
|
|
241
|
-
|
|
242
|
-
ui.status = name;
|
|
263
|
+
ui.status = LAYOUT_LABEL[ui.layout] || ui.layout;
|
|
243
264
|
ui.nav.scroll = 0;
|
|
244
265
|
ui.nav.clearSelection();
|
|
266
|
+
saveUiPrefs(ui.top || ui.cwd, { layout: ui.layout });
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
const saveLineNumbers = (ui) => {
|
|
270
|
+
const shown = ui.lineNumbers === true;
|
|
271
|
+
saveUiPrefs(ui.top || ui.cwd, { lineNumbers: shown });
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
const onLineNumbers = (ui) => {
|
|
275
|
+
ui.lineNumbers = ui.lineNumbers !== true;
|
|
276
|
+
ui.status = ui.lineNumbers ? 'line numbers' : 'no line numbers';
|
|
277
|
+
saveLineNumbers(ui);
|
|
278
|
+
ui.composer.syncCodeScroll();
|
|
245
279
|
};
|
|
246
280
|
|
|
247
281
|
const onTheme = (ui) => {
|
|
@@ -249,12 +283,13 @@ const onTheme = (ui) => {
|
|
|
249
283
|
const next = THEME_NAMES[(i + 1) % THEME_NAMES.length];
|
|
250
284
|
setTheme(next);
|
|
251
285
|
ui.status = next;
|
|
286
|
+
saveUiPrefs(ui.top || ui.cwd, { theme: next });
|
|
252
287
|
};
|
|
253
288
|
|
|
254
289
|
const onHome = (ui) => onJump(ui, false);
|
|
255
290
|
const onEnd = (ui) => onJump(ui, true);
|
|
256
|
-
const onFileScope = (ui) => setFileScope(ui, 'file');
|
|
257
|
-
const onDiffScope = (ui) => setFileScope(ui, 'diff');
|
|
291
|
+
const onFileScope = (ui, quiet) => setFileScope(ui, 'file', quiet === true);
|
|
292
|
+
const onDiffScope = (ui, quiet) => setFileScope(ui, 'diff', quiet === true);
|
|
258
293
|
|
|
259
294
|
module.exports = {
|
|
260
295
|
fileCursorEntry,
|
|
@@ -272,6 +307,7 @@ module.exports = {
|
|
|
272
307
|
onHome,
|
|
273
308
|
onEnd,
|
|
274
309
|
onLayout,
|
|
310
|
+
onLineNumbers,
|
|
275
311
|
onTheme,
|
|
276
312
|
onFileScope,
|
|
277
313
|
onDiffScope,
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { isTasksEntry, TASKS_FILE } = require('../common/files.js');
|
|
4
|
+
const { fileMatches } = require('../common/find.js');
|
|
5
|
+
const { lineKey, lineField } = require('../input/line-edit.js');
|
|
6
|
+
|
|
7
|
+
const FIND_PANES = ['files', 'diff'];
|
|
8
|
+
|
|
9
|
+
const startFind = (ui) => {
|
|
10
|
+
if (ui.mode !== 'review') return;
|
|
11
|
+
if (!FIND_PANES.includes(ui.pane)) return;
|
|
12
|
+
ui.mode = 'find';
|
|
13
|
+
const find = lineField('query');
|
|
14
|
+
find.at = 0;
|
|
15
|
+
ui.nav.find = find;
|
|
16
|
+
ui.status = '';
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const stopFind = (ui) => {
|
|
20
|
+
ui.mode = 'review';
|
|
21
|
+
ui.nav.find = null;
|
|
22
|
+
ui.status = '';
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const focusMatch = (ui, hit, jump) => {
|
|
26
|
+
const entry = ui.fileList()[hit.index];
|
|
27
|
+
if (!entry) return;
|
|
28
|
+
const moved = ui.nav.fileCursor !== hit.index;
|
|
29
|
+
ui.nav.fileCursor = hit.index;
|
|
30
|
+
if (isTasksEntry(entry)) {
|
|
31
|
+
ui.nav.reviewPath = TASKS_FILE;
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
ui.nav.reviewPath = entry.path;
|
|
35
|
+
if (ui.nav.pane !== 'diff') {
|
|
36
|
+
ui.nav.index = entry.firstIndex;
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (!moved && !jump) return;
|
|
40
|
+
ui.nav.index = entry.openIndex ?? entry.firstIndex;
|
|
41
|
+
ui.nav.scroll = 0;
|
|
42
|
+
ui.nav.clearSelection();
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const showHit = (ui, jump) => {
|
|
46
|
+
const state = ui.nav.find;
|
|
47
|
+
if (!state) return;
|
|
48
|
+
const hits = fileMatches(ui.fileList(), state.query);
|
|
49
|
+
if (!hits.length) return;
|
|
50
|
+
const count = hits.length;
|
|
51
|
+
let at = state.at % count;
|
|
52
|
+
if (at < 0) at += count;
|
|
53
|
+
state.at = at;
|
|
54
|
+
focusMatch(ui, hits[at], jump);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const moveFind = (ui, delta) => {
|
|
58
|
+
const state = ui.nav.find;
|
|
59
|
+
if (!state.query) return;
|
|
60
|
+
state.at += delta;
|
|
61
|
+
showHit(ui, false);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const onFindKey = (ui, key) => {
|
|
65
|
+
if (!ui.nav.find) {
|
|
66
|
+
ui.mode = 'review';
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (key === 'escape' || key === 'enter') return void stopFind(ui);
|
|
70
|
+
if (key === 'backspace' && !ui.nav.find.query) return void stopFind(ui);
|
|
71
|
+
const handled = lineKey(ui.nav.find.editor, key, ui, true);
|
|
72
|
+
if (handled === 'text') {
|
|
73
|
+
ui.nav.find.at = 0;
|
|
74
|
+
showHit(ui, true);
|
|
75
|
+
}
|
|
76
|
+
if (handled) return;
|
|
77
|
+
if (key === 'up') return void moveFind(ui, -1);
|
|
78
|
+
if (key === 'down') moveFind(ui, 1);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
module.exports = { FIND_PANES, startFind, onFindKey };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { remoteKind, selectChangeSource } = require('../source/source.js');
|
|
4
|
+
const { lineKey, lineField } = require('../input/line-edit.js');
|
|
5
|
+
|
|
6
|
+
const startImport = (ui) => {
|
|
7
|
+
if (ui.mode !== 'review' || ui.pane !== 'tasks') return;
|
|
8
|
+
ui.composer.tasks.closePlanMenu();
|
|
9
|
+
ui.mode = 'import';
|
|
10
|
+
ui.nav.import = lineField('url');
|
|
11
|
+
ui.status = '';
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const stopImport = (ui) => {
|
|
15
|
+
ui.mode = 'review';
|
|
16
|
+
ui.nav.import = null;
|
|
17
|
+
ui.status = '';
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const noteImport = (ui, message) => {
|
|
21
|
+
ui.status = message;
|
|
22
|
+
if (ui.uiOpen) ui.paint();
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const finishImport = (ui, store, loaded) => {
|
|
26
|
+
const imported = loaded && loaded.imported;
|
|
27
|
+
if (!imported || ui.review.store !== store) {
|
|
28
|
+
noteImport(ui, 'import failed');
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
store.applyImportedNotes(imported);
|
|
32
|
+
ui.flushReview();
|
|
33
|
+
noteImport(ui, 'imported');
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const runImport = (ui, selected) => {
|
|
37
|
+
const kind = remoteKind(selected);
|
|
38
|
+
if (!kind) return Promise.resolve(null);
|
|
39
|
+
const load = ui[kind.option] ?? kind.load;
|
|
40
|
+
const target = kind.target(selected);
|
|
41
|
+
if (!load || !target) return Promise.resolve(null);
|
|
42
|
+
const fetch = ui.fetchImpl ?? globalThis.fetch;
|
|
43
|
+
const options = { cwd: ui.top || ui.cwd, token: kind.token(), fetch };
|
|
44
|
+
return Promise.resolve().then(() => load(target, options));
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const submitImport = (ui) => {
|
|
48
|
+
const state = ui.nav.import;
|
|
49
|
+
const url = state ? state.url.trim() : '';
|
|
50
|
+
const store = ui.review.store;
|
|
51
|
+
stopImport(ui);
|
|
52
|
+
if (!url) return;
|
|
53
|
+
const selected = selectChangeSource([url]);
|
|
54
|
+
if (!remoteKind(selected)) {
|
|
55
|
+
ui.status = 'bad url';
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
ui.status = 'importing';
|
|
59
|
+
const pending = runImport(ui, selected);
|
|
60
|
+
ui.importPromise = pending;
|
|
61
|
+
pending.then(
|
|
62
|
+
(loaded) => finishImport(ui, store, loaded),
|
|
63
|
+
(error) => {
|
|
64
|
+
const message = error instanceof Error ? error.message : '';
|
|
65
|
+
noteImport(ui, message || 'import failed');
|
|
66
|
+
},
|
|
67
|
+
);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const onImportKey = (ui, key) => {
|
|
71
|
+
const state = ui.nav.import;
|
|
72
|
+
if (!state) {
|
|
73
|
+
ui.mode = 'review';
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (key === 'escape') return void stopImport(ui);
|
|
77
|
+
if (key === 'enter') return void submitImport(ui);
|
|
78
|
+
if (key === 'backspace' && !state.url) return void stopImport(ui);
|
|
79
|
+
lineKey(state.editor, key, ui, true);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
module.exports = { startImport, stopImport, onImportKey };
|
package/lib/session/items.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
const {
|
|
4
|
+
itemPath,
|
|
5
|
+
TASKS_FILE,
|
|
6
|
+
isTaskItem,
|
|
7
|
+
groupByPath,
|
|
8
|
+
} = require('../common/files.js');
|
|
9
|
+
const { feedbackKey } = require('../review/review.js');
|
|
10
|
+
const { refreshIndexPatches } = require('../diff/diff.js');
|
|
9
11
|
|
|
10
12
|
const depChangeKey = (item) => {
|
|
11
13
|
const change = item.dep && item.dep.change;
|
|
@@ -72,17 +74,6 @@ const takeBucket = (map, key, used) => {
|
|
|
72
74
|
return null;
|
|
73
75
|
};
|
|
74
76
|
|
|
75
|
-
const groupByPath = (items) => {
|
|
76
|
-
const groups = new Map();
|
|
77
|
-
for (const item of items) {
|
|
78
|
-
const rel = itemPath(item) || '';
|
|
79
|
-
const list = groups.get(rel) ?? [];
|
|
80
|
-
list.push(item);
|
|
81
|
-
groups.set(rel, list);
|
|
82
|
-
}
|
|
83
|
-
return groups;
|
|
84
|
-
};
|
|
85
|
-
|
|
86
77
|
const alignGroup = (prev, next) => {
|
|
87
78
|
if (!prev.length) return next;
|
|
88
79
|
if (!next.length) return [];
|
package/lib/session/lifecycle.js
CHANGED
|
@@ -3,20 +3,16 @@
|
|
|
3
3
|
const fs = require('node:fs');
|
|
4
4
|
const path = require('node:path');
|
|
5
5
|
|
|
6
|
-
const
|
|
7
|
-
const {
|
|
8
|
-
const
|
|
9
|
-
const {
|
|
10
|
-
const
|
|
11
|
-
const {
|
|
12
|
-
const
|
|
13
|
-
const {
|
|
14
|
-
const
|
|
15
|
-
const {
|
|
16
|
-
const review = require('../review.js');
|
|
17
|
-
const { hasNotes, loadReview } = review;
|
|
18
|
-
const reviewMerge = require('../review-merge.js');
|
|
19
|
-
const { mergeReview } = reviewMerge;
|
|
6
|
+
const { isAbort } = require('../common/process.js');
|
|
7
|
+
const { tagLoaded, fetchSnapshot, fetchExtras } = require('./load.js');
|
|
8
|
+
const { DiskWatcher, DEBOUNCE_MS } = require('../dashboard/watch.js');
|
|
9
|
+
const { itemPath, REVIEW_DIR } = require('../common/files.js');
|
|
10
|
+
const { relativeAge } = require('../common/format.js');
|
|
11
|
+
const { MANIFEST, LOCKFILE, depFileMeta } = require('../deps/deps.js');
|
|
12
|
+
const { hasNotes, loadReview } = require('../review/review.js');
|
|
13
|
+
const { setTheme } = require('../term/ansi.js');
|
|
14
|
+
const { readUiPrefs } = require('./prefs.js');
|
|
15
|
+
const { mergeReview } = require('../review/merge.js');
|
|
20
16
|
|
|
21
17
|
const WATCH_IGNORE_MS = 500;
|
|
22
18
|
|
|
@@ -147,8 +143,9 @@ class Lifecycle {
|
|
|
147
143
|
syncUnitEditor() {
|
|
148
144
|
const ui = this.ui;
|
|
149
145
|
const rel = ui.nav.reviewPath;
|
|
150
|
-
if (ui.nav.pane !== 'unit' || !rel
|
|
151
|
-
ui.
|
|
146
|
+
if (ui.nav.pane !== 'unit' || !rel) return;
|
|
147
|
+
const text = ui.reads.cached(rel);
|
|
148
|
+
if (text !== null) ui.composer.applyDiskText(text);
|
|
152
149
|
}
|
|
153
150
|
|
|
154
151
|
syncItemWatches() {
|
|
@@ -161,28 +158,49 @@ class Lifecycle {
|
|
|
161
158
|
this.diskWatcher.watchFiles(rels);
|
|
162
159
|
}
|
|
163
160
|
|
|
161
|
+
applyPrefs() {
|
|
162
|
+
const ui = this.ui;
|
|
163
|
+
const saved = readUiPrefs(ui.top);
|
|
164
|
+
ui.lineNumbers = saved.lineNumbers;
|
|
165
|
+
ui.layout = saved.layout;
|
|
166
|
+
ui.commits.commitView = saved.commitView;
|
|
167
|
+
setTheme(saved.theme);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
applySource(loaded) {
|
|
171
|
+
const ui = this.ui;
|
|
172
|
+
if (loaded.rev) ui.rev = loaded.rev;
|
|
173
|
+
if (loaded.revShort) ui.revShort = loaded.revShort;
|
|
174
|
+
else if (ui.rev && !ui.revShort) ui.revShort = ui.rev.slice(0, 7);
|
|
175
|
+
if (loaded.sourceLabel) ui.sourceLabel = loaded.sourceLabel;
|
|
176
|
+
if (loaded.change) ui.change = loaded.change;
|
|
177
|
+
if (loaded.branch !== undefined) ui.branch = loaded.branch || '';
|
|
178
|
+
const repository = ui.change ? ui.change.repository : '';
|
|
179
|
+
if (!ui.repoName && repository) ui.repoName = repository;
|
|
180
|
+
}
|
|
181
|
+
|
|
164
182
|
applyLoaded(loaded) {
|
|
165
183
|
const ui = this.ui;
|
|
184
|
+
if (ui.nav.pane === 'repos') return false;
|
|
166
185
|
const { loader, collection, nav } = ui;
|
|
167
186
|
if (loaded.generation !== undefined) {
|
|
168
187
|
if (!loader.isCurrent(loaded.generation, ui.done)) return false;
|
|
169
188
|
}
|
|
170
189
|
const wasTasks = nav.tasksOpen === true;
|
|
190
|
+
const firstLoad = !loader.didLoad;
|
|
171
191
|
const here = ui.current();
|
|
172
192
|
ui.top = loaded.top ?? ui.cwd;
|
|
193
|
+
this.applyPrefs();
|
|
173
194
|
collection.replace(loaded.items);
|
|
174
195
|
stampItemDates(ui.top, collection.items);
|
|
175
|
-
|
|
176
|
-
if (loaded.revShort) ui.revShort = loaded.revShort;
|
|
177
|
-
else if (ui.rev && !ui.revShort) ui.revShort = ui.rev.slice(0, 7);
|
|
178
|
-
if (loaded.sourceLabel) ui.sourceLabel = loaded.sourceLabel;
|
|
179
|
-
if (loaded.change) ui.change = loaded.change;
|
|
180
|
-
if (loaded.branch !== undefined) ui.branch = loaded.branch || '';
|
|
181
|
-
const repository = ui.change ? ui.change.repository : '';
|
|
182
|
-
if (!ui.repoName && repository) ui.repoName = repository;
|
|
196
|
+
this.applySource(loaded);
|
|
183
197
|
if (!ui.review.store) ui.initReview();
|
|
184
198
|
ui.review.applyImported(loaded.imported);
|
|
185
199
|
this.restoreAfterLoad(here, wasTasks);
|
|
200
|
+
const issue = loaded.change && loaded.change.source === 'issue';
|
|
201
|
+
if (firstLoad && issue && !collection.items.length) {
|
|
202
|
+
ui.composer.tasks.openTasksPage();
|
|
203
|
+
}
|
|
186
204
|
loader.didLoad = true;
|
|
187
205
|
ui.updater.maybePrompt();
|
|
188
206
|
loader.pendingExtras = loaded.pending === true;
|
|
@@ -192,10 +210,9 @@ class Lifecycle {
|
|
|
192
210
|
return true;
|
|
193
211
|
}
|
|
194
212
|
|
|
195
|
-
load() {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
this.applyLoaded(loaded);
|
|
213
|
+
async load() {
|
|
214
|
+
await this.openLoad();
|
|
215
|
+
if (this.ui.loadError) throw this.ui.loadError;
|
|
199
216
|
}
|
|
200
217
|
|
|
201
218
|
npmStamp() {
|
|
@@ -245,13 +262,18 @@ class Lifecycle {
|
|
|
245
262
|
paths: ui.paths,
|
|
246
263
|
signal: ui.loader.signal,
|
|
247
264
|
});
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
265
|
+
const takeExtras = async () => {
|
|
266
|
+
try {
|
|
267
|
+
const value = await fetchExtras(ui.repo, loaded, extra);
|
|
268
|
+
return { value };
|
|
269
|
+
} catch (error) {
|
|
270
|
+
if (ui.loader.isCurrent(gen)) this.failOpen(error);
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
const outcome = await takeExtras();
|
|
275
|
+
if (!outcome) return;
|
|
276
|
+
const next = outcome.value;
|
|
255
277
|
if (!this.isWanted(gen)) return;
|
|
256
278
|
if (next) {
|
|
257
279
|
this.applyLoaded(tagLoaded(next, gen));
|
|
@@ -283,7 +305,7 @@ class Lifecycle {
|
|
|
283
305
|
|
|
284
306
|
afterSnapshot(kind, extra = {}) {
|
|
285
307
|
const nav = this.ui.nav;
|
|
286
|
-
if (nav.pane === 'commits') this.ui.commits.
|
|
308
|
+
if (nav.pane === 'commits') this.ui.commits.reload();
|
|
287
309
|
if (kind !== 'open' && kind !== 'watch') {
|
|
288
310
|
nav.scroll = 0;
|
|
289
311
|
nav.clearSelection();
|
|
@@ -298,15 +320,18 @@ class Lifecycle {
|
|
|
298
320
|
ui.ops.setBusy('loading');
|
|
299
321
|
ui.paint();
|
|
300
322
|
}
|
|
301
|
-
let loaded;
|
|
323
|
+
let loaded = null;
|
|
324
|
+
const side = ui.reads.read();
|
|
302
325
|
try {
|
|
303
326
|
const opts = this.snapshotOpts({ signal: ui.loader.signal });
|
|
304
327
|
loaded = await fetchSnapshot(ui.repo, ui.cwd, ui.paths, opts);
|
|
305
328
|
} catch (error) {
|
|
306
329
|
if (ui.loader.isCurrent(gen)) this.failOpen(error);
|
|
307
|
-
return;
|
|
308
330
|
}
|
|
331
|
+
if (!loaded) return;
|
|
332
|
+
const found = await side;
|
|
309
333
|
if (!this.isWanted(gen)) return;
|
|
334
|
+
ui.reads.apply(found);
|
|
310
335
|
this.applyLoaded(tagLoaded(loaded, gen));
|
|
311
336
|
this.afterSnapshot(kind, extra);
|
|
312
337
|
if (loaded.pending === true || this.npmExtrasStale()) {
|
|
@@ -325,42 +350,35 @@ class Lifecycle {
|
|
|
325
350
|
|
|
326
351
|
openLoad() {
|
|
327
352
|
const loader = this.ui.loader;
|
|
353
|
+
if (this.ui.nav.pane === 'repos') {
|
|
354
|
+
loader.didLoad = true;
|
|
355
|
+
loader.promise = Promise.resolve();
|
|
356
|
+
return loader.promise;
|
|
357
|
+
}
|
|
328
358
|
if (loader.promise) return loader.promise;
|
|
329
359
|
if (loader.didLoad && !loader.pendingExtras) {
|
|
330
360
|
loader.promise = Promise.resolve();
|
|
331
361
|
} else {
|
|
332
|
-
|
|
362
|
+
const run = this.runSnapshot(loader.bump(), 'open');
|
|
363
|
+
loader.promise = this.ui.ops.track(run);
|
|
333
364
|
}
|
|
334
365
|
return loader.promise;
|
|
335
366
|
}
|
|
336
367
|
|
|
337
368
|
async loadReady() {
|
|
338
|
-
this.ui.
|
|
339
|
-
|
|
340
|
-
|
|
369
|
+
if (this.ui.nav.pane === 'repos') {
|
|
370
|
+
this.ui.loader.didLoad = true;
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
await this.ui.ensureRepo();
|
|
374
|
+
await this.load();
|
|
341
375
|
}
|
|
342
376
|
|
|
343
377
|
refreshFromRepo(extra = {}) {
|
|
344
378
|
const ui = this.ui;
|
|
345
379
|
ui.loader.promise = null;
|
|
346
380
|
const gen = ui.loader.bump();
|
|
347
|
-
|
|
348
|
-
if (ui.uiOpen && ui.repo.loadAsync) {
|
|
349
|
-
return void this.runSnapshot(gen, kind, extra);
|
|
350
|
-
}
|
|
351
|
-
const loaded = ui.repo.load(ui.cwd, ui.paths, this.snapshotOpts(extra));
|
|
352
|
-
this.applyLoaded(tagLoaded(loaded, gen));
|
|
353
|
-
this.afterSnapshot(kind, extra);
|
|
354
|
-
if (loaded.pending === true || this.npmExtrasStale()) {
|
|
355
|
-
ui.ops.setBusy('checking npm');
|
|
356
|
-
return void this.followExtras(loaded, gen, kind);
|
|
357
|
-
}
|
|
358
|
-
if (kind === 'keep') return;
|
|
359
|
-
this.finishIfEmpty(kind);
|
|
360
|
-
if (extra.doneStatus && !ui.done && !ui.loader.pendingExtras) {
|
|
361
|
-
ui.status = extra.doneStatus;
|
|
362
|
-
}
|
|
363
|
-
if (kind === 'watch') ui.paint();
|
|
381
|
+
ui.ops.track(this.runSnapshot(gen, kindFromExtra(extra), extra));
|
|
364
382
|
}
|
|
365
383
|
|
|
366
384
|
reloadAfterChange(afterLoad) {
|
|
@@ -375,11 +393,7 @@ class Lifecycle {
|
|
|
375
393
|
ui.syncFileCursor();
|
|
376
394
|
if (afterLoad) afterLoad();
|
|
377
395
|
};
|
|
378
|
-
|
|
379
|
-
return void this.refreshFromRepo({ afterLoad: restore });
|
|
380
|
-
}
|
|
381
|
-
this.refreshFromRepo();
|
|
382
|
-
restore();
|
|
396
|
+
this.refreshFromRepo({ afterLoad: restore });
|
|
383
397
|
}
|
|
384
398
|
|
|
385
399
|
onReload() {
|
|
@@ -407,6 +421,7 @@ class Lifecycle {
|
|
|
407
421
|
}
|
|
408
422
|
|
|
409
423
|
onDiskChange() {
|
|
424
|
+
if (this.ui.nav.pane === 'repos') return;
|
|
410
425
|
if (Date.now() < this.ignoreWatchUntil) return;
|
|
411
426
|
if (!this.shouldDeferWatch()) {
|
|
412
427
|
return void this.refreshFromRepo({ keepView: true, quiet: true });
|
|
@@ -444,12 +459,13 @@ class Lifecycle {
|
|
|
444
459
|
return;
|
|
445
460
|
}
|
|
446
461
|
const store = this.ui.review.store;
|
|
447
|
-
let disk;
|
|
462
|
+
let disk = null;
|
|
448
463
|
try {
|
|
449
464
|
disk = loadReview(store.reviewPath, store.templates);
|
|
450
465
|
} catch {
|
|
451
|
-
|
|
466
|
+
// unreadable review
|
|
452
467
|
}
|
|
468
|
+
if (!disk) return;
|
|
453
469
|
mergeReview(store, disk);
|
|
454
470
|
this.reviewStamp = stamp;
|
|
455
471
|
this.ui.paint();
|
|
@@ -465,6 +481,20 @@ class Lifecycle {
|
|
|
465
481
|
|
|
466
482
|
startWatch() {
|
|
467
483
|
this.stopWatch();
|
|
484
|
+
const ui = this.ui;
|
|
485
|
+
if (ui.nav.pane === 'repos') {
|
|
486
|
+
const root = ui.workspaceRoot;
|
|
487
|
+
if (!root) return;
|
|
488
|
+
const info = ui.workspaceInfo;
|
|
489
|
+
const repos = info ? info.repos : [];
|
|
490
|
+
const paths = ['.'];
|
|
491
|
+
for (const repo of repos) paths.push(path.join(repo.name, '.git'));
|
|
492
|
+
const matchReview = (rel) => rel.includes(`/${REVIEW_DIR}/`);
|
|
493
|
+
const onPaths = (rels) => ui.workspace.notePaths(rels);
|
|
494
|
+
const options = { root, paths, matchReview, onPaths };
|
|
495
|
+
this.diskWatcher = new DiskWatcher(options);
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
468
498
|
const root = this.root;
|
|
469
499
|
if (!isWorktree(this.ui) || !root) return;
|
|
470
500
|
this.diskWatcher = new DiskWatcher({
|
package/lib/session/load.js
CHANGED
|
@@ -40,10 +40,8 @@ class LoadCoordinator {
|
|
|
40
40
|
|
|
41
41
|
const tagLoaded = (loaded, gen) => ({ ...loaded, generation: gen });
|
|
42
42
|
|
|
43
|
-
const fetchSnapshot = async (repo, cwd, paths, options) =>
|
|
44
|
-
|
|
45
|
-
return repo.load(cwd, paths, options);
|
|
46
|
-
};
|
|
43
|
+
const fetchSnapshot = async (repo, cwd, paths, options) =>
|
|
44
|
+
repo.load(cwd, paths, options);
|
|
47
45
|
|
|
48
46
|
const fetchExtras = async (repo, loaded, extra) => {
|
|
49
47
|
if (!repo.loadExtras) return null;
|