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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 41cc1fef750cecd3e0a9a530976e8818955bc0db86a434319420dbe250496405
4
- data.tar.gz: 60db0f330203e559828d1cc87f701af503d174a6577bc351bfcc2fce2dc88752
3
+ metadata.gz: 37a2b99f4864cf2d4d4b4cffc4eb62a3c89a28216833084ad587bafd8b49c773
4
+ data.tar.gz: 2ddb28f7922812df52333b8bd49eef9dbec5c1e91c6d2523a72b3857fd7f9c4c
5
5
  SHA512:
6
- metadata.gz: 1b14250ce188e25a4c709b94ffcf16a88d57f4ceb715c737feaab66d1628fcbc041993ffc9e71c2e6496670e1f78846464932a804f392619608896f059aaf62f
7
- data.tar.gz: 696600fa8dbf40b884949ca2cd1bb4ab16c21b5f1ec947fa77acb7593274638cbb54fb41be80a40ec820981e164ac9eeddf0643c6e9f52e0335b91ccd1c84b60
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 |
@@ -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
- help_lines = [
13
- 'Navigation:',
14
- ' ↑/k - Move up',
15
- ' ↓/j - Move down',
16
- ' Enter / →/l - Open directory or preview file',
17
- ' ←/h - Go back to parent directory',
18
- '',
19
- 'File Operations:',
20
- ' Space - Mark/unmark item',
21
- ' c - Copy marked items',
22
- ' x - Cut marked items',
23
- ' p - Paste copied/cut items',
24
- ' d - Delete marked items',
25
- ' r - Rename current item',
26
- ' u - Unmark all items',
27
- ' n - Create new file or directory',
28
- '',
29
- 'View & Search:',
30
- ' e - Edit file ($EDITOR, nano, nvim, vim)',
31
- ' v - Preview file or archive contents',
32
- ' f - Filter current directory view',
33
- ' / - Search files (with fzf if available)',
34
- '',
35
- 'Other:',
36
- ' : - Execute terminal command',
37
- ' o - Toggle ownership display',
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
- setpos(modal_y, modal_x)
61
- attron(color_pair(4) | Curses::A_BOLD) do
62
- addstr("\u250C#{'─' * (modal_width - 2)}\u2510")
63
- end
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
- setpos(modal_y + 1, modal_x)
66
- attron(color_pair(4) | Curses::A_BOLD) do
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(modal_y + 2, modal_x)
77
- attron(color_pair(4)) do
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
- help_lines.each_with_index do |line, idx|
82
- break if idx >= modal_height - 4 # Stop if we run out of vertical space
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
- setpos(modal_y + 3 + idx, modal_x)
85
- attron(color_pair(4)) do
86
- addstr('│ ')
87
- end
96
+ setpos(y + 2, x)
97
+ attron(color_pair(4)) { addstr("├#{'─' * (width - 2)}┤") }
88
98
 
89
- # Truncate line if it's too long for the modal width
90
- display_line = if line.length > modal_width - 4
91
- "#{line[0...(modal_width - 7)]}..."
92
- else
93
- line.ljust(modal_width - 4)
94
- end
95
-
96
- if line.start_with?('Navigation:', 'File Operations:', 'Other:')
97
- attron(color_pair(1) | Curses::A_BOLD) do
98
- addstr(display_line)
99
- end
100
- else
101
- addstr(display_line)
102
- end
103
- attron(color_pair(4)) do
104
- addstr(' │')
105
- end
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
- setpos(modal_y + modal_height - 1, modal_x)
109
- attron(color_pair(4) | Curses::A_BOLD) do
110
- addstr("\u2514#{'─' * (modal_width - 2)}\u2518")
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
- refresh
114
- getch
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
@@ -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
- PREVIEW_WIDTH_RATIO = 0.35
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 * PREVIEW_WIDTH_RATIO).to_i, MIN_PREVIEW_WIDTH].max
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sergeant
4
- VERSION = '1.0.10'
4
+ VERSION = '1.0.11'
5
5
  end
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'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sergeant
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.10
4
+ version: 1.0.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mateusz Grotha