reslop 0.2.4 → 0.2.6
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 -1
- package/LICENSE +1 -1
- package/README.md +12 -13
- package/bin/reslop.js +3 -8
- package/lib/ansi.js +32 -19
- package/lib/capabilities.js +8 -12
- package/lib/cli.js +40 -74
- package/lib/clipboard.js +48 -18
- package/lib/dashboard/git.js +128 -0
- package/lib/dashboard/model.js +332 -0
- package/lib/dashboard/npm.js +193 -0
- package/lib/dashboard/tree.js +278 -0
- package/lib/deps-diff.js +196 -404
- package/lib/deps.js +240 -399
- package/lib/detect.js +8 -18
- package/lib/diff/align.js +41 -113
- package/lib/diff/diff.js +12 -43
- package/lib/diff/display.js +25 -36
- package/lib/diff/edit.js +6 -20
- package/lib/diff/inline.js +138 -141
- package/lib/diff/items.js +33 -47
- package/lib/diff/parse.js +28 -56
- package/lib/diff/patch.js +12 -33
- package/lib/diff/unit.js +90 -206
- package/lib/editor.js +98 -55
- package/lib/files.js +86 -103
- package/lib/git-branches.js +254 -416
- package/lib/git-deps.js +182 -344
- package/lib/git-reword-todo.js +5 -13
- package/lib/git-worktree.js +141 -212
- package/lib/git.js +21 -49
- package/lib/github.js +67 -181
- package/lib/gitlab.js +40 -90
- package/lib/highlight/bash.js +55 -177
- package/lib/highlight/core.js +157 -132
- package/lib/highlight/css.js +25 -58
- package/lib/highlight/csv.js +19 -37
- package/lib/highlight/dart.js +72 -213
- package/lib/highlight/dot.js +16 -26
- package/lib/highlight/html.js +42 -63
- package/lib/highlight/js.js +177 -506
- package/lib/highlight/json.js +17 -37
- package/lib/highlight/log.js +22 -35
- package/lib/highlight/txt.js +9 -22
- package/lib/highlight.js +17 -46
- package/lib/keys.js +62 -85
- package/lib/manifest.js +67 -130
- package/lib/npm-commands.js +119 -189
- package/lib/remote.js +141 -197
- package/lib/render/branches.js +66 -73
- package/lib/render/chrome.js +264 -323
- package/lib/render/commits.js +187 -500
- package/lib/render/dash-activity.js +212 -0
- package/lib/render/dash-blocks.js +245 -0
- package/lib/render/dash-table.js +290 -0
- package/lib/render/dashboard.js +62 -0
- package/lib/render/diff.js +123 -228
- package/lib/render/files.js +45 -99
- package/lib/render/frame.js +129 -183
- package/lib/render/notes.js +144 -160
- package/lib/render/npm.js +234 -112
- package/lib/render/present.js +4 -11
- package/lib/render/primitives.js +131 -170
- package/lib/render/render.js +5 -14
- package/lib/render/report.js +43 -0
- package/lib/render/tiles.js +260 -0
- package/lib/render/unit.js +15 -28
- package/lib/report-model.js +110 -33
- package/lib/report-parse.js +56 -38
- package/lib/report-render.js +102 -59
- package/lib/report-run.js +87 -78
- package/lib/report-test.js +256 -141
- package/lib/report-tools.js +96 -161
- package/lib/review-merge.js +114 -152
- package/lib/review.js +115 -189
- package/lib/runs.js +225 -0
- package/lib/select.js +89 -75
- package/lib/session/accessors.js +14 -21
- package/lib/session/actions.js +82 -63
- package/lib/session/branches.js +193 -247
- package/lib/session/change-actions.js +171 -214
- package/lib/session/commit.js +280 -342
- package/lib/session/compose.js +559 -674
- package/lib/session/dashboard.js +385 -0
- package/lib/session/dispatch.js +164 -157
- package/lib/session/edit-click.js +57 -0
- package/lib/session/files-pane.js +85 -53
- package/lib/session/items.js +73 -104
- package/lib/session/lifecycle.js +403 -541
- package/lib/session/load.js +48 -68
- package/lib/session/navigation.js +96 -87
- package/lib/session/npm.js +409 -345
- package/lib/session/ops.js +196 -63
- package/lib/session/pointer.js +23 -15
- package/lib/session/progress.js +33 -21
- package/lib/session/review.js +33 -54
- package/lib/session/tasks.js +333 -0
- package/lib/session/templates.js +51 -59
- package/lib/session/terminal.js +85 -136
- package/lib/session/unit.js +3 -4
- package/lib/session/update.js +76 -97
- package/lib/session/view.js +52 -83
- package/lib/session/watch.js +208 -204
- package/lib/session.js +112 -237
- package/lib/update.js +45 -116
- package/lib/utilities.js +67 -89
- package/lib/wrap.js +33 -44
- package/package.json +1 -1
- package/lib/session/todos.js +0 -293
package/lib/render/chrome.js
CHANGED
|
@@ -5,38 +5,24 @@ const keys = require('../keys.js');
|
|
|
5
5
|
const files = require('../files.js');
|
|
6
6
|
const primitives = require('./primitives.js');
|
|
7
7
|
|
|
8
|
-
const { itemPath,
|
|
9
|
-
const { ACTION_IDS,
|
|
10
|
-
const { buttonWord } = keys;
|
|
8
|
+
const { itemPath, REPO_TASKS_LABEL, isTasksEntry, fileTotals } = files;
|
|
9
|
+
const { ACTION_IDS, actionLetter } = keys;
|
|
10
|
+
const { DASH_IDS, DASH_HIDDEN, buttonWord } = keys;
|
|
11
11
|
const { FILES_HIDDEN, FILES_GIT_DISABLED, BRANCHES_DISABLED } = keys;
|
|
12
|
-
const { COMMITS_DISABLED,
|
|
12
|
+
const { COMMITS_DISABLED, TASKS_DISABLED, DIFF_DISABLED, UNIT_DISABLED } = keys;
|
|
13
13
|
const { NPM_DISABLED } = keys;
|
|
14
14
|
const { diffGitDisabled, branchGitDisabled, commitGitDisabled } = keys;
|
|
15
15
|
const { CONFIRM, confirmPrefix, promptFromChoices } = keys;
|
|
16
16
|
const { THEME, paint, visibleWidth } = ansi;
|
|
17
17
|
const { truncateVisible, fg, RESET, EL, seq } = ansi;
|
|
18
|
-
const { fill, branchLabel } = primitives;
|
|
19
|
-
|
|
20
|
-
const HEADER_ROLE_FG = {
|
|
21
|
-
repo: THEME.headerRepoFg,
|
|
22
|
-
dir: THEME.headerDirFg,
|
|
23
|
-
slash: THEME.headerSlashFg,
|
|
24
|
-
file: THEME.headerFileFg,
|
|
25
|
-
};
|
|
18
|
+
const { fill, branchLabel, isTaskView } = primitives;
|
|
26
19
|
|
|
27
20
|
const WAVE = '·•●•·';
|
|
21
|
+
const BUSY_BAR = Array.from(WAVE, (_, frame) => {
|
|
22
|
+
const start = (WAVE.length - frame) % WAVE.length;
|
|
23
|
+
return WAVE.slice(start) + WAVE.slice(0, start);
|
|
24
|
+
});
|
|
28
25
|
|
|
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);
|
|
40
26
|
const BUSY_PROGRESS = [
|
|
41
27
|
'pulling',
|
|
42
28
|
'pushing',
|
|
@@ -112,143 +98,159 @@ const INFO_STATUS = [
|
|
|
112
98
|
];
|
|
113
99
|
const INFO_PREFIX = ['checked out ', 'created ', 'rebased onto ', 'dropped '];
|
|
114
100
|
|
|
115
|
-
const
|
|
116
|
-
const
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
101
|
+
const LOGO = '👁️';
|
|
102
|
+
const HEADER_LEAD = ` ${LOGO} reslop: `;
|
|
103
|
+
const HEADER_ROLE_FG = {
|
|
104
|
+
chrome: THEME.headerChromeFg,
|
|
105
|
+
repo: THEME.headerRepoFg,
|
|
106
|
+
dir: THEME.headerDirFg,
|
|
107
|
+
slash: THEME.headerSlashFg,
|
|
108
|
+
file: THEME.headerFileFg,
|
|
109
|
+
};
|
|
120
110
|
|
|
121
111
|
const BUTTON_LEAD = ' ';
|
|
122
112
|
const BUTTON_GAP = ' ';
|
|
123
113
|
const BUTTON_EDGE = 1;
|
|
124
|
-
const
|
|
125
|
-
const
|
|
114
|
+
const BACK_ARROW = '🢐';
|
|
115
|
+
const STOP_MARK = '⊗';
|
|
116
|
+
const BACK_KEY = 'esc';
|
|
117
|
+
|
|
118
|
+
const BUTTON_MARK = {
|
|
119
|
+
bgRgb: THEME.buttonBg,
|
|
120
|
+
fgRgb: THEME.buttonFg,
|
|
121
|
+
hotFg: THEME.buttonHotFg,
|
|
122
|
+
missFg: THEME.buttonHotFg,
|
|
123
|
+
missBold: true,
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const QUIT_MARK = {
|
|
127
|
+
bgRgb: THEME.chromeBg,
|
|
128
|
+
fgRgb: THEME.mutedFg,
|
|
129
|
+
hotFg: THEME.warnFg,
|
|
130
|
+
missFg: THEME.mutedFg,
|
|
131
|
+
missBold: false,
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const STATUS_MARK = /(\+[0-9]+(?:\/[0-9]+)?|-[0-9]+(?:\/[0-9]+)?)/g;
|
|
135
|
+
const OLD_LOGS = /old logs \S+/;
|
|
136
|
+
const SAVE_HINT = 'Press **Ctrl-S** or **Enter** at EOF to save';
|
|
137
|
+
const HINT_HOT_FG = 0xff;
|
|
138
|
+
|
|
139
|
+
const selectedFile = (view) => (view.files ?? [])[view.fileCursor ?? 0];
|
|
140
|
+
|
|
141
|
+
const selectedBranch = (view) => (view.branches ?? [])[view.branchCursor ?? 0];
|
|
142
|
+
|
|
143
|
+
const selectedCommit = (view) => (view.commits ?? [])[view.commitCursor ?? 0];
|
|
144
|
+
|
|
145
|
+
const piecesText = (pieces) => pieces.map((piece) => piece.text).join('');
|
|
126
146
|
|
|
127
147
|
const pathSegments = (rel) => {
|
|
128
148
|
const segs = [];
|
|
129
149
|
if (!rel) return segs;
|
|
130
150
|
const bits = rel.split('/');
|
|
131
151
|
for (let i = 0; i < bits.length; i++) {
|
|
132
|
-
if (i) {
|
|
133
|
-
|
|
134
|
-
}
|
|
135
|
-
if (!bits[i]) {
|
|
136
|
-
continue;
|
|
137
|
-
}
|
|
138
|
-
const last = i === bits.length - 1;
|
|
139
|
-
segs.push({ text: bits[i], role: last ? 'file' : 'dir' });
|
|
152
|
+
if (i) segs.push({ text: '/', role: 'slash' });
|
|
153
|
+
const role = i === bits.length - 1 ? 'file' : 'dir';
|
|
154
|
+
if (bits[i]) segs.push({ text: bits[i], role });
|
|
140
155
|
}
|
|
141
156
|
return segs;
|
|
142
157
|
};
|
|
143
158
|
|
|
144
159
|
const headerScreen = (view) => {
|
|
145
|
-
const pane = view
|
|
146
|
-
if (
|
|
160
|
+
const { pane } = view;
|
|
161
|
+
if (
|
|
162
|
+
pane === 'dashboard' ||
|
|
163
|
+
pane === 'branches' ||
|
|
164
|
+
pane === 'npm' ||
|
|
165
|
+
pane === 'tasks'
|
|
166
|
+
) {
|
|
167
|
+
return pane;
|
|
168
|
+
}
|
|
147
169
|
if (pane === 'commits') {
|
|
148
|
-
|
|
149
|
-
return `commits ${mode}`;
|
|
170
|
+
return view.commitView === 'full' ? 'commits full' : 'commits brief';
|
|
150
171
|
}
|
|
151
|
-
if (pane === 'npm') return 'npm';
|
|
152
172
|
if (pane === 'files') {
|
|
153
|
-
|
|
154
|
-
const entry = files[view.fileCursor ?? 0];
|
|
155
|
-
if (isTodosEntry(entry)) return REPO_TODOS_LABEL;
|
|
156
|
-
return '';
|
|
173
|
+
return isTasksEntry(selectedFile(view)) ? REPO_TASKS_LABEL : '';
|
|
157
174
|
}
|
|
158
|
-
|
|
159
|
-
return '';
|
|
175
|
+
return isTaskView(view) ? REPO_TASKS_LABEL : '';
|
|
160
176
|
};
|
|
161
177
|
|
|
162
178
|
const headerTarget = (view) => {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
if (!entry || isTodosEntry(entry)) return '';
|
|
179
|
+
if (view.pane === 'files') {
|
|
180
|
+
const entry = selectedFile(view);
|
|
181
|
+
if (!entry || isTasksEntry(entry)) return '';
|
|
167
182
|
return entry.path;
|
|
168
183
|
}
|
|
169
|
-
if (pane === 'unit') return view.reviewPath ?? '';
|
|
170
|
-
if (
|
|
184
|
+
if (view.pane === 'unit') return view.reviewPath ?? '';
|
|
185
|
+
if (isTaskView(view)) return '';
|
|
171
186
|
return itemPath(view.item);
|
|
172
187
|
};
|
|
173
188
|
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
const headerPrefix = (view, label) => {
|
|
189
|
+
const headerLead = (view) => {
|
|
190
|
+
const screen = headerScreen(view);
|
|
191
|
+
const label = screen || headerTarget(view);
|
|
179
192
|
const repo = view.repoName || '';
|
|
180
|
-
|
|
181
|
-
if (repo)
|
|
182
|
-
|
|
193
|
+
const pieces = [{ text: HEADER_LEAD, role: 'chrome' }];
|
|
194
|
+
if (repo) {
|
|
195
|
+
pieces.push({ text: repo, role: 'repo' });
|
|
196
|
+
if (!label) pieces.push({ text: ' ', role: 'chrome' });
|
|
197
|
+
else if (screen) pieces.push({ text: ': ', role: 'chrome' });
|
|
198
|
+
else pieces.push({ text: '/', role: 'slash' });
|
|
199
|
+
}
|
|
200
|
+
return { pieces, label };
|
|
183
201
|
};
|
|
184
202
|
|
|
185
203
|
const headerText = (view) => {
|
|
186
|
-
const label =
|
|
187
|
-
return `${
|
|
204
|
+
const { pieces, label } = headerLead(view);
|
|
205
|
+
return `${piecesText(pieces)}${label}`;
|
|
188
206
|
};
|
|
189
207
|
|
|
190
|
-
const
|
|
191
|
-
const
|
|
192
|
-
const path = headerLabel(view);
|
|
208
|
+
const paintHeader = (view, width, color) => {
|
|
209
|
+
const { pieces, label } = headerLead(view);
|
|
193
210
|
const right = ' ';
|
|
194
|
-
const
|
|
195
|
-
const prefixW = visibleWidth(headerPrefix(view, path));
|
|
196
|
-
const used = prefixW + visibleWidth(right);
|
|
211
|
+
const used = visibleWidth(piecesText(pieces)) + visibleWidth(right);
|
|
197
212
|
const room = Math.max(0, width - used);
|
|
198
|
-
const clipped = truncateVisible(
|
|
213
|
+
const clipped = truncateVisible(label, room);
|
|
199
214
|
const pad = Math.max(0, room - visibleWidth(clipped));
|
|
200
|
-
|
|
201
|
-
if (
|
|
202
|
-
pieces.push({ text: repo, role: 'repo' });
|
|
203
|
-
if (path) {
|
|
204
|
-
const sep = headerSep(view);
|
|
205
|
-
const role = sep === '/' ? 'slash' : 'chrome';
|
|
206
|
-
pieces.push({ text: sep, role });
|
|
207
|
-
} else {
|
|
208
|
-
pieces.push({ text: ' ', role: 'chrome' });
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
for (const seg of pathSegments(clipped)) {
|
|
212
|
-
pieces.push(seg);
|
|
213
|
-
}
|
|
214
|
-
if (pad) {
|
|
215
|
-
pieces.push({ text: ' '.repeat(pad), role: 'chrome' });
|
|
216
|
-
}
|
|
215
|
+
pieces.push(...pathSegments(clipped));
|
|
216
|
+
if (pad) pieces.push({ text: ' '.repeat(pad), role: 'chrome' });
|
|
217
217
|
pieces.push({ text: right, role: 'chrome' });
|
|
218
|
-
return pieces;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
const paintHeader = (view, width, color) => {
|
|
222
|
-
const pieces = headerPieces(view, width);
|
|
223
|
-
if (!color) {
|
|
224
|
-
return pieces.map((piece) => piece.text).join('');
|
|
225
|
-
}
|
|
226
|
-
const bgRgb = THEME.headerBg;
|
|
227
|
-
let out = `${seq(THEME.headerChromeFg, bgRgb)}${EL}`;
|
|
218
|
+
if (!color) return piecesText(pieces);
|
|
219
|
+
let out = `${seq(THEME.headerChromeFg, THEME.headerBg)}${EL}`;
|
|
228
220
|
for (const piece of pieces) {
|
|
229
|
-
|
|
230
|
-
continue;
|
|
231
|
-
}
|
|
232
|
-
const roleFg = HEADER_ROLE_FG[piece.role] ?? THEME.headerChromeFg;
|
|
233
|
-
out += `${fg(roleFg)}${piece.text}`;
|
|
221
|
+
out += `${fg(HEADER_ROLE_FG[piece.role])}${piece.text}`;
|
|
234
222
|
}
|
|
235
223
|
return `${out}${RESET}`;
|
|
236
224
|
};
|
|
237
225
|
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
226
|
+
const backFooterId = (view) => {
|
|
227
|
+
const { pane } = view;
|
|
228
|
+
if (pane === 'branches' || pane === 'commits' || pane === 'tasks') {
|
|
229
|
+
return 'back';
|
|
230
|
+
}
|
|
231
|
+
if (pane === 'npm') return view.npmView ? 'npmStop' : 'back';
|
|
232
|
+
return isTaskView(view) ? 'back' : '';
|
|
244
233
|
};
|
|
245
234
|
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
235
|
+
const backHintMark = (view) => {
|
|
236
|
+
if (view.pane === 'npm' && view.npmView && view.npmRunning) return STOP_MARK;
|
|
237
|
+
return BACK_ARROW;
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
const buttonGap = (index) => (index ? BUTTON_GAP : BUTTON_LEAD);
|
|
241
|
+
|
|
242
|
+
const hintBody = (mark) => {
|
|
243
|
+
const gap = mark === STOP_MARK ? ' ' : '';
|
|
244
|
+
return `${mark}${gap}${BACK_KEY}`;
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
const buttonLabel = (id, short) => (short ? actionLetter(id) : buttonWord(id));
|
|
248
|
+
|
|
249
|
+
const paintBackHint = (color, mark) => {
|
|
250
|
+
const bgRgb = THEME.buttonBg;
|
|
251
|
+
const rest = hintBody(mark).slice(mark.length);
|
|
252
|
+
const arrow = paint(mark, THEME.buttonHotFg, bgRgb, color, true);
|
|
253
|
+
return arrow + paint(rest, THEME.buttonFg, bgRgb, color);
|
|
252
254
|
};
|
|
253
255
|
|
|
254
256
|
const paintMarkedWord = (label, letter, color, mark) => {
|
|
@@ -259,47 +261,46 @@ const paintMarkedWord = (label, letter, color, mark) => {
|
|
|
259
261
|
const before = label.slice(0, at);
|
|
260
262
|
const hot = label.slice(at, at + letter.length);
|
|
261
263
|
const after = label.slice(at + letter.length);
|
|
262
|
-
let out =
|
|
263
|
-
if (before) out += paint(before, mark.fgRgb, mark.bgRgb, color);
|
|
264
|
+
let out = paint(before, mark.fgRgb, mark.bgRgb, color);
|
|
264
265
|
out += paint(hot, mark.hotFg, mark.bgRgb, color, true);
|
|
265
|
-
|
|
266
|
-
return out;
|
|
266
|
+
return out + paint(after, mark.fgRgb, mark.bgRgb, color);
|
|
267
267
|
};
|
|
268
268
|
|
|
269
|
-
const
|
|
270
|
-
const
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
|
|
269
|
+
const footerIds = (hidden, extra, backId) => {
|
|
270
|
+
const ids = backId ? [backId] : [];
|
|
271
|
+
for (const add of extra) {
|
|
272
|
+
if (!ids.includes(add)) ids.push(add);
|
|
273
|
+
}
|
|
274
274
|
for (const id of ACTION_IDS) {
|
|
275
|
-
if (id === 'quit')
|
|
276
|
-
for (const add of extraIds) {
|
|
277
|
-
if (!ids.includes(add)) ids.push(add);
|
|
278
|
-
}
|
|
279
|
-
continue;
|
|
280
|
-
}
|
|
281
|
-
if (hiddenIds.includes(id)) continue;
|
|
275
|
+
if (id === 'quit' || hidden.includes(id) || ids.includes(id)) continue;
|
|
282
276
|
ids.push(id);
|
|
283
277
|
}
|
|
278
|
+
return ids;
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
const layoutButtons = (width, options = {}) => {
|
|
282
|
+
const { compact = false, hidden = [], extra = [], disabled = [] } = options;
|
|
283
|
+
const { backId = '', mark = BACK_ARROW } = options;
|
|
284
|
+
const ids = footerIds(hidden, extra, backId);
|
|
285
|
+
const limit = Math.max(0, width - BUTTON_EDGE);
|
|
284
286
|
const build = (short) => {
|
|
285
287
|
const hits = [];
|
|
286
288
|
const parts = [];
|
|
287
289
|
let x = 0;
|
|
288
|
-
const limit = Math.max(0, width - BUTTON_EDGE);
|
|
289
290
|
for (const id of ids) {
|
|
290
|
-
const
|
|
291
|
-
const
|
|
292
|
-
const label =
|
|
293
|
-
const
|
|
294
|
-
const piece = `${gap}${label}`;
|
|
291
|
+
const back = id === backId;
|
|
292
|
+
const letter = back ? BACK_KEY : actionLetter(id);
|
|
293
|
+
const label = back ? hintBody(mark) : buttonLabel(id, short);
|
|
294
|
+
const piece = `${buttonGap(parts.length)}${label}`;
|
|
295
295
|
const w = visibleWidth(piece);
|
|
296
296
|
if (x + w > limit && parts.length) break;
|
|
297
|
-
const off =
|
|
297
|
+
const off = disabled.includes(id);
|
|
298
298
|
if (!off) hits.push({ id, x0: x, x1: x + w });
|
|
299
|
-
|
|
299
|
+
const hint = back ? mark : '';
|
|
300
|
+
parts.push({ id, piece, letter, label, disabled: off, back, mark: hint });
|
|
300
301
|
x += w;
|
|
301
302
|
}
|
|
302
|
-
return { hits, parts
|
|
303
|
+
return { hits, parts };
|
|
303
304
|
};
|
|
304
305
|
if (compact) return build(true);
|
|
305
306
|
const words = build(false);
|
|
@@ -308,72 +309,55 @@ const layoutButtons = (width, compact, hidden, extra, disabled) => {
|
|
|
308
309
|
};
|
|
309
310
|
|
|
310
311
|
const paintButtons = (layout, width, color) => {
|
|
311
|
-
let out = '';
|
|
312
|
-
let used = 0;
|
|
313
312
|
const fgRgb = THEME.buttonFg;
|
|
314
313
|
const bgRgb = THEME.buttonBg;
|
|
314
|
+
let out = '';
|
|
315
|
+
let used = 0;
|
|
315
316
|
for (let i = 0; i < layout.parts.length; i++) {
|
|
316
317
|
const part = layout.parts[i];
|
|
317
|
-
|
|
318
|
-
out +=
|
|
319
|
-
if (part.disabled) out += paint(part.label, fgRgb, bgRgb, color);
|
|
318
|
+
out += paint(buttonGap(i), fgRgb, bgRgb, color);
|
|
319
|
+
if (part.back) out += paintBackHint(color, part.mark);
|
|
320
|
+
else if (part.disabled) out += paint(part.label, fgRgb, bgRgb, color);
|
|
320
321
|
else out += paintMarkedWord(part.label, part.letter, color, BUTTON_MARK);
|
|
321
322
|
used += visibleWidth(part.piece);
|
|
322
323
|
}
|
|
323
|
-
|
|
324
|
-
out += fill(width - used, THEME.buttonFg, THEME.buttonBg, color);
|
|
325
|
-
}
|
|
326
|
-
return out;
|
|
324
|
+
return out + fill(width - used, fgRgb, bgRgb, color);
|
|
327
325
|
};
|
|
328
326
|
|
|
329
|
-
const isInfoStatus = (status) =>
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
if (status.startsWith(prefix)) return true;
|
|
333
|
-
}
|
|
334
|
-
return false;
|
|
335
|
-
};
|
|
327
|
+
const isInfoStatus = (status) =>
|
|
328
|
+
INFO_STATUS.includes(status) ||
|
|
329
|
+
INFO_PREFIX.some((prefix) => status.startsWith(prefix));
|
|
336
330
|
|
|
337
|
-
const joinStatus = (parts) =>
|
|
338
|
-
const bits = [];
|
|
339
|
-
for (const part of parts) {
|
|
340
|
-
if (!part) continue;
|
|
341
|
-
bits.push(part);
|
|
342
|
-
}
|
|
343
|
-
return bits.join(' ');
|
|
344
|
-
};
|
|
331
|
+
const joinStatus = (parts) => parts.filter(Boolean).join(' ');
|
|
345
332
|
|
|
346
333
|
const countsKind = (sourceKind, revShort) => {
|
|
347
334
|
if (sourceKind === 'pr' || sourceKind === 'mr') return sourceKind;
|
|
348
|
-
|
|
349
|
-
return 'worktree';
|
|
335
|
+
return revShort ? 'commit' : 'worktree';
|
|
350
336
|
};
|
|
351
337
|
|
|
338
|
+
const isWorktree = (view) =>
|
|
339
|
+
countsKind(view.sourceKind, view.revShort) === 'worktree';
|
|
340
|
+
|
|
352
341
|
const notesLine = (counts) => {
|
|
353
342
|
const feedback = counts.feedback ?? 0;
|
|
354
|
-
const
|
|
343
|
+
const tasks = counts.tasks ?? 0;
|
|
355
344
|
const code = counts.code ?? 0;
|
|
356
|
-
return `feedback ${feedback}
|
|
345
|
+
return `feedback ${feedback} tasks ${tasks} code ${code}`;
|
|
357
346
|
};
|
|
358
347
|
|
|
359
348
|
const statusLead = (view) => {
|
|
360
349
|
const counts = view.counts ?? {};
|
|
361
350
|
const kind = countsKind(view.sourceKind, view.revShort);
|
|
362
|
-
if (kind === 'worktree') return branchLabel(view.branch
|
|
351
|
+
if (kind === 'worktree') return branchLabel(view.branch);
|
|
363
352
|
if (kind === 'commit') {
|
|
364
|
-
|
|
365
|
-
return `commit ${view.revShort} ${n}`;
|
|
353
|
+
return `commit ${view.revShort} ${counts.commit ?? 0}`;
|
|
366
354
|
}
|
|
367
|
-
const n = counts.pr ?? 0;
|
|
368
355
|
const label = view.sourceLabel || kind;
|
|
369
|
-
return `${kind} ${label} ${
|
|
356
|
+
return `${kind} ${label} ${counts.pr ?? 0}`;
|
|
370
357
|
};
|
|
371
358
|
|
|
372
|
-
const statusLeft = (view) =>
|
|
373
|
-
|
|
374
|
-
if (!body) return '';
|
|
375
|
-
return ` ${body}`;
|
|
376
|
-
};
|
|
359
|
+
const statusLeft = (view) =>
|
|
360
|
+
` ${joinStatus([statusLead(view), notesLine(view.counts ?? {})])}`;
|
|
377
361
|
|
|
378
362
|
const statusDelta = (total) => {
|
|
379
363
|
const stagedAdd = total.stagedAdded ?? 0;
|
|
@@ -385,28 +369,24 @@ const statusDelta = (total) => {
|
|
|
385
369
|
if (!hasDelta && !total.removed) return '';
|
|
386
370
|
const plus = `+${stagedAdd}/${stagedAdd + unstagedAdd}`;
|
|
387
371
|
const minus = `-${stagedDel}/${stagedDel + unstagedDel}`;
|
|
388
|
-
return
|
|
372
|
+
return `${plus} ${minus}`;
|
|
389
373
|
};
|
|
390
374
|
|
|
391
375
|
const statusStats = (view) => {
|
|
392
376
|
const counts = view.counts ?? {};
|
|
377
|
+
const staged = counts.staged ?? 0;
|
|
393
378
|
const total = fileTotals(view.files ?? []);
|
|
394
|
-
const counted = (counts.staged ?? 0) + (counts.unstaged ?? 0);
|
|
395
379
|
const hasGit =
|
|
396
380
|
total.remaining || total.staged || total.added || total.removed;
|
|
397
|
-
const
|
|
398
|
-
const
|
|
399
|
-
|
|
400
|
-
const git = joinStatus([statusDelta(total), `${n}/${m}`]);
|
|
401
|
-
if (!view.npmLogs) return git;
|
|
402
|
-
return joinStatus([view.npmLogs, git]);
|
|
381
|
+
const n = hasGit ? total.staged : staged;
|
|
382
|
+
const m = hasGit ? total.remaining : staged + (counts.unstaged ?? 0);
|
|
383
|
+
return joinStatus([view.npmLogs, statusDelta(total), `${n}/${m}`]);
|
|
403
384
|
};
|
|
404
385
|
|
|
405
386
|
const statusLayout = (view, status, width) => {
|
|
406
|
-
const
|
|
407
|
-
const
|
|
408
|
-
|
|
409
|
-
if (msg && !isInfoStatus(status)) msgFg = THEME.errorFg;
|
|
387
|
+
const msg = formatBusyStatus(status, view.progressFrame ?? 0);
|
|
388
|
+
const isError = msg && !isInfoStatus(status);
|
|
389
|
+
const msgFg = isError ? THEME.errorFg : THEME.chromeFg;
|
|
410
390
|
const stats = statusStats(view);
|
|
411
391
|
const right = stats ? `${stats} ` : '';
|
|
412
392
|
const rightW = visibleWidth(right);
|
|
@@ -422,60 +402,36 @@ const statusLayout = (view, status, width) => {
|
|
|
422
402
|
};
|
|
423
403
|
|
|
424
404
|
const choiceHits = (choices, prefix, y) => {
|
|
425
|
-
const { length } = choices;
|
|
426
|
-
const hits = new Array(length);
|
|
427
405
|
let x = visibleWidth(prefix);
|
|
428
|
-
|
|
429
|
-
const
|
|
430
|
-
const
|
|
431
|
-
const w = visibleWidth(piece);
|
|
432
|
-
hits[i] = { id: choice.letter, y, x0: x, x1: x + w };
|
|
406
|
+
return choices.map((choice) => {
|
|
407
|
+
const w = visibleWidth(`${choice.lead}${choice.label}`);
|
|
408
|
+
const hit = { id: choice.letter, y, x0: x, x1: x + w };
|
|
433
409
|
x += w;
|
|
434
|
-
|
|
435
|
-
|
|
410
|
+
return hit;
|
|
411
|
+
});
|
|
436
412
|
};
|
|
437
413
|
|
|
438
|
-
const paintChoicePrompt = (choices, width, color, prefix
|
|
414
|
+
const paintChoicePrompt = (choices, width, color, prefix) => {
|
|
439
415
|
const bgRgb = THEME.chromeBg;
|
|
440
|
-
const
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
if (!color) {
|
|
445
|
-
const pad = Math.max(0, width - visibleWidth(body));
|
|
446
|
-
return body + ' '.repeat(pad);
|
|
447
|
-
}
|
|
448
|
-
let out = '';
|
|
449
|
-
if (prefix) out += paint(prefix, THEME.mutedFg, bgRgb, color);
|
|
416
|
+
const body = `${prefix}${promptFromChoices(choices)}`;
|
|
417
|
+
const pad = Math.max(0, width - visibleWidth(body));
|
|
418
|
+
if (!color) return body + ' '.repeat(pad);
|
|
419
|
+
let out = paint(prefix, THEME.mutedFg, bgRgb, color);
|
|
450
420
|
for (const choice of choices) {
|
|
451
421
|
const label = `${choice.lead}${choice.label}`;
|
|
452
422
|
out += paintMarkedWord(label, choice.letter, color, QUIT_MARK);
|
|
453
423
|
}
|
|
454
|
-
const pad = Math.max(0, width - visibleWidth(body));
|
|
455
424
|
return out + fill(pad, THEME.mutedFg, bgRgb, color);
|
|
456
425
|
};
|
|
457
426
|
|
|
458
|
-
const STATUS_MARK = /(\+[0-9]+(?:\/[0-9]+)?|-[0-9]+(?:\/[0-9]+)?)/g;
|
|
459
|
-
const OLD_LOGS = /old logs \S+/;
|
|
460
|
-
|
|
461
|
-
const paintStatusMark = (token, color) => {
|
|
462
|
-
const bgRgb = THEME.chromeBg;
|
|
463
|
-
if (token.startsWith('+')) {
|
|
464
|
-
return paint(token, THEME.addLineFg, bgRgb, color);
|
|
465
|
-
}
|
|
466
|
-
return paint(token, THEME.delLineFg, bgRgb, color);
|
|
467
|
-
};
|
|
468
|
-
|
|
469
427
|
const paintStatusLeft = (text, color) => {
|
|
470
|
-
const bgRgb = THEME.chromeBg;
|
|
471
|
-
const muted = THEME.mutedFg;
|
|
472
428
|
if (!color) return text;
|
|
429
|
+
const bgRgb = THEME.chromeBg;
|
|
473
430
|
const found = text.match(OLD_LOGS);
|
|
474
431
|
if (found) {
|
|
475
|
-
const at = found.index;
|
|
476
432
|
const token = found[0];
|
|
477
|
-
const before = text.slice(0,
|
|
478
|
-
const after = text.slice(
|
|
433
|
+
const before = text.slice(0, found.index);
|
|
434
|
+
const after = text.slice(found.index + token.length);
|
|
479
435
|
return (
|
|
480
436
|
paintStatusLeft(before, color) +
|
|
481
437
|
paint(token, THEME.shaFg, bgRgb, color) +
|
|
@@ -486,24 +442,18 @@ const paintStatusLeft = (text, color) => {
|
|
|
486
442
|
let last = 0;
|
|
487
443
|
for (const match of text.matchAll(STATUS_MARK)) {
|
|
488
444
|
const token = match[0];
|
|
489
|
-
const
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
out += paintStatusMark(token, color);
|
|
494
|
-
last = start + token.length;
|
|
445
|
+
const tokenFg = token.startsWith('+') ? THEME.addLineFg : THEME.delLineFg;
|
|
446
|
+
out += paint(text.slice(last, match.index), THEME.mutedFg, bgRgb, color);
|
|
447
|
+
out += paint(token, tokenFg, bgRgb, color);
|
|
448
|
+
last = match.index + token.length;
|
|
495
449
|
}
|
|
496
|
-
|
|
497
|
-
out += paint(text.slice(last), muted, bgRgb, color);
|
|
498
|
-
}
|
|
499
|
-
return out;
|
|
450
|
+
return out + paint(text.slice(last), THEME.mutedFg, bgRgb, color);
|
|
500
451
|
};
|
|
501
452
|
|
|
502
453
|
const paintBranchHead = (text, branch, color) => {
|
|
503
454
|
const name = branchLabel(branch);
|
|
504
455
|
if (!name || !text.startsWith(' ')) return null;
|
|
505
|
-
const
|
|
506
|
-
const shown = text.startsWith(head) ? name : text.slice(1);
|
|
456
|
+
const shown = text.startsWith(` ${name}`) ? name : text.slice(1);
|
|
507
457
|
if (!shown || !name.startsWith(shown)) return null;
|
|
508
458
|
const rest = text.slice(1 + shown.length);
|
|
509
459
|
const bgRgb = THEME.chromeBg;
|
|
@@ -514,40 +464,23 @@ const paintBranchHead = (text, branch, color) => {
|
|
|
514
464
|
);
|
|
515
465
|
};
|
|
516
466
|
|
|
517
|
-
const
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
const bits = source.split('**');
|
|
522
|
-
const parts = [];
|
|
523
|
-
for (let i = 0; i < bits.length; i++) {
|
|
524
|
-
if (!bits[i]) continue;
|
|
525
|
-
parts.push({ text: bits[i], hot: i % 2 === 1 });
|
|
526
|
-
}
|
|
527
|
-
return parts;
|
|
528
|
-
};
|
|
529
|
-
|
|
530
|
-
const saveHintText = () => SAVE_HINT.replaceAll('**', '');
|
|
531
|
-
|
|
532
|
-
const commitSaveHint = (view) => {
|
|
533
|
-
if (view.mode !== 'compose' || view.commitView !== 'full') return false;
|
|
534
|
-
const compose = view.compose;
|
|
535
|
-
return Boolean(compose && compose.kind === 'commit');
|
|
536
|
-
};
|
|
467
|
+
const commitSaveHint = (view) =>
|
|
468
|
+
view.mode === 'compose' &&
|
|
469
|
+
view.commitView === 'full' &&
|
|
470
|
+
view.compose?.kind === 'commit';
|
|
537
471
|
|
|
538
472
|
const paintSaveHintBar = (width, color) => {
|
|
539
473
|
const bgRgb = THEME.buttonBg;
|
|
540
|
-
const plain = `${BUTTON_LEAD}${
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
return plain + ' '.repeat(pad);
|
|
544
|
-
}
|
|
474
|
+
const plain = `${BUTTON_LEAD}${SAVE_HINT.replaceAll('**', '')}`;
|
|
475
|
+
const pad = Math.max(0, width - visibleWidth(plain));
|
|
476
|
+
if (!color) return plain + ' '.repeat(pad);
|
|
545
477
|
let out = paint(BUTTON_LEAD, THEME.buttonFg, bgRgb, color);
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
478
|
+
const bits = SAVE_HINT.split('**');
|
|
479
|
+
for (let i = 0; i < bits.length; i++) {
|
|
480
|
+
const hot = i % 2 === 1;
|
|
481
|
+
const tone = hot ? HINT_HOT_FG : THEME.buttonFg;
|
|
482
|
+
out += paint(bits[i], tone, bgRgb, color, hot);
|
|
549
483
|
}
|
|
550
|
-
const pad = Math.max(0, width - visibleWidth(plain));
|
|
551
484
|
return out + fill(pad, THEME.buttonFg, bgRgb, color);
|
|
552
485
|
};
|
|
553
486
|
|
|
@@ -556,10 +489,7 @@ const paintFooter = (view, layout, width, color) => {
|
|
|
556
489
|
return paintButtons(layout, width, color);
|
|
557
490
|
};
|
|
558
491
|
|
|
559
|
-
const footerHits = (view, layout) =>
|
|
560
|
-
if (commitSaveHint(view)) return [];
|
|
561
|
-
return layout.hits;
|
|
562
|
-
};
|
|
492
|
+
const footerHits = (view, layout) => (commitSaveHint(view) ? [] : layout.hits);
|
|
563
493
|
|
|
564
494
|
const paintStatusLine = (view, status, width, color) => {
|
|
565
495
|
const confirm = CONFIRM[view.mode];
|
|
@@ -574,8 +504,7 @@ const paintStatusLine = (view, status, width, color) => {
|
|
|
574
504
|
return layout.clipped + leftGap + layout.msg + rightGap + layout.right;
|
|
575
505
|
}
|
|
576
506
|
const bgRgb = THEME.chromeBg;
|
|
577
|
-
const
|
|
578
|
-
const head = worktree
|
|
507
|
+
const head = isWorktree(view)
|
|
579
508
|
? paintBranchHead(layout.clipped, view.branch, color)
|
|
580
509
|
: null;
|
|
581
510
|
const left = head ?? paintStatusLeft(layout.clipped, color);
|
|
@@ -591,97 +520,109 @@ const paintStatusLine = (view, status, width, color) => {
|
|
|
591
520
|
const statusChoiceHits = (view, y) => {
|
|
592
521
|
const spec = CONFIRM[view.mode];
|
|
593
522
|
if (!spec) return [];
|
|
594
|
-
|
|
595
|
-
return choiceHits(spec.choices, prefix, y);
|
|
523
|
+
return choiceHits(spec.choices, confirmPrefix(spec, view), y);
|
|
596
524
|
};
|
|
597
525
|
|
|
598
526
|
const statusBranchHit = (view, width, y) => {
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
if (!branchLabel(branch)) return null;
|
|
603
|
-
if (countsKind(view.sourceKind, view.revShort) !== 'worktree') return null;
|
|
527
|
+
if (CONFIRM[view.mode]) return null;
|
|
528
|
+
const name = branchLabel(view.branch);
|
|
529
|
+
if (!name || !isWorktree(view)) return null;
|
|
604
530
|
const layout = statusLayout(view, view.status ?? '', width);
|
|
605
|
-
const name = branchLabel(branch);
|
|
606
531
|
const at = layout.clipped.indexOf(name);
|
|
607
532
|
if (at < 0) return null;
|
|
608
533
|
return { id: 'branch', y, x0: at, x1: at + visibleWidth(name) };
|
|
609
534
|
};
|
|
610
535
|
|
|
611
|
-
const
|
|
612
|
-
|
|
613
|
-
const selectedCommit = (view) => (view.commits ?? [])[view.commitCursor ?? 0];
|
|
536
|
+
const withHidden = (ids, more) => [...ids, ...more];
|
|
614
537
|
|
|
615
538
|
const hiddenActions = (view) => {
|
|
616
|
-
const pane = view
|
|
539
|
+
const { pane } = view;
|
|
540
|
+
if (pane === 'dashboard') return DASH_HIDDEN;
|
|
617
541
|
if (pane === 'files') return FILES_HIDDEN;
|
|
618
|
-
if (pane === 'unit') return UNIT_DISABLED;
|
|
542
|
+
if (pane === 'unit') return withHidden(UNIT_DISABLED, ['feedback', 'tasks']);
|
|
619
543
|
if (pane === 'branches') return BRANCHES_DISABLED;
|
|
620
544
|
if (pane === 'commits') return COMMITS_DISABLED;
|
|
621
545
|
if (pane === 'npm') return NPM_DISABLED;
|
|
622
|
-
if (
|
|
623
|
-
|
|
546
|
+
if (pane === 'tasks' || isTaskView(view)) {
|
|
547
|
+
return withHidden(TASKS_DISABLED, ['prev', 'next']);
|
|
548
|
+
}
|
|
549
|
+
return withHidden(DIFF_DISABLED, ['feedback', 'tasks']);
|
|
624
550
|
};
|
|
625
551
|
|
|
626
552
|
const dimFooterActions = (view) => {
|
|
627
553
|
const pane = view.pane ?? 'diff';
|
|
628
554
|
if (pane === 'files') {
|
|
629
|
-
|
|
630
|
-
const entry = files[view.fileCursor ?? 0];
|
|
631
|
-
if (!entry || entry.kind !== 'todos') return [];
|
|
632
|
-
return FILES_GIT_DISABLED;
|
|
555
|
+
return isTasksEntry(selectedFile(view)) ? FILES_GIT_DISABLED : [];
|
|
633
556
|
}
|
|
634
557
|
if (pane === 'diff' || pane === 'unit') return diffGitDisabled(view.item);
|
|
635
558
|
if (pane === 'branches') return branchGitDisabled(selectedBranch(view));
|
|
636
559
|
if (pane === 'commits') {
|
|
637
|
-
const entry = selectedCommit(view);
|
|
638
|
-
const staged = (view.counts?.staged ?? 0) > 0;
|
|
639
|
-
const unstagedCurrent = !view.rev && !view.revShort;
|
|
640
560
|
return commitGitDisabled({
|
|
641
|
-
...
|
|
642
|
-
canCommit: staged,
|
|
643
|
-
unstagedCurrent,
|
|
561
|
+
...selectedCommit(view),
|
|
562
|
+
canCommit: (view.counts?.staged ?? 0) > 0,
|
|
563
|
+
unstagedCurrent: !view.rev && !view.revShort,
|
|
644
564
|
});
|
|
645
565
|
}
|
|
646
|
-
if (pane
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
}
|
|
650
|
-
if (pane === 'npm' && !view.npmLogs) return ['npmLogs'];
|
|
651
|
-
return [];
|
|
566
|
+
if (pane !== 'npm') return [];
|
|
567
|
+
if (view.npmView) return view.npmRunning ? ['npmRerun'] : [];
|
|
568
|
+
return view.npmLogs ? [] : ['npmLogs'];
|
|
652
569
|
};
|
|
653
570
|
|
|
571
|
+
const navExcept = (skip) => DASH_IDS.filter((id) => !skip.includes(id));
|
|
572
|
+
|
|
654
573
|
const extraFooterActions = (view) => {
|
|
655
|
-
const pane = view
|
|
656
|
-
if (pane === '
|
|
657
|
-
|
|
658
|
-
return [
|
|
574
|
+
const { pane } = view;
|
|
575
|
+
if (pane === 'dashboard') return DASH_IDS;
|
|
576
|
+
if (pane === 'branches') {
|
|
577
|
+
return [
|
|
578
|
+
...navExcept(['dashNpm']),
|
|
579
|
+
'newBranch',
|
|
580
|
+
'rebase',
|
|
581
|
+
'drop',
|
|
582
|
+
'pull',
|
|
583
|
+
'push',
|
|
584
|
+
];
|
|
585
|
+
}
|
|
586
|
+
if (pane === 'commits') {
|
|
587
|
+
return [
|
|
588
|
+
...navExcept(['dashCommits']),
|
|
589
|
+
'commit',
|
|
590
|
+
'amend',
|
|
591
|
+
'apply',
|
|
592
|
+
'reword',
|
|
593
|
+
'fixup',
|
|
594
|
+
'drop',
|
|
595
|
+
'view',
|
|
596
|
+
];
|
|
659
597
|
}
|
|
660
|
-
if (pane === 'branches') return Object.keys(BRANCH_ACTIONS);
|
|
661
|
-
if (pane === 'commits') return Object.keys(COMMIT_ACTIONS);
|
|
662
598
|
if (pane === 'npm') {
|
|
663
|
-
if (view.npmView)
|
|
664
|
-
|
|
599
|
+
if (view.npmView) {
|
|
600
|
+
return [...navExcept(['dashRun']), 'npmVerbose', 'npmRerun'];
|
|
601
|
+
}
|
|
602
|
+
return [...DASH_IDS, 'npmEdit', 'npmNew', 'npmDrop', 'npmLogs'];
|
|
665
603
|
}
|
|
666
|
-
return [];
|
|
604
|
+
if (pane === 'tasks' || isTaskView(view)) return [...DASH_IDS, 'drop'];
|
|
605
|
+
return navExcept(['dashDiffs']);
|
|
667
606
|
};
|
|
668
607
|
|
|
608
|
+
const footerLayout = (view, width) =>
|
|
609
|
+
layoutButtons(width, {
|
|
610
|
+
hidden: hiddenActions(view),
|
|
611
|
+
extra: extraFooterActions(view),
|
|
612
|
+
disabled: dimFooterActions(view),
|
|
613
|
+
backId: backFooterId(view),
|
|
614
|
+
mark: backHintMark(view),
|
|
615
|
+
});
|
|
616
|
+
|
|
669
617
|
module.exports = {
|
|
670
|
-
QUIT_PROMPT,
|
|
671
|
-
PUSH_PROMPT,
|
|
672
|
-
updatePrompt,
|
|
673
|
-
dropPrompt,
|
|
674
618
|
formatBusyStatus,
|
|
675
619
|
headerText,
|
|
676
620
|
paintHeader,
|
|
677
621
|
layoutButtons,
|
|
678
|
-
|
|
622
|
+
footerLayout,
|
|
679
623
|
paintFooter,
|
|
680
624
|
footerHits,
|
|
681
625
|
paintStatusLine,
|
|
682
626
|
statusChoiceHits,
|
|
683
627
|
statusBranchHit,
|
|
684
|
-
hiddenActions,
|
|
685
|
-
dimFooterActions,
|
|
686
|
-
extraFooterActions,
|
|
687
628
|
};
|