reslop 0.2.7 → 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 +17 -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 +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} +123 -96
  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 +111 -48
  45. package/lib/{editor.js → input/editor.js} +7 -8
  46. package/lib/{keys.js → input/keys.js} +15 -43
  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/{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 +383 -0
  59. package/lib/render/frame.js +123 -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 +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} +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 +33 -39
  98. package/lib/session/change-actions.js +34 -100
  99. package/lib/session/commit.js +91 -52
  100. package/lib/session/compose.js +181 -145
  101. package/lib/session/dashboard.js +23 -22
  102. package/lib/session/dispatch.js +85 -21
  103. package/lib/session/files-pane.js +47 -26
  104. package/lib/session/find.js +16 -29
  105. package/lib/session/import.js +82 -0
  106. package/lib/session/items.js +8 -17
  107. package/lib/session/lifecycle.js +75 -72
  108. package/lib/session/load.js +2 -4
  109. package/lib/session/navigation.js +6 -3
  110. package/lib/session/npm/history.js +249 -0
  111. package/lib/session/npm/npm.js +815 -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} +156 -63
  120. package/lib/session/tasks.js +320 -65
  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} +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 -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,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)) => {
@@ -144,76 +128,92 @@ const commitRows = (ui) => {
144
128
  return rows;
145
129
  };
146
130
 
147
- const view = (ui) => {
148
- const { composer, nav, gitBranches, commits, npm, updater } = ui;
149
- const templates = composer.templates.shownTemplates();
150
- const change = ui.change;
151
- const sourceKind = (change && change.source) || '';
152
- const notes = noteCounts(ui.review.store);
131
+ const navView = (ui) => {
132
+ const { nav } = ui;
153
133
  return {
154
134
  item: ui.current(),
155
135
  index: nav.index,
156
136
  total: ui.collection.items.length,
157
137
  scroll: nav.scroll,
158
138
  listScroll: nav.listScroll,
159
- status: viewStatus(ui),
160
- progressFrame: ui.progressFrame,
161
- layout: ui.layout,
162
- counts: counts(ui, notes),
163
139
  selection: nav.selection,
164
140
  pane: nav.pane,
165
141
  fileScope: nav.fileScope,
166
- files: fileList(ui),
167
142
  fileCursor: nav.fileCursor,
168
143
  find: nav.find,
144
+ import: nav.import,
169
145
  reviewPath: nav.reviewPath,
170
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),
171
168
  unitLines: unitBodyLines(ui),
172
169
  branch: ui.branch,
173
- branches: gitBranches.branches,
174
- branchCursor: nav.branchCursor,
170
+ branches: ui.gitBranches.branches,
175
171
  commits: commitRows(ui),
176
- commitCursor: nav.commitCursor,
177
- commitView: commits.commitView,
178
- npmCommands: npm.commands,
179
- npmCursor: nav.npmCursor,
180
- npmView: npm.viewing,
181
- npmOutput: npm.output,
182
- npmFollow: npm.followEnd,
183
- npmScroll: npm.logScroll,
184
- npmEditName: npm.editName,
185
- npmEditField: npm.editField,
186
- npmDraftName: npm.draftName,
187
- npmDraftCommand: npm.draftCommand,
188
- npmRunning: npm.running,
189
- npmLogs: nav.pane === 'npm' ? npm.logLabel : '',
172
+ commitView: ui.commits.commitView,
173
+ quitWarning: ui.quitWarning || '',
190
174
  repoName: repoNameOf(ui),
191
175
  rev: ui.rev || '',
192
176
  revShort: ui.revShort || '',
193
177
  sourceLabel: ui.sourceLabel || '',
194
- sourceKind,
178
+ sourceKind: (change && change.source) || '',
195
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 {
196
189
  compose: composer.composeView(),
197
190
  noteText: composer.idleNoteText(),
198
- tasks: composer.tasks.taskTexts(),
199
- taskSections: composer.tasks.taskSections(),
200
- tasksFocus: composer.tasks.clampedTaskFocus(),
201
- taskEdit: composer.tasks.taskEditView(),
202
191
  codeOverlay: composer.codeOverlayView(),
203
192
  templates,
204
193
  templateIndex: composer.templates.clampedTemplateIndex(templates),
205
- updateFrom: updater.from,
206
- updateTo: updater.to,
207
- dropName:
208
- ui.packages.dropName ||
209
- gitBranches.dropName ||
210
- commits.dropName ||
211
- npm.dropName,
212
- dashboard: nav.pane === 'dashboard' ? ui.dashboard.view() : null,
213
- packages: nav.pane === 'packages' ? ui.dashboard.packagesView() : null,
214
- packagesCursor: nav.packagesCursor,
215
- repos: nav.pane === 'repos' ? ui.workspace.view() : null,
216
194
  };
217
195
  };
218
196
 
219
- 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 };