diffstalker 0.2.3 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/.dependency-cruiser.cjs +2 -2
  2. package/.githooks/pre-push +2 -2
  3. package/.github/workflows/release.yml +3 -0
  4. package/CHANGELOG.md +6 -0
  5. package/dist/App.js +278 -758
  6. package/dist/KeyBindings.js +103 -91
  7. package/dist/ModalController.js +166 -0
  8. package/dist/MouseHandlers.js +37 -30
  9. package/dist/NavigationController.js +290 -0
  10. package/dist/StagingOperations.js +199 -0
  11. package/dist/config.js +39 -0
  12. package/dist/core/CompareManager.js +134 -0
  13. package/dist/core/ExplorerStateManager.js +7 -3
  14. package/dist/core/GitStateManager.js +28 -771
  15. package/dist/core/HistoryManager.js +72 -0
  16. package/dist/core/RemoteOperationManager.js +109 -0
  17. package/dist/core/WorkingTreeManager.js +412 -0
  18. package/dist/index.js +57 -57
  19. package/dist/state/FocusRing.js +40 -0
  20. package/dist/state/UIState.js +82 -48
  21. package/dist/ui/PaneRenderers.js +3 -6
  22. package/dist/ui/modals/BaseBranchPicker.js +4 -7
  23. package/dist/ui/modals/CommitActionConfirm.js +4 -4
  24. package/dist/ui/modals/DiscardConfirm.js +4 -7
  25. package/dist/ui/modals/FileFinder.js +3 -6
  26. package/dist/ui/modals/HotkeysModal.js +24 -21
  27. package/dist/ui/modals/Modal.js +1 -0
  28. package/dist/ui/modals/RepoPicker.js +109 -0
  29. package/dist/ui/modals/ThemePicker.js +4 -7
  30. package/dist/ui/widgets/CommitPanel.js +26 -94
  31. package/dist/ui/widgets/CompareListView.js +1 -11
  32. package/dist/ui/widgets/DiffView.js +2 -27
  33. package/dist/ui/widgets/ExplorerContent.js +1 -4
  34. package/dist/ui/widgets/ExplorerView.js +1 -11
  35. package/dist/ui/widgets/FileList.js +2 -8
  36. package/dist/ui/widgets/Footer.js +1 -0
  37. package/dist/utils/ansi.js +38 -0
  38. package/dist/utils/ansiTruncate.js +1 -5
  39. package/dist/utils/displayRows.js +72 -59
  40. package/dist/utils/fileCategories.js +7 -0
  41. package/dist/utils/fileResolution.js +23 -0
  42. package/dist/utils/languageDetection.js +3 -2
  43. package/dist/utils/logger.js +32 -0
  44. package/metrics/v0.2.4.json +236 -0
  45. package/metrics/v0.2.5.json +236 -0
  46. package/package.json +1 -1
  47. package/dist/ui/modals/BranchPicker.js +0 -157
  48. package/dist/ui/modals/SoftResetConfirm.js +0 -68
  49. package/dist/ui/modals/StashListModal.js +0 -98
  50. package/dist/utils/layoutCalculations.js +0 -100
