rufio 1.3.0 → 1.4.1

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.
data/info/keybindings.md CHANGED
@@ -9,22 +9,26 @@ Complete reference of all keyboard shortcuts in **rufio**.
9
9
  | `j` / `Down` | Move down |
10
10
  | `k` / `Up` | Move up |
11
11
  | `h` / `Left` | Go to parent directory |
12
- | `l` / `Right` / `Enter` | Enter directory or open file |
12
+ | `l` / `Right` / `Enter` | Enter directory |
13
13
  | `g` | Jump to top of list |
14
- | `G` | Jump to bottom of list |
14
+ | `}` | Jump to bottom of list |
15
15
 
16
16
  ## File Operations
17
17
 
18
18
  | Key | Action |
19
19
  |-----|--------|
20
20
  | `o` | Open file with external editor |
21
- | `e` | Open in file explorer (system default) |
21
+ | `e` | Open current directory in file explorer |
22
22
  | `R` | Refresh directory contents |
23
23
  | `r` | Rename file or directory |
24
24
  | `d` | Delete file or directory (with confirmation) |
25
25
  | `a` | Create new file |
26
26
  | `A` | Create new directory |
27
27
 
28
+ `.zip` previews list the archive entries (size, method, modified time, name) without extracting them.
29
+
30
+ Text previews read at most the first 64 KiB. PDF bodies are not previewed, keeping file navigation responsive.
31
+
28
32
  ## File Selection
29
33
 
30
34
  | Key | Action |
@@ -33,7 +37,9 @@ Complete reference of all keyboard shortcuts in **rufio**.
33
37
  | `m` | Move selected items to current directory |
34
38
  | `c` | Copy selected items to current directory |
35
39
  | `x` | Delete selected items |
36
- | `z` | Extract selected `.zip` files / compress selected items into a `.zip` |
40
+ | `z` | Extract selected `.zip` files (each into `<name>/`) / compress selected items into one `.zip` |
41
+
42
+ ZIP extraction and compression run as background jobs. Check progress in Jobs mode (`J`).
37
43
 
38
44
  ## Filtering & Search
39
45
 
@@ -41,7 +47,9 @@ Complete reference of all keyboard shortcuts in **rufio**.
41
47
  |-----|--------|
42
48
  | `f` | Enter filter mode (type to filter) |
43
49
  | `s` | Search file names with fzf |
50
+ | `/` | Search file names with fzf (same as `s`) |
44
51
  | `F` | Search file contents with rga |
52
+ | `G` | Global search: file names or contents across all bookmarks |
45
53
 
46
54
  ## Bookmarks & Navigation
47
55
 
@@ -51,31 +59,33 @@ Complete reference of all keyboard shortcuts in **rufio**.
51
59
  | `B` | Bookmark menu (view/add/remove/script paths) |
52
60
  | `0` | Jump to startup directory |
53
61
  | `1-9` | Jump to bookmark 1-9 |
62
+ | `Tab` | Cycle to next bookmark (Files mode) |
63
+ | `Shift+Tab` | Cycle to previous bookmark (Files mode) |
54
64
  | `z` | Navigate using zoxide history (when nothing is selected) |
55
65
 
56
- ## Command Mode & Tool Runtime
66
+ ## Modes & Tool Runtime
57
67
 
58
68
  | Key | Action |
59
69
  |-----|--------|
60
70
  | `:` | Enter command mode |
71
+ | `G` | Global search mode |
72
+ | `C` | Config mode: open `~/.config/rufio/` |
61
73
  | `J` | Job mode (view background jobs) |
62
74
  | `L` | View execution logs |
63
-
64
- ## Mode Switching
65
-
66
- | Key | Action |
67
- |-----|--------|
68
- | `Tab` | Switch mode: Files -> Logs -> Jobs -> Help |
69
- | `Shift+Tab` | Switch mode (reverse direction) |
70
- | `?` | Enter help mode |
71
- | `ESC` | Exit current mode (help/filter/command) |
72
- | `q` | Quit rufio |
75
+ | `?` | Open the floating help menu |
76
+ | `q` | Quit rufio (with confirmation) |
73
77
  | `T` | Quit rufio and cd the shell to the current directory (requires `rufio --shell-init`) |
