reslop 0.1.0 → 0.1.2
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 +25 -9
- package/README.md +25 -40
- package/bin/reslop.js +2 -2
- package/lib/ansi.js +0 -1
- package/lib/api.js +5 -0
- package/lib/cli.js +84 -79
- package/lib/clipboard.js +1 -1
- package/lib/deps.js +1440 -0
- package/lib/detect.js +4 -4
- package/lib/diff.js +40 -63
- package/lib/editor.js +1 -1
- package/lib/files.js +23 -4
- package/lib/git.js +367 -18
- package/lib/github.js +625 -0
- package/lib/highlight.js +149 -166
- package/lib/keys.js +34 -41
- package/lib/render.js +218 -143
- package/lib/review.js +52 -45
- package/lib/select.js +21 -31
- package/lib/session.js +298 -235
- package/lib/source.js +30 -0
- package/package.json +37 -18
package/lib/session.js
CHANGED
|
@@ -16,43 +16,51 @@ const { refreshIndexPatches } = require('./diff.js');
|
|
|
16
16
|
const { renderFrame, presentRows, presentCursor } = render;
|
|
17
17
|
const { noteInnerWidth, todoInnerWidth } = render;
|
|
18
18
|
const { QUIT_FINISH, QUIT_CONTINUE } = render;
|
|
19
|
-
const { actionFromKey, hitAction, decodeChunk
|
|
19
|
+
const { actionFromKey, hitAction, decodeChunk } = keys;
|
|
20
|
+
const { FILES_DISABLED } = keys;
|
|
20
21
|
const { cursorInWrap, wrapDoc, wrapMove } = wrap;
|
|
21
22
|
const { AUTOSAVE_MS, listReviewNames, loadTemplates } = review;
|
|
22
23
|
const { resolveReviewPath, loadReview, createStore } = review;
|
|
23
24
|
const { noteCounts, hasNotes, setFeedback, addTodo } = review;
|
|
25
|
+
const { applyImportedNotes } = review;
|
|
24
26
|
const { setTodoText, removeTodo, flushReview, mergeTodos } = review;
|
|
25
27
|
const { feedbackKey, checkLabel, rankedTemplates } = review;
|
|
26
28
|
const { prefixTemplates, TEMPLATE_SHOW, rememberTemplate } = review;
|
|
27
29
|
|
|
28
30
|
const LAYOUTS = ['unified', 'mixed', 'side'];
|
|
29
|
-
const
|
|
30
|
-
const FILES_MOVE = Object.assign(Object.create(null), {
|
|
31
|
-
next: 1,
|
|
32
|
-
prev: -1,
|
|
33
|
-
});
|
|
34
|
-
const ADD_STATUS = Object.assign(Object.create(null), {
|
|
35
|
-
staged: 'already staged',
|
|
36
|
-
commit: 'read only',
|
|
37
|
-
});
|
|
38
|
-
const UNSTAGE_STATUS = Object.assign(Object.create(null), {
|
|
39
|
-
commit: 'read only',
|
|
40
|
-
});
|
|
41
|
-
const REVERT_STATUS = Object.assign(Object.create(null), {
|
|
42
|
-
commit: 'read only',
|
|
43
|
-
});
|
|
31
|
+
const READ_ONLY_ORIGINS = ['commit', 'pr'];
|
|
44
32
|
const ENTER_TERM = '\x1b[?1049h\x1b[?25l\x1b[?7l\x1b[?1002h\x1b[?1006h';
|
|
45
33
|
const LEAVE_TERM =
|
|
46
34
|
'\x1b[?1006l\x1b[?1002l\x1b[?7h\x1b[0 q\x1b[?12l\x1b[?25h\x1b[?1049l';
|
|
47
35
|
const CURSOR_BLINK_MS = 500;
|
|
48
36
|
|
|
49
|
-
const
|
|
37
|
+
const systemNow = () => new Date();
|
|
38
|
+
|
|
39
|
+
const isReadOnlyOrigin = (origin) => READ_ONLY_ORIGINS.includes(origin);
|
|
40
|
+
|
|
41
|
+
const relabelStatus = (entries, from, to) => {
|
|
42
|
+
if (!to) return entries;
|
|
43
|
+
return entries.map((entry) => {
|
|
44
|
+
if (entry.status !== from) return entry;
|
|
45
|
+
return { ...entry, status: to };
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const depChangeKey = (item) => {
|
|
50
|
+
const change = item.dep && item.dep.change;
|
|
51
|
+
if (!change) return '';
|
|
52
|
+
return `${change.section}/${change.name}`;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const needsReload = (item) => item.reload === true;
|
|
50
56
|
|
|
51
57
|
const itemKey = (item) => {
|
|
52
58
|
if (!item || !item.file) return '';
|
|
53
59
|
const rel = itemPath(item);
|
|
54
60
|
if (item.origin === 'todo') return `todo:${rel}`;
|
|
55
61
|
const origin = item.origin ?? '';
|
|
62
|
+
const depId = depChangeKey(item);
|
|
63
|
+
if (depId) return `${origin}:${rel}:dep:${depId}`;
|
|
56
64
|
if (!item.hunk) {
|
|
57
65
|
const block = item.blockId ?? '-';
|
|
58
66
|
return `${origin}:${rel}:file:${block}`;
|
|
@@ -66,12 +74,12 @@ const itemKey = (item) => {
|
|
|
66
74
|
const itemFeedKey = (item) => {
|
|
67
75
|
if (!item || !item.file || item.origin === 'todo') return '';
|
|
68
76
|
const hunk = item.hunk;
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
});
|
|
77
|
+
const depId = depChangeKey(item);
|
|
78
|
+
const file = depId ? `${itemPath(item)}#${depId}` : itemPath(item);
|
|
79
|
+
const oldStart = hunk ? hunk.oldStart : 0;
|
|
80
|
+
const newStart = hunk ? hunk.newStart : 0;
|
|
81
|
+
const blockId = item.blockId ?? 0;
|
|
82
|
+
return feedbackKey({ file, oldStart, newStart, blockId });
|
|
75
83
|
};
|
|
76
84
|
|
|
77
85
|
const sameCursor = (a, b) => {
|
|
@@ -80,16 +88,16 @@ const sameCursor = (a, b) => {
|
|
|
80
88
|
return a.x === b.x && a.y === b.y;
|
|
81
89
|
};
|
|
82
90
|
|
|
83
|
-
const QUIT_CONFIRM =
|
|
91
|
+
const QUIT_CONFIRM = {
|
|
84
92
|
[QUIT_FINISH]: (session) => session.finishQuit('ready'),
|
|
85
93
|
[QUIT_CONTINUE]: (session) => session.finishQuit('editing'),
|
|
86
94
|
'ctrl-c': (session) => session.finishQuit('ready'),
|
|
87
95
|
escape: (session) => session.cancelQuit(),
|
|
88
|
-
}
|
|
96
|
+
};
|
|
89
97
|
|
|
90
98
|
const COMPOSE_EDITS = ['backspace', 'delete', 'ctrl-z', 'ctrl-y'];
|
|
91
99
|
|
|
92
|
-
const COMPOSE_SPECIAL =
|
|
100
|
+
const COMPOSE_SPECIAL = {
|
|
93
101
|
backspace: (session) => session.editor.backspace(),
|
|
94
102
|
delete: (session) => session.editor.delete(),
|
|
95
103
|
left: (session) => session.editor.move(-1),
|
|
@@ -103,13 +111,12 @@ const COMPOSE_SPECIAL = Object.assign(Object.create(null), {
|
|
|
103
111
|
'ctrl-z': (session) => session.editor.undoEdit(),
|
|
104
112
|
'ctrl-y': (session) => session.editor.redoEdit(),
|
|
105
113
|
tab: (session) => session.applyTemplate(),
|
|
106
|
-
}
|
|
114
|
+
};
|
|
107
115
|
|
|
108
116
|
const HANDLERS = {
|
|
109
117
|
add: (session) => session.onAdd(),
|
|
110
118
|
unstage: (session) => session.onUnstage(),
|
|
111
119
|
revert: (session) => session.onRevert(),
|
|
112
|
-
skip: (session) => session.onSkip(),
|
|
113
120
|
next: (session) => session.onNext(),
|
|
114
121
|
prev: (session) => session.onPrev(),
|
|
115
122
|
layout: (session) => session.onLayout(),
|
|
@@ -121,21 +128,20 @@ const HANDLERS = {
|
|
|
121
128
|
quit: (session) => session.onQuit(),
|
|
122
129
|
scrollUp: (session) => session.onScroll(-1),
|
|
123
130
|
scrollDown: (session) => session.onScroll(1),
|
|
124
|
-
help: (session) => session.onHelp(),
|
|
125
131
|
};
|
|
126
132
|
|
|
127
|
-
const KEY_MODE =
|
|
133
|
+
const KEY_MODE = {
|
|
128
134
|
confirmQuit: (session, key) => session.onConfirmQuit(key),
|
|
129
135
|
compose: (session, key) => session.handleComposeKey(key),
|
|
130
|
-
}
|
|
136
|
+
};
|
|
131
137
|
|
|
132
|
-
const MOUSE_KIND =
|
|
138
|
+
const MOUSE_KIND = {
|
|
133
139
|
wheelUp: (session) => session.onScroll(-1),
|
|
134
140
|
wheelDown: (session) => session.onScroll(1),
|
|
135
141
|
press: (session, event) => session.onMousePress(event),
|
|
136
142
|
drag: (session, event) => session.onMouseDrag(event),
|
|
137
143
|
release: (session, event) => session.onMouseRelease(event),
|
|
138
|
-
}
|
|
144
|
+
};
|
|
139
145
|
|
|
140
146
|
const RELEASE_HITS = [
|
|
141
147
|
{
|
|
@@ -167,35 +173,40 @@ class Session {
|
|
|
167
173
|
this.stdout = options.stdout;
|
|
168
174
|
this.color = options.color ?? true;
|
|
169
175
|
this.copyText = options.copyText ?? copyText;
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
this.pane = options.startPane ?? '
|
|
176
|
+
const termSize = () => {
|
|
177
|
+
const width = this.stdout.columns ?? 80;
|
|
178
|
+
const height = this.stdout.rows ?? 24;
|
|
179
|
+
return { width, height };
|
|
180
|
+
};
|
|
181
|
+
this.getSize = options.getSize ?? termSize;
|
|
182
|
+
this.pane = options.startPane ?? 'files';
|
|
177
183
|
this.rev = options.rev ?? null;
|
|
178
|
-
this.revShort =
|
|
184
|
+
this.revShort = '';
|
|
185
|
+
this.sourceLabel = options.sourceLabel ?? '';
|
|
186
|
+
this.repoName = options.repoName ?? '';
|
|
187
|
+
this.change = options.change ?? null;
|
|
179
188
|
this.readdirSync = options.readdirSync ?? fs.readdirSync;
|
|
180
189
|
this.readFileSync = options.readFileSync ?? fs.readFileSync;
|
|
181
190
|
this.writeFileSync = options.writeFileSync ?? fs.writeFileSync;
|
|
182
191
|
this.mkdirSync = options.mkdirSync ?? fs.mkdirSync;
|
|
183
|
-
this.now = options.now ??
|
|
192
|
+
this.now = options.now ?? systemNow;
|
|
184
193
|
this.forceNewReview = options.newReview === true;
|
|
194
|
+
this.audit = options.audit === true;
|
|
195
|
+
this.outdatedMap = options.outdatedMap;
|
|
196
|
+
this.auditMap = options.auditMap;
|
|
185
197
|
this.setInterval = options.setInterval ?? setInterval;
|
|
186
198
|
this.clearInterval = options.clearInterval ?? clearInterval;
|
|
187
199
|
}
|
|
188
200
|
|
|
189
201
|
resetState() {
|
|
190
202
|
this.items = [];
|
|
203
|
+
this.dismissed = new Set();
|
|
191
204
|
this.top = this.cwd;
|
|
192
205
|
this.index = 0;
|
|
193
|
-
this.skipped = new Set();
|
|
194
206
|
this.fileCursor = 0;
|
|
195
207
|
this.reviewPath = null;
|
|
196
208
|
this.scroll = 0;
|
|
197
209
|
this.status = '';
|
|
198
|
-
this.help = false;
|
|
199
210
|
this.layout = 'unified';
|
|
200
211
|
this.done = false;
|
|
201
212
|
this.exitCode = 0;
|
|
@@ -205,10 +216,11 @@ class Session {
|
|
|
205
216
|
this.lastSize = null;
|
|
206
217
|
this.selection = null;
|
|
207
218
|
this.mouseAnchor = null;
|
|
208
|
-
this.dragging = false;
|
|
209
219
|
this.pendingClick = null;
|
|
210
220
|
this.abortController = null;
|
|
211
221
|
this.notes = null;
|
|
222
|
+
this.didResumeReview = false;
|
|
223
|
+
this.importedRemoteNotes = false;
|
|
212
224
|
this.mode = 'review';
|
|
213
225
|
this.editor = null;
|
|
214
226
|
this.composeKind = null;
|
|
@@ -230,11 +242,15 @@ class Session {
|
|
|
230
242
|
|
|
231
243
|
load() {
|
|
232
244
|
const here = itemKey(this.items[this.index]);
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
|
|
245
|
+
const commit = this.rev;
|
|
246
|
+
const audit = this.audit;
|
|
247
|
+
const outdatedMap = this.outdatedMap;
|
|
248
|
+
const auditMap = this.auditMap;
|
|
249
|
+
const options = { commit, audit, outdatedMap, auditMap };
|
|
250
|
+
const loaded = this.repo.load(this.cwd, this.paths, options);
|
|
236
251
|
this.top = loaded.top ?? this.cwd;
|
|
237
|
-
|
|
252
|
+
const items = loaded.items;
|
|
253
|
+
this.items = items.filter((item) => !this.dismissed.has(itemKey(item)));
|
|
238
254
|
if (loaded.rev) {
|
|
239
255
|
this.rev = loaded.rev;
|
|
240
256
|
}
|
|
@@ -243,19 +259,22 @@ class Session {
|
|
|
243
259
|
} else if (this.rev && !this.revShort) {
|
|
244
260
|
this.revShort = this.rev.slice(0, 7);
|
|
245
261
|
}
|
|
262
|
+
if (loaded.sourceLabel) this.sourceLabel = loaded.sourceLabel;
|
|
263
|
+
if (loaded.change) this.change = loaded.change;
|
|
264
|
+
const repository = this.change ? this.change.repository : '';
|
|
265
|
+
if (!this.repoName && repository) this.repoName = repository;
|
|
246
266
|
if (!this.notes) this.initReview();
|
|
267
|
+
const imported = loaded.imported;
|
|
268
|
+
if (imported && !this.didResumeReview && !this.importedRemoteNotes) {
|
|
269
|
+
applyImportedNotes(this.notes, imported);
|
|
270
|
+
this.importedRemoteNotes = true;
|
|
271
|
+
}
|
|
247
272
|
this.injectTodos();
|
|
248
273
|
if (here) {
|
|
249
274
|
const idx = this.items.findIndex((item) => itemKey(item) === here);
|
|
250
275
|
if (idx >= 0) this.index = idx;
|
|
251
276
|
}
|
|
252
|
-
this.
|
|
253
|
-
if (this.index >= this.items.length) {
|
|
254
|
-
this.index = Math.max(0, this.items.length - 1);
|
|
255
|
-
}
|
|
256
|
-
if (!this.isRemaining(this.items[this.index])) {
|
|
257
|
-
this.focusRemaining(this.index, 1);
|
|
258
|
-
}
|
|
277
|
+
this.clampIndex();
|
|
259
278
|
this.syncReviewPath();
|
|
260
279
|
this.syncFileCursor();
|
|
261
280
|
}
|
|
@@ -273,6 +292,7 @@ class Session {
|
|
|
273
292
|
templates,
|
|
274
293
|
this.readFileSync,
|
|
275
294
|
);
|
|
295
|
+
this.didResumeReview = true;
|
|
276
296
|
return;
|
|
277
297
|
}
|
|
278
298
|
this.notes = createStore(resolved.reviewPath, templates);
|
|
@@ -284,62 +304,23 @@ class Session {
|
|
|
284
304
|
this.items = mergeTodos(this.items, todos, extra);
|
|
285
305
|
}
|
|
286
306
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
for (const item of this.items) live.add(itemKey(item));
|
|
290
|
-
for (const key of [...this.skipped]) {
|
|
291
|
-
if (!live.has(key)) this.skipped.delete(key);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
isRemaining(item) {
|
|
296
|
-
if (!item) return false;
|
|
297
|
-
return !this.skipped.has(itemKey(item));
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
remainingIndices() {
|
|
301
|
-
const indices = [];
|
|
302
|
-
for (let i = 0; i < this.items.length; i++) {
|
|
303
|
-
if (this.isRemaining(this.items[i])) indices.push(i);
|
|
304
|
-
}
|
|
305
|
-
return indices;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
focusRemaining(from, direction) {
|
|
309
|
-
const remaining = this.remainingIndices();
|
|
310
|
-
if (!remaining.length) {
|
|
307
|
+
clampIndex() {
|
|
308
|
+
if (!this.items.length) {
|
|
311
309
|
this.index = 0;
|
|
312
|
-
return
|
|
313
|
-
}
|
|
314
|
-
if (direction >= 0) {
|
|
315
|
-
for (const i of remaining) {
|
|
316
|
-
if (i >= from) {
|
|
317
|
-
this.index = i;
|
|
318
|
-
return true;
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
this.index = remaining[remaining.length - 1];
|
|
322
|
-
return true;
|
|
310
|
+
return;
|
|
323
311
|
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
this.index = i;
|
|
328
|
-
return true;
|
|
329
|
-
}
|
|
312
|
+
if (this.index < 0) this.index = 0;
|
|
313
|
+
if (this.index >= this.items.length) {
|
|
314
|
+
this.index = this.items.length - 1;
|
|
330
315
|
}
|
|
331
|
-
this.index = remaining[0];
|
|
332
|
-
return true;
|
|
333
316
|
}
|
|
334
317
|
|
|
335
318
|
fileList() {
|
|
336
|
-
const
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
return { ...entry, status: this.revShort };
|
|
342
|
-
});
|
|
319
|
+
const entries = fileEntries(this.items);
|
|
320
|
+
if (this.sourceLabel) {
|
|
321
|
+
return relabelStatus(entries, 'pr', this.sourceLabel);
|
|
322
|
+
}
|
|
323
|
+
return relabelStatus(entries, 'commit', this.revShort);
|
|
343
324
|
}
|
|
344
325
|
|
|
345
326
|
syncReviewPath() {
|
|
@@ -348,7 +329,18 @@ class Session {
|
|
|
348
329
|
this.reviewPath = item ? itemPath(item) : null;
|
|
349
330
|
}
|
|
350
331
|
|
|
351
|
-
|
|
332
|
+
clampFileCursor() {
|
|
333
|
+
const files = this.fileList();
|
|
334
|
+
if (!files.length) {
|
|
335
|
+
this.fileCursor = 0;
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
if (this.fileCursor >= files.length) {
|
|
339
|
+
this.fileCursor = files.length - 1;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
followReviewPath() {
|
|
352
344
|
const files = this.fileList();
|
|
353
345
|
if (!files.length) {
|
|
354
346
|
this.fileCursor = 0;
|
|
@@ -358,9 +350,17 @@ class Session {
|
|
|
358
350
|
const idx = files.findIndex((entry) => entry.path === here);
|
|
359
351
|
if (idx >= 0) {
|
|
360
352
|
this.fileCursor = idx;
|
|
361
|
-
|
|
362
|
-
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
this.clampFileCursor();
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
syncFileCursor() {
|
|
359
|
+
if (this.pane === 'files') {
|
|
360
|
+
this.clampFileCursor();
|
|
361
|
+
return;
|
|
363
362
|
}
|
|
363
|
+
this.followReviewPath();
|
|
364
364
|
}
|
|
365
365
|
|
|
366
366
|
counts() {
|
|
@@ -369,15 +369,11 @@ class Session {
|
|
|
369
369
|
unstaged: 0,
|
|
370
370
|
untracked: 0,
|
|
371
371
|
commit: 0,
|
|
372
|
-
|
|
372
|
+
pr: 0,
|
|
373
373
|
todo: 0,
|
|
374
374
|
feedback: 0,
|
|
375
375
|
};
|
|
376
376
|
for (const item of this.items) {
|
|
377
|
-
if (!this.isRemaining(item)) {
|
|
378
|
-
counts.skipped += 1;
|
|
379
|
-
continue;
|
|
380
|
-
}
|
|
381
377
|
if (!Object.hasOwn(counts, item.origin)) continue;
|
|
382
378
|
counts[item.origin] += 1;
|
|
383
379
|
}
|
|
@@ -388,16 +384,16 @@ class Session {
|
|
|
388
384
|
}
|
|
389
385
|
|
|
390
386
|
view() {
|
|
391
|
-
const remaining = this.remainingIndices();
|
|
392
|
-
const pos = remaining.indexOf(this.index);
|
|
393
387
|
const templates = this.shownTemplates();
|
|
388
|
+
const change = this.change;
|
|
389
|
+
const isGithubPr = change && change.source === 'github-pr';
|
|
390
|
+
const repoName = this.repoName || path.basename(this.top || this.cwd || '');
|
|
394
391
|
return {
|
|
395
392
|
item: this.current(),
|
|
396
|
-
index:
|
|
397
|
-
total:
|
|
393
|
+
index: this.index,
|
|
394
|
+
total: this.items.length,
|
|
398
395
|
scroll: this.scroll,
|
|
399
396
|
status: this.status,
|
|
400
|
-
help: this.help,
|
|
401
397
|
layout: this.layout,
|
|
402
398
|
counts: this.counts(),
|
|
403
399
|
selection: this.selection,
|
|
@@ -405,12 +401,13 @@ class Session {
|
|
|
405
401
|
files: this.fileList(),
|
|
406
402
|
fileCursor: this.fileCursor,
|
|
407
403
|
reviewPath: this.reviewPath,
|
|
408
|
-
repoName
|
|
404
|
+
repoName,
|
|
409
405
|
revShort: this.revShort || '',
|
|
406
|
+
sourceLabel: this.sourceLabel || '',
|
|
407
|
+
sourceKind: isGithubPr ? 'pr' : '',
|
|
410
408
|
mode: this.mode,
|
|
411
409
|
compose: this.composeView(),
|
|
412
410
|
noteText: this.idleNoteText(),
|
|
413
|
-
noteKind: this.idleNoteKind(),
|
|
414
411
|
todos: this.todoTexts(),
|
|
415
412
|
todoFocus: this.clampedTodoFocus(),
|
|
416
413
|
todoEdit: this.todoEditView(),
|
|
@@ -454,12 +451,9 @@ class Session {
|
|
|
454
451
|
this.closeCompose();
|
|
455
452
|
}
|
|
456
453
|
if (this.pane === 'files') {
|
|
457
|
-
|
|
458
|
-
if (
|
|
459
|
-
|
|
460
|
-
return;
|
|
461
|
-
}
|
|
462
|
-
if (FILES_IDLE.includes(action)) return;
|
|
454
|
+
if (action === 'next') return void this.onFileMove(1);
|
|
455
|
+
if (action === 'prev') return void this.onFileMove(-1);
|
|
456
|
+
if (FILES_DISABLED.includes(action)) return;
|
|
463
457
|
}
|
|
464
458
|
const handler = HANDLERS[action];
|
|
465
459
|
if (!handler) return;
|
|
@@ -467,9 +461,7 @@ class Session {
|
|
|
467
461
|
}
|
|
468
462
|
|
|
469
463
|
current() {
|
|
470
|
-
|
|
471
|
-
if (!this.isRemaining(item)) return null;
|
|
472
|
-
return item;
|
|
464
|
+
return this.items[this.index] ?? null;
|
|
473
465
|
}
|
|
474
466
|
|
|
475
467
|
reloadAfterChange() {
|
|
@@ -482,7 +474,8 @@ class Session {
|
|
|
482
474
|
this.status = 'nothing to review';
|
|
483
475
|
return;
|
|
484
476
|
}
|
|
485
|
-
this.
|
|
477
|
+
this.index = here;
|
|
478
|
+
this.clampIndex();
|
|
486
479
|
this.syncReviewPath();
|
|
487
480
|
this.syncFileCursor();
|
|
488
481
|
this.scroll = 0;
|
|
@@ -508,7 +501,7 @@ class Session {
|
|
|
508
501
|
|
|
509
502
|
keepOrigin(item, origin) {
|
|
510
503
|
item.origin = origin;
|
|
511
|
-
refreshIndexPatches(this.items, item);
|
|
504
|
+
if (!item.dep) refreshIndexPatches(this.items, item);
|
|
512
505
|
this.clearSelection();
|
|
513
506
|
this.syncReviewPath();
|
|
514
507
|
this.syncFileCursor();
|
|
@@ -524,27 +517,160 @@ class Session {
|
|
|
524
517
|
return item;
|
|
525
518
|
}
|
|
526
519
|
|
|
520
|
+
fileCursorEntry() {
|
|
521
|
+
const files = this.fileList();
|
|
522
|
+
return files[this.fileCursor] ?? null;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
fileActionItems() {
|
|
526
|
+
const entry = this.fileCursorEntry();
|
|
527
|
+
if (!entry) return [];
|
|
528
|
+
const rel = entry.path;
|
|
529
|
+
const items = [];
|
|
530
|
+
for (const item of this.items) {
|
|
531
|
+
if (item.origin === 'todo') continue;
|
|
532
|
+
if (itemPath(item) !== rel) continue;
|
|
533
|
+
items.push(item);
|
|
534
|
+
}
|
|
535
|
+
return items;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
guardFileGit(items) {
|
|
539
|
+
if (!items.length) return false;
|
|
540
|
+
const first = items[0];
|
|
541
|
+
if (isReadOnlyOrigin(first.origin)) {
|
|
542
|
+
this.status = 'read only';
|
|
543
|
+
return false;
|
|
544
|
+
}
|
|
545
|
+
return true;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
applyFileGit(items, label, write, afterItem) {
|
|
549
|
+
try {
|
|
550
|
+
for (const item of items) {
|
|
551
|
+
write(this.top, item);
|
|
552
|
+
afterItem(item);
|
|
553
|
+
}
|
|
554
|
+
this.status = label;
|
|
555
|
+
return true;
|
|
556
|
+
} catch (error) {
|
|
557
|
+
this.status = error.message;
|
|
558
|
+
return false;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
selectNextFileAfter(rel) {
|
|
563
|
+
const files = this.fileList();
|
|
564
|
+
if (!files.length) {
|
|
565
|
+
this.fileCursor = 0;
|
|
566
|
+
this.reviewPath = null;
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
const idx = files.findIndex((entry) => entry.path === rel);
|
|
570
|
+
let at = idx < 0 ? this.fileCursor : idx + 1;
|
|
571
|
+
if (at >= files.length) at = files.length - 1;
|
|
572
|
+
const entry = files[at];
|
|
573
|
+
this.fileCursor = at;
|
|
574
|
+
this.reviewPath = entry.path;
|
|
575
|
+
this.index = entry.firstIndex;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
onFileAdd() {
|
|
579
|
+
const items = this.fileActionItems();
|
|
580
|
+
if (!this.guardFileGit(items)) return;
|
|
581
|
+
const targets = items.filter((item) => item.origin !== 'staged');
|
|
582
|
+
if (!targets.length) {
|
|
583
|
+
this.status = 'already staged';
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
const rel = itemPath(targets[0]);
|
|
587
|
+
const proposed = targets.some((item) => needsReload(item));
|
|
588
|
+
const ok = this.applyFileGit(
|
|
589
|
+
targets,
|
|
590
|
+
'staged',
|
|
591
|
+
(top, current) => this.repo.add(top, current),
|
|
592
|
+
(item) => this.keepOrigin(item, 'staged'),
|
|
593
|
+
);
|
|
594
|
+
if (ok && proposed) this.reloadAfterChange();
|
|
595
|
+
if (ok) this.selectNextFileAfter(rel);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
onFileUnstage() {
|
|
599
|
+
const items = this.fileActionItems();
|
|
600
|
+
if (!this.guardFileGit(items)) return;
|
|
601
|
+
const targets = items.filter((item) => item.origin === 'staged');
|
|
602
|
+
if (!targets.length) {
|
|
603
|
+
this.status = 'not staged';
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
const rel = itemPath(targets[0]);
|
|
607
|
+
const ok = this.applyFileGit(
|
|
608
|
+
targets,
|
|
609
|
+
'unstaged',
|
|
610
|
+
(top, current) => this.repo.unstage(top, current),
|
|
611
|
+
(item) => {
|
|
612
|
+
const origin = item.file.isNew === true ? 'untracked' : 'unstaged';
|
|
613
|
+
this.keepOrigin(item, origin);
|
|
614
|
+
},
|
|
615
|
+
);
|
|
616
|
+
if (ok) this.selectNextFileAfter(rel);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
onFileRevert() {
|
|
620
|
+
const items = this.fileActionItems();
|
|
621
|
+
if (!this.guardFileGit(items)) return;
|
|
622
|
+
const rel = itemPath(items[0]);
|
|
623
|
+
for (const item of items) {
|
|
624
|
+
if (needsReload(item)) this.dismissed.add(itemKey(item));
|
|
625
|
+
}
|
|
626
|
+
let ok = false;
|
|
627
|
+
try {
|
|
628
|
+
this.repo.revertFile(this.top, rel, items);
|
|
629
|
+
this.status = 'reverted';
|
|
630
|
+
ok = true;
|
|
631
|
+
} catch (error) {
|
|
632
|
+
this.status = error.message;
|
|
633
|
+
}
|
|
634
|
+
this.reloadAfterChange();
|
|
635
|
+
if (ok) this.selectNextFileAfter(rel);
|
|
636
|
+
}
|
|
637
|
+
|
|
527
638
|
onAdd() {
|
|
639
|
+
if (this.pane === 'files') return void this.onFileAdd();
|
|
528
640
|
const item = this.activeDiffItem();
|
|
529
641
|
if (!item) return;
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
642
|
+
if (isReadOnlyOrigin(item.origin)) {
|
|
643
|
+
this.status = 'read only';
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
if (item.origin === 'staged') {
|
|
647
|
+
this.status = 'already staged';
|
|
533
648
|
return;
|
|
534
649
|
}
|
|
535
650
|
this.applyGit(
|
|
536
651
|
item,
|
|
537
652
|
'staged',
|
|
538
653
|
(top, current) => this.repo.add(top, current),
|
|
539
|
-
() =>
|
|
654
|
+
() => {
|
|
655
|
+
if (needsReload(item)) {
|
|
656
|
+
this.reloadAfterChange();
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
this.keepOrigin(item, 'staged');
|
|
660
|
+
},
|
|
540
661
|
);
|
|
541
662
|
}
|
|
542
663
|
|
|
543
664
|
onUnstage() {
|
|
665
|
+
if (this.pane === 'files') return void this.onFileUnstage();
|
|
544
666
|
const item = this.activeDiffItem();
|
|
545
667
|
if (!item) return;
|
|
668
|
+
if (isReadOnlyOrigin(item.origin)) {
|
|
669
|
+
this.status = 'read only';
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
546
672
|
if (item.origin !== 'staged') {
|
|
547
|
-
this.status =
|
|
673
|
+
this.status = 'not staged';
|
|
548
674
|
return;
|
|
549
675
|
}
|
|
550
676
|
const origin = item.file.isNew === true ? 'untracked' : 'unstaged';
|
|
@@ -557,62 +683,39 @@ class Session {
|
|
|
557
683
|
}
|
|
558
684
|
|
|
559
685
|
onRevert() {
|
|
686
|
+
if (this.pane === 'files') return void this.onFileRevert();
|
|
560
687
|
const item = this.activeDiffItem();
|
|
561
688
|
if (!item) return;
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
this.status = blocked;
|
|
689
|
+
if (isReadOnlyOrigin(item.origin)) {
|
|
690
|
+
this.status = 'read only';
|
|
565
691
|
return;
|
|
566
692
|
}
|
|
567
693
|
this.applyGit(
|
|
568
694
|
item,
|
|
569
695
|
'reverted',
|
|
570
696
|
(top, current) => this.repo.revert(top, current),
|
|
571
|
-
() =>
|
|
697
|
+
() => {
|
|
698
|
+
if (needsReload(item)) this.dismissed.add(itemKey(item));
|
|
699
|
+
this.reloadAfterChange();
|
|
700
|
+
},
|
|
572
701
|
);
|
|
573
702
|
}
|
|
574
703
|
|
|
575
|
-
onSkip() {
|
|
576
|
-
const item = this.items[this.index];
|
|
577
|
-
if (!this.isRemaining(item)) return;
|
|
578
|
-
this.skipped.add(itemKey(item));
|
|
579
|
-
this.scroll = 0;
|
|
580
|
-
this.clearSelection();
|
|
581
|
-
const remaining = this.remainingIndices();
|
|
582
|
-
if (!remaining.length) {
|
|
583
|
-
this.status = 'all skipped';
|
|
584
|
-
return;
|
|
585
|
-
}
|
|
586
|
-
this.focusRemaining(this.index + 1, 1);
|
|
587
|
-
this.syncReviewPath();
|
|
588
|
-
this.status = 'skipped';
|
|
589
|
-
}
|
|
590
|
-
|
|
591
704
|
moveRemaining(step) {
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
this.status = 'all skipped';
|
|
595
|
-
return;
|
|
596
|
-
}
|
|
597
|
-
const pos = remaining.indexOf(this.index);
|
|
598
|
-
if (pos < 0) {
|
|
599
|
-
this.focusRemaining(this.index, step);
|
|
600
|
-
this.scroll = 0;
|
|
601
|
-
this.status = '';
|
|
602
|
-
this.clearSelection();
|
|
603
|
-
this.syncReviewPath();
|
|
705
|
+
if (!this.items.length) {
|
|
706
|
+
this.status = 'nothing to review';
|
|
604
707
|
return;
|
|
605
708
|
}
|
|
606
|
-
const next =
|
|
709
|
+
const next = this.index + step;
|
|
607
710
|
if (next < 0) {
|
|
608
711
|
this.status = 'first block';
|
|
609
712
|
return;
|
|
610
713
|
}
|
|
611
|
-
if (next >=
|
|
714
|
+
if (next >= this.items.length) {
|
|
612
715
|
this.status = 'last block';
|
|
613
716
|
return;
|
|
614
717
|
}
|
|
615
|
-
this.index =
|
|
718
|
+
this.index = next;
|
|
616
719
|
this.scroll = 0;
|
|
617
720
|
this.status = '';
|
|
618
721
|
this.clearSelection();
|
|
@@ -620,18 +723,10 @@ class Session {
|
|
|
620
723
|
}
|
|
621
724
|
|
|
622
725
|
onNext() {
|
|
623
|
-
if (this.mode === 'compose') {
|
|
624
|
-
this.commitCompose();
|
|
625
|
-
this.closeCompose();
|
|
626
|
-
}
|
|
627
726
|
this.moveRemaining(1);
|
|
628
727
|
}
|
|
629
728
|
|
|
630
729
|
onPrev() {
|
|
631
|
-
if (this.mode === 'compose') {
|
|
632
|
-
this.commitCompose();
|
|
633
|
-
this.closeCompose();
|
|
634
|
-
}
|
|
635
730
|
this.moveRemaining(-1);
|
|
636
731
|
}
|
|
637
732
|
|
|
@@ -680,7 +775,6 @@ class Session {
|
|
|
680
775
|
this.status = 'not a diff block';
|
|
681
776
|
return;
|
|
682
777
|
}
|
|
683
|
-
this.help = false;
|
|
684
778
|
const prev = this.notes.feedback.get(itemFeedKey(item));
|
|
685
779
|
const text = prev ? prev.text : '';
|
|
686
780
|
this.openCompose('feedback', text);
|
|
@@ -688,8 +782,7 @@ class Session {
|
|
|
688
782
|
|
|
689
783
|
todoFile() {
|
|
690
784
|
if (this.pane === 'files') {
|
|
691
|
-
const
|
|
692
|
-
const entry = files[this.fileCursor];
|
|
785
|
+
const entry = this.fileCursorEntry();
|
|
693
786
|
return entry ? entry.path : '';
|
|
694
787
|
}
|
|
695
788
|
const item = this.current();
|
|
@@ -768,9 +861,7 @@ class Session {
|
|
|
768
861
|
this.injectTodos();
|
|
769
862
|
const left = this.fileTodos(file);
|
|
770
863
|
if (!left.length) {
|
|
771
|
-
|
|
772
|
-
this.focusRemaining(this.index, 1);
|
|
773
|
-
}
|
|
864
|
+
this.clampIndex();
|
|
774
865
|
} else if (this.todoFocus >= left.length) {
|
|
775
866
|
this.todoFocus = left.length - 1;
|
|
776
867
|
}
|
|
@@ -799,7 +890,6 @@ class Session {
|
|
|
799
890
|
createTodo(file) {
|
|
800
891
|
if (!file) return;
|
|
801
892
|
this.pane = 'diff';
|
|
802
|
-
this.help = false;
|
|
803
893
|
this.scroll = 0;
|
|
804
894
|
this.clearSelection();
|
|
805
895
|
this.startDraftCompose(file);
|
|
@@ -841,8 +931,8 @@ class Session {
|
|
|
841
931
|
|
|
842
932
|
commitTodo(text) {
|
|
843
933
|
const id = this.composeTodoId;
|
|
844
|
-
const
|
|
845
|
-
|
|
934
|
+
const todos = this.notes.todos;
|
|
935
|
+
const prev = id === null ? null : todos.find((entry) => entry.id === id);
|
|
846
936
|
const current = this.current();
|
|
847
937
|
const file = prev ? prev.file : itemPath(current) || this.todoDraftFile;
|
|
848
938
|
if (prev) {
|
|
@@ -873,9 +963,7 @@ class Session {
|
|
|
873
963
|
this.templateFocus = false;
|
|
874
964
|
this.todoDraftFile = null;
|
|
875
965
|
this.injectTodos();
|
|
876
|
-
|
|
877
|
-
this.focusRemaining(this.index, 1);
|
|
878
|
-
}
|
|
966
|
+
this.clampIndex();
|
|
879
967
|
this.syncReviewPath();
|
|
880
968
|
this.syncFileCursor();
|
|
881
969
|
}
|
|
@@ -930,11 +1018,10 @@ class Session {
|
|
|
930
1018
|
composeView() {
|
|
931
1019
|
if (this.mode !== 'compose' || !this.editor) return null;
|
|
932
1020
|
if (this.composeKind !== 'feedback') return null;
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
};
|
|
1021
|
+
const kind = this.composeKind;
|
|
1022
|
+
const text = this.editor.text;
|
|
1023
|
+
const cursor = this.editor.cursor;
|
|
1024
|
+
return { kind, text, cursor };
|
|
938
1025
|
}
|
|
939
1026
|
|
|
940
1027
|
todoEditView() {
|
|
@@ -945,8 +1032,6 @@ class Session {
|
|
|
945
1032
|
|
|
946
1033
|
fileTodos(rel) {
|
|
947
1034
|
if (!this.notes || !rel) return [];
|
|
948
|
-
const page = { origin: 'todo', file: { newPath: rel, oldPath: rel } };
|
|
949
|
-
if (!this.isRemaining(page)) return [];
|
|
950
1035
|
const todos = [];
|
|
951
1036
|
for (const todo of this.notes.todos) {
|
|
952
1037
|
if (todo.file !== rel) continue;
|
|
@@ -959,10 +1044,8 @@ class Session {
|
|
|
959
1044
|
const item = this.current();
|
|
960
1045
|
if (!item || item.origin !== 'todo') return [];
|
|
961
1046
|
const texts = [];
|
|
962
|
-
const
|
|
963
|
-
|
|
964
|
-
? this.composeTodoId
|
|
965
|
-
: null;
|
|
1047
|
+
const editingTodo = this.mode === 'compose' && this.composeKind === 'todo';
|
|
1048
|
+
const editingId = editingTodo ? this.composeTodoId : null;
|
|
966
1049
|
for (const todo of this.fileTodos(itemPath(item))) {
|
|
967
1050
|
const live = editingId === todo.id && this.editor;
|
|
968
1051
|
const text = live ? this.editor.text : todo.text;
|
|
@@ -981,7 +1064,7 @@ class Session {
|
|
|
981
1064
|
}
|
|
982
1065
|
|
|
983
1066
|
clampedTodoFocus() {
|
|
984
|
-
const last = Math.max(0, this.
|
|
1067
|
+
const last = Math.max(0, this.todoRowCount() - 1);
|
|
985
1068
|
if (this.todoFocus < 0) return 0;
|
|
986
1069
|
if (this.todoFocus > last) return last;
|
|
987
1070
|
return this.todoFocus;
|
|
@@ -1004,12 +1087,6 @@ class Session {
|
|
|
1004
1087
|
else this.todoFocus = next;
|
|
1005
1088
|
}
|
|
1006
1089
|
|
|
1007
|
-
idleNoteKind() {
|
|
1008
|
-
const item = this.current();
|
|
1009
|
-
if (!item || item.origin === 'todo') return '';
|
|
1010
|
-
return 'feedback';
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1013
1090
|
idleNoteText() {
|
|
1014
1091
|
if (!this.notes) return '';
|
|
1015
1092
|
if (this.mode === 'compose') return '';
|
|
@@ -1023,11 +1100,10 @@ class Session {
|
|
|
1023
1100
|
flushReview(force) {
|
|
1024
1101
|
if (!this.notes) return false;
|
|
1025
1102
|
try {
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
});
|
|
1103
|
+
const writeFileSync = this.writeFileSync;
|
|
1104
|
+
const mkdirSync = this.mkdirSync;
|
|
1105
|
+
const io = { writeFileSync, mkdirSync, force: force === true };
|
|
1106
|
+
return flushReview(this.notes, io);
|
|
1031
1107
|
} catch (error) {
|
|
1032
1108
|
this.status = error.message;
|
|
1033
1109
|
return false;
|
|
@@ -1146,9 +1222,8 @@ class Session {
|
|
|
1146
1222
|
|
|
1147
1223
|
showFiles() {
|
|
1148
1224
|
this.pane = 'files';
|
|
1149
|
-
this.help = false;
|
|
1150
1225
|
this.clearSelection();
|
|
1151
|
-
this.
|
|
1226
|
+
this.followReviewPath();
|
|
1152
1227
|
}
|
|
1153
1228
|
|
|
1154
1229
|
onEscape() {
|
|
@@ -1163,7 +1238,6 @@ class Session {
|
|
|
1163
1238
|
if (this.pane === 'files') {
|
|
1164
1239
|
if (!this.reviewPath) return;
|
|
1165
1240
|
this.pane = 'diff';
|
|
1166
|
-
this.help = false;
|
|
1167
1241
|
this.clearSelection();
|
|
1168
1242
|
return;
|
|
1169
1243
|
}
|
|
@@ -1183,7 +1257,6 @@ class Session {
|
|
|
1183
1257
|
this.pane = 'diff';
|
|
1184
1258
|
this.scroll = 0;
|
|
1185
1259
|
this.status = '';
|
|
1186
|
-
this.help = false;
|
|
1187
1260
|
this.clearSelection();
|
|
1188
1261
|
this.syncReviewPath();
|
|
1189
1262
|
}
|
|
@@ -1191,7 +1264,7 @@ class Session {
|
|
|
1191
1264
|
onFileMove(delta) {
|
|
1192
1265
|
const files = this.fileList();
|
|
1193
1266
|
if (!files.length) {
|
|
1194
|
-
this.status = '
|
|
1267
|
+
this.status = 'nothing to review';
|
|
1195
1268
|
return;
|
|
1196
1269
|
}
|
|
1197
1270
|
const next = this.fileCursor + delta;
|
|
@@ -1204,6 +1277,9 @@ class Session {
|
|
|
1204
1277
|
return;
|
|
1205
1278
|
}
|
|
1206
1279
|
this.fileCursor = next;
|
|
1280
|
+
const entry = files[next];
|
|
1281
|
+
this.reviewPath = entry.path;
|
|
1282
|
+
this.index = entry.firstIndex;
|
|
1207
1283
|
this.status = '';
|
|
1208
1284
|
}
|
|
1209
1285
|
|
|
@@ -1221,16 +1297,11 @@ class Session {
|
|
|
1221
1297
|
this.clearSelection();
|
|
1222
1298
|
}
|
|
1223
1299
|
|
|
1224
|
-
onHelp() {
|
|
1225
|
-
this.help = !this.help;
|
|
1226
|
-
this.clearSelection();
|
|
1227
|
-
}
|
|
1228
|
-
|
|
1229
1300
|
onLayout() {
|
|
1230
1301
|
const i = LAYOUTS.indexOf(this.layout);
|
|
1231
1302
|
const next = (i < 0 ? 0 : i + 1) % LAYOUTS.length;
|
|
1232
1303
|
this.layout = LAYOUTS[next];
|
|
1233
|
-
this.status =
|
|
1304
|
+
this.status = this.layout === 'side' ? 'side-by-side' : this.layout;
|
|
1234
1305
|
this.scroll = 0;
|
|
1235
1306
|
this.clearSelection();
|
|
1236
1307
|
}
|
|
@@ -1245,7 +1316,6 @@ class Session {
|
|
|
1245
1316
|
onMousePress(event) {
|
|
1246
1317
|
const cell = this.mouseCell(event);
|
|
1247
1318
|
this.mouseAnchor = cell;
|
|
1248
|
-
this.dragging = false;
|
|
1249
1319
|
this.pendingClick = null;
|
|
1250
1320
|
this.selection = null;
|
|
1251
1321
|
if (this.lastFrame && cell.y === this.lastFrame.height) {
|
|
@@ -1255,7 +1325,6 @@ class Session {
|
|
|
1255
1325
|
|
|
1256
1326
|
onMouseDrag(event) {
|
|
1257
1327
|
if (!this.mouseAnchor) return;
|
|
1258
|
-
this.dragging = true;
|
|
1259
1328
|
this.pendingClick = null;
|
|
1260
1329
|
this.selection = {
|
|
1261
1330
|
start: this.mouseAnchor,
|
|
@@ -1298,7 +1367,6 @@ class Session {
|
|
|
1298
1367
|
const clickId = this.pendingClick;
|
|
1299
1368
|
this.pendingClick = null;
|
|
1300
1369
|
this.mouseAnchor = null;
|
|
1301
|
-
this.dragging = false;
|
|
1302
1370
|
if (this.clickButton(cell, clickId)) return;
|
|
1303
1371
|
const frame = this.lastFrame;
|
|
1304
1372
|
for (const target of RELEASE_HITS) {
|
|
@@ -1328,7 +1396,7 @@ class Session {
|
|
|
1328
1396
|
return;
|
|
1329
1397
|
}
|
|
1330
1398
|
if (event.type !== 'mouse') return;
|
|
1331
|
-
const kind =
|
|
1399
|
+
const kind = event.kind;
|
|
1332
1400
|
const wheel = kind === 'wheelUp' || kind === 'wheelDown';
|
|
1333
1401
|
if (!wheel && event.btn !== 0 && event.btn !== undefined) return;
|
|
1334
1402
|
const handler = MOUSE_KIND[kind];
|
|
@@ -1406,11 +1474,6 @@ class Session {
|
|
|
1406
1474
|
}
|
|
1407
1475
|
|
|
1408
1476
|
module.exports = {
|
|
1409
|
-
LAYOUTS,
|
|
1410
|
-
ENTER_TERM,
|
|
1411
1477
|
LEAVE_TERM,
|
|
1412
|
-
CURSOR_BLINK_MS,
|
|
1413
|
-
HANDLERS,
|
|
1414
|
-
itemKey,
|
|
1415
1478
|
Session,
|
|
1416
1479
|
};
|