rufio 1.1.0 → 1.3.0

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.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +34 -0
  3. data/README.md +74 -3
  4. data/README_ja.md +71 -2
  5. data/Rakefile +14 -1
  6. data/bin/rufio +31 -2
  7. data/docs/fast_zip_design_ja.md +167 -0
  8. data/info/help.md +2 -0
  9. data/info/keybindings.md +3 -1
  10. data/info/welcome.md +1 -1
  11. data/lib/rufio/application.rb +12 -1
  12. data/lib/rufio/archive_controller.rb +211 -0
  13. data/lib/rufio/background_command_executor.rb +9 -0
  14. data/lib/rufio/background_status.rb +49 -0
  15. data/lib/rufio/bookmark_controller.rb +38 -121
  16. data/lib/rufio/bookmark_manager.rb +77 -331
  17. data/lib/rufio/command_completion.rb +6 -1
  18. data/lib/rufio/command_history.rb +5 -2
  19. data/lib/rufio/command_mode.rb +40 -134
  20. data/lib/rufio/command_mode_ui.rb +4 -42
  21. data/lib/rufio/config.rb +88 -136
  22. data/lib/rufio/config_loader.rb +3 -11
  23. data/lib/rufio/dialog_renderer.rb +64 -152
  24. data/lib/rufio/dialog_support.rb +69 -0
  25. data/lib/rufio/document_text_extractor.rb +167 -0
  26. data/lib/rufio/file_operation_controller.rb +49 -222
  27. data/lib/rufio/file_preview.rb +46 -2
  28. data/lib/rufio/global_search.rb +121 -9
  29. data/lib/rufio/help_menu.rb +8 -2
  30. data/lib/rufio/interpreter_resolver.rb +6 -11
  31. data/lib/rufio/job_manager.rb +2 -5
  32. data/lib/rufio/key_input.rb +77 -0
  33. data/lib/rufio/keybind_handler.rb +81 -115
  34. data/lib/rufio/local_script_scanner.rb +6 -18
  35. data/lib/rufio/native/aarch64-linux-gnu/librufio_zig.so +0 -0
  36. data/lib/rufio/native/aarch64-linux-musl/librufio_zig.so +0 -0
  37. data/lib/rufio/native/aarch64-macos/librufio_zig.dylib +0 -0
  38. data/lib/rufio/native/aarch64-windows/rufio_zig.dll +0 -0
  39. data/lib/rufio/native/x86_64-linux-gnu/librufio_zig.so +0 -0
  40. data/lib/rufio/native/x86_64-linux-musl/librufio_zig.so +0 -0
  41. data/lib/rufio/native/x86_64-macos/librufio_zig.dylib +0 -0
  42. data/lib/rufio/native/x86_64-windows/rufio_zig.dll +0 -0
  43. data/lib/rufio/native_content_search.rb +153 -0
  44. data/lib/rufio/native_scanner.rb +13 -68
  45. data/lib/rufio/native_scanner_zig.rb +106 -56
  46. data/lib/rufio/native_zip.rb +121 -0
  47. data/lib/rufio/navigation_controller.rb +18 -20
  48. data/lib/rufio/screen.rb +2 -12
  49. data/lib/rufio/script_executor.rb +6 -73
  50. data/lib/rufio/script_file.rb +44 -0
  51. data/lib/rufio/script_path_manager.rb +9 -43
  52. data/lib/rufio/script_runner.rb +13 -50
  53. data/lib/rufio/search_controller.rb +1 -1
  54. data/lib/rufio/shell_command_completion.rb +1 -5
  55. data/lib/rufio/shell_init.rb +90 -0
  56. data/lib/rufio/task_status.rb +5 -1
  57. data/lib/rufio/terminal_ui.rb +55 -294
  58. data/lib/rufio/text_utils.rb +26 -3
  59. data/lib/rufio/ui_renderer.rb +63 -40
  60. data/lib/rufio/version.rb +1 -1
  61. data/lib/rufio/zip_archive.rb +314 -0
  62. data/lib/rufio/zip_extractor.rb +346 -0
  63. data/lib/rufio/zip_writer.rb +268 -0
  64. data/lib/rufio/zoxide_integration.rb +5 -43
  65. data/lib/rufio.rb +5 -0
  66. data/lib_zig/rufio_native/Makefile +54 -17
  67. data/lib_zig/rufio_native/bench.sh +36 -0
  68. data/lib_zig/rufio_native/build.zig +32 -35
  69. data/lib_zig/rufio_native/src/cli.zig +99 -0
  70. data/lib_zig/rufio_native/src/main.zig +187 -83
  71. data/lib_zig/rufio_native/src/os.zig +889 -0
  72. data/lib_zig/rufio_native/src/search.zig +919 -0
  73. data/lib_zig/rufio_native/src/search_test.zig +88 -0
  74. data/lib_zig/rufio_native/src/zip.zig +482 -0
  75. metadata +125 -3
  76. data/lib/rufio/native/rufio_zig.bundle +0 -0
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'open3'
4
+ require_relative 'script_runner'
4
5
 