74
78
 
75
79
  ---
76
80
 
77
81
  ## Mode-Specific Keys
78
82
 
83
+ ### Preview
84
+ - **P**: Open fullscreen file preview (hides the file list)
85
+ - **j/k**: Scroll preview
86
+ - **Ctrl+D/U**: Page down/up
87
+ - **ESC**: Back to the file list
88
+
79
89
  ### Filter Mode
80
90
  - **Any character**: Add to filter query
81
91
  - **Backspace**: Remove last character
@@ -84,7 +94,7 @@ Complete reference of all keyboard shortcuts in **rufio**.
84
94
 
85
95
  ### Command Mode
86
96
  - **Any character**: Add to command input
87
- - **Tab**: Auto-complete (scripts, rake tasks, commands)
97
+ - **Tab**: Auto-complete (scripts, rake tasks, commands, paths)
88
98
  - **Up/Down**: Navigate command history
89
99
  - **Enter**: Execute command
90
100
  - **Backspace**: Remove last character
@@ -94,27 +104,45 @@ Complete reference of all keyboard shortcuts in **rufio**.
94
104
  - `@script` - Execute a registered or local script
95
105
  - `rake:task` - Execute a Rakefile task
96
106
  - `!command` - Execute a shell command
107
+ - `cd <path>` - Go to a directory (paste a path; a file path selects the file, `cd -` goes back, `Tab` completes directories)
97
108
  - `command` - Execute a built-in or plugin command
98
109
 
99
- ### Help Mode
100
- - **j/k/h/l**: Navigate help files
101
- - **ESC**: Exit help mode and return to previous directory
110
+ ### Global Search (G)
111
+ - **Type**: Filter results (literal, case insensitive)
112
+ - **Tab**: Switch between file names and content
113
+ - **Up/Down** or **Ctrl+J/K or Ctrl+N/P**: Select a result
114
+ - **Enter**: Go to the selected file in Files mode
115
+ - **Ctrl+U**: Clear the query
116
+ - **ESC**: Return to the previous mode
117
+ - Content search also reads `.pdf`, `.xlsx` and `.pptx`
102
118
 
103
- ### Job Mode
119
+ ### Config Mode (C)
120
+ - Same navigation, preview and file operations as Files mode
121
+ - Leaving `~/.config/rufio/` returns to Files mode
122
+
123
+ ### Job Mode (J)
104
124
  - **j/k**: Navigate through jobs
125
+ - **g/G**: First / last job
126
+ - **Space**: View job log
127
+ - **`x`: Cancel** the selected job (including ZIP jobs)
105
128
  - **ESC**: Exit job mode
106
129
 
130
+ ### Help Menu (?)
131
+ - **1/2/3** or **Tab/Shift+Tab**: Switch section (Controls / Modes / Scripts)
132
+ - **j/k**: Scroll | **Ctrl+D/U**: Page down/up | **g/G**: Top / bottom
133
+ - **ESC**, **q** or **?**: Close and keep your current mode and directory
134
+
107
135
  ---
108
136
 
109
137
  ## Tips
110
138
 
111
139
  - **Vim users**: Navigation keys (`hjkl`) work exactly like Vim
112
140
  - **Selection**: Use `Space` to mark multiple files, then operate on all at once
113
- - **Quick access**: Number keys `1-9` provide instant access to bookmarks
141
+ - **Quick access**: Number keys `1-9` and `Tab` provide instant access to bookmarks
114
142
  - **Filtering**: Press `f` and start typing for real-time filtering
115
143
  - **Rake tasks**: Type `:rake:` then `Tab` to see available tasks
116
144
  - **Local scripts**: Scripts in the current directory are auto-detected with `@` prefix
117
145
 
118
146
  ---
119
147
 
120
- *Press `?` anytime to return to help mode*
148
+ *Press `?` anytime to open the help menu*
data/info/welcome.md CHANGED
@@ -10,10 +10,14 @@ A TUI file manager that executes and coordinates tools and scripts from files.
10
10
  - **Vim-like Keybindings** - Navigate with `j/k/h/l` keys
