sergeant 1.0.10 → 1.0.11
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/README.md +1 -1
- data/lib/sergeant/modals/help.rb +115 -90
- data/lib/sergeant/rendering.rb +5 -2
- data/lib/sergeant/version.rb +1 -1
- data/lib/sergeant.rb +7 -2
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 37a2b99f4864cf2d4d4b4cffc4eb62a3c89a28216833084ad587bafd8b49c773
|
|
4
|
+
data.tar.gz: 2ddb28f7922812df52333b8bd49eef9dbec5c1e91c6d2523a72b3857fd7f9c4c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b659a1fc589acc4ee89eadbae2c3095643e494ce8e5402fd4a7f118ba79fe132d17a44002187428ea5951dd5c9b2e5355868ae86947d38416b0e3d40e29d634e
|
|
7
|
+
data.tar.gz: 1145c08b4d59cb1a47da1e8e6b42d1802a74b9ade0c6918b2fd0e43754300a4dee4db43e19e866fa939a8bf59166d44a32b641263cf6adcb6dd0aba6a1638dc2
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
8
|
|
|
9
|
+
## [1.0.11] - 2026-08-24
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- **Resizable side preview panel** - `[` / `]` shrink/grow the preview panel in 5% steps (clamped 15%-60% of terminal width), instead of a fixed 35% ratio
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
- **Help modal (`m`) now scrolls** - if the key mapping list doesn't fit the terminal height, it scrolls with `j`/`k`/arrows and shows a scrollbar, instead of silently cutting off the bottom of the list
|
|
16
|
+
- Removed the `v` key - it was bound to the exact same "preview file" action as `Enter`/`→`/`l` on a file, so it was a pure duplicate with no functionality behind it
|
|
17
|
+
|
|
9
18
|
## [1.0.10] - 2026-08-24
|
|
10
19
|
|
|
11
20
|
### Fixed
|
data/README.md
CHANGED
|
@@ -231,7 +231,6 @@ cd $(sgt --pwd /usr/local)
|
|
|
231
231
|
| `u` | Unmark all items |
|
|
232
232
|
| `n` | Create new file or directory |
|
|
233
233
|
| `e` | Edit file with $EDITOR (or nano/nvim/vim) |
|
|
234
|
-
| `v` | Preview file or archive contents |
|
|
235
234
|
|
|
236
235
|
### Other Commands
|
|
237
236
|
|
|
@@ -246,6 +245,7 @@ cd $(sgt --pwd /usr/local)
|
|
|
246
245
|
| `:` | Execute terminal command in current directory |
|
|
247
246
|
| `o` | Toggle ownership/permissions display |
|
|
248
247
|
| `P` | Toggle side preview panel |
|
|
248
|
+
| `[` / `]` | Shrink / grow side preview panel |
|
|
249
249
|
| `b` | Go to bookmark |
|
|
250
250
|
| `H` | Show recent directories history |
|
|
251
251
|
| `R` | Force refresh and clear cache |
|
data/lib/sergeant/modals/help.rb
CHANGED
|
@@ -5,113 +5,138 @@
|
|
|
5
5
|
module Sergeant
|
|
6
6
|
module Modals
|
|
7
7
|
module Help
|
|
8
|
+
HELP_LINES = [
|
|
9
|
+
'Navigation:',
|
|
10
|
+
' ↑/k - Move up',
|
|
11
|
+
' ↓/j - Move down',
|
|
12
|
+
' Enter / →/l - Open directory or preview file',
|
|
13
|
+
' ←/h - Go back to parent directory',
|
|
14
|
+
'',
|
|
15
|
+
'File Operations:',
|
|
16
|
+
' Space - Mark/unmark item',
|
|
17
|
+
' c - Copy marked items',
|
|
18
|
+
' x - Cut marked items',
|
|
19
|
+
' p - Paste copied/cut items',
|
|
20
|
+
' d - Delete marked items',
|
|
21
|
+
' r - Rename current item',
|
|
22
|
+
' u - Unmark all items',
|
|
23
|
+
' n - Create new file or directory',
|
|
24
|
+
'',
|
|
25
|
+
'View & Search:',
|
|
26
|
+
' e - Edit file ($EDITOR, nano, nvim, vim)',
|
|
27
|
+
' f - Filter current directory view',
|
|
28
|
+
' / - Search files (with fzf if available)',
|
|
29
|
+
'',
|
|
30
|
+
'Other:',
|
|
31
|
+
' : - Execute terminal command',
|
|
32
|
+
' o - Toggle ownership display',
|
|
33
|
+
' P - Toggle side preview panel',
|
|
34
|
+
' [ / ] - Shrink/grow side preview panel',
|
|
35
|
+
' b - Go to bookmark',
|
|
36
|
+
' H - Show recent directories history',
|
|
37
|
+
' R - Force refresh and clear cache',
|
|
38
|
+
' q / ESC - Quit and cd to current directory'
|
|
39
|
+
].freeze
|
|
40
|
+
|
|
41
|
+
HELP_SECTION_HEADERS = ['Navigation:', 'File Operations:', 'View & Search:', 'Other:'].freeze
|
|
42
|
+
|
|
8
43
|
def show_help_modal
|
|
9
44
|
max_y = lines
|
|
10
45
|
max_x = cols
|
|
11
46
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
''
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
' P - Toggle side preview panel',
|
|
39
|
-
' b - Go to bookmark',
|
|
40
|
-
' H - Show recent directories history',
|
|
41
|
-
' R - Force refresh and clear cache',
|
|
42
|
-
' q / ESC - Quit and cd to current directory'
|
|
43
|
-
]
|
|
44
|
-
|
|
45
|
-
# Height must track the number of help lines - a fixed cap silently
|
|
46
|
-
# truncates the bottom of the list (including "quit"!) every time a
|
|
47
|
-
# new key mapping is added without also bumping the cap by hand.
|
|
48
|
-
modal_height = [help_lines.length + 4, max_y - 4].min
|
|
49
|
-
modal_width = [70, max_x - 4].min # Adaptive width
|
|
50
|
-
modal_y = (max_y - modal_height) / 2
|
|
51
|
-
modal_x = (max_x - modal_width) / 2
|
|
52
|
-
|
|
53
|
-
(modal_y..(modal_y + modal_height)).each do |y|
|
|
54
|
-
setpos(y, modal_x)
|
|
55
|
-
attron(color_pair(3)) do
|
|
56
|
-
addstr(' ' * modal_width)
|
|
47
|
+
modal_height = [HELP_LINES.length + 4, max_y - 4].min
|
|
48
|
+
modal_width = [70, max_x - 4].min
|
|
49
|
+
geometry = {
|
|
50
|
+
y: (max_y - modal_height) / 2,
|
|
51
|
+
x: (max_x - modal_width) / 2,
|
|
52
|
+
width: modal_width,
|
|
53
|
+
height: modal_height
|
|
54
|
+
}
|
|
55
|
+
visible_rows = modal_height - 4
|
|
56
|
+
max_scroll = [HELP_LINES.length - visible_rows, 0].max
|
|
57
|
+
scroll_offset = 0
|
|
58
|
+
|
|
59
|
+
loop do
|
|
60
|
+
draw_help_frame(geometry)
|
|
61
|
+
draw_help_lines(geometry, visible_rows, scroll_offset)
|
|
62
|
+
draw_help_scrollbar(geometry, visible_rows, scroll_offset, max_scroll)
|
|
63
|
+
|
|
64
|
+
refresh
|
|
65
|
+
|
|
66
|
+
case getch
|
|
67
|
+
when Curses::Key::DOWN, 'j'
|
|
68
|
+
scroll_offset = [scroll_offset + 1, max_scroll].min
|
|
69
|
+
when Curses::Key::UP, 'k'
|
|
70
|
+
scroll_offset = [scroll_offset - 1, 0].max
|
|
71
|
+
else
|
|
72
|
+
break
|
|
57
73
|
end
|
|
58
74
|
end
|
|
75
|
+
end
|
|
59
76
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
77
|
+
def draw_help_frame(geometry)
|
|
78
|
+
y = geometry[:y]
|
|
79
|
+
x = geometry[:x]
|
|
80
|
+
width = geometry[:width]
|
|
81
|
+
height = geometry[:height]
|
|
64
82
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
addstr('
|
|
68
|
-
end
|
|
69
|
-
attron(color_pair(5) | Curses::A_BOLD) do
|
|
70
|
-
addstr(' Key Mappings '.center(modal_width - 2))
|
|
71
|
-
end
|
|
72
|
-
attron(color_pair(4) | Curses::A_BOLD) do
|
|
73
|
-
addstr('│')
|
|
83
|
+
(y..(y + height)).each do |row|
|
|
84
|
+
setpos(row, x)
|
|
85
|
+
attron(color_pair(3)) { addstr(' ' * width) }
|
|
74
86
|
end
|
|
75
87
|
|
|
76
|
-
setpos(
|
|
77
|
-
attron(color_pair(4))
|
|
78
|
-
addstr("\u251C#{'─' * (modal_width - 2)}\u2524")
|
|
79
|
-
end
|
|
88
|
+
setpos(y, x)
|
|
89
|
+
attron(color_pair(4) | Curses::A_BOLD) { addstr("┌#{'─' * (width - 2)}┐") }
|
|
80
90
|
|
|
81
|
-
|
|
82
|
-
|
|
91
|
+
setpos(y + 1, x)
|
|
92
|
+
attron(color_pair(4) | Curses::A_BOLD) { addstr('│') }
|
|
93
|
+
attron(color_pair(5) | Curses::A_BOLD) { addstr(' Key Mappings '.center(width - 2)) }
|
|
94
|
+
attron(color_pair(4) | Curses::A_BOLD) { addstr('│') }
|
|
83
95
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
addstr('│ ')
|
|
87
|
-
end
|
|
96
|
+
setpos(y + 2, x)
|
|
97
|
+
attron(color_pair(4)) { addstr("├#{'─' * (width - 2)}┤") }
|
|
88
98
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
99
|
+
setpos(y + height - 1, x)
|
|
100
|
+
attron(color_pair(4) | Curses::A_BOLD) { addstr("└#{'─' * (width - 2)}┘") }
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def draw_help_lines(geometry, visible_rows, scroll_offset)
|
|
104
|
+
visible_rows.times do |row|
|
|
105
|
+
line = HELP_LINES[scroll_offset + row]
|
|
106
|
+
|
|
107
|
+
setpos(geometry[:y] + 3 + row, geometry[:x])
|
|
108
|
+
attron(color_pair(4)) { addstr('│ ') }
|
|
109
|
+
draw_help_line_text(line, geometry[:width])
|
|
110
|
+
attron(color_pair(4)) { addstr(' │') }
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def draw_help_line_text(line, width)
|
|
115
|
+
unless line
|
|
116
|
+
addstr(''.ljust(width - 4))
|
|
117
|
+
return
|
|
106
118
|
end
|
|
107
119
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
120
|
+
display_line = if line.length > width - 4
|
|
121
|
+
"#{line[0...(width - 7)]}..."
|
|
122
|
+
else
|
|
123
|
+
line.ljust(width - 4)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
if HELP_SECTION_HEADERS.include?(line)
|
|
127
|
+
attron(color_pair(1) | Curses::A_BOLD) { addstr(display_line) }
|
|
128
|
+
else
|
|
129
|
+
addstr(display_line)
|
|
111
130
|
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def draw_help_scrollbar(geometry, visible_rows, scroll_offset, max_scroll)
|
|
134
|
+
return unless max_scroll.positive?
|
|
135
|
+
|
|
136
|
+
scroll_pos = ((scroll_offset.to_f / max_scroll) * (visible_rows - 1)).round.clamp(0, visible_rows - 1)
|
|
112
137
|
|
|
113
|
-
|
|
114
|
-
|
|
138
|
+
setpos(geometry[:y] + 3 + scroll_pos, geometry[:x] + geometry[:width] - 2)
|
|
139
|
+
attron(color_pair(4) | Curses::A_BOLD) { addstr('█') }
|
|
115
140
|
end
|
|
116
141
|
end
|
|
117
142
|
end
|
data/lib/sergeant/rendering.rb
CHANGED
|
@@ -15,7 +15,10 @@ module Sergeant
|
|
|
15
15
|
# both panes stay readable; below that it just doesn't fit.
|
|
16
16
|
MIN_PREVIEW_TOTAL_WIDTH = 100
|
|
17
17
|
MIN_PREVIEW_WIDTH = 24
|
|
18
|
-
|
|
18
|
+
PREVIEW_WIDTH_RATIO_DEFAULT = 0.35
|
|
19
|
+
PREVIEW_WIDTH_RATIO_MIN = 0.15
|
|
20
|
+
PREVIEW_WIDTH_RATIO_MAX = 0.6
|
|
21
|
+
PREVIEW_WIDTH_RATIO_STEP = 0.05
|
|
19
22
|
|
|
20
23
|
# Maps file_category (Sergeant::Utils) to the color pair set up in
|
|
21
24
|
# Sergeant#apply_color_theme (pairs 7-10).
|
|
@@ -50,7 +53,7 @@ module Sergeant
|
|
|
50
53
|
|
|
51
54
|
preview_enabled = @show_preview && max_x >= MIN_PREVIEW_TOTAL_WIDTH
|
|
52
55
|
if preview_enabled
|
|
53
|
-
preview_width = [(max_x *
|
|
56
|
+
preview_width = [(max_x * @preview_width_ratio).to_i, MIN_PREVIEW_WIDTH].max
|
|
54
57
|
list_width = max_x - preview_width - 1
|
|
55
58
|
else
|
|
56
59
|
preview_width = 0
|
data/lib/sergeant/version.rb
CHANGED
data/lib/sergeant.rb
CHANGED
|
@@ -46,6 +46,7 @@ class SergeantApp
|
|
|
46
46
|
|
|
47
47
|
# Side preview panel
|
|
48
48
|
@show_preview = true
|
|
49
|
+
@preview_width_ratio = Sergeant::Rendering::PREVIEW_WIDTH_RATIO_DEFAULT
|
|
49
50
|
@last_preview_path = :unset
|
|
50
51
|
@preview_kind = :empty
|
|
51
52
|
@preview_item = nil
|
|
@@ -111,10 +112,14 @@ class SergeantApp
|
|
|
111
112
|
when 'P'
|
|
112
113
|
@show_preview = !@show_preview
|
|
113
114
|
@last_preview_path = :unset
|
|
115
|
+
when '['
|
|
116
|
+
step = Sergeant::Rendering::PREVIEW_WIDTH_RATIO_STEP
|
|
117
|
+
@preview_width_ratio = [@preview_width_ratio - step, Sergeant::Rendering::PREVIEW_WIDTH_RATIO_MIN].max
|
|
118
|
+
when ']'
|
|
119
|
+
step = Sergeant::Rendering::PREVIEW_WIDTH_RATIO_STEP
|
|
120
|
+
@preview_width_ratio = [@preview_width_ratio + step, Sergeant::Rendering::PREVIEW_WIDTH_RATIO_MAX].min
|
|
114
121
|
when 'e'
|
|
115
122
|
edit_file
|
|
116
|
-
when 'v'
|
|
117
|
-
preview_file
|
|
118
123
|
when 32, ' '
|
|
119
124
|
toggle_mark
|
|
120
125
|
when 'c'
|