5
6
  module Rufio
6
7
  # コマンドモード - DSLコマンドを実行するための統一インターフェース
@@ -37,14 +38,8 @@ module Rufio
37
38
  # @param config_file [String] 設定ファイルのパス
38
39
  # @param job_manager [JobManager] ジョブマネージャー
39
40
  def setup_script_path_manager(config_file:, job_manager:)
40
- @job_manager = job_manager
41
41
  @script_path_manager = ScriptPathManager.new(config_file)
42
- # ScriptRunnerも設定(ScriptPathManagerのパスを使用)
43
- @script_runner = ScriptRunner.new(
44
- script_paths: @script_path_manager.paths,
45
- job_manager: job_manager,
46
- command_logger: @background_executor&.command_logger
47
- )
42
+ setup_script_runner(script_paths: @script_path_manager.paths, job_manager: job_manager)
48
43
  end
49
44
 
50
45
  # 閲覧中ディレクトリを更新
@@ -214,27 +209,28 @@ module Rufio
214
209
  # コマンドが空の場合
215
210
  return { success: false, error: "コマンドが指定されていません" } if shell_command.strip.empty?
216
211
 
217
- begin
218
- # Open3を使って標準出力と標準エラーを分離して取得
219
- stdout, stderr, status = Open3.capture3(shell_command)
220
-
221
- result = {
222
- success: status.success?,
223
- output: stdout.strip,
224
- stderr: stderr.strip
225
- }
212
+ run_shell_capture(shell_command)
213
+ end
226
214
 
227
- # コマンドが失敗した場合、エラーメッセージを追加
228
- unless status.success?
229
- result[:error] = "コマンドが失敗しました (終了コード: #{status.exitstatus})"
230
- end
215
+ # Open3.capture3 によるコマンド実行と結果ハッシュの組み立て
216
+ def run_shell_capture(command, chdir: nil, not_found_label: "コマンド", error_label: "コマンド実行")
217
+ stdout, stderr, status = if chdir
218
+ Open3.capture3(command, chdir: chdir)
219
+ else
220
+ Open3.capture3(command)
221
+ end
231
222
 
232
- result
233
- rescue Errno::ENOENT => e
234
- { success: false, error: "コマンドが見つかりません: #{e.message}" }
235
- rescue StandardError => e
236
- { success: false, error: "コマンド実行エラー: #{e.message}" }
237
- end
223
+ result = {
224
+ success: status.success?,
225
+ output: stdout.strip,
226
+ stderr: stderr.strip
227
+ }
228
+ result[:error] = "コマンドが失敗しました (終了コード: #{status.exitstatus})" unless status.success?
229
+ result
230
+ rescue Errno::ENOENT => e
231
+ { success: false, error: "#{not_found_label}が見つかりません: #{e.message}" }
232
+ rescue StandardError => e
233
+ { success: false, error: "#{error_label}エラー: #{e.message}" }
238
234
  end
