reslop 0.2.5 → 0.2.7

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 (114) hide show
  1. package/CHANGELOG.md +22 -1
  2. package/LICENSE +1 -1
  3. package/README.md +11 -11
  4. package/bin/reslop.js +3 -8
  5. package/lib/ansi.js +72 -19
  6. package/lib/api.js +1 -1
  7. package/lib/capabilities.js +8 -8
  8. package/lib/cli.js +61 -75
  9. package/lib/clipboard.js +39 -47
  10. package/lib/dashboard/git.js +128 -0
  11. package/lib/dashboard/model.js +357 -0
  12. package/lib/dashboard/npm.js +290 -0
  13. package/lib/dashboard/tree.js +278 -0
  14. package/lib/deps-diff.js +197 -402
  15. package/lib/deps.js +113 -240
  16. package/lib/detect.js +8 -18
  17. package/lib/diff/align.js +41 -113
  18. package/lib/diff/diff.js +12 -43
  19. package/lib/diff/display.js +25 -36
  20. package/lib/diff/edit.js +6 -20
  21. package/lib/diff/inline.js +76 -144
  22. package/lib/diff/items.js +33 -47
  23. package/lib/diff/parse.js +28 -56
  24. package/lib/diff/patch.js +12 -33
  25. package/lib/diff/unit.js +79 -178
  26. package/lib/editor.js +52 -69
  27. package/lib/files.js +79 -91
  28. package/lib/find.js +61 -0
  29. package/lib/git-branches.js +254 -416
  30. package/lib/git-deps.js +148 -272
  31. package/lib/git-reword-todo.js +5 -13
  32. package/lib/git-worktree.js +161 -214
  33. package/lib/git.js +21 -49
  34. package/lib/github.js +67 -181
  35. package/lib/gitlab.js +40 -90
  36. package/lib/highlight/bash.js +55 -177
  37. package/lib/highlight/core.js +155 -141
  38. package/lib/highlight/css.js +25 -58
  39. package/lib/highlight/csv.js +19 -37
  40. package/lib/highlight/dart.js +72 -213
  41. package/lib/highlight/dot.js +14 -14
  42. package/lib/highlight/html.js +42 -63
  43. package/lib/highlight/js.js +177 -506
  44. package/lib/highlight/json.js +17 -37
  45. package/lib/highlight/log.js +19 -25
  46. package/lib/highlight/txt.js +7 -10
  47. package/lib/highlight.js +17 -46
  48. package/lib/keys.js +75 -83
  49. package/lib/manifest.js +67 -126
  50. package/lib/npm-commands.js +120 -169
  51. package/lib/remote.js +140 -190
  52. package/lib/render/branches.js +63 -61
  53. package/lib/render/chrome.js +412 -347
  54. package/lib/render/commits.js +160 -361
  55. package/lib/render/dash-activity.js +212 -0
  56. package/lib/render/dash-blocks.js +496 -0
  57. package/lib/render/dash-table.js +295 -0
  58. package/lib/render/dashboard.js +62 -0
  59. package/lib/render/diff.js +123 -258
  60. package/lib/render/files.js +60 -86
  61. package/lib/render/frame.js +82 -117
  62. package/lib/render/notes.js +150 -210
  63. package/lib/render/npm.js +213 -177
  64. package/lib/render/packages.js +93 -0
  65. package/lib/render/present.js +4 -11
  66. package/lib/render/primitives.js +77 -172
  67. package/lib/render/render.js +5 -14
  68. package/lib/render/report.js +43 -0
  69. package/lib/render/repos.js +226 -0
  70. package/lib/render/search.js +25 -0
  71. package/lib/render/tiles.js +355 -0
  72. package/lib/render/unit.js +15 -31
  73. package/lib/report-model.js +110 -33
  74. package/lib/report-parse.js +56 -37
  75. package/lib/report-render.js +102 -59
  76. package/lib/report-run.js +87 -77
  77. package/lib/report-test.js +221 -99
  78. package/lib/report-tools.js +96 -154
  79. package/lib/review-merge.js +33 -41
  80. package/lib/review.js +69 -129
  81. package/lib/runs.js +225 -0
  82. package/lib/select.js +53 -91
  83. package/lib/session/accessors.js +14 -21
  84. package/lib/session/actions.js +189 -65
  85. package/lib/session/branches.js +164 -222
  86. package/lib/session/change-actions.js +49 -119
  87. package/lib/session/commit.js +298 -352
  88. package/lib/session/compose.js +572 -715
  89. package/lib/session/dashboard.js +530 -0
  90. package/lib/session/dispatch.js +197 -171
  91. package/lib/session/files-pane.js +92 -45
  92. package/lib/session/find.js +94 -0
  93. package/lib/session/items.js +6 -17
  94. package/lib/session/lifecycle.js +420 -466
  95. package/lib/session/load.js +1 -14
  96. package/lib/session/navigation.js +4 -2
  97. package/lib/session/npm.js +403 -420
  98. package/lib/session/ops.js +5 -13
  99. package/lib/session/packages.js +143 -0
  100. package/lib/session/pointer.js +30 -14
  101. package/lib/session/progress.js +39 -30
  102. package/lib/session/review.js +1 -3
  103. package/lib/session/tasks.js +277 -236
  104. package/lib/session/templates.js +51 -59
  105. package/lib/session/terminal.js +85 -132
  106. package/lib/session/view.js +59 -80
  107. package/lib/session/watch.js +201 -212
  108. package/lib/session/workspace.js +455 -0
  109. package/lib/session.js +95 -161
  110. package/lib/update.js +44 -111
  111. package/lib/utilities.js +46 -88
  112. package/lib/workspace.js +166 -0
  113. package/lib/wrap.js +29 -55
  114. package/package.json +1 -1