11
11
  - **Real-time Filtering** - Press `f` to filter files instantly
12
12
  - **Advanced Search** - Use `fzf` for file names and `rga` for content search
13
+ - **Global Search** - Press `G` to search names or contents (including PDF/Excel/PowerPoint) across all bookmarks
14
+ - **ZIP Support** - Preview `.zip` contents, extract or compress selected items with `z`
13
15
  - **Bookmark Support** - Quick access to favorite directories
14
16
  - **Job Management** - Run commands in the background
15
17
  - **Plugin System** - Extend functionality with custom plugins
16
18
 
19
+ Text previews read at most the first 64 KiB. PDF bodies are not previewed, keeping file navigation responsive.
20
+
17
21
  ## Quick Start
18
22
 
19
23
  | Key | Action |
@@ -24,12 +28,18 @@ A TUI file manager that executes and coordinates tools and scripts from files.
24
28
  | `s` | Search with fzf |
25
29
  | `b` | Add bookmark |
26
30
  | `B` | Bookmark menu |
31
+ | `Tab` | Next bookmark |
27
32
  | `z` | zoxide history / extract or compress selected items |
33
+ | `G` | Global search across bookmarks |
34
+ | `C` | Config mode |
28
35
  | `:` | Command mode |
29
36
  | `J` | Job mode |
30
- | `?` | Help mode |
31
- | `Tab` | Switch mode (Files / Logs / Jobs / Help) |
37
+ | `L` | Execution logs |
38
+ | `?` | Help menu |
32
39
  | `q` | Quit |
40
+ | `T` | Quit and cd to the current directory |
41
+
42
+ In G mode, use `Ctrl+J` / `Ctrl+K` to select the next / previous result and `Enter` to open it.
33
43
 
34
44
  ## Command Mode
35
45
 
@@ -38,12 +48,13 @@ Press `:` to enter command mode:
38
48
  - `@script.sh` - Execute a script
39
49
  - `rake:test` - Execute a rake task
40
50
  - `!git status` - Run a shell command
51
+ - `cd ~/projects` - Go to a pasted path
41
52
  - `Tab` - Auto-complete commands
42
53
 
43
54
  ## Getting Help
44
55
 
45
- Press `?` to enter **Help Mode** where you can browse all documentation files.
46
- Press `ESC` to exit Help Mode.
56
+ Press `?` to open the floating **Help Menu** (Controls / Modes / Scripts).
57
+ Switch sections with `1/2/3` or `Tab`, scroll with `j/k`, and close with `ESC`.
47
58
 
48
59
  ## More Information
49
60
 
@@ -3,6 +3,9 @@
3
3
  module Rufio
4
4
  # コマンド補完機能を提供するクラス
5
5
  class CommandCompletion
6
+ # CommandMode が直接処理する(DSL コマンドではない)コマンド
7
+ NAVIGATION_COMMANDS = %w[cd].freeze
8
+
6
9
  # 初期化
7
10
  # @param history [CommandHistory, CommandMode, nil] 履歴、または単独で渡すコマンドモード
8
11
  # @param command_mode [CommandMode, nil] コマンドモード(スクリプト補完に使用)
@@ -28,6 +31,11 @@ module Rufio
28
31
  rake_candidates('')
29
32
  end
30
33
 
34
+ # ディレクトリ補完(cd で始まる場合)
35
+ if (match = input.match(/\A\s*cd\s+(.*)\z/))
36
+ return complete_cd(match[1])
37
+ end
38
+
31
39
  # シェルコマンド補完(!で始まる場合)
32
40
  if input.strip.start_with?('!')
33
41
  return complete_shell_command(input.strip)
@@ -45,7 +53,7 @@ module Rufio
45
53
  end
46
54
 
47
55
  # 通常のコマンド補完(内部コマンド + rakeタスク)
48
- available_commands = @command_mode.available_commands.map(&:to_s)
56
+ available_commands = @command_mode.available_commands.map(&:to_s) + NAVIGATION_COMMANDS
49
57
  input_lower = input.downcase
50
58
  candidates = available_commands.select do |command|
