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
package/lib/render/tiles.js
CHANGED
|
@@ -1,24 +1,32 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
|
|
3
|
+
const { clamp } = require('../common/utilities.js');
|
|
4
|
+
|
|
5
|
+
const {
|
|
6
|
+
THEME,
|
|
7
|
+
paint,
|
|
8
|
+
visibleWidth,
|
|
9
|
+
truncateVisible,
|
|
10
|
+
} = require('../term/ansi.js');
|
|
5
11
|
|
|
6
12
|
const GAP_X = 2;
|
|
7
13
|
const GAP_Y = 1;
|
|
8
14
|
const MIN_W = 34;
|
|
9
15
|
const MIN_H = 6;
|
|
10
16
|
const MAX_COLS = 4;
|
|
17
|
+
const MAX_NORMAL = 3;
|
|
18
|
+
const MAX_WIDE = 2;
|
|
11
19
|
const RULE = '─';
|
|
12
20
|
const ELLIPSIS = '…';
|
|
13
21
|
const BAR_ON = '█';
|
|
14
22
|
const BAR_OFF = '░';
|
|
15
|
-
const SPARK = '▁▂▃▄▅▆▇█';
|
|
16
23
|
const SPINNER = '⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏';
|
|
17
24
|
|
|
18
25
|
const seg = (text, tone = 'text', bold = false) => ({
|
|
19
26
|
text: `${text}`,
|
|
20
27
|
tone,
|
|
21
28
|
bold,
|
|
29
|
+
bg: null,
|
|
22
30
|
});
|
|
23
31
|
|
|
24
32
|
const toneColors = () => ({
|
|
@@ -30,8 +38,18 @@ const toneColors = () => ({
|
|
|
30
38
|
warn: THEME.warnFg,
|
|
31
39
|
error: THEME.errorFg,
|
|
32
40
|
sha: THEME.shaFg,
|
|
33
|
-
|
|
41
|
+
dep: THEME.pkgDepFg,
|
|
42
|
+
dev: THEME.pkgDevFg,
|
|
43
|
+
pkg: THEME.pkgFg,
|
|
44
|
+
chain: THEME.pkgChainFg,
|
|
45
|
+
heat: THEME.heatFg,
|
|
46
|
+
heat1: THEME.heat1,
|
|
47
|
+
heat2: THEME.heat2,
|
|
48
|
+
heat3: THEME.heat3,
|
|
49
|
+
heat4: THEME.heat4,
|
|
50
|
+
blink: THEME.heatBlinkFg,
|
|
34
51
|
key: THEME.dashKeyFg,
|
|
52
|
+
caption: THEME.dashCaptionFg,
|
|
35
53
|
});
|
|
36
54
|
|
|
37
55
|
const headColors = () => ({
|
|
@@ -78,13 +96,14 @@ const paintSegments = (segs, width, bg, color, fill = ' ', colors = null) => {
|
|
|
78
96
|
}
|
|
79
97
|
let out = '';
|
|
80
98
|
for (const item of clipped) {
|
|
81
|
-
|
|
99
|
+
const pieceBg = item.bg ?? bg;
|
|
100
|
+
out += paint(item.text, ink[item.tone], pieceBg, color, item.bold);
|
|
82
101
|
}
|
|
83
102
|
return out + paint(fill.repeat(pad), ink.muted, bg, color);
|
|
84
103
|
};
|
|
85
104
|
|
|
86
105
|
const barSegments = (ratio, width, tone = 'add') => {
|
|
87
|
-
const clamped =
|
|
106
|
+
const clamped = clamp(Number.isFinite(ratio) ? ratio : 0, 0, 1);
|
|
88
107
|
const on = Math.round(clamped * width);
|
|
89
108
|
return [
|
|
90
109
|
seg(BAR_ON.repeat(on), tone),
|
|
@@ -92,52 +111,67 @@ const barSegments = (ratio, width, tone = 'add') => {
|
|
|
92
111
|
];
|
|
93
112
|
};
|
|
94
113
|
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const top = SPARK.length - 1;
|
|
100
|
-
let out = '';
|
|
101
|
-
for (let i = 0; i < width; i++) {
|
|
102
|
-
const span = width <= 1 ? count - 1 : (i * (count - 1)) / (width - 1);
|
|
103
|
-
const at = Math.round(span);
|
|
104
|
-
const level = max > 0 ? Math.round((values[at] / max) * top) : 0;
|
|
105
|
-
out += SPARK[level];
|
|
106
|
-
}
|
|
107
|
-
return out;
|
|
114
|
+
const RUN_ICON = {
|
|
115
|
+
passed: ['✔', 'add'],
|
|
116
|
+
failed: ['✖', 'error'],
|
|
117
|
+
aborted: ['⊘', 'muted'],
|
|
108
118
|
};
|
|
109
119
|
|
|
110
120
|
const spinner = (frame) => SPINNER[Math.abs(frame ?? 0) % SPINNER.length];
|
|
111
121
|
|
|
122
|
+
const blinkTone = (frame) => (Math.abs(frame) % 2 ? 'hot' : 'warn');
|
|
123
|
+
|
|
112
124
|
const spread = (total, parts) => {
|
|
113
125
|
const base = Math.floor(total / parts);
|
|
114
126
|
const extra = total % parts;
|
|
115
127
|
return Array.from({ length: parts }, (_, i) => base + (i < extra ? 1 : 0));
|
|
116
128
|
};
|
|
117
129
|
|
|
118
|
-
const
|
|
130
|
+
const isWide = (tile) => tile.size === 'wide';
|
|
131
|
+
|
|
132
|
+
const rowCap = (row, tile, maxCols) => {
|
|
133
|
+
const wide = isWide(tile) || row.some(isWide);
|
|
134
|
+
const cap = wide ? MAX_WIDE : MAX_NORMAL;
|
|
135
|
+
return Math.min(maxCols, cap);
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const canFit = (row, tile, maxCols) => row.length < rowCap(row, tile, maxCols);
|
|
139
|
+
|
|
140
|
+
const packRows = (shown, maxCols) => {
|
|
141
|
+
const rows = [];
|
|
142
|
+
let current = [];
|
|
143
|
+
for (const tile of shown) {
|
|
144
|
+
if (current.length && !canFit(current, tile, maxCols)) {
|
|
145
|
+
rows.push(current);
|
|
146
|
+
current = [];
|
|
147
|
+
}
|
|
148
|
+
current.push(tile);
|
|
149
|
+
}
|
|
150
|
+
if (current.length) rows.push(current);
|
|
151
|
+
return rows;
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const placeBand = (row, width) => {
|
|
155
|
+
const size = row.length;
|
|
119
156
|
const room = Math.max(0, width - (size - 1) * GAP_X);
|
|
120
157
|
const widths = spread(room, size);
|
|
121
158
|
const tiles = [];
|
|
122
159
|
let x = 0;
|
|
123
160
|
for (let i = 0; i < size; i++) {
|
|
124
|
-
tiles.push({ id:
|
|
161
|
+
tiles.push({ id: row[i].id, x, w: widths[i] });
|
|
125
162
|
x += widths[i] + GAP_X;
|
|
126
163
|
}
|
|
127
164
|
return tiles;
|
|
128
165
|
};
|
|
129
166
|
|
|
130
|
-
const
|
|
131
|
-
const
|
|
132
|
-
const
|
|
133
|
-
const heights = spread(height - (rows - 1) * GAP_Y, rows);
|
|
167
|
+
const placePacked = (rows, width, height) => {
|
|
168
|
+
const count = Math.max(1, rows.length);
|
|
169
|
+
const heights = spread(height - (count - 1) * GAP_Y, count);
|
|
134
170
|
const bands = [];
|
|
135
|
-
let from = 0;
|
|
136
171
|
let y = 0;
|
|
137
|
-
for (let row = 0; row < rows; row++) {
|
|
138
|
-
const tiles = placeBand(
|
|
172
|
+
for (let row = 0; row < rows.length; row++) {
|
|
173
|
+
const tiles = placeBand(rows[row], width);
|
|
139
174
|
bands.push({ y, h: Math.max(1, heights[row]), tiles });
|
|
140
|
-
from += sizes[row];
|
|
141
175
|
y += heights[row] + GAP_Y;
|
|
142
176
|
}
|
|
143
177
|
return bands;
|
|
@@ -145,16 +179,16 @@ const placeTiles = (shown, cols, width, height) => {
|
|
|
145
179
|
|
|
146
180
|
const layoutTiles = (width, height, tiles) => {
|
|
147
181
|
const cap = Math.floor((width + GAP_X) / (MIN_W + GAP_X));
|
|
148
|
-
const maxCols =
|
|
182
|
+
const maxCols = clamp(cap, 1, MAX_COLS);
|
|
149
183
|
const ranked = [...tiles].sort((left, right) => left.rank - right.rank);
|
|
150
184
|
for (let count = tiles.length; count >= 1; count--) {
|
|
151
185
|
const keep = new Set(ranked.slice(0, count).map((tile) => tile.id));
|
|
152
186
|
const shown = tiles.filter((tile) => keep.has(tile.id));
|
|
153
|
-
const
|
|
154
|
-
const
|
|
155
|
-
const rowH = Math.floor((height - (
|
|
187
|
+
const rows = packRows(shown, maxCols);
|
|
188
|
+
const n = rows.length;
|
|
189
|
+
const rowH = Math.floor((height - (n - 1) * GAP_Y) / n);
|
|
156
190
|
if (rowH >= MIN_H || count === 1) {
|
|
157
|
-
return
|
|
191
|
+
return placePacked(rows, width, height);
|
|
158
192
|
}
|
|
159
193
|
}
|
|
160
194
|
return [];
|
|
@@ -173,9 +207,15 @@ const paintFramed = (line, width, bg, color, fill = ' ', colors = null) => {
|
|
|
173
207
|
return edge + paintSegments(line, inner, bg, color, fill, colors) + edge;
|
|
174
208
|
};
|
|
175
209
|
|
|
176
|
-
const
|
|
210
|
+
const paintFlushRight = (line, width, bg, color) => {
|
|
211
|
+
const edge = paintSegments([seg(' ')], 1, bg, color);
|
|
212
|
+
const inner = Math.max(0, width - 1);
|
|
213
|
+
return edge + paintSegments(line, inner, bg, color);
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
const paintHead = (tile, content, width, color, headBg) => {
|
|
177
217
|
const fill = color ? ' ' : RULE;
|
|
178
|
-
const bg =
|
|
218
|
+
const bg = headBg;
|
|
179
219
|
const colors = headColors();
|
|
180
220
|
if (content.titleLine) {
|
|
181
221
|
return paintFramed(content.titleLine, width, bg, color, fill, colors);
|
|
@@ -184,22 +224,58 @@ const paintHead = (tile, content, width, color) => {
|
|
|
184
224
|
return paintSegments(segs, width, bg, color, fill, colors);
|
|
185
225
|
};
|
|
186
226
|
|
|
187
|
-
const rowBackground = (index, filled) => {
|
|
188
|
-
if (filled && index % 2 === 1) return
|
|
189
|
-
return
|
|
227
|
+
const rowBackground = (index, filled, backs) => {
|
|
228
|
+
if (filled && index % 2 === 1) return backs.row;
|
|
229
|
+
return backs.tile;
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const centeredSegments = (line, room) => {
|
|
233
|
+
const clipped = clipSegments(line, room);
|
|
234
|
+
const pad = Math.floor((room - segmentsWidth(clipped)) / 2);
|
|
235
|
+
if (pad < 1) return clipped;
|
|
236
|
+
return [seg(' '.repeat(pad)), ...clipped];
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
const centeredBody = (lines, room, inner) => {
|
|
240
|
+
const count = Math.min(lines.length, room);
|
|
241
|
+
const start = Math.floor((room - count) / 2);
|
|
242
|
+
const rows = [];
|
|
243
|
+
for (let i = 0; i < room; i++) {
|
|
244
|
+
const at = i - start;
|
|
245
|
+
const line = at >= 0 && at < count ? lines[at] : null;
|
|
246
|
+
if (!line || !line.length) {
|
|
247
|
+
rows.push([]);
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
rows.push(centeredSegments(line, inner));
|
|
251
|
+
}
|
|
252
|
+
return rows;
|
|
190
253
|
};
|
|
191
254
|
|
|
192
|
-
const paintTile = (tile, content, width, height, color) => {
|
|
193
|
-
const
|
|
194
|
-
|
|
255
|
+
const paintTile = (tile, content, width, height, color, backs = null) => {
|
|
256
|
+
const shade = backs ?? {
|
|
257
|
+
head: THEME.dashHeadBg,
|
|
258
|
+
tile: THEME.dashTileBg,
|
|
259
|
+
row: THEME.dashRowBg,
|
|
260
|
+
};
|
|
261
|
+
const lines = content.lines ?? [];
|
|
262
|
+
const { footer } = content;
|
|
263
|
+
const rows = [paintHead(tile, content, width, color, shade.head)];
|
|
195
264
|
const bodyRoom = footer ? height - 2 : height - 1;
|
|
265
|
+
const inner = Math.max(0, width - 2);
|
|
266
|
+
const centered = content.center ? centeredBody(lines, bodyRoom, inner) : null;
|
|
196
267
|
for (let i = 0; i < bodyRoom; i++) {
|
|
197
|
-
|
|
198
|
-
|
|
268
|
+
let line = [];
|
|
269
|
+
if (centered) line = centered[i];
|
|
270
|
+
else if (lines[i]) line = lines[i];
|
|
271
|
+
const filled = !centered && i < lines.length;
|
|
272
|
+
const bg = rowBackground(i, filled, shade);
|
|
273
|
+
rows.push(paintFramed(line, width, bg, color));
|
|
199
274
|
}
|
|
200
275
|
if (footer && height > 1) {
|
|
201
|
-
const bg = rowBackground(bodyRoom, true);
|
|
202
|
-
|
|
276
|
+
const bg = rowBackground(bodyRoom, true, shade);
|
|
277
|
+
const paintRow = content.footerFlush ? paintFlushRight : paintFramed;
|
|
278
|
+
rows.push(paintRow(footer, width, bg, color));
|
|
203
279
|
}
|
|
204
280
|
return rows;
|
|
205
281
|
};
|
|
@@ -229,6 +305,81 @@ const composeBands = (bands, painted, width, height, color) => {
|
|
|
229
305
|
return rows.slice(0, height);
|
|
230
306
|
};
|
|
231
307
|
|
|
308
|
+
const gridSize = (width, height, cols, rows) => ({
|
|
309
|
+
tileW: Math.floor((width - (cols - 1) * GAP_X) / cols),
|
|
310
|
+
tileH: Math.floor((height - (rows - 1) * GAP_Y) / rows),
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
const equalGrid = (width, height, count, minW = MIN_W, minH = MIN_H) => {
|
|
314
|
+
const safe = Math.max(1, count);
|
|
315
|
+
const colCap = Math.max(
|
|
316
|
+
1,
|
|
317
|
+
Math.min(safe, Math.floor((width + GAP_X) / (minW + GAP_X)) || 1),
|
|
318
|
+
);
|
|
319
|
+
const rowCap = Math.max(
|
|
320
|
+
1,
|
|
321
|
+
Math.min(safe, Math.floor((height + GAP_Y) / (minH + GAP_Y)) || 1),
|
|
322
|
+
);
|
|
323
|
+
let best = null;
|
|
324
|
+
for (let cols = 1; cols <= colCap; cols++) {
|
|
325
|
+
for (let rows = 1; rows <= rowCap; rows++) {
|
|
326
|
+
const size = gridSize(width, height, cols, rows);
|
|
327
|
+
if (size.tileW < minW || size.tileH < minH) continue;
|
|
328
|
+
const cap = cols * rows;
|
|
329
|
+
const shown = Math.min(safe, cap);
|
|
330
|
+
const score = shown * 1000000 + size.tileW * size.tileH;
|
|
331
|
+
if (!best || score > best.score) {
|
|
332
|
+
best = { width, cols, rows, cap, ...size, score };
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
if (best) return best;
|
|
337
|
+
return {
|
|
338
|
+
width,
|
|
339
|
+
cols: 1,
|
|
340
|
+
rows: 1,
|
|
341
|
+
cap: 1,
|
|
342
|
+
tileW: Math.max(1, width),
|
|
343
|
+
tileH: Math.max(1, height),
|
|
344
|
+
score: 0,
|
|
345
|
+
};
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
const pageWindow = (count, cursor, cap) => {
|
|
349
|
+
const room = Math.max(1, cap);
|
|
350
|
+
if (count <= room) return { start: 0, end: count };
|
|
351
|
+
const at = clamp(cursor, 0, count - 1);
|
|
352
|
+
let start = Math.floor(at / room) * room;
|
|
353
|
+
if (start + room > count) start = count - room;
|
|
354
|
+
return { start, end: start + room };
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
const placeEqualTiles = (tiles, grid) => {
|
|
358
|
+
const bands = [];
|
|
359
|
+
const cols = Math.max(1, grid.cols);
|
|
360
|
+
const rows = Math.ceil(tiles.length / cols);
|
|
361
|
+
const gaps = Math.max(0, cols - 1) * GAP_X;
|
|
362
|
+
const full = grid.width ?? cols * grid.tileW + gaps;
|
|
363
|
+
const room = Math.max(0, full - gaps);
|
|
364
|
+
const widths = spread(room, cols);
|
|
365
|
+
for (let row = 0; row < rows; row++) {
|
|
366
|
+
const slice = tiles.slice(row * cols, (row + 1) * cols);
|
|
367
|
+
if (!slice.length) break;
|
|
368
|
+
const placed = [];
|
|
369
|
+
let x = 0;
|
|
370
|
+
for (let i = 0; i < slice.length; i++) {
|
|
371
|
+
const w = widths[i];
|
|
372
|
+
placed.push({ id: slice[i].id, x, w });
|
|
373
|
+
x += w + GAP_X;
|
|
374
|
+
}
|
|
375
|
+
const y = row * (grid.tileH + GAP_Y);
|
|
376
|
+
const h = grid.tileH;
|
|
377
|
+
const band = { y, h, tiles: placed };
|
|
378
|
+
bands.push(band);
|
|
379
|
+
}
|
|
380
|
+
return bands;
|
|
381
|
+
};
|
|
382
|
+
|
|
232
383
|
const tileHits = (bands, headerLines) => {
|
|
233
384
|
const hits = [];
|
|
234
385
|
for (const band of bands) {
|
|
@@ -249,10 +400,15 @@ module.exports = {
|
|
|
249
400
|
segmentsWidth,
|
|
250
401
|
clipSegments,
|
|
251
402
|
barSegments,
|
|
252
|
-
|
|
403
|
+
RUN_ICON,
|
|
253
404
|
spinner,
|
|
405
|
+
blinkTone,
|
|
254
406
|
titleSegments,
|
|
255
407
|
layoutTiles,
|
|
408
|
+
equalGrid,
|
|
409
|
+
pageWindow,
|
|
410
|
+
placeEqualTiles,
|
|
411
|
+
paintSegments,
|
|
256
412
|
paintTile,
|
|
257
413
|
composeBands,
|
|
258
414
|
tileHits,
|
package/lib/render/unit.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
const { detectLang } = require('../highlight/highlight.js');
|
|
4
|
+
const { sameHunk } = require('../diff/diff.js');
|
|
5
|
+
const { paneResult, lazyRows } = require('./primitives.js');
|
|
6
|
+
const {
|
|
7
|
+
paintDiffLine,
|
|
8
|
+
expandSoftRows,
|
|
9
|
+
codeHits,
|
|
10
|
+
viewDigits,
|
|
11
|
+
} = require('./diff.js');
|
|
12
12
|
|
|
13
13
|
const paintBodyUnit = (view, width, color) => {
|
|
14
14
|
const { item, codeOverlay: overlay } = view;
|
|
@@ -17,14 +17,33 @@ const paintBodyUnit = (view, width, color) => {
|
|
|
17
17
|
view.mode === 'compose' && typeof overlay?.cursor === 'number';
|
|
18
18
|
const scrollCol = (editing ? overlay.scrollCol : 0) ?? 0;
|
|
19
19
|
const lines = view.unitLines ?? [];
|
|
20
|
-
const
|
|
21
|
-
|
|
20
|
+
const digits = viewDigits(view);
|
|
21
|
+
let source = lines;
|
|
22
|
+
if (!editing) source = expandSoftRows(lines, width, false, true, digits);
|
|
23
|
+
const body = lazyRows(source.length, (index) => {
|
|
24
|
+
const line = source[index];
|
|
22
25
|
const changed = line.type === 'add' || line.type === 'del';
|
|
23
26
|
const outside = changed && !(item && sameHunk(line.item, item));
|
|
24
27
|
const row = editing || outside ? { ...line, mark: ' ' } : line;
|
|
25
|
-
return paintDiffLine(
|
|
28
|
+
return paintDiffLine(
|
|
29
|
+
row,
|
|
30
|
+
width,
|
|
31
|
+
color,
|
|
32
|
+
row.origin,
|
|
33
|
+
lang,
|
|
34
|
+
scrollCol,
|
|
35
|
+
digits,
|
|
36
|
+
);
|
|
26
37
|
});
|
|
27
|
-
const hits = codeHits(
|
|
38
|
+
const hits = codeHits(
|
|
39
|
+
source,
|
|
40
|
+
false,
|
|
41
|
+
width,
|
|
42
|
+
overlay,
|
|
43
|
+
editing,
|
|
44
|
+
scrollCol,
|
|
45
|
+
digits,
|
|
46
|
+
);
|
|
28
47
|
return paneResult({ body, ...hits });
|
|
29
48
|
};
|
|
30
49
|
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const {
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
const { traceLine, relativize } = commands;
|
|
3
|
+
const { stripAnsi } = require('../term/ansi.js');
|
|
4
|
+
const { traceLine, relativize } = require('../runs/output.js');
|
|
5
|
+
const { trimText } = require('../common/utilities.js');
|
|
8
6
|
|
|
9
7
|
const summary = () => ({
|
|
10
8
|
tests: null,
|
|
@@ -59,7 +57,7 @@ const userTrace = (text, root) => {
|
|
|
59
57
|
};
|
|
60
58
|
|
|
61
59
|
const normalizeMessage = (message) => {
|
|
62
|
-
const text =
|
|
60
|
+
const text = trimText(message).replace(/\s+/g, ' ');
|
|
63
61
|
return text.replace(/\s+\S+:\d+:\d+$/, '');
|
|
64
62
|
};
|
|
65
63
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const { stripAnsi } = ansi;
|
|
5
|
-
const model = require('./
|
|
6
|
-
const testParse = require('./
|
|
7
|
-
const toolParse = require('./
|
|
3
|
+
const { jsonParse } = require('metautil');
|
|
4
|
+
const { stripAnsi } = require('../term/ansi.js');
|
|
5
|
+
const model = require('./model.js');
|
|
6
|
+
const testParse = require('./test.js');
|
|
7
|
+
const toolParse = require('./tools.js');
|
|
8
8
|
|
|
9
9
|
const { problem, report, userTrace, groupReport, envelope } = model;
|
|
10
10
|
const { parseTap, parseNodeJsonl, parseTestJson, parseSpec } = testParse;
|
|
@@ -27,11 +27,7 @@ const toolHint = (program, args) => {
|
|
|
27
27
|
const parseJson = (text) => {
|
|
28
28
|
const trimmed = text.trim();
|
|
29
29
|
if (!trimmed || (trimmed[0] !== '{' && trimmed[0] !== '[')) return null;
|
|
30
|
-
|
|
31
|
-
return JSON.parse(trimmed);
|
|
32
|
-
} catch {
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
30
|
+
return jsonParse(trimmed);
|
|
35
31
|
};
|
|
36
32
|
|
|
37
33
|
const parseJsonReports = (text, root) => {
|
|
@@ -91,10 +87,9 @@ const mergeReports = (left, right) => {
|
|
|
91
87
|
const buildDocument = (stdout, stderr, root, exit, hint) => {
|
|
92
88
|
const out = stripAnsi(stdout);
|
|
93
89
|
const err = stripAnsi(stderr);
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
);
|
|
90
|
+
const fromOut = parseReports(out, root);
|
|
91
|
+
const fromErr = parseReports(err, root);
|
|
92
|
+
const reports = mergeReports(fromOut, fromErr);
|
|
98
93
|
const text = out.trim() ? out : err;
|
|
99
94
|
const grouped = withHint(reports, text, exit, hint).map(groupReport);
|
|
100
95
|
const rest = stderrRest(err, grouped);
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const {
|
|
3
|
+
const { unquote } = require('../common/utilities.js');
|
|
4
|
+
const { formatTable } = require('../runs/output.js');
|
|
5
5
|
|
|
6
6
|
const NOTICE_HEAD =
|
|
7
7
|
'reslop: reduced output; passing results are omitted ' +
|
|
8
8
|
'and similar problems are grouped';
|
|
9
9
|
|
|
10
10
|
const notice = (rawFile) => {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
: 'the matching raw log under `.log/`';
|
|
11
|
+
let target = 'the matching raw log under `.log/`';
|
|
12
|
+
if (rawFile) target = `the raw log \`.log/${rawFile}\``;
|
|
14
13
|
const hint = `Agent: if this report hides a needed detail, read ${target}.`;
|
|
15
14
|
return `${NOTICE_HEAD}\n${hint}`;
|
|
16
15
|
};
|
|
@@ -38,12 +37,12 @@ const sameValue = (line, value) => {
|
|
|
38
37
|
return text === value || unquote(text) === value;
|
|
39
38
|
};
|
|
40
39
|
|
|
41
|
-
const startsDiff = (line, item) =>
|
|
42
|
-
isComparison(line) ||
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
sameValue(line, item.
|
|
46
|
-
|
|
40
|
+
const startsDiff = (line, item) => {
|
|
41
|
+
if (isComparison(line) || isDiffHeader(line)) return true;
|
|
42
|
+
if (isMarkedDiff(line)) return true;
|
|
43
|
+
if (sameValue(line, item.expected)) return true;
|
|
44
|
+
return sameValue(line, item.actual);
|
|
45
|
+
};
|
|
47
46
|
|
|
48
47
|
const displayMessage = (item) => {
|
|
49
48
|
const values = item.expected !== '' || item.actual !== '';
|
|
@@ -135,13 +134,46 @@ const loggedText = (text, rawFile) => {
|
|
|
135
134
|
return `${head}\n\n${body}`;
|
|
136
135
|
};
|
|
137
136
|
|
|
137
|
+
const reportProblem = (item) => {
|
|
138
|
+
const title = item.name || item.message || item.code || 'problem';
|
|
139
|
+
const mark = item.severity === 'warning' ? '△' : '✖';
|
|
140
|
+
const count = item.count > 1 ? ` × ${item.count}` : '';
|
|
141
|
+
const lines = ['', `${mark} ${title.split('\n')[0]}${count}`, ''];
|
|
142
|
+
lines.push(...formatTable(problemRows(item)));
|
|
143
|
+
const message = displayMessage(item);
|
|
144
|
+
if (message) lines.push('', ...quoteLines(message));
|
|
145
|
+
if (item.trace) {
|
|
146
|
+
lines.push('', 'Stack');
|
|
147
|
+
for (const row of item.trace.split(/\r?\n/)) {
|
|
148
|
+
const frame = row.trim().replace(/^at\s+/, '');
|
|
149
|
+
if (frame) lines.push(` at ${frame}`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (item.related.length) {
|
|
153
|
+
lines.push('', 'Related failures');
|
|
154
|
+
for (const row of item.related) lines.push(` ↳ ${row}`);
|
|
155
|
+
}
|
|
156
|
+
return lines;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const renderReport = (doc) => {
|
|
160
|
+
const lines = [];
|
|
161
|
+
for (const item of doc.reports) {
|
|
162
|
+
if (lines.length) lines.push('');
|
|
163
|
+
const mark = item.ok ? '✔' : '✖';
|
|
164
|
+
lines.push(`${mark} ${item.tool} · exit ${doc.exit}`, '');
|
|
165
|
+
lines.push(...formatTable(summaryRows(item.summary)));
|
|
166
|
+
for (const entry of item.problems) lines.push(...reportProblem(entry));
|
|
167
|
+
}
|
|
168
|
+
if (doc.stderr) lines.push('', 'Stderr', doc.stderr);
|
|
169
|
+
return `${lines.join('\n')}\n`;
|
|
170
|
+
};
|
|
171
|
+
|
|
138
172
|
module.exports = {
|
|
139
173
|
NOTICE_HEAD,
|
|
140
174
|
notice,
|
|
141
|
-
summaryRows,
|
|
142
|
-
problemRows,
|
|
143
175
|
displayMessage,
|
|
144
|
-
quoteLines,
|
|
145
176
|
loggedText,
|
|
146
177
|
renderDocument,
|
|
178
|
+
renderReport,
|
|
147
179
|
};
|