@@ -1,200 +1,226 @@
1
1
  'use strict';
2
2
 
3
3
  const compose = require('./compose.js');
4
+ const { COMPOSE_LEAVE } = compose;
4
5
  const commit = require('./commit.js');
5
6
  const { hasStagedChanges, listedCommits } = commit;
6
- const { COMPOSE_LEAVE } = compose;
7
7
  const actions = require('./actions.js');
8
- const {
9
- LIST_PANES,
10
- CONFIRM_MODE,
11
- disabledActions,
12
- actionFromKey,
13
- applyConfirm,
14
- } = actions;
8
+ const { LIST_PANES, DASH_BLOCKS, DASH_ALLOWED, CONFIRM_MODE } = actions;
9
+ const { REPOS_ALLOWED } = actions;
10
+ const { disabledActions, actionFromKey, confirmKey, lineHasDiff } = actions;
11
+ const changeActions = require('./change-actions.js');
12
+ const { onCommand } = changeActions;
13
+ const filesPane = require('./files-pane.js');
14
+ const pointer = require('./pointer.js');
15
+ const { handlePointer } = pointer;
16
+ const finder = require('./find.js');
17
+ const { FIND_PANES, startFind, onFindKey } = finder;
15
18
 
16
19
  const currentTarget = (ui) => {
17
20
  if (ui.pane === 'files') return ui.fileCursorEntry();
18
- if (ui.pane === 'branches') {
19
- return ui.branches[ui.branchCursor];
20
- }
21
+ if (ui.pane === 'branches') return ui.branches[ui.branchCursor];
21
22
  if (ui.pane === 'commits') {
22
23
  const entry = listedCommits(ui.commits.commits)[ui.commitCursor];
23
- const staged = hasStagedChanges(ui);
24
+ const canCommit = hasStagedChanges(ui);
24
25
  const unstagedCurrent = !ui.rev && !ui.revShort;
25
- return { ...entry, canCommit: staged, unstagedCurrent };
26
+ return { ...entry, canCommit, unstagedCurrent };
26
27
  }
27
- return ui.current();
28
+ if (ui.pane !== 'unit') return ui.current();
29
+ const item = ui.current();
30
+ const lines = ui.view().unitLines ?? [];
31
+ if (!lines.length) return item;
32
+ const line = lines[ui.nav.unitLine] ?? null;
33
+ const lineDiff = lineHasDiff(line);
34
+ if (!item) return { lineDiff };
35
+ return { ...item, lineDiff };
28
36
  };
29
37
 