51
59
  command.downcase.start_with?(input_lower)
@@ -87,6 +95,19 @@ module Rufio
87
95
  # スクリプト候補を取得(ScriptRunner + ローカルスクリプト)
88
96
  # @param prefix [String] 入力中の文字列
89
97
  # @return [Array<String>] スクリプト候補(@付き)
98
+ # :cd のディレクトリ補完。相対パスは閲覧中ディレクトリを基準にする
99
+ def complete_cd(argument)
100
+ if argument.start_with?('~', '/') || File.absolute_path?(argument)
101
+ candidates = @shell_completion.complete_path(argument, directories_only: true)
102
+ else
103
+ base = @command_mode.browsing_directory || Dir.pwd
104
+ prefix = File.join(base, '')
105
+ candidates = @shell_completion.complete_path(File.join(base, argument), directories_only: true)
106
+ .map { |path| path.delete_prefix(prefix) }
107
+ end
108
+ candidates.map { |path| "cd #{path}/" }
109
+ end
110
+
90
111
  def script_candidates(prefix)
91
112
  @command_mode.complete_script("@#{prefix}")
92
113
  end
@@ -45,6 +45,7 @@ module Rufio
45
45
  # 閲覧中ディレクトリを更新
46
46
  # @param directory [String] 現在の閲覧ディレクトリ
47
47
  def update_browsing_directory(directory)
48
+ @browsing_directory = directory
48
49
  @local_script_scanner.update_directory(directory)
49
50
  @rakefile_parser.update_directory(directory)
50
51
  end
@@ -56,6 +57,11 @@ module Rufio
56
57
  # 空のコマンドは無視
57
58
  return nil if command_string.nil? || command_string.strip.empty?
58
59
 
60
+ # ディレクトリ移動 (cd <path>)。実際の移動は TerminalUI が行う
61
+ if (match = command_string.strip.match(/\Acd(?:\s+(.*))?\z/m))
62
+ return resolve_cd(match[1].to_s, working_dir)
63
+ end
64
+
59
65
  # スクリプト実行 (@ で始まる場合)
60
66
  if command_string.strip.start_with?('@')
61
67
  return execute_script(command_string.strip[1..-1], working_dir)
@@ -103,6 +109,9 @@ module Rufio
103
109
  "⚠️ コマンドが見つかりません: #{command_name}"
104
110
  end
105
111
 
112
+ # 閲覧中ディレクトリ(:cd の補完基準)
113
+ attr_reader :browsing_directory
114
+
106
115
  # 利用可能なコマンドのリストを取得
107
116
  def available_commands
108
117
  @commands.keys
@@ -185,6 +194,17 @@ module Rufio
185
194
  # @param command_name [Symbol] コマンド名
186
195
  # @param command [Hash] コマンド情報 { command: DslCommand, source: String }
187
196
  # @return [Hash] 実行結果
197
+ # :cd の引数を移動先に解決する
198
+ # @return [Hash] { cd: { directory:, file: } } / { cd: :previous }、失敗時はエラーメッセージ
199
+ def resolve_cd(argument, working_dir)
200
+ return { cd: :previous } if argument.strip == '-'
201
+
202
+ result = PathJump.resolve(argument, working_dir || @browsing_directory || Dir.pwd)
203
+ return "⚠️ #{result[:error]}" unless result[:directory]
204
+
205
+ { cd: { directory: result[:directory], file: result[:file] } }
206
+ end
207
+
188
208
  def execute_unified_command(command_name, command)
189
209
  dsl_cmd = command[:command]
190
210
 
@@ -51,6 +51,7 @@ module Rufio
51
51
  refresh: 'R',
52
52
  # ファイル操作
53
53
  open_file: 'o',
54
+ preview: 'P',
54
55
  rename: 'r',
55
56
  delete: 'd',
56
57
  create_file: 'a',
@@ -19,6 +19,9 @@ module Rufio
19
19
  # @option options [String] :border_color Border color ANSI code
20
20
  # @option options [String] :title_color Title color ANSI code
21
21
  # @option options [String] :content_color Content color ANSI code
