reslop 0.2.5 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -1
- package/LICENSE +1 -1
- package/README.md +11 -11
- package/bin/reslop.js +3 -8
- package/lib/ansi.js +32 -19
- package/lib/capabilities.js +8 -8
- package/lib/cli.js +40 -74
- package/lib/clipboard.js +39 -47
- package/lib/dashboard/git.js +128 -0
- package/lib/dashboard/model.js +332 -0
- package/lib/dashboard/npm.js +193 -0
- package/lib/dashboard/tree.js +278 -0
- package/lib/deps-diff.js +197 -402
- package/lib/deps.js +113 -240
- package/lib/detect.js +8 -18
- package/lib/diff/align.js +41 -113
- package/lib/diff/diff.js +12 -43
- package/lib/diff/display.js +25 -36
- package/lib/diff/edit.js +6 -20
- package/lib/diff/inline.js +76 -144
- package/lib/diff/items.js +33 -47
- package/lib/diff/parse.js +28 -56
- package/lib/diff/patch.js +12 -33
- package/lib/diff/unit.js +79 -178
- package/lib/editor.js +52 -69
- package/lib/files.js +71 -89
- package/lib/git-branches.js +254 -416
- package/lib/git-deps.js +147 -272
- package/lib/git-reword-todo.js +5 -13
- package/lib/git-worktree.js +141 -212
- package/lib/git.js +21 -49
- package/lib/github.js +67 -181
- package/lib/gitlab.js +40 -90
- package/lib/highlight/bash.js +55 -177
- package/lib/highlight/core.js +155 -141
- package/lib/highlight/css.js +25 -58
- package/lib/highlight/csv.js +19 -37
- package/lib/highlight/dart.js +72 -213
- package/lib/highlight/dot.js +14 -14
- package/lib/highlight/html.js +42 -63
- package/lib/highlight/js.js +177 -506
- package/lib/highlight/json.js +17 -37
- package/lib/highlight/log.js +19 -25
- package/lib/highlight/txt.js +7 -10
- package/lib/highlight.js +17 -46
- package/lib/keys.js +49 -82
- package/lib/manifest.js +67 -126
- package/lib/npm-commands.js +120 -169
- package/lib/remote.js +140 -190
- package/lib/render/branches.js +63 -61
- package/lib/render/chrome.js +220 -333
- package/lib/render/commits.js +160 -361
- package/lib/render/dash-activity.js +212 -0
- package/lib/render/dash-blocks.js +245 -0
- package/lib/render/dash-table.js +290 -0
- package/lib/render/dashboard.js +62 -0
- package/lib/render/diff.js +123 -258
- package/lib/render/files.js +40 -85
- package/lib/render/frame.js +78 -117
- package/lib/render/notes.js +139 -210
- package/lib/render/npm.js +213 -177
- package/lib/render/present.js +4 -11
- package/lib/render/primitives.js +72 -174
- package/lib/render/render.js +5 -14
- package/lib/render/report.js +43 -0
- package/lib/render/tiles.js +260 -0
- package/lib/render/unit.js +15 -31
- package/lib/report-model.js +110 -33
- package/lib/report-parse.js +56 -37
- package/lib/report-render.js +102 -59
- package/lib/report-run.js +87 -77
- package/lib/report-test.js +221 -99
- package/lib/report-tools.js +96 -154
- package/lib/review-merge.js +33 -41
- package/lib/review.js +68 -129
- package/lib/runs.js +225 -0
- package/lib/select.js +53 -91
- package/lib/session/accessors.js +14 -21
- package/lib/session/actions.js +61 -43
- package/lib/session/branches.js +166 -222
- package/lib/session/change-actions.js +49 -119
- package/lib/session/commit.js +274 -355
- package/lib/session/compose.js +556 -714
- package/lib/session/dashboard.js +385 -0
- package/lib/session/dispatch.js +162 -170
- package/lib/session/files-pane.js +76 -44
- package/lib/session/items.js +6 -17
- package/lib/session/lifecycle.js +393 -466
- package/lib/session/load.js +1 -14
- package/lib/session/navigation.js +2 -2
- package/lib/session/npm.js +403 -420
- package/lib/session/ops.js +5 -13
- package/lib/session/pointer.js +16 -14
- package/lib/session/progress.js +39 -30
- package/lib/session/review.js +1 -3
- package/lib/session/tasks.js +276 -235
- package/lib/session/templates.js +51 -59
- package/lib/session/terminal.js +85 -132
- package/lib/session/view.js +47 -81
- package/lib/session/watch.js +196 -212
- package/lib/session.js +81 -161
- package/lib/update.js +44 -111
- package/lib/utilities.js +46 -88
- package/lib/wrap.js +29 -55
- package/package.json +1 -1
package/lib/render/files.js
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
const ansi = require('../ansi.js');
|
|
4
4
|
const files = require('../files.js');
|
|
5
5
|
const primitives = require('./primitives.js');
|
|
6
|
-
const { measureColumns, padColumns } = primitives;
|
|
7
6
|
|
|
8
7
|
const { isTasksEntry, shortAge } = files;
|
|
9
|
-
const { THEME, paint, visibleWidth,
|
|
10
|
-
const { fill,
|
|
8
|
+
const { THEME, paint, visibleWidth, trimVisible, seq, EL } = ansi;
|
|
9
|
+
const { fill, markPrefix, paintCursorList } = primitives;
|
|
10
|
+
const { measureColumns, padColumns } = primitives;
|
|
11
11
|
|
|
12
12
|
const FILE_STATUS_FG = {
|
|
13
13
|
unstaged: THEME.delLineFg,
|
|
@@ -27,115 +27,70 @@ const FILE_COL_ALIGN = {
|
|
|
27
27
|
|
|
28
28
|
const FILE_DATE_GAP = ' ';
|
|
29
29
|
|
|
30
|
-
const signedCount = (sign, n) => `${sign}${n ?? 0}`;
|
|
31
|
-
|
|
32
30
|
const fileRowFields = (entry) => {
|
|
31
|
+
const n = `${entry.staged ?? 0}`;
|
|
32
|
+
const m = `${entry.remaining ?? 0}`;
|
|
33
33
|
if (isTasksEntry(entry)) {
|
|
34
|
-
const n = `${entry.staged ?? 0}`;
|
|
35
|
-
const m = `${entry.remaining ?? 0}`;
|
|
36
34
|
return { add: '', del: '', status: '', n, m, date: '' };
|
|
37
35
|
}
|
|
38
|
-
const {
|
|
39
|
-
const
|
|
40
|
-
const del = signedCount('-', removed);
|
|
36
|
+
const add = `+${entry.added ?? 0}`;
|
|
37
|
+
const del = `-${entry.removed ?? 0}`;
|
|
41
38
|
const status = entry.status ?? '';
|
|
42
|
-
|
|
43
|
-
const m = `${remaining ?? 0}`;
|
|
44
|
-
const date = shortAge(entry.date ?? '');
|
|
45
|
-
return { add, del, status, n, m, date };
|
|
39
|
+
return { add, del, status, n, m, date: shortAge(entry.date) };
|
|
46
40
|
};
|
|
47
41
|
|
|
48
|
-
const fileRowFg = (
|
|
49
|
-
|
|
42
|
+
const fileRowFg = (entry, selected) => {
|
|
43
|
+
if (isTasksEntry(entry)) return THEME.buttonHotFg;
|
|
44
|
+
const statusFg = FILE_STATUS_FG[entry.status];
|
|
50
45
|
if (statusFg) return statusFg;
|
|
51
|
-
|
|
52
|
-
return THEME.mutedFg;
|
|
46
|
+
return selected ? THEME.chromeFg : THEME.mutedFg;
|
|
53
47
|
};
|
|
54
48
|
|
|
55
|
-
const
|
|
56
|
-
if (!cols?.date) return '';
|
|
57
|
-
return `${FILE_DATE_GAP}${parts.date}`;
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
const joinFileMeta = (parts) => {
|
|
61
|
-
const { add, del, status, n, m } = parts;
|
|
62
|
-
return ` ${add} ${del} ${status} ${n}/${m}`;
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
const paintFileSuffix = (parts, cols, fgRgb, bgRgb, color) => {
|
|
66
|
-
const { add, del, status, n, m } = parts;
|
|
49
|
+
const paintFileMeta = (parts, fgRgb, bgRgb, color) => {
|
|
67
50
|
const gap = paint(' ', fgRgb, bgRgb, color);
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const counts = addText + gap + delText;
|
|
75
|
-
const meta = statusText + gap + nText + slash + mText;
|
|
76
|
-
if (!cols?.date) return gap + counts + gap + meta;
|
|
77
|
-
const dateGap = paint(FILE_DATE_GAP, fgRgb, bgRgb, color);
|
|
78
|
-
const age = paint(parts.date, THEME.mutedFg, bgRgb, color);
|
|
79
|
-
return gap + counts + gap + meta + dateGap + age;
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
const paintTodosRow = (entry, width, color, selected, cols) => {
|
|
83
|
-
const inner = Math.max(1, width - 1);
|
|
84
|
-
const mark = selected ? FILE_MARK : ' ';
|
|
85
|
-
const prefix = ` ${mark} `;
|
|
86
|
-
const parts = padColumns(fileRowFields(entry), cols, FILE_COL_ALIGN);
|
|
87
|
-
const meta = joinFileMeta(parts);
|
|
88
|
-
const dated = joinFileDate(parts, cols);
|
|
89
|
-
const suffix = `${meta}${dated}`;
|
|
90
|
-
const pathW = Math.max(1, inner - visibleWidth(prefix + suffix));
|
|
91
|
-
const clipped = truncateVisible(entry.path, pathW);
|
|
92
|
-
const pad = Math.max(0, pathW - visibleWidth(clipped));
|
|
93
|
-
const left = `${prefix}${clipped}${' '.repeat(pad)}`;
|
|
94
|
-
const fgRgb = THEME.buttonHotFg;
|
|
95
|
-
const bgRgb = selected ? THEME.buttonBg : THEME.ctxBg;
|
|
96
|
-
const edge = fill(1, fgRgb, bgRgb, color);
|
|
97
|
-
if (!color) return `${left}${suffix}${edge}`;
|
|
98
|
-
let out = `${seq(fgRgb, bgRgb)}${EL}`;
|
|
99
|
-
out += paint(left, fgRgb, bgRgb, color, true);
|
|
100
|
-
out += paint(meta, fgRgb, bgRgb, color, true);
|
|
101
|
-
if (!dated) return out + edge;
|
|
102
|
-
out += paint(FILE_DATE_GAP, fgRgb, bgRgb, color);
|
|
103
|
-
out += paint(parts.date, THEME.mutedFg, bgRgb, color);
|
|
104
|
-
return out + edge;
|
|
51
|
+
let out = gap + paint(parts.add, THEME.addLineFg, bgRgb, color);
|
|
52
|
+
out += gap + paint(parts.del, THEME.delLineFg, bgRgb, color);
|
|
53
|
+
out += gap + paint(parts.status, fgRgb, bgRgb, color);
|
|
54
|
+
out += gap + paint(parts.n, fgRgb, bgRgb, color);
|
|
55
|
+
out += paint('/', fgRgb, bgRgb, color);
|
|
56
|
+
return out + paint(parts.m, fgRgb, bgRgb, color);
|
|
105
57
|
};
|
|
106
58
|
|
|
107
59
|
const paintFileRow = (entry, width, color, selected, cols) => {
|
|
108
|
-
|
|
109
|
-
return paintTodosRow(entry, width, color, selected, cols);
|
|
110
|
-
}
|
|
60
|
+
const tasks = isTasksEntry(entry);
|
|
111
61
|
const inner = Math.max(1, width - 1);
|
|
112
|
-
const
|
|
113
|
-
const prefix = ` ${mark} `;
|
|
62
|
+
const prefix = markPrefix(selected);
|
|
114
63
|
const parts = padColumns(fileRowFields(entry), cols, FILE_COL_ALIGN);
|
|
115
|
-
const
|
|
116
|
-
const
|
|
117
|
-
const
|
|
118
|
-
const
|
|
119
|
-
const
|
|
120
|
-
const
|
|
121
|
-
const
|
|
64
|
+
const { add, del, status, n, m } = parts;
|
|
65
|
+
const meta = ` ${add} ${del} ${status} ${n}/${m}`;
|
|
66
|
+
const date = cols.date ? `${FILE_DATE_GAP}${parts.date}` : '';
|
|
67
|
+
const pathW = Math.max(1, inner - visibleWidth(prefix + meta + date));
|
|
68
|
+
const path = trimVisible(entry.path, pathW);
|
|
69
|
+
const pad = ' '.repeat(Math.max(0, pathW - visibleWidth(path)));
|
|
70
|
+
const left = `${prefix}${path}${pad}`;
|
|
71
|
+
const fgRgb = fileRowFg(entry, selected);
|
|
122
72
|
const bgRgb = selected ? THEME.buttonBg : THEME.ctxBg;
|
|
123
73
|
const edge = fill(1, fgRgb, bgRgb, color);
|
|
124
|
-
if (!color) return `${left}${
|
|
74
|
+
if (!color) return `${left}${meta}${date}${edge}`;
|
|
125
75
|
let out = `${seq(fgRgb, bgRgb)}${EL}`;
|
|
126
|
-
out += paint(left, fgRgb, bgRgb, color);
|
|
127
|
-
out +=
|
|
76
|
+
out += paint(left, fgRgb, bgRgb, color, tasks);
|
|
77
|
+
if (tasks) out += paint(meta, fgRgb, bgRgb, color, true);
|
|
78
|
+
else out += paintFileMeta(parts, fgRgb, bgRgb, color);
|
|
79
|
+
if (date) {
|
|
80
|
+
out += paint(FILE_DATE_GAP, fgRgb, bgRgb, color);
|
|
81
|
+
out += paint(parts.date, THEME.mutedFg, bgRgb, color);
|
|
82
|
+
}
|
|
128
83
|
return out + edge;
|
|
129
84
|
};
|
|
130
85
|
|
|
131
86
|
const paintBodyFiles = (view, width, color, bodyH, headerLines) => {
|
|
132
|
-
const
|
|
133
|
-
const cols = measureColumns(
|
|
87
|
+
const entries = view.files ?? [];
|
|
88
|
+
const cols = measureColumns(entries, fileRowFields, FILE_COL_ALIGN);
|
|
134
89
|
const cursor = view.fileCursor ?? 0;
|
|
135
90
|
const pane = { width, color, bodyH, headerLines, offset: view.listScroll };
|
|
136
91
|
const paintRow = (entry, selected) =>
|
|
137
92
|
paintFileRow(entry, width, color, selected, cols);
|
|
138
|
-
return paintCursorList(
|
|
93
|
+
return paintCursorList(entries, cursor, pane, paintRow);
|
|
139
94
|
};
|
|
140
95
|
|
|
141
96
|
module.exports = { paintBodyFiles };
|
package/lib/render/frame.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
const ansi = require('../ansi.js');
|
|
4
4
|
const select = require('../select.js');
|
|
5
|
-
const { overlayRows, markEditorSelection } = select;
|
|
6
5
|
const primitives = require('./primitives.js');
|
|
7
6
|
const chrome = require('./chrome.js');
|
|
8
7
|
const files = require('./files.js');
|
|
@@ -12,91 +11,54 @@ const notes = require('./notes.js');
|
|
|
12
11
|
const npm = require('./npm.js');
|
|
13
12
|
const diff = require('./diff.js');
|
|
14
13
|
const unit = require('./unit.js');
|
|
14
|
+
const dashboard = require('./dashboard.js');
|
|
15
15
|
|
|
16
16
|
const { RESET } = ansi;
|
|
17
|
-
const {
|
|
17
|
+
const { overlayRows, markEditorSelection } = select;
|
|
18
|
+
const { isListPane, isTaskView, paintBodyFill, paneResult } = primitives;
|
|
18
19
|
const { paintHeader, paintFooter, footerHits, paintStatusLine } = chrome;
|
|
19
20
|
const { footerLayout, statusBranchHit, statusChoiceHits } = chrome;
|
|
20
21
|
const { paintNotePanel, paintBodyTodo } = notes;
|
|
21
|
-
const { paintBodyFiles } = files;
|
|
22
|
-
const { paintBodyBranches } = branches;
|
|
23
|
-
const { paintBodyCommits } = commits;
|
|
24
|
-
const { paintBodyNpm } = npm;
|
|
25
22
|
const { paintBodyDiff } = diff;
|
|
26
|
-
const { paintBodyUnit } = unit;
|
|
27
23
|
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (view.item.origin === 'task') return 'tasks';
|
|
37
|
-
return 'diff';
|
|
24
|
+
const PANE_BODY = {
|
|
25
|
+
dashboard: dashboard.paintBodyDashboard,
|
|
26
|
+
files: files.paintBodyFiles,
|
|
27
|
+
branches: branches.paintBodyBranches,
|
|
28
|
+
commits: commits.paintBodyCommits,
|
|
29
|
+
npm: npm.paintBodyNpm,
|
|
30
|
+
unit: unit.paintBodyUnit,
|
|
31
|
+
tasks: paintBodyTodo,
|
|
38
32
|
};
|
|
39
33
|
|
|
40
34
|
const paintBodyEmpty = () => paneResult();
|
|
41
35
|
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
npm: paintBodyNpm,
|
|
47
|
-
empty: paintBodyEmpty,
|
|
48
|
-
tasks: paintBodyTodo,
|
|
49
|
-
diff: paintBodyDiff,
|
|
50
|
-
unit: paintBodyUnit,
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
const paintFrameBody = (view, width, color, bodyH, headerLines) => {
|
|
54
|
-
const paint = BODY_PAINT[bodyKind(view)];
|
|
55
|
-
return paint(view, width, color, bodyH, headerLines);
|
|
36
|
+
const bodyPainter = (view) => {
|
|
37
|
+
if (Object.hasOwn(PANE_BODY, view.pane)) return PANE_BODY[view.pane];
|
|
38
|
+
if (!view.item) return paintBodyEmpty;
|
|
39
|
+
return paintBodyDiff;
|
|
56
40
|
};
|
|
57
41
|
|
|
58
42
|
const frameNotes = (view, width, height, color) => {
|
|
59
43
|
const notesCap = Math.max(0, height - 4);
|
|
60
|
-
const
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
return { notesWanted, noteRows, noteCursor, bodyH };
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
const bodyFillKind = (listPane, splitBody) => {
|
|
70
|
-
if (listPane) return 'files';
|
|
71
|
-
if (splitBody) return 'split';
|
|
72
|
-
return 'plain';
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
const ownerSpan = (owners, focus) => {
|
|
76
|
-
let first = -1;
|
|
77
|
-
let last = -1;
|
|
78
|
-
for (let i = 0; i < owners.length; i++) {
|
|
79
|
-
if (owners[i] !== focus) continue;
|
|
80
|
-
if (first < 0) first = i;
|
|
81
|
-
last = i;
|
|
82
|
-
}
|
|
83
|
-
return { first, last };
|
|
44
|
+
const panel = paintNotePanel(view, width, color, notesCap);
|
|
45
|
+
const rows = panel.rows.slice(0, notesCap);
|
|
46
|
+
const inside = panel.cursor && panel.cursor.row < rows.length;
|
|
47
|
+
const cursor = inside ? panel.cursor : null;
|
|
48
|
+
const bodyH = Math.max(1, height - 3 - rows.length);
|
|
49
|
+
return { panel, rows, cursor, bodyH };
|
|
84
50
|
};
|
|
85
51
|
|
|
86
52
|
const revealScroll = (owners, focus, height, prefer) => {
|
|
87
|
-
const
|
|
88
|
-
if (
|
|
53
|
+
const first = owners.indexOf(focus);
|
|
54
|
+
if (first < 0) return prefer;
|
|
55
|
+
const last = owners.lastIndexOf(focus);
|
|
89
56
|
const room = Math.max(1, height);
|
|
90
|
-
let start = prefer;
|
|
91
|
-
if (
|
|
92
|
-
|
|
93
|
-
const fits = span.last - span.first + 1 <= room;
|
|
94
|
-
start = fits ? span.last - room + 1 : span.first;
|
|
57
|
+
let start = Math.min(prefer, first);
|
|
58
|
+
if (last >= start + room) {
|
|
59
|
+
start = last - first + 1 <= room ? last - room + 1 : first;
|
|
95
60
|
}
|
|
96
|
-
|
|
97
|
-
if (start > maxStart) start = maxStart;
|
|
98
|
-
if (start < 0) start = 0;
|
|
99
|
-
return start;
|
|
61
|
+
return Math.max(0, Math.min(start, owners.length - room));
|
|
100
62
|
};
|
|
101
63
|
|
|
102
64
|
const windowY = (win, row) => {
|
|
@@ -123,20 +85,25 @@ const mapBodyCursor = (cursor, win) => {
|
|
|
123
85
|
return point;
|
|
124
86
|
};
|
|
125
87
|
|
|
126
|
-
const
|
|
88
|
+
const bodyFillKind = (listPane, splitBody) => {
|
|
89
|
+
if (listPane) return 'files';
|
|
90
|
+
return splitBody ? 'split' : 'plain';
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const windowBody = (view, painted, frame) => {
|
|
127
94
|
const { width, color, bodyH, headerLines } = frame;
|
|
128
|
-
const listPane =
|
|
129
|
-
const fillKind = bodyFillKind(listPane,
|
|
95
|
+
const listPane = isListPane(view.pane);
|
|
96
|
+
const fillKind = bodyFillKind(listPane, painted.splitBody);
|
|
130
97
|
const filler = paintBodyFill(width, color, fillKind);
|
|
131
|
-
const shift = !listPane &&
|
|
98
|
+
const shift = !listPane && painted.body.length > 0 ? 1 : 0;
|
|
132
99
|
const lead = (items, first) =>
|
|
133
100
|
shift && items.length ? [first, ...items] : items;
|
|
134
|
-
const body = lead(
|
|
135
|
-
const taskOwners = lead(
|
|
136
|
-
const taskChecks = lead(
|
|
101
|
+
const body = lead(painted.body, filler);
|
|
102
|
+
const taskOwners = lead(painted.taskOwners, null);
|
|
103
|
+
const taskChecks = lead(painted.taskChecks, false);
|
|
137
104
|
const scrollMax = Math.max(0, body.length - bodyH);
|
|
138
105
|
let scroll = listPane ? 0 : Math.min(view.scroll ?? 0, scrollMax);
|
|
139
|
-
if (view
|
|
106
|
+
if (isTaskView(view)) {
|
|
140
107
|
scroll = revealScroll(taskOwners, view.tasksFocus ?? 0, bodyH, scroll);
|
|
141
108
|
}
|
|
142
109
|
const slice = body.slice(scroll, scroll + bodyH);
|
|
@@ -151,85 +118,79 @@ const windowBody = (view, paintedBody, frame) => {
|
|
|
151
118
|
}
|
|
152
119
|
taskHits.push({ y, cursor: owner });
|
|
153
120
|
}
|
|
154
|
-
const cursor = mapBodyCursor(paintedBody.cursor, win);
|
|
155
|
-
const editHits = mapEditHits(paintedBody.editHits, win);
|
|
156
121
|
while (slice.length < bodyH) slice.push(filler);
|
|
157
|
-
const { listScroll, fileHits } = paintedBody;
|
|
158
122
|
return {
|
|
159
123
|
slice,
|
|
160
124
|
scroll,
|
|
161
125
|
scrollMax,
|
|
162
|
-
listScroll,
|
|
126
|
+
listScroll: painted.listScroll,
|
|
163
127
|
taskHits,
|
|
164
|
-
fileHits,
|
|
165
|
-
editHits,
|
|
166
|
-
cursor,
|
|
128
|
+
fileHits: painted.fileHits,
|
|
129
|
+
editHits: mapEditHits(painted.editHits, win),
|
|
130
|
+
cursor: mapBodyCursor(painted.cursor, win),
|
|
167
131
|
};
|
|
168
132
|
};
|
|
169
133
|
|
|
170
134
|
const notePointerHits = (notes, bodyH) => {
|
|
171
|
-
const {
|
|
135
|
+
const { panel, rows, cursor } = notes;
|
|
172
136
|
const top = 2 + bodyH;
|
|
173
|
-
const x = noteCursor ? noteCursor.x : 0;
|
|
174
|
-
const y = noteCursor ? top + noteCursor.row : 0;
|
|
175
|
-
const cursor = noteCursor ? { x, y } : null;
|
|
176
137
|
const templateHits = [];
|
|
177
|
-
for (const hit of
|
|
178
|
-
if (hit.row
|
|
179
|
-
|
|
138
|
+
for (const hit of panel.templateHits) {
|
|
139
|
+
if (hit.row < rows.length) {
|
|
140
|
+
templateHits.push({ y: top + hit.row, cursor: hit.cursor });
|
|
141
|
+
}
|
|
180
142
|
}
|
|
181
143
|
const editHits = [];
|
|
182
|
-
for (const hit of
|
|
183
|
-
if (hit.row
|
|
184
|
-
editHits.push({ ...hit, y: top + hit.row });
|
|
144
|
+
for (const hit of panel.editHits) {
|
|
145
|
+
if (hit.row < rows.length) editHits.push({ ...hit, y: top + hit.row });
|
|
185
146
|
}
|
|
186
|
-
|
|
147
|
+
const point = cursor ? { x: cursor.x, y: top + cursor.row } : null;
|
|
148
|
+
return { cursor: point, templateHits, editHits };
|
|
187
149
|
};
|
|
188
150
|
|
|
189
151
|
const renderFrame = (view, options = {}) => {
|
|
190
152
|
const width = Math.max(20, options.width ?? 80);
|
|
191
153
|
const height = Math.max(6, options.height ?? 24);
|
|
192
154
|
const color = options.color ?? true;
|
|
193
|
-
const
|
|
155
|
+
const headerLines = 1;
|
|
194
156
|
const layout = footerLayout(view, width);
|
|
195
157
|
const notes = frameNotes(view, width, height, color);
|
|
196
|
-
const headerLines = 1;
|
|
197
158
|
const { bodyH } = notes;
|
|
198
|
-
const
|
|
159
|
+
const paint = bodyPainter(view);
|
|
160
|
+
const painted = paint(view, width, color, bodyH, headerLines);
|
|
199
161
|
const frame = { width, color, bodyH, headerLines };
|
|
200
|
-
const windowed = windowBody(view,
|
|
201
|
-
const rows =
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
162
|
+
const windowed = windowBody(view, painted, frame);
|
|
163
|
+
const rows = overlayRows(
|
|
164
|
+
[
|
|
165
|
+
paintHeader(view, width, color),
|
|
166
|
+
...windowed.slice,
|
|
167
|
+
...notes.rows,
|
|
168
|
+
paintStatusLine(view, view.status ?? '', width, color),
|
|
169
|
+
paintFooter(view, layout, width, color),
|
|
170
|
+
],
|
|
171
|
+
view.selection,
|
|
172
|
+
);
|
|
209
173
|
const hits = notePointerHits(notes, bodyH);
|
|
210
|
-
const statusY =
|
|
174
|
+
const statusY = rows.length - 1;
|
|
211
175
|
const branchHit = statusBranchHit(view, width, statusY);
|
|
212
176
|
const statusHits = branchHit ? [branchHit] : statusChoiceHits(view, statusY);
|
|
213
|
-
const { fileHits, taskHits, scroll, scrollMax, listScroll } = windowed;
|
|
214
|
-
const { templateHits } = hits;
|
|
215
177
|
const editHits = [...windowed.editHits, ...hits.editHits];
|
|
216
|
-
const shown = markEditorSelection(
|
|
178
|
+
const shown = markEditorSelection(rows, view, editHits, color);
|
|
217
179
|
const text = shown.map((row) => (color ? row + RESET : row)).join('\n');
|
|
218
|
-
const footerY = painted.length;
|
|
219
180
|
return {
|
|
220
181
|
text,
|
|
221
182
|
rows: shown,
|
|
222
183
|
buttons: footerHits(view, layout),
|
|
223
|
-
fileHits,
|
|
224
|
-
taskHits,
|
|
225
|
-
templateHits,
|
|
184
|
+
fileHits: windowed.fileHits,
|
|
185
|
+
taskHits: windowed.taskHits,
|
|
186
|
+
templateHits: hits.templateHits,
|
|
226
187
|
editHits,
|
|
227
188
|
statusHits,
|
|
228
|
-
footerY,
|
|
229
|
-
height:
|
|
230
|
-
scroll,
|
|
231
|
-
scrollMax,
|
|
232
|
-
listScroll,
|
|
189
|
+
footerY: rows.length,
|
|
190
|
+
height: rows.length,
|
|
191
|
+
scroll: windowed.scroll,
|
|
192
|
+
scrollMax: windowed.scrollMax,
|
|
193
|
+
listScroll: windowed.listScroll,
|
|
233
194
|
cursor: hits.cursor ?? windowed.cursor,
|
|
234
195
|
bodyH,
|
|
235
196
|
};
|