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.
Files changed (105) hide show
  1. package/CHANGELOG.md +12 -1
  2. package/LICENSE +1 -1
  3. package/README.md +11 -11
  4. package/bin/reslop.js +3 -8
  5. package/lib/ansi.js +32 -19
  6. package/lib/capabilities.js +8 -8
  7. package/lib/cli.js +40 -74
  8. package/lib/clipboard.js +39 -47
  9. package/lib/dashboard/git.js +128 -0
  10. package/lib/dashboard/model.js +332 -0
  11. package/lib/dashboard/npm.js +193 -0
  12. package/lib/dashboard/tree.js +278 -0
  13. package/lib/deps-diff.js +197 -402
  14. package/lib/deps.js +113 -240
  15. package/lib/detect.js +8 -18
  16. package/lib/diff/align.js +41 -113
  17. package/lib/diff/diff.js +12 -43
  18. package/lib/diff/display.js +25 -36
  19. package/lib/diff/edit.js +6 -20
  20. package/lib/diff/inline.js +76 -144
  21. package/lib/diff/items.js +33 -47
  22. package/lib/diff/parse.js +28 -56
  23. package/lib/diff/patch.js +12 -33
  24. package/lib/diff/unit.js +79 -178
  25. package/lib/editor.js +52 -69
  26. package/lib/files.js +71 -89
  27. package/lib/git-branches.js +254 -416
  28. package/lib/git-deps.js +147 -272
  29. package/lib/git-reword-todo.js +5 -13
  30. package/lib/git-worktree.js +141 -212
  31. package/lib/git.js +21 -49
  32. package/lib/github.js +67 -181
  33. package/lib/gitlab.js +40 -90
  34. package/lib/highlight/bash.js +55 -177
  35. package/lib/highlight/core.js +155 -141
  36. package/lib/highlight/css.js +25 -58
  37. package/lib/highlight/csv.js +19 -37
  38. package/lib/highlight/dart.js +72 -213
  39. package/lib/highlight/dot.js +14 -14
  40. package/lib/highlight/html.js +42 -63
  41. package/lib/highlight/js.js +177 -506
  42. package/lib/highlight/json.js +17 -37
  43. package/lib/highlight/log.js +19 -25
  44. package/lib/highlight/txt.js +7 -10
  45. package/lib/highlight.js +17 -46
  46. package/lib/keys.js +49 -82
  47. package/lib/manifest.js +67 -126
  48. package/lib/npm-commands.js +120 -169
  49. package/lib/remote.js +140 -190
  50. package/lib/render/branches.js +63 -61
  51. package/lib/render/chrome.js +220 -333
  52. package/lib/render/commits.js +160 -361
  53. package/lib/render/dash-activity.js +212 -0
  54. package/lib/render/dash-blocks.js +245 -0
  55. package/lib/render/dash-table.js +290 -0
  56. package/lib/render/dashboard.js +62 -0
  57. package/lib/render/diff.js +123 -258
  58. package/lib/render/files.js +40 -85
  59. package/lib/render/frame.js +78 -117
  60. package/lib/render/notes.js +139 -210
  61. package/lib/render/npm.js +213 -177
  62. package/lib/render/present.js +4 -11
  63. package/lib/render/primitives.js +72 -174
  64. package/lib/render/render.js +5 -14
  65. package/lib/render/report.js +43 -0
  66. package/lib/render/tiles.js +260 -0
  67. package/lib/render/unit.js +15 -31
  68. package/lib/report-model.js +110 -33
  69. package/lib/report-parse.js +56 -37
  70. package/lib/report-render.js +102 -59
  71. package/lib/report-run.js +87 -77
  72. package/lib/report-test.js +221 -99
  73. package/lib/report-tools.js +96 -154
  74. package/lib/review-merge.js +33 -41
  75. package/lib/review.js +68 -129
  76. package/lib/runs.js +225 -0
  77. package/lib/select.js +53 -91
  78. package/lib/session/accessors.js +14 -21
  79. package/lib/session/actions.js +61 -43
  80. package/lib/session/branches.js +166 -222
  81. package/lib/session/change-actions.js +49 -119
  82. package/lib/session/commit.js +274 -355
  83. package/lib/session/compose.js +556 -714
  84. package/lib/session/dashboard.js +385 -0
  85. package/lib/session/dispatch.js +162 -170
  86. package/lib/session/files-pane.js +76 -44
  87. package/lib/session/items.js +6 -17
  88. package/lib/session/lifecycle.js +393 -466
  89. package/lib/session/load.js +1 -14
  90. package/lib/session/navigation.js +2 -2
  91. package/lib/session/npm.js +403 -420
  92. package/lib/session/ops.js +5 -13
  93. package/lib/session/pointer.js +16 -14
  94. package/lib/session/progress.js +39 -30
  95. package/lib/session/review.js +1 -3
  96. package/lib/session/tasks.js +276 -235
  97. package/lib/session/templates.js +51 -59
  98. package/lib/session/terminal.js +85 -132
  99. package/lib/session/view.js +47 -81
  100. package/lib/session/watch.js +196 -212
  101. package/lib/session.js +81 -161
  102. package/lib/update.js +44 -111
  103. package/lib/utilities.js +46 -88
  104. package/lib/wrap.js +29 -55
  105. package/package.json +1 -1