239
235
 
240
236
  # スクリプトを実行する(@プレフィックス用)
@@ -276,36 +272,17 @@ module Rufio
276
272
  # @param args [String, nil] スクリプトに渡す引数
277
273
  # @return [String, Hash] 実行結果メッセージ
278
274
  def execute_local_script(script, working_dir, args = nil)
279
- command = build_script_command(script)
280
- command = "#{command} #{args}" if args && !args.empty?
281
-
282
- if @job_manager
283
- job = @job_manager.add_job(
284
- name: script[:name],
285
- path: working_dir,
286
- command: command
287
- )
288
- job.start
289
-
290
- Thread.new do
291
- execute_script_in_background(job, script, working_dir, command)
292
- end
293
-
294
- "🚀 ジョブを開始: #{script[:name]}"
295
- else
296
- # 同期実行
297
- stdout, stderr, status = Open3.capture3(command, chdir: working_dir)
298
- result = {
299
- success: status.success?,
300
- output: stdout.strip,
301
- stderr: stderr.strip
302
- }
303
-
304
- # Logsに記録
305
- log_execution("@#{script[:name]}", result)
275
+ runner = ScriptRunner.new(
276
+ script_paths: [], job_manager: @job_manager,
277
+ command_logger: @background_executor&.command_logger
278
+ )
279
+ result = runner.execute(script, working_dir: working_dir, args: args)
280
+ return "🚀 ジョブを開始: #{script[:name]}" if @job_manager
306
281
 
307
- result
308
- end
282
+ # CommandMode の表示用の戻り値は維持する。
283
+ result = { success: result[:success], output: result[:output].strip, stderr: result[:error].strip }
284
+ log_execution("@#{script[:name]}", result)
285
+ result
309
286
  end
310
287
 
311
288
  # 実行結果をCommandLoggerに記録
@@ -324,59 +301,6 @@ module Rufio
324
301
  )
325
302
  end
326
303
 
327
- # スクリプトの実行コマンドを構築
328
- # @param script [Hash] スクリプト情報
329
- # @return [String] 実行コマンド
330
- def build_script_command(script)
331
- path = script[:path]
332
- ext = File.extname(path).downcase
333
-
334
- case ext
335
- when '.rb'
336
- "ruby #{path.shellescape}"
337
- when '.py'
338
- "python3 #{path.shellescape}"
339
- when '.js'
340
- "node #{path.shellescape}"
341
- when '.ts'
342
- "ts-node #{path.shellescape}"
343
- when '.pl'
344
- "perl #{path.shellescape}"
345
- when '.ps1'
346
- "pwsh #{path.shellescape}"
347
- else
348
- path.shellescape
349
- end
350
- end
351
-
352
- # ローカルスクリプトをバックグラウンドで実行
353
- def execute_script_in_background(job, script, working_dir, command)
354
- stdout, stderr, status = Open3.capture3(command, chdir: working_dir)
355
-
356
- job.append_log(stdout) unless stdout.empty?
357
- job.append_log(stderr) unless stderr.empty?
358
-
359
- if status.success?
360
- job.complete(exit_code: status.exitstatus)
361
- else
362
- job.fail(exit_code: status.exitstatus)
363
- end
364
-
365
- # Logsに記録
366
- log_execution("@#{script[:name]}", {
367
- success: status.success?,
368
- output: stdout.strip,
369
- stderr: stderr.strip
370
- })
371
-
372
- @job_manager&.notify_completion(job)
373
- rescue StandardError => e
374
- job.append_log("Error: #{e.message}")
375
- job.fail(exit_code: -1)
376
- log_execution("@#{script[:name]}", { success: false, output: '', stderr: e.message })
377
- @job_manager&.notify_completion(job)
378
- end
379
-
380
304
  # rakeタスクを実行する
381
305
  # @param task_name [String] タスク名
382
306
  # @param working_dir [String, nil] 作業ディレクトリ