@@ -0,0 +1,236 @@
1
+ {
2
+ "timestamp": "2026-03-06T05:12:44.045Z",
3
+ "gitRef": "v0.2.4",
4
+ "gitSha": "0677167",
5
+ "summary": {
6
+ "files": 91,
7
+ "lines": 16342,
8
+ "functions": 1163,
9
+ "avgCyclomaticComplexity": 4.9,
10
+ "maxCyclomaticComplexity": {
11
+ "value": 34,
12
+ "function": "reconcileSelectionAfterStateChange",
13
+ "file": "src/App.ts:538"
14
+ },
15
+ "avgCognitiveComplexity": 6.1,
16
+ "maxCognitiveComplexity": {
17
+ "value": 41,
18
+ "function": "(anonymous)",
19
+ "file": "src/git/diff.ts:517"
20
+ },
21
+ "smells": 0
22
+ },
23
+ "hotspots": [
24
+ {
25
+ "file": "src/App.ts",
26
+ "lines": 1004,
27
+ "cyclomaticMax": 34,
28
+ "cognitiveMax": 19,
29
+ "smells": 0
30
+ },
31
+ {
32
+ "file": "src/utils/flatFileList.ts",
33
+ "lines": 100,
34
+ "cyclomaticMax": 29,
35
+ "cognitiveMax": 14,
36
+ "smells": 0
37
+ },
38
+ {
39
+ "file": "src/git/diff.ts",
40
+ "lines": 656,
41
+ "cyclomaticMax": 26,
42
+ "cognitiveMax": 41,
43
+ "smells": 0
44
+ },
45
+ {
46
+ "file": "src/utils/displayRows.ts",
47
+ "lines": 614,
48
+ "cyclomaticMax": 25,
49
+ "cognitiveMax": 39,
50
+ "smells": 0
51
+ },
52
+ {
53
+ "file": "src/git/status.ts",
54
+ "lines": 392,
55
+ "cyclomaticMax": 22,
56
+ "cognitiveMax": 25,
57
+ "smells": 0
58
+ },
59
+ {
60
+ "file": "src/ui/PaneRenderers.ts",
61
+ "lines": 240,
62
+ "cyclomaticMax": 20,
63
+ "cognitiveMax": 8,
64
+ "smells": 0
65
+ },
66
+ {
67
+ "file": "src/ui/widgets/ExplorerContent.ts",
68
+ "lines": 126,
69
+ "cyclomaticMax": 20,
70
+ "cognitiveMax": 24,
71
+ "smells": 0
72
+ },
73
+ {
74
+ "file": "src/NavigationController.ts",
75
+ "lines": 352,
76
+ "cyclomaticMax": 19,
77
+ "cognitiveMax": 13,
78
+ "smells": 0
79
+ },
80
+ {
81
+ "file": "src/config.ts",
82
+ "lines": 168,
83
+ "cyclomaticMax": 18,
84
+ "cognitiveMax": 25,
85
+ "smells": 0
86
+ },
87
+ {
88
+ "file": "src/ui/widgets/CommitPanel.ts",
89
+ "lines": 133,
90
+ "cyclomaticMax": 18,
91
+ "cognitiveMax": 16,
92
+ "smells": 0
93
+ },
94
+ {
95
+ "file": "src/ui/widgets/Header.ts",
96
+ "lines": 134,
97
+ "cyclomaticMax": 18,
98
+ "cognitiveMax": 22,
99
+ "smells": 0
100
+ },
101
+ {
102
+ "file": "src/MouseHandlers.ts",
103
+ "lines": 276,
104
+ "cyclomaticMax": 17,
105
+ "cognitiveMax": 15,
106
+ "smells": 0
107
+ },
108
+ {
109
+ "file": "src/ipc/CommandServer.ts",
110
+ "lines": 266,
111
+ "cyclomaticMax": 17,
112
+ "cognitiveMax": 6,
113
+ "smells": 0
114
+ },
115
+ {
116
+ "file": "src/index.ts",
117
+ "lines": 180,
118
+ "cyclomaticMax": 16,
119
+ "cognitiveMax": 17,
120
+ "smells": 0
121
+ },
122
+ {
123
+ "file": "src/ui/widgets/ExplorerView.ts",
124
+ "lines": 245,
125
+ "cyclomaticMax": 16,
126
+ "cognitiveMax": 21,
127
+ "smells": 0
128
+ },
129
+ {
130
+ "file": "src/utils/ansiTruncate.ts",
131
+ "lines": 118,
132
+ "cyclomaticMax": 16,
133
+ "cognitiveMax": 24,
134
+ "smells": 0
135
+ },
136
+ {
137
+ "file": "src/ui/widgets/DiffView.ts",
138
+ "lines": 493,
139
+ "cyclomaticMax": 15,
140
+ "cognitiveMax": 12,
141
+ "smells": 0
142
+ },
143
+ {
144
+ "file": "src/ui/widgets/CompareListView.ts",
145
+ "lines": 383,
146
+ "cyclomaticMax": 14,
147
+ "cognitiveMax": 16,
148
+ "smells": 0
149
+ },
150
+ {
151
+ "file": "src/core/ExplorerStateManager.ts",
152
+ "lines": 735,
153
+ "cyclomaticMax": 13,
154
+ "cognitiveMax": 17,
155
+ "smells": 0
156
+ },
157
+ {
158
+ "file": "src/utils/diffRowCalculations.ts",
159
+ "lines": 136,
160
+ "cyclomaticMax": 13,
161
+ "cognitiveMax": 24,
162
+ "smells": 0
163
+ },
164
+ {
165
+ "file": "src/StagingOperations.ts",
166
+ "lines": 224,
167
+ "cyclomaticMax": 12,
168
+ "cognitiveMax": 10,
169
+ "smells": 0
170
+ },
171
+ {
172
+ "file": "src/ui/widgets/FileList.ts",
173
+ "lines": 209,
174
+ "cyclomaticMax": 12,
175
+ "cognitiveMax": 12,
176
+ "smells": 0
177
+ },
178
+ {
179
+ "file": "src/utils/lineBreaking.ts",
180
+ "lines": 114,
181
+ "cyclomaticMax": 12,
182
+ "cognitiveMax": 17,
183
+ "smells": 0
184
+ },
185
+ {
186
+ "file": "src/core/WorkingTreeManager.ts",
187
+ "lines": 515,
188
+ "cyclomaticMax": 10,
189
+ "cognitiveMax": 13,
190
+ "smells": 0
191
+ },
192
+ {
193
+ "file": "src/ui/widgets/HistoryView.ts",
194
+ "lines": 97,
195
+ "cyclomaticMax": 9,
196
+ "cognitiveMax": 12,
197
+ "smells": 0
198
+ },
199
+ {
200
+ "file": "src/utils/formatPath.ts",
201
+ "lines": 72,
202
+ "cyclomaticMax": 9,
203
+ "cognitiveMax": 11,
204
+ "smells": 0
205
+ },
206
+ {
207
+ "file": "src/utils/explorerDisplayRows.ts",
208
+ "lines": 206,
209
+ "cyclomaticMax": 8,
210
+ "cognitiveMax": 15,
211
+ "smells": 0
212
+ },
213
+ {
214
+ "file": "src/ui/modals/FileFinder.ts",
215
+ "lines": 236,
216
+ "cyclomaticMax": 7,
217
+ "cognitiveMax": 13,
218
+ "smells": 0
219
+ },
220
+ {
221
+ "file": "src/ui/modals/BaseBranchPicker.ts",
222
+ "lines": 132,
223
+ "cyclomaticMax": 6,
224
+ "cognitiveMax": 13,
225
+ "smells": 0
226
+ },
227
+ {
228
+ "file": "src/ui/modals/RepoPicker.ts",
229
+ "lines": 134,
230
+ "cyclomaticMax": 6,
231
+ "cognitiveMax": 13,
232
+ "smells": 0
233
+ }
234
+ ],
235
+ "smellsByRule": {}
236
+ }
@@ -0,0 +1,236 @@
1
+ {
2
+ "timestamp": "2026-03-06T05:33:09.063Z",
3
+ "gitRef": "v0.2.5",
4
+ "gitSha": "d019555",
5
+ "summary": {
6
+ "files": 91,
7
+ "lines": 16350,
8
+ "functions": 1163,
9
+ "avgCyclomaticComplexity": 4.9,
10
+ "maxCyclomaticComplexity": {
11
+ "value": 34,
12
+ "function": "reconcileSelectionAfterStateChange",
13
+ "file": "src/App.ts:538"
14
+ },
15
+ "avgCognitiveComplexity": 6.1,
16
+ "maxCognitiveComplexity": {
17
+ "value": 41,
18
+ "function": "(anonymous)",
19
+ "file": "src/git/diff.ts:517"
20
+ },
21
+ "smells": 0
22
+ },
23
+ "hotspots": [
24
+ {
25
+ "file": "src/App.ts",
26
+ "lines": 1004,
27
+ "cyclomaticMax": 34,
28
+ "cognitiveMax": 19,
29
+ "smells": 0
30
+ },
31
+ {
32
+ "file": "src/utils/flatFileList.ts",
33
+ "lines": 100,
34
+ "cyclomaticMax": 29,
35
+ "cognitiveMax": 14,
36
+ "smells": 0
37
+ },
38
+ {
39
+ "file": "src/git/diff.ts",
40
+ "lines": 656,
41
+ "cyclomaticMax": 26,
42
+ "cognitiveMax": 41,
43
+ "smells": 0
44
+ },
45
+ {
46
+ "file": "src/utils/displayRows.ts",
47
+ "lines": 614,
48
+ "cyclomaticMax": 25,
49
+ "cognitiveMax": 39,
50
+ "smells": 0
51
+ },
52
+ {
53
+ "file": "src/git/status.ts",
54
+ "lines": 392,
55
+ "cyclomaticMax": 22,
56
+ "cognitiveMax": 25,
57
+ "smells": 0
58
+ },
59
+ {
60
+ "file": "src/ui/PaneRenderers.ts",
61
+ "lines": 240,
62
+ "cyclomaticMax": 20,
63
+ "cognitiveMax": 8,
64
+ "smells": 0
65
+ },
66
+ {
67
+ "file": "src/ui/widgets/ExplorerContent.ts",
68
+ "lines": 126,
69
+ "cyclomaticMax": 20,
70
+ "cognitiveMax": 24,
71
+ "smells": 0
72
+ },
73
+ {
74
+ "file": "src/NavigationController.ts",
75
+ "lines": 352,
76
+ "cyclomaticMax": 19,
77
+ "cognitiveMax": 13,
78
+ "smells": 0
79
+ },
80
+ {
81
+ "file": "src/config.ts",
82
+ "lines": 168,
83
+ "cyclomaticMax": 18,
84
+ "cognitiveMax": 25,
85
+ "smells": 0
86
+ },
87
+ {
88
+ "file": "src/ui/widgets/CommitPanel.ts",
89
+ "lines": 133,
90
+ "cyclomaticMax": 18,
91
+ "cognitiveMax": 16,
92
+ "smells": 0
93
+ },
94
+ {
95
+ "file": "src/ui/widgets/Header.ts",
96
+ "lines": 134,
97
+ "cyclomaticMax": 18,
98
+ "cognitiveMax": 22,
99
+ "smells": 0
100
+ },
101
+ {
102
+ "file": "src/MouseHandlers.ts",
103
+ "lines": 276,
104
+ "cyclomaticMax": 17,
105
+ "cognitiveMax": 15,
106
+ "smells": 0
107
+ },
108
+ {
109
+ "file": "src/ipc/CommandServer.ts",
110
+ "lines": 266,
111
+ "cyclomaticMax": 17,
112
+ "cognitiveMax": 6,
113
+ "smells": 0
114
+ },
115
+ {
116
+ "file": "src/index.ts",
117
+ "lines": 180,
118
+ "cyclomaticMax": 16,
119
+ "cognitiveMax": 17,
120
+ "smells": 0
121
+ },
122
+ {
123
+ "file": "src/ui/widgets/ExplorerView.ts",
124
+ "lines": 245,
125
+ "cyclomaticMax": 16,
126
+ "cognitiveMax": 21,
127
+ "smells": 0
128
+ },
129
+ {
130
+ "file": "src/utils/ansiTruncate.ts",
131
+ "lines": 118,
132
+ "cyclomaticMax": 16,
133
+ "cognitiveMax": 24,
134
+ "smells": 0
135
+ },
136
+ {
137
+ "file": "src/ui/widgets/DiffView.ts",
138
+ "lines": 493,
139
+ "cyclomaticMax": 15,
140
+ "cognitiveMax": 12,
141
+ "smells": 0
142
+ },
143
+ {
144
+ "file": "src/ui/widgets/CompareListView.ts",
145
+ "lines": 383,
146
+ "cyclomaticMax": 14,
147
+ "cognitiveMax": 16,
148
+ "smells": 0
149
+ },
150
+ {
151
+ "file": "src/core/ExplorerStateManager.ts",
152
+ "lines": 735,
153
+ "cyclomaticMax": 13,
154
+ "cognitiveMax": 17,
155
+ "smells": 0
156
+ },
157
+ {
158
+ "file": "src/utils/diffRowCalculations.ts",
159
+ "lines": 136,
160
+ "cyclomaticMax": 13,
161
+ "cognitiveMax": 24,
162
+ "smells": 0
163
+ },
164
+ {
165
+ "file": "src/StagingOperations.ts",
166
+ "lines": 224,
167
+ "cyclomaticMax": 12,
168
+ "cognitiveMax": 10,
169
+ "smells": 0
170
+ },
171
+ {
172
+ "file": "src/ui/widgets/FileList.ts",
173
+ "lines": 209,
174
+ "cyclomaticMax": 12,
175
+ "cognitiveMax": 12,
176
+ "smells": 0
177
+ },
178
+ {
179
+ "file": "src/utils/lineBreaking.ts",
180
+ "lines": 114,
181
+ "cyclomaticMax": 12,
182
+ "cognitiveMax": 17,
183
+ "smells": 0
184
+ },
185
+ {
186
+ "file": "src/core/WorkingTreeManager.ts",
187
+ "lines": 515,
188
+ "cyclomaticMax": 10,
189
+ "cognitiveMax": 13,
190
+ "smells": 0
191
+ },
192
+ {
193
+ "file": "src/ui/widgets/HistoryView.ts",
194
+ "lines": 97,
195
+ "cyclomaticMax": 9,
196
+ "cognitiveMax": 12,
197
+ "smells": 0
198
+ },
199
+ {
200
+ "file": "src/utils/formatPath.ts",
201
+ "lines": 72,
202
+ "cyclomaticMax": 9,
203
+ "cognitiveMax": 11,
204
+ "smells": 0
205
+ },
206
+ {
207
+ "file": "src/utils/explorerDisplayRows.ts",
208
+ "lines": 206,
209
+ "cyclomaticMax": 8,
210
+ "cognitiveMax": 15,
211
+ "smells": 0
212
+ },
213
+ {
214
+ "file": "src/ui/modals/FileFinder.ts",
215
+ "lines": 236,
216
+ "cyclomaticMax": 7,
217
+ "cognitiveMax": 13,
218
+ "smells": 0
219
+ },
220
+ {
221
+ "file": "src/ui/modals/BaseBranchPicker.ts",
222
+ "lines": 132,
223
+ "cyclomaticMax": 6,
224
+ "cognitiveMax": 13,
225
+ "smells": 0
226
+ },
227
+ {
228
+ "file": "src/ui/modals/RepoPicker.ts",
229
+ "lines": 134,
230
+ "cyclomaticMax": 6,
231
+ "cognitiveMax": 13,
232
+ "smells": 0
233
+ }
234
+ ],
235
+ "smellsByRule": {}
236
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "diffstalker",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "Terminal application that displays git diff/status for directories",
5
5
  "author": "yogh-io",
6
6
  "license": "MIT",
@@ -1,157 +0,0 @@
1
- import blessed from 'neo-blessed';
2
- /**
3
- * BranchPicker modal for switching or creating branches.
4
- * Text input at top for filtering; branch list below.
5
- * If typed name matches no existing branch, shows "Create: <name>" as first option.
6
- */
7
- export class BranchPicker {
8
- box;
9
- textbox;
10
- screen;
11
- branches;
12
- filteredBranches = [];
13
- selectedIndex = 0;
14
- query = '';
15
- showCreate = false;
16
- onSwitch;
17
- onCreate;
18
- onCancel;
19
- constructor(screen, branches, onSwitch, onCreate, onCancel) {
20
- this.screen = screen;
21
- this.branches = branches;
22
- this.onSwitch = onSwitch;
23
- this.onCreate = onCreate;
24
- this.onCancel = onCancel;
25
- this.filteredBranches = branches;
26
- const width = Math.min(60, screen.width - 6);
27
- const maxVisible = Math.min(branches.length + 1, 15);
28
- const height = maxVisible + 7;
29
- this.box = blessed.box({
30
- parent: screen,
31
- top: 'center',
32
- left: 'center',
33
- width,
34
- height,
35
- border: {
36
- type: 'line',
37
- },
38
- style: {
39
- border: {
40
- fg: 'cyan',
41
- },
42
- },
43
- tags: true,
44
- keys: false,
45
- });
46
- this.textbox = blessed.textarea({
47
- parent: this.box,
48
- top: 1,
49
- left: 1,
50
- width: width - 4,
51
- height: 1,
52
- inputOnFocus: true,
53
- style: {
54
- fg: 'white',
55
- bg: 'default',
56
- },
57
- });
58
- this.setupKeyHandlers();
59
- this.render();
60
- }
61
- setupKeyHandlers() {
62
- this.textbox.key(['escape'], () => {
63
- this.close();
64
- this.onCancel();
65
- });
66
- this.textbox.key(['enter'], () => {
67
- if (this.showCreate && this.selectedIndex === 0) {
68
- this.close();
69
- this.onCreate(this.query.trim());
70
- }
71
- else {
72
- const adjustedIndex = this.showCreate ? this.selectedIndex - 1 : this.selectedIndex;
73
- const branch = this.filteredBranches[adjustedIndex];
74
- if (branch && !branch.current) {
75
- this.close();
76
- this.onSwitch(branch.name);
77
- }
78
- }
79
- });
80
- this.textbox.key(['C-j', 'down'], () => {
81
- const maxIndex = this.filteredBranches.length + (this.showCreate ? 1 : 0) - 1;
82
- this.selectedIndex = Math.min(maxIndex, this.selectedIndex + 1);
83
- this.render();
84
- });
85
- this.textbox.key(['C-k', 'up'], () => {
86
- this.selectedIndex = Math.max(0, this.selectedIndex - 1);
87
- this.render();
88
- });
89
- this.textbox.on('keypress', () => {
90
- setImmediate(() => {
91
- const newQuery = this.textbox.getValue() || '';
92
- if (newQuery !== this.query) {
93
- this.query = newQuery;
94
- this.selectedIndex = 0;
95
- this.updateFilter();
96
- this.render();
97
- }
98
- });
99
- });
100
- }
101
- updateFilter() {
102
- const q = this.query.trim().toLowerCase();
103
- if (!q) {
104
- this.filteredBranches = this.branches;
105
- this.showCreate = false;
106
- }
107
- else {
108
- this.filteredBranches = this.branches.filter((b) => b.name.toLowerCase().includes(q));
109
- // Show create option if no exact match
110
- this.showCreate = !this.branches.some((b) => b.name === q);
111
- }
112
- }
113
- render() {
114
- const lines = [];
115
- lines.push('{bold}{cyan-fg}Switch / Create Branch{/cyan-fg}{/bold}');
116
- lines.push(''); // Space for input
117
- lines.push('');
118
- if (this.showCreate) {
119
- const isSelected = this.selectedIndex === 0;
120
- if (isSelected) {
121
- lines.push(`{green-fg}{bold}> Create: ${this.query.trim()}{/bold}{/green-fg}`);
122
- }
123
- else {
124
- lines.push(` {green-fg}Create: ${this.query.trim()}{/green-fg}`);
125
- }
126
- }
127
- for (let i = 0; i < this.filteredBranches.length; i++) {
128
- const branch = this.filteredBranches[i];
129
- const listIndex = this.showCreate ? i + 1 : i;
130
- const isSelected = listIndex === this.selectedIndex;
131
- let line = isSelected ? '{cyan-fg}{bold}> ' : ' ';
132
- if (branch.current) {
133
- line += '* ';
134
- }
135
- line += branch.name;
136
- if (isSelected)
137
- line += '{/bold}{/cyan-fg}';
138
- if (branch.current)
139
- line += ' {gray-fg}(current){/gray-fg}';
140
- lines.push(line);
141
- }
142
- if (this.filteredBranches.length === 0 && !this.showCreate) {
143
- lines.push('{gray-fg}No matching branches{/gray-fg}');
144
- }
145
- lines.push('');
146
- lines.push('{gray-fg}Enter: select | Esc: cancel | Ctrl+j/k: navigate{/gray-fg}');
147
- this.box.setContent(lines.join('\n'));
148
- this.screen.render();
149
- }
150
- close() {
151
- this.textbox.destroy();
152
- this.box.destroy();
153
- }
154
- focus() {
155
- this.textbox.focus();
156
- }
157
- }
@@ -1,68 +0,0 @@
1
- import blessed from 'neo-blessed';
2
- /**
3
- * SoftResetConfirm modal for confirming soft reset HEAD~1.
4
- */
5
- export class SoftResetConfirm {
6
- box;
7
- screen;
8
- onConfirm;
9
- onCancel;
10
- constructor(screen, headCommit, onConfirm, onCancel) {
11
- this.screen = screen;
12
- this.onConfirm = onConfirm;
13
- this.onCancel = onCancel;
14
- const width = Math.min(60, screen.width - 6);
15
- const height = 9;
16
- this.box = blessed.box({
17
- parent: screen,
18
- top: 'center',
19
- left: 'center',
20
- width,
21
- height,
22
- border: {
23
- type: 'line',
24
- },
25
- style: {
26
- border: {
27
- fg: 'yellow',
28
- },
29
- },
30
- tags: true,
31
- keys: true,
32
- });
33
- this.setupKeyHandlers();
34
- this.renderContent(headCommit, width);
35
- }
36
- setupKeyHandlers() {
37
- this.box.key(['y', 'Y'], () => {
38
- this.close();
39
- this.onConfirm();
40
- });
41
- this.box.key(['n', 'N', 'escape', 'q'], () => {
42
- this.close();
43
- this.onCancel();
44
- });
45
- }
46
- renderContent(commit, width) {
47
- const lines = [];
48
- const innerWidth = width - 6;
49
- lines.push('{bold}{yellow-fg} Soft Reset HEAD~1?{/yellow-fg}{/bold}');
50
- lines.push('');
51
- const msg = commit.message.length > innerWidth
52
- ? commit.message.slice(0, innerWidth - 3) + '\u2026'
53
- : commit.message;
54
- lines.push(`{yellow-fg}${commit.shortHash}{/yellow-fg} ${msg}`);
55
- lines.push('');
56
- lines.push('{gray-fg}Changes will return to staged state{/gray-fg}');
57
- lines.push('');
58
- lines.push('{gray-fg}Press {/gray-fg}{green-fg}y{/green-fg}{gray-fg} to confirm, {/gray-fg}{red-fg}n{/red-fg}{gray-fg} or Esc to cancel{/gray-fg}');
59
- this.box.setContent(lines.join('\n'));
60
- this.screen.render();
61
- }
62
- close() {
63
- this.box.destroy();
64
- }
65
- focus() {
66
- this.box.focus();
67
- }
68
- }