reslop 0.2.0 → 0.2.3
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 +13 -2
- package/lib/ansi.js +263 -6
- package/lib/api.js +2 -0
- package/lib/capabilities.js +6 -0
- package/lib/cli.js +13 -2
- package/lib/deps.js +43 -3
- package/lib/git-branches.js +228 -17
- package/lib/git-reword-todo.js +20 -0
- package/lib/git.js +20 -3
- package/lib/keys.js +10 -4
- package/lib/npm-commands.js +513 -0
- package/lib/render/branches.js +23 -58
- package/lib/render/chrome.js +156 -99
- package/lib/render/commits.js +643 -31
- package/lib/render/diff.js +4 -4
- package/lib/render/frame.js +51 -5
- package/lib/render/notes.js +22 -5
- package/lib/render/npm.js +228 -0
- package/lib/render/primitives.js +48 -34
- package/lib/render/render.js +3 -3
- package/lib/report-model.js +127 -0
- package/lib/report-parse.js +94 -0
- package/lib/report-render.js +87 -0
- package/lib/report-run.js +164 -0
- package/lib/report-test.js +506 -0
- package/lib/report-tools.js +311 -0
- package/lib/review-merge.js +270 -0
- package/lib/review.js +38 -6
- package/lib/session/actions.js +81 -5
- package/lib/session/commit.js +187 -19
- package/lib/session/compose.js +96 -32
- package/lib/session/dispatch.js +36 -2
- package/lib/session/files-pane.js +36 -6
- package/lib/session/lifecycle.js +116 -16
- package/lib/session/navigation.js +1 -0
- package/lib/session/npm.js +335 -0
- package/lib/session/pointer.js +12 -1
- package/lib/session/todos.js +29 -1
- package/lib/session/view.js +14 -3
- package/lib/session/watch.js +120 -8
- package/lib/session.js +11 -2
- package/package.json +7 -7
package/lib/render/chrome.js
CHANGED
|
@@ -10,21 +10,33 @@ const { ACTION_IDS, BRANCH_ACTIONS, COMMIT_ACTIONS, actionLetter } = keys;
|
|
|
10
10
|
const { buttonWord } = keys;
|
|
11
11
|
const { FILES_HIDDEN, FILES_GIT_DISABLED, BRANCHES_DISABLED } = keys;
|
|
12
12
|
const { COMMITS_DISABLED, TODO_DISABLED, DIFF_DISABLED, UNIT_DISABLED } = keys;
|
|
13
|
+
const { NPM_DISABLED } = keys;
|
|
13
14
|
const { diffGitDisabled, branchGitDisabled, commitGitDisabled } = keys;
|
|
14
15
|
const { CONFIRM, confirmPrefix, promptFromChoices } = keys;
|
|
15
16
|
const { THEME, paint, visibleWidth } = ansi;
|
|
16
17
|
const { truncateVisible, fg, RESET, EL, seq } = ansi;
|
|
17
|
-
const { fill, branchLabel
|
|
18
|
+
const { fill, branchLabel } = primitives;
|
|
18
19
|
|
|
19
20
|
const HEADER_ROLE_FG = {
|
|
20
21
|
repo: THEME.headerRepoFg,
|
|
21
22
|
dir: THEME.headerDirFg,
|
|
22
23
|
slash: THEME.headerSlashFg,
|
|
23
24
|
file: THEME.headerFileFg,
|
|
24
|
-
meta: THEME.headerMetaFg,
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
-
const
|
|
27
|
+
const WAVE = '·•●•·';
|
|
28
|
+
|
|
29
|
+
const busyBars = (wave) => {
|
|
30
|
+
const n = wave.length;
|
|
31
|
+
const frames = new Array(n);
|
|
32
|
+
for (let frame = 0; frame < n; frame++) {
|
|
33
|
+
const start = (n - frame) % n;
|
|
34
|
+
frames[frame] = wave.slice(start) + wave.slice(0, start);
|
|
35
|
+
}
|
|
36
|
+
return frames;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const BUSY_BAR = busyBars(WAVE);
|
|
28
40
|
const BUSY_PROGRESS = [
|
|
29
41
|
'pulling',
|
|
30
42
|
'pushing',
|
|
@@ -34,11 +46,13 @@ const BUSY_PROGRESS = [
|
|
|
34
46
|
'npm i',
|
|
35
47
|
'npm uninstall',
|
|
36
48
|
'npm audit fix',
|
|
49
|
+
'running',
|
|
37
50
|
'checking out',
|
|
38
51
|
'creating branch',
|
|
39
52
|
'rebasing',
|
|
40
53
|
'dropping',
|
|
41
54
|
'committing',
|
|
55
|
+
'rewording',
|
|
42
56
|
'force pushing',
|
|
43
57
|
];
|
|
44
58
|
|
|
@@ -62,6 +76,8 @@ const INFO_STATUS = [
|
|
|
62
76
|
'unified',
|
|
63
77
|
'mixed',
|
|
64
78
|
'side-by-side',
|
|
79
|
+
'dark',
|
|
80
|
+
'light',
|
|
65
81
|
'saved',
|
|
66
82
|
'reloaded',
|
|
67
83
|
'not a diff block',
|
|
@@ -69,6 +85,7 @@ const INFO_STATUS = [
|
|
|
69
85
|
'diff mode',
|
|
70
86
|
'committed',
|
|
71
87
|
'amended',
|
|
88
|
+
'reworded',
|
|
72
89
|
'fixup',
|
|
73
90
|
'updated',
|
|
74
91
|
'updating reslop',
|
|
@@ -91,6 +108,7 @@ const INFO_STATUS = [
|
|
|
91
108
|
'checked out',
|
|
92
109
|
'created',
|
|
93
110
|
'empty branch name',
|
|
111
|
+
'empty commit message',
|
|
94
112
|
];
|
|
95
113
|
const INFO_PREFIX = ['checked out ', 'created ', 'rebased onto ', 'dropped '];
|
|
96
114
|
|
|
@@ -100,21 +118,12 @@ const dropPrompt = (name) => ` drop ${name}? y/n`;
|
|
|
100
118
|
const PUSH_REJECT_PREFIX = CONFIRM.confirmPush.prefix;
|
|
101
119
|
const PUSH_PROMPT = `${PUSH_REJECT_PREFIX} force push`;
|
|
102
120
|
|
|
121
|
+
const BUTTON_LEAD = ' ';
|
|
103
122
|
const BUTTON_GAP = ' ';
|
|
123
|
+
const BUTTON_EDGE = 1;
|
|
124
|
+
const buttonGap = (index) => (index ? BUTTON_GAP : BUTTON_LEAD);
|
|
104
125
|
const LOGO = '👁️';
|
|
105
126
|
|
|
106
|
-
const ORIGIN_LABEL = {
|
|
107
|
-
commit: (view) => view.revShort || 'commit',
|
|
108
|
-
pr: (view) => view.sourceLabel || 'pr',
|
|
109
|
-
todo: () => 'todo',
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
const originLabel = (origin, view) => {
|
|
113
|
-
const named = ORIGIN_LABEL[origin];
|
|
114
|
-
if (named) return named(view);
|
|
115
|
-
return origin ?? 'unknown';
|
|
116
|
-
};
|
|
117
|
-
|
|
118
127
|
const pathSegments = (rel) => {
|
|
119
128
|
const segs = [];
|
|
120
129
|
if (!rel) return segs;
|
|
@@ -132,91 +141,59 @@ const pathSegments = (rel) => {
|
|
|
132
141
|
return segs;
|
|
133
142
|
};
|
|
134
143
|
|
|
135
|
-
const
|
|
144
|
+
const headerScreen = (view) => {
|
|
136
145
|
const pane = view.pane ?? 'diff';
|
|
146
|
+
if (pane === 'branches') return 'branches';
|
|
147
|
+
if (pane === 'commits') {
|
|
148
|
+
const mode = view.commitView === 'full' ? 'full' : 'brief';
|
|
149
|
+
return `commits ${mode}`;
|
|
150
|
+
}
|
|
151
|
+
if (pane === 'npm') return 'npm';
|
|
137
152
|
if (pane === 'files') {
|
|
138
153
|
const files = view.files ?? [];
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
const n = total ? cursor + 1 : 0;
|
|
143
|
-
if (!entry) return { path: '', origin: '', n, total };
|
|
144
|
-
if (isTodosEntry(entry)) {
|
|
145
|
-
return { path: TODO_FILE, origin: 'todo', n, total };
|
|
146
|
-
}
|
|
147
|
-
return { path: entry.path, origin: entry.status, n, total };
|
|
148
|
-
}
|
|
149
|
-
if (pane === 'branches') {
|
|
150
|
-
const compose = view.compose;
|
|
151
|
-
const branches = view.branches ?? [];
|
|
152
|
-
if (compose && compose.kind === 'branch') {
|
|
153
|
-
const total = branches.length + 1;
|
|
154
|
-
return { path: compose.text || '', origin: 'new', n: total, total };
|
|
155
|
-
}
|
|
156
|
-
const cursor = view.branchCursor ?? 0;
|
|
157
|
-
const entry = branches[cursor];
|
|
158
|
-
const total = branches.length;
|
|
159
|
-
const n = total ? cursor + 1 : 0;
|
|
160
|
-
if (!entry) return { path: '', origin: 'branch', n, total };
|
|
161
|
-
const origin = entry.current ? 'current' : 'branch';
|
|
162
|
-
return { path: entry.name, origin, n, total };
|
|
154
|
+
const entry = files[view.fileCursor ?? 0];
|
|
155
|
+
if (isTodosEntry(entry)) return TODO_FILE;
|
|
156
|
+
return '';
|
|
163
157
|
}
|
|
164
|
-
if (
|
|
165
|
-
|
|
166
|
-
const cursor = view.commitCursor ?? 0;
|
|
167
|
-
const entry = commits[cursor];
|
|
168
|
-
const total = commits.length;
|
|
169
|
-
const n = total ? cursor + 1 : 0;
|
|
170
|
-
if (!entry) return { path: '', origin: 'commit', n, total };
|
|
171
|
-
const origin = entry.head ? 'HEAD' : 'commit';
|
|
172
|
-
return { path: entry.subject ?? '', origin, n, total };
|
|
173
|
-
}
|
|
174
|
-
if (pane === 'unit') {
|
|
175
|
-
const path = view.reviewPath ?? '';
|
|
176
|
-
const lines = view.unitLines ?? [];
|
|
177
|
-
const top = Math.max(0, (view.scroll ?? 0) - 1);
|
|
178
|
-
const n = lines.length ? Math.min(lines.length, top + 1) : 0;
|
|
179
|
-
const origin = view.item ? originLabel(view.item.origin, view) : 'unit';
|
|
180
|
-
return { path, origin, n, total: lines.length };
|
|
181
|
-
}
|
|
182
|
-
if (!view.item) return { path: '', origin: '', n: 0, total: view.total ?? 0 };
|
|
183
|
-
if (view.item.origin === 'todo') {
|
|
184
|
-
return { path: TODO_FILE, origin: 'todo', n: 1, total: 1 };
|
|
185
|
-
}
|
|
186
|
-
return {
|
|
187
|
-
path: itemPath(view.item),
|
|
188
|
-
origin: originLabel(view.item.origin, view),
|
|
189
|
-
n: view.index + 1,
|
|
190
|
-
total: view.total,
|
|
191
|
-
};
|
|
158
|
+
if (view.item && view.item.origin === 'todo') return TODO_FILE;
|
|
159
|
+
return '';
|
|
192
160
|
};
|
|
193
161
|
|
|
194
|
-
const
|
|
162
|
+
const headerTarget = (view) => {
|
|
163
|
+
const pane = view.pane ?? 'diff';
|
|
164
|
+
if (pane === 'files') {
|
|
165
|
+
const entry = (view.files ?? [])[view.fileCursor ?? 0];
|
|
166
|
+
if (!entry || isTodosEntry(entry)) return '';
|
|
167
|
+
return entry.path;
|
|
168
|
+
}
|
|
169
|
+
if (pane === 'unit') return view.reviewPath ?? '';
|
|
170
|
+
if (!view.item || view.item.origin === 'todo') return '';
|
|
171
|
+
return itemPath(view.item);
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const headerLabel = (view) => headerScreen(view) || headerTarget(view);
|
|
175
|
+
|
|
176
|
+
const headerSep = (view) => (headerScreen(view) ? ': ' : '/');
|
|
177
|
+
|
|
178
|
+
const headerPrefix = (view, label) => {
|
|
195
179
|
const repo = view.repoName || '';
|
|
196
|
-
|
|
197
|
-
if (repo && path) return ` ${LOGO} reslop: ${repo}/`;
|
|
180
|
+
if (repo && label) return ` ${LOGO} reslop: ${repo}${headerSep(view)}`;
|
|
198
181
|
if (repo) return ` ${LOGO} reslop: ${repo} `;
|
|
199
182
|
return ` ${LOGO} reslop: `;
|
|
200
183
|
};
|
|
201
184
|
|
|
202
|
-
const headerSuffix = ({ origin, n, total }) =>
|
|
203
|
-
origin ? ` ${origin} ${n}/${total}` : ` ${n}/${total}`;
|
|
204
|
-
|
|
205
185
|
const headerText = (view) => {
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
return `${prefix}${target.path}${headerSuffix(target)}`;
|
|
186
|
+
const label = headerLabel(view);
|
|
187
|
+
return `${headerPrefix(view, label)}${label}`;
|
|
209
188
|
};
|
|
210
189
|
|
|
211
190
|
const headerPieces = (view, width) => {
|
|
212
191
|
const repo = view.repoName || '';
|
|
213
|
-
const
|
|
214
|
-
const path = target.path;
|
|
215
|
-
const suffix = headerSuffix(target);
|
|
192
|
+
const path = headerLabel(view);
|
|
216
193
|
const right = ' ';
|
|
217
194
|
const chrome = ` ${LOGO} reslop: `;
|
|
218
|
-
const prefixW = visibleWidth(headerPrefix(view));
|
|
219
|
-
const used = prefixW + visibleWidth(
|
|
195
|
+
const prefixW = visibleWidth(headerPrefix(view, path));
|
|
196
|
+
const used = prefixW + visibleWidth(right);
|
|
220
197
|
const room = Math.max(0, width - used);
|
|
221
198
|
const clipped = truncateVisible(path, room);
|
|
222
199
|
const pad = Math.max(0, room - visibleWidth(clipped));
|
|
@@ -224,7 +201,9 @@ const headerPieces = (view, width) => {
|
|
|
224
201
|
if (repo) {
|
|
225
202
|
pieces.push({ text: repo, role: 'repo' });
|
|
226
203
|
if (path) {
|
|
227
|
-
|
|
204
|
+
const sep = headerSep(view);
|
|
205
|
+
const role = sep === '/' ? 'slash' : 'chrome';
|
|
206
|
+
pieces.push({ text: sep, role });
|
|
228
207
|
} else {
|
|
229
208
|
pieces.push({ text: ' ', role: 'chrome' });
|
|
230
209
|
}
|
|
@@ -235,7 +214,6 @@ const headerPieces = (view, width) => {
|
|
|
235
214
|
if (pad) {
|
|
236
215
|
pieces.push({ text: ' '.repeat(pad), role: 'chrome' });
|
|
237
216
|
}
|
|
238
|
-
pieces.push({ text: suffix, role: 'meta' });
|
|
239
217
|
pieces.push({ text: right, role: 'chrome' });
|
|
240
218
|
return pieces;
|
|
241
219
|
};
|
|
@@ -298,6 +276,7 @@ const layoutButtons = (width, compact, hidden, extra, disabled) => {
|
|
|
298
276
|
for (const add of extraIds) {
|
|
299
277
|
if (!ids.includes(add)) ids.push(add);
|
|
300
278
|
}
|
|
279
|
+
continue;
|
|
301
280
|
}
|
|
302
281
|
if (hiddenIds.includes(id)) continue;
|
|
303
282
|
ids.push(id);
|
|
@@ -306,13 +285,15 @@ const layoutButtons = (width, compact, hidden, extra, disabled) => {
|
|
|
306
285
|
const hits = [];
|
|
307
286
|
const parts = [];
|
|
308
287
|
let x = 0;
|
|
288
|
+
const limit = Math.max(0, width - BUTTON_EDGE);
|
|
309
289
|
for (const id of ids) {
|
|
310
290
|
const letter = actionLetter(id);
|
|
311
291
|
const word = buttonWord(id);
|
|
312
292
|
const label = short ? letter : word;
|
|
313
|
-
const
|
|
293
|
+
const gap = buttonGap(parts.length);
|
|
294
|
+
const piece = `${gap}${label}`;
|
|
314
295
|
const w = visibleWidth(piece);
|
|
315
|
-
if (x + w >
|
|
296
|
+
if (x + w > limit && parts.length) break;
|
|
316
297
|
const off = disabledIds.includes(id);
|
|
317
298
|
if (!off) hits.push({ id, x0: x, x1: x + w });
|
|
318
299
|
parts.push({ action: { id }, piece, letter, label, disabled: off });
|
|
@@ -331,8 +312,10 @@ const paintButtons = (layout, width, color) => {
|
|
|
331
312
|
let used = 0;
|
|
332
313
|
const fgRgb = THEME.buttonFg;
|
|
333
314
|
const bgRgb = THEME.buttonBg;
|
|
334
|
-
for (
|
|
335
|
-
|
|
315
|
+
for (let i = 0; i < layout.parts.length; i++) {
|
|
316
|
+
const part = layout.parts[i];
|
|
317
|
+
const gap = buttonGap(i);
|
|
318
|
+
out += paint(gap, fgRgb, bgRgb, color);
|
|
336
319
|
if (part.disabled) out += paint(part.label, fgRgb, bgRgb, color);
|
|
337
320
|
else out += paintMarkedWord(part.label, part.letter, color, BUTTON_MARK);
|
|
338
321
|
used += visibleWidth(part.piece);
|
|
@@ -470,14 +453,10 @@ const paintChoicePrompt = (choices, width, color, prefix = '') => {
|
|
|
470
453
|
return out + fill(pad, THEME.mutedFg, bgRgb, color);
|
|
471
454
|
};
|
|
472
455
|
|
|
473
|
-
const STATUS_MARK = /(
|
|
456
|
+
const STATUS_MARK = /(\+[0-9]+(?:\/[0-9]+)?|-[0-9]+(?:\/[0-9]+)?)/g;
|
|
474
457
|
|
|
475
458
|
const paintStatusMark = (token, color) => {
|
|
476
459
|
const bgRgb = THEME.chromeBg;
|
|
477
|
-
const muted = THEME.mutedFg;
|
|
478
|
-
if (token.startsWith('[')) {
|
|
479
|
-
return paintBracketName(token, THEME.buttonHotFg, muted, bgRgb, color);
|
|
480
|
-
}
|
|
481
460
|
if (token.startsWith('+')) {
|
|
482
461
|
return paint(token, THEME.addLineFg, bgRgb, color);
|
|
483
462
|
}
|
|
@@ -505,6 +484,68 @@ const paintStatusLeft = (text, color) => {
|
|
|
505
484
|
return out;
|
|
506
485
|
};
|
|
507
486
|
|
|
487
|
+
const paintBranchHead = (text, branch, color) => {
|
|
488
|
+
const name = branchLabel(branch);
|
|
489
|
+
if (!name || !text.startsWith(' ')) return null;
|
|
490
|
+
const head = ` ${name}`;
|
|
491
|
+
const shown = text.startsWith(head) ? name : text.slice(1);
|
|
492
|
+
if (!shown || !name.startsWith(shown)) return null;
|
|
493
|
+
const rest = text.slice(1 + shown.length);
|
|
494
|
+
const bgRgb = THEME.chromeBg;
|
|
495
|
+
return (
|
|
496
|
+
paint(' ', THEME.mutedFg, bgRgb, color) +
|
|
497
|
+
paint(shown, THEME.buttonHotFg, bgRgb, color, true) +
|
|
498
|
+
paintStatusLeft(rest, color)
|
|
499
|
+
);
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
const SAVE_HINT = 'Press **Ctrl-S** or **Enter** at EOF to save';
|
|
503
|
+
const HINT_HOT_FG = 0xff;
|
|
504
|
+
|
|
505
|
+
const hintParts = (source) => {
|
|
506
|
+
const bits = source.split('**');
|
|
507
|
+
const parts = [];
|
|
508
|
+
for (let i = 0; i < bits.length; i++) {
|
|
509
|
+
if (!bits[i]) continue;
|
|
510
|
+
parts.push({ text: bits[i], hot: i % 2 === 1 });
|
|
511
|
+
}
|
|
512
|
+
return parts;
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
const saveHintText = () => SAVE_HINT.replaceAll('**', '');
|
|
516
|
+
|
|
517
|
+
const commitSaveHint = (view) => {
|
|
518
|
+
if (view.mode !== 'compose' || view.commitView !== 'full') return false;
|
|
519
|
+
const compose = view.compose;
|
|
520
|
+
return Boolean(compose && compose.kind === 'commit');
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
const paintSaveHintBar = (width, color) => {
|
|
524
|
+
const bgRgb = THEME.buttonBg;
|
|
525
|
+
const plain = `${BUTTON_LEAD}${saveHintText()}`;
|
|
526
|
+
if (!color) {
|
|
527
|
+
const pad = Math.max(0, width - visibleWidth(plain));
|
|
528
|
+
return plain + ' '.repeat(pad);
|
|
529
|
+
}
|
|
530
|
+
let out = paint(BUTTON_LEAD, THEME.buttonFg, bgRgb, color);
|
|
531
|
+
for (const part of hintParts(SAVE_HINT)) {
|
|
532
|
+
const tone = part.hot ? HINT_HOT_FG : THEME.buttonFg;
|
|
533
|
+
out += paint(part.text, tone, bgRgb, color, part.hot);
|
|
534
|
+
}
|
|
535
|
+
const pad = Math.max(0, width - visibleWidth(plain));
|
|
536
|
+
return out + fill(pad, THEME.buttonFg, bgRgb, color);
|
|
537
|
+
};
|
|
538
|
+
|
|
539
|
+
const paintFooter = (view, layout, width, color) => {
|
|
540
|
+
if (commitSaveHint(view)) return paintSaveHintBar(width, color);
|
|
541
|
+
return paintButtons(layout, width, color);
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
const footerHits = (view, layout) => {
|
|
545
|
+
if (commitSaveHint(view)) return [];
|
|
546
|
+
return layout.hits;
|
|
547
|
+
};
|
|
548
|
+
|
|
508
549
|
const paintStatusLine = (view, status, width, color) => {
|
|
509
550
|
const confirm = CONFIRM[view.mode];
|
|
510
551
|
if (confirm) {
|
|
@@ -518,8 +559,13 @@ const paintStatusLine = (view, status, width, color) => {
|
|
|
518
559
|
return layout.clipped + leftGap + layout.msg + rightGap + layout.right;
|
|
519
560
|
}
|
|
520
561
|
const bgRgb = THEME.chromeBg;
|
|
562
|
+
const worktree = countsKind(view.sourceKind, view.revShort) === 'worktree';
|
|
563
|
+
const head = worktree
|
|
564
|
+
? paintBranchHead(layout.clipped, view.branch, color)
|
|
565
|
+
: null;
|
|
566
|
+
const left = head ?? paintStatusLeft(layout.clipped, color);
|
|
521
567
|
return (
|
|
522
|
-
|
|
568
|
+
left +
|
|
523
569
|
paint(leftGap, THEME.mutedFg, bgRgb, color) +
|
|
524
570
|
paint(layout.msg, layout.msgFg, bgRgb, color) +
|
|
525
571
|
paint(rightGap, THEME.mutedFg, bgRgb, color) +
|
|
@@ -541,10 +587,10 @@ const statusBranchHit = (view, width, y) => {
|
|
|
541
587
|
if (!branchLabel(branch)) return null;
|
|
542
588
|
if (countsKind(view.sourceKind, view.revShort) !== 'worktree') return null;
|
|
543
589
|
const layout = statusLayout(view, view.status ?? '', width);
|
|
544
|
-
const
|
|
545
|
-
const
|
|
546
|
-
if (
|
|
547
|
-
return { id: 'branch', y, x0:
|
|
590
|
+
const name = branchLabel(branch);
|
|
591
|
+
const at = layout.clipped.indexOf(name);
|
|
592
|
+
if (at < 0) return null;
|
|
593
|
+
return { id: 'branch', y, x0: at, x1: at + visibleWidth(name) };
|
|
548
594
|
};
|
|
549
595
|
|
|
550
596
|
const selectedBranch = (view) => (view.branches ?? [])[view.branchCursor ?? 0];
|
|
@@ -557,6 +603,7 @@ const hiddenActions = (view) => {
|
|
|
557
603
|
if (pane === 'unit') return UNIT_DISABLED;
|
|
558
604
|
if (pane === 'branches') return BRANCHES_DISABLED;
|
|
559
605
|
if (pane === 'commits') return COMMITS_DISABLED;
|
|
606
|
+
if (pane === 'npm') return NPM_DISABLED;
|
|
560
607
|
if (view.item && view.item.origin === 'todo') return TODO_DISABLED;
|
|
561
608
|
return DIFF_DISABLED;
|
|
562
609
|
};
|
|
@@ -577,6 +624,10 @@ const dimFooterActions = (view) => {
|
|
|
577
624
|
if (!entry) return commitGitDisabled({ canCommit: staged });
|
|
578
625
|
return commitGitDisabled({ ...entry, canCommit: staged });
|
|
579
626
|
}
|
|
627
|
+
if (pane === 'npm' && view.npmView) {
|
|
628
|
+
if (view.npmRunning) return ['npmRerun'];
|
|
629
|
+
return [];
|
|
630
|
+
}
|
|
580
631
|
return [];
|
|
581
632
|
};
|
|
582
633
|
|
|
@@ -584,10 +635,14 @@ const extraFooterActions = (view) => {
|
|
|
584
635
|
const pane = view.pane ?? 'diff';
|
|
585
636
|
if (pane === 'files') {
|
|
586
637
|
const scope = view.fileScope === 'file' ? 'diff' : 'file';
|
|
587
|
-
return [scope, 'commit', 'pull', 'push'];
|
|
638
|
+
return ['npm', scope, 'commit', 'pull', 'push'];
|
|
588
639
|
}
|
|
589
640
|
if (pane === 'branches') return Object.keys(BRANCH_ACTIONS);
|
|
590
641
|
if (pane === 'commits') return Object.keys(COMMIT_ACTIONS);
|
|
642
|
+
if (pane === 'npm') {
|
|
643
|
+
if (view.npmView) return ['npmStop', 'npmRerun'];
|
|
644
|
+
return ['npmEdit', 'npmNew'];
|
|
645
|
+
}
|
|
591
646
|
return [];
|
|
592
647
|
};
|
|
593
648
|
|
|
@@ -601,6 +656,8 @@ module.exports = {
|
|
|
601
656
|
paintHeader,
|
|
602
657
|
layoutButtons,
|
|
603
658
|
paintButtons,
|
|
659
|
+
paintFooter,
|
|
660
|
+
footerHits,
|
|
604
661
|
paintStatusLine,
|
|
605
662
|
statusChoiceHits,
|
|
606
663
|
statusBranchHit,
|