@@ -393,32 +317,14 @@ module Rufio
393
317
  working_dir ||= Dir.pwd
394
318
  shell_command = "rake #{task_name.shellescape}"
395
319
 
396
- begin
397
- stdout, stderr, status = Open3.capture3(shell_command, chdir: working_dir)
398
-
399
- result = {
400
- success: status.success?,
401
- output: stdout.strip,
402
- stderr: stderr.strip
403
- }
404
-
405
- unless status.success?
406
- result[:error] = "コマンドが失敗しました (終了コード: #{status.exitstatus})"
407
- end
408
-
409
- # Logsに記録
410
- log_execution("rake:#{task_name}", result)
411
-
412
- result
413
- rescue Errno::ENOENT => e
414
- result = { success: false, error: "rakeが見つかりません: #{e.message}" }
415
- log_execution("rake:#{task_name}", result)
416
- result
417
- rescue StandardError => e
418
- result = { success: false, error: "rake実行エラー: #{e.message}" }
419
- log_execution("rake:#{task_name}", result)
420
- result
421
- end
320
+ result = run_shell_capture(
321
+ shell_command,
322
+ chdir: working_dir,
323
+ not_found_label: "rake",
324
+ error_label: "rake実行"
325
+ )
326
+ log_execution("rake:#{task_name}", result)
327
+ result
422
328
  end
423
329
 
424
330
  # スクリプトパスからスクリプトを検索して実行を試みる
@@ -1,10 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'dialog_support'
4
+
3
5
  require 'io/console'
4
6
 
5
7
  module Rufio
6
8
  # コマンドモードのUI - Tab補完とフローティングウィンドウでの結果表示
7
9
  class CommandModeUI
10
+ include DialogSupport
11
+
8
12
  def initialize(command_mode, dialog_renderer)
9
13
  @command_mode = command_mode
10
14
  @dialog_renderer = dialog_renderer
@@ -155,48 +159,6 @@ module Rufio
155
159
 
156
160
  private
157
161
 
158
- # オーバーレイダイアログを表示してキー入力を待つヘルパーメソッド
159
- def show_overlay_dialog(title, content_lines, options = {}, &block)
160
- # terminal_ui が利用可能で、screen と renderer が存在する場合のみオーバーレイを使用
161
- use_overlay = @terminal_ui &&
162
- @terminal_ui.respond_to?(:screen) &&
163
- @terminal_ui.respond_to?(:renderer) &&
164
- @terminal_ui.screen &&
165
- @terminal_ui.renderer
166
-
167
- if use_overlay
168
- # オーバーレイを使用
169
- @terminal_ui.show_overlay_dialog(title, content_lines, options, &block)
170
- else
171
- # フォールバック: 従来の方法
172
- width = options[:width]
173
- height = options[:height]
174
-
175
- unless width && height
176
- width, height = @dialog_renderer.calculate_dimensions(content_lines, {
177
- title: title,
178
- min_width: options[:min_width] || 40,
179
- max_width: options[:max_width] || 80
180
- })
181
- end
182
-
183
- x, y = @dialog_renderer.calculate_center(width, height)
184
-
185
- @dialog_renderer.draw_floating_window(x, y, width, height, title, content_lines, {
186
- border_color: options[:border_color] || "\e[37m",
187
- title_color: options[:title_color] || "\e[1;33m",
188
- content_color: options[:content_color] || "\e[37m"
189
- })
190
-
191
- key = block_given? ? yield : STDIN.getch
192
-
193
- @dialog_renderer.clear_area(x, y, width, height)
194
- @terminal_ui&.refresh_display
195
-
196
- key
197
- end
198
- end
199
-
200
162
  # 文字列配列の共通プレフィックスを見つける
201
163
  # @param strings [Array<String>] 文字列配列
202
164
  # @return [String] 共通プレフィックス
data/lib/rufio/config.rb CHANGED
@@ -19,147 +19,92 @@ module Rufio
19
19
  YAML_CONFIG_PATH = File.join(CONFIG_DIR, 'config.yml').freeze
