reslop 0.2.6 → 0.3.0

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 +27 -1
  2. package/README.md +16 -14
  3. package/bin/reslop.js +8 -5
  4. package/lib/agents/agents.js +504 -0
  5. package/lib/agents/allow.js +71 -0
  6. package/lib/agents/allow.json +16 -0
  7. package/lib/agents/models.js +460 -0
  8. package/lib/api.js +15 -12
  9. package/lib/cli.js +61 -66
  10. package/lib/{files.js → common/files.js} +51 -52
  11. package/lib/common/find.js +61 -0
  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 +117 -51
  18. package/lib/dashboard/npm.js +133 -32
  19. package/lib/dashboard/tree.js +27 -30
  20. package/lib/{session → dashboard}/watch.js +41 -31
  21. package/lib/dashboard/workspace.js +151 -0
  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} +123 -95
  34. package/lib/git/git.js +71 -0
  35. package/lib/git/worktree.js +475 -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 +240 -71
  45. package/lib/{editor.js → input/editor.js} +7 -8
  46. package/lib/{keys.js → input/keys.js} +36 -39
  47. package/lib/input/line-edit.js +77 -0
  48. package/lib/input/menu-pick.js +70 -0
  49. package/lib/render/agents.js +640 -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/dashboard/blocks.js +490 -0
  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 +22 -8
  58. package/lib/render/footer.js +383 -0
  59. package/lib/render/frame.js +127 -25
  60. package/lib/render/header.js +176 -0
  61. package/lib/render/log.js +319 -0
  62. package/lib/render/menu.js +60 -0
  63. package/lib/render/notes.js +29 -13
  64. package/lib/render/npm.js +322 -196
  65. package/lib/render/packages.js +89 -0
  66. package/lib/render/primitives.js +109 -16
  67. package/lib/render/render.js +7 -11
  68. package/lib/render/repos.js +214 -0
  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} +19 -61
  72. package/lib/render/tiles.js +203 -47
  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} +46 -14
  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 +9 -0
  81. package/lib/review/format.js +426 -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 +958 -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 +36 -44
  98. package/lib/session/change-actions.js +34 -100
  99. package/lib/session/commit.js +122 -56
  100. package/lib/session/compose.js +197 -146
  101. package/lib/session/dashboard.js +183 -37
  102. package/lib/session/dispatch.js +120 -22
  103. package/lib/session/files-pane.js +60 -24
  104. package/lib/session/find.js +81 -0
  105. package/lib/session/import.js +82 -0
  106. package/lib/session/items.js +8 -17
  107. package/lib/session/lifecycle.js +96 -66
  108. package/lib/session/load.js +2 -4
  109. package/lib/session/navigation.js +8 -3
  110. package/lib/session/npm/history.js +249 -0
  111. package/lib/session/npm/npm.js +815 -0
  112. package/lib/session/npm/packages.js +150 -0
  113. package/lib/session/ops.js +125 -34
  114. package/lib/session/pointer.js +216 -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} +166 -59
  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 +72 -59
  126. package/lib/session/workspace.js +416 -0
  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} +2 -2
  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 -505
  142. package/lib/detect.js +0 -34
  143. package/lib/diff/edit.js +0 -33
  144. package/lib/git-worktree.js +0 -437
  145. package/lib/git.js +0 -79
  146. package/lib/npm-commands.js +0 -522
  147. package/lib/render/chrome.js +0 -628
  148. package/lib/render/dash-blocks.js +0 -245
  149. package/lib/render/present.js +0 -19
  150. package/lib/render/report.js +0 -43
  151. package/lib/review.js +0 -744
  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,33 +1,23 @@
1
1
  'use strict';
2
2
 
3
- const files = require('../files.js');
4
- const { listPath, isTasksEntry } = files;
3
+ const { listPath, isTasksEntry } = require('../common/files.js');
5
4
  const diff = require('../diff/diff.js');
6
5
  const { unitLines, itemsForPath, blockLineIndex } = diff;
7
6
  const { hunkLineRange, sameHunk, hunkKey } = diff;
8
7
  const { fileOffset, splitEditor, mergeEditRows, fileLineIndex } = diff;
9
8
 
10
- const fileText = (ui, rel) => {
11
- if (!rel) return '';
12
- const repo = ui.repo;
13
- if (repo && typeof repo.fileText === 'function') {
14
- return repo.fileText(ui.top, rel, ui.rev);
15
- }
16
- return '';
17
- };
9
+ const fileText = (ui, rel) => ui.reads.textOf(rel);
18
10
 
