reslop 0.2.7 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -1
- package/README.md +16 -14
- package/bin/reslop.js +8 -5
- package/lib/agents/agents.js +504 -0
- package/lib/agents/allow.js +71 -0
- package/lib/agents/allow.json +16 -0
- package/lib/agents/models.js +460 -0
- package/lib/api.js +15 -12
- package/lib/cli.js +43 -68
- package/lib/{files.js → common/files.js} +48 -55
- package/lib/{find.js → common/find.js} +1 -1
- package/lib/common/format.js +101 -0
- package/lib/{utilities.js → common/process.js} +7 -95
- package/lib/common/tasks.json +27 -0
- package/lib/common/utilities.js +112 -0
- package/lib/dashboard/git.js +9 -16
- package/lib/dashboard/model.js +90 -49
- package/lib/dashboard/npm.js +17 -13
- package/lib/dashboard/tree.js +27 -30
- package/lib/{session → dashboard}/watch.js +36 -31
- package/lib/{workspace.js → dashboard/workspace.js} +23 -38
- package/lib/{deps.js → deps/deps.js} +31 -45
- package/lib/{deps-diff.js → deps/diff.js} +35 -25
- package/lib/{manifest.js → deps/manifest.js} +47 -30
- package/lib/diff/align.js +1 -2
- package/lib/diff/diff.js +2 -2
- package/lib/diff/display.js +29 -5
- package/lib/diff/inline.js +10 -14
- package/lib/diff/items.js +6 -4
- package/lib/diff/patch.js +39 -3
- package/lib/diff/unit.js +45 -16
- package/lib/{git-branches.js → git/branches.js} +53 -48
- package/lib/{git-deps.js → git/deps.js} +123 -96
- package/lib/git/git.js +71 -0
- package/lib/git/worktree.js +475 -0
- package/lib/highlight/bash.js +1 -2
- package/lib/highlight/csv.js +1 -2
- package/lib/highlight/dot.js +1 -2
- package/lib/{highlight.js → highlight/highlight.js} +42 -11
- package/lib/highlight/html.js +20 -18
- package/lib/highlight/json.js +1 -2
- package/lib/highlight/log.js +1 -2
- package/lib/highlight/txt.js +1 -2
- package/lib/{session → input}/actions.js +111 -48
- package/lib/{editor.js → input/editor.js} +7 -8
- package/lib/{keys.js → input/keys.js} +15 -43
- package/lib/input/line-edit.js +77 -0
- package/lib/input/menu-pick.js +70 -0
- package/lib/render/agents.js +640 -0
- package/lib/render/branches.js +4 -5
- package/lib/render/commits.js +54 -64
- package/lib/render/{dash-activity.js → dashboard/activity.js} +25 -40
- package/lib/render/{dash-blocks.js → dashboard/blocks.js} +131 -137
- package/lib/render/{dashboard.js → dashboard/dashboard.js} +25 -22
- package/lib/render/diff.js +166 -45
- package/lib/render/field.js +131 -0
- package/lib/render/files.js +9 -14
- package/lib/render/footer.js +383 -0
- package/lib/render/frame.js +123 -25
- package/lib/render/header.js +176 -0
- package/lib/render/log.js +319 -0
- package/lib/render/menu.js +60 -0
- package/lib/render/notes.js +16 -11
- package/lib/render/npm.js +322 -196
- package/lib/render/packages.js +9 -13
- package/lib/render/primitives.js +101 -15
- package/lib/render/render.js +7 -11
- package/lib/render/repos.js +13 -25
- package/lib/render/status.js +370 -0
- package/lib/render/status.json +85 -0
- package/lib/render/{dash-table.js → table.js} +13 -60
- package/lib/render/tiles.js +109 -48
- package/lib/render/unit.js +32 -13
- package/lib/{report-model.js → report/model.js} +4 -6
- package/lib/{report-parse.js → report/parse.js} +9 -14
- package/lib/{report-render.js → report/render.js} +46 -14
- package/lib/{report-run.js → report/run.js} +42 -30
- package/lib/{report-test.js → report/test.js} +56 -55
- package/lib/{report-tools.js → report/tools.js} +24 -33
- package/lib/review/agent-instructions.md +9 -0
- package/lib/review/format.js +426 -0
- package/lib/{review-merge.js → review/merge.js} +9 -8
- package/lib/review/review.js +190 -0
- package/lib/review/store.js +281 -0
- package/lib/review/templates.js +83 -0
- package/lib/runs/commands.js +14 -0
- package/lib/runs/logs.js +299 -0
- package/lib/runs/output.js +174 -0
- package/lib/runs/process.js +113 -0
- package/lib/{runs.js → runs/runs.js} +84 -36
- package/lib/runs/scripts.js +150 -0
- package/lib/session/agents/agents.js +958 -0
- package/lib/session/agents/job.js +320 -0
- package/lib/session/agents/jobs.js +355 -0
- package/lib/session/agents/options.js +153 -0
- package/lib/session/agents/plans.js +83 -0
- package/lib/session/branches.js +33 -39
- package/lib/session/change-actions.js +34 -100
- package/lib/session/commit.js +91 -52
- package/lib/session/compose.js +181 -145
- package/lib/session/dashboard.js +23 -22
- package/lib/session/dispatch.js +85 -21
- package/lib/session/files-pane.js +47 -26
- package/lib/session/find.js +16 -29
- package/lib/session/import.js +82 -0
- package/lib/session/items.js +8 -17
- package/lib/session/lifecycle.js +75 -72
- package/lib/session/load.js +2 -4
- package/lib/session/navigation.js +6 -3
- package/lib/session/npm/history.js +249 -0
- package/lib/session/npm/npm.js +815 -0
- package/lib/session/{packages.js → npm/packages.js} +21 -14
- package/lib/session/ops.js +125 -34
- package/lib/session/pointer.js +202 -10
- package/lib/session/prefs.js +112 -0
- package/lib/session/reads.js +109 -0
- package/lib/session/review.js +28 -10
- package/lib/session/run-panel.js +40 -0
- package/lib/{session.js → session/session.js} +156 -63
- package/lib/session/tasks.js +320 -65
- package/lib/session/templates.js +1 -1
- package/lib/session/terminal.js +94 -12
- package/lib/session/unit.js +35 -30
- package/lib/session/update.js +194 -6
- package/lib/session/view.js +65 -65
- package/lib/session/workspace.js +39 -78
- package/lib/{capabilities.js → source/capabilities.js} +3 -12
- package/lib/{github.js → source/github.js} +102 -32
- package/lib/{gitlab.js → source/gitlab.js} +87 -10
- package/lib/{remote.js → source/remote.js} +144 -40
- package/lib/source/review-threads.graphql +20 -0
- package/lib/source/source.js +74 -0
- package/lib/term/ansi.js +396 -0
- package/lib/{clipboard.js → term/clipboard.js} +3 -6
- package/lib/{select.js → term/select.js} +2 -2
- package/lib/term/themes/dark.json +122 -0
- package/lib/term/themes/light.json +122 -0
- package/lib/term/wide.txt +21 -0
- package/lib/{wrap.js → term/wrap.js} +4 -4
- package/package.json +2 -2
- package/lib/ansi.js +0 -545
- package/lib/detect.js +0 -34
- package/lib/diff/edit.js +0 -33
- package/lib/git-worktree.js +0 -455
- package/lib/git.js +0 -79
- package/lib/npm-commands.js +0 -522
- package/lib/render/chrome.js +0 -806
- package/lib/render/present.js +0 -19
- package/lib/render/report.js +0 -43
- package/lib/render/search.js +0 -25
- package/lib/review.js +0 -745
- package/lib/session/accessors.js +0 -26
- package/lib/session/edit-click.js +0 -57
- package/lib/session/npm.js +0 -452
- package/lib/session/progress.js +0 -41
- package/lib/source.js +0 -36
- package/lib/update.js +0 -192
- /package/lib/{git-reword-todo.js → git/reword-todo.js} +0 -0
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const ansi = require('../term/ansi.js');
|
|
4
|
+
const actions = require('../input/actions.js');
|
|
5
|
+
const files = require('../common/files.js');
|
|
6
|
+
const primitives = require('./primitives.js');
|
|
7
|
+
const { fill, isTaskView } = primitives;
|
|
8
|
+
const { isTasksEntry } = files;
|
|
9
|
+
const { ACTION_IDS, actionLetter } = actions;
|
|
10
|
+
const { DASH_IDS, buttonWord } = actions;
|
|
11
|
+
const { FILES_GIT_DISABLED } = actions;
|
|
12
|
+
const { diffGitDisabled, branchGitDisabled, commitGitDisabled } = actions;
|
|
13
|
+
const { entryHasDiff, lineHasDiff, withoutMoves } = actions;
|
|
14
|
+
const { THEME, paint, visibleWidth } = ansi;
|
|
15
|
+
const { selectedFile, selectedBranch, selectedCommit } = require('./header.js');
|
|
16
|
+
|
|
17
|
+
const BUTTON_LEAD = ' ';
|
|
18
|
+
|
|
19
|
+
const BUTTON_GAP = ' ';
|
|
20
|
+
|
|
21
|
+
const HINT_ONLY = ['taskNudge'];
|
|
22
|
+
|
|
23
|
+
const BUTTON_EDGE = 1;
|
|
24
|
+
|
|
25
|
+
const BACK_ARROW = '🢐';
|
|
26
|
+
|
|
27
|
+
const BACK_KEY = 'esc';
|
|
28
|
+
|
|
29
|
+
const BUTTON_MARK = {
|
|
30
|
+
bgRgb: THEME.buttonBg,
|
|
31
|
+
fgRgb: THEME.buttonFg,
|
|
32
|
+
hotFg: THEME.buttonHotFg,
|
|
33
|
+
missFg: THEME.buttonHotFg,
|
|
34
|
+
missBold: true,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const QUIT_MARK = {
|
|
38
|
+
bgRgb: THEME.chromeBg,
|
|
39
|
+
fgRgb: THEME.mutedFg,
|
|
40
|
+
hotFg: THEME.warnFg,
|
|
41
|
+
missFg: THEME.mutedFg,
|
|
42
|
+
missBold: false,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const SAVE_HINT = 'Press **Ctrl-S** or **Enter** at EOF to save';
|
|
46
|
+
|
|
47
|
+
const HINT_HOT_FG = 0xff;
|
|
48
|
+
|
|
49
|
+
const buttonGap = (index) => (index ? BUTTON_GAP : BUTTON_LEAD);
|
|
50
|
+
|
|
51
|
+
const hintBody = (mark) => `${mark}${BACK_KEY}`;
|
|
52
|
+
|
|
53
|
+
const buttonLabel = (id, short) => (short ? actionLetter(id) : buttonWord(id));
|
|
54
|
+
|
|
55
|
+
const paintBackHint = (color, mark) => {
|
|
56
|
+
const bgRgb = THEME.buttonBg;
|
|
57
|
+
const rest = hintBody(mark).slice(mark.length);
|
|
58
|
+
const arrow = paint(mark, THEME.buttonHotFg, bgRgb, color, true);
|
|
59
|
+
return arrow + paint(rest, THEME.buttonFg, bgRgb, color);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const paintMarkedWord = (label, letter, color, mark) => {
|
|
63
|
+
const at = label.indexOf(letter);
|
|
64
|
+
if (at < 0) {
|
|
65
|
+
return paint(label, mark.missFg, mark.bgRgb, color, mark.missBold);
|
|
66
|
+
}
|
|
67
|
+
const before = label.slice(0, at);
|
|
68
|
+
const hot = label.slice(at, at + letter.length);
|
|
69
|
+
const after = label.slice(at + letter.length);
|
|
70
|
+
let out = paint(before, mark.fgRgb, mark.bgRgb, color);
|
|
71
|
+
out += paint(hot, mark.hotFg, mark.bgRgb, color, true);
|
|
72
|
+
return out + paint(after, mark.fgRgb, mark.bgRgb, color);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const footerIds = (hidden, extra, backId, globals, tail, after) => {
|
|
76
|
+
const ids = backId ? [backId] : [];
|
|
77
|
+
const push = (id) => {
|
|
78
|
+
if (!ids.includes(id)) ids.push(id);
|
|
79
|
+
};
|
|
80
|
+
for (const add of extra) push(add);
|
|
81
|
+
for (const id of globals) push(id);
|
|
82
|
+
if (tail) {
|
|
83
|
+
for (const id of ACTION_IDS) {
|
|
84
|
+
if (id === 'quit' || hidden.includes(id) || ids.includes(id)) continue;
|
|
85
|
+
ids.push(id);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
for (const id of after) push(id);
|
|
89
|
+
return ids;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const buttonPiece = (id, index, short, backId, mark) => {
|
|
93
|
+
const back = id === backId;
|
|
94
|
+
const letter = back ? BACK_KEY : actionLetter(id);
|
|
95
|
+
const label = back ? hintBody(mark) : buttonLabel(id, short);
|
|
96
|
+
const gap = buttonGap(index);
|
|
97
|
+
return {
|
|
98
|
+
id,
|
|
99
|
+
piece: `${gap}${label}`,
|
|
100
|
+
gap,
|
|
101
|
+
letter,
|
|
102
|
+
label,
|
|
103
|
+
back,
|
|
104
|
+
mark: back ? mark : '',
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const packGroup = (ids, short, start, backId, mark) => {
|
|
109
|
+
const parts = [];
|
|
110
|
+
for (let i = 0; i < ids.length; i++) {
|
|
111
|
+
parts.push(buttonPiece(ids[i], start + i, short, backId, mark));
|
|
112
|
+
}
|
|
113
|
+
return parts;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const groupWidth = (parts) => {
|
|
117
|
+
let width = 0;
|
|
118
|
+
for (const part of parts) width += visibleWidth(part.piece);
|
|
119
|
+
return width;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const placeGroup = (parts, hits, disabled, x) => {
|
|
123
|
+
const placed = [];
|
|
124
|
+
let cursor = x;
|
|
125
|
+
for (const part of parts) {
|
|
126
|
+
const w = visibleWidth(part.piece);
|
|
127
|
+
const off = disabled.includes(part.id);
|
|
128
|
+
const hint = HINT_ONLY.includes(part.id);
|
|
129
|
+
if (!off && !hint) hits.push({ id: part.id, x0: cursor, x1: cursor + w });
|
|
130
|
+
placed.push({ ...part, disabled: off });
|
|
131
|
+
cursor += w;
|
|
132
|
+
}
|
|
133
|
+
return { placed, cursor };
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const splitFooter = (ids, options, limit) => {
|
|
137
|
+
const { disabled = [], backId = '', mark = BACK_ARROW } = options;
|
|
138
|
+
const { globals = [], after = [] } = options;
|
|
139
|
+
const rightIds = [];
|
|
140
|
+
for (const id of [...globals, ...after]) {
|
|
141
|
+
if (ids.includes(id) && !rightIds.includes(id)) rightIds.push(id);
|
|
142
|
+
}
|
|
143
|
+
const leftIds = ids.filter((id) => !rightIds.includes(id));
|
|
144
|
+
const pack = (leftShort, rightShort) => {
|
|
145
|
+
const left = packGroup(leftIds, leftShort, 0, backId, mark);
|
|
146
|
+
const start = left.length ? 1 : 0;
|
|
147
|
+
const right = packGroup(rightIds, rightShort, start, backId, mark);
|
|
148
|
+
return { left, right };
|
|
149
|
+
};
|
|
150
|
+
const wide = (packed) =>
|
|
151
|
+
groupWidth(packed.left) + groupWidth(packed.right) > limit;
|
|
152
|
+
let packed = pack(false, false);
|
|
153
|
+
if (wide(packed)) packed = pack(false, true);
|
|
154
|
+
if (wide(packed)) packed = pack(true, true);
|
|
155
|
+
const left = packed.left.slice();
|
|
156
|
+
const right = packed.right.slice();
|
|
157
|
+
while (groupWidth(left) + groupWidth(right) > limit && left.length) {
|
|
158
|
+
left.pop();
|
|
159
|
+
}
|
|
160
|
+
while (groupWidth(left) + groupWidth(right) > limit && right.length) {
|
|
161
|
+
right.pop();
|
|
162
|
+
}
|
|
163
|
+
const hits = [];
|
|
164
|
+
const leftPlace = placeGroup(left, hits, disabled, 0);
|
|
165
|
+
const parts = leftPlace.placed;
|
|
166
|
+
let x = leftPlace.cursor;
|
|
167
|
+
const gap = Math.max(0, limit - x - groupWidth(right));
|
|
168
|
+
if (gap && right.length) {
|
|
169
|
+
parts.push({ spacer: true, piece: ' '.repeat(gap) });
|
|
170
|
+
x += gap;
|
|
171
|
+
}
|
|
172
|
+
const rightPlace = placeGroup(right, hits, disabled, x);
|
|
173
|
+
parts.push(...rightPlace.placed);
|
|
174
|
+
return { hits, parts };
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const layoutButtons = (width, options = {}) => {
|
|
178
|
+
const { compact = false, hidden = [], extra = [], disabled = [] } = options;
|
|
179
|
+
const { backId = '', mark = BACK_ARROW, globals = [] } = options;
|
|
180
|
+
const { tail = true, after = [] } = options;
|
|
181
|
+
const ids = footerIds(hidden, extra, backId, globals, tail, after);
|
|
182
|
+
const limit = Math.max(0, width - BUTTON_EDGE);
|
|
183
|
+
if (globals.length) return splitFooter(ids, options, limit);
|
|
184
|
+
const build = (short) => {
|
|
185
|
+
const hits = [];
|
|
186
|
+
const parts = [];
|
|
187
|
+
let x = 0;
|
|
188
|
+
for (const id of ids) {
|
|
189
|
+
const part = buttonPiece(id, parts.length, short, backId, mark);
|
|
190
|
+
const w = visibleWidth(part.piece);
|
|
191
|
+
if (x + w > limit && parts.length) break;
|
|
192
|
+
const off = disabled.includes(id);
|
|
193
|
+
const hint = HINT_ONLY.includes(id);
|
|
194
|
+
if (!off && !hint) hits.push({ id, x0: x, x1: x + w });
|
|
195
|
+
parts.push({ ...part, disabled: off });
|
|
196
|
+
x += w;
|
|
197
|
+
}
|
|
198
|
+
return { hits, parts };
|
|
199
|
+
};
|
|
200
|
+
if (compact) return build(true);
|
|
201
|
+
const words = build(false);
|
|
202
|
+
if (words.parts.length === ids.length) return words;
|
|
203
|
+
return build(true);
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
const paintButtons = (layout, width, color) => {
|
|
207
|
+
const fgRgb = THEME.buttonFg;
|
|
208
|
+
const bgRgb = THEME.buttonBg;
|
|
209
|
+
let out = '';
|
|
210
|
+
let used = 0;
|
|
211
|
+
for (let i = 0; i < layout.parts.length; i++) {
|
|
212
|
+
const part = layout.parts[i];
|
|
213
|
+
if (part.spacer) {
|
|
214
|
+
out += paint(part.piece, fgRgb, bgRgb, color);
|
|
215
|
+
used += visibleWidth(part.piece);
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
const gap = part.gap ?? buttonGap(i);
|
|
219
|
+
out += paint(gap, fgRgb, bgRgb, color);
|
|
220
|
+
if (part.back) out += paintBackHint(color, part.mark);
|
|
221
|
+
else if (part.disabled) out += paint(part.label, fgRgb, bgRgb, color);
|
|
222
|
+
else out += paintMarkedWord(part.label, part.letter, color, BUTTON_MARK);
|
|
223
|
+
used += visibleWidth(part.piece);
|
|
224
|
+
}
|
|
225
|
+
return out + fill(width - used, fgRgb, bgRgb, color);
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
const commitSaveHint = (view) => {
|
|
229
|
+
const composing = view.mode === 'compose';
|
|
230
|
+
const full = view.commitView === 'full';
|
|
231
|
+
const commit = view.compose?.kind === 'commit';
|
|
232
|
+
return composing && full && commit;
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
const paintSaveHintBar = (width, color) => {
|
|
236
|
+
const bgRgb = THEME.buttonBg;
|
|
237
|
+
const plain = `${BUTTON_LEAD}${SAVE_HINT.replaceAll('**', '')}`;
|
|
238
|
+
const pad = Math.max(0, width - visibleWidth(plain));
|
|
239
|
+
if (!color) return plain + ' '.repeat(pad);
|
|
240
|
+
let out = paint(BUTTON_LEAD, THEME.buttonFg, bgRgb, color);
|
|
241
|
+
const bits = SAVE_HINT.split('**');
|
|
242
|
+
for (let i = 0; i < bits.length; i++) {
|
|
243
|
+
const hot = i % 2 === 1;
|
|
244
|
+
const tone = hot ? HINT_HOT_FG : THEME.buttonFg;
|
|
245
|
+
out += paint(bits[i], tone, bgRgb, color, hot);
|
|
246
|
+
}
|
|
247
|
+
return out + fill(pad, THEME.buttonFg, bgRgb, color);
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const paintFooter = (view, layout, width, color) => {
|
|
251
|
+
if (commitSaveHint(view)) return paintSaveHintBar(width, color);
|
|
252
|
+
return paintButtons(layout, width, color);
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
const footerHits = (view, layout) => (commitSaveHint(view) ? [] : layout.hits);
|
|
256
|
+
|
|
257
|
+
const filesDim = (view) => {
|
|
258
|
+
const entry = selectedFile(view);
|
|
259
|
+
if (isTasksEntry(entry)) return FILES_GIT_DISABLED;
|
|
260
|
+
return withoutMoves([], entryHasDiff(entry));
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
const unitDim = (view) => {
|
|
264
|
+
const off = [...diffGitDisabled(view.item)];
|
|
265
|
+
const lines = view.unitLines;
|
|
266
|
+
if (!lines || !lines.length) return off;
|
|
267
|
+
const line = lines[view.unitLine ?? 0];
|
|
268
|
+
return withoutMoves(off, lineHasDiff(line));
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
const commitsDim = (view) => {
|
|
272
|
+
const canCommit = (view.counts?.staged ?? 0) > 0;
|
|
273
|
+
const unstagedCurrent = !view.rev && !view.revShort;
|
|
274
|
+
const target = { ...selectedCommit(view), canCommit, unstagedCurrent };
|
|
275
|
+
return commitGitDisabled(target);
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
const PACKAGE_ROW_ACTIONS = ['packageDrop', 'packageWanted', 'packageLatest'];
|
|
279
|
+
|
|
280
|
+
const packagesDim = (view) => {
|
|
281
|
+
const listed = view.packages && view.packages.modules;
|
|
282
|
+
const rows = listed && listed.packages ? listed.packages : [];
|
|
283
|
+
const cursor = view.packagesCursor ?? 0;
|
|
284
|
+
const onRow = rows.length > 0 && cursor < rows.length;
|
|
285
|
+
if (!onRow) return PACKAGE_ROW_ACTIONS;
|
|
286
|
+
const row = rows[cursor];
|
|
287
|
+
return row && row.transitive ? ['packageNew', 'packageDrop'] : [];
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
const agentListDim = (view) => {
|
|
291
|
+
const list = view.agents ?? [];
|
|
292
|
+
const row = list[view.agentCursor ?? 0];
|
|
293
|
+
const installed = Boolean(row && row.bin);
|
|
294
|
+
const levels = (row && row.efforts) || [];
|
|
295
|
+
const sizes = (row && row.contexts) || [];
|
|
296
|
+
const off = [];
|
|
297
|
+
if (!installed || levels.length < 2) off.push('agentEffort');
|
|
298
|
+
if (!installed) off.push('agentFast');
|
|
299
|
+
if (!installed || !sizes.length) off.push('agentContext');
|
|
300
|
+
if ((view.agentPlanCount ?? 0) < 2) off.push('agentReview');
|
|
301
|
+
return off;
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
const agentsDim = (view) => {
|
|
305
|
+
if (!view.agentView) return agentListDim(view);
|
|
306
|
+
return view.agentRunning ? ['agentRerun'] : ['agentStop'];
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
const npmDim = (view) => {
|
|
310
|
+
if (view.npmView) return view.npmRunning ? [] : ['npmStop'];
|
|
311
|
+
return view.npmLogs ? [] : ['npmLogs'];
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
const FOOTER_DIM = {
|
|
315
|
+
files: filesDim,
|
|
316
|
+
unit: unitDim,
|
|
317
|
+
diff: (view) => diffGitDisabled(view.item),
|
|
318
|
+
branches: (view) => branchGitDisabled(selectedBranch(view)),
|
|
319
|
+
commits: commitsDim,
|
|
320
|
+
packages: packagesDim,
|
|
321
|
+
agents: agentsDim,
|
|
322
|
+
npm: npmDim,
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
const dimFooterActions = (view) => {
|
|
326
|
+
const pick = FOOTER_DIM[view.pane ?? 'diff'];
|
|
327
|
+
return pick ? pick(view) : [];
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
const GIT_FOOTER = ['add', 'unstage', 'revert', 'ignore'];
|
|
331
|
+
|
|
332
|
+
const FOOTER_EXTRA = {
|
|
333
|
+
repos: ['theme'],
|
|
334
|
+
dashboard: ['theme', 'pull', 'push'],
|
|
335
|
+
unit: ['add', 'unstage', 'revert', 'feedback', 'code', 'lines'],
|
|
336
|
+
files: [...GIT_FOOTER, 'code'],
|
|
337
|
+
diff: [...GIT_FOOTER, 'feedback', 'code', 'layout', 'lines'],
|
|
338
|
+
branches: ['newBranch', 'rebase', 'drop', 'pull', 'push'],
|
|
339
|
+
commits: ['newCommit', 'apply', 'reword', 'drop', 'view', 'pull', 'push'],
|
|
340
|
+
npmLog: ['npmVerbose', 'npmStop', 'npmRerun'],
|
|
341
|
+
npm: ['npmEdit', 'npmNew', 'npmDrop', 'npmLogs'],
|
|
342
|
+
packages: ['packageNew', 'packageDrop', 'packageWanted', 'packageLatest'],
|
|
343
|
+
agentLog: ['agentStop', 'agentRerun'],
|
|
344
|
+
agents: [
|
|
345
|
+
'agentModel',
|
|
346
|
+
'agentEffort',
|
|
347
|
+
'agentFast',
|
|
348
|
+
'agentContext',
|
|
349
|
+
'agentReview',
|
|
350
|
+
],
|
|
351
|
+
tasks: ['plan', 'import', 'drop', 'check', 'taskNudge'],
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
const extraFooterActions = (view) => {
|
|
355
|
+
const { pane } = view;
|
|
356
|
+
if (pane === 'npm' && view.npmView) return FOOTER_EXTRA.npmLog;
|
|
357
|
+
if (pane === 'npm') return FOOTER_EXTRA.npm;
|
|
358
|
+
if (pane === 'agents' && view.agentView) return FOOTER_EXTRA.agentLog;
|
|
359
|
+
if (pane === 'agents') return FOOTER_EXTRA.agents;
|
|
360
|
+
const listed = FOOTER_EXTRA[pane];
|
|
361
|
+
if (listed) return listed;
|
|
362
|
+
if (isTaskView(view)) return FOOTER_EXTRA.tasks;
|
|
363
|
+
return GIT_FOOTER;
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
const footerLayout = (view, width) => {
|
|
367
|
+
const extra = extraFooterActions(view);
|
|
368
|
+
const disabled = dimFooterActions(view);
|
|
369
|
+
const backId = 'back';
|
|
370
|
+
const globals = view.pane === 'repos' ? [] : DASH_IDS;
|
|
371
|
+
const tail = false;
|
|
372
|
+
const options = { extra, disabled, backId, globals, tail };
|
|
373
|
+
return layoutButtons(width, options);
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
module.exports = {
|
|
377
|
+
QUIT_MARK,
|
|
378
|
+
paintMarkedWord,
|
|
379
|
+
layoutButtons,
|
|
380
|
+
paintFooter,
|
|
381
|
+
footerHits,
|
|
382
|
+
footerLayout,
|
|
383
|
+
};
|
package/lib/render/frame.js
CHANGED
|
@@ -1,27 +1,28 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const
|
|
3
|
+
const { clamp } = require('../common/utilities.js');
|
|
4
|
+
const { ESC, RESET, stampVisible } = require('../term/ansi.js');
|
|
5
|
+
const { overlayRows, markEditorSelection } = require('../term/select.js');
|
|
5
6
|
const primitives = require('./primitives.js');
|
|
6
|
-
const
|
|
7
|
+
const { paintHeader } = require('./header.js');
|
|
8
|
+
const { paintFooter, footerHits, footerLayout } = require('./footer.js');
|
|
9
|
+
const status = require('./status.js');
|
|
7
10
|
const files = require('./files.js');
|
|
8
11
|
const branches = require('./branches.js');
|
|
9
12
|
const commits = require('./commits.js');
|
|
10
|
-
const
|
|
13
|
+
const { paintNotePanel, paintBodyTodo } = require('./notes.js');
|
|
11
14
|
const npm = require('./npm.js');
|
|
12
|
-
const
|
|
15
|
+
const { paintBodyDiff, viewDigits, paintNumberFill } = require('./diff.js');
|
|
13
16
|
const unit = require('./unit.js');
|
|
14
|
-
const dashboard = require('./dashboard.js');
|
|
17
|
+
const dashboard = require('./dashboard/dashboard.js');
|
|
15
18
|
const packages = require('./packages.js');
|
|
19
|
+
const agents = require('./agents.js');
|
|
16
20
|
const repos = require('./repos.js');
|
|
17
21
|
|
|
18
|
-
const { RESET } = ansi;
|
|
19
|
-
const { overlayRows, markEditorSelection } = select;
|
|
20
22
|
const { isListPane, isTaskView, paintBodyFill, paneResult } = primitives;
|
|
21
|
-
const {
|
|
22
|
-
const {
|
|
23
|
-
const {
|
|
24
|
-
const { paintBodyDiff } = diff;
|
|
23
|
+
const { bodyRow } = primitives;
|
|
24
|
+
const { paintStatusLine, statusBranchHit, statusChoiceHits } = status;
|
|
25
|
+
const { statusPlanHit, planMenu } = status;
|
|
25
26
|
|
|
26
27
|
const PANE_BODY = {
|
|
27
28
|
dashboard: dashboard.paintBodyDashboard,
|
|
@@ -31,6 +32,7 @@ const PANE_BODY = {
|
|
|
31
32
|
commits: commits.paintBodyCommits,
|
|
32
33
|
npm: npm.paintBodyNpm,
|
|
33
34
|
packages: packages.paintBodyPackages,
|
|
35
|
+
agents: agents.paintBodyAgents,
|
|
34
36
|
unit: unit.paintBodyUnit,
|
|
35
37
|
tasks: paintBodyTodo,
|
|
36
38
|
};
|
|
@@ -53,16 +55,35 @@ const frameNotes = (view, width, height, color) => {
|
|
|
53
55
|
return { panel, rows, cursor, bodyH };
|
|
54
56
|
};
|
|
55
57
|
|
|
58
|
+
const headsOnly = (owners, first) => {
|
|
59
|
+
for (let i = 0; i < first; i++) {
|
|
60
|
+
if (owners[i] !== null && owners[i] !== undefined) return false;
|
|
61
|
+
}
|
|
62
|
+
return true;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const tailBlank = (owners, last) => {
|
|
66
|
+
for (let i = last + 1; i < owners.length; i++) {
|
|
67
|
+
if (owners[i] !== null && owners[i] !== undefined) return last;
|
|
68
|
+
}
|
|
69
|
+
return Math.max(last, owners.length - 1);
|
|
70
|
+
};
|
|
71
|
+
|
|
56
72
|
const revealScroll = (owners, focus, height, prefer) => {
|
|
57
73
|
const first = owners.indexOf(focus);
|
|
58
74
|
if (first < 0) return prefer;
|
|
59
75
|
const last = owners.lastIndexOf(focus);
|
|
60
76
|
const room = Math.max(1, height);
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
77
|
+
const end = tailBlank(owners, last);
|
|
78
|
+
const top = headsOnly(owners, first) ? 0 : first;
|
|
79
|
+
let start = Math.min(prefer, top);
|
|
80
|
+
if (end >= start + room) {
|
|
81
|
+
const fits = end - top + 1 <= room;
|
|
82
|
+
if (fits || end > last) start = end - room + 1;
|
|
83
|
+
else start = first;
|
|
64
84
|
}
|
|
65
|
-
|
|
85
|
+
const max = Math.max(0, owners.length - room);
|
|
86
|
+
return clamp(start, 0, max);
|
|
66
87
|
};
|
|
67
88
|
|
|
68
89
|
const windowY = (win, row) => {
|
|
@@ -89,28 +110,64 @@ const mapBodyCursor = (cursor, win) => {
|
|
|
89
110
|
return point;
|
|
90
111
|
};
|
|
91
112
|
|
|
113
|
+
const numberDigits = (view) => {
|
|
114
|
+
if (!view || view.lineNumbers !== true) return 0;
|
|
115
|
+
if (view.pane !== 'diff' && view.pane !== 'unit') return 0;
|
|
116
|
+
return viewDigits(view);
|
|
117
|
+
};
|
|
118
|
+
|
|
92
119
|
const bodyFillKind = (listPane, splitBody) => {
|
|
93
120
|
if (listPane) return 'files';
|
|
94
121
|
return splitBody ? 'split' : 'plain';
|
|
95
122
|
};
|
|
96
123
|
|
|
124
|
+
const mapScrollBar = (bar, win) => {
|
|
125
|
+
if (!bar) return null;
|
|
126
|
+
const row = bar.row + win.shift - win.scroll;
|
|
127
|
+
if (row < 0 || row >= win.shown) return null;
|
|
128
|
+
const height = Math.min(bar.height, win.shown - row);
|
|
129
|
+
return {
|
|
130
|
+
x: bar.col,
|
|
131
|
+
y: win.headerLines + row + 1,
|
|
132
|
+
height,
|
|
133
|
+
track: bar.track,
|
|
134
|
+
rows: bar.rows,
|
|
135
|
+
count: bar.count,
|
|
136
|
+
start: bar.start,
|
|
137
|
+
};
|
|
138
|
+
};
|
|
139
|
+
|
|
97
140
|
const windowBody = (view, painted, frame) => {
|
|
98
141
|
const { width, color, bodyH, headerLines } = frame;
|
|
99
142
|
const listPane = isListPane(view.pane);
|
|
100
143
|
const fillKind = bodyFillKind(listPane, painted.splitBody);
|
|
101
|
-
const
|
|
102
|
-
const
|
|
144
|
+
const digits = numberDigits(view);
|
|
145
|
+
const filler = digits
|
|
146
|
+
? paintNumberFill(width, color, digits, painted.splitBody)
|
|
147
|
+
: paintBodyFill(width, color, fillKind);
|
|
148
|
+
const count = painted.body.length;
|
|
149
|
+
const shift = !listPane && count > 0 ? 1 : 0;
|
|
103
150
|
const lead = (items, first) =>
|
|
104
151
|
shift && items.length ? [first, ...items] : items;
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
152
|
+
let total = count + shift;
|
|
153
|
+
let taskOwners = lead(painted.taskOwners, null);
|
|
154
|
+
let taskChecks = lead(painted.taskChecks, false);
|
|
155
|
+
if (isTaskView(view) && total > bodyH) {
|
|
156
|
+
total += 1;
|
|
157
|
+
taskOwners = taskOwners.concat(null);
|
|
158
|
+
taskChecks = taskChecks.concat(false);
|
|
159
|
+
}
|
|
160
|
+
const scrollMax = Math.max(0, total - bodyH);
|
|
109
161
|
let scroll = listPane ? 0 : Math.min(view.scroll ?? 0, scrollMax);
|
|
110
162
|
if (isTaskView(view)) {
|
|
111
163
|
scroll = revealScroll(taskOwners, view.tasksFocus ?? 0, bodyH, scroll);
|
|
112
164
|
}
|
|
113
|
-
const slice =
|
|
165
|
+
const slice = [];
|
|
166
|
+
for (let at = scroll; at < Math.min(total, scroll + bodyH); at++) {
|
|
167
|
+
const index = at - shift;
|
|
168
|
+
const inBody = index >= 0 && index < count;
|
|
169
|
+
slice.push(inBody ? bodyRow(painted.body, index) : filler);
|
|
170
|
+
}
|
|
114
171
|
const win = { shift, scroll, shown: slice.length, headerLines };
|
|
115
172
|
const taskHits = [];
|
|
116
173
|
for (let i = 0; i < slice.length; i++) {
|
|
@@ -132,6 +189,7 @@ const windowBody = (view, painted, frame) => {
|
|
|
132
189
|
fileHits: painted.fileHits,
|
|
133
190
|
editHits: mapEditHits(painted.editHits, win),
|
|
134
191
|
cursor: mapBodyCursor(painted.cursor, win),
|
|
192
|
+
scrollBar: mapScrollBar(painted.scrollBar, win),
|
|
135
193
|
};
|
|
136
194
|
};
|
|
137
195
|
|
|
@@ -177,7 +235,21 @@ const renderFrame = (view, options = {}) => {
|
|
|
177
235
|
const hits = notePointerHits(notes, bodyH);
|
|
178
236
|
const statusY = rows.length - 1;
|
|
179
237
|
const branchHit = statusBranchHit(view, width, statusY);
|
|
180
|
-
const
|
|
238
|
+
const planHit = statusPlanHit(view, width, statusY);
|
|
239
|
+
const choices = statusChoiceHits(view, statusY);
|
|
240
|
+
const statusHits = [];
|
|
241
|
+
if (branchHit) statusHits.push(branchHit);
|
|
242
|
+
if (planHit) statusHits.push(planHit);
|
|
243
|
+
if (!statusHits.length) statusHits.push(...choices);
|
|
244
|
+
const menu = planMenu(view, width, color, statusY);
|
|
245
|
+
if (menu) {
|
|
246
|
+
for (const line of menu.lines) {
|
|
247
|
+
const index = line.y - 1;
|
|
248
|
+
if (index <= 0 || index >= rows.length) continue;
|
|
249
|
+
rows[index] = stampVisible(rows[index], menu.x, line.text);
|
|
250
|
+
}
|
|
251
|
+
statusHits.push(...menu.hits);
|
|
252
|
+
}
|
|
181
253
|
const editHits = [...windowed.editHits, ...hits.editHits];
|
|
182
254
|
const shown = markEditorSelection(rows, view, editHits, color);
|
|
183
255
|
const text = shown.map((row) => (color ? row + RESET : row)).join('\n');
|
|
@@ -195,9 +267,35 @@ const renderFrame = (view, options = {}) => {
|
|
|
195
267
|
scroll: windowed.scroll,
|
|
196
268
|
scrollMax: windowed.scrollMax,
|
|
197
269
|
listScroll: windowed.listScroll,
|
|
270
|
+
menuScroll: menu ? menu.scroll : painted.menuScroll,
|
|
271
|
+
runScroll: painted.runScroll,
|
|
272
|
+
gridCols: painted.gridCols,
|
|
198
273
|
cursor: hits.cursor ?? windowed.cursor,
|
|
274
|
+
scrollBar: windowed.scrollBar,
|
|
199
275
|
bodyH,
|
|
200
276
|
};
|
|
201
277
|
};
|
|
202
278
|
|
|
203
|
-
|
|
279
|
+
const presentCursor = (cursor) => {
|
|
280
|
+
if (!cursor || cursor.x <= 0 || cursor.y <= 0) return `${ESC}[?25l`;
|
|
281
|
+
const pos = `${ESC}[${cursor.y};${cursor.x}H`;
|
|
282
|
+
return `${pos}${ESC}[1 q${ESC}[?12h${ESC}[?25h`;
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
const presentRows = (rows, options = {}) => {
|
|
286
|
+
let out = `${ESC}[?25l${ESC}[?2026h`;
|
|
287
|
+
if (options.clear === true) out += `${ESC}[H${ESC}[2J`;
|
|
288
|
+
const prev = options.prev;
|
|
289
|
+
const same = prev && prev.length === rows.length ? prev : null;
|
|
290
|
+
for (let i = 0; i < rows.length; i++) {
|
|
291
|
+
if (same && same[i] === rows[i]) continue;
|
|
292
|
+
out += `${ESC}[${i + 1};1H${rows[i]}`;
|
|
293
|
+
}
|
|
294
|
+
return `${out}${ESC}[?2026l${presentCursor(options.cursor)}`;
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
module.exports = {
|
|
298
|
+
renderFrame,
|
|
299
|
+
presentCursor,
|
|
300
|
+
presentRows,
|
|
301
|
+
};
|