reslop 0.2.7 → 0.3.1

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 (158) hide show
  1. package/CHANGELOG.md +30 -1
  2. package/README.md +16 -14
  3. package/bin/reslop.js +8 -5
  4. package/lib/agents/agents.js +573 -0
  5. package/lib/agents/allow.js +71 -0
  6. package/lib/agents/allow.json +17 -0
  7. package/lib/agents/models.js +467 -0
  8. package/lib/api.js +15 -12
  9. package/lib/cli.js +43 -68
  10. package/lib/{files.js → common/files.js} +48 -55
  11. package/lib/{find.js → common/find.js} +1 -1
  12. package/lib/common/format.js +101 -0
  13. package/lib/{utilities.js → common/process.js} +7 -95
  14. package/lib/common/tasks.json +27 -0
  15. package/lib/common/utilities.js +112 -0
  16. package/lib/dashboard/git.js +9 -16
  17. package/lib/dashboard/model.js +90 -49
  18. package/lib/dashboard/npm.js +17 -13
  19. package/lib/dashboard/tree.js +27 -30
  20. package/lib/{session → dashboard}/watch.js +36 -31
  21. package/lib/{workspace.js → dashboard/workspace.js} +23 -38
  22. package/lib/{deps.js → deps/deps.js} +31 -45
  23. package/lib/{deps-diff.js → deps/diff.js} +35 -25
  24. package/lib/{manifest.js → deps/manifest.js} +47 -30
  25. package/lib/diff/align.js +1 -2
  26. package/lib/diff/diff.js +2 -2
  27. package/lib/diff/display.js +29 -5
  28. package/lib/diff/inline.js +10 -14
  29. package/lib/diff/items.js +6 -4
  30. package/lib/diff/patch.js +39 -3
  31. package/lib/diff/unit.js +45 -16
  32. package/lib/{git-branches.js → git/branches.js} +53 -48
  33. package/lib/{git-deps.js → git/deps.js} +151 -95
  34. package/lib/git/git.js +71 -0
  35. package/lib/git/worktree.js +504 -0
  36. package/lib/highlight/bash.js +1 -2
  37. package/lib/highlight/csv.js +1 -2
  38. package/lib/highlight/dot.js +1 -2
  39. package/lib/{highlight.js → highlight/highlight.js} +42 -11
  40. package/lib/highlight/html.js +20 -18
  41. package/lib/highlight/json.js +1 -2
  42. package/lib/highlight/log.js +1 -2
  43. package/lib/highlight/txt.js +1 -2
  44. package/lib/{session → input}/actions.js +114 -48
  45. package/lib/{editor.js → input/editor.js} +16 -8
  46. package/lib/{keys.js → input/keys.js} +24 -45
  47. package/lib/input/line-edit.js +78 -0
  48. package/lib/input/menu-pick.js +70 -0
  49. package/lib/render/agents.js +642 -0
  50. package/lib/render/branches.js +4 -5
  51. package/lib/render/commits.js +54 -64
  52. package/lib/render/{dash-activity.js → dashboard/activity.js} +25 -40
  53. package/lib/render/{dash-blocks.js → dashboard/blocks.js} +131 -137
  54. package/lib/render/{dashboard.js → dashboard/dashboard.js} +25 -22
  55. package/lib/render/diff.js +166 -45
  56. package/lib/render/field.js +131 -0
  57. package/lib/render/files.js +9 -14
  58. package/lib/render/footer.js +385 -0
  59. package/lib/render/frame.js +123 -25
  60. package/lib/render/header.js +176 -0
  61. package/lib/render/log.js +320 -0
  62. package/lib/render/menu.js +60 -0
  63. package/lib/render/notes.js +16 -11
  64. package/lib/render/npm.js +322 -196
  65. package/lib/render/packages.js +9 -13
  66. package/lib/render/primitives.js +101 -15
  67. package/lib/render/render.js +7 -11
  68. package/lib/render/repos.js +13 -25
  69. package/lib/render/status.js +370 -0
  70. package/lib/render/status.json +85 -0
  71. package/lib/render/{dash-table.js → table.js} +13 -60
  72. package/lib/render/tiles.js +109 -48
  73. package/lib/render/unit.js +32 -13
  74. package/lib/{report-model.js → report/model.js} +4 -6
  75. package/lib/{report-parse.js → report/parse.js} +9 -14
  76. package/lib/{report-render.js → report/render.js} +48 -19
  77. package/lib/{report-run.js → report/run.js} +42 -30
  78. package/lib/{report-test.js → report/test.js} +56 -55
  79. package/lib/{report-tools.js → report/tools.js} +24 -33
  80. package/lib/review/agent-instructions.md +14 -0
  81. package/lib/review/format.js +420 -0
  82. package/lib/{review-merge.js → review/merge.js} +9 -8
  83. package/lib/review/review.js +190 -0
  84. package/lib/review/store.js +281 -0
  85. package/lib/review/templates.js +83 -0
  86. package/lib/runs/commands.js +14 -0
  87. package/lib/runs/logs.js +299 -0
  88. package/lib/runs/output.js +174 -0
  89. package/lib/runs/process.js +113 -0
  90. package/lib/{runs.js → runs/runs.js} +84 -36
  91. package/lib/runs/scripts.js +150 -0
  92. package/lib/session/agents/agents.js +976 -0
  93. package/lib/session/agents/job.js +320 -0
  94. package/lib/session/agents/jobs.js +355 -0
  95. package/lib/session/agents/options.js +153 -0
  96. package/lib/session/agents/plans.js +83 -0
  97. package/lib/session/branches.js +33 -40
  98. package/lib/session/change-actions.js +34 -100
  99. package/lib/session/commit.js +91 -52
  100. package/lib/session/compose.js +189 -147
  101. package/lib/session/dashboard.js +23 -22
  102. package/lib/session/dispatch.js +86 -21
  103. package/lib/session/files-pane.js +47 -26
  104. package/lib/session/find.js +16 -29
  105. package/lib/session/import.js +93 -0
  106. package/lib/session/items.js +8 -17
  107. package/lib/session/lifecycle.js +78 -72
  108. package/lib/session/load.js +2 -4
  109. package/lib/session/navigation.js +6 -3
  110. package/lib/session/npm/history.js +250 -0
  111. package/lib/session/npm/npm.js +836 -0
  112. package/lib/session/{packages.js → npm/packages.js} +21 -14
  113. package/lib/session/ops.js +125 -34
  114. package/lib/session/pointer.js +202 -10
  115. package/lib/session/prefs.js +112 -0
  116. package/lib/session/reads.js +109 -0
  117. package/lib/session/review.js +28 -10
  118. package/lib/session/run-panel.js +40 -0
  119. package/lib/{session.js → session/session.js} +158 -63
  120. package/lib/session/tasks.js +321 -66
  121. package/lib/session/templates.js +1 -1
  122. package/lib/session/terminal.js +94 -12
  123. package/lib/session/unit.js +35 -30
  124. package/lib/session/update.js +194 -6
  125. package/lib/session/view.js +65 -65
  126. package/lib/session/workspace.js +39 -78
  127. package/lib/{capabilities.js → source/capabilities.js} +3 -12
  128. package/lib/{github.js → source/github.js} +102 -32
  129. package/lib/{gitlab.js → source/gitlab.js} +87 -10
  130. package/lib/{remote.js → source/remote.js} +144 -40
  131. package/lib/source/review-threads.graphql +20 -0
  132. package/lib/source/source.js +74 -0
  133. package/lib/term/ansi.js +396 -0
  134. package/lib/{clipboard.js → term/clipboard.js} +3 -6
  135. package/lib/{select.js → term/select.js} +4 -3
  136. package/lib/term/themes/dark.json +122 -0
  137. package/lib/term/themes/light.json +122 -0
  138. package/lib/term/wide.txt +21 -0
  139. package/lib/{wrap.js → term/wrap.js} +4 -4
  140. package/package.json +2 -2
  141. package/lib/ansi.js +0 -545
  142. package/lib/detect.js +0 -34
  143. package/lib/diff/edit.js +0 -33
  144. package/lib/git-worktree.js +0 -455
  145. package/lib/git.js +0 -79
  146. package/lib/npm-commands.js +0 -522
  147. package/lib/render/chrome.js +0 -806
  148. package/lib/render/present.js +0 -19
  149. package/lib/render/report.js +0 -43
  150. package/lib/render/search.js +0 -25
  151. package/lib/review.js +0 -745
  152. package/lib/session/accessors.js +0 -26
  153. package/lib/session/edit-click.js +0 -57
  154. package/lib/session/npm.js +0 -452
  155. package/lib/session/progress.js +0 -41
  156. package/lib/source.js +0 -36
  157. package/lib/update.js +0 -192
  158. /package/lib/{git-reword-todo.js → git/reword-todo.js} +0 -0