20
20
  LOCAL_YAML_PATH = './rufio.yml'
21
21
 
22
+ # Base English message definitions
23
+ BASE_MESSAGES = {
24
+ # Application messages
25
+ 'app.interrupted' => 'rufio interrupted',
26
+ 'app.error_occurred' => 'Error occurred',
27
+ 'app.terminated' => 'rufio terminated',
28
+
29
+ # File operations
30
+ 'file.not_found' => 'File not found',
31
+ 'file.not_readable' => 'File not readable',
32
+ 'file.read_error' => 'File read error',
33
+ 'file.binary_file' => 'Binary file',
34
+ 'file.cannot_preview' => 'Cannot preview',
35
+ 'file.encoding_error' => 'Character encoding error - cannot read file',
36
+ 'file.invalid_archive' => 'Invalid ZIP archive',
37
+ 'file.preview_error' => 'Preview error',
38
+ 'file.error_prefix' => 'Error',
39
+
40
+ # Keybind messages
41
+ 'keybind.invalid_key' => 'invalid key',
42
+ 'keybind.search_text' => 'Search text: ',
43
+ 'keybind.no_matches' => 'No matches found.',
44
+ 'keybind.press_any_key' => 'Press any key to continue...',
45
+ 'keybind.input_filename' => 'Enter filename: ',
46
+ 'keybind.input_dirname' => 'Enter directory name: ',
47
+ 'keybind.invalid_filename' => 'Invalid filename (cannot contain / or \\)',
48
+ 'keybind.invalid_dirname' => 'Invalid directory name (cannot contain / or \\)',
49
+ 'keybind.file_exists' => 'File already exists',
50
+ 'keybind.directory_exists' => 'Directory already exists',
51
+ 'keybind.file_created' => 'File created',
52
+ 'keybind.directory_created' => 'Directory created',
53
+ 'keybind.creation_error' => 'Creation error',
54
+
55
+ # UI messages
56
+ 'ui.operation_prompt' => 'Operation: ',
57
+
58
+ # Help text
59
+ 'help.full' => 'j/k:move h:back l:enter o:open g/}:top/bottom G:global-search r:refresh f:filter s:search F:content a/A:create m/p/x:ops b:bookmark z:zoxide 1-9:goto q:quit',
60
+ 'help.short' => 'j/k:move h:back l:enter o:open f:filter s:search b:bookmark z:zoxide 1-9:goto q:quit',
61
+
62
+ # Health check messages
63
+ 'health.title' => 'rufio Health Check',
64
+ 'health.ruby_version' => 'Ruby version',
65
+ 'health.required_gems' => 'Required gems',
66
+ 'health.fzf' => 'fzf (file search)',
67
+ 'health.rga' => 'rga (content search)',
68
+ 'health.zoxide' => 'zoxide (directory history)',
69
+ 'health.bat' => 'bat (syntax highlight)',
70
+ 'health.file_opener' => 'System file opener',
71
+ 'health.summary' => 'Summary:',
72
+ 'health.ok' => 'OK',
73
+ 'health.warnings' => 'Warnings',
74
+ 'health.errors' => 'Errors',
75
+ 'health.all_passed' => 'All checks passed! rufio is ready to use.',
76
+ 'health.critical_missing' => 'Some critical components are missing. rufio may not work properly.',
77
+ 'health.optional_missing' => 'Some optional features are unavailable. Basic functionality will work.',
78
+ 'health.all_gems_installed' => 'All required gems installed',
79
+ 'health.missing_gems' => 'Missing gems',
80
+ 'health.gem_install_instruction' => 'Run: gem install',
81
+ 'health.tool_not_found' => 'not found',
82
+ 'health.unknown_platform' => 'Unknown platform',
83
+ 'health.file_open_may_not_work' => 'File opening may not work properly',
84
+ 'health.macos_opener' => 'macOS file opener',
85
+ 'health.linux_opener' => 'Linux file opener',
86
+ 'health.windows_opener' => 'Windows file opener',
87
+ 'health.install_brew' => 'Install: brew install',
88
+ 'health.install_apt' => 'Install: apt install',
89
+ 'health.install_guide' => 'Install: Check the installation guide for your platform',
90
+ 'health.rga_releases' => 'Install: https://github.com/phiresky/ripgrep-all/releases',
91
+ 'health.ruby_upgrade_needed' => 'Please upgrade Ruby to version 2.7.0 or higher'
92
+ }.freeze
93
+
22
94
  # Multi-language message definitions
