reslop 0.2.4 → 0.2.5

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 (67) hide show
  1. package/CHANGELOG.md +14 -1
  2. package/README.md +1 -2
  3. package/lib/capabilities.js +0 -4
  4. package/lib/clipboard.js +39 -1
  5. package/lib/deps-diff.js +0 -3
  6. package/lib/deps.js +144 -176
  7. package/lib/diff/inline.js +66 -1
  8. package/lib/diff/unit.js +11 -28
  9. package/lib/editor.js +69 -9
  10. package/lib/files.js +17 -16
  11. package/lib/git-deps.js +58 -95
  12. package/lib/github.js +7 -7
  13. package/lib/gitlab.js +3 -3
  14. package/lib/highlight/core.js +11 -0
  15. package/lib/highlight/dot.js +2 -12
  16. package/lib/highlight/log.js +4 -11
  17. package/lib/highlight/txt.js +2 -12
  18. package/lib/keys.js +22 -12
  19. package/lib/manifest.js +0 -4
  20. package/lib/npm-commands.js +1 -22
  21. package/lib/remote.js +1 -7
  22. package/lib/render/branches.js +4 -13
  23. package/lib/render/chrome.js +85 -31
  24. package/lib/render/commits.js +95 -207
  25. package/lib/render/diff.js +31 -1
  26. package/lib/render/files.js +7 -16
  27. package/lib/render/frame.js +95 -110
  28. package/lib/render/notes.js +71 -16
  29. package/lib/render/npm.js +104 -18
  30. package/lib/render/primitives.js +108 -45
  31. package/lib/render/unit.js +4 -1
  32. package/lib/report-parse.js +0 -1
  33. package/lib/report-run.js +0 -1
  34. package/lib/report-test.js +40 -47
  35. package/lib/report-tools.js +0 -7
  36. package/lib/review-merge.js +101 -131
  37. package/lib/review.js +64 -77
  38. package/lib/select.js +52 -0
  39. package/lib/session/actions.js +23 -22
  40. package/lib/session/branches.js +5 -3
  41. package/lib/session/change-actions.js +173 -146
  42. package/lib/session/commit.js +32 -13
  43. package/lib/session/compose.js +120 -77
  44. package/lib/session/dispatch.js +36 -21
  45. package/lib/session/edit-click.js +57 -0
  46. package/lib/session/files-pane.js +24 -24
  47. package/lib/session/items.js +69 -89
  48. package/lib/session/lifecycle.js +31 -96
  49. package/lib/session/load.js +51 -58
  50. package/lib/session/navigation.js +105 -96
  51. package/lib/session/npm.js +158 -77
  52. package/lib/session/ops.js +192 -51
  53. package/lib/session/pointer.js +11 -5
  54. package/lib/session/progress.js +9 -6
  55. package/lib/session/review.js +35 -54
  56. package/lib/session/tasks.js +292 -0
  57. package/lib/session/terminal.js +13 -17
  58. package/lib/session/unit.js +3 -4
  59. package/lib/session/update.js +76 -97
  60. package/lib/session/view.js +17 -14
  61. package/lib/session/watch.js +47 -27
  62. package/lib/session.js +34 -79
  63. package/lib/update.js +1 -5
  64. package/lib/utilities.js +21 -1
  65. package/lib/wrap.js +15 -0
  66. package/package.json +1 -1
  67. package/lib/session/todos.js +0 -293
package/lib/review.js CHANGED
@@ -4,29 +4,31 @@ const fs = require('node:fs');
4
4
  const path = require('node:path');
5
5
 
6
6
  const files = require('./files.js');
7
- const { TODO_FILE, REVIEW_DIR } = files;
7
+ const { TASKS_FILE, REVIEW_DIR } = files;
8
8
  const reviewMerge = require('./review-merge.js');
9
9
  const { captureBaseline, mergeReview } = reviewMerge;
10
+ const utilities = require('./utilities.js');
11
+ const { pad2, dateStamp } = utilities;
10
12
 
11
13
  const createStore = (reviewPath, templates = []) => ({
12
14
  reviewPath,
13
15
  dirty: false,
14
- nextTodoId: 1,
16
+ nextTaskId: 1,
15
17
  feedback: new Map(),
16
18
  code: new Map(),
17
- todos: [],
19
+ tasks: [],
18
20
  templates: [...templates],
19
21
  status: 'editing',
20
22
  baseline: null,
21
23
  });
22
24
 