22
+ # @option options [Integer] :scroll_offset First visible content line
23
+ # @option options [Integer] :total_lines Total scrollable content lines
24
+ # @option options [Integer] :visible_lines Number of visible scrollable lines
22
25
  def draw_floating_window_to_buffer(screen, x, y, width, height, title, content_lines, options = {})
23
26
  draw_window_cells(x, y, width, height, title, content_lines, options,
24
27
  put_cell: ->(cx, cy, ch, fg: nil) { screen.put(cx, cy, ch, fg: fg) },
@@ -66,6 +69,9 @@ module Rufio
66
69
 
67
70
  bottom_y = y + height - 1
68
71
  print "\e[#{bottom_y};#{x}H#{border_color}└#{'─' * inner_width}┘#{reset_color}"
72
+ draw_scroll_indicators(x, y, width, height, options) do |cx, cy, label|
73
+ print "\e[#{cy};#{cx}H#{border_color}#{label}#{reset_color}"
74
+ end
69
75
  end
70
76
 
71
77
  # Calculate center position for a window
@@ -217,6 +223,26 @@ module Rufio
217
223
 
218
224
  bottom_y = y + height - 1
219
225
  put_str.call(x, bottom_y, "└#{'─' * inner_width}┘", fg: border_color)
226
+ draw_scroll_indicators(x, y, width, height, options) do |cx, cy, label|
227
+ put_str.call(cx, cy, label, fg: border_color)
228
+ end
229
+ end
230
+
231
+ # 枠内に表示し、本文や操作ガイドの行を消費しない。
232
+ def draw_scroll_indicators(x, y, width, height, options)
233
+ total = options[:total_lines]
234
+ visible = options[:visible_lines]
235
+ return unless total && visible && total > visible
236
+ return if width < 3 || height < 2
237
+
238
+ offset = [[options.fetch(:scroll_offset, 0), 0].max, total - visible].min
239
+ [[offset > 0, y, '▲ 上に続く'],
240
+ [offset + visible < total, y + height - 1, '▼ 下に続く']].each do |shown, row, text|
241
+ next unless shown
242
+
243
+ label = TextUtils.truncate_to_width(text, width - 2)
244
+ yield x + 1 + (width - 2 - TextUtils.display_width(label)) / 2, row, label
245
+ end
220
246
  end
221
247
 
222
248
  # ASCII character range constants
@@ -246,16 +246,17 @@ module Rufio
246
246
  dest_path = File.join(destination, item_name)
247
247
 
248
248
  begin
249
- if File.exist?(dest_path)
250
- error_messages << "#{item_name}: Already exists in destination"
251
- next
252
- end
253
-
254
249
  case operation
255
250
  when :move
251
+ if File.exist?(dest_path)
252
+ error_messages << "#{item_name}: Already exists in destination"
253
+ next
254
+ end
256
255
  FileUtils.mv(source_path, dest_path)
257
256
  when :copy
258
- if File.directory?(source_path)
257
+ is_directory = File.directory?(source_path)
258
+ dest_path = resolve_copy_destination(destination, item_name, is_directory)
259
+ if is_directory
259
260
  FileUtils.cp_r(source_path, dest_path)
260
261
  else
261
262
  FileUtils.cp(source_path, dest_path)
@@ -281,6 +282,27 @@ module Rufio
281
282
  )
282
283
  end
283
284
 
285
+ def resolve_copy_destination(destination, item_name, is_directory)
286
+ dest_path = File.join(destination, item_name)
287
+ return dest_path unless File.exist?(dest_path)
288
+
289
+ if is_directory
290
+ base = item_name
291
+ ext = ''
292
+ else
293
+ ext = File.extname(item_name)
294
+ base = File.basename(item_name, ext)
295
+ end
296
+
297
+ index = 1
298
+ loop do
299
+ candidate = File.join(destination, "#{base}_#{index}#{ext}")
300
+ return candidate unless File.exist?(candidate)
301
+
302
+ index += 1
303
+ end
304
+ end
305
+
284
306
  # Build delete operation message
285
307
  # @param success_count [Integer] Number of successfully deleted items
286
308
  # @param total_count [Integer] Total number of items
