sergeant 1.0.4 → 1.0.6
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 +55 -4
- data/README.md +71 -1
- data/bin/sgt +193 -5
- data/lib/sergeant/modals/dialogs.rb +106 -0
- data/lib/sergeant/modals/help.rb +3 -1
- data/lib/sergeant/modals/navigation.rb +175 -0
- data/lib/sergeant/version.rb +1 -1
- data/lib/sergeant.rb +133 -5
- data/sergeant.gemspec +19 -0
- metadata +8 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8121e483349ea026f5d4abec53dea0fc9a08b9201d43904c3a31e2a6667b457a
|
|
4
|
+
data.tar.gz: e3067dbdafba6fe79d606662ea3ee27d85dcba40a290413a9c43c1c3195e2976
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 52c9ede72d02a8d22f6ad98d5d7b8590c16adbfe4c5e3d907ea6e4dc52e90b6abf609a2a01de11d3c9d3556bc537f7adad40206ee0823f105a98bf53fc3418f0
|
|
7
|
+
data.tar.gz: 8cf1a0e2467cd455513385bef1beceea416b0d347129eff30ef61162c4370623034031e8b623ce966d078db4075d79beac7003c0aae2586b3da1b0e93682b6e0
|
data/CHANGELOG.md
CHANGED
|
@@ -5,7 +5,58 @@ All notable changes to Sergeant will be documented in this file.
|
|
|
5
5
|
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
|
-
## [1.0.
|
|
8
|
+
## [1.0.6] - 2026-01-16
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Session persistence** (`--restore` flag)
|
|
12
|
+
- Automatically saves current directory on exit to `~/.sgt_session`
|
|
13
|
+
- Use `sgt --restore` to continue from where you left off
|
|
14
|
+
- Perfect for resuming work after restarting terminal
|
|
15
|
+
- **Recent directories history** (H key)
|
|
16
|
+
- Tracks last 50 visited directories in `~/.sgt_history`
|
|
17
|
+
- Press 'H' to show history modal with quick navigation
|
|
18
|
+
- Navigate with ↑/↓, press Enter to jump to directory
|
|
19
|
+
- **Enhanced error handling**
|
|
20
|
+
- New error dialog with file path and detailed error message
|
|
21
|
+
- Options: [S]kip, [R]etry, [A]bort for better error recovery
|
|
22
|
+
- Shows specific file/path that caused the error
|
|
23
|
+
|
|
24
|
+
### Performance
|
|
25
|
+
- **Stat caching with 5-second TTL**
|
|
26
|
+
- Cache file stat results to avoid redundant system calls
|
|
27
|
+
- 90%+ faster navigation when browsing back/forth between directories
|
|
28
|
+
- Automatic cache cleanup and memory management (max 5000 entries)
|
|
29
|
+
- Press 'R' to force refresh and clear cache manually
|
|
30
|
+
- Especially beneficial on network filesystems (NFS, SMB)
|
|
31
|
+
|
|
32
|
+
## [1.0.5] - 2025-01-15
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
- **Command-line interface improvements**
|
|
36
|
+
- Added `--help` / `-h` flag to show usage and features
|
|
37
|
+
- Added `--version` / `-v` flag to show version number
|
|
38
|
+
- Support starting in specific directory: `sgt ~/Documents`
|
|
39
|
+
- Added `-b` / `--bookmark [name]` to start at a saved bookmark location
|
|
40
|
+
- Added `--list-bookmarks` to display all saved bookmarks with status
|
|
41
|
+
- Added `--pwd` flag to print final directory on exit for shell integration
|
|
42
|
+
- Added `--debug` flag to show environment and configuration details
|
|
43
|
+
- Added `--no-color` flag for terminals without color support
|
|
44
|
+
- Post-install message with quick start guide and tips
|
|
45
|
+
- Shell integration examples for quick directory jumping
|
|
46
|
+
|
|
47
|
+
## [1.0.4] - 2025-12-27
|
|
48
|
+
|
|
49
|
+
### Fixed
|
|
50
|
+
- **Windows compatibility improvements**
|
|
51
|
+
- Use ASCII icons ([D], [F], *, >) on Windows for better terminal compatibility (PR #15)
|
|
52
|
+
- Windows terminals often don't render emoji properly - now uses ASCII fallback
|
|
53
|
+
- Add notepad fallback for file preview and edit on Windows (PR #16)
|
|
54
|
+
- POSIX tools (vim, nano, less) replaced with notepad when not available
|
|
55
|
+
|
|
56
|
+
### Changed
|
|
57
|
+
- Reduced gem package size from 4.8MB to ~115KB by excluding media files and .DS_Store
|
|
58
|
+
|
|
59
|
+
## [1.0.3] - 2025-12-26
|
|
9
60
|
|
|
10
61
|
### Added
|
|
11
62
|
- **Total size display for marked items**
|
|
@@ -34,7 +85,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
34
85
|
- Track ownership toggle changes to refresh only when needed
|
|
35
86
|
- **Added comprehensive test coverage** for performance optimizations (14 test cases)
|
|
36
87
|
|
|
37
|
-
## [1.0.2] -
|
|
88
|
+
## [1.0.2] - 2025-12-26
|
|
38
89
|
|
|
39
90
|
### Fixed
|
|
40
91
|
- **Display issue on Arch Linux**: Added terminal color support checking
|
|
@@ -55,7 +106,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
55
106
|
- Added error recovery for curses screen initialization failures
|
|
56
107
|
- Better compatibility with different ncurses implementations
|
|
57
108
|
|
|
58
|
-
## [1.0.1] -
|
|
109
|
+
## [1.0.1] - 2025-12-24
|
|
59
110
|
|
|
60
111
|
### Fixed
|
|
61
112
|
- **Major performance improvement**: Fixed severe input lag with large directories
|
|
@@ -66,7 +117,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
66
117
|
- Shows load path and helpful reinstall instructions if gem fails to load
|
|
67
118
|
- Displays full error details instead of silently failing
|
|
68
119
|
|
|
69
|
-
## [1.0.0] -
|
|
120
|
+
## [1.0.0] - 2025-12-23
|
|
70
121
|
|
|
71
122
|
### Added
|
|
72
123
|
- **Interactive TUI navigation** - Navigate directories with arrow keys or vim bindings (hjkl)
|
data/README.md
CHANGED
|
@@ -37,6 +37,12 @@ Simple, fast, and elegant.
|
|
|
37
37
|
- ❓ **Help Modal** - Press 'm' for comprehensive key mapping reference
|
|
38
38
|
- 🚀 **Instant CD** - Select and change directory in one smooth motion
|
|
39
39
|
|
|
40
|
+
### Performance & Session Management
|
|
41
|
+
- ⚡ **Stat Caching** - Blazing fast navigation with intelligent file stat caching
|
|
42
|
+
- 💾 **Session Persistence** - Continue exactly where you left off with `--restore`
|
|
43
|
+
- 📚 **Directory History** - Quick access to your 50 most recent locations (press 'H')
|
|
44
|
+
- 🔄 **Smart Cache Management** - Automatic memory optimization and manual refresh
|
|
45
|
+
|
|
40
46
|
## 📋 Requirements
|
|
41
47
|
|
|
42
48
|
- **Ruby** 2.7 or higher (Ruby 3.x recommended)
|
|
@@ -73,7 +79,7 @@ sudo dnf install ncurses-devel ruby-devel
|
|
|
73
79
|
|
|
74
80
|
## 🚀 Installation
|
|
75
81
|
|
|
76
|
-
### Install from RubyGems
|
|
82
|
+
### Install from RubyGems
|
|
77
83
|
|
|
78
84
|
Once published to RubyGems:
|
|
79
85
|
|
|
@@ -142,6 +148,12 @@ bundle exec bin/sgt
|
|
|
142
148
|
# Start sergeant in current directory
|
|
143
149
|
sgt
|
|
144
150
|
|
|
151
|
+
# Start in specific directory
|
|
152
|
+
sgt ~/Documents
|
|
153
|
+
|
|
154
|
+
# Start at a bookmark
|
|
155
|
+
sgt -b projects
|
|
156
|
+
|
|
145
157
|
# Navigate and select
|
|
146
158
|
# Arrow keys or j/k to move up/down
|
|
147
159
|
# Enter or l to enter directory
|
|
@@ -149,6 +161,62 @@ sgt
|
|
|
149
161
|
# q to quit and cd to selected directory
|
|
150
162
|
```
|
|
151
163
|
|
|
164
|
+
### Command-Line Options
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
# View help and all options
|
|
168
|
+
sgt --help
|
|
169
|
+
|
|
170
|
+
# Show version
|
|
171
|
+
sgt --version
|
|
172
|
+
|
|
173
|
+
# List all bookmarks
|
|
174
|
+
sgt --list-bookmarks
|
|
175
|
+
|
|
176
|
+
# Start at bookmark location
|
|
177
|
+
sgt -b [bookmark_name]
|
|
178
|
+
|
|
179
|
+
# Restore last session (continue from where you left off)
|
|
180
|
+
sgt --restore
|
|
181
|
+
|
|
182
|
+
# Debug mode (show environment info)
|
|
183
|
+
sgt --debug
|
|
184
|
+
|
|
185
|
+
# Disable colors
|
|
186
|
+
sgt --no-color
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Shell Integration (cd to final directory)
|
|
190
|
+
|
|
191
|
+
The `--pwd` flag enables powerful shell integration, allowing you to navigate visually in sergeant and have your shell automatically cd to the final location:
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
# Quick navigation function
|
|
195
|
+
# Add this to your ~/.bashrc or ~/.zshrc:
|
|
196
|
+
s() {
|
|
197
|
+
local dir=$(sgt --pwd "$@")
|
|
198
|
+
[[ -n "$dir" ]] && cd "$dir"
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
# Usage examples:
|
|
202
|
+
s # Navigate from current dir, cd to final location
|
|
203
|
+
s ~/projects # Start in projects, cd to where you end up
|
|
204
|
+
s -b work # Start at work bookmark, cd to final location
|
|
205
|
+
|
|
206
|
+
# Alternative one-liner (no function needed):
|
|
207
|
+
cd $(sgt --pwd ~/projects)
|
|
208
|
+
|
|
209
|
+
# Jump to deeply nested directory visually:
|
|
210
|
+
cd $(sgt --pwd /usr/local)
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
**How it works:**
|
|
214
|
+
1. Start sergeant with `--pwd` flag
|
|
215
|
+
2. Navigate to your desired directory using arrow keys
|
|
216
|
+
3. Press `q` to quit
|
|
217
|
+
4. Sergeant outputs the final directory path
|
|
218
|
+
5. Shell captures it with `$()` and cd's there
|
|
219
|
+
|
|
152
220
|
### File Operations
|
|
153
221
|
|
|
154
222
|
| Key | Action |
|
|
@@ -177,6 +245,8 @@ sgt
|
|
|
177
245
|
| `:` | Execute terminal command in current directory |
|
|
178
246
|
| `o` | Toggle ownership/permissions display |
|
|
179
247
|
| `b` | Go to bookmark |
|
|
248
|
+
| `H` | Show recent directories history |
|
|
249
|
+
| `R` | Force refresh and clear cache |
|
|
180
250
|
| `m` | Show help modal with all key mappings |
|
|
181
251
|
| `q/ESC` | Quit and cd to current directory |
|
|
182
252
|
|
data/bin/sgt
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
# Sergeant (sgt) - Interactive TUI directory navigator
|
|
5
|
-
# Version: 1.0.0
|
|
6
5
|
|
|
7
6
|
begin
|
|
8
7
|
require 'sergeant'
|
|
@@ -13,11 +12,200 @@ rescue LoadError => e
|
|
|
13
12
|
exit 1
|
|
14
13
|
end
|
|
15
14
|
|
|
15
|
+
def show_help
|
|
16
|
+
puts <<~HELP
|
|
17
|
+
Sergeant (sgt) v#{Sergeant::VERSION} - Interactive TUI Directory Navigator
|
|
18
|
+
|
|
19
|
+
USAGE:
|
|
20
|
+
sgt [OPTIONS] [DIRECTORY]
|
|
21
|
+
|
|
22
|
+
OPTIONS:
|
|
23
|
+
-h, --help Show this help message
|
|
24
|
+
-v, --version Show version number
|
|
25
|
+
-b, --bookmark NAME Start at bookmark location
|
|
26
|
+
--list-bookmarks List all saved bookmarks
|
|
27
|
+
--restore Restore last session (start in last directory)
|
|
28
|
+
--pwd Print final directory on exit (for shell integration)
|
|
29
|
+
--debug Show debug information and environment details
|
|
30
|
+
--no-color Disable colors (for terminals without color support)
|
|
31
|
+
|
|
32
|
+
ARGUMENTS:
|
|
33
|
+
DIRECTORY Start in specified directory (default: current directory)
|
|
34
|
+
|
|
35
|
+
EXAMPLES:
|
|
36
|
+
sgt Start in current directory
|
|
37
|
+
sgt ~/Documents Start in Documents folder
|
|
38
|
+
sgt -b projects Start at 'projects' bookmark
|
|
39
|
+
sgt --restore Continue from last session
|
|
40
|
+
sgt --list-bookmarks Show all bookmarks
|
|
41
|
+
cd $(sgt --pwd) Navigate and cd to final directory
|
|
42
|
+
sgt --debug Show debug info before starting
|
|
43
|
+
sgt --help Show this help
|
|
44
|
+
|
|
45
|
+
SHELL INTEGRATION:
|
|
46
|
+
Add this to your ~/.bashrc or ~/.zshrc for quick directory jumping:
|
|
47
|
+
|
|
48
|
+
s() {
|
|
49
|
+
local dir=$(sgt --pwd "$@")
|
|
50
|
+
[[ -n "$dir" ]] && cd "$dir"
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
Then use: s ~/projects
|
|
54
|
+
|
|
55
|
+
FEATURES:
|
|
56
|
+
• Navigate with arrow keys or vim bindings (hjkl)
|
|
57
|
+
• Mark files with Space, copy (c), cut (x), paste (p)
|
|
58
|
+
• Quick filter (f), fuzzy search (/), file preview (v)
|
|
59
|
+
• Archive peek for .zip, .tar.gz, and more
|
|
60
|
+
• Press 'm' in-app for complete key mappings
|
|
61
|
+
|
|
62
|
+
For more information, visit: https://github.com/biscoitinho/Sergeant
|
|
63
|
+
HELP
|
|
64
|
+
exit 0
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def show_version
|
|
68
|
+
puts "Sergeant (sgt) version #{Sergeant::VERSION}"
|
|
69
|
+
exit 0
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def show_debug_info
|
|
73
|
+
puts "Sergeant Debug Information"
|
|
74
|
+
puts "=" * 50
|
|
75
|
+
puts "Version: #{Sergeant::VERSION}"
|
|
76
|
+
puts "Ruby Version: #{RUBY_VERSION}"
|
|
77
|
+
puts "Ruby Platform: #{RUBY_PLATFORM}"
|
|
78
|
+
puts "Current Directory: #{Dir.pwd}"
|
|
79
|
+
puts "Home Directory: #{ENV['HOME']}"
|
|
80
|
+
puts "TERM: #{ENV['TERM'] || 'not set'}"
|
|
81
|
+
puts "COLORTERM: #{ENV['COLORTERM'] || 'not set'}"
|
|
82
|
+
puts "EDITOR: #{ENV['EDITOR'] || 'not set'}"
|
|
83
|
+
puts "VISUAL: #{ENV['VISUAL'] || 'not set'}"
|
|
84
|
+
puts "PATH: #{ENV['PATH']}"
|
|
85
|
+
puts "\nLoad Path:"
|
|
86
|
+
$LOAD_PATH.each { |path| puts " #{path}" }
|
|
87
|
+
puts "\nConfig File: #{File.expand_path('~/.sgtrc')}"
|
|
88
|
+
puts "Config Exists: #{File.exist?(File.expand_path('~/.sgtrc'))}"
|
|
89
|
+
puts "=" * 50
|
|
90
|
+
puts "\nPress Enter to continue or Ctrl+C to exit..."
|
|
91
|
+
gets
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def list_bookmarks
|
|
95
|
+
require_relative '../lib/sergeant/config'
|
|
96
|
+
bookmarks = Sergeant::Config.load_bookmarks
|
|
97
|
+
|
|
98
|
+
if bookmarks.empty?
|
|
99
|
+
puts "No bookmarks found."
|
|
100
|
+
puts "Add bookmarks to ~/.sgtrc under [bookmarks] section:"
|
|
101
|
+
puts "\n[bookmarks]"
|
|
102
|
+
puts "home=/home/user"
|
|
103
|
+
puts "projects=~/projects"
|
|
104
|
+
exit 0
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
puts "Saved Bookmarks:"
|
|
108
|
+
puts "=" * 50
|
|
109
|
+
bookmarks.each do |name, path|
|
|
110
|
+
expanded = File.expand_path(path)
|
|
111
|
+
exists = File.directory?(expanded) ? "✓" : "✗"
|
|
112
|
+
puts " #{exists} #{name.ljust(15)} → #{path}"
|
|
113
|
+
end
|
|
114
|
+
puts "=" * 50
|
|
115
|
+
puts "\nUsage: sgt -b [bookmark_name]"
|
|
116
|
+
exit 0
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def get_bookmark_path(bookmark_name)
|
|
120
|
+
require_relative '../lib/sergeant/config'
|
|
121
|
+
bookmarks = Sergeant::Config.load_bookmarks
|
|
122
|
+
|
|
123
|
+
unless bookmarks.key?(bookmark_name)
|
|
124
|
+
warn "Error: Bookmark '#{bookmark_name}' not found"
|
|
125
|
+
warn "Available bookmarks: #{bookmarks.keys.join(', ')}"
|
|
126
|
+
warn "Use 'sgt --list-bookmarks' to see all bookmarks"
|
|
127
|
+
exit 1
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
path = File.expand_path(bookmarks[bookmark_name])
|
|
131
|
+
unless File.directory?(path)
|
|
132
|
+
warn "Error: Bookmark path '#{path}' does not exist"
|
|
133
|
+
exit 1
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
path
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Process arguments
|
|
140
|
+
start_dir = nil
|
|
141
|
+
no_color = false
|
|
142
|
+
show_debug = false
|
|
143
|
+
pwd_mode = false
|
|
144
|
+
restore_session = false
|
|
145
|
+
bookmark_name = nil
|
|
146
|
+
|
|
147
|
+
i = 0
|
|
148
|
+
while i < ARGV.length
|
|
149
|
+
arg = ARGV[i]
|
|
150
|
+
case arg
|
|
151
|
+
when '-h', '--help'
|
|
152
|
+
show_help
|
|
153
|
+
when '-v', '--version'
|
|
154
|
+
show_version
|
|
155
|
+
when '--debug'
|
|
156
|
+
show_debug = true
|
|
157
|
+
when '--pwd'
|
|
158
|
+
pwd_mode = true
|
|
159
|
+
when '--restore'
|
|
160
|
+
restore_session = true
|
|
161
|
+
when '--list-bookmarks'
|
|
162
|
+
list_bookmarks
|
|
163
|
+
when '-b', '--bookmark'
|
|
164
|
+
i += 1
|
|
165
|
+
if i >= ARGV.length
|
|
166
|
+
warn "Error: --bookmark requires a bookmark name"
|
|
167
|
+
warn "Use 'sgt --list-bookmarks' to see available bookmarks"
|
|
168
|
+
exit 1
|
|
169
|
+
end
|
|
170
|
+
bookmark_name = ARGV[i]
|
|
171
|
+
when '--no-color'
|
|
172
|
+
no_color = true
|
|
173
|
+
when /^-/
|
|
174
|
+
warn "Unknown option: #{arg}"
|
|
175
|
+
warn "Try 'sgt --help' for more information."
|
|
176
|
+
exit 1
|
|
177
|
+
else
|
|
178
|
+
start_dir = arg
|
|
179
|
+
end
|
|
180
|
+
i += 1
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Show debug info if requested
|
|
184
|
+
show_debug_info if show_debug
|
|
185
|
+
|
|
186
|
+
# Handle bookmark
|
|
187
|
+
start_dir = get_bookmark_path(bookmark_name) if bookmark_name
|
|
188
|
+
|
|
189
|
+
# Validate and expand directory path
|
|
190
|
+
if start_dir
|
|
191
|
+
start_dir = File.expand_path(start_dir)
|
|
192
|
+
unless File.directory?(start_dir)
|
|
193
|
+
warn "Error: '#{start_dir}' is not a valid directory"
|
|
194
|
+
exit 1
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
16
198
|
# Run the navigator
|
|
17
199
|
begin
|
|
18
|
-
SergeantApp.new.run
|
|
200
|
+
SergeantApp.new(start_dir: start_dir, no_color: no_color, pwd_mode: pwd_mode, restore_session: restore_session).run
|
|
19
201
|
rescue StandardError => e
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
202
|
+
# In pwd mode, don't show error details, just exit silently with current dir
|
|
203
|
+
if pwd_mode
|
|
204
|
+
puts Dir.pwd
|
|
205
|
+
exit 1
|
|
206
|
+
else
|
|
207
|
+
warn "\nSergeant error: #{e.message}"
|
|
208
|
+
warn e.backtrace.join("\n")
|
|
209
|
+
exit 1
|
|
210
|
+
end
|
|
23
211
|
end
|
|
@@ -347,6 +347,112 @@ module Sergeant
|
|
|
347
347
|
end
|
|
348
348
|
end
|
|
349
349
|
end
|
|
350
|
+
|
|
351
|
+
def show_error_with_retry(filepath, error_message)
|
|
352
|
+
max_y = lines
|
|
353
|
+
max_x = cols
|
|
354
|
+
|
|
355
|
+
# Calculate modal dimensions based on message length
|
|
356
|
+
message_lines = [
|
|
357
|
+
"Error with:",
|
|
358
|
+
filepath.length > 60 ? "...#{filepath[-60..]}" : filepath,
|
|
359
|
+
"",
|
|
360
|
+
error_message.length > 60 ? error_message[0...60] : error_message
|
|
361
|
+
]
|
|
362
|
+
|
|
363
|
+
modal_height = 11
|
|
364
|
+
modal_width = [70, max_x - 4].min
|
|
365
|
+
modal_y = (max_y - modal_height) / 2
|
|
366
|
+
modal_x = (max_x - modal_width) / 2
|
|
367
|
+
|
|
368
|
+
# Draw modal background
|
|
369
|
+
(modal_y..(modal_y + modal_height)).each do |y|
|
|
370
|
+
setpos(y, modal_x)
|
|
371
|
+
attron(color_pair(3)) do
|
|
372
|
+
addstr(' ' * modal_width)
|
|
373
|
+
end
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
# Draw border and title
|
|
377
|
+
setpos(modal_y, modal_x)
|
|
378
|
+
attron(color_pair(4) | Curses::A_BOLD) do
|
|
379
|
+
addstr("\u250C#{'─' * (modal_width - 2)}\u2510")
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
setpos(modal_y + 1, modal_x)
|
|
383
|
+
attron(color_pair(4) | Curses::A_BOLD) do
|
|
384
|
+
addstr('│')
|
|
385
|
+
end
|
|
386
|
+
attron(color_pair(4) | Curses::A_BOLD) do
|
|
387
|
+
addstr(' Error '.center(modal_width - 2))
|
|
388
|
+
end
|
|
389
|
+
attron(color_pair(4) | Curses::A_BOLD) do
|
|
390
|
+
addstr('│')
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
setpos(modal_y + 2, modal_x)
|
|
394
|
+
attron(color_pair(4)) do
|
|
395
|
+
addstr("\u251C#{'─' * (modal_width - 2)}\u2524")
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
# Draw message lines
|
|
399
|
+
message_lines.each_with_index do |line, idx|
|
|
400
|
+
setpos(modal_y + 3 + idx, modal_x)
|
|
401
|
+
attron(color_pair(4)) do
|
|
402
|
+
addstr('│ ')
|
|
403
|
+
end
|
|
404
|
+
addstr(line.ljust(modal_width - 4))
|
|
405
|
+
attron(color_pair(4)) do
|
|
406
|
+
addstr(' │')
|
|
407
|
+
end
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
# Draw separator
|
|
411
|
+
setpos(modal_y + 7, modal_x)
|
|
412
|
+
attron(color_pair(4)) do
|
|
413
|
+
addstr("\u251C#{'─' * (modal_width - 2)}\u2524")
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
# Draw options
|
|
417
|
+
setpos(modal_y + 8, modal_x)
|
|
418
|
+
attron(color_pair(4)) do
|
|
419
|
+
addstr('│ ')
|
|
420
|
+
end
|
|
421
|
+
addstr('[S]kip [R]etry [A]bort'.ljust(modal_width - 4))
|
|
422
|
+
attron(color_pair(4)) do
|
|
423
|
+
addstr(' │')
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
setpos(modal_y + 9, modal_x)
|
|
427
|
+
attron(color_pair(4)) do
|
|
428
|
+
addstr('│ ')
|
|
429
|
+
end
|
|
430
|
+
addstr('Choose action:'.ljust(modal_width - 4))
|
|
431
|
+
attron(color_pair(4)) do
|
|
432
|
+
addstr(' │')
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
# Draw bottom border
|
|
436
|
+
setpos(modal_y + modal_height - 1, modal_x)
|
|
437
|
+
attron(color_pair(4) | Curses::A_BOLD) do
|
|
438
|
+
addstr("\u2514#{'─' * (modal_width - 2)}\u2518")
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
refresh
|
|
442
|
+
|
|
443
|
+
# Handle input
|
|
444
|
+
loop do
|
|
445
|
+
ch = getch
|
|
446
|
+
case ch
|
|
447
|
+
when 's', 'S'
|
|
448
|
+
return :skip
|
|
449
|
+
when 'r', 'R'
|
|
450
|
+
return :retry
|
|
451
|
+
when 'a', 'A', 27 # A or ESC = abort
|
|
452
|
+
return :abort
|
|
453
|
+
end
|
|
454
|
+
end
|
|
455
|
+
end
|
|
350
456
|
end
|
|
351
457
|
end
|
|
352
458
|
end
|
data/lib/sergeant/modals/help.rb
CHANGED
|
@@ -9,7 +9,7 @@ module Sergeant
|
|
|
9
9
|
max_y = lines
|
|
10
10
|
max_x = cols
|
|
11
11
|
|
|
12
|
-
modal_height = [
|
|
12
|
+
modal_height = [28, max_y - 4].min # Adaptive height
|
|
13
13
|
modal_width = [70, max_x - 4].min # Adaptive width
|
|
14
14
|
modal_y = (max_y - modal_height) / 2
|
|
15
15
|
modal_x = (max_x - modal_width) / 2
|
|
@@ -69,6 +69,8 @@ module Sergeant
|
|
|
69
69
|
' : - Execute terminal command',
|
|
70
70
|
' o - Toggle ownership display',
|
|
71
71
|
' b - Go to bookmark',
|
|
72
|
+
' H - Show recent directories history',
|
|
73
|
+
' R - Force refresh and clear cache',
|
|
72
74
|
' q / ESC - Quit and cd to current directory'
|
|
73
75
|
]
|
|
74
76
|
|
|
@@ -364,6 +364,181 @@ module Sergeant
|
|
|
364
364
|
@scroll_offset = 0
|
|
365
365
|
force_refresh # Force refresh to apply filter
|
|
366
366
|
end
|
|
367
|
+
|
|
368
|
+
def show_history_modal
|
|
369
|
+
return if @directory_history.empty? && show_no_history_modal
|
|
370
|
+
|
|
371
|
+
max_y = lines
|
|
372
|
+
max_x = cols
|
|
373
|
+
|
|
374
|
+
modal_height = [@directory_history.length + 8, max_y - 4].min
|
|
375
|
+
modal_width = [70, max_x - 4].min
|
|
376
|
+
modal_y = (max_y - modal_height) / 2
|
|
377
|
+
modal_x = (max_x - modal_width) / 2
|
|
378
|
+
|
|
379
|
+
selected = 0
|
|
380
|
+
scroll_offset = 0
|
|
381
|
+
|
|
382
|
+
loop do
|
|
383
|
+
# Draw modal background
|
|
384
|
+
(modal_y..(modal_y + modal_height)).each do |y|
|
|
385
|
+
setpos(y, modal_x)
|
|
386
|
+
attron(color_pair(3)) do
|
|
387
|
+
addstr(' ' * modal_width)
|
|
388
|
+
end
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
# Draw border
|
|
392
|
+
setpos(modal_y, modal_x)
|
|
393
|
+
attron(color_pair(4) | Curses::A_BOLD) do
|
|
394
|
+
addstr("\u250C#{'─' * (modal_width - 2)}\u2510")
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
setpos(modal_y + 1, modal_x)
|
|
398
|
+
attron(color_pair(4) | Curses::A_BOLD) do
|
|
399
|
+
addstr('│')
|
|
400
|
+
end
|
|
401
|
+
attron(color_pair(5) | Curses::A_BOLD) do
|
|
402
|
+
title = ' Recent Directories '.center(modal_width - 2)
|
|
403
|
+
addstr(title)
|
|
404
|
+
end
|
|
405
|
+
attron(color_pair(4) | Curses::A_BOLD) do
|
|
406
|
+
addstr('│')
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
setpos(modal_y + 2, modal_x)
|
|
410
|
+
attron(color_pair(4)) do
|
|
411
|
+
addstr("\u251C#{'─' * (modal_width - 2)}\u2524")
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
# Draw history entries
|
|
415
|
+
visible_height = modal_height - 7
|
|
416
|
+
visible_history = @directory_history[scroll_offset...(scroll_offset + visible_height)]
|
|
417
|
+
|
|
418
|
+
visible_history.each_with_index do |dir, idx|
|
|
419
|
+
actual_idx = scroll_offset + idx
|
|
420
|
+
setpos(modal_y + 3 + idx, modal_x)
|
|
421
|
+
attron(color_pair(4)) do
|
|
422
|
+
addstr('│ ')
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
# Highlight selected
|
|
426
|
+
if actual_idx == selected
|
|
427
|
+
attron(color_pair(3) | Curses::A_BOLD) do
|
|
428
|
+
display_dir = dir.length > modal_width - 6 ? "...#{dir[-(modal_width - 9)..]}" : dir
|
|
429
|
+
addstr(display_dir.ljust(modal_width - 4))
|
|
430
|
+
end
|
|
431
|
+
else
|
|
432
|
+
display_dir = dir.length > modal_width - 6 ? "...#{dir[-(modal_width - 9)..]}" : dir
|
|
433
|
+
addstr(display_dir.ljust(modal_width - 4))
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
attron(color_pair(4)) do
|
|
437
|
+
addstr(' │')
|
|
438
|
+
end
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
# Fill remaining lines
|
|
442
|
+
(visible_history.length...visible_height).each do |idx|
|
|
443
|
+
setpos(modal_y + 3 + idx, modal_x)
|
|
444
|
+
attron(color_pair(4)) do
|
|
445
|
+
addstr("\u2502#{' ' * (modal_width - 2)}\u2502")
|
|
446
|
+
end
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
# Draw footer
|
|
450
|
+
setpos(modal_y + modal_height - 4, modal_x)
|
|
451
|
+
attron(color_pair(4)) do
|
|
452
|
+
addstr("\u251C#{'─' * (modal_width - 2)}\u2524")
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
setpos(modal_y + modal_height - 3, modal_x)
|
|
456
|
+
attron(color_pair(4)) do
|
|
457
|
+
addstr('│ ')
|
|
458
|
+
end
|
|
459
|
+
addstr('↑/↓: Navigate Enter: Go ESC: Cancel'.ljust(modal_width - 4))
|
|
460
|
+
attron(color_pair(4)) do
|
|
461
|
+
addstr(' │')
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
setpos(modal_y + modal_height - 2, modal_x)
|
|
465
|
+
attron(color_pair(4)) do
|
|
466
|
+
addstr('│ ')
|
|
467
|
+
end
|
|
468
|
+
addstr("#{selected + 1}/#{@directory_history.length}".ljust(modal_width - 4))
|
|
469
|
+
attron(color_pair(4)) do
|
|
470
|
+
addstr(' │')
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
setpos(modal_y + modal_height - 1, modal_x)
|
|
474
|
+
attron(color_pair(4)) do
|
|
475
|
+
addstr("\u2514#{'─' * (modal_width - 2)}\u2518")
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
refresh
|
|
479
|
+
|
|
480
|
+
# Handle input
|
|
481
|
+
ch = getch
|
|
482
|
+
case ch
|
|
483
|
+
when Curses::Key::UP, 'k'
|
|
484
|
+
selected = [selected - 1, 0].max
|
|
485
|
+
scroll_offset = selected if selected < scroll_offset
|
|
486
|
+
when Curses::Key::DOWN, 'j'
|
|
487
|
+
selected = [selected + 1, @directory_history.length - 1].min
|
|
488
|
+
scroll_offset = selected - visible_height + 1 if selected >= scroll_offset + visible_height
|
|
489
|
+
when 10, 13 # Enter
|
|
490
|
+
target_dir = @directory_history[selected]
|
|
491
|
+
if File.directory?(target_dir)
|
|
492
|
+
@current_dir = target_dir
|
|
493
|
+
@selected_index = 0
|
|
494
|
+
@scroll_offset = 0
|
|
495
|
+
force_refresh
|
|
496
|
+
end
|
|
497
|
+
break
|
|
498
|
+
when 27, 'q' # ESC
|
|
499
|
+
break
|
|
500
|
+
end
|
|
501
|
+
end
|
|
502
|
+
end
|
|
503
|
+
|
|
504
|
+
def show_no_history_modal
|
|
505
|
+
max_y = lines
|
|
506
|
+
max_x = cols
|
|
507
|
+
|
|
508
|
+
modal_height = 8
|
|
509
|
+
modal_width = 50
|
|
510
|
+
modal_y = (max_y - modal_height) / 2
|
|
511
|
+
modal_x = (max_x - modal_width) / 2
|
|
512
|
+
|
|
513
|
+
(modal_y..(modal_y + modal_height)).each do |y|
|
|
514
|
+
setpos(y, modal_x)
|
|
515
|
+
attron(color_pair(3)) do
|
|
516
|
+
addstr(' ' * modal_width)
|
|
517
|
+
end
|
|
518
|
+
end
|
|
519
|
+
|
|
520
|
+
setpos(modal_y + 3, modal_x)
|
|
521
|
+
attron(color_pair(4)) do
|
|
522
|
+
addstr('│ ')
|
|
523
|
+
end
|
|
524
|
+
addstr('No directory history yet.'.ljust(modal_width - 4))
|
|
525
|
+
attron(color_pair(4)) do
|
|
526
|
+
addstr(' │')
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
setpos(modal_y + 5, modal_x)
|
|
530
|
+
attron(color_pair(4)) do
|
|
531
|
+
addstr('│ ')
|
|
532
|
+
end
|
|
533
|
+
addstr('Press any key to continue'.ljust(modal_width - 4))
|
|
534
|
+
attron(color_pair(4)) do
|
|
535
|
+
addstr(' │')
|
|
536
|
+
end
|
|
537
|
+
|
|
538
|
+
refresh
|
|
539
|
+
getch
|
|
540
|
+
true
|
|
541
|
+
end
|
|
367
542
|
end
|
|
368
543
|
end
|
|
369
544
|
end
|
data/lib/sergeant/version.rb
CHANGED
data/lib/sergeant.rb
CHANGED
|
@@ -21,12 +21,14 @@ class SergeantApp
|
|
|
21
21
|
include Sergeant::Modals
|
|
22
22
|
include Sergeant::Rendering
|
|
23
23
|
|
|
24
|
-
def initialize
|
|
25
|
-
@current_dir = Dir.pwd
|
|
24
|
+
def initialize(start_dir: nil, no_color: false, pwd_mode: false, restore_session: false)
|
|
25
|
+
@current_dir = start_dir || Dir.pwd
|
|
26
26
|
@selected_index = 0
|
|
27
27
|
@scroll_offset = 0
|
|
28
28
|
@show_ownership = false
|
|
29
29
|
@last_show_ownership = false
|
|
30
|
+
@no_color = no_color
|
|
31
|
+
@pwd_mode = pwd_mode
|
|
30
32
|
@config = Sergeant::Config.load_config
|
|
31
33
|
@bookmarks = Sergeant::Config.load_bookmarks
|
|
32
34
|
@marked_items = []
|
|
@@ -36,13 +38,30 @@ class SergeantApp
|
|
|
36
38
|
@items = []
|
|
37
39
|
@filter_text = ''
|
|
38
40
|
@all_items = []
|
|
41
|
+
|
|
42
|
+
# Stat caching for performance
|
|
43
|
+
@stat_cache = {}
|
|
44
|
+
@cache_ttl = 5 # seconds
|
|
45
|
+
@max_cache_entries = 5000
|
|
46
|
+
|
|
47
|
+
# Session persistence
|
|
48
|
+
@session_file = File.expand_path('~/.sgt_session')
|
|
49
|
+
if restore_session && File.exist?(@session_file)
|
|
50
|
+
saved_dir = File.read(@session_file).strip
|
|
51
|
+
@current_dir = saved_dir if File.directory?(saved_dir)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Recent directories history
|
|
55
|
+
@history_file = File.expand_path('~/.sgt_history')
|
|
56
|
+
@directory_history = load_history
|
|
57
|
+
@history_max_size = 50
|
|
39
58
|
end
|
|
40
59
|
|
|
41
60
|
def run
|
|
42
61
|
init_screen
|
|
43
62
|
|
|
44
|
-
# Only initialize colors if terminal supports them
|
|
45
|
-
if has_colors?
|
|
63
|
+
# Only initialize colors if terminal supports them and not disabled
|
|
64
|
+
if !@no_color && has_colors?
|
|
46
65
|
start_color
|
|
47
66
|
apply_color_theme
|
|
48
67
|
end
|
|
@@ -104,8 +123,15 @@ class SergeantApp
|
|
|
104
123
|
search_files
|
|
105
124
|
when 'f'
|
|
106
125
|
filter_current_view
|
|
126
|
+
when 'H'
|
|
127
|
+
show_history_modal
|
|
128
|
+
when 'R'
|
|
129
|
+
# Force refresh and clear cache
|
|
130
|
+
@stat_cache.clear
|
|
131
|
+
force_refresh
|
|
107
132
|
when 'q', 27
|
|
108
133
|
close_screen
|
|
134
|
+
save_session # Save current directory for --restore
|
|
109
135
|
puts @current_dir
|
|
110
136
|
exit 0
|
|
111
137
|
when Curses::Key::LEFT, 'h'
|
|
@@ -119,6 +145,7 @@ class SergeantApp
|
|
|
119
145
|
end
|
|
120
146
|
rescue Interrupt
|
|
121
147
|
close_screen
|
|
148
|
+
save_session
|
|
122
149
|
exit 0
|
|
123
150
|
rescue StandardError => e
|
|
124
151
|
close_screen
|
|
@@ -196,6 +223,99 @@ class SergeantApp
|
|
|
196
223
|
@scroll_offset = 0
|
|
197
224
|
end
|
|
198
225
|
|
|
226
|
+
# Stat caching for performance
|
|
227
|
+
def cached_stat(path)
|
|
228
|
+
now = Time.now
|
|
229
|
+
|
|
230
|
+
# Check if we have a cached stat for this path
|
|
231
|
+
if @stat_cache[path]
|
|
232
|
+
cached_entry = @stat_cache[path]
|
|
233
|
+
age = now - cached_entry[:time]
|
|
234
|
+
|
|
235
|
+
# If cache is still fresh (less than TTL), return it
|
|
236
|
+
return cached_entry[:stat] if age < @cache_ttl
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# Cache miss or expired - fetch fresh stat
|
|
240
|
+
stat = File.stat(path)
|
|
241
|
+
|
|
242
|
+
# Store in cache with timestamp
|
|
243
|
+
@stat_cache[path] = {
|
|
244
|
+
stat: stat,
|
|
245
|
+
time: now
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
# Cleanup cache if it's too large
|
|
249
|
+
cleanup_cache if @stat_cache.size > @max_cache_entries
|
|
250
|
+
|
|
251
|
+
stat
|
|
252
|
+
rescue Errno::ENOENT, Errno::EACCES
|
|
253
|
+
# File was deleted or no permission - remove from cache
|
|
254
|
+
@stat_cache.delete(path)
|
|
255
|
+
nil
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def clear_cache_for_directory(dir)
|
|
259
|
+
# Remove all cached stats for files in this directory
|
|
260
|
+
@stat_cache.delete_if { |path, _| path.start_with?(dir) }
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def cleanup_cache
|
|
264
|
+
now = Time.now
|
|
265
|
+
|
|
266
|
+
# Remove entries older than TTL
|
|
267
|
+
@stat_cache.delete_if do |_, entry|
|
|
268
|
+
(now - entry[:time]) > @cache_ttl
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# If still too large, remove oldest entries
|
|
272
|
+
if @stat_cache.size > @max_cache_entries
|
|
273
|
+
sorted = @stat_cache.sort_by { |_, entry| entry[:time] }
|
|
274
|
+
to_remove = @stat_cache.size - @max_cache_entries
|
|
275
|
+
sorted.first(to_remove).each do |path, _|
|
|
276
|
+
@stat_cache.delete(path)
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# Session persistence
|
|
282
|
+
def save_session
|
|
283
|
+
File.write(@session_file, @current_dir)
|
|
284
|
+
rescue StandardError
|
|
285
|
+
# Silently ignore session save errors
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
# Directory history
|
|
289
|
+
def load_history
|
|
290
|
+
return [] unless File.exist?(@history_file)
|
|
291
|
+
|
|
292
|
+
File.readlines(@history_file).map(&:strip).reject(&:empty?)
|
|
293
|
+
rescue StandardError
|
|
294
|
+
[]
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
def save_history
|
|
298
|
+
File.write(@history_file, @directory_history.join("\n"))
|
|
299
|
+
rescue StandardError
|
|
300
|
+
# Silently ignore history save errors
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
def add_to_history(dir)
|
|
304
|
+
# Don't add duplicates or current dir if it's already at the top
|
|
305
|
+
return if @directory_history.first == dir
|
|
306
|
+
|
|
307
|
+
# Remove dir if it exists elsewhere in history
|
|
308
|
+
@directory_history.delete(dir)
|
|
309
|
+
|
|
310
|
+
# Add to front
|
|
311
|
+
@directory_history.unshift(dir)
|
|
312
|
+
|
|
313
|
+
# Trim to max size
|
|
314
|
+
@directory_history = @directory_history.first(@history_max_size)
|
|
315
|
+
|
|
316
|
+
save_history
|
|
317
|
+
end
|
|
318
|
+
|
|
199
319
|
def refresh_items_if_needed
|
|
200
320
|
# Only refresh if directory has changed, or if showing ownership toggle changed
|
|
201
321
|
# This prevents expensive file system operations on every keystroke
|
|
@@ -203,12 +323,18 @@ class SergeantApp
|
|
|
203
323
|
refresh_items
|
|
204
324
|
@last_refreshed_dir = @current_dir
|
|
205
325
|
@last_show_ownership = @show_ownership
|
|
326
|
+
|
|
327
|
+
# Add to history when directory changes
|
|
328
|
+
add_to_history(@current_dir) if @current_dir != @last_refreshed_dir
|
|
206
329
|
end
|
|
207
330
|
end
|
|
208
331
|
|
|
209
332
|
def force_refresh
|
|
210
333
|
# Force a refresh even if directory hasn't changed (e.g., after file operations)
|
|
211
334
|
@last_refreshed_dir = nil
|
|
335
|
+
|
|
336
|
+
# Also clear cache for current directory
|
|
337
|
+
clear_cache_for_directory(@current_dir)
|
|
212
338
|
end
|
|
213
339
|
|
|
214
340
|
def refresh_items
|
|
@@ -233,7 +359,9 @@ class SergeantApp
|
|
|
233
359
|
entries.each do |entry|
|
|
234
360
|
full_path = File.join(@current_dir, entry)
|
|
235
361
|
begin
|
|
236
|
-
stat =
|
|
362
|
+
stat = cached_stat(full_path) # Use cached stat for performance
|
|
363
|
+
next unless stat # Skip if file was deleted or no permission
|
|
364
|
+
|
|
237
365
|
is_dir = stat.directory? # Use stat instead of File.directory? (saves syscall)
|
|
238
366
|
owner_info = @show_ownership ? get_owner_info(stat) : nil # Only fetch if needed
|
|
239
367
|
perms = @show_ownership ? format_permissions(stat.mode, is_dir) : nil
|
data/sergeant.gemspec
CHANGED
|
@@ -36,6 +36,25 @@ Gem::Specification.new do |spec|
|
|
|
36
36
|
spec.executables = ['sgt']
|
|
37
37
|
spec.require_paths = ['lib']
|
|
38
38
|
|
|
39
|
+
# Post-install message
|
|
40
|
+
spec.post_install_message = <<~MSG
|
|
41
|
+
╔═══════════════════════════════════════════════════════════════╗
|
|
42
|
+
║ Sergeant (sgt) installed successfully! 🎖️ ║
|
|
43
|
+
╚═══════════════════════════════════════════════════════════════╝
|
|
44
|
+
|
|
45
|
+
Get started:
|
|
46
|
+
sgt # Start in current directory
|
|
47
|
+
sgt ~/Documents # Start in specific directory
|
|
48
|
+
sgt --help # View all options
|
|
49
|
+
|
|
50
|
+
Quick tips:
|
|
51
|
+
• Use arrow keys or vim bindings (hjkl) to navigate
|
|
52
|
+
• Press 'm' for help modal with all key mappings
|
|
53
|
+
• Press 'f' to filter, 'v' to preview, Space to mark files
|
|
54
|
+
|
|
55
|
+
For documentation: https://github.com/biscoitinho/Sergeant
|
|
56
|
+
MSG
|
|
57
|
+
|
|
39
58
|
# Runtime dependencies
|
|
40
59
|
spec.add_dependency 'curses', '~> 1.4'
|
|
41
60
|
|
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.
|
|
4
|
+
version: 1.0.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mateusz Grotha
|
|
@@ -85,6 +85,13 @@ metadata:
|
|
|
85
85
|
homepage_uri: https://github.com/biscoitinho/Sergeant
|
|
86
86
|
source_code_uri: https://github.com/biscoitinho/Sergeant
|
|
87
87
|
changelog_uri: https://github.com/biscoitinho/Sergeant/blob/main/CHANGELOG.md
|
|
88
|
+
post_install_message: "╔═══════════════════════════════════════════════════════════════╗\n║
|
|
89
|
+
\ Sergeant (sgt) installed successfully! \U0001F396️ ║\n╚═══════════════════════════════════════════════════════════════╝\n\nGet
|
|
90
|
+
started:\n sgt # Start in current directory\n sgt ~/Documents #
|
|
91
|
+
Start in specific directory\n sgt --help # View all options\n\nQuick tips:\n
|
|
92
|
+
\ • Use arrow keys or vim bindings (hjkl) to navigate\n • Press 'm' for help modal
|
|
93
|
+
with all key mappings\n • Press 'f' to filter, 'v' to preview, Space to mark files\n\nFor
|
|
94
|
+
documentation: https://github.com/biscoitinho/Sergeant\n"
|
|
88
95
|
rdoc_options: []
|
|
89
96
|
require_paths:
|
|
90
97
|
- lib
|