23
25
  const noteCounts = (store) => {
24
- const counts = { feedback: 0, todo: 0, todoDone: 0, code: 0 };
26
+ const counts = { feedback: 0, tasks: 0, tasksDone: 0, code: 0 };
25
27
  if (!store) return counts;
26
- for (const todo of store.todos) {
27
- if (!todo.text.trim()) continue;
28
- counts.todo += 1;
29
- if (todo.done) counts.todoDone += 1;
28
+ for (const task of store.tasks) {
29
+ if (!task.text.trim()) continue;
30
+ counts.tasks += 1;
31
+ if (task.done) counts.tasksDone += 1;
30
32
  }
31
33
  for (const note of store.feedback.values()) {
32
34
  if (note.text.trim()) counts.feedback += 1;
@@ -37,21 +39,20 @@ const noteCounts = (store) => {
37
39
 
38
40
  const hasNotes = (store) => {
39
41
  const counts = noteCounts(store);
40
- return counts.feedback > 0 || counts.todo > 0 || counts.code > 0;
42
+ return counts.feedback > 0 || counts.tasks > 0 || counts.code > 0;
41
43
  };
42
44
 
43
- const setFeedback = (store, key, note) => {
44
- const text = note.text ?? '';
45
- const trimmed = text.trim();
46
- const prev = store.feedback.get(key);
47
- if (!trimmed) {
45
+ const putNote = (store, notes, key, note, clear) => {
46
+ const prev = notes.get(key);
47
+ if (clear) {
48
48
  if (!prev) return;
49
- store.feedback.delete(key);
49
+ notes.delete(key);
50
50
  store.dirty = true;
51
51
  return;
52
52
  }
53
+ const text = note.text ?? '';
53
54
  if (prev && prev.text === text) return;
54
- store.feedback.set(key, {
55
+ notes.set(key, {
55
56
  file: note.file,
56
57
  oldStart: note.oldStart ?? 0,
57
58
  newStart: note.newStart ?? 0,
@@ -64,42 +65,28 @@ const setFeedback = (store, key, note) => {
64
65
  store.dirty = true;
65
66
  };
66
67
 
68
+ const setFeedback = (store, key, note) => {
69
+ const clear = !(note.text ?? '').trim();
70
+ putNote(store, store.feedback, key, note, clear);
71
+ };
72
+
67
73
  const setCode = (store, key, note) => {
68
- const prev = store.code.get(key);
69
- if (note.clear) {
70
- if (!prev) return;
71
- store.code.delete(key);
72
- store.dirty = true;
73
- return;
74
- }
75
- const text = note.text ?? '';
76
- if (prev && prev.text === text) return;
77
- store.code.set(key, {
78
- file: note.file,
79
- oldStart: note.oldStart ?? 0,
80
- newStart: note.newStart ?? 0,
81
- blockId: note.blockId ?? 0,
82
- origin: note.origin ?? '',
83
- header: note.header ?? '',
84
- text,
85
- done: note.done ?? prev?.done ?? false,
86
- });
87
- store.dirty = true;
74
+ putNote(store, store.code, key, note, Boolean(note.clear));
88
75
  };
89
76
 
90
- const addTodo = (store, file, text = '') => {
91
- const id = store.nextTodoId;
92
- store.nextTodoId += 1;
77
+ const addTask = (store, file, text = '') => {
78
+ const id = store.nextTaskId;
79
+ store.nextTaskId += 1;
93
80
  const todo = { id, file, text, done: false };
94
- store.todos.push(todo);
81
+ store.tasks.push(todo);
95
82
  store.dirty = true;
96
83
  return todo;
97
84
  };
98
85
 
99
- const removeTodo = (store, id) => {
86
+ const removeTask = (store, id) => {
100
87
  const next = [];
101
88
  let found = false;
102
- for (const todo of store.todos) {
89
+ for (const todo of store.tasks) {
103
90
  if (todo.id === id) {
104
91
  found = true;
105
92
  continue;
@@ -107,28 +94,28 @@ const removeTodo = (store, id) => {
107
94
  next.push(todo);
108
95
  }
109
96
  if (!found) return false;
110
- store.todos = next;
97
+ store.tasks = next;
111
98
  store.dirty = true;
112
99
  return true;
113
100
  };
114
101
 
115
- const setTodoText = (store, id, text) => {
116
- const i = store.todos.findIndex((todo) => todo.id === id);
102
+ const setTaskText = (store, id, text) => {
103
+ const i = store.tasks.findIndex((todo) => todo.id === id);
117
104
  if (i < 0) return;
118
- const prev = store.todos[i];
105
+ const prev = store.tasks[i];
119
106
  const trimmed = text.trim();
120
- if (!trimmed) return void removeTodo(store, id);
107
+ if (!trimmed) return void removeTask(store, id);
121
108
  if (prev.text === text) return;
122
- store.todos[i] = { id: prev.id, file: prev.file, text, done: prev.done };
109
+ store.tasks[i] = { id: prev.id, file: prev.file, text, done: prev.done };
123
110
  store.dirty = true;
124
111
  };
125
112
 
126
- const setTodoDone = (store, id, done) => {
127
- const i = store.todos.findIndex((todo) => todo.id === id);
113
+ const setTaskDone = (store, id, done) => {
114
+ const i = store.tasks.findIndex((todo) => todo.id === id);
128
115
  if (i < 0) return;
129
- const prev = store.todos[i];
116
+ const prev = store.tasks[i];
130
117
  if (prev.done === done) return;
131
- store.todos[i] = { id: prev.id, file: prev.file, text: prev.text, done };
118
+ store.tasks[i] = { id: prev.id, file: prev.file, text: prev.text, done };
132
119
  store.dirty = true;
133
120
  };
134
121
 
@@ -164,9 +151,9 @@ const applyImportedNotes = (store, imported) => {
164
151
  done,
165
152
  });
166
153
  }
167
- const todos = imported.todos ?? [];
154
+ const todos = imported.tasks ?? imported.todos ?? [];
168
155
  for (const todo of todos) {
169
- const added = addTodo(store, todo.file || '', todo.text ?? '');
156
+ const added = addTask(store, todo.file || '', todo.text ?? '');
170
157
  if (todo.done) added.done = true;
171
158
  }
172
159
  };
@@ -233,6 +220,8 @@ const SECTION_LINE = {
233
220
  '### Todo': 'todo',
234
221
  '### Feedback': 'feedback',
235
222
  };
223
+ const BACKLOG_HEAD = 'Backlog';
224
+ const LEGACY_BACKLOG = 'TODOs';
236
225
 
237
226
  const AGENT_INSTRUCTIONS = [
238
227
  '- Execute reviews with `status` `ready, partial, editing`',
@@ -273,9 +262,9 @@ const parseFrontmatterStatus = (text) => {
273
262
  const isDoneMark = (mark) => mark === 'x' || mark === 'X';
274
263
 
275
264
  const pushTodo = (builder, file, text, done) => {
276
- const id = builder.nextTodoId;
277
- builder.nextTodoId += 1;
278
- builder.todos.push({ id, file, text, done });
265
+ const id = builder.nextTaskId;
266
+ builder.nextTaskId += 1;
267
+ builder.tasks.push({ id, file, text, done });
279
268
  };
280
269
 
281
270
  const checkboxLine = (text, done) => {
@@ -350,7 +339,7 @@ const parseFeedSuffix = (textItem) => {
350
339
  const fileGroup = (groups, file) => {
351
340
  let group = groups.get(file);
352
341
  if (!group) {
353
- group = { todos: [], feedback: [], code: [] };
342
+ group = { tasks: [], feedback: [], code: [] };
354
343
  groups.set(file, group);
355
344
  }
356
345
  return group;
@@ -358,9 +347,9 @@ const fileGroup = (groups, file) => {
358
347
 
359
348
  const groupReviewNotes = (notes) => {
360
349
  const groups = new Map();
361
- for (const todo of notes.todos) {
350
+ for (const todo of notes.tasks) {
362
351
  if (!todo.text.trim()) continue;
363
- fileGroup(groups, TODO_FILE).todos.push(todo);
352
+ fileGroup(groups, TASKS_FILE).tasks.push(todo);
364
353
  }
365
354
  for (const key of notes.feedback.keys()) {
366
355
  const note = notes.feedback.get(key);
@@ -411,12 +400,19 @@ const emitCodeItem = (lines, note) => {
411
400
  lines.push(fence);
412
401
  };
413
402
 
403
+ const sectionTitle = (file) => (file === TASKS_FILE ? BACKLOG_HEAD : file);
404
+
405
+ const backlogFile = (name) => {
406
+ if (name === BACKLOG_HEAD || name === LEGACY_BACKLOG) return TASKS_FILE;
407
+ return name;
408
+ };
409
+
414
410
  const emitReviewGroups = (lines, groups) => {
415
411
  for (const file of groups.keys()) {
416
412
  const group = groups.get(file);
417
- lines.push(`## ${file}`);
413
+ lines.push(`## ${sectionTitle(file)}`);
418
414
  lines.push('');
419
- for (const todo of group.todos) {
415
+ for (const todo of group.tasks) {
420
416
  lines.push(checkboxLine(todo.text, todo.done));
421
417
  }
422
418
  for (const item of group.feedback) {
@@ -462,9 +458,9 @@ const flushPendingFeedback = (builder, pending) => {
462
458
 
463
459
  const headingFile = (line) => {
464
460
  const quoted = FILE_QUOTE.exec(line);
465
- if (quoted) return quoted[1];
461
+ if (quoted) return backlogFile(quoted[1]);
466
462
  if (line.startsWith('## ') && line !== '## Agent instructions') {
467
- return line.slice(3);
463
+ return backlogFile(line.slice(3));
468
464
  }
469
465
  return null;
470
466
  };
@@ -646,15 +642,6 @@ const TEMPLATES_FILE = 'templates.json';
646
642
  const AUTOSAVE_MS = 3000;
647
643
  const REVIEW_NAME = /^(\d{4}-\d{2}-\d{2})-(\d+)\.md$/;
648
644
 
649
- const pad2 = (n) => `${n}`.padStart(2, '0');
650
-
651
- const dateStamp = (date) => {
652
- const y = date.getFullYear();
653
- const month = pad2(date.getMonth() + 1);
654
- const day = pad2(date.getDate());
655
- return `${y}-${month}-${day}`;
656
- };
657
-
658
645
  const allocateReviewPath = (dir, date, existingNames) => {
659
646
  const stamp = dateStamp(date);
660
647
  let max = -1;
@@ -802,10 +789,10 @@ module.exports = {
802
789
  rememberTemplate,
803
790
  setFeedback,
804
791
  setCode,
805
- addTodo,
806
- removeTodo,
807
- setTodoText,
808
- setTodoDone,
792
+ addTask,
793
+ removeTask,
794
+ setTaskText,
795
+ setTaskDone,
809
796
  checkLabel,
810
797
  feedbackKey,
811
798
  applyImportedNotes,
package/lib/select.js CHANGED
@@ -3,6 +3,7 @@
3
3
  const ansi = require('./ansi.js');
4
4
 
5
5
  const { ESC, RESET, THEME, seq, stripAnsi, graphemes, graphemeWidth } = ansi;
6
+ const { visibleWidth } = ansi;
6
7
 
7
8
  const CSI_FINAL = /[@-~]/;
8
9
 
@@ -177,6 +178,56 @@ const overlayRange = (line, fromCol, toCol) => {
177
178
  return out;
178
179
  };
179
180
 
181
+ const selectionOf = (source) => {
182
+ if (!source || typeof source.anchor !== 'number') return null;
183
+ const cursor = source.cursor ?? 0;
184
+ if (source.anchor === cursor) return null;
185
+ return { anchor: source.anchor, cursor };
186
+ };
187
+
188
+ const editorSelection = (view) => {
189
+ if (!view) return null;
190
+ if (view.compose) return selectionOf(view.compose);
191
+ return selectionOf(view.codeOverlay);
192
+ };
193
+
194
+ const spanCols = (hit, lo, hi) => {
195
+ const start = hit.start ?? 0;
196
+ const from = Math.max(lo, start);
197
+ const to = Math.min(hi, hit.limit ?? start);
198
+ if (to <= from) return null;
199
+ const text = hit.text ?? '';
200
+ const head = Math.max(0, from - start);
201
+ const tail = Math.max(0, to - start);
202
+ const c0 = visibleWidth(text.slice(0, head));
203
+ const c1 = visibleWidth(text.slice(0, tail));
204
+ const scroll = hit.scroll ?? 0;
205
+ const room = Math.max(0, (hit.x1 ?? hit.textX ?? 1) - (hit.textX ?? 1));
206
+ const vis0 = Math.max(c0, scroll);
207
+ const vis1 = Math.min(c1, scroll + room);
208
+ if (vis1 <= vis0) return null;
209
+ const origin = (hit.textX ?? 1) - 1;
210
+ return { from: origin + vis0 - scroll, to: origin + vis1 - scroll };
211
+ };
212
+
213
+ const markEditorSelection = (rows, view, hits, color) => {
214
+ if (!color || !hits) return rows;
215
+ const sel = editorSelection(view);
216
+ if (!sel) return rows;
217
+ const lo = Math.min(sel.anchor, sel.cursor);
218
+ const hi = Math.max(sel.anchor, sel.cursor);
219
+ const next = rows.slice();
220
+ for (const hit of hits) {
221
+ if (!hit.live || !hit.y) continue;
222
+ const range = spanCols(hit, lo, hi);
223
+ if (!range) continue;
224
+ const i = hit.y - 1;
225
+ if (i < 0 || i >= next.length) continue;
226
+ next[i] = overlayRange(next[i], range.from, range.to);
227
+ }
228
+ return next;
229
+ };
230
+
180
231
  const overlayRows = (rows, sel) => {
181
232
  if (!isRange(sel)) return rows;
182
233
  const { length } = rows;
@@ -193,4 +244,5 @@ module.exports = {
193
244
  extractText,
194
245
  overlayRange,
195
246
  overlayRows,
247
+ markEditorSelection,
196
248
  };
@@ -6,13 +6,14 @@ const ACTIONS = {
6
6
  add: ['a'],
7
7
  unstage: ['u'],
8
8
  revert: ['d'],
9
+ ignore: ['i'],
9
10
  commit: ['c'],
10
11
  prev: ['left', 'p', 'k'],
11
12
  next: ['right', 'n', 'j'],
12
13
  check: ['x', ' '],
13
14
  layout: ['m'],
14
15
  feedback: ['f'],
15
- todo: ['t'],
16
+ tasks: ['t'],
16
17
  code: ['e'],
17
18
  branch: ['b'],
18
19
  file: ['f'],
@@ -47,6 +48,7 @@ const NPM_ACTIONS = {
47
48
  npmDrop: ['d'],
48
49
  npmLogs: ['c'],
49
50
  npmStop: ['escape'],
51
+ npmVerbose: ['v'],
50
52
  npmRerun: ['r'],
51
53
  };
52
54
 
@@ -60,6 +62,7 @@ const ACTION_LABEL = {
60
62
  revert: 'drop',
61
63
  check: 'x',
62
64
  layout: 'mode',
65
+ tasks: 'tasks',
63
66
  view: 'view',
64
67
  code: 'edit',
65
68
  newBranch: 'new',
@@ -68,6 +71,7 @@ const ACTION_LABEL = {
68
71
  npmDrop: 'delete',
69
72
  npmLogs: 'cleanup',
70
73
  npmStop: 'esc',
74
+ npmVerbose: 'verbose',
71
75
  npmRerun: 're-run',
72
76
  quit: 'q',
73
77
  prev: '←',
@@ -82,6 +86,7 @@ const DIFF_DISABLED = [
82
86
  'push',
83
87
  'file',
84
88
  'diff',
89
+ 'ignore',
85
90
  ];
86
91
  const UNIT_DISABLED = [...DIFF_DISABLED, 'layout'];
87
92
  const FILES_DISABLED = ['layout', 'feedback', 'code'];
@@ -94,16 +99,17 @@ const FILES_HIDDEN = [
94
99
  'file',
95
100
  'diff',
96
101
  ];
97
- const FILES_GIT_DISABLED = ['add', 'unstage', 'revert'];
98
- const FILES_TODO_DISABLED = [...FILES_GIT_DISABLED, ...FILES_DISABLED];
99
- const TODO_DISABLED = [
102
+ const FILES_GIT_DISABLED = ['add', 'unstage', 'revert', 'ignore'];
103
+ const FILES_TASKS_DISABLED = [...FILES_GIT_DISABLED, ...FILES_DISABLED];
104
+ const TASKS_DISABLED = [
100
105
  'add',
101
106
  'unstage',
102
107
  'revert',
108
+ 'ignore',
103
109
  'commit',
104
110
  'layout',
105
111
  'feedback',
106
- 'todo',
112
+ 'tasks',
107
113
  'code',
108
114
  'branch',
109
115
  'file',
@@ -115,10 +121,11 @@ const BRANCHES_DISABLED = [
115
121
  'add',
116
122
  'unstage',
117
123
  'revert',
124
+ 'ignore',
118
125
  'commit',
119
126
  'layout',
120
127
  'feedback',
121
- 'todo',
128
+ 'tasks',
122
129
  'code',
123
130
  'check',
124
131
  'branch',
@@ -130,11 +137,11 @@ const BRANCHES_DISABLED = [
130
137
  const COMMITS_DISABLED = BRANCHES_DISABLED.filter((id) => id !== 'commit');
131
138
  const NPM_DISABLED = [...BRANCHES_DISABLED, 'pull', 'push'];
132
139
 
133
- const isTodosTarget = (item) =>
134
- !!(item && (item.kind === 'todos' || item.origin === 'todo'));
140
+ const isTasksTarget = (item) =>
141
+ !!(item && (item.kind === 'tasks' || item.origin === 'task'));
135
142
 
136
143
  const diffGitDisabled = (item) => {
137
- if (!item || isTodosTarget(item)) return [];
144
+ if (!item || isTasksTarget(item)) return [];
138
145
  if (item.origin === 'staged') return ['add'];
139
146
  return ['unstage'];
140
147
  };
@@ -171,8 +178,8 @@ const commitGitDisabled = (item) => {
171
178
  };
172
179
 
173
180
  const disabledActions = (pane, item) => {
174
- const onTodos = isTodosTarget(item);
175
- if (pane === 'files') return onTodos ? FILES_TODO_DISABLED : FILES_DISABLED;
181
+ const onTodos = isTasksTarget(item);
182
+ if (pane === 'files') return onTodos ? FILES_TASKS_DISABLED : FILES_DISABLED;
176
183
  if (pane === 'unit') return [...UNIT_DISABLED, ...diffGitDisabled(item)];
177
184
  if (pane === 'branches') {
178
185
  return [...BRANCHES_DISABLED, ...branchGitDisabled(item)];
@@ -181,7 +188,7 @@ const disabledActions = (pane, item) => {
181
188
  return [...COMMITS_DISABLED, ...commitWriteDisabled(item)];
182
189
  }
183
190
  if (pane === 'npm') return NPM_DISABLED;
184
- if (onTodos) return TODO_DISABLED;
191
+ if (onTodos) return TASKS_DISABLED;
185
192
  return [...DIFF_DISABLED, ...diffGitDisabled(item)];
186
193
  };
187
194
 
@@ -203,8 +210,8 @@ const SCROLL = {
203
210
  home: 'home',
204
211
  end: 'end',
205
212
  enter: 'open',
206
- backspace: 'removeTodo',
207
- delete: 'removeTodo',
213
+ backspace: 'removeTask',
214
+ delete: 'removeTask',
208
215
  };
209
216
 
210
217
  const keysForActions = (actions) => {
@@ -358,28 +365,22 @@ module.exports = {
358
365
  ACTION_IDS,
359
366
  BRANCH_ACTIONS,
360
367
  COMMIT_ACTIONS,
361
- NPM_ACTIONS,
362
- ACTION_HOT,
363
- ACTION_LABEL,
364
368
  DIFF_DISABLED,
365
369
  UNIT_DISABLED,
366
370
  FILES_DISABLED,
367
371
  FILES_HIDDEN,
368
372
  FILES_GIT_DISABLED,
369
- FILES_TODO_DISABLED,
370
- TODO_DISABLED,
373
+ FILES_TASKS_DISABLED,
374
+ TASKS_DISABLED,
371
375
  BRANCHES_DISABLED,
372
376
  COMMITS_DISABLED,
373
377
  NPM_DISABLED,
374
- SCROLL,
375
- PANE_ONLY,
376
378
  disabledActions,
377
379
  diffGitDisabled,
378
380
  branchGitDisabled,
379
381
  commitGitDisabled,
380
382
  isFixupCommit,
381
383
  actionFromKey,
382
- actionKeys,
383
384
  actionHot,
384
385
  actionLetter,
385
386
  buttonWord,
@@ -47,10 +47,12 @@ const onBranch = (api) => {
47
47
  return;
48
48
  }
49
49
  const current = api.state.branches.findIndex((entry) => entry.current);
50
- api.ui.nav.branchCursor = current < 0 ? 0 : current;
51
- api.ui.nav.pane = 'branches';
50
+ const nav = api.ui.nav;
51
+ nav.branchCursor = current < 0 ? 0 : current;
52
+ nav.pane = 'branches';
53
+ nav.resetListScroll('branches');
52
54
  api.ui.status = '';
53
- api.ui.nav.clearSelection();
55
+ nav.clearSelection();
54
56
  };
55
57
 
56
58
  const onBranchMove = (api, delta) => {