@@ -5,6 +5,7 @@ module Rufio
5
5
  BINARY_THRESHOLD = 0.3 # treat as binary if 30% or more binary characters
6
6
  DEFAULT_MAX_LINES = 50
7
7
  MAX_LINE_LENGTH = 500
8
+ MAX_PREVIEW_BYTES = 64 * 1024
8
9
 
9
10
  # Binary detection constants
10
11
  BINARY_SAMPLE_SIZE = 512
@@ -28,6 +29,9 @@ module Rufio
28
29
  return empty_response if file_size == 0
29
30
 
30
31
  begin
32
+ # PDF bodies are not displayed; never scan their binary streams as text.
33
+ return binary_response(file_path) if File.extname(file_path).casecmp?('.pdf')
34
+
31
35
  return archive_response(file_path, max_lines) if zip_archive?(file_path)
32
36
 
33
37
  # binary file detection
@@ -102,71 +106,30 @@ module Rufio
102
106
  end
103
107
 
104
108
  def read_text_file(file_path, max_lines)
109
+ # Bound the read before decoding or splitting: a binary stream or minified
110
+ # file can contain a single line spanning the entire file.
111
+ content = File.binread(file_path, MAX_PREVIEW_BYTES + 1)
112
+ truncated = content.bytesize > MAX_PREVIEW_BYTES
113
+ content = content.byteslice(0, MAX_PREVIEW_BYTES) if truncated
114
+ content = content.force_encoding('UTF-8').encode(
115
+ 'UTF-8', invalid: :replace, undef: :replace, replace: '?'
116
+ )
105
117
  lines = []
106
- truncated = false
107
- encoding = "UTF-8"
108
-
109
- # UTF-8で読み込み、不正なバイト列は置換文字に変換
110
- File.open(file_path, "r:UTF-8:UTF-8", invalid: :replace, undef: :replace, replace: '�') do |file|
111
- file.each_line.with_index do |line, index|
112
- break if index >= max_lines
113
-
114
- begin
115
- # Ensure line is properly encoded as UTF-8
116
- line = line.encode('UTF-8', invalid: :replace, undef: :replace, replace: '?')
117
-
118
- # truncate too long lines
119
- if line.length > MAX_LINE_LENGTH
120
- line = line[0...MAX_LINE_LENGTH] + "..."
121
- end
122
-
123
- lines << line.chomp
124
- rescue EncodingError, ArgumentError
125
- # If encoding fails, add placeholder
126
- lines << "[encoding error in line #{index + 1}]"
127
- end
118
+ content.each_line.with_index do |line, index|
119
+ if index >= max_lines
120
+ truncated = true
121
+ break
128
122
  end
129
123
 
130
- # check if there are more lines to read
131
- truncated = !file.eof?
124
+ line = line[0...MAX_LINE_LENGTH] + '...' if line.length > MAX_LINE_LENGTH
125
+ lines << line.chomp
132
126
  end
133
127
 
134
128
  {
135
129
  content: lines,
136
130
  truncated: truncated,
137
- encoding: encoding
131
+ encoding: 'UTF-8'
138
132
  }
139
- rescue Encoding::InvalidByteSequenceError, Encoding::CompatibilityError
140
- # try Shift_JIS if UTF-8 fails
141
- begin
142
- lines = []
143
- File.open(file_path, "r:Shift_JIS:UTF-8", invalid: :replace, undef: :replace, replace: '�') do |file|
144
- file.each_line.with_index do |line, index|
145
- break if index >= max_lines
146
-
147
- begin
148
- # Ensure line is properly encoded as UTF-8
149
- line = line.encode('UTF-8', invalid: :replace, undef: :replace, replace: '?')
150
- lines << line.chomp
151
- rescue EncodingError, ArgumentError
152
- # If encoding fails, add placeholder
153
- lines << "[encoding error in line #{index + 1}]"
154
- end
155
- end
156
- truncated = !file.eof?
157
- end
158
- {
159
- content: lines,
160
- truncated: truncated,
161
- encoding: "Shift_JIS"
162
- }
163
- rescue
164
- {
165
- content: ["(#{ConfigLoader.message('file.encoding_error')})"],
166
- truncated: false,
167
- encoding: "unknown"
168
- }
169
- end
170
133
  end