23
95
  MESSAGES = {
24
- 'en' => {
25
- # Application messages
26
- 'app.interrupted' => 'rufio interrupted',
27
- 'app.error_occurred' => 'Error occurred',
28
- 'app.terminated' => 'rufio terminated',
29
-
30
- # File operations
31
- 'file.not_found' => 'File not found',
32
- 'file.not_readable' => 'File not readable',
33
- 'file.read_error' => 'File read error',
34
- 'file.binary_file' => 'Binary file',
35
- 'file.cannot_preview' => 'Cannot preview',
36
- 'file.encoding_error' => 'Character encoding error - cannot read file',
37
- 'file.preview_error' => 'Preview error',
38
- 'file.error_prefix' => 'Error',
39
-
40
- # Keybind messages
41
- 'keybind.invalid_key' => 'invalid key',
42
- 'keybind.search_text' => 'Search text: ',
43
- 'keybind.no_matches' => 'No matches found.',
44
- 'keybind.press_any_key' => 'Press any key to continue...',
45
- 'keybind.input_filename' => 'Enter filename: ',
46
- 'keybind.input_dirname' => 'Enter directory name: ',
47
- 'keybind.invalid_filename' => 'Invalid filename (cannot contain / or \\)',
48
- 'keybind.invalid_dirname' => 'Invalid directory name (cannot contain / or \\)',
49
- 'keybind.file_exists' => 'File already exists',
50
- 'keybind.directory_exists' => 'Directory already exists',
51
- 'keybind.file_created' => 'File created',
52
- 'keybind.directory_created' => 'Directory created',
53
- 'keybind.creation_error' => 'Creation error',
54
-
55
- # UI messages
56
- 'ui.operation_prompt' => 'Operation: ',
57
-
58
- # Help text
59
- 'help.full' => 'j/k:move h:back l:enter o:open g/}:top/bottom G:global-search r:refresh f:filter s:search F:content a/A:create m/p/x:ops b:bookmark z:zoxide 1-9:goto q:quit',
60
- 'help.short' => 'j/k:move h:back l:enter o:open f:filter s:search b:bookmark z:zoxide 1-9:goto q:quit',
61
-
62
- # Health check messages
63
- 'health.title' => 'rufio Health Check',
64
- 'health.ruby_version' => 'Ruby version',
65
- 'health.required_gems' => 'Required gems',
66
- 'health.fzf' => 'fzf (file search)',
67
- 'health.rga' => 'rga (content search)',
68
- 'health.zoxide' => 'zoxide (directory history)',
69
- 'health.bat' => 'bat (syntax highlight)',
70
- 'health.file_opener' => 'System file opener',
71
- 'health.summary' => 'Summary:',
72
- 'health.ok' => 'OK',
73
- 'health.warnings' => 'Warnings',
74
- 'health.errors' => 'Errors',
75
- 'health.all_passed' => 'All checks passed! rufio is ready to use.',
76
- 'health.critical_missing' => 'Some critical components are missing. rufio may not work properly.',
77
- 'health.optional_missing' => 'Some optional features are unavailable. Basic functionality will work.',
78
- 'health.all_gems_installed' => 'All required gems installed',
79
- 'health.missing_gems' => 'Missing gems',
80
- 'health.gem_install_instruction' => 'Run: gem install',
81
- 'health.tool_not_found' => 'not found',
82
- 'health.unknown_platform' => 'Unknown platform',
83
- 'health.file_open_may_not_work' => 'File opening may not work properly',
84
- 'health.macos_opener' => 'macOS file opener',
85
- 'health.linux_opener' => 'Linux file opener',
86
- 'health.windows_opener' => 'Windows file opener',
87
- 'health.install_brew' => 'Install: brew install',
88
- 'health.install_apt' => 'Install: apt install',
89
- 'health.install_guide' => 'Install: Check the installation guide for your platform',
90
- 'health.rga_releases' => 'Install: https://github.com/phiresky/ripgrep-all/releases',
91
- 'health.ruby_upgrade_needed' => 'Please upgrade Ruby to version 2.7.0 or higher'
92
- },
93
-
94
- 'ja' => {
95
- # Application messages
96
- 'app.interrupted' => 'rufio interrupted',
97
- 'app.error_occurred' => 'Error occurred',
98
- 'app.terminated' => 'rufio terminated',
99
-
100
- # File operations
101
- 'file.not_found' => 'File not found',
102
- 'file.not_readable' => 'File not readable',
103
- 'file.read_error' => 'File read error',
104
- 'file.binary_file' => 'Binary file',
105
- 'file.cannot_preview' => 'Cannot preview',
96
+ 'en' => BASE_MESSAGES,
97
+ 'ja' => BASE_MESSAGES.merge(
106
98
  'file.encoding_error' => 'Encoding error - cannot read file',
107
- 'file.preview_error' => 'Preview error',
108
- 'file.error_prefix' => 'Error',
109
-
110
- # Keybind messages
111
99
  'keybind.invalid_key' => 'Invalid key',
112
100
  'keybind.search_text' => 'Search: ',
113
- 'keybind.no_matches' => 'No matches found.',
114
- 'keybind.press_any_key' => 'Press any key to continue...',
115
- 'keybind.input_filename' => 'Enter filename: ',
116
- 'keybind.input_dirname' => 'Enter directory name: ',
117
- 'keybind.invalid_filename' => 'Invalid filename (cannot contain / or \\)',
118
- 'keybind.invalid_dirname' => 'Invalid directory name (cannot contain / or \\)',
119
- 'keybind.file_exists' => 'File already exists',
120
- 'keybind.directory_exists' => 'Directory already exists',
121
- 'keybind.file_created' => 'File created',
122
- 'keybind.directory_created' => 'Directory created',
123
- 'keybind.creation_error' => 'Creation error',
124
-
125
- # UI messages
126
- 'ui.operation_prompt' => 'Operation: ',
127
-
128
- # Help text
129
101
  'help.full' => 'j/k:move h:back l:enter o:open g/}:top/end G:global-search r:refresh f:filter s:search F:content a/A:create m/c/x:ops b:bookmark z:zoxide 1-9:jump q:quit',
130
102
  'help.short' => 'j/k:move h:back l:enter o:open f:filter s:search b:bookmark z:zoxide 1-9:jump q:quit',
131
-
132
- # Health check messages
133
- 'health.title' => 'rufio Health Check',
134
- 'health.ruby_version' => 'Ruby version',
135
- 'health.required_gems' => 'Required gems',
136
- 'health.fzf' => 'fzf (file search)',
137
- 'health.rga' => 'rga (content search)',
138
- 'health.zoxide' => 'zoxide (directory history)',
139
- 'health.bat' => 'bat (syntax highlight)',
140
- 'health.file_opener' => 'System file opener',
141
- 'health.summary' => 'Summary:',
142
- 'health.ok' => 'OK',
143
- 'health.warnings' => 'Warnings',
144
- 'health.errors' => 'Errors',
145
- 'health.all_passed' => 'All checks passed! rufio is ready to use.',
146
103
  'health.critical_missing' => 'Critical components missing. rufio may not work properly.',
147
104
  'health.optional_missing' => 'Optional features unavailable. Basic features will work.',
148
105
  'health.all_gems_installed' => 'All required gems are installed',
149
- 'health.missing_gems' => 'Missing gems',
150
- 'health.gem_install_instruction' => 'Run: gem install',
151
- 'health.tool_not_found' => 'not found',
152
- 'health.unknown_platform' => 'Unknown platform',
153
- 'health.file_open_may_not_work' => 'File open may not work properly',
154
- 'health.macos_opener' => 'macOS file opener',
155
- 'health.linux_opener' => 'Linux file opener',
156
- 'health.windows_opener' => 'Windows file opener',
157
- 'health.install_brew' => 'Install: brew install',
158
- 'health.install_apt' => 'Install: apt install',
159
- 'health.install_guide' => 'Install: Check the installation guide for your platform',
160
- 'health.rga_releases' => 'Install: https://github.com/phiresky/ripgrep-all/releases',
161
- 'health.ruby_upgrade_needed' => 'Please upgrade Ruby to version 2.7.0 or higher'
162
- }
106
+ 'health.file_open_may_not_work' => 'File open may not work properly'
107
+ ).freeze
163
108
  }.freeze