@@ -1,21 +1,17 @@
1
1
  'use strict';
2
2
 
3
- const ansi = require('../ansi.js');
4
- const diff = require('../diff/diff.js');
5
- const highlight = require('../highlight.js');
6
- const detect = require('../detect.js');
7
- const files = require('../files.js');
8
- const wrap = require('../wrap.js');
3
+ const { clamp } = require('../common/utilities.js');
4
+ const ansi = require('../term/ansi.js');
5
+ const { displayLines } = require('../diff/diff.js');
6
+ const highlight = require('../highlight/highlight.js');
7
+ const { tokenize, overlayTokens, detectLang } = highlight;
8
+ const { itemPath } = require('../common/files.js');
9
+ const { wrapPlain } = require('../term/wrap.js');
9
10
  const primitives = require('./primitives.js');
10
11
 
11
- const { displayLines } = diff;
12
- const { detectLang } = detect;
13
- const { itemPath } = files;
14
- const { tokenize, overlayTokens } = highlight;
15
- const { wrapPlain } = wrap;
16
12
  const { THEME, CODE_FG, codeFg, paint, visibleWidth, truncateVisible } = ansi;
17
13
  const { GUTTER_W, fill, splitWidths, paintSplitRule } = primitives;
18
- const { paneResult, sliceVisible } = primitives;
14
+ const { paneResult, lazyRows, sliceVisible } = primitives;
19
15
 