171
134
 
172
135
  def determine_file_type(file_path)
@@ -106,11 +106,11 @@ module Rufio
106
106
  when "\t"
107
107
  @search_type = @search_type == :filename ? :content : :filename
108
108
  start_scan
109
- when "\r", "\n" then return @results[@selected_index]
109
+ when "\r" then return @results[@selected_index]
110
110
  when :down then @selected_index = [@selected_index + 1, [@results.length - 1, 0].max].min
111
111
  when :up then @selected_index = [@selected_index - 1, 0].max
112
- when "\x0e" then handle_key(:down) # Ctrl+N
113
- when "\x10" then handle_key(:up) # Ctrl+P
112
+ when "\x0a", "\x0e" then handle_key(:down) # Ctrl+J / Ctrl+N
113
+ when "\x0b", "\x10" then handle_key(:up) # Ctrl+K / Ctrl+P
114
114
  when "\x15" then update_query('') # Ctrl+U
115
115
  when "\u007f", "\b" then update_query(@query[0...-1])
116
116
  else
@@ -169,7 +169,7 @@ module Rufio
169
169
  write_line(screen, 3, message, width)
170
170
  end
171
171
  write_line(screen, screen.height - 3, @results[@selected_index]&.[](:path) || @errors.first.to_s, width, "\e[90m")
172
- write_line(screen, screen.height - 2, 'ESC: Back | Enter: Go to file | Up/Down: Select | Type: Search', width)
172
+ write_line(screen, screen.height - 2, 'ESC: Back | Enter: Go to file | Up/Down or Ctrl+J/K: Select | Type: Search', width)
173
173
  end
174
174
 
175
175
  private
@@ -54,20 +54,24 @@ module Rufio
54
54
  'Navigation',
55
55
  "#{key(:move_down)}/#{key(:move_up)} or arrows: Move down/up",
56
56
  "#{key(:navigate_parent)}/#{key(:navigate_enter)}: Parent / enter directory",
57
- 'Enter: Open file preview | ESC: Back to file list',
57
+ "#{key(:preview)}: Fullscreen file preview | ESC: Back to file list",
58
+ 'Text preview: first 64 KiB; PDF bodies are not previewed.',
58
59
  'Preview: j/k scroll, Ctrl+D/U page down/up',
59
60
  "#{key(:top)}/#{key(:bottom)}: First / last entry",
60
61
  "#{key(:open_file)}: Open in external editor",
62
+ "#{key(:open_explorer)}: Open directory in file explorer",
63
+ 'Preview .zip: Entry list (size, method, date, name)',
61
64
  "#{key(:refresh)}: Refresh directory",
62
65
  '', 'Selection and file operations',
63
66
  "#{key(:select)}: Select / unselect file",
64
67
  "#{key(:copy_selected)}/#{key(:move_selected)}/#{key(:delete_selected)}: Copy / move / delete selected files",
65
68
  "#{key(:zoxide)}: Extract selected .zip files / compress selected items",
69
+ " Runs as a job: #{key(:job_mode)} Jobs mode shows progress; x cancels",
66
70
  "#{key(:rename)}/#{key(:delete)}: Rename / delete current file",
67
71
  "#{key(:create_file)}/#{key(:create_dir)}: Create file / directory",
68
72
  '', 'Search and bookmarks',
69
73
  "#{key(:filter)}: Filter | Enter: Apply | ESC: Clear",
70
- "#{key(:fzf_search)}: Filename search (fzf)",
74
+ "#{key(:fzf_search)} or #{key(:fzf_search_alt)}: Filename search (fzf)",
71
75
  "#{key(:rga_search)}: Content search (rga)",
72
76
  "#{key(:global_search)}: Search file names or contents across all bookmarks",
73
77
  "#{key(:add_bookmark)}/#{key(:bookmark_menu)}: Add / manage bookmarks",
@@ -99,14 +103,15 @@ module Rufio
99
103
  'GLOBAL_SEARCH_EXCLUDED_EXTENSIONS = %w[.zip .log]',