@@ -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, truncateVisible, seq, EL } = ansi;
10
- const { fill, FILE_MARK, paintCursorList } = primitives;
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 { added, removed, staged, remaining } = entry;
39
- const add = signedCount('+', added);
40
- const del = signedCount('-', removed);
36
+ const add = `+${entry.added ?? 0}`;
37
+ const del = `-${entry.removed ?? 0}`;
41
38
  const status = entry.status ?? '';
42
- const n = `${staged ?? 0}`;
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 = (status, selected) => {
49
- const statusFg = FILE_STATUS_FG[status];
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
- if (selected) return THEME.chromeFg;
52
- return THEME.mutedFg;
46
+ return selected ? THEME.chromeFg : THEME.mutedFg;
53
47
  };
54
48
 
55
- const joinFileDate = (parts, cols) => {
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
- const addText = paint(add, THEME.addLineFg, bgRgb, color);
69
- const delText = paint(del, THEME.delLineFg, bgRgb, color);
70
- const statusText = paint(status, fgRgb, bgRgb, color);
71
- const nText = paint(n, fgRgb, bgRgb, color);
72
- const slash = paint('/', fgRgb, bgRgb, color);
73
- const mText = paint(m, fgRgb, bgRgb, color);
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
- if (isTasksEntry(entry)) {
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 mark = selected ? FILE_MARK : ' ';
113
- const prefix = ` ${mark} `;
62
+ const prefix = markPrefix(selected);
114
63
  const parts = padColumns(fileRowFields(entry), cols, FILE_COL_ALIGN);
115
- const suffix = `${joinFileMeta(parts)}${joinFileDate(parts, cols)}`;
116
- const pathW = Math.max(1, inner - visibleWidth(prefix + suffix));
117
- const { path, status } = entry;
118
- const clipped = truncateVisible(path, pathW);
119
- const pad = Math.max(0, pathW - visibleWidth(clipped));
120
- const left = `${prefix}${clipped}${' '.repeat(pad)}`;
121
- const fgRgb = fileRowFg(status, selected);
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}${suffix}${edge}`;
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 += paintFileSuffix(parts, cols, fgRgb, bgRgb, color);
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 files = view.files ?? [];
133
- const cols = measureColumns(files, fileRowFields, FILE_COL_ALIGN);
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(files, cursor, pane, paintRow);
93
+ return paintCursorList(entries, cursor, pane, paintRow);
139
94
  };
140
95
 
141
96
  module.exports = { paintBodyFiles };
@@ -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 { LIST_PANES, paintBodyFill, paneResult } = primitives;
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 bodyKind = (view) => {
29
- const pane = view.pane ?? 'diff';
30
- if (pane === 'files' || pane === 'branches' || pane === 'commits') {
31
- return pane;
32
- }
33
- if (pane === 'npm') return 'npm';
34
- if (pane === 'unit') return 'unit';
35
- if (!view.item) return 'empty';
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 BODY_PAINT = {
43
- files: paintBodyFiles,
44
- branches: paintBodyBranches,
45
- commits: paintBodyCommits,
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 notesWanted = paintNotePanel(view, width, color, notesCap);
61
- const noteRows = notesWanted.rows.slice(0, notesCap);
62
- let noteCursor = notesWanted.cursor;
63
- if (noteCursor && noteCursor.row >= noteRows.length) noteCursor = null;
64
- const chrome = 3 + noteRows.length;
65
- const bodyH = Math.max(1, height - chrome);
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 span = ownerSpan(owners, focus);
88
- if (span.first < 0) return prefer;
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 (span.first < start) start = span.first;
92
- if (span.last >= start + room) {
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
- const maxStart = Math.max(0, owners.length - room);
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 windowBody = (view, paintedBody, frame) => {
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 = LIST_PANES.includes(view.pane ?? 'diff');
129
- const fillKind = bodyFillKind(listPane, paintedBody.splitBody);
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 && paintedBody.body.length > 0 ? 1 : 0;
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(paintedBody.body, filler);
135
- const taskOwners = lead(paintedBody.taskOwners, null);
136
- const taskChecks = lead(paintedBody.taskChecks, false);
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.item && view.item.origin === 'task') {
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 { notesWanted, noteRows, noteCursor } = notes;
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 notesWanted.templateHits ?? []) {
178
- if (hit.row >= noteRows.length) continue;
179
- templateHits.push({ y: top + hit.row, cursor: hit.cursor });
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 notesWanted.editHits ?? []) {
183
- if (hit.row >= noteRows.length) continue;
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
- return { cursor, templateHits, editHits };
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 header = paintHeader(view, width, color);
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 paintedBody = paintFrameBody(view, width, color, bodyH, headerLines);
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, paintedBody, frame);
201
- const rows = [
202
- header,
203
- ...windowed.slice,
204
- ...notes.noteRows,
205
- paintStatusLine(view, view.status ?? '', width, color),
206
- paintFooter(view, layout, width, color),
207
- ];
208
- const painted = overlayRows(rows, view.selection);
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 = painted.length - 1;
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(painted, view, editHits, color);
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: footerY,
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
  };