reslop 0.1.8 → 0.1.9

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.
package/lib/git.js CHANGED
@@ -73,6 +73,7 @@ const createGitRepo = () =>
73
73
  hasStaged: branches.hasStaged,
74
74
  lastMessage: branches.lastMessage,
75
75
  listBranches: branches.listBranches,
76
+ listCommits: branches.listCommits,
76
77
  currentBranch: read.currentBranch,
77
78
  checkout: branches.checkoutBranch,
78
79
  checkoutAsync: branches.checkoutBranchAsync,
@@ -82,6 +83,8 @@ const createGitRepo = () =>
82
83
  rebaseAsync: branches.rebaseBranchAsync,
83
84
  drop: branches.dropBranch,
84
85
  dropAsync: branches.dropBranchAsync,
86
+ dropCommit: branches.dropCommit,
87
+ dropCommitAsync: branches.dropCommitAsync,
85
88
  pull: branches.pullChanges,
86
89
  pullAsync: branches.pullChangesAsync,
87
90
  push: branches.pushChanges,
@@ -104,6 +107,7 @@ module.exports = {
104
107
  currentBranch: read.currentBranch,
105
108
  lastMessage: branches.lastMessage,
106
109
  listBranches: branches.listBranches,
110
+ listCommits: branches.listCommits,
107
111
  checkoutBranch: branches.checkoutBranch,
108
112
  checkoutBranchAsync: branches.checkoutBranchAsync,
109
113
  createBranch: branches.createBranch,
@@ -112,6 +116,8 @@ module.exports = {
112
116
  rebaseBranchAsync: branches.rebaseBranchAsync,
113
117
  dropBranch: branches.dropBranch,
114
118
  dropBranchAsync: branches.dropBranchAsync,
119
+ dropCommit: branches.dropCommit,
120
+ dropCommitAsync: branches.dropCommitAsync,
115
121
  pullChanges: branches.pullChanges,
116
122
  pullChangesAsync: branches.pullChangesAsync,
117
123
  pushChanges: branches.pushChanges,
package/lib/keys.js CHANGED
@@ -2,10 +2,13 @@
2
2
 
3
3
  const actions = require('./session/actions.js');
4
4
 
5
- const { ACTIONS, ACTION_IDS, BRANCH_ACTIONS } = actions;
5
+ const { ACTIONS, ACTION_IDS, BRANCH_ACTIONS, COMMIT_ACTIONS } = actions;
6
6
  const { DIFF_DISABLED, FILES_DISABLED, FILES_GIT_DISABLED } = actions;
7
- const { FILES_TODO_DISABLED, TODO_DISABLED, BRANCHES_DISABLED } = actions;
7
+ const { FILES_HIDDEN, FILES_TODO_DISABLED, TODO_DISABLED, BRANCHES_DISABLED } =
8
+ actions;
9
+ const { COMMITS_DISABLED } = actions;
8
10
  const { disabledActions, diffGitDisabled, branchGitDisabled } = actions;
11
+ const { commitGitDisabled } = actions;
9
12
  const { actionFromKey, actionHot, actionLetter, buttonWord } = actions;
10
13
  const { CONFIRM, CONFIRM_MODE, confirmKey, applyConfirm } = actions;
11
14
  const { confirmPrefix, promptFromChoices } = actions;
@@ -176,15 +179,19 @@ module.exports = {
176
179
  ACTIONS,
177
180
  ACTION_IDS,
178
181
  BRANCH_ACTIONS,
182
+ COMMIT_ACTIONS,
179
183
  DIFF_DISABLED,
180
184
  FILES_DISABLED,
185
+ FILES_HIDDEN,
181
186
  FILES_GIT_DISABLED,
182
187
  FILES_TODO_DISABLED,
183
188
  TODO_DISABLED,
184
189
  BRANCHES_DISABLED,
190
+ COMMITS_DISABLED,
185
191
  disabledActions,
186
192
  diffGitDisabled,
187
193
  branchGitDisabled,
194
+ commitGitDisabled,
188
195
  decodeChunk,
189
196
  actionFromKey,
190
197
  actionHot,
@@ -14,7 +14,7 @@ const BRANCH_COL_ALIGN = {
14
14
  ahead: 'start',
15
15
  behind: 'start',
16
16
  gone: 'end',
17
- date: 'end',
17
+ date: 'start',
18
18
  };
19
19
 
20
20
  const BRANCH_COL_FG = {
@@ -25,14 +25,25 @@ const BRANCH_COL_FG = {
25
25
  date: THEME.mutedFg,
26
26
  };
27
27
 
28
+ const BRANCH_CURRENT_COL_FG = {
29
+ sha: THEME.shaDarkFg,
30
+ date: THEME.headerFg,
31
+ };
32
+
28
33
  const BRANCH_NAME_MAX = 24;
34
+ const BRANCH_EDGE = 2;
35
+ const BRANCH_TAIL = 1;
36
+ const BRANCH_GAP = ' ';
37
+ const BRANCH_DATE_GAP = ' ';
38
+
39
+ const colGap = (key) => (key === 'date' ? BRANCH_DATE_GAP : BRANCH_GAP);
29
40
 
30
41
  const branchNameChip = (entry, rowBg) => {
31
42
  if (entry.current) {
32
43
  return {
33
- nameFg: THEME.headerFg,
44
+ nameFg: THEME.mutedFg,
34
45
  wrapFg: THEME.mutedFg,
35
- nameBg: THEME.buttonHotFg,
46
+ nameBg: THEME.currentBg,
36
47
  nameBold: false,
37
48
  };
38
49
  }
@@ -97,26 +108,33 @@ const joinBranchSuffix = (parts, cols) => {
97
108
  const bits = [];
98
109
  for (const key of BRANCH_COL_KEYS) {
99
110
  if (!cols[key]) continue;
100
- bits.push(parts[key]);
111
+ bits.push(colGap(key) + parts[key]);
101
112
  }
102
113
  if (!bits.length) return '';
103
- return ` ${bits.join(' ')}`;
114
+ return bits.join('');
115
+ };
116
+
117
+ const branchColFg = (key, current) => {
118
+ if (current && Object.hasOwn(BRANCH_CURRENT_COL_FG, key)) {
119
+ return BRANCH_CURRENT_COL_FG[key];
120
+ }
121
+ return BRANCH_COL_FG[key];
104
122
  };
105
123
 
106
- const paintBranchSuffix = (parts, cols, fgRgb, bgRgb, color) => {
107
- const gap = paint(' ', fgRgb, bgRgb, color);
124
+ const paintBranchSuffix = (parts, cols, fgRgb, bgRgb, color, current) => {
108
125
  const bits = [];
109
126
  for (const key of BRANCH_COL_KEYS) {
110
127
  if (!cols[key]) continue;
111
- const tone = BRANCH_COL_FG[key] ?? fgRgb;
112
- bits.push(paint(parts[key], tone, bgRgb, color));
128
+ const tone = branchColFg(key, current) ?? fgRgb;
129
+ const gap = paint(colGap(key), fgRgb, bgRgb, color);
130
+ bits.push(gap + paint(parts[key], tone, bgRgb, color));
113
131
  }
114
132
  if (!bits.length) return '';
115
- return gap + bits.join(gap);
133
+ return bits.join('');
116
134
  };
117
135
 
118
136
  const paintBranchRow = (entry, width, color, selected, cols) => {
119
- const inner = Math.max(1, width - 1);
137
+ const inner = Math.max(1, width - BRANCH_EDGE - BRANCH_TAIL);
120
138
  const mark = selected ? FILE_MARK : ' ';
121
139
  const prefix = ` ${mark} `;
122
140
  const parts = branchRowParts(entry, cols);
@@ -136,8 +154,11 @@ const paintBranchRow = (entry, width, color, selected, cols) => {
136
154
  const fgRgb = branchRowFg(entry, selected);
137
155
  const bgRgb = selected ? THEME.buttonBg : THEME.ctxBg;
138
156
  const chip = branchNameChip(entry, bgRgb);
139
- const edge = fill(1, fgRgb, bgRgb, color);
140
- if (!color) return `${left}${suffix}${edge}`;
157
+ const markFg = entry.current ? THEME.headerFg : fgRgb;
158
+ const markBg = entry.current ? THEME.currentBg : bgRgb;
159
+ const tail = ' '.repeat(BRANCH_TAIL);
160
+ const edge = fill(BRANCH_EDGE, fgRgb, THEME.ctxBg, color);
161
+ if (!color) return `${left}${suffix}${tail}${edge}`;
141
162
  let out = `${seq(fgRgb, bgRgb)}${EL}`;
142
163
  out += paint(prefix, fgRgb, bgRgb, color);
143
164
  out += paintBracketName(
@@ -148,13 +169,14 @@ const paintBranchRow = (entry, width, color, selected, cols) => {
148
169
  color,
149
170
  chip.nameBold,
150
171
  );
151
- out += paint(after, fgRgb, bgRgb, color);
152
- out += paintBranchSuffix(parts, cols, fgRgb, bgRgb, color);
172
+ out += paint(after, markFg, markBg, color);
173
+ out += paintBranchSuffix(parts, cols, markFg, markBg, color, entry.current);
174
+ out += paint(tail, markFg, markBg, color);
153
175
  return out + edge;
154
176
  };
155
177
 
156
178
  const paintBranchEdit = (compose, width, color) => {
157
- const inner = Math.max(1, width - 1);
179
+ const inner = Math.max(1, width - BRANCH_EDGE);
158
180
  const prefix = ` ${FILE_MARK} `;
159
181
  const prefixW = visibleWidth(prefix);
160
182
  const rest = Math.max(1, inner - prefixW);
@@ -165,7 +187,7 @@ const paintBranchEdit = (compose, width, color) => {
165
187
  const left = `${prefix}${clipped}${' '.repeat(pad)}`;
166
188
  const fgRgb = THEME.chromeFg;
167
189
  const bgRgb = THEME.buttonBg;
168
- const edge = fill(1, fgRgb, bgRgb, color);
190
+ const edge = fill(BRANCH_EDGE, fgRgb, THEME.ctxBg, color);
169
191
  let row = `${left}${edge}`;
170
192
  if (color) {
171
193
  const gap = ' '.repeat(pad);
@@ -6,10 +6,12 @@ const files = require('../files.js');
6
6
  const primitives = require('./primitives.js');
7
7
 
8
8
  const { itemPath, TODO_FILE, isTodosEntry, fileTotals } = files;
9
- const { ACTION_IDS, BRANCH_ACTIONS, actionLetter, buttonWord } = keys;
10
- const { FILES_DISABLED, FILES_GIT_DISABLED, BRANCHES_DISABLED } = keys;
11
- const { TODO_DISABLED, DIFF_DISABLED, diffGitDisabled } = keys;
12
- const { branchGitDisabled, CONFIRM, confirmPrefix, promptFromChoices } = keys;
9
+ const { ACTION_IDS, BRANCH_ACTIONS, COMMIT_ACTIONS, actionLetter } = keys;
10
+ const { buttonWord } = keys;
11
+ const { FILES_HIDDEN, FILES_GIT_DISABLED, BRANCHES_DISABLED } = keys;
12
+ const { COMMITS_DISABLED, TODO_DISABLED, DIFF_DISABLED } = keys;
13
+ const { diffGitDisabled, branchGitDisabled, commitGitDisabled } = keys;
14
+ const { CONFIRM, confirmPrefix, promptFromChoices } = keys;
13
15
  const { THEME, paint, visibleWidth } = ansi;
14
16
  const { truncateVisible, fg, RESET, EL, seq } = ansi;
15
17
  const { fill, branchLabel, paintBracketName } = primitives;
@@ -91,7 +93,6 @@ const INFO_STATUS = [
91
93
  const INFO_PREFIX = ['checked out ', 'created ', 'rebased onto ', 'dropped '];
92
94
 
93
95
  const QUIT_PROMPT = promptFromChoices(CONFIRM.confirmQuit.choices);
94
- const COMMIT_PROMPT = promptFromChoices(CONFIRM.confirmCommit.choices);
95
96
  const updatePrompt = (from, to) => ` update reslop ${from} → ${to}? y/n`;
96
97
  const dropPrompt = (name) => ` drop ${name}? y/n`;
97
98
  const PUSH_REJECT_PREFIX = CONFIRM.confirmPush.prefix;
@@ -160,6 +161,16 @@ const headerTarget = (view) => {
160
161
  const origin = entry.current ? 'current' : 'branch';
161
162
  return { path: entry.name, origin, n, total };
162
163
  }
164
+ if (pane === 'commits') {
165
+ const commits = view.commits ?? [];
166
+ const cursor = view.commitCursor ?? 0;
167
+ const entry = commits[cursor];
168
+ const total = commits.length;
169
+ const n = total ? cursor + 1 : 0;
170
+ if (!entry) return { path: '', origin: 'commit', n, total };
171
+ const origin = entry.head ? 'HEAD' : 'commit';
172
+ return { path: entry.subject ?? '', origin, n, total };
173
+ }
163
174
  if (!view.item) return { path: '', origin: '', n: 0, total: view.total ?? 0 };
164
175
  if (view.item.origin === 'todo') {
165
176
  return { path: TODO_FILE, origin: 'todo', n: 1, total: 1 };
@@ -276,7 +287,7 @@ const layoutButtons = (width, compact, hidden, extra, disabled) => {
276
287
  for (const id of ACTION_IDS) {
277
288
  if (id === 'quit') {
278
289
  for (const add of extraIds) {
279
- if (!hiddenIds.includes(add)) ids.push(add);
290
+ if (!ids.includes(add)) ids.push(add);
280
291
  }
281
292
  }
282
293
  if (hiddenIds.includes(id)) continue;
@@ -380,8 +391,8 @@ const statusDelta = (total) => {
380
391
  const hasDelta =
381
392
  stagedAdd || unstagedAdd || stagedDel || unstagedDel || total.added;
382
393
  if (!hasDelta && !total.removed) return '';
383
- const plus = `+${stagedAdd}/${unstagedAdd}`;
384
- const minus = `-${stagedDel}/${unstagedDel}`;
394
+ const plus = `+${stagedAdd}/${stagedAdd + unstagedAdd}`;
395
+ const minus = `-${stagedDel}/${stagedDel + unstagedDel}`;
385
396
  return joinStatus([plus, minus]);
386
397
  };
387
398
 
@@ -525,10 +536,15 @@ const statusBranchHit = (view, width, y) => {
525
536
  return { id: 'branch', y, x0: open, x1: close + 1 };
526
537
  };
527
538
 
539
+ const selectedBranch = (view) => (view.branches ?? [])[view.branchCursor ?? 0];
540
+
541
+ const selectedCommit = (view) => (view.commits ?? [])[view.commitCursor ?? 0];
542
+
528
543
  const hiddenActions = (view) => {
529
544
  const pane = view.pane ?? 'diff';
530
- if (pane === 'files') return FILES_DISABLED;
545
+ if (pane === 'files') return FILES_HIDDEN;
531
546
  if (pane === 'branches') return BRANCHES_DISABLED;
547
+ if (pane === 'commits') return COMMITS_DISABLED;
532
548
  if (view.item && view.item.origin === 'todo') return TODO_DISABLED;
533
549
  return DIFF_DISABLED;
534
550
  };
@@ -542,22 +558,26 @@ const dimFooterActions = (view) => {
542
558
  return FILES_GIT_DISABLED;
543
559
  }
544
560
  if (pane === 'diff') return diffGitDisabled(view.item);
545
- if (pane === 'branches') {
546
- const branches = view.branches ?? [];
547
- const entry = branches[view.branchCursor ?? 0];
548
- return branchGitDisabled(entry);
561
+ if (pane === 'branches') return branchGitDisabled(selectedBranch(view));
562
+ if (pane === 'commits') {
563
+ const entry = selectedCommit(view);
564
+ const staged = (view.counts?.staged ?? 0) > 0;
565
+ if (!entry) return commitGitDisabled({ canCommit: staged });
566
+ return commitGitDisabled({ ...entry, canCommit: staged });
549
567
  }
550
568
  return [];
551
569
  };
552
570
 
553
571
  const extraFooterActions = (view) => {
554
- if ((view.pane ?? 'diff') === 'branches') return Object.keys(BRANCH_ACTIONS);
572
+ const pane = view.pane ?? 'diff';
573
+ if (pane === 'files') return ['commit', 'pull', 'push'];
574
+ if (pane === 'branches') return Object.keys(BRANCH_ACTIONS);
575
+ if (pane === 'commits') return Object.keys(COMMIT_ACTIONS);
555
576
  return [];
556
577
  };
557
578
 
558
579
  module.exports = {
559
580
  QUIT_PROMPT,
560
- COMMIT_PROMPT,
561
581
  PUSH_PROMPT,
562
582
  updatePrompt,
563
583
  dropPrompt,
@@ -0,0 +1,138 @@
1
+ 'use strict';
2
+
3
+ const ansi = require('../ansi.js');
4
+ const primitives = require('./primitives.js');
5
+
6
+ const { THEME, paint, visibleWidth, truncateVisible, seq, EL } = ansi;
7
+ const { fill, padVisible, FILE_MARK, paneResult, paintCursorList } = primitives;
8
+
9
+ const COMMIT_COL_KEYS = ['sha', 'author', 'date'];
10
+
11
+ const COMMIT_COL_ALIGN = {
12
+ sha: 'end',
13
+ author: 'start',
14
+ date: 'start',
15
+ };
16
+
17
+ const COMMIT_COL_FG = {
18
+ sha: THEME.shaFg,
19
+ author: THEME.mutedFg,
20
+ date: THEME.mutedFg,
21
+ };
22
+
23
+ const COMMIT_EDGE = 2;
24
+ const COMMIT_TAIL = 1;
25
+ const COMMIT_GAP = ' ';
26
+ const COMMIT_DATE_GAP = ' ';
27
+
28
+ const colGap = (key) => (key === 'date' ? COMMIT_DATE_GAP : COMMIT_GAP);
29
+
30
+ const commitRowFields = (entry) => ({
31
+ sha: entry.shortSha ?? '',
32
+ author: entry.author ?? '',
33
+ date: entry.date ?? '',
34
+ });
35
+
36
+ const commitColumns = (commits) => {
37
+ const widths = {};
38
+ for (const key of COMMIT_COL_KEYS) widths[key] = 0;
39
+ for (const entry of commits) {
40
+ const fields = commitRowFields(entry);
41
+ for (const key of COMMIT_COL_KEYS) {
42
+ const w = visibleWidth(fields[key]);
43
+ if (w > widths[key]) widths[key] = w;
44
+ }
45
+ }
46
+ return widths;
47
+ };
48
+
49
+ const commitRowParts = (entry, cols) => {
50
+ const fields = commitRowFields(entry);
51
+ const parts = {};
52
+ for (const key of COMMIT_COL_KEYS) {
53
+ const width = cols?.[key] ?? visibleWidth(fields[key]);
54
+ parts[key] = padVisible(fields[key], width, COMMIT_COL_ALIGN[key]);
55
+ }
56
+ return parts;
57
+ };
58
+
59
+ const joinCommitSuffix = (parts, cols) => {
60
+ const bits = [];
61
+ for (const key of COMMIT_COL_KEYS) {
62
+ if (!cols[key]) continue;
63
+ bits.push(colGap(key) + parts[key]);
64
+ }
65
+ if (!bits.length) return '';
66
+ return bits.join('');
67
+ };
68
+
69
+ const commitColFg = (key) => COMMIT_COL_FG[key];
70
+
71
+ const paintCommitSuffix = (parts, cols, fgRgb, bgRgb, color) => {
72
+ const bits = [];
73
+ for (const key of COMMIT_COL_KEYS) {
74
+ if (!cols[key]) continue;
75
+ const tone = commitColFg(key) ?? fgRgb;
76
+ const gap = paint(colGap(key), fgRgb, bgRgb, color);
77
+ bits.push(gap + paint(parts[key], tone, bgRgb, color));
78
+ }
79
+ if (!bits.length) return '';
80
+ return bits.join('');
81
+ };
82
+
83
+ const paintCommitRow = (entry, width, color, selected, cols) => {
84
+ const inner = Math.max(1, width - COMMIT_EDGE - COMMIT_TAIL);
85
+ const mark = selected ? FILE_MARK : ' ';
86
+ const prefix = ` ${mark} `;
87
+ const lead = ' ';
88
+ const parts = commitRowParts(entry, cols);
89
+ const suffix = joinCommitSuffix(parts, cols);
90
+ const rest = Math.max(1, inner - visibleWidth(prefix + lead + suffix));
91
+ const subject = truncateVisible(entry.subject ?? '', rest);
92
+ const pad = Math.max(0, rest - visibleWidth(subject));
93
+ const after = `${' '.repeat(pad)}`;
94
+ const left = `${prefix}${lead}${subject}${after}`;
95
+ const fgRgb = selected ? THEME.chromeFg : THEME.mutedFg;
96
+ const bgRgb = selected ? THEME.buttonBg : THEME.ctxBg;
97
+ const nameFg = selected ? THEME.buttonHotFg : THEME.chromeFg;
98
+ const tail = ' '.repeat(COMMIT_TAIL);
99
+ const edge = fill(COMMIT_EDGE, fgRgb, THEME.ctxBg, color);
100
+ if (!color) return `${left}${suffix}${tail}${edge}`;
101
+ let out = `${seq(fgRgb, bgRgb)}${EL}`;
102
+ out += paint(prefix, fgRgb, bgRgb, color);
103
+ out += paint(lead, nameFg, bgRgb, color);
104
+ out += paint(subject, nameFg, bgRgb, color);
105
+ out += paint(after, fgRgb, bgRgb, color);
106
+ out += paintCommitSuffix(parts, cols, fgRgb, bgRgb, color);
107
+ out += paint(tail, fgRgb, bgRgb, color);
108
+ return out + edge;
109
+ };
110
+
111
+ const paintBodyCommits = (view, width, color, bodyH, headerLines) => {
112
+ const commits = view.commits ?? [];
113
+ const cols = commitColumns(commits);
114
+ const cursor = view.commitCursor ?? 0;
115
+ const paintRow = (entry, rowWidth, rowColor, selected) =>
116
+ paintCommitRow(entry, rowWidth, rowColor, selected, cols);
117
+ const painted = paintCursorList(
118
+ commits,
119
+ cursor,
120
+ width,
121
+ color,
122
+ bodyH,
123
+ headerLines,
124
+ paintRow,
125
+ null,
126
+ view.listScroll,
127
+ );
128
+ return paneResult({
129
+ body: painted.body,
130
+ fileHits: painted.fileHits,
131
+ todoOwners: [],
132
+ cursor: painted.cursor,
133
+ splitBody: false,
134
+ listScroll: painted.offset,
135
+ });
136
+ };
137
+
138
+ module.exports = { paintBodyCommits };
@@ -15,7 +15,7 @@ const FILE_STATUS_FG = {
15
15
  partial: THEME.warnFg,
16
16
  };
17
17
 
18
- const FILE_COL_KEYS = ['add', 'del', 'status', 'n', 'm'];
18
+ const FILE_COL_KEYS = ['add', 'del', 'status', 'n', 'm', 'date'];
19
19
 
20
20
  const FILE_COL_ALIGN = {
21
21
  add: 'start',
@@ -23,15 +23,18 @@ const FILE_COL_ALIGN = {
23
23
  status: 'start',
24
24
  n: 'start',
25
25
  m: 'end',
26
+ date: 'start',
26
27
  };
27
28
 
29
+ const FILE_DATE_GAP = ' ';
30
+
28
31
  const signedCount = (sign, n) => `${sign}${n ?? 0}`;
29
32
 
30
33
  const fileRowFields = (entry) => {
31
34
  if (isTodosEntry(entry)) {
32
35
  const n = `${entry.staged ?? 0}`;
33
36
  const m = `${entry.remaining ?? 0}`;
34
- return { add: '', del: '', status: '', n, m };
37
+ return { add: '', del: '', status: '', n, m, date: '' };
35
38
  }
36
39
  const { added, removed, staged, remaining } = entry;
37
40
  const add = signedCount('+', added);
@@ -39,7 +42,8 @@ const fileRowFields = (entry) => {
39
42
  const status = entry.status ?? '';
40
43
  const n = `${staged ?? 0}`;
41
44
  const m = `${remaining ?? 0}`;
42
- return { add, del, status, n, m };
45
+ const date = entry.date ?? '';
46
+ return { add, del, status, n, m, date };
43
47
  };
44
48
 
45
49
  const fileColumns = (files) => {
@@ -73,12 +77,20 @@ const fileRowParts = (entry, cols) => {
73
77
  return parts;
74
78
  };
75
79
 
76
- const joinFileSuffix = (parts) => {
80
+ const joinFileDate = (parts, cols) => {
81
+ if (!cols?.date) return '';
82
+ return `${FILE_DATE_GAP}${parts.date}`;
83
+ };
84
+
85
+ const joinFileMeta = (parts) => {
77
86
  const { add, del, status, n, m } = parts;
78
87
  return ` ${add} ${del} ${status} ${n}/${m}`;
79
88
  };
80
89
 
81
- const paintFileSuffix = (parts, fgRgb, bgRgb, color) => {
90
+ const joinFileSuffix = (parts, cols) =>
91
+ `${joinFileMeta(parts)}${joinFileDate(parts, cols)}`;
92
+
93
+ const paintFileSuffix = (parts, cols, fgRgb, bgRgb, color) => {
82
94
  const { add, del, status, n, m } = parts;
83
95
  const gap = paint(' ', fgRgb, bgRgb, color);
84
96
  const addText = paint(add, THEME.addLineFg, bgRgb, color);
@@ -89,7 +101,10 @@ const paintFileSuffix = (parts, fgRgb, bgRgb, color) => {
89
101
  const mText = paint(m, fgRgb, bgRgb, color);
90
102
  const counts = addText + gap + delText;
91
103
  const meta = statusText + gap + nText + slash + mText;
92
- return gap + counts + gap + meta;
104
+ if (!cols?.date) return gap + counts + gap + meta;
105
+ const dateGap = paint(FILE_DATE_GAP, fgRgb, bgRgb, color);
106
+ const age = paint(parts.date, THEME.mutedFg, bgRgb, color);
107
+ return gap + counts + gap + meta + dateGap + age;
93
108
  };
94
109
 
95
110
  const paintTodosRow = (entry, width, color, selected, cols) => {
@@ -97,7 +112,9 @@ const paintTodosRow = (entry, width, color, selected, cols) => {
97
112
  const mark = selected ? FILE_MARK : ' ';
98
113
  const prefix = ` ${mark} `;
99
114
  const parts = fileRowParts(entry, cols);
100
- const suffix = joinFileSuffix(parts);
115
+ const meta = joinFileMeta(parts);
116
+ const dated = joinFileDate(parts, cols);
117
+ const suffix = `${meta}${dated}`;
101
118
  const pathW = Math.max(1, inner - visibleWidth(prefix + suffix));
102
119
  const clipped = truncateVisible(entry.path, pathW);
103
120
  const pad = Math.max(0, pathW - visibleWidth(clipped));
@@ -108,7 +125,10 @@ const paintTodosRow = (entry, width, color, selected, cols) => {
108
125
  if (!color) return `${left}${suffix}${edge}`;
109
126
  let out = `${seq(fgRgb, bgRgb)}${EL}`;
110
127
  out += paint(left, fgRgb, bgRgb, color, true);
111
- out += paint(suffix, fgRgb, bgRgb, color, true);
128
+ out += paint(meta, fgRgb, bgRgb, color, true);
129
+ if (!dated) return out + edge;
130
+ out += paint(FILE_DATE_GAP, fgRgb, bgRgb, color);
131
+ out += paint(parts.date, THEME.mutedFg, bgRgb, color);
112
132
  return out + edge;
113
133
  };
114
134
 
@@ -120,7 +140,7 @@ const paintFileRow = (entry, width, color, selected, cols) => {
120
140
  const mark = selected ? FILE_MARK : ' ';
121
141
  const prefix = ` ${mark} `;
122
142
  const parts = fileRowParts(entry, cols);
123
- const suffix = joinFileSuffix(parts);
143
+ const suffix = joinFileSuffix(parts, cols);
124
144
  const pathW = Math.max(1, inner - visibleWidth(prefix + suffix));
125
145
  const { path, status } = entry;
126
146
  const clipped = truncateVisible(path, pathW);
@@ -132,7 +152,7 @@ const paintFileRow = (entry, width, color, selected, cols) => {
132
152
  if (!color) return `${left}${suffix}${edge}`;
133
153
  let out = `${seq(fgRgb, bgRgb)}${EL}`;
134
154
  out += paint(left, fgRgb, bgRgb, color);
135
- out += paintFileSuffix(parts, fgRgb, bgRgb, color);
155
+ out += paintFileSuffix(parts, cols, fgRgb, bgRgb, color);
136
156
  return out + edge;
137
157
  };
138
158
 
@@ -6,6 +6,7 @@ const primitives = require('./primitives.js');
6
6
  const chrome = require('./chrome.js');
7
7
  const files = require('./files.js');
8
8
  const branches = require('./branches.js');
9
+ const commits = require('./commits.js');
9
10
  const notes = require('./notes.js');
10
11
  const diff = require('./diff.js');
11
12
 
@@ -17,11 +18,14 @@ const { hiddenActions, dimFooterActions, extraFooterActions } = chrome;
17
18
  const { paintNotePanel, paintBodyTodo } = notes;
18
19
  const { paintBodyFiles } = files;
19
20
  const { paintBodyBranches } = branches;
21
+ const { paintBodyCommits } = commits;
20
22
  const { paintBodyDiff } = diff;
21
23
 
22
24
  const bodyKind = (view) => {
23
25
  const pane = view.pane ?? 'diff';
24
- if (pane === 'files' || pane === 'branches') return pane;
26
+ if (pane === 'files' || pane === 'branches' || pane === 'commits') {
27
+ return pane;
28
+ }
25
29
  if (!view.item) return 'empty';
26
30
  if (view.item.origin === 'todo') return 'todo';
27
31
  return 'diff';
@@ -39,6 +43,7 @@ const paintBodyEmpty = () =>
39
43
  const BODY_PAINT = {
40
44
  files: paintBodyFiles,
41
45
  branches: paintBodyBranches,
46
+ commits: paintBodyCommits,
42
47
  empty: paintBodyEmpty,
43
48
  todo: paintBodyTodo,
44
49
  diff: paintBodyDiff,
@@ -8,7 +8,7 @@ const GUTTER_W = 2;
8
8
  const FILE_MARK = '▶';
9
9
  const FILE_LIST_PAD = 1;
10
10
  const CHECK_DONE = 'xX';
11
- const LIST_PANES = ['files', 'branches'];
11
+ const LIST_PANES = ['files', 'branches', 'commits'];
12
12
  const CHECK_TOKEN = /\[([ xX])\]/;
13
13
  const CHECK_LEAD = /^\[[ xX]\] /;
14
14
 
package/lib/render.js CHANGED
@@ -6,7 +6,7 @@ const diff = require('./render/diff.js');
6
6
  const present = require('./render/present.js');
7
7
  const { renderFrame } = require('./render/frame.js');
8
8
 
9
- const { QUIT_PROMPT, COMMIT_PROMPT, PUSH_PROMPT } = chrome;
9
+ const { QUIT_PROMPT, PUSH_PROMPT } = chrome;
10
10
  const { updatePrompt, dropPrompt } = chrome;
11
11
  const { formatBusyStatus, headerText, layoutButtons } = chrome;
12
12
  const { DEL_CHAR_BG, ADD_CHAR_BG, paintDiffLine, codeInnerWidth } = diff;
@@ -15,7 +15,6 @@ const { presentCursor, presentRows } = present;
15
15
 
16
16
  module.exports = {
17
17
  QUIT_PROMPT,
18
- COMMIT_PROMPT,
19
18
  PUSH_PROMPT,
20
19
  updatePrompt,
21
20
  dropPrompt,