164
109
 
165
110
  class << self
@@ -272,14 +217,21 @@ module Rufio
272
217
  return [] unless File.exist?(path)
273
218
 
274
219
  yaml = YAML.safe_load(File.read(path), symbolize_names: true)
275
- return [] unless yaml.is_a?(Array)
276
-
277
- yaml.select { |b| b.is_a?(Hash) && b[:path] && b[:name] }
220
+ filter_valid_bookmarks(yaml)
278
221
  rescue StandardError => e
279
222
  warn "Failed to load bookmarks: #{e.message}"
280
223
  []
281
224
  end
282
225
 
226
+ # 有効なブックマーク要素のみを抽出
227
+ # @param bookmarks [Array<Hash>] ブックマーク配列
228
+ # @return [Array<Hash>] フィルタ済みブックマーク配列
229
+ def filter_valid_bookmarks(bookmarks)
230
+ return [] unless bookmarks.is_a?(Array)
231
+
232
+ bookmarks.select { |b| b.is_a?(Hash) && b[:path] && b[:name] }
233
+ end
234
+
283
235
  # ブックマークを保存
284
236
  # @param path [String] YAMLファイルのパス
285
237
  # @param bookmarks [Array<Hash>] ブックマーク配列
@@ -398,9 +350,9 @@ module Rufio
398
350
  end