30
- const createDispatcher = (ui) => {
31
- const handlers = {
32
- add: ui.changeActions.onAdd,
33
- unstage: ui.changeActions.onUnstage,
34
- revert: ui.changeActions.onRevert,
35
- ignore: ui.changeActions.onIgnore,
36
- next: ui.filesPane.onNext,
37
- prev: ui.filesPane.onPrev,
38
- layout: ui.filesPane.onLayout,
39
- theme: ui.filesPane.onTheme,
40
- files: ui.filesPane.onFiles,
41
- feedback: ui.composer.onFeedback,
42
- tasks: ui.composer.onTasks,
43
- code: ui.composer.onCode,
44
- reload: ui.lifecycle.onReload,
45
- commit: ui.commits.onCommit,
46
- amend: ui.commits.onAmend,
47
- reword: ui.commits.onReword,
48
- apply: ui.commits.onApply,
49
- fixup: ui.commits.onFixup,
50
- view: () => ui.commits.onView(),
51
- branch: ui.gitBranches.onBranch,
52
- pull: ui.gitBranches.onPull,
53
- push: ui.gitBranches.onPush,
54
- newBranch: ui.gitBranches.onNewBranch,
55
- rebase: ui.gitBranches.onRebaseBranch,
56
- drop: () => {
57
- if (ui.pane === 'commits') {
58
- return void ui.commits.onDropCommit();
59
- }
60
- ui.gitBranches.onDropBranch();
61
- },
62
- open: ui.filesPane.onOpenFile,
63
- file: ui.filesPane.onFileScope,
64
- diff: ui.filesPane.onDiffScope,
65
- npm: ui.npm.open,
66
- npmEdit: ui.npm.edit,
67
- npmNew: ui.npm.create,
68
- npmDrop: () => ui.npm.askDrop(),
69
- npmLogs: () => ui.npm.askLogs(),
70
- npmStop: () => ui.npm.closeView(),
71
- back: () => ui.filesPane.onEscape(),
72
- npmVerbose: () => ui.npm.toggleVerbose(),
73
- npmRerun: () => ui.npm.rerun(),
74
- check: () => ui.composer.toggleFocusedTask(),
75
- removeTask: () => {
76
- if (ui.pane === 'npm') return void ui.npm.askDrop();
77
- ui.composer.removeFocusedTask();
78
- },
79
- quit: () => ui.onQuit(),
80
- scrollUp: () => ui.filesPane.onScroll(-1),
81
- scrollDown: () => ui.filesPane.onScroll(1),
82
- pageUp: () => ui.filesPane.onScroll(ui.filesPane.scrollStep(-1)),
83
- pageDown: () => ui.filesPane.onScroll(ui.filesPane.scrollStep(1)),
84
- halfUp: () => ui.filesPane.onScroll(ui.filesPane.scrollStep(-0.5)),
85
- halfDown: () => ui.filesPane.onScroll(ui.filesPane.scrollStep(0.5)),
86
- home: ui.filesPane.onHome,
87
- end: ui.filesPane.onEnd,
88
- };
38
+ const scrollBy = (fraction) => (ui) =>
39
+ filesPane.onScroll(ui, filesPane.scrollStep(ui, fraction));
89
40
 
90
- const dispatch = (action) => {
91
- if (CONFIRM_MODE.includes(ui.mode)) return;
92
- if (ui.mode === 'compose') {
93
- if (!COMPOSE_LEAVE.includes(action)) return;
94
- const result = ui.composer.saveCompose();
95
- if (result && result.kind === 'error') return;
96
- }
97
- const pane = ui.pane;
98
- if (LIST_PANES.includes(pane) && (action === 'next' || action === 'prev')) {
99
- ui.filesPane.onScroll(action === 'next' ? 1 : -1);
100
- return;
101
- }
102
- const hidden = disabledActions(pane, currentTarget(ui));
103
- if (hidden.includes(action)) return;
104
- const handler = handlers[action];
105
- if (!handler) return;
106
- handler();
107
- };
41
+ const DASH_HANDLERS = Object.fromEntries(
42
+ Object.entries(DASH_BLOCKS).map(([id, block]) => [
43
+ id,
44
+ (ui) => ui.dashboard.open(block),
45
+ ]),
46
+ );
108
47
 
109
- return { dispatch, handlers };
48
+ const HANDLERS = {
49
+ add: (ui) => onCommand(ui, 'add'),
50
+ unstage: (ui) => onCommand(ui, 'unstage'),
51
+ revert: (ui) => onCommand(ui, 'revert'),
52
+ ignore: (ui) => onCommand(ui, 'ignore'),
53
+ next: filesPane.onNext,
54
+ prev: filesPane.onPrev,
55
+ layout: filesPane.onLayout,
56
+ theme: filesPane.onTheme,
57
+ files: filesPane.onFiles,
58
+ open: (ui) => {
59
+ if (ui.pane === 'repos') return void ui.workspace.openSelected();
60
+ filesPane.onOpenFile(ui);
61
+ },
62
+ file: filesPane.onFileScope,
63
+ diff: filesPane.onDiffScope,
64
+ back: filesPane.onEscape,
65
+ home: filesPane.onHome,
66
+ end: filesPane.onEnd,
67
+ scrollUp: (ui) => filesPane.onScroll(ui, -1),
68
+ scrollDown: (ui) => filesPane.onScroll(ui, 1),
69
+ pageUp: scrollBy(-1),
70
+ pageDown: scrollBy(1),
71
+ halfUp: scrollBy(-0.5),
72
+ halfDown: scrollBy(0.5),
73
+ feedback: (ui) => ui.composer.onFeedback(),
74
+ code: (ui) => ui.composer.onCode(),
75
+ tasks: (ui) => ui.composer.tasks.onTasks(),
76
+ check: (ui) => ui.composer.tasks.toggleFocusedTask(),
77
+ removeTask: (ui) => {
78
+ if (ui.pane === 'npm') return void ui.npm.askDrop();
79
+ if (ui.pane === 'packages') return;
80
+ ui.composer.tasks.removeFocusedTask();
81
+ },
82
+ reload: (ui) => ui.lifecycle.onReload(),
83
+ commit: (ui) => ui.commits.onCommit(),
84
+ newCommit: (ui) => ui.commits.onInsert(),
85
+ amend: (ui) => ui.commits.onAmend(),
86
+ reword: (ui) => ui.commits.onReword(),
87
+ apply: (ui) => ui.commits.onApply(),
88
+ fixup: (ui) => ui.commits.onFixup(),
89
+ view: (ui) => ui.commits.onView(),
90
+ branch: (ui) => ui.gitBranches.onBranch(),
91
+ pull: (ui) => ui.gitBranches.onPull(),
92
+ push: (ui) => ui.gitBranches.onPush(),
93
+ newBranch: (ui) => ui.gitBranches.onNewBranch(),
94
+ rebase: (ui) => ui.gitBranches.onRebaseBranch(),
95
+ drop: (ui) => {
96
+ if (ui.pane === 'commits') return void ui.commits.onDropCommit();
97
+ if (ui.pane === 'tasks') return void ui.composer.tasks.removeFocusedTask();
98
+ ui.gitBranches.onDropBranch();
99
+ },
100
+ npm: (ui) => ui.npm.open(),
101
+ npmEdit: (ui) => ui.npm.edit(),
102
+ npmNew: (ui) => ui.npm.create(),
103
+ npmDrop: (ui) => ui.npm.askDrop(),
104
+ packageNew: (ui) => ui.packages.create(),
105
+ packageDrop: (ui) => ui.packages.askDrop(),
106
+ packageWanted: (ui) => ui.packages.updateWanted(),
107
+ packageLatest: (ui) => ui.packages.updateLatest(),
108
+ npmLogs: (ui) => ui.npm.askLogs(),
109
+ npmStop: (ui) => ui.npm.closeView(),
110
+ npmVerbose: (ui) => ui.npm.toggleVerbose(),
111
+ npmRerun: (ui) => ui.npm.rerun(),
112
+ quit: (ui) => ui.onQuit(),
113
+ repoLeft: (ui) => ui.workspace.move(-1, 0),
114
+ repoRight: (ui) => ui.workspace.move(1, 0),
115
+ repoUp: (ui) => ui.workspace.move(0, -1),
116
+ repoDown: (ui) => ui.workspace.move(0, 1),
117
+ ...DASH_HANDLERS,
110
118
  };
111
119
 
112
- const createInput = (ui) => {
113
- const confirmations = {
114
- confirmQuit: {
115
- ready: () => ui.finishQuit('ready'),
116
- editing: () => ui.finishQuit('editing'),
117
- cancel: () => ui.cancelQuit(),
118
- },
119
- confirmUpdate: {
120
- accept: () => ui.updater.accept(),
121
- decline: () => ui.updater.decline(),
122
- },
123
- confirmDrop: {
124
- confirm: () => {
125
- if (ui.npm.dropName) return void ui.npm.confirmDrop();
126
- if (ui.commits.dropName) {
127
- return void ui.commits.confirmDropCommit();
128
- }
129
- ui.gitBranches.confirmDropBranch();
130
- },
131
- cancel: () => {
132
- if (ui.npm.dropName) return void ui.npm.cancelDrop();
133
- if (ui.commits.dropName) {
134
- return void ui.commits.cancelDropCommit();
135
- }
136
- ui.gitBranches.cancelDropBranch();
137
- },
120
+ const dispatchAction = (ui, action) => {
121
+ if (CONFIRM_MODE.includes(ui.mode)) return;
122
+ if (ui.mode === 'compose') {
123
+ if (!COMPOSE_LEAVE.includes(action)) return;
124
+ const result = ui.composer.saveCompose();
125
+ if (result && result.kind === 'error') return;
126
+ }
127
+ const pane = ui.pane;
128
+ if (pane === 'repos' && !REPOS_ALLOWED.includes(action)) return;
129
+ if (pane === 'dashboard' && !DASH_ALLOWED.includes(action)) return;
130
+ if (LIST_PANES.includes(pane) && (action === 'next' || action === 'prev')) {
131
+ return void filesPane.onScroll(ui, action === 'next' ? 1 : -1);
132
+ }
133
+ if (disabledActions(pane, currentTarget(ui)).includes(action)) return;
134
+ const handler = HANDLERS[action];
135
+ if (handler) handler(ui);
136
+ };
137
+
138
+ const CONFIRMATIONS = {
139
+ confirmQuit: {
140
+ ready: (ui) => ui.finishQuit('ready'),
141
+ editing: (ui) => ui.finishQuit('editing'),
142
+ cancel: (ui) => ui.cancelQuit(),
143
+ },
144
+ confirmUpdate: {
145
+ accept: (ui) => ui.updater.accept(),
146
+ decline: (ui) => ui.updater.decline(),
147
+ },
148
+ confirmDrop: {
149
+ confirm: (ui) => {
150
+ if (ui.packages.dropName) return void ui.packages.confirmDrop();
151
+ if (ui.npm.dropName) return void ui.npm.confirmDrop();
152
+ if (ui.commits.dropName) return void ui.commits.confirmDropCommit();
153
+ ui.gitBranches.confirmDropBranch();
138
154
  },
139
- confirmPush: {
140
- force: ui.gitBranches.onForcePush,
141
- cancel: ui.gitBranches.cancelPush,
155
+ cancel: (ui) => {
156
+ if (ui.packages.dropName) return void ui.packages.cancelDrop();
157
+ if (ui.npm.dropName) return void ui.npm.cancelDrop();
158
+ if (ui.commits.dropName) return void ui.commits.cancelDropCommit();
159
+ ui.gitBranches.cancelDropBranch();
142
160
  },
143
- };
144
-
145
- const confirmChoice = (key) => {
146
- const mode = ui.mode;
147
- if (!CONFIRM_MODE.includes(mode)) return;
148
- applyConfirm(mode, key, confirmations[mode]);
149
- };
161
+ },
162
+ confirmPush: {
163
+ force: (ui) => ui.gitBranches.onForcePush(),
164
+ cancel: (ui) => ui.gitBranches.cancelPush(),
165
+ },
166
+ confirmCommit: {
167
+ commit: (ui) => ui.commits.chooseCommit('commit'),
168
+ amend: (ui) => ui.commits.chooseCommit('amend'),
169
+ fixup: (ui) => ui.commits.chooseCommit('fixup'),
170
+ cancel: (ui) => ui.commits.cancelCommitChoice(),
171
+ },
172
+ };
150
173
 
151
- const npmViewKeys = {
152
- escape: () => ui.npm.closeView(),
153
- r: () => ui.npm.rerun(),
154
- v: () => ui.npm.toggleVerbose(),
155
- e: () => {},
156
- n: () => {},
157
- };
174
+ const confirmChoice = (ui, key) => {
175
+ const handlers = CONFIRMATIONS[ui.mode];
176
+ const action = handlers ? confirmKey(ui.mode, key) : null;
177
+ if (action && handlers[action]) handlers[action](ui);
178
+ };
158
179
 
159
- const npmListKeys = {
160
- 'ctrl-up': () => ui.npm.reorder(-1),
161
- 'ctrl-down': () => ui.npm.reorder(1),
162
- up: () => ui.npm.move(-1),
163
- down: () => ui.npm.move(1),
164
- };
180
+ const NPM_VIEW_KEYS = {
181
+ escape: (ui) => ui.npm.closeView(),
182
+ r: (ui) => ui.npm.rerun(),
183
+ v: (ui) => ui.npm.toggleVerbose(),
184
+ e: () => {},
185
+ i: () => {},
186
+ };
165
187
 
166
- const npmKey = (key) => {
167
- if (ui.pane !== 'npm') return null;
168
- if (ui.npm.viewing && Object.hasOwn(npmViewKeys, key)) {
169
- return npmViewKeys[key];
170
- }
171
- return Object.hasOwn(npmListKeys, key) ? npmListKeys[key] : null;
172
- };
188
+ const NPM_LIST_KEYS = {
189
+ 'ctrl-up': (ui) => ui.npm.reorder(-1),
190
+ 'ctrl-down': (ui) => ui.npm.reorder(1),
191
+ up: (ui) => ui.npm.move(-1),
192
+ down: (ui) => ui.npm.move(1),
193
+ };
173
194
 
174
- const handleKey = (key) => {
175
- const mode = ui.mode;
176
- if (CONFIRM_MODE.includes(mode)) return void confirmChoice(key);
177
- if (mode === 'compose') {
178
- const command = ui.composer.handleKey(key);
179
- return void ui.runComposeCommand(command);
180
- }
181
- const onNpmKey = npmKey(key);
182
- if (onNpmKey) return void onNpmKey();
183
- if (key === 'escape') return void ui.filesPane.onEscape();
184
- const toggle = key === 'x' || key === ' ';
185
- if (toggle && ui.composer.toggleFocusedTask()) return;
186
- if (ui.composer.typeIntoTask(key)) return;
187
- const action = actionFromKey(key, ui.pane, ui.fileScope);
188
- if (action) ui.dispatch(action);
189
- };
195
+ const npmKey = (ui, key) => {
196
+ if (ui.pane !== 'npm') return null;
197
+ if (ui.npm.viewing && Object.hasOwn(NPM_VIEW_KEYS, key)) {
198
+ return NPM_VIEW_KEYS[key];
199
+ }
200
+ return Object.hasOwn(NPM_LIST_KEYS, key) ? NPM_LIST_KEYS[key] : null;
201
+ };
190
202
 
191
- const handleEvent = (event) => {
192
- if (event.type === 'key') return void handleKey(event.key);
193
- if (event.type !== 'mouse') return;
194
- ui.pointer.handle(event);
195
- };
203
+ const handleKey = (ui, key) => {
204
+ if (CONFIRM_MODE.includes(ui.mode)) return void confirmChoice(ui, key);
205
+ if (ui.mode === 'compose') {
206
+ return void ui.runComposeCommand(ui.composer.handleKey(key));
207
+ }
208
+ if (ui.mode === 'find') return void onFindKey(ui, key);
209
+ if (key === '/' && FIND_PANES.includes(ui.pane)) return void startFind(ui);
210
+ const onNpmKey = npmKey(ui, key);
211
+ if (onNpmKey) return void onNpmKey(ui);
212
+ if (key === 'escape') return void filesPane.onEscape(ui);
213
+ const tasks = ui.composer.tasks;
214
+ const toggle = key === ' ';
215
+ if (toggle && tasks.toggleFocusedTask()) return;
216
+ if (tasks.typeIntoTask(key)) return;
217
+ const action = actionFromKey(key, ui.pane, ui.fileScope);
218
+ if (action) ui.dispatch(action);
219
+ };
196
220
 
197
- return { handleEvent, confirmChoice };
221
+ const handleEvent = (ui, event) => {
222
+ if (event.type === 'key') return void handleKey(ui, event.key);
223
+ if (event.type === 'mouse') handlePointer(ui, event);
198
224
  };
199
225
 
200
- module.exports = { createDispatcher, createInput };
226
+ module.exports = { dispatchAction, confirmChoice, handleEvent };
@@ -13,16 +13,16 @@ const { setFileScope, openUnitFile, moveUnitBlock } = unit;
13
13
 
14
14
  const LAYOUTS = ['unified', 'mixed', 'side'];
15
15
 
16
- const current = (ui) => ui.nav.current(ui.items, ui.tasksItem);
16
+ const fileCursorEntry = (ui) => ui.fileList()[ui.nav.fileCursor] ?? null;
17
17
 
18
18
  const clampIndex = (ui) => ui.nav.clampIndex(ui.items.length);
19
19
 
20
- const syncReviewPath = (ui) => ui.nav.syncReviewPath(current(ui));
20
+ const syncReviewPath = (ui) => ui.nav.syncReviewPath(ui.current());
21
21
 
22
22
  const clampFileCursor = (ui) => ui.nav.clampFileCursor(ui.fileList().length);
23
23
 
24
24
  const followReviewPath = (ui) =>
25
- ui.nav.followReviewPath(ui.fileList(), current(ui));
25
+ ui.nav.followReviewPath(ui.fileList(), ui.current());
26
26
 
27
27
  const syncFileCursor = (ui) => {
28
28
  if (ui.nav.pane === 'files') return void clampFileCursor(ui);
@@ -35,7 +35,8 @@ const showFiles = (ui) => {
35
35
  nav.tasksOpen === true ||
36
36
  nav.pane === 'branches' ||
37
37
  nav.pane === 'commits' ||
38
- nav.pane === 'npm';
38
+ nav.pane === 'npm' ||
39
+ nav.pane === 'packages';
39
40
  nav.pane = 'files';
40
41
  nav.resetListScroll('files');
41
42
  nav.clearSelection();
@@ -44,7 +45,46 @@ const showFiles = (ui) => {
44
45
  if (held) ui.lifecycle.catchUpWatch();
45
46
  };
46
47
 
48
+ const viewingCommit = (ui) => !ui.change && Boolean(ui.rev || ui.revShort);
49
+
50
+ const releaseCommit = (ui) => {
51
+ if (!viewingCommit(ui)) return;
52
+ ui.rev = '';
53
+ ui.revShort = '';
54
+ ui.refreshFromRepo({ keepEmpty: true });
55
+ };
56
+
57
+ const showDashboard = (ui) => {
58
+ const nav = ui.nav;
59
+ const held = nav.pane !== 'dashboard';
60
+ nav.pane = 'dashboard';
61
+ nav.tasksOpen = false;
62
+ nav.scroll = 0;
63
+ nav.clearSelection();
64
+ ui.status = '';
65
+ releaseCommit(ui);
66
+ if (held) ui.lifecycle.catchUpWatch();
67
+ ui.dashboard.enter();
68
+ };
69
+
70
+ const HOME_CHILDREN = ['files', 'branches', 'commits', 'npm', 'packages'];
71
+
72
+ const returnsHome = (ui) => {
73
+ if (!ui.dashboardHome) return false;
74
+ const { pane, tasksOpen } = ui.nav;
75
+ return HOME_CHILDREN.includes(pane) || tasksOpen === true;
76
+ };
77
+
47
78
  const onEscape = (ui) => {
79
+ if (ui.nav.pane === 'repos') return void ui.onQuit();
80
+ if (ui.nav.pane === 'dashboard' && ui.workspaceRoot) {
81
+ return void ui.workspace.show();
82
+ }
83
+ if (ui.nav.pane === 'dashboard') return void ui.onQuit();
84
+ const commitDiff = ui.nav.pane === 'diff' && viewingCommit(ui);
85
+ if (returnsHome(ui) || (ui.dashboardHome && commitDiff)) {
86
+ return void showDashboard(ui);
87
+ }
48
88
  if (ui.nav.pane === 'files') return void ui.onQuit();
49
89
  showFiles(ui);
50
90
  };
@@ -60,27 +100,27 @@ const onFiles = (ui) => {
60
100
  }
61
101
  ui.nav.pane = 'diff';
62
102
  ui.nav.clearSelection();
63
- if (ui.nav.reviewPath === TASKS_FILE) ui.composer.openTasksPage();
64
103
  return;
65
104
  }
66
105
  showFiles(ui);
67
106
  };
68
107
 
69
108
  const onOpenFile = (ui) => {
109
+ if (ui.nav.pane === 'packages') return;
70
110
  if (ui.nav.pane === 'branches') {
71
111
  return void ui.gitBranches.onCheckoutBranch();
72
112
  }
73
113
  if (ui.nav.pane === 'commits') return void ui.commits.onShowDiff();
74
114
  if (ui.nav.pane === 'npm') return void ui.npm.run();
75
115
  if (ui.nav.pane !== 'files') {
76
- if (ui.nav.tasksOpen) ui.composer.editFocusedTask();
116
+ if (ui.nav.tasksOpen) ui.composer.tasks.editFocusedTask();
77
117
  return;
78
118
  }
79
119
  const list = ui.fileList();
80
120
  const entry = list[ui.nav.fileCursor];
81
121
  if (!entry) return;
82
122
  if (isTasksEntry(entry)) {
83
- ui.composer.openTasksPage();
123
+ ui.composer.tasks.openTasksPage();
84
124
  ui.status = '';
85
125
  return;
86
126
  }
@@ -134,21 +174,13 @@ const moveRemaining = (ui, step) => {
134
174
 
135
175
  const onNext = (ui) => {
136
176
  if (ui.nav.pane === 'unit') return void moveUnitBlock(ui, 1);
137
- if (ui.nav.tasksOpen) {
138
- ui.composer.closeTasksPage();
139
- if (ui.items.length) ui.nav.index = 0;
140
- ui.nav.scroll = 0;
141
- syncReviewPath(ui);
142
- syncFileCursor(ui);
143
- return;
144
- }
177
+ if (ui.nav.pane === 'tasks') return void ui.composer.tasks.moveTaskFocus(1);
145
178
  moveRemaining(ui, 1);
146
179
  };
147
180
 
148
181
  const onPrev = (ui) => {
149
182
  if (ui.nav.pane === 'unit') return void moveUnitBlock(ui, -1);
150
- if (ui.nav.tasksOpen) return;
151
- if (ui.nav.index <= 0) return void ui.composer.openTasksPage();
183
+ if (ui.nav.pane === 'tasks') return void ui.composer.tasks.moveTaskFocus(-1);
152
184
  moveRemaining(ui, -1);
153
185
  };
154
186
 
@@ -158,7 +190,7 @@ const scrollStep = (ui, fraction) => {
158
190
  const rows = size.height ?? 24;
159
191
  const fallback = Math.max(1, rows - 4);
160
192
  const bodyH = frame?.bodyH ?? fallback;
161
- const viewing = ui.nav.pane === 'npm' && ui.npm && ui.npm.viewing;
193
+ const viewing = ui.nav.pane === 'npm' && ui.npm.viewing;
162
194
  const viewRows = viewing ? logViewRows(bodyH) : bodyH;
163
195
  const height = Math.max(1, viewRows);
164
196
  const span = Math.floor(height * Math.abs(fraction));
@@ -168,6 +200,7 @@ const scrollStep = (ui, fraction) => {
168
200
  };
169
201
 
170
202
  const onScroll = (ui, delta) => {
203
+ if (ui.nav.pane === 'dashboard') return;
171
204
  if (ui.nav.pane === 'files') return void onFileMove(ui, delta);
172
205
  if (ui.nav.pane === 'branches') {
173
206
  return void ui.gitBranches.onBranchMove(delta);
@@ -176,8 +209,17 @@ const onScroll = (ui, delta) => {
176
209
  return void ui.commits.onCommitMove(delta);
177
210
  }
178
211
  if (ui.nav.pane === 'npm') return void ui.npm.move(delta);
179
- if (ui.nav.tasksOpen && ui.composer.taskRowCount() > 1) {
180
- return void ui.composer.moveTaskFocus(delta);
212
+ if (ui.nav.pane === 'packages') {
213
+ const count = ui.dashboard.packageCount();
214
+ const nav = ui.nav;
215
+ const next = nav.packagesCursor + delta;
216
+ const last = Math.max(0, count - 1);
217
+ nav.packagesCursor = Math.max(0, Math.min(last, next));
218
+ ui.status = '';
219
+ return;
220
+ }
221
+ if (ui.nav.tasksOpen && ui.composer.tasks.taskRowCount() > 1) {
222
+ return void ui.composer.tasks.moveTaskFocus(delta);
181
223
  }
182
224
  const frame = ui.lastFrame;
183
225
  const max = frame ? frame.scrollMax : null;
@@ -190,15 +232,16 @@ const onScroll = (ui, delta) => {
190
232
  };
191
233
 
192
234
  const onJump = (ui, toEnd) => {
235
+ if (ui.nav.pane === 'dashboard') return;
193
236
  if (LIST_PANES.includes(ui.nav.pane)) {
194
237
  const delta = toEnd ? Number.MAX_SAFE_INTEGER : -Number.MAX_SAFE_INTEGER;
195
238
  return void onScroll(ui, delta);
196
239
  }
197
- if (ui.nav.tasksOpen && ui.composer.taskRowCount() > 0) {
198
- const last = ui.composer.taskRowCount() - 1;
240
+ if (ui.nav.tasksOpen && ui.composer.tasks.taskRowCount() > 0) {
241
+ const last = ui.composer.tasks.taskRowCount() - 1;
199
242
  const target = toEnd ? last : 0;
200
- const at = ui.composer.clampedTaskFocus();
201
- return void ui.composer.moveTaskFocus(target - at);
243
+ const at = ui.composer.tasks.clampedTaskFocus();
244
+ return void ui.composer.tasks.moveTaskFocus(target - at);
202
245
  }
203
246
  const frame = ui.lastFrame;
204
247
  const max = frame ? frame.scrollMax : 0;
@@ -223,24 +266,28 @@ const onTheme = (ui) => {
223
266
  ui.status = next;
224
267
  };
225
268
 
226
- const createFilesPane = (ui) => ({
227
- clampIndex: () => clampIndex(ui),
228
- syncReviewPath: () => syncReviewPath(ui),
229
- syncFileCursor: () => syncFileCursor(ui),
230
- showFiles: () => showFiles(ui),
231
- onEscape: () => onEscape(ui),
232
- onFiles: () => onFiles(ui),
233
- onOpenFile: () => onOpenFile(ui),
234
- onNext: () => onNext(ui),
235
- onPrev: () => onPrev(ui),
236
- scrollStep: (fraction) => scrollStep(ui, fraction),
237
- onScroll: (delta) => onScroll(ui, delta),
238
- onHome: () => onJump(ui, false),
239
- onEnd: () => onJump(ui, true),
240
- onLayout: () => onLayout(ui),
241
- onTheme: () => onTheme(ui),
242
- onFileScope: () => setFileScope(ui, 'file'),
243
- onDiffScope: () => setFileScope(ui, 'diff'),
244
- });
245
-
246
- module.exports = { createFilesPane };
269
+ const onHome = (ui) => onJump(ui, false);
270
+ const onEnd = (ui) => onJump(ui, true);
271
+ const onFileScope = (ui) => setFileScope(ui, 'file');
272
+ const onDiffScope = (ui) => setFileScope(ui, 'diff');
273
+
274
+ module.exports = {
275
+ fileCursorEntry,
276
+ clampIndex,
277
+ syncReviewPath,
278
+ syncFileCursor,
279
+ showFiles,
280
+ onEscape,
281
+ onFiles,
282
+ onOpenFile,
283
+ onNext,
284
+ onPrev,
285
+ scrollStep,
286
+ onScroll,
287
+ onHome,
288
+ onEnd,
289
+ onLayout,
290
+ onTheme,
291
+ onFileScope,
292
+ onDiffScope,
293
+ };