reslop 0.2.7 → 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 +17 -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 +43 -68
- package/lib/{files.js → common/files.js} +48 -55
- package/lib/{find.js → common/find.js} +1 -1
- 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 +90 -49
- package/lib/dashboard/npm.js +17 -13
- package/lib/dashboard/tree.js +27 -30
- package/lib/{session → dashboard}/watch.js +36 -31
- package/lib/{workspace.js → dashboard/workspace.js} +23 -38
- 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 -96
- 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 +111 -48
- package/lib/{editor.js → input/editor.js} +7 -8
- package/lib/{keys.js → input/keys.js} +15 -43
- 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/{dash-blocks.js → dashboard/blocks.js} +131 -137
- 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 +9 -14
- package/lib/render/footer.js +383 -0
- package/lib/render/frame.js +123 -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 +16 -11
- package/lib/render/npm.js +322 -196
- package/lib/render/packages.js +9 -13
- package/lib/render/primitives.js +101 -15
- package/lib/render/render.js +7 -11
- package/lib/render/repos.js +13 -25
- package/lib/render/status.js +370 -0
- package/lib/render/status.json +85 -0
- package/lib/render/{dash-table.js → table.js} +13 -60
- package/lib/render/tiles.js +109 -48
- 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 +33 -39
- package/lib/session/change-actions.js +34 -100
- package/lib/session/commit.js +91 -52
- package/lib/session/compose.js +181 -145
- package/lib/session/dashboard.js +23 -22
- package/lib/session/dispatch.js +85 -21
- package/lib/session/files-pane.js +47 -26
- package/lib/session/find.js +16 -29
- package/lib/session/import.js +82 -0
- package/lib/session/items.js +8 -17
- package/lib/session/lifecycle.js +75 -72
- package/lib/session/load.js +2 -4
- package/lib/session/navigation.js +6 -3
- package/lib/session/npm/history.js +249 -0
- package/lib/session/npm/npm.js +815 -0
- package/lib/session/{packages.js → npm/packages.js} +21 -14
- package/lib/session/ops.js +125 -34
- package/lib/session/pointer.js +202 -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} +156 -63
- package/lib/session/tasks.js +320 -65
- 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 +65 -65
- package/lib/session/workspace.js +39 -78
- 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 -545
- package/lib/detect.js +0 -34
- package/lib/diff/edit.js +0 -33
- package/lib/git-worktree.js +0 -455
- package/lib/git.js +0 -79
- package/lib/npm-commands.js +0 -522
- package/lib/render/chrome.js +0 -806
- package/lib/render/present.js +0 -19
- package/lib/render/report.js +0 -43
- package/lib/render/search.js +0 -25
- package/lib/review.js +0 -745
- 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
package/lib/session/compose.js
CHANGED
|
@@ -1,30 +1,24 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const { cursorInWrap, wrapDoc } = wrap;
|
|
3
|
+
const { cursorInWrap, wrapDoc } = require('../term/wrap.js');
|
|
5
4
|
const render = require('../render/render.js');
|
|
6
|
-
const { noteInnerWidth, codeInnerWidth, todoBodyWidth } = render;
|
|
7
|
-
const
|
|
8
|
-
const {
|
|
9
|
-
const
|
|
10
|
-
const {
|
|
11
|
-
const
|
|
12
|
-
const { Editor } = editor;
|
|
13
|
-
const files = require('../files.js');
|
|
5
|
+
const { noteInnerWidth, codeInnerWidth, todoBodyWidth, activeDigits } = render;
|
|
6
|
+
const { commitMessageWidth } = require('../render/commits.js');
|
|
7
|
+
const { listedCommits } = require('./commit.js');
|
|
8
|
+
const { Editor } = require('../input/editor.js');
|
|
9
|
+
const { isTextKey } = require('../input/keys.js');
|
|
10
|
+
const files = require('../common/files.js');
|
|
14
11
|
const { itemPath, isTaskItem, isTasksEntry, listPath } = files;
|
|
15
12
|
const { isReadOnlyOrigin } = files;
|
|
16
|
-
const
|
|
17
|
-
const {
|
|
18
|
-
const
|
|
19
|
-
const {
|
|
20
|
-
const
|
|
21
|
-
const {
|
|
22
|
-
const templates = require('./templates.js');
|
|
23
|
-
const { TemplatePick } = templates;
|
|
24
|
-
const tasks = require('./tasks.js');
|
|
25
|
-
const { TaskPage } = tasks;
|
|
13
|
+
const { blockAddText } = require('../diff/diff.js');
|
|
14
|
+
const { itemFeedKey, canEditCode } = require('./items.js');
|
|
15
|
+
const review = require('../review/review.js');
|
|
16
|
+
const { checkLabel } = review;
|
|
17
|
+
const { TemplatePick } = require('./templates.js');
|
|
18
|
+
const { TaskPage } = require('./tasks.js');
|
|
26
19
|
const unit = require('./unit.js');
|
|
27
|
-
const clipboard = require('../clipboard.js');
|
|
20
|
+
const clipboard = require('../term/clipboard.js');
|
|
21
|
+
const { EDIT_OP, copyLine, cutLine } = require('../input/line-edit.js');
|
|
28
22
|
|
|
29
23
|
const COMPOSE_EDITS = ['backspace', 'delete', 'ctrl-z', 'ctrl-y'];
|
|
30
24
|
const COMPOSE_CLIP = ['ctrl-x', 'ctrl-v'];
|
|
@@ -78,17 +72,13 @@ const blankTail = (text) => {
|
|
|
78
72
|
return count;
|
|
79
73
|
};
|
|
80
74
|
|
|
75
|
+
const EDITOR_KEYS = {};
|
|
76
|
+
for (const key of Object.keys(EDIT_OP)) {
|
|
77
|
+
EDITOR_KEYS[key] = (c) => EDIT_OP[key](c.editor);
|
|
78
|
+
}
|
|
79
|
+
|
|
81
80
|
const COMPOSE_SPECIAL = {
|
|
82
|
-
|
|
83
|
-
delete: (c) => c.editor.delete(),
|
|
84
|
-
left: (c) => c.editor.move(-1),
|
|
85
|
-
right: (c) => c.editor.move(1),
|
|
86
|
-
'shift-left': (c) => c.editor.move(-1, true),
|
|
87
|
-
'shift-right': (c) => c.editor.move(1, true),
|
|
88
|
-
'ctrl-left': (c) => c.editor.moveWord(-1),
|
|
89
|
-
'ctrl-right': (c) => c.editor.moveWord(1),
|
|
90
|
-
'ctrl-shift-left': (c) => c.editor.moveWord(-1, true),
|
|
91
|
-
'ctrl-shift-right': (c) => c.editor.moveWord(1, true),
|
|
81
|
+
...EDITOR_KEYS,
|
|
92
82
|
up: (c) => c.moveLine(-1),
|
|
93
83
|
down: (c) => c.moveLine(1),
|
|
94
84
|
'shift-up': (c) => c.moveLine(-1, true),
|
|
@@ -118,7 +108,9 @@ class Composer {
|
|
|
118
108
|
}
|
|
119
109
|
|
|
120
110
|
reset() {
|
|
111
|
+
this.tasks.closePlanMenu();
|
|
121
112
|
this.fileDirty = false;
|
|
113
|
+
this.saving = false;
|
|
122
114
|
this.clear();
|
|
123
115
|
}
|
|
124
116
|
|
|
@@ -128,6 +120,7 @@ class Composer {
|
|
|
128
120
|
this.commitKind = null;
|
|
129
121
|
this.commitBody = '';
|
|
130
122
|
this.composeTaskId = null;
|
|
123
|
+
this.composeQuoteKey = null;
|
|
131
124
|
this.composeBaseline = '';
|
|
132
125
|
this.templateIndex = -1;
|
|
133
126
|
this.templateFocus = false;
|
|
@@ -157,7 +150,8 @@ class Composer {
|
|
|
157
150
|
if (kind !== 'code' && kind !== 'file') return;
|
|
158
151
|
if (!this.editor) return;
|
|
159
152
|
const width = this.viewSize().width;
|
|
160
|
-
|
|
153
|
+
const digits = activeDigits(this.ui);
|
|
154
|
+
this.editor.reveal(codeInnerWidth(width, this.ui.layout, digits));
|
|
161
155
|
}
|
|
162
156
|
|
|
163
157
|
followFileEdit() {
|
|
@@ -173,6 +167,7 @@ class Composer {
|
|
|
173
167
|
this.ui.mode = 'compose';
|
|
174
168
|
this.composeKind = kind;
|
|
175
169
|
this.composeTaskId = taskId;
|
|
170
|
+
this.composeQuoteKey = null;
|
|
176
171
|
this.composeBaseline = text;
|
|
177
172
|
this.fileOpened = kind === 'file' ? text : '';
|
|
178
173
|
this.editor = new Editor(text);
|
|
@@ -182,6 +177,11 @@ class Composer {
|
|
|
182
177
|
this.ui.nav.clearSelection();
|
|
183
178
|
}
|
|
184
179
|
|
|
180
|
+
openQuote(key, text) {
|
|
181
|
+
this.openCompose('tasks', text, null);
|
|
182
|
+
this.composeQuoteKey = key;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
185
|
closeCompose() {
|
|
186
186
|
const ui = this.ui;
|
|
187
187
|
ui.mode = 'review';
|
|
@@ -205,7 +205,7 @@ class Composer {
|
|
|
205
205
|
commitFeedback(text) {
|
|
206
206
|
const item = this.ui.current();
|
|
207
207
|
if (!item || isTaskItem(item)) return;
|
|
208
|
-
|
|
208
|
+
this.notes.setFeedback(itemFeedKey(item), itemNote(item, text));
|
|
209
209
|
}
|
|
210
210
|
|
|
211
211
|
dropCodeNote(item, text) {
|
|
@@ -213,34 +213,52 @@ class Composer {
|
|
|
213
213
|
if (!notes) return;
|
|
214
214
|
const key = itemFeedKey(item);
|
|
215
215
|
if (!notes.code.has(key)) return;
|
|
216
|
-
setCode(
|
|
216
|
+
notes.setCode(key, { ...itemNote(item, text), clear: true });
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Edits that land in the worktree or index, not in the review notes
|
|
220
|
+
writesDisk() {
|
|
221
|
+
const kind = this.composeKind;
|
|
222
|
+
if (!this.editor) return false;
|
|
223
|
+
if (kind === 'file') return true;
|
|
224
|
+
return kind === 'code' && this.writesCodeFile(this.ui.current());
|
|
217
225
|
}
|
|
218
226
|
|
|
219
227
|
commitCode(text) {
|
|
228
|
+
const item = this.ui.current();
|
|
229
|
+
if (!canEditCode(item) || this.writesCodeFile(item)) return;
|
|
230
|
+
const note = itemNote(item, text);
|
|
231
|
+
const clear = text === blockAddText(item.hunk, item.blockId);
|
|
232
|
+
this.notes.setCode(itemFeedKey(item), clear ? { ...note, clear } : note);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
async write(action) {
|
|
220
236
|
const ui = this.ui;
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
setCode(this.notes, itemFeedKey(item), clear ? { ...note, clear } : note);
|
|
228
|
-
return true;
|
|
237
|
+
ui.ignoreWatch();
|
|
238
|
+
try {
|
|
239
|
+
await ui.ops.exclusive(action);
|
|
240
|
+
} catch (error) {
|
|
241
|
+
ui.status = error.message;
|
|
242
|
+
return false;
|
|
229
243
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
244
|
+
ui.ignoreWatch();
|
|
245
|
+
return true;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
async writeCode(text) {
|
|
249
|
+
const ui = this.ui;
|
|
250
|
+
const item = ui.current();
|
|
251
|
+
if (text !== blockAddText(item.hunk, item.blockId)) {
|
|
252
|
+
const edit = (top) => ui.repo.edit(top, item, text);
|
|
253
|
+
const written = await this.write(edit);
|
|
254
|
+
if (!written) return false;
|
|
237
255
|
this.fileDirty = true;
|
|
238
256
|
}
|
|
239
257
|
this.dropCodeNote(item, text);
|
|
240
258
|
return true;
|
|
241
259
|
}
|
|
242
260
|
|
|
243
|
-
saveUnitFile(text) {
|
|
261
|
+
async saveUnitFile(text) {
|
|
244
262
|
const ui = this.ui;
|
|
245
263
|
const rel = ui.nav.reviewPath;
|
|
246
264
|
if (!rel || !ui.repo.writeFile) return true;
|
|
@@ -248,48 +266,37 @@ class Composer {
|
|
|
248
266
|
ui.status = 'read only';
|
|
249
267
|
return false;
|
|
250
268
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
return false;
|
|
256
|
-
}
|
|
257
|
-
ui.ignoreWatch();
|
|
269
|
+
const save = (top) => ui.repo.writeFile(top, rel, text);
|
|
270
|
+
const written = await this.write(save);
|
|
271
|
+
if (!written) return false;
|
|
272
|
+
ui.reads.keepText(rel, text);
|
|
258
273
|
this.composeBaseline = text;
|
|
259
274
|
this.fileDirty = true;
|
|
260
275
|
return true;
|
|
261
276
|
}
|
|
262
277
|
|
|
263
|
-
|
|
278
|
+
stageItems(rel) {
|
|
264
279
|
const ui = this.ui;
|
|
265
280
|
const repo = ui.repo;
|
|
266
|
-
if (!
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
ui.status = error.message;
|
|
286
|
-
return staged > 0;
|
|
287
|
-
}
|
|
288
|
-
ui.collection.keepOrigin(item, 'staged');
|
|
289
|
-
staged += 1;
|
|
290
|
-
}
|
|
291
|
-
if (staged) ui.ignoreWatch();
|
|
292
|
-
return staged > 0;
|
|
281
|
+
if (!repo.add) return;
|
|
282
|
+
const targets = ui.items.filter((item) => {
|
|
283
|
+
if (listPath(item) !== rel || isReadOnlyOrigin(item.origin)) return false;
|
|
284
|
+
return item.origin !== 'staged';
|
|
285
|
+
});
|
|
286
|
+
if (!targets.length) return;
|
|
287
|
+
for (const item of targets) ui.collection.keepOrigin(item, 'staged');
|
|
288
|
+
ui.ignoreWatch();
|
|
289
|
+
const write = async () => {
|
|
290
|
+
for (const item of targets) await ui.ops.add(item);
|
|
291
|
+
};
|
|
292
|
+
ui.ops.enqueueLazy({ kind: 'staged', done: '', reload: false, write });
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
async stageEditedFile(rel) {
|
|
296
|
+
const repo = this.ui.repo;
|
|
297
|
+
if (!rel || !this.canChange()) return;
|
|
298
|
+
if (!repo.stagePath) return void this.stageItems(rel);
|
|
299
|
+
await this.write((top) => repo.stagePath(top, rel));
|
|
293
300
|
}
|
|
294
301
|
|
|
295
302
|
finishSave(reload, doneStatus) {
|
|
@@ -300,84 +307,112 @@ class Composer {
|
|
|
300
307
|
if (!ui.status) ui.status = doneStatus;
|
|
301
308
|
return;
|
|
302
309
|
}
|
|
310
|
+
this.fileDirty = false;
|
|
303
311
|
ui.reloadAfterChange(() => {
|
|
304
312
|
if (!ui.status) ui.status = doneStatus;
|
|
305
313
|
});
|
|
306
314
|
}
|
|
307
315
|
|
|
308
|
-
|
|
316
|
+
// Closes the editor after a note or a block edit; reloads if a file changed
|
|
317
|
+
finishNote(doneStatus) {
|
|
318
|
+
const ui = this.ui;
|
|
319
|
+
if (this.fileDirty === true) return void this.finishSave(true, doneStatus);
|
|
320
|
+
ui.flushReview();
|
|
321
|
+
this.closeCompose();
|
|
322
|
+
ui.status = doneStatus;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
async saveFile() {
|
|
309
326
|
const rel = this.ui.nav.reviewPath;
|
|
310
327
|
const opened = this.fileOpened;
|
|
311
|
-
const text = this.editor
|
|
312
|
-
|
|
313
|
-
|
|
328
|
+
const text = this.editor.text;
|
|
329
|
+
this.ui.nav.unitLine = this.editor.linePos().line;
|
|
330
|
+
const saved = await this.saveUnitFile(text);
|
|
331
|
+
if (!saved) return false;
|
|
314
332
|
const changed = text !== opened;
|
|
315
333
|
const reload = this.fileDirty === true;
|
|
316
|
-
if (changed) this.stageEditedFile(rel);
|
|
334
|
+
if (changed) await this.stageEditedFile(rel);
|
|
317
335
|
this.finishSave(reload, changed ? 'staged' : 'saved');
|
|
318
|
-
return
|
|
336
|
+
return true;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
async saveCode() {
|
|
340
|
+
const ui = this.ui;
|
|
341
|
+
const item = ui.current();
|
|
342
|
+
const unstaged = item.origin !== 'staged';
|
|
343
|
+
const rel = itemPath(item);
|
|
344
|
+
const saved = await this.writeCode(this.editor.text);
|
|
345
|
+
if (!saved) return false;
|
|
346
|
+
const staging = unstaged && this.fileDirty === true;
|
|
347
|
+
if (staging) await this.stageEditedFile(rel);
|
|
348
|
+
this.finishNote(staging ? 'staged' : 'saved');
|
|
349
|
+
return true;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
saveEdit() {
|
|
353
|
+
return this.composeKind === 'file' ? this.saveFile() : this.saveCode();
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// One save at a time; `after` runs once the edit is safely on disk
|
|
357
|
+
async saveToDisk(after) {
|
|
358
|
+
if (this.saving) return;
|
|
359
|
+
this.saving = true;
|
|
360
|
+
const settle = () => {
|
|
361
|
+
this.saving = false;
|
|
362
|
+
};
|
|
363
|
+
const saved = await this.saveEdit().finally(settle);
|
|
364
|
+
if (saved && after) after();
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Leaves the editor whether or not the edit could be written
|
|
368
|
+
async writeAndClose() {
|
|
369
|
+
const text = this.editor.text;
|
|
370
|
+
if (this.composeKind === 'file') await this.saveUnitFile(text);
|
|
371
|
+
else await this.writeCode(text);
|
|
372
|
+
this.closeCompose();
|
|
319
373
|
}
|
|
320
374
|
|
|
321
375
|
commitCompose() {
|
|
322
376
|
const kind = this.composeKind;
|
|
323
|
-
if (!kind || !this.editor) return
|
|
377
|
+
if (!kind || !this.editor) return;
|
|
324
378
|
const text = this.editor.text;
|
|
325
|
-
if (kind === 'feedback')
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
}
|
|
329
|
-
if (kind === 'tasks') {
|
|
330
|
-
this.tasks.commitTask(text);
|
|
331
|
-
return true;
|
|
332
|
-
}
|
|
333
|
-
if (kind === 'code') return this.commitCode(text);
|
|
334
|
-
if (kind === 'file') return this.saveUnitFile(text);
|
|
335
|
-
return true;
|
|
379
|
+
if (kind === 'feedback' && this.notes) this.commitFeedback(text);
|
|
380
|
+
if (kind === 'tasks') this.tasks.commitTask(text);
|
|
381
|
+
if (kind === 'code') this.commitCode(text);
|
|
336
382
|
}
|
|
337
383
|
|
|
384
|
+
// Notes save at once. An edit to a file answers `saving` and finishes later
|
|
338
385
|
saveCompose(opts = {}) {
|
|
339
386
|
const ui = this.ui;
|
|
340
387
|
const kind = this.composeKind;
|
|
341
388
|
if (COMMAND_KINDS.includes(kind)) return { kind };
|
|
342
|
-
if (
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
const rel = itemPath(item);
|
|
389
|
+
if (this.writesDisk()) {
|
|
390
|
+
ui.ops.hold(this.saveToDisk(opts.after));
|
|
391
|
+
return { kind: 'saving' };
|
|
392
|
+
}
|
|
347
393
|
const baseline = this.composeBaseline;
|
|
348
|
-
|
|
349
|
-
const reload = this.fileDirty === true;
|
|
394
|
+
this.commitCompose();
|
|
350
395
|
if (kind === 'feedback' && this.notes) {
|
|
351
396
|
const next = this.editor ? this.editor.text : '';
|
|
352
|
-
|
|
353
|
-
}
|
|
354
|
-
const staging = shouldStage && reload;
|
|
355
|
-
if (staging) this.stageEditedFile(rel);
|
|
356
|
-
const doneStatus = staging ? 'staged' : 'saved';
|
|
357
|
-
if (reload) {
|
|
358
|
-
this.finishSave(true, doneStatus);
|
|
359
|
-
} else {
|
|
360
|
-
ui.flushReview();
|
|
361
|
-
this.closeCompose();
|
|
362
|
-
ui.status = doneStatus;
|
|
397
|
+
this.notes.rememberTemplate(baseline, next);
|
|
363
398
|
}
|
|
399
|
+
this.finishNote('saved');
|
|
364
400
|
if (opts.editNext === true) this.tasks.editNextTask();
|
|
365
401
|
return { kind: 'saved' };
|
|
366
402
|
}
|
|
367
403
|
|
|
404
|
+
autosaveFile(text) {
|
|
405
|
+
if (this.saving || text === this.composeBaseline) return;
|
|
406
|
+
this.ui.ops.track(this.saveUnitFile(text));
|
|
407
|
+
}
|
|
408
|
+
|
|
368
409
|
autosave() {
|
|
369
410
|
const kind = this.composeKind;
|
|
370
411
|
const text = this.editor ? this.editor.text : '';
|
|
371
|
-
if (kind === 'file' && this.editor)
|
|
372
|
-
if (text === this.composeBaseline) return;
|
|
373
|
-
return void this.saveUnitFile(text);
|
|
374
|
-
}
|
|
375
|
-
const editingCode = kind === 'code';
|
|
412
|
+
if (kind === 'file' && this.editor) return void this.autosaveFile(text);
|
|
376
413
|
const reviewKind = kind !== null && !COMMAND_KINDS.includes(kind);
|
|
377
|
-
const
|
|
378
|
-
if (
|
|
379
|
-
this.commitCompose();
|
|
380
|
-
}
|
|
414
|
+
const noted = reviewKind && !this.writesDisk();
|
|
415
|
+
if (noted && (kind === 'code' || text.trim())) this.commitCompose();
|
|
381
416
|
if (this.notes && this.notes.dirty) this.ui.flushReview();
|
|
382
417
|
}
|
|
383
418
|
|
|
@@ -538,6 +573,12 @@ class Composer {
|
|
|
538
573
|
ui.status = 'read only';
|
|
539
574
|
return;
|
|
540
575
|
}
|
|
576
|
+
ui.ops.hold(this.editViewed());
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
async editViewed() {
|
|
580
|
+
const ui = this.ui;
|
|
581
|
+
await ui.reads.loadText(ui.nav.reviewPath);
|
|
541
582
|
const at = unit.editCursor(ui);
|
|
542
583
|
this.openCompose('file', at.text);
|
|
543
584
|
this.fileEditRows = at.rows ?? null;
|
|
@@ -554,8 +595,8 @@ class Composer {
|
|
|
554
595
|
ui.status = 'not a diff block';
|
|
555
596
|
return;
|
|
556
597
|
}
|
|
557
|
-
|
|
558
|
-
|
|
598
|
+
const edit = () => this.onUnitEdit();
|
|
599
|
+
ui.ops.hold(unit.showFile(ui, entry).then(edit));
|
|
559
600
|
}
|
|
560
601
|
|
|
561
602
|
onCode() {
|
|
@@ -563,7 +604,7 @@ class Composer {
|
|
|
563
604
|
if (ui.nav.pane === 'files') return void this.onFileEdit();
|
|
564
605
|
if (ui.nav.pane === 'unit') return void this.onUnitEdit();
|
|
565
606
|
const item = ui.current();
|
|
566
|
-
if (
|
|
607
|
+
if (!canEditCode(item)) {
|
|
567
608
|
ui.status = 'not a diff block';
|
|
568
609
|
return;
|
|
569
610
|
}
|
|
@@ -609,16 +650,11 @@ class Composer {
|
|
|
609
650
|
}
|
|
610
651
|
|
|
611
652
|
copy() {
|
|
612
|
-
|
|
613
|
-
if (!text) return;
|
|
614
|
-
const ok = clipboard.copyText(text, this.ui.stdout);
|
|
615
|
-
this.ui.status = ok ? 'copied' : 'copy failed';
|
|
653
|
+
copyLine(this.editor, this.ui);
|
|
616
654
|
}
|
|
617
655
|
|
|
618
656
|
cut() {
|
|
619
|
-
|
|
620
|
-
this.copy();
|
|
621
|
-
this.editor.removeSpan();
|
|
657
|
+
cutLine(this.editor, this.ui);
|
|
622
658
|
}
|
|
623
659
|
|
|
624
660
|
paste() {
|
|
@@ -646,10 +682,7 @@ class Composer {
|
|
|
646
682
|
this.afterEdit();
|
|
647
683
|
return result ?? null;
|
|
648
684
|
}
|
|
649
|
-
if (
|
|
650
|
-
return null;
|
|
651
|
-
}
|
|
652
|
-
if (key.length === 1 && key.charCodeAt(0) >= 32) {
|
|
685
|
+
if (isTextKey(key)) {
|
|
653
686
|
this.templates.clearTemplatePick();
|
|
654
687
|
this.editor.insert(key);
|
|
655
688
|
this.afterEdit();
|
|
@@ -660,12 +693,15 @@ class Composer {
|
|
|
660
693
|
applyDiskText(text) {
|
|
661
694
|
const editor = this.editor;
|
|
662
695
|
if (this.composeKind !== 'file' || !editor) return;
|
|
663
|
-
|
|
696
|
+
// The baseline is the last text known on disk: no news, keep the typing
|
|
697
|
+
if (text === editor.text || text === this.composeBaseline) return;
|
|
664
698
|
const cursor = editor.cursor;
|
|
665
699
|
editor.text = text;
|
|
666
700
|
editor.cursor = Math.min(cursor, text.length);
|
|
667
701
|
editor.anchor = null;
|
|
668
702
|
this.composeBaseline = text;
|
|
703
|
+
// An outside change is the new starting point, not an edit made here
|
|
704
|
+
this.fileOpened = text;
|
|
669
705
|
this.fileEditRows = unit.linesFor(this.ui, this.ui.nav.reviewPath, null);
|
|
670
706
|
this.syncCodeScroll();
|
|
671
707
|
this.followFileEdit();
|
package/lib/session/dashboard.js
CHANGED
|
@@ -1,22 +1,15 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const {
|
|
5
|
-
const
|
|
6
|
-
const {
|
|
7
|
-
const
|
|
8
|
-
const {
|
|
9
|
-
const tree = require('../dashboard/tree.js');
|
|
10
|
-
const { FileIndex, HEAT_MS } = tree;
|
|
11
|
-
const gitInfo = require('../dashboard/git.js');
|
|
12
|
-
const { readGitSummary } = gitInfo;
|
|
13
|
-
const npmInfo = require('../dashboard/npm.js');
|
|
14
|
-
const { readNpmSummary } = npmInfo;
|
|
3
|
+
const { fileEntries, fileTotals } = require('../common/files.js');
|
|
4
|
+
const { noteCounts } = require('../review/review.js');
|
|
5
|
+
const { readRuns, settleRecord, isRunsRel } = require('../runs/runs.js');
|
|
6
|
+
const { FileIndex, HEAT_MS } = require('../dashboard/tree.js');
|
|
7
|
+
const { readGitSummary } = require('../dashboard/git.js');
|
|
8
|
+
const { readNpmSummary } = require('../dashboard/npm.js');
|
|
15
9
|
const model = require('../dashboard/model.js');
|
|
16
10
|
const { buildModel, minuteOf, MINUTE_MS, npmModel } = model;
|
|
17
11
|
const filesPane = require('./files-pane.js');
|
|
18
|
-
const
|
|
19
|
-
const { UNKNOWN_PATH, MODULES_STAMP } = watch;
|
|
12
|
+
const { UNKNOWN_PATH, MODULES_STAMP } = require('../dashboard/watch.js');
|
|
20
13
|
|
|
21
14
|
const TICK_ID = 'dashboard';
|
|
22
15
|
const HEAT_TIMER = 'dashboard-heat';
|
|
@@ -27,7 +20,7 @@ const BLINK_MS = 3000;
|
|
|
27
20
|
const MINUTE_KEEP = 360;
|
|
28
21
|
const SWITCHES = 4;
|
|
29
22
|
const RUN_MARK = { failed: 'fail', passed: 'pass', aborted: 'abort' };
|
|
30
|
-
const GIT_NAMES =
|
|
23
|
+
const GIT_NAMES = [
|
|
31
24
|
'HEAD',
|
|
32
25
|
'packed-refs',
|
|
33
26
|
'FETCH_HEAD',
|
|
@@ -36,8 +29,8 @@ const GIT_NAMES = new Set([
|
|
|
36
29
|
'logs',
|
|
37
30
|
'rebase-merge',
|
|
38
31
|
'rebase-apply',
|
|
39
|
-
]
|
|
40
|
-
const NPM_FILES =
|
|
32
|
+
];
|
|
33
|
+
const NPM_FILES = ['package.json', 'package-lock.json', MODULES_STAMP];
|
|
41
34
|
const AGE_FAST_MS = 1000;
|
|
42
35
|
const AGE_MID_MS = 30000;
|
|
43
36
|
const AGE_SLOW_MS = 600000;
|
|
@@ -85,11 +78,12 @@ const classify = (paths) => {
|
|
|
85
78
|
if (rel === UNKNOWN_PATH) {
|
|
86
79
|
found.all = true;
|
|
87
80
|
} else if (rel.startsWith('.git/')) {
|
|
88
|
-
|
|
81
|
+
const gitName = rel.split('/')[1];
|
|
82
|
+
if (GIT_NAMES.includes(gitName)) found.git = true;
|
|
89
83
|
} else if (isRunsRel(rel)) {
|
|
90
84
|
found.runs = true;
|
|
91
85
|
} else {
|
|
92
|
-
if (NPM_FILES.
|
|
86
|
+
if (NPM_FILES.includes(rel)) found.npm = true;
|
|
93
87
|
found.files.push(rel);
|
|
94
88
|
}
|
|
95
89
|
}
|
|
@@ -410,8 +404,12 @@ class Dashboard {
|
|
|
410
404
|
}
|
|
411
405
|
|
|
412
406
|
noteDisk(paths) {
|
|
413
|
-
if (!this.active) return;
|
|
414
407
|
const found = classify(paths);
|
|
408
|
+
const npm = this.ui.npm;
|
|
409
|
+
if ((found.runs || found.all) && npm && npm.refreshRecorded) {
|
|
410
|
+
npm.refreshRecorded();
|
|
411
|
+
}
|
|
412
|
+
if (!this.active) return;
|
|
415
413
|
if (found.all) return void this.requestAll();
|
|
416
414
|
const tasks = this.tasks;
|
|
417
415
|
if (found.files.length) {
|
|
@@ -465,9 +463,11 @@ class Dashboard {
|
|
|
465
463
|
npm: this.npm,
|
|
466
464
|
npmExtras: ui.lifecycle.npmExtras(),
|
|
467
465
|
npmRun: ui.npm.lastRun,
|
|
466
|
+
npmRuns: ui.npm.dashboardRuns(),
|
|
468
467
|
runs: this.liveRuns(),
|
|
469
468
|
notes: noteCounts(store),
|
|
470
469
|
store,
|
|
470
|
+
agents: ui.agents.summary(),
|
|
471
471
|
});
|
|
472
472
|
}
|
|
473
473
|
|
|
@@ -498,8 +498,8 @@ class Dashboard {
|
|
|
498
498
|
showFiles(scope) {
|
|
499
499
|
const ui = this.ui;
|
|
500
500
|
ui.showFiles();
|
|
501
|
-
if (scope === 'file') filesPane.onFileScope(ui);
|
|
502
|
-
else filesPane.onDiffScope(ui);
|
|
501
|
+
if (scope === 'file') filesPane.onFileScope(ui, true);
|
|
502
|
+
else filesPane.onDiffScope(ui, true);
|
|
503
503
|
ui.status = '';
|
|
504
504
|
}
|
|
505
505
|
|
|
@@ -513,6 +513,7 @@ class Dashboard {
|
|
|
513
513
|
npm: () => this.openPackages(),
|
|
514
514
|
run: () => ui.npm.open(),
|
|
515
515
|
tasks: () => ui.composer.tasks.openTasksPage(),
|
|
516
|
+
agents: () => ui.agents.open(),
|
|
516
517
|
};
|
|
517
518
|
const opener = openers[id];
|
|
518
519
|
if (!opener || ui.mode !== 'review') return;
|