399
351
 
400
352
  def detect_language
401
- # Only BENIYA_LANG environment variable takes precedence
353
+ # RUFIO_LANG or BENIYA_LANG (legacy) environment variable takes precedence
402
354
  # This ensures English is default unless explicitly requested
403
- env_lang = ENV['BENIYA_LANG']
355
+ env_lang = ENV['RUFIO_LANG'] || ENV['BENIYA_LANG']
404
356
 
405
357
  if env_lang && !env_lang.empty?
406
358
  # Extract language code (e.g., 'ja_JP.UTF-8' -> 'ja')
@@ -77,7 +77,8 @@ module Rufio
77
77
  help: '?',
78
78
  log_viewer: 'L',
79
79
  command_mode: ':',
80
- quit: 'q'
80
+ quit: 'q',
81
+ quit_cd: 'T'
81
82
  }.freeze
82
83
  end
83
84
 
@@ -282,17 +283,8 @@ module Rufio
282
283
  Config.save_script_paths(SCRIPT_PATHS_YML, paths)
283
284
  end
284
285
 
285
- # YAMLファイルにセクションを保存(Config経由)
286
- def save_to_yaml(key, value)
287
- Config.save_yaml_config(YAML_CONFIG_PATH, key, value)
288
- end
289
-
290
286
  def filter_valid_bookmarks(bookmarks)
291
- return [] unless bookmarks.is_a?(Array)
292
-
293
- bookmarks.select do |b|
294
- b.is_a?(Hash) && b[:path] && b[:name]
295
- end
287
+ Config.filter_valid_bookmarks(bookmarks)
296
288
  end
297
289
 
298
290
  def load_config_file