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.
- package/.dependency-cruiser.cjs +2 -2
- package/.githooks/pre-push +2 -2
- package/.github/workflows/release.yml +3 -0
- package/CHANGELOG.md +6 -0
- package/dist/App.js +278 -758
- package/dist/KeyBindings.js +103 -91
- package/dist/ModalController.js +166 -0
- package/dist/MouseHandlers.js +37 -30
- package/dist/NavigationController.js +290 -0
- package/dist/StagingOperations.js +199 -0
- package/dist/config.js +39 -0
- package/dist/core/CompareManager.js +134 -0
- package/dist/core/ExplorerStateManager.js +7 -3
- package/dist/core/GitStateManager.js +28 -771
- package/dist/core/HistoryManager.js +72 -0
- package/dist/core/RemoteOperationManager.js +109 -0
- package/dist/core/WorkingTreeManager.js +412 -0
- package/dist/index.js +57 -57
- package/dist/state/FocusRing.js +40 -0
- package/dist/state/UIState.js +82 -48
- package/dist/ui/PaneRenderers.js +3 -6
- package/dist/ui/modals/BaseBranchPicker.js +4 -7
- package/dist/ui/modals/CommitActionConfirm.js +4 -4
- package/dist/ui/modals/DiscardConfirm.js +4 -7
- package/dist/ui/modals/FileFinder.js +3 -6
- package/dist/ui/modals/HotkeysModal.js +24 -21
- package/dist/ui/modals/Modal.js +1 -0
- package/dist/ui/modals/RepoPicker.js +109 -0
- package/dist/ui/modals/ThemePicker.js +4 -7
- package/dist/ui/widgets/CommitPanel.js +26 -94
- package/dist/ui/widgets/CompareListView.js +1 -11
- package/dist/ui/widgets/DiffView.js +2 -27
- package/dist/ui/widgets/ExplorerContent.js +1 -4
- package/dist/ui/widgets/ExplorerView.js +1 -11
- package/dist/ui/widgets/FileList.js +2 -8
- package/dist/ui/widgets/Footer.js +1 -0
- package/dist/utils/ansi.js +38 -0
- package/dist/utils/ansiTruncate.js +1 -5
- package/dist/utils/displayRows.js +72 -59
- package/dist/utils/fileCategories.js +7 -0
- package/dist/utils/fileResolution.js +23 -0
- package/dist/utils/languageDetection.js +3 -2
- package/dist/utils/logger.js +32 -0
- package/metrics/v0.2.4.json +236 -0
- package/metrics/v0.2.5.json +236 -0
- package/package.json +1 -1
- package/dist/ui/modals/BranchPicker.js +0 -157
- package/dist/ui/modals/SoftResetConfirm.js +0 -68
- package/dist/ui/modals/StashListModal.js +0 -98
- package/dist/utils/layoutCalculations.js +0 -100
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import blessed from 'neo-blessed';
|
|
2
|
-
/**
|
|
3
|
-
* StashListModal shows stash entries and allows popping one.
|
|
4
|
-
*/
|
|
5
|
-
export class StashListModal {
|
|
6
|
-
box;
|
|
7
|
-
screen;
|
|
8
|
-
entries;
|
|
9
|
-
selectedIndex = 0;
|
|
10
|
-
onPop;
|
|
11
|
-
onCancel;
|
|
12
|
-
constructor(screen, entries, onPop, onCancel) {
|
|
13
|
-
this.screen = screen;
|
|
14
|
-
this.entries = entries;
|
|
15
|
-
this.onPop = onPop;
|
|
16
|
-
this.onCancel = onCancel;
|
|
17
|
-
// Create modal box
|
|
18
|
-
const width = Math.min(70, screen.width - 6);
|
|
19
|
-
const maxVisible = Math.min(entries.length, 15);
|
|
20
|
-
const height = maxVisible + 6;
|
|
21
|
-
this.box = blessed.box({
|
|
22
|
-
parent: screen,
|
|
23
|
-
top: 'center',
|
|
24
|
-
left: 'center',
|
|
25
|
-
width,
|
|
26
|
-
height,
|
|
27
|
-
border: {
|
|
28
|
-
type: 'line',
|
|
29
|
-
},
|
|
30
|
-
style: {
|
|
31
|
-
border: {
|
|
32
|
-
fg: 'cyan',
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
tags: true,
|
|
36
|
-
keys: true,
|
|
37
|
-
scrollable: true,
|
|
38
|
-
alwaysScroll: true,
|
|
39
|
-
});
|
|
40
|
-
this.setupKeyHandlers();
|
|
41
|
-
this.render();
|
|
42
|
-
}
|
|
43
|
-
setupKeyHandlers() {
|
|
44
|
-
this.box.key(['escape', 'q'], () => {
|
|
45
|
-
this.close();
|
|
46
|
-
this.onCancel();
|
|
47
|
-
});
|
|
48
|
-
this.box.key(['enter'], () => {
|
|
49
|
-
if (this.entries.length > 0) {
|
|
50
|
-
const index = this.entries[this.selectedIndex].index;
|
|
51
|
-
this.close();
|
|
52
|
-
this.onPop(index);
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
this.box.key(['up', 'k'], () => {
|
|
56
|
-
this.selectedIndex = Math.max(0, this.selectedIndex - 1);
|
|
57
|
-
this.render();
|
|
58
|
-
});
|
|
59
|
-
this.box.key(['down', 'j'], () => {
|
|
60
|
-
this.selectedIndex = Math.min(this.entries.length - 1, this.selectedIndex + 1);
|
|
61
|
-
this.render();
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
render() {
|
|
65
|
-
const lines = [];
|
|
66
|
-
const width = this.box.width - 4;
|
|
67
|
-
lines.push('{bold}{cyan-fg} Stash List{/cyan-fg}{/bold}');
|
|
68
|
-
lines.push('');
|
|
69
|
-
if (this.entries.length === 0) {
|
|
70
|
-
lines.push('{gray-fg}No stash entries{/gray-fg}');
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
for (let i = 0; i < this.entries.length; i++) {
|
|
74
|
-
const entry = this.entries[i];
|
|
75
|
-
const isSelected = i === this.selectedIndex;
|
|
76
|
-
const msg = entry.message.length > width - 10
|
|
77
|
-
? entry.message.slice(0, width - 13) + '\u2026'
|
|
78
|
-
: entry.message;
|
|
79
|
-
if (isSelected) {
|
|
80
|
-
lines.push(`{cyan-fg}{bold}> {${i}} ${msg}{/bold}{/cyan-fg}`);
|
|
81
|
-
}
|
|
82
|
-
else {
|
|
83
|
-
lines.push(` {gray-fg}{${i}}{/gray-fg} ${msg}`);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
lines.push('');
|
|
88
|
-
lines.push('{gray-fg}j/k: navigate | Enter: pop | Esc: cancel{/gray-fg}');
|
|
89
|
-
this.box.setContent(lines.join('\n'));
|
|
90
|
-
this.screen.render();
|
|
91
|
-
}
|
|
92
|
-
close() {
|
|
93
|
-
this.box.destroy();
|
|
94
|
-
}
|
|
95
|
-
focus() {
|
|
96
|
-
this.box.focus();
|
|
97
|
-
}
|
|
98
|
-
}
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import { getFileListSectionCounts } from './fileCategories.js';
|
|
2
|
-
// Re-export for backwards compatibility
|
|
3
|
-
export { getFileListSectionCounts } from './fileCategories.js';
|
|
4
|
-
/**
|
|
5
|
-
* Calculate total rows for the FileList component.
|
|
6
|
-
* Accounts for headers and spacers between sections.
|
|
7
|
-
*/
|
|
8
|
-
export function getFileListTotalRows(files) {
|
|
9
|
-
const { modifiedCount, untrackedCount, stagedCount } = getFileListSectionCounts(files);
|
|
10
|
-
let rows = 0;
|
|
11
|
-
// Modified section
|
|
12
|
-
if (modifiedCount > 0) {
|
|
13
|
-
rows += 1 + modifiedCount; // header + files
|
|
14
|
-
}
|
|
15
|
-
// Untracked section
|
|
16
|
-
if (untrackedCount > 0) {
|
|
17
|
-
if (modifiedCount > 0)
|
|
18
|
-
rows += 1; // spacer
|
|
19
|
-
rows += 1 + untrackedCount; // header + files
|
|
20
|
-
}
|
|
21
|
-
// Staged section
|
|
22
|
-
if (stagedCount > 0) {
|
|
23
|
-
if (modifiedCount > 0 || untrackedCount > 0)
|
|
24
|
-
rows += 1; // spacer
|
|
25
|
-
rows += 1 + stagedCount; // header + files
|
|
26
|
-
}
|
|
27
|
-
return rows;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Calculate the heights of the top (file list) and bottom (diff/commit/etc) panes
|
|
31
|
-
* based on the number of files and available content area.
|
|
32
|
-
*
|
|
33
|
-
* The top pane grows to fit files up to 40% of content height.
|
|
34
|
-
* The bottom pane gets the remaining space.
|
|
35
|
-
*
|
|
36
|
-
* When flatRowCount is provided (flat view mode), uses that directly instead
|
|
37
|
-
* of computing row count from categorized file list.
|
|
38
|
-
*/
|
|
39
|
-
export function calculatePaneHeights(files, contentHeight, maxTopRatio = 0.4, flatRowCount) {
|
|
40
|
-
// Calculate content rows needed for staging area
|
|
41
|
-
const neededRows = flatRowCount !== undefined ? flatRowCount : getFileListTotalRows(files);
|
|
42
|
-
// Minimum height of 3 (header + 2 lines for empty state)
|
|
43
|
-
const minHeight = 3;
|
|
44
|
-
// Maximum is maxTopRatio of content area
|
|
45
|
-
const maxHeight = Math.floor(contentHeight * maxTopRatio);
|
|
46
|
-
// Use the smaller of needed or max, but at least min
|
|
47
|
-
const topHeight = Math.max(minHeight, Math.min(neededRows, maxHeight));
|
|
48
|
-
const bottomHeight = contentHeight - topHeight;
|
|
49
|
-
return { topPaneHeight: topHeight, bottomPaneHeight: bottomHeight };
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Calculate which row in the file list a file at a given index occupies.
|
|
53
|
-
* This accounts for headers and spacers in the list.
|
|
54
|
-
* File order: Modified → Untracked → Staged (matches FileList.tsx)
|
|
55
|
-
*/
|
|
56
|
-
export function getRowForFileIndex(selectedIndex, modifiedCount, untrackedCount, _stagedCount) {
|
|
57
|
-
let row = 0;
|
|
58
|
-
// Modified section
|
|
59
|
-
if (selectedIndex < modifiedCount) {
|
|
60
|
-
// In modified section: header + file rows
|
|
61
|
-
return 1 + selectedIndex;
|
|
62
|
-
}
|
|
63
|
-
if (modifiedCount > 0) {
|
|
64
|
-
row += 1 + modifiedCount; // header + files
|
|
65
|
-
}
|
|
66
|
-
// Untracked section
|
|
67
|
-
const untrackedStart = modifiedCount;
|
|
68
|
-
if (selectedIndex < untrackedStart + untrackedCount) {
|
|
69
|
-
// In untracked section
|
|
70
|
-
const untrackedIdx = selectedIndex - untrackedStart;
|
|
71
|
-
if (modifiedCount > 0)
|
|
72
|
-
row += 1; // spacer
|
|
73
|
-
return row + 1 + untrackedIdx; // header + file position
|
|
74
|
-
}
|
|
75
|
-
if (untrackedCount > 0) {
|
|
76
|
-
if (modifiedCount > 0)
|
|
77
|
-
row += 1; // spacer
|
|
78
|
-
row += 1 + untrackedCount; // header + files
|
|
79
|
-
}
|
|
80
|
-
// Staged section
|
|
81
|
-
const stagedStart = modifiedCount + untrackedCount;
|
|
82
|
-
const stagedIdx = selectedIndex - stagedStart;
|
|
83
|
-
if (modifiedCount > 0 || untrackedCount > 0)
|
|
84
|
-
row += 1; // spacer
|
|
85
|
-
return row + 1 + stagedIdx; // header + file position
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* Calculate the scroll offset needed to keep a selected row visible.
|
|
89
|
-
*/
|
|
90
|
-
export function calculateScrollOffset(selectedRow, currentScrollOffset, visibleHeight) {
|
|
91
|
-
// Scroll up if selected is above visible area
|
|
92
|
-
if (selectedRow < currentScrollOffset) {
|
|
93
|
-
return Math.max(0, selectedRow - 1);
|
|
94
|
-
}
|
|
95
|
-
// Scroll down if selected is below visible area
|
|
96
|
-
else if (selectedRow >= currentScrollOffset + visibleHeight) {
|
|
97
|
-
return selectedRow - visibleHeight + 1;
|
|
98
|
-
}
|
|
99
|
-
return currentScrollOffset;
|
|
100
|
-
}
|