20
16
  const lineStyle = (baseFg, baseBg, chBg = baseBg, mark = ' ') => ({
21
17
  baseFg,
@@ -54,9 +50,80 @@ const STAGED_STYLES = {
54
50
  ),
55
51
  };
56
52
 
57
- const gutterWidth = (width) => Math.min(GUTTER_W, Math.max(0, width));
53
+ const numberCols = (digits) => (digits > 0 ? digits + 1 : 0);
58
54
 
59
- const textWidth = (width) => Math.max(1, width - gutterWidth(width));
55
+ const paintNumberFill = (width, color, digits, split) => {
56
+ const half = (lineW) => {
57
+ const cols = clamp(lineW, 0, numberCols(digits));
58
+ const rest = Math.max(0, lineW - cols);
59
+ const gutter = fill(cols, THEME.lineNoFg, THEME.lineNoBg, color);
60
+ const code = fill(rest, THEME.ctxFg, THEME.ctxBg, color);
61
+ return gutter + code;
62
+ };
63
+ if (!split) return half(width);
64
+ const { leftW, rightW } = splitWidths(width);
65
+ const rule = paintSplitRule(color);
66
+ return half(leftW) + rule + half(rightW);
67
+ };
68
+
69
+ const gutterWidth = (width, digits = 0) => {
70
+ const want = GUTTER_W + numberCols(digits);
71
+ return Math.min(want, Math.max(0, width));
72
+ };
73
+
74
+ const textWidth = (width, digits = 0) =>
75
+ Math.max(1, width - gutterWidth(width, digits));
76
+
77
+ const digitsForMax = (max) => {
78
+ const n = Math.max(1, max || 1);
79
+ return Math.max(2, String(n).length);
80
+ };
81
+
82
+ const hunkDigits = (hunk) => {
83
+ if (!hunk) return digitsForMax(1);
84
+ const end = (start, count) => {
85
+ if (!start || start < 1) return 0;
86
+ return start + Math.max(count || 1, 1) - 1;
87
+ };
88
+ const oldEnd = end(hunk.oldStart, hunk.oldCount);
89
+ const newEnd = end(hunk.newStart, hunk.newCount);
90
+ return digitsForMax(Math.max(oldEnd, newEnd, 1));
91
+ };
92
+
93
+ const overlayDigits = (view, hunk, fromHunk) => {
94
+ const text = view.codeOverlay && view.codeOverlay.text;
95
+ if (typeof text !== 'string') return fromHunk;
96
+ const count = text ? text.split('\n').length : 1;
97
+ const start = hunk && hunk.newStart > 0 ? hunk.newStart : 1;
98
+ return Math.max(fromHunk, digitsForMax(start + count - 1));
99
+ };
100
+
101
+ const viewDigits = (view) => {
102
+ if (!view || view.lineNumbers !== true) return 0;
103
+ if (view.pane === 'unit') {
104
+ let max = 1;
105
+ for (const line of view.unitLines ?? []) {
106
+ max = Math.max(max, line.newNo || 0);
107
+ }
108
+ return digitsForMax(max);
109
+ }
110
+ const hunk = view.item && view.item.hunk;
111
+ return overlayDigits(view, hunk, hunkDigits(hunk));
112
+ };
113
+
114
+ const activeDigits = (ui) => {
115
+ if (!ui || ui.lineNumbers !== true) return 0;
116
+ if (typeof ui.view !== 'function') return 0;
117
+ return viewDigits(ui.view());
118
+ };
119
+
120
+ const lineNumber = (line, side) => {
121
+ if (!line) return 0;
122
+ if (side === 'left') return line.oldNo || 0;
123
+ if (side === 'right') return line.newNo || 0;
124
+ if (line.type === 'del') return line.oldNo || 0;
125
+ return line.newNo || 0;
126
+ };
60
127
 
61
128
  const clipPieces = (pieces, skip) => {
62
129
  if (skip <= 0) return pieces;
@@ -91,45 +158,79 @@ const paintSpans = (pieces, style, budget, color) => {
91
158
  return out + fill(budget - used, CODE_FG.plain, style.baseBg, color);
92
159
  };
93
160
 
94
- const paintAuditNote = (line, width, color) => {
161
+ const paintNumber = (line, digits, side, color) => {
162
+ if (!digits) return '';
163
+ const value = lineNumber(line, side);
164
+ const label = value ? String(value) : '';
165
+ const text = `${label.padStart(digits, ' ')} `;
166
+ return paint(text, THEME.lineNoFg, THEME.lineNoBg, color);
167
+ };
168
+
169
+ const paintAuditNote = (line, width, color, digits = 0) => {
95
170
  const { baseFg, baseBg, mark } = STYLES.note;
96
- const gutterW = gutterWidth(width);
97
- const label = `${mark} `.slice(0, gutterW);
171
+ const gutterW = gutterWidth(width, digits);
172
+ const numbers = paintNumber(line, digits, '', color);
173
+ const markW = Math.max(0, gutterW - numberCols(digits));
174
+ const label = `${mark} `.slice(0, markW);
98
175
  const budget = Math.max(0, width - gutterW);
99
176
  const text = truncateVisible(line.text ?? '', budget);
100
- let out = paint(label, baseFg, baseBg, color);
177
+ let out = numbers + paint(label, baseFg, baseBg, color);
101
178
  out += paint(text, baseFg, baseBg, color);
102
179
  return out + fill(budget - visibleWidth(text), baseFg, baseBg, color);
103
180
  };
104
181
 
105
- const paintAuditRule = (width, color) => {
182
+ const paintAuditRule = (width, color, digits = 0) => {
106
183
  const { baseFg, baseBg } = STYLES.noteSep;
107
- return paint('─'.repeat(Math.max(0, width)), baseFg, baseBg, color);
184
+ const cols = clamp(width, 0, numberCols(digits));
185
+ const gutter = fill(cols, THEME.lineNoFg, THEME.lineNoBg, color);
186
+ const rule = '─'.repeat(Math.max(0, width - cols));
187
+ return gutter + paint(rule, baseFg, baseBg, color);
108
188
  };
109
189
 
110
- const paintDiffLine = (line, width, color, origin, lang, scrollCol = 0) => {
111
- if (line.type === 'noteSep') return paintAuditRule(width, color);
112
- if (line.type === 'note') return paintAuditNote(line, width, color);
190
+ const paintDiffLine = (line, width, color, origin, lang, col, places, edge) => {
191
+ const scrollCol = col ?? 0;
192
+ const digits = places ?? 0;
193
+ const side = edge ?? '';
194
+ if (line.type === 'noteSep') return paintAuditRule(width, color, digits);
195
+ if (line.type === 'note') return paintAuditNote(line, width, color, digits);
113
196
  const styles = origin === 'staged' ? STAGED_STYLES : STYLES;
114
197
  const style = styles[line.type] ?? styles.ctx;
115
198
  const diffSpans = line.spans ?? [{ text: line.text, changed: false }];
116
199
  const tokens = tokenize(lang, line.text ?? '');
117
200
  const pieces = clipPieces(overlayTokens(tokens, diffSpans), scrollCol);
118
- const gutterW = gutterWidth(width);
119
- const label = `${line.mark ?? style.mark} `.slice(0, gutterW);
201
+ const gutterW = gutterWidth(width, digits);
202
+ const numbers = paintNumber(line, digits, side, color);
203
+ const markW = Math.max(0, gutterW - numberCols(digits));
204
+ const label = `${line.mark ?? style.mark} `.slice(0, markW);
120
205
  const gutter = paint(label, CODE_FG.punct, style.baseBg, color);
121
206
  const budget = Math.max(0, width - gutterW);
122
- return gutter + paintSpans(pieces, style, budget, color);
207
+ return numbers + gutter + paintSpans(pieces, style, budget, color);
123
208
  };
124
209
 
125
- const paintSplitRow = (row, width, color, origin, lang, scrollCol) => {
210
+ const emptySide = () => ({
211
+ type: 'ctx',
212
+ text: '',
213
+ spans: [{ text: '', changed: false }],
214
+ });
215
+
216
+ const paintSplitRow = (row, width, color, origin, lang, scrollCol, digits) => {
126
217
  const { leftW, rightW } = splitWidths(width);
127
- const half = (line, halfW) => {
128
- if (!line) return fill(halfW, THEME.ctxFg, THEME.ctxBg, color);
129
- return paintDiffLine(line, halfW, color, origin, lang, scrollCol);
218
+ const half = (line, halfW, side) => {
219
+ const shown = line || (digits ? emptySide() : null);
220
+ if (!shown) return fill(halfW, THEME.ctxFg, THEME.ctxBg, color);
221
+ return paintDiffLine(
222
+ shown,
223
+ halfW,
224
+ color,
225
+ origin,
226
+ lang,
227
+ scrollCol,
228
+ digits,
229
+ side,
230
+ );
130
231
  };
131
- const left = half(row.left, leftW);
132
- const right = half(row.right, rightW);
232
+ const left = half(row.left, leftW, 'left');
233
+ const right = half(row.right, rightW, 'right');
133
234
  return `${left}${paintSplitRule(color)}${right}`;
134
235
  };
135
236
 
@@ -181,8 +282,8 @@ const cutSpans = (spans, count) => {
181
282
  return { taken, rest };
182
283
  };
183
284
 
184
- const wrapSoftLine = (line, width) => {
185
- const rows = wrapPlain(line.text ?? '', textWidth(width));
285
+ const wrapSoftLine = (line, width, digits = 0) => {
286
+ const rows = wrapPlain(line.text ?? '', textWidth(width, digits));
186
287
  const changed = line.wrap === true;
187
288
  let used = 0;
188
289
  let spans = line.spans ?? [{ text: line.text ?? '', changed }];
@@ -192,6 +293,10 @@ const wrapSoftLine = (line, width) => {
192
293
  spans = cut.rest;
193
294
  const nextSpans = cut.taken.length ? cut.taken : [{ text, changed }];
194
295
  const next = { ...line, text, spans: nextSpans, editLast: last };
296
+ if (index > 0) {
297
+ next.oldNo = 0;
298
+ next.newNo = 0;
299
+ }
195
300
  if (line.editStart !== undefined) {
196
301
  next.editStart = line.editStart + used;
197
302
  next.editEnd = last ? line.editEnd : next.editStart + text.length;
@@ -201,12 +306,13 @@ const wrapSoftLine = (line, width) => {
201
306
  });
202
307
  };
203
308
 
204
- const expandSoftRows = (lines, width, split, wrapView) => {
309
+ const expandSoftRows = (lines, width, split, wrapView, digits = 0) => {
205
310
  const { leftW, rightW } = splitWidths(width);
206
311
  const partsOf = (line, lineW) => {
207
312
  if (!wrapView || !line || line.type === 'noteSep') return [line];
208
- if (visibleWidth(line.text ?? '') <= textWidth(lineW)) return [line];
209
- return wrapSoftLine(line, lineW);
313
+ const fits = visibleWidth(line.text ?? '') <= textWidth(lineW, digits);
314
+ if (fits) return [line];
315
+ return wrapSoftLine(line, lineW, digits);
210
316
  };
211
317
  if (!split) return lines.flatMap((line) => partsOf(line, width));
212
318
  const out = [];
@@ -221,11 +327,11 @@ const expandSoftRows = (lines, width, split, wrapView) => {
221
327
  return out;
222
328
  };
223
329
 
224
- const codeHits = (rows, split, width, overlay, editing, scrollCol) => {
330
+ const codeHits = (rows, split, width, overlay, editing, scrollCol, digits) => {
225
331
  const { leftW, rightW } = splitWidths(width);
226
332
  const lineW = split ? rightW : width;
227
333
  const x0 = split ? leftW + 1 : 0;
228
- const textX = x0 + gutterWidth(lineW) + 1;
334
+ const textX = x0 + gutterWidth(lineW, digits) + 1;
229
335
  const at = overlay ? overlay.cursor : null;
230
336
  const hasCursor = at !== null && at !== undefined;
231
337
  const editHits = [];
@@ -256,8 +362,10 @@ const codeHits = (rows, split, width, overlay, editing, scrollCol) => {
256
362
  return { cursor, editHits };
257
363
  };
258
364
 
259
- const codeInnerWidth = (width, layout = 'unified') =>
260
- textWidth(layout === 'side' ? splitWidths(width).rightW : width);
365
+ const codeInnerWidth = (width, layout = 'unified', digits = 0) => {
366
+ const lineW = layout === 'side' ? splitWidths(width).rightW : width;
367
+ return textWidth(lineW, digits);
368
+ };
261
369
 
262
370
  const paintBodyDiff = (view, width, color) => {
263
371
  const { item, codeOverlay: overlay } = view;
@@ -267,18 +375,31 @@ const paintBodyDiff = (view, width, color) => {
267
375
  const editing = typeof overlay?.cursor === 'number';
268
376
  const scrollCol = (editing ? overlay.scrollCol : 0) ?? 0;
269
377
  const raw = dropLeadingEmpty(bodyLines(item, layout, overlay), splitBody);
270
- const lines = expandSoftRows(raw, width, splitBody, !editing);
378
+ const digits = viewDigits(view);
379
+ const lines = expandSoftRows(raw, width, splitBody, !editing, digits);
271
380
  const paintRow = splitBody ? paintSplitRow : paintDiffLine;
272
- const body = lines.map((line) =>
273
- paintRow(line, width, color, item.origin, lang, scrollCol),
381
+ const body = lazyRows(lines.length, (index) => {
382
+ const line = lines[index];
383
+ return paintRow(line, width, color, item.origin, lang, scrollCol, digits);
384
+ });
385
+ const hits = codeHits(
386
+ lines,
387
+ splitBody,
388
+ width,
389
+ overlay,
390
+ editing,
391
+ scrollCol,
392
+ digits,
274
393
  );
275
- const hits = codeHits(lines, splitBody, width, overlay, editing, scrollCol);
276
394
  return paneResult({ body, splitBody, ...hits });
277
395
  };
278
396
 
279
397
  module.exports = {
280
398
  paintDiffLine,
281
399
  codeInnerWidth,
400
+ activeDigits,
401
+ viewDigits,
402
+ paintNumberFill,
282
403
  paintBodyDiff,
283
404
  expandSoftRows,
284
405
  codeHits,
@@ -0,0 +1,131 @@
1
+ 'use strict';
2
+
3
+ const ansi = require('../term/ansi.js');
4
+
5
+ const { THEME, paint, graphemes, graphemeWidth, visibleWidth } = ansi;
6
+ const { fileMatches } = require('../common/find.js');
7
+
8
+ const pieceKind = (index, cursor, lo, hi) => {
9
+ if (index === cursor) return 'caret';
10
+ if (lo >= 0 && index >= lo && index < hi) return 'select';
11
+ return 'plain';
12
+ };
13
+
14
+ const pushPiece = (pieces, kind, ch) => {
15
+ const last = pieces[pieces.length - 1];
16
+ if (last && last.kind === kind) last.text += ch;
17
+ else pieces.push({ kind, text: ch });
18
+ };
19
+
20
+ const fieldPieces = (text, cursor, anchor, scroll, room) => {
21
+ const raw = `${text ?? ''}`;
22
+ const selected = typeof anchor === 'number' && anchor !== cursor;
23
+ const lo = selected ? Math.min(anchor, cursor) : -1;
24
+ const hi = selected ? Math.max(anchor, cursor) : -1;
25
+ const pieces = [];
26
+ let index = 0;
27
+ let col = 0;
28
+ for (const ch of graphemes(raw)) {
29
+ const width = graphemeWidth(ch, col);
30
+ const startCol = col;
31
+ const start = index;
32
+ index += ch.length;
33
+ col += width;
34
+ if (col <= scroll) continue;
35
+ if (startCol >= scroll + room) break;
36
+ const kind = pieceKind(start, cursor, lo, hi);
37
+ pushPiece(pieces, kind, ch);
38
+ }
39
+ const caretAt = visibleWidth(raw.slice(0, Math.min(cursor, raw.length)));
40
+ const atEnd = cursor >= raw.length;
41
+ const caretFits = caretAt >= scroll && caretAt < scroll + room;
42
+ if (atEnd && caretFits) pushPiece(pieces, 'caret', ' ');
43
+ return pieces;
44
+ };
45
+
46
+ const paintField = (text, editor, room, color, tones) => {
47
+ const cursor = editor ? editor.cursor : `${text ?? ''}`.length;
48
+ const anchor = editor ? editor.anchor : null;
49
+ const scroll = editor ? editor.scrollCol : 0;
50
+ const pieces = fieldPieces(text, cursor, anchor, scroll, room);
51
+ let used = 0;
52
+ let out = '';
53
+ for (const piece of pieces) {
54
+ used += visibleWidth(piece.text);
55
+ const tone = tones[piece.kind] ?? tones.plain;
56
+ const painted = paint(piece.text, tone.fg, tone.bg, color);
57
+ out += color ? painted : piece.text;
58
+ }
59
+ const pad = ' '.repeat(Math.max(0, room - used));
60
+ const plain = tones.plain;
61
+ if (!color) return out + pad;
62
+ return out + paint(pad, plain.fg, plain.bg, color);
63
+ };
64
+
65
+ const paintPlanFace = (view, face, fgRgb, bgRgb, color) => {
66
+ const editor = view.planEditor;
67
+ if (!editor || !view.planQuery) {
68
+ return paint(face, fgRgb, bgRgb, color, true);
69
+ }
70
+ const room = visibleWidth(face);
71
+ if (room) editor.reveal(room);
72
+ const tones = {
73
+ plain: { fg: THEME.searchFg, bg: THEME.searchBg },
74
+ select: { fg: THEME.chromeFg, bg: THEME.checkBg },
75
+ caret: { fg: THEME.searchOnFg, bg: THEME.searchOnBg },
76
+ };
77
+ return paintField(view.planQuery, editor, room, color, tones);
78
+ };
79
+
80
+ const fieldTones = (fgRgb, bgRgb) => ({
81
+ plain: { fg: fgRgb, bg: bgRgb },
82
+ select: { fg: THEME.searchFg, bg: THEME.searchBg },
83
+ caret: { fg: THEME.searchOnFg, bg: THEME.searchOnBg },
84
+ });
85
+
86
+ const paintPromptLine = (lead, leadFg, text, editor, width, color, fgRgb) => {
87
+ const room = Math.max(0, width - lead.length);
88
+ if (editor && room) editor.reveal(room);
89
+ const bgRgb = THEME.chromeBg;
90
+ const body = paintField(text, editor, room, color, fieldTones(fgRgb, bgRgb));
91
+ if (!color) return `${lead}${body}`;
92
+ return paint(lead, leadFg, bgRgb, color, true) + body;
93
+ };
94
+
95
+ const paintImportStatus = (view, width, color) => {
96
+ const state = view.import ?? {};
97
+ const query = state.url ?? '';
98
+ return paintPromptLine(
99
+ ' url ',
100
+ THEME.warnFg,
101
+ query,
102
+ state.editor,
103
+ width,
104
+ color,
105
+ THEME.chromeFg,
106
+ );
107
+ };
108
+
109
+ const paintFindStatus = (view, width, color) => {
110
+ const find = view.find ?? {};
111
+ const query = find.query ?? '';
112
+ const hits = fileMatches(view.files ?? [], query);
113
+ const miss = query.length > 0 && hits.length === 0;
114
+ const tone = miss ? THEME.errorFg : THEME.chromeFg;
115
+ return paintPromptLine(
116
+ ' /',
117
+ THEME.warnFg,
118
+ query,
119
+ find.editor,
120
+ width,
121
+ color,
122
+ tone,
123
+ );
124
+ };
125
+
126
+ module.exports = {
127
+ paintField,
128
+ paintPlanFace,
129
+ paintImportStatus,
130
+ paintFindStatus,
131
+ };
@@ -1,17 +1,14 @@
1
1
  'use strict';
2
2
 
3
- const ansi = require('../ansi.js');
4
- const files = require('../files.js');
3
+ const ansi = require('../term/ansi.js');
4
+ const { isTasksEntry } = require('../common/files.js');
5
+ const { shortAge } = require('../common/format.js');
5
6
  const primitives = require('./primitives.js');
6
7
 
7
- const { isTasksEntry, shortAge } = files;
8
8
  const { THEME, paint, visibleWidth, trimVisible, seq, EL } = ansi;
9
9
  const { fill, markPrefix, paintCursorList } = primitives;
10
- const { measureColumns, padColumns } = primitives;
11
- const searchPaint = require('./search.js');
12
- const { paintPathText } = searchPaint;
13
- const locate = require('../find.js');
14
- const { activeHit, spansFor } = locate;
10
+ const { measureColumns, padColumns, paintPathText } = primitives;
11
+ const { activeHit, spansFor } = require('../common/find.js');
15
12
 
16
13
  const FILE_STATUS_FG = {
17
14
  unstaged: THEME.delLineFg,
@@ -103,12 +100,10 @@ const paintBodyFiles = (view, width, color, bodyH, headerLines) => {
103
100
  const pane = { width, color, bodyH, headerLines, offset: view.listScroll };
104
101
  const hit = activeHit(entries, view.find);
105
102
  const query = view.find && view.find.query ? view.find.query : '';
106
- const paintRow = (entry, selected, index) =>
107
- paintFileRow(entry, width, color, selected, cols, {
108
- query,
109
- hit,
110
- index,
111
- });
103
+ const paintRow = (entry, selected, index) => {
104
+ const marks = { query, hit, index };
105
+ return paintFileRow(entry, width, color, selected, cols, marks);
106
+ };
112
107
  return paintCursorList(entries, cursor, pane, paintRow);
113
108
  };
114
109