19
11
  const pathItems = (ui, rel) => itemsForPath(ui.items ?? [], rel);
20
12
 
21
- const linesFor = (
22
- ui,
23
- rel,
24
- overlay = ui.composer.liveFileText(),
25
- base = null,
26
- ) => {
27
- const text = overlay === null ? fileText(ui, rel) : overlay;
13
+ const linesFor = (ui, rel, overlay, base) => {
14
+ const live = ui.composer.liveFileText();
15
+ const shown = overlay === undefined ? live : overlay;
16
+ const seed = base === undefined ? null : base;
17
+ const text = shown === null ? fileText(ui, rel) : shown;
28
18
  const group = pathItems(ui, rel);
29
- if (overlay !== null && base && base.length) {
30
- return mergeEditRows(base, splitEditor(text));
19
+ if (shown !== null && seed && seed.length) {
20
+ return mergeEditRows(seed, splitEditor(text));
31
21
  }
32
22
  return unitLines(text, group);
33
23
  };
@@ -149,8 +139,8 @@ const moveUnitBlock = (ui, step) => {
149
139
  nav.clearSelection();
150
140
  };
151
141
 
152
- const openUnitFile = (ui, entry) => {
153
- if (!entry || isTasksEntry(entry)) return;
142
+ const showFile = async (ui, entry) => {
143
+ await ui.reads.loadText(entry.path);
154
144
  const nav = ui.nav;
155
145
  nav.reviewPath = entry.path;
156
146
  nav.tasksOpen = false;
@@ -170,12 +160,28 @@ const openUnitFile = (ui, entry) => {
170
160
  ui.status = '';
171
161
  };
172
162
 
173
- const setFileScope = (ui, next) => {
174
- if (ui.nav.pane !== 'files') return;
175
- if (ui.nav.fileScope === next) return;
163
+ const openUnitFile = (ui, entry) => {
164
+ if (!entry || isTasksEntry(entry)) return;
165
+ ui.ops.hold(showFile(ui, entry));
166
+ };
167
+
168
+ const applyFileScope = (ui, next, quiet) => {
176
169
  ui.nav.fileScope = next;
177
170
  ui.nav.restoreFileCursor(ui.fileList());
178
- ui.status = next === 'file' ? 'file scope' : 'diff mode';
171
+ const label = next === 'file' ? 'file scope' : 'diff mode';
172
+ ui.status = quiet ? '' : label;
173
+ };
174
+
175
+ const listScope = async (ui, quiet) => {
176
+ await ui.reads.loadNames();
177
+ applyFileScope(ui, 'file', quiet);
178
+ };
179
+
180
+ const setFileScope = (ui, next, quiet = false) => {
181
+ if (ui.nav.pane !== 'files') return;
182
+ if (ui.nav.fileScope === next) return;
183
+ if (next === 'file') return void ui.ops.hold(listScope(ui, quiet));
184
+ applyFileScope(ui, next, quiet);
179
185
  };
180
186
 
181
187
  const editCursor = (ui) => {
@@ -189,11 +195,9 @@ const editCursor = (ui) => {
189
195
  const at = atBlock
190
196
  ? blockLineIndex(lines, item)
191
197
  : viewLine(ui.nav, lines.length);
192
- return {
193
- text,
194
- cursor: fileOffset(lines, at),
195
- rows: lines,
196
- };
198
+ const cursor = fileOffset(lines, at);
199
+ const rows = lines;
200
+ return { text, cursor, rows };
197
201
  };
198
202
 
199
203
  module.exports = {
@@ -201,6 +205,7 @@ module.exports = {
201
205
  linesFor,
202
206
  moveUnitBlock,
203
207
  openUnitFile,
208
+ showFile,
204
209
  setFileScope,
205
210
  editCursor,
206
211
  followFileCursor,
@@ -1,7 +1,184 @@
1
1
  'use strict';
2
2
 
3
- const update = require('../update.js');
4
- const { checkUpdate, installUpdate, markSkipped } = update;
3
+ const fs = require('node:fs');
4
+ const os = require('node:os');
5
+ const path = require('node:path');
6
+
7
+ const { isHashObject } = require('metautil');
8
+ const utilities = require('../common/utilities.js');
9
+ const {
10
+ INSTALL_MS,
11
+ npmBin,
12
+ npmOpts,
13
+ runProc,
14
+ } = require('../common/process.js');
15
+ const { readJson } = utilities;
16
+ const { parseVersion, cmpVersion } = utilities;
17
+ const { oneLine, asText } = utilities;
18
+
19
+ const PKG = require('../../package.json');
20
+
21
+ const PKG_NAME = PKG.name;
22
+ const PKG_VERSION = PKG.version;
23
+ const PKG_DIR = path.resolve(__dirname, '../..');
24
+ const CHECK_INTERVAL_MS = 12 * 60 * 60 * 1000;
25
+ const FETCH_MS = 8000;
26
+ const REGISTRY = 'https://registry.npmjs.org';
27
+
28
+ const RELEASE = /^\d+\.\d+\.\d+$/;
29
+ const NO_TARGETS = { compatible: '', major: '' };
30
+
31
+ const isNpmInstall = (pkgDir) =>
32
+ path.basename(path.dirname(pkgDir)) === 'node_modules';
33
+
34
+ const cacheFile = (env = process.env) => {
35
+ if (env.XDG_CACHE_HOME) {
36
+ return path.join(env.XDG_CACHE_HOME, 'reslop', 'update.json');
37
+ }
38
+ if (process.platform === 'win32') {
39
+ const local =
40
+ env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
41
+ return path.join(local, 'reslop', 'update.json');
42
+ }
43
+ return path.join(os.homedir(), '.cache', 'reslop', 'update.json');
44
+ };
45
+
46
+ const readCache = (file) => {
47
+ const data = readJson(file);
48
+ if (!isHashObject(data)) {
49
+ return { checkedAt: 0, compatible: '', major: '', skipped: '' };
50
+ }
51
+ const checkedAt = parseInt(data.checkedAt, 10) || 0;
52
+ const compatible = asText(data.compatible);
53
+ const latest = asText(data.latest);
54
+ const major = asText(data.major) || latest;
55
+ const handled = asText(data.handled);
56
+ const skipped = asText(data.skipped) || handled;
57
+ return { checkedAt, compatible, major, skipped };
58
+ };
59
+
60
+ const writeCache = (file, data) => {
61
+ const { checkedAt, compatible, major, skipped } = data;
62
+ const body = JSON.stringify({ checkedAt, compatible, major, skipped });
63
+ try {
64
+ fs.mkdirSync(path.dirname(file), { recursive: true });
65
+ fs.writeFileSync(file, `${body}\n`);
66
+ } catch {
67
+ // ignore cache write errors
68
+ }
69
+ };
70
+
71
+ const isFresh = (cache, now, interval = CHECK_INTERVAL_MS) => {
72
+ if (!cache.checkedAt || now < cache.checkedAt) return false;
73
+ return now - cache.checkedAt < interval;
74
+ };
75
+
76
+ const listVersions = (data) => {
77
+ if (!data || typeof data !== 'object') return [];
78
+ const names = [];
79
+ if (isHashObject(data.versions)) names.push(...Object.keys(data.versions));
80
+ const tags = data['dist-tags'];
81
+ if (tags && typeof tags.latest === 'string') names.push(tags.latest);
82
+ if (typeof data.version === 'string') names.push(data.version);
83
+ return [...new Set(names)].filter((name) => RELEASE.test(name));
84
+ };
85
+
86
+ const newer = (best, name) => {
87
+ if (!best) return name;
88
+ return cmpVersion(parseVersion(name), parseVersion(best)) > 0 ? name : best;
89
+ };
90
+
91
+ const pickTargets = (current, names) => {
92
+ const from = parseVersion(current);
93
+ if (!from) return NO_TARGETS;
94
+ let compatible = '';
95
+ let major = '';
96
+ for (const name of names) {
97
+ const ver = parseVersion(name);
98
+ if (cmpVersion(ver, from) <= 0) continue;
99
+ if (ver.major === from.major) compatible = newer(compatible, name);
100
+ else if (ver.major > from.major) major = newer(major, name);
101
+ }
102
+ return { compatible, major };
103
+ };
104
+
105
+ const planUpdate = (current, targets) => {
106
+ const from = parseVersion(current);
107
+ if (!from) return { action: 'none' };
108
+ const { compatible, major, skipped } = targets;
109
+ const compatVer = parseVersion(compatible);
110
+ if (compatVer && compatVer.major === from.major) {
111
+ if (cmpVersion(compatVer, from) > 0) {
112
+ return { action: 'install', current, latest: compatible };
113
+ }
114
+ }
115
+ const majorVer = parseVersion(major);
116
+ if (!majorVer || majorVer.major <= from.major) return { action: 'none' };
117
+ const skipVer = parseVersion(skipped);
118
+ if (skipVer && cmpVersion(majorVer, skipVer) <= 0) return { action: 'none' };
119
+ return { action: 'confirm', current, latest: major };
120
+ };
121
+
122
+ const isEnabled = (options) => {
123
+ if (typeof options.enabled === 'boolean') return options.enabled;
124
+ return isNpmInstall(options.pkgDir ?? PKG_DIR);
125
+ };
126
+
127
+ const fetchTargets = async (current, options) => {
128
+ const fetchImpl = options.fetch ?? globalThis.fetch;
129
+ if (typeof fetchImpl !== 'function') return NO_TARGETS;
130
+ const url = `${REGISTRY}/${encodeURIComponent(PKG_NAME)}`;
131
+ const headers = { accept: 'application/json' };
132
+ const signal = options.signal ?? AbortSignal.timeout(FETCH_MS);
133
+ const response = await fetchImpl(url, { headers, signal });
134
+ if (!response || !response.ok) return NO_TARGETS;
135
+ const data = await response.json();
136
+ return pickTargets(current, listVersions(data));
137
+ };
138
+
139
+ const installUpdate = async (version, options = {}) => {
140
+ if (typeof options.install === 'function') {
141
+ await options.install(version);
142
+ return;
143
+ }
144
+ const timeout = options.timeout ?? INSTALL_MS;
145
+ const args = ['install', '-g', `${PKG_NAME}@${version}`];
146
+ const result = await runProc(npmBin(), args, npmOpts({ timeout }));
147
+ if (result.error) throw result.error;
148
+ if (result.status !== 0) {
149
+ throw new Error(oneLine(result.stderr, 'npm install failed'));
150
+ }
151
+ };
152
+
153
+ const markSkipped = (version, options = {}) => {
154
+ const file = options.cacheFile ?? cacheFile(options.env);
155
+ writeCache(file, { ...readCache(file), skipped: version });
156
+ };
157
+
158
+ const checkUpdate = async (options = {}) => {
159
+ if (!isEnabled(options)) return { action: 'skip' };
160
+ const now = Date.now();
161
+ const current = options.current ?? PKG_VERSION;
162
+ const file = options.cacheFile ?? cacheFile(options.env);
163
+ const cache = readCache(file);
164
+ if (isFresh(cache, now, options.interval)) {
165
+ const plan = planUpdate(current, cache);
166
+ return plan.action === 'install' ? { action: 'none' } : plan;
167
+ }
168
+ let targets = NO_TARGETS;
169
+ try {
170
+ targets = await fetchTargets(current, options);
171
+ } catch {
172
+ // ignore registry errors
173
+ }
174
+ if (!targets.compatible && !targets.major) {
175
+ writeCache(file, { ...cache, checkedAt: now });
176
+ return { action: 'none' };
177
+ }
178
+ const next = { ...cache, checkedAt: now, ...targets };
179
+ writeCache(file, next);
180
+ return planUpdate(current, next);
181
+ };
5
182
 
6
183
  class UpdateCoordinator {
7
184
  constructor(ui) {
@@ -62,13 +239,13 @@ class UpdateCoordinator {
62
239
  }
63
240
 
64
241
  async check() {
65
- let plan;
242
+ let plan = null;
66
243
  try {
67
244
  plan = await checkUpdate(this.opts);
68
245
  } catch {
69
- return;
246
+ // update check failed
70
247
  }
71
- if (this.ui.done) return;
248
+ if (!plan || this.ui.done) return;
72
249
  if (plan.action === 'install') {
73
250
  this.installPromise = this.install(plan.latest);
74
251
  await this.installPromise;
@@ -89,4 +266,15 @@ class UpdateCoordinator {
89
266
  }
90
267
  }
91
268
 
92
- module.exports = { UpdateCoordinator };
269
+ module.exports = {
270
+ CHECK_INTERVAL_MS,
271
+ isNpmInstall,
272
+ cacheFile,
273
+ readCache,
274
+ writeCache,
275
+ isFresh,
276
+ planUpdate,
277
+ checkUpdate,
278
+ markSkipped,
279
+ UpdateCoordinator,
280
+ };
@@ -2,12 +2,9 @@
2
2
 
3
3
  const path = require('node:path');
4
4
 
5
- const files = require('../files.js');
6
- const commit = require('./commit.js');
7
- const { listedCommits } = commit;
8
- const { fileEntries, fileTotals } = files;
9
- const review = require('../review.js');
10
- const { noteCounts } = review;
5
+ const { fileEntries, fileTotals } = require('../common/files.js');
6
+ const { listedCommits } = require('./commit.js');
7
+ const { noteCounts } = require('../review/review.js');
11
8
  const diff = require('../diff/diff.js');
12
9
  const { unitLines, itemsForPath, splitEditor, mergeEditRows } = diff;
13
10
  const unit = require('./unit.js');
@@ -43,18 +40,6 @@ const blankEntry = (rel) => ({
43
40
  status: '',
44
41
  });
45
42
 
46
- const listedNames = (ui) => {
47
- const repo = ui.repo;
48
- if (!repo.listFiles) return [];
49
- try {
50
- return repo.listFiles(ui.top || ui.cwd, ui.paths ?? [], {
51
- commit: ui.rev,
52
- });
53
- } catch {
54
- return [];
55
- }
56
- };
57
-
58
43
  const mergeUnitFiles = (entries, names) => {
59
44
  const have = new Set();
60
45
  for (const entry of entries) have.add(entry.path);
@@ -74,9 +59,8 @@ const fileList = (ui) => {
74
59
  const from = sourceLabel ? 'pr' : 'commit';
75
60
  const to = sourceLabel || ui.revShort;
76
61
  const labeled = relabelStatus(entries, from, to);
77
- const names = listedNames(ui);
78
- if (ui.nav.fileScope === 'file') return mergeUnitFiles(labeled, names);
79
- return labeled;
62
+ if (ui.nav.fileScope !== 'file') return labeled;
63
+ return mergeUnitFiles(labeled, ui.reads.names);
80
64
  };
81
65
 
82
66
  const counts = (ui, notes = noteCounts(ui.review.store)) => {
@@ -87,6 +71,7 @@ const counts = (ui, notes = noteCounts(ui.review.store)) => {
87
71
  commit: 0,
88
72
  pr: 0,
89
73
  tasks: 0,
74
+ tasksDone: 0,
90
75
  feedback: 0,
91
76
  code: 0,
92
77
  };
@@ -96,11 +81,15 @@ const counts = (ui, notes = noteCounts(ui.review.store)) => {
96
81
  }
97
82
  next.feedback = notes.feedback;
98
83
  next.tasks = notes.tasks;
84
+ next.tasksDone = notes.tasksDone;
99
85
  next.code = notes.code;
100
86
  return next;
101
87
  };
102
88
 
103
- const viewStatus = (ui) => ui.status || ui.busy;
89
+ const viewStatus = (ui) => {
90
+ if (ui.status || ui.busy) return ui.status || ui.busy;
91
+ return '';
92
+ };
104
93
 
105
94
  const unitBodyLines = (ui) => {
106
95
  if (ui.nav.pane !== 'unit') return [];
@@ -139,68 +128,92 @@ const commitRows = (ui) => {
139
128
  return rows;
140
129
  };
141
130
 
142
- const view = (ui) => {
143
- const { composer, nav, gitBranches, commits, npm, updater } = ui;
144
- const templates = composer.templates.shownTemplates();
145
- const change = ui.change;
146
- const sourceKind = (change && change.source) || '';
147
- const notes = noteCounts(ui.review.store);
131
+ const navView = (ui) => {
132
+ const { nav } = ui;
148
133
  return {
149
134
  item: ui.current(),
150
135
  index: nav.index,
151
136
  total: ui.collection.items.length,
152
137
  scroll: nav.scroll,
153
138
  listScroll: nav.listScroll,
154
- status: viewStatus(ui),
155
- progressFrame: ui.progressFrame,
156
- layout: ui.layout,
157
- counts: counts(ui, notes),
158
139
  selection: nav.selection,
159
140
  pane: nav.pane,
160
141
  fileScope: nav.fileScope,
161
- files: fileList(ui),
162
142
  fileCursor: nav.fileCursor,
143
+ find: nav.find,
144
+ import: nav.import,
163
145
  reviewPath: nav.reviewPath,
164
146
  unitLine: nav.unitLine,
147
+ branchCursor: nav.branchCursor,
148
+ commitCursor: nav.commitCursor,
149
+ packagesCursor: nav.packagesCursor,
150
+ };
151
+ };
152
+
153
+ const dropNameOf = (ui) => {
154
+ const { packages, gitBranches, commits, npm } = ui;
155
+ const name = packages.dropName || gitBranches.dropName;
156
+ return name || commits.dropName || npm.dropName;
157
+ };
158
+
159
+ const repoView = (ui) => {
160
+ const { change, updater } = ui;
161
+ return {
162
+ status: viewStatus(ui),
163
+ progressFrame: ui.progressFrame,
164
+ layout: ui.layout,
165
+ lineNumbers: ui.lineNumbers === true,
166
+ counts: counts(ui, noteCounts(ui.review.store)),
167
+ files: fileList(ui),
165
168
  unitLines: unitBodyLines(ui),
166
169
  branch: ui.branch,
167
- branches: gitBranches.branches,
168
- branchCursor: nav.branchCursor,
170
+ branches: ui.gitBranches.branches,
169
171
  commits: commitRows(ui),
170
- commitCursor: nav.commitCursor,
171
- commitView: commits.commitView,
172
- npmCommands: npm.commands,
173
- npmCursor: nav.npmCursor,
174
- npmView: npm.viewing,
175
- npmOutput: npm.output,
176
- npmFollow: npm.followEnd,
177
- npmScroll: npm.logScroll,
178
- npmEditName: npm.editName,
179
- npmEditField: npm.editField,
180
- npmDraftName: npm.draftName,
181
- npmDraftCommand: npm.draftCommand,
182
- npmRunning: npm.running,
183
- npmLogs: nav.pane === 'npm' ? npm.logLabel : '',
172
+ commitView: ui.commits.commitView,
173
+ quitWarning: ui.quitWarning || '',
184
174
  repoName: repoNameOf(ui),
185
175
  rev: ui.rev || '',
186
176
  revShort: ui.revShort || '',
187
177
  sourceLabel: ui.sourceLabel || '',
188
- sourceKind,
178
+ sourceKind: (change && change.source) || '',
189
179
  mode: ui.mode,
180
+ updateFrom: updater.from,
181
+ updateTo: updater.to,
182
+ dropName: dropNameOf(ui),
183
+ };
184
+ };
185
+
186
+ const composeView = (composer) => {
187
+ const templates = composer.templates.shownTemplates();
188
+ return {
190
189
  compose: composer.composeView(),
191
190
  noteText: composer.idleNoteText(),
192
- tasks: composer.tasks.taskTexts(),
193
- taskSections: composer.tasks.taskSections(),
194
- tasksFocus: composer.tasks.clampedTaskFocus(),
195
- taskEdit: composer.tasks.taskEditView(),
196
191
  codeOverlay: composer.codeOverlayView(),
197
192
  templates,
198
193
  templateIndex: composer.templates.clampedTemplateIndex(templates),
199
- updateFrom: updater.from,
200
- updateTo: updater.to,
201
- dropName: gitBranches.dropName || commits.dropName || npm.dropName,
202
- dashboard: nav.pane === 'dashboard' ? ui.dashboard.view() : null,
203
194
  };
204
195
  };
205
196
 
206
- module.exports = { fileList, counts, viewStatus, view };
197
+ const screenView = (ui) => {
198
+ const { pane } = ui.nav;
199
+ return {
200
+ dashboard: pane === 'dashboard' ? ui.dashboard.view() : null,
201
+ packages: pane === 'packages' ? ui.dashboard.packagesView() : null,
202
+ repos: pane === 'repos' ? ui.workspace.view() : null,
203
+ };
204
+ };
205
+
206
+ const view = (ui) => {
207
+ const { pane } = ui.nav;
208
+ return {
209
+ ...navView(ui),
210
+ ...repoView(ui),
211
+ ...ui.npm.view(pane === 'npm'),
212
+ ...ui.agents.view(pane === 'agents'),
213
+ ...composeView(ui.composer),
214
+ ...ui.composer.tasks.view(pane === 'tasks'),
215
+ ...screenView(ui),
216
+ };
217
+ };
218
+
219
+ module.exports = { fileList, counts, unitBodyLines, view };