100
104
  'Applies to names and contents. Restart Rufio after editing.',
101
105
  'Extensions ignore case; leading dots are optional; [] excludes none.',
102
- 'Type to filter | Up/Down or Ctrl+N/P: Select',
106
+ 'Type to filter | Up/Down or Ctrl+J/K or Ctrl+N/P: Select',
103
107
  'Enter: Go to the selected file in Files mode',
104
108
  'ESC: Return to the previous mode | Ctrl+U: Clear query',
105
109
  '', "[#{key(:log_viewer)}] Logs",
106
- 'Browse command logs. Enter opens a preview.',
110
+ "Browse command logs. #{key(:preview)} opens a fullscreen preview.",
107
111
  'ESC: Leave preview; ESC again: Return to Files.',
108
112
  '', "[#{key(:job_mode)}] Jobs",
109
113
  'View background commands and their status.',
114
+ 'ZIP extract/compress jobs show progress here.',
110
115
  'Space: View log | x: Cancel job | ESC: Back',
111
116
  '', "[#{key(:help)}] Help",
112
117
  'This floating menu keeps your current mode and directory.',
@@ -115,6 +120,7 @@ module Rufio
115
120
  'Type a command, then Enter to run it.',
116
121
  'Tab: Complete | Up/Down: History | ESC: Cancel',
117
122
  ':!git status: Run a shell command',
123
+ ':cd <path>: Go to a pasted path (file: select it, cd -: back)',
118
124
  ':@hello.rb: Run a script in the current directory'
119
125
  ]
120
126
  when 'Scripts'
@@ -161,7 +167,8 @@ module Rufio
161
167
  DialogRenderer.new.draw_floating_window_to_overlay(
162
168
  screen, (screen.width - width) / 2, (screen.height - height) / 2,
163
169
  width, height, TextUtils.truncate_to_width(title, width - 2), visible,
164
- border_color: "\e[36m", title_color: "\e[1;36m"
170
+ border_color: "\e[36m", title_color: "\e[1;36m",
171
+ scroll_offset: @scroll_offset, total_lines: lines.length, visible_lines: @page_size
165
172
  )
166
173
  end
167
174
 
@@ -228,7 +228,7 @@ module Rufio
228
228
 
229
229
  entries = get_active_entries
230
230
 
231
- # Enter キー(特殊処理: プレビューペインフォーカスまたはナビゲーション)
231
+ # Enter キー(ディレクトリ移動)
232
232
  if key == "\r" || key == "\n"
233
233
  return @nav_controller.handle_enter_key(current_entry, @in_help_mode, @in_log_viewer_mode)
234
234
  end
@@ -476,6 +476,7 @@ module Rufio
476
476
  when :top then @nav_controller.move_to_top
477
477
  when :bottom then @nav_controller.move_to_bottom(entries)
478
478
  when :refresh then @nav_controller.refresh
479
+ when :preview then focus_preview_pane
479
480
  when :open_file then @nav_controller.open_current_file(current_entry)
480
481
  when :open_explorer then @nav_controller.open_directory_in_explorer
481
482
  when :rename then @file_op_controller.rename_current_file(current_entry)
@@ -236,18 +236,14 @@ module Rufio
236
236
  end
237
237
  end
238
238
 
239
- # Enterキーの処理:ファイルならプレビューフォーカス、ディレクトリならナビゲート
239
+ # Enterキーの処理:ディレクトリへ移動する
240
240
  # @param entry [Hash] 現在選択中のエントリ
241
241
  # @param in_help_mode [Boolean]
242
242
  # @param in_log_viewer_mode [Boolean]
243
243
  def handle_enter_key(entry, in_help_mode, in_log_viewer_mode)
244
244
  return false unless entry
245
245
 
246
- if entry[:type] == 'file'
247
- focus_preview_pane(entry)
248
- else
249
- navigate_enter(entry, in_help_mode, in_log_viewer_mode)
250
- end
246
+ navigate_enter(entry, in_help_mode, in_log_viewer_mode)
251
247
  end
252
248
 
253
249
  private