reslop 0.2.6 → 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 +27 -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 +61 -66
- package/lib/{files.js → common/files.js} +51 -52
- package/lib/common/find.js +61 -0
- 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 +117 -51
- package/lib/dashboard/npm.js +133 -32
- package/lib/dashboard/tree.js +27 -30
- package/lib/{session → dashboard}/watch.js +41 -31
- package/lib/dashboard/workspace.js +151 -0
- 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 -95
- 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 +240 -71
- package/lib/{editor.js → input/editor.js} +7 -8
- package/lib/{keys.js → input/keys.js} +36 -39
- 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/dashboard/blocks.js +490 -0
- 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 +22 -8
- package/lib/render/footer.js +383 -0
- package/lib/render/frame.js +127 -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 +29 -13
- package/lib/render/npm.js +322 -196
- package/lib/render/packages.js +89 -0
- package/lib/render/primitives.js +109 -16
- package/lib/render/render.js +7 -11
- package/lib/render/repos.js +214 -0
- package/lib/render/status.js +370 -0
- package/lib/render/status.json +85 -0
- package/lib/render/{dash-table.js → table.js} +19 -61
- package/lib/render/tiles.js +203 -47
- 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 +36 -44
- package/lib/session/change-actions.js +34 -100
- package/lib/session/commit.js +122 -56
- package/lib/session/compose.js +197 -146
- package/lib/session/dashboard.js +183 -37
- package/lib/session/dispatch.js +120 -22
- package/lib/session/files-pane.js +60 -24
- package/lib/session/find.js +81 -0
- package/lib/session/import.js +82 -0
- package/lib/session/items.js +8 -17
- package/lib/session/lifecycle.js +96 -66
- package/lib/session/load.js +2 -4
- package/lib/session/navigation.js +8 -3
- package/lib/session/npm/history.js +249 -0
- package/lib/session/npm/npm.js +815 -0
- package/lib/session/npm/packages.js +150 -0
- package/lib/session/ops.js +125 -34
- package/lib/session/pointer.js +216 -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} +166 -59
- package/lib/session/tasks.js +321 -66
- 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 +72 -59
- package/lib/session/workspace.js +416 -0
- 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 -505
- package/lib/detect.js +0 -34
- package/lib/diff/edit.js +0 -33
- package/lib/git-worktree.js +0 -437
- package/lib/git.js +0 -79
- package/lib/npm-commands.js +0 -522
- package/lib/render/chrome.js +0 -628
- package/lib/render/dash-blocks.js +0 -245
- package/lib/render/present.js +0 -19
- package/lib/render/report.js +0 -43
- package/lib/review.js +0 -744
- 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,32 +1,38 @@
|
|
|
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');
|
|
18
|
+
const packages = require('./packages.js');
|
|
19
|
+
const agents = require('./agents.js');
|
|
20
|
+
const repos = require('./repos.js');
|
|
15
21
|
|
|
16
|
-
const { RESET } = ansi;
|
|
17
|
-
const { overlayRows, markEditorSelection } = select;
|
|
18
22
|
const { isListPane, isTaskView, paintBodyFill, paneResult } = primitives;
|
|
19
|
-
const {
|
|
20
|
-
const {
|
|
21
|
-
const {
|
|
22
|
-
const { paintBodyDiff } = diff;
|
|
23
|
+
const { bodyRow } = primitives;
|
|
24
|
+
const { paintStatusLine, statusBranchHit, statusChoiceHits } = status;
|
|
25
|
+
const { statusPlanHit, planMenu } = status;
|
|
23
26
|
|
|
24
27
|
const PANE_BODY = {
|
|
25
28
|
dashboard: dashboard.paintBodyDashboard,
|
|
29
|
+
repos: repos.paintBodyRepos,
|
|
26
30
|
files: files.paintBodyFiles,
|
|
27
31
|
branches: branches.paintBodyBranches,
|
|
28
32
|
commits: commits.paintBodyCommits,
|
|
29
33
|
npm: npm.paintBodyNpm,
|
|
34
|
+
packages: packages.paintBodyPackages,
|
|
35
|
+
agents: agents.paintBodyAgents,
|
|
30
36
|
unit: unit.paintBodyUnit,
|
|
31
37
|
tasks: paintBodyTodo,
|
|
32
38
|
};
|
|
@@ -49,16 +55,35 @@ const frameNotes = (view, width, height, color) => {
|
|
|
49
55
|
return { panel, rows, cursor, bodyH };
|
|
50
56
|
};
|
|
51
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
|
+
|
|
52
72
|
const revealScroll = (owners, focus, height, prefer) => {
|
|
53
73
|
const first = owners.indexOf(focus);
|
|
54
74
|
if (first < 0) return prefer;
|
|
55
75
|
const last = owners.lastIndexOf(focus);
|
|
56
76
|
const room = Math.max(1, height);
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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;
|
|
60
84
|
}
|
|
61
|
-
|
|
85
|
+
const max = Math.max(0, owners.length - room);
|
|
86
|
+
return clamp(start, 0, max);
|
|
62
87
|
};
|
|
63
88
|
|
|
64
89
|
const windowY = (win, row) => {
|
|
@@ -85,28 +110,64 @@ const mapBodyCursor = (cursor, win) => {
|
|
|
85
110
|
return point;
|
|
86
111
|
};
|
|
87
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
|
+
|
|
88
119
|
const bodyFillKind = (listPane, splitBody) => {
|
|
89
120
|
if (listPane) return 'files';
|
|
90
121
|
return splitBody ? 'split' : 'plain';
|
|
91
122
|
};
|
|
92
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
|
+
|
|
93
140
|
const windowBody = (view, painted, frame) => {
|
|
94
141
|
const { width, color, bodyH, headerLines } = frame;
|
|
95
142
|
const listPane = isListPane(view.pane);
|
|
96
143
|
const fillKind = bodyFillKind(listPane, painted.splitBody);
|
|
97
|
-
const
|
|
98
|
-
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;
|
|
99
150
|
const lead = (items, first) =>
|
|
100
151
|
shift && items.length ? [first, ...items] : items;
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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);
|
|
105
161
|
let scroll = listPane ? 0 : Math.min(view.scroll ?? 0, scrollMax);
|
|
106
162
|
if (isTaskView(view)) {
|
|
107
163
|
scroll = revealScroll(taskOwners, view.tasksFocus ?? 0, bodyH, scroll);
|
|
108
164
|
}
|
|
109
|
-
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
|
+
}
|
|
110
171
|
const win = { shift, scroll, shown: slice.length, headerLines };
|
|
111
172
|
const taskHits = [];
|
|
112
173
|
for (let i = 0; i < slice.length; i++) {
|
|
@@ -128,6 +189,7 @@ const windowBody = (view, painted, frame) => {
|
|
|
128
189
|
fileHits: painted.fileHits,
|
|
129
190
|
editHits: mapEditHits(painted.editHits, win),
|
|
130
191
|
cursor: mapBodyCursor(painted.cursor, win),
|
|
192
|
+
scrollBar: mapScrollBar(painted.scrollBar, win),
|
|
131
193
|
};
|
|
132
194
|
};
|
|
133
195
|
|
|
@@ -173,7 +235,21 @@ const renderFrame = (view, options = {}) => {
|
|
|
173
235
|
const hits = notePointerHits(notes, bodyH);
|
|
174
236
|
const statusY = rows.length - 1;
|
|
175
237
|
const branchHit = statusBranchHit(view, width, statusY);
|
|
176
|
-
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
|
+
}
|
|
177
253
|
const editHits = [...windowed.editHits, ...hits.editHits];
|
|
178
254
|
const shown = markEditorSelection(rows, view, editHits, color);
|
|
179
255
|
const text = shown.map((row) => (color ? row + RESET : row)).join('\n');
|
|
@@ -191,9 +267,35 @@ const renderFrame = (view, options = {}) => {
|
|
|
191
267
|
scroll: windowed.scroll,
|
|
192
268
|
scrollMax: windowed.scrollMax,
|
|
193
269
|
listScroll: windowed.listScroll,
|
|
270
|
+
menuScroll: menu ? menu.scroll : painted.menuScroll,
|
|
271
|
+
runScroll: painted.runScroll,
|
|
272
|
+
gridCols: painted.gridCols,
|
|
194
273
|
cursor: hits.cursor ?? windowed.cursor,
|
|
274
|
+
scrollBar: windowed.scrollBar,
|
|
195
275
|
bodyH,
|
|
196
276
|
};
|
|
197
277
|
};
|
|
198
278
|
|
|
199
|
-
|
|
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
|
+
};
|