rufio 0.9.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 (47) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +188 -0
  3. data/CHANGELOG_v0.4.0.md +146 -0
  4. data/CHANGELOG_v0.5.0.md +26 -0
  5. data/CHANGELOG_v0.6.0.md +182 -0
  6. data/CHANGELOG_v0.7.0.md +280 -0
  7. data/CHANGELOG_v0.8.0.md +267 -0
  8. data/CHANGELOG_v0.9.0.md +279 -0
  9. data/README.md +631 -0
  10. data/README_EN.md +561 -0
  11. data/Rakefile +156 -0
  12. data/bin/rufio +34 -0
  13. data/config_example.rb +88 -0
  14. data/docs/PLUGIN_GUIDE.md +431 -0
  15. data/docs/plugin_example.rb +119 -0
  16. data/lib/rufio/application.rb +32 -0
  17. data/lib/rufio/bookmark.rb +115 -0
  18. data/lib/rufio/bookmark_manager.rb +173 -0
  19. data/lib/rufio/color_helper.rb +150 -0
  20. data/lib/rufio/command_mode.rb +72 -0
  21. data/lib/rufio/command_mode_ui.rb +168 -0
  22. data/lib/rufio/config.rb +199 -0
  23. data/lib/rufio/config_loader.rb +110 -0
  24. data/lib/rufio/dialog_renderer.rb +127 -0
  25. data/lib/rufio/directory_listing.rb +113 -0
  26. data/lib/rufio/file_opener.rb +140 -0
  27. data/lib/rufio/file_operations.rb +231 -0
  28. data/lib/rufio/file_preview.rb +200 -0
  29. data/lib/rufio/filter_manager.rb +114 -0
  30. data/lib/rufio/health_checker.rb +246 -0
  31. data/lib/rufio/keybind_handler.rb +828 -0
  32. data/lib/rufio/logger.rb +103 -0
  33. data/lib/rufio/plugin.rb +89 -0
  34. data/lib/rufio/plugin_config.rb +59 -0
  35. data/lib/rufio/plugin_manager.rb +84 -0
  36. data/lib/rufio/plugins/file_operations.rb +44 -0
  37. data/lib/rufio/selection_manager.rb +79 -0
  38. data/lib/rufio/terminal_ui.rb +630 -0
  39. data/lib/rufio/text_utils.rb +108 -0
  40. data/lib/rufio/version.rb +5 -0
  41. data/lib/rufio/zoxide_integration.rb +188 -0
  42. data/lib/rufio.rb +33 -0
  43. data/publish_gem.zsh +131 -0
  44. data/rufio.gemspec +40 -0
  45. data/test_delete/test1.txt +1 -0
  46. data/test_delete/test2.txt +1 -0
  47. metadata +189 -0
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rufio
4
+ # Unified logger for debug and error messages
5
+ # Only logs when BENIYA_DEBUG environment variable is set to '1'
6
+ class Logger
7
+ LOG_FILE = File.join(Dir.home, '.rufio_debug.log')
8
+
9
+ # Log levels
10
+ DEBUG = :debug
11
+ INFO = :info
12
+ WARN = :warn
13
+ ERROR = :error
14
+
15
+ class << self
16
+ # Log a debug message with optional context
17
+ # @param message [String] The log message
18
+ # @param context [Hash] Additional context information
19
+ def debug(message, context: {})
20
+ return unless debug_enabled?
21
+
22
+ write_log(DEBUG, message, context)
23
+ end
24
+
25
+ # Log an info message
26
+ # @param message [String] The log message
27
+ # @param context [Hash] Additional context information
28
+ def info(message, context: {})
29
+ return unless debug_enabled?
30
+
31
+ write_log(INFO, message, context)
32
+ end
33
+
34
+ # Log a warning message
35
+ # @param message [String] The log message
36
+ # @param context [Hash] Additional context information
37
+ def warn(message, context: {})
38
+ return unless debug_enabled?
39
+
40
+ write_log(WARN, message, context)
41
+ end
42
+
43
+ # Log an error message with optional exception
44
+ # @param message [String] The error message
45
+ # @param exception [Exception, nil] Optional exception object
46
+ # @param context [Hash] Additional context information
47
+ def error(message, exception: nil, context: {})
48
+ return unless debug_enabled?
49
+
50
+ full_context = context.dup
51
+ if exception
52
+ full_context[:exception] = exception.message
53
+ full_context[:backtrace] = exception.backtrace&.first(5)
54
+ end
55
+
56
+ write_log(ERROR, message, full_context)
57
+ end
58
+
59
+ # Clear the log file
60
+ def clear_log
61
+ return unless debug_enabled?
62
+
63
+ File.open(LOG_FILE, 'w') { |f| f.puts "=== Rufio Debug Log Cleared at #{Time.now} ===" }
64
+ end
65
+
66
+ private
67
+
68
+ # Check if debug logging is enabled
69
+ # @return [Boolean]
70
+ def debug_enabled?
71
+ ENV['BENIYA_DEBUG'] == '1'
72
+ end
73
+
74
+ # Write a log entry to the log file
75
+ # @param level [Symbol] Log level
76
+ # @param message [String] Log message
77
+ # @param context [Hash] Context information
78
+ def write_log(level, message, context)
79
+ File.open(LOG_FILE, 'a') do |f|
80
+ timestamp = Time.now.strftime('%Y-%m-%d %H:%M:%S')
81
+ f.puts "[#{timestamp}] [#{level.to_s.upcase}] #{message}"
82
+
83
+ unless context.empty?
84
+ f.puts ' Context:'
85
+ context.each do |key, value|
86
+ if value.is_a?(Array) && value.length > 10
87
+ f.puts " #{key}: [#{value.length} items]"
88
+ else
89
+ f.puts " #{key}: #{value.inspect}"
90
+ end
91
+ end
92
+ end
93
+
94
+ f.puts ''
95
+ end
96
+ rescue StandardError => e
97
+ # Silently fail if we can't write to log file
98
+ # Don't want logging to break the application
99
+ warn "Failed to write to log file: #{e.message}"
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rufio
4
+ # プラグインを格納するモジュール
5
+ module Plugins
6
+ end
7
+
8
+ # プラグインの基底クラス
9
+ class Plugin
10
+ # 依存gemが不足している場合に投げられるエラー
11
+ class DependencyError < StandardError; end
12
+
13
+ class << self
14
+ # 継承時に自動的にPluginManagerに登録する
15
+ def inherited(subclass)
16
+ super
17
+ PluginManager.register(subclass)
18
+ end
19
+
20
+ # 依存gemを宣言する
21
+ def requires(*gems)
22
+ @required_gems ||= []
23
+ @required_gems.concat(gems)
24
+ end
25
+
26
+ # 宣言された依存gemのリストを取得する
27
+ def required_gems
28
+ @required_gems || []
29
+ end
30
+ end
31
+
32
+ # 初期化時に依存gemをチェックする
33
+ def initialize
34
+ check_dependencies!
35
+ end
36
+
37
+ # プラグイン名(必須オーバーライド)
38
+ def name
39
+ raise NotImplementedError, "#{self.class}#name must be implemented"
40
+ end
41
+
42
+ # プラグインの説明(オプション)
43
+ def description
44
+ ""
45
+ end
46
+
47
+ # プラグインのバージョン(オプション)
48
+ def version
49
+ "1.0.0"
50
+ end
51
+
52
+ # コマンド定義(オプション)
53
+ # { command_name: method(:method_name) } の形式で返す
54
+ def commands
55
+ {}
56
+ end
57
+
58
+ private
59
+
60
+ # 依存gemが全て利用可能かチェックする
61
+ def check_dependencies!
62
+ required_gems = self.class.required_gems
63
+ return if required_gems.empty?
64
+
65
+ missing_gems = []
66
+
67
+ required_gems.each do |gem_name|
68
+ begin
69
+ Gem::Specification.find_by_name(gem_name)
70
+ rescue Gem::LoadError
71
+ missing_gems << gem_name
72
+ end
73
+ end
74
+
75
+ return if missing_gems.empty?
76
+
77
+ # 不足しているgemがある場合はエラーを投げる
78
+ error_message = <<~ERROR
79
+ Plugin '#{name}' は以下のgemに依存していますが、インストールされていません:
80
+ - #{missing_gems.join("\n - ")}
81
+
82
+ 以下のコマンドでインストールしてください:
83
+ gem install #{missing_gems.join(' ')}
84
+ ERROR
85
+
86
+ raise DependencyError, error_message
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+
5
+ module Rufio
6
+ # プラグインの設定を管理するクラス
7
+ class PluginConfig
8
+ class << self
9
+ # 設定ファイルを読み込む
10
+ def load
11
+ config_path = File.expand_path('~/.rufio/config.yml')
12
+
13
+ if File.exist?(config_path)
14
+ begin
15
+ @config = YAML.load_file(config_path) || {}
16
+ rescue StandardError => e
17
+ warn "⚠️ Failed to load config file: #{e.message}"
18
+ @config = {}
19
+ end
20
+ else
21
+ # 設定ファイルが存在しない場合はデフォルト設定(空のハッシュ)
22
+ @config = {}
23
+ end
24
+ end
25
+
26
+ # プラグインが有効かどうかをチェックする
27
+ def plugin_enabled?(name)
28
+ # 設定が未読み込みの場合は読み込む
29
+ load if @config.nil?
30
+
31
+ # pluginsセクションがない場合は全プラグイン有効
32
+ return true unless @config.is_a?(Hash) && @config['plugins']
33
+
34
+ plugins_config = @config['plugins']
35
+ return true unless plugins_config.is_a?(Hash)
36
+
37
+ # プラグイン名を小文字に統一して検索
38
+ normalized_name = name.to_s.downcase
39
+
40
+ # 設定のキーも小文字に変換して検索
41
+ plugin_setting = nil
42
+ plugins_config.each do |key, value|
43
+ if key.downcase == normalized_name
44
+ plugin_setting = value
45
+ break
46
+ end
47
+ end
48
+
49
+ # 設定が存在しない場合は有効とみなす
50
+ return true if plugin_setting.nil?
51
+
52
+ # enabled設定を確認(デフォルトはtrue)
53
+ return true unless plugin_setting.is_a?(Hash)
54
+
55
+ plugin_setting.fetch('enabled', true)
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rufio
4
+ # プラグインを管理するクラス
5
+ class PluginManager
6
+ class << self
7
+ # 登録済みプラグインクラスのリスト
8
+ def plugins
9
+ @plugins ||= []
10
+ end
11
+
12
+ # プラグインを登録する
13
+ def register(plugin_class)
14
+ @plugins ||= []
15
+ @plugins << plugin_class unless @plugins.include?(plugin_class)
16
+ end
17
+
18
+ # 全プラグインを読み込む(本体同梱 + ユーザープラグイン)
19
+ def load_all
20
+ load_builtin_plugins
21
+ load_user_plugins
22
+ end
23
+
24
+ # 有効なプラグインインスタンスのリストを取得
25
+ def enabled_plugins
26
+ return @enabled_plugins if @enabled_plugins
27
+
28
+ @enabled_plugins = []
29
+
30
+ plugins.each do |plugin_class|
31
+ # プラグイン名を取得(クラス名から推測)
32
+ plugin_name = plugin_class.name.split('::').last
33
+
34
+ # PluginConfigで有効かチェック
35
+ next unless PluginConfig.plugin_enabled?(plugin_name)
36
+
37
+ # プラグインのインスタンスを作成
38
+ begin
39
+ plugin_instance = plugin_class.new
40
+ @enabled_plugins << plugin_instance
41
+ rescue Plugin::DependencyError => e
42
+ warn "⚠️ #{e.message}"
43
+ # プラグインは無効化されるが、rufioは起動継続
44
+ rescue StandardError => e
45
+ warn "⚠️ Failed to load plugin #{plugin_name}: #{e.message}"
46
+ end
47
+ end
48
+
49
+ @enabled_plugins
50
+ end
51
+
52
+ private
53
+
54
+ # 本体同梱プラグインを読み込む
55
+ def load_builtin_plugins
56
+ # plugin_manager.rbは/lib/rufio/にあるので、pluginsディレクトリは同じディレクトリ内
57
+ builtin_plugins_dir = File.join(__dir__, 'plugins')
58
+ return unless Dir.exist?(builtin_plugins_dir)
59
+
60
+ Dir.glob(File.join(builtin_plugins_dir, '*.rb')).sort.each do |file|
61
+ begin
62
+ require file
63
+ rescue StandardError => e
64
+ warn "⚠️ Failed to load builtin plugin #{File.basename(file)}: #{e.message}"
65
+ end
66
+ end
67
+ end
68
+
69
+ # ユーザープラグインを読み込む
70
+ def load_user_plugins
71
+ user_plugins_dir = File.expand_path('~/.rufio/plugins')
72
+ return unless Dir.exist?(user_plugins_dir)
73
+
74
+ Dir.glob(File.join(user_plugins_dir, '*.rb')).sort.each do |file|
75
+ begin
76
+ require file
77
+ rescue SyntaxError, StandardError => e
78
+ warn "⚠️ Failed to load user plugin #{File.basename(file)}: #{e.message}"
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rufio
4
+ module Plugins
5
+ # 基本的なファイル操作を提供するプラグイン
6
+ class FileOperations < Plugin
7
+ def name
8
+ "FileOperations"
9
+ end
10
+
11
+ def description
12
+ "基本的なファイル操作(コピー、移動、削除)"
13
+ end
14
+
15
+ def commands
16
+ {
17
+ copy: method(:copy),
18
+ move: method(:move),
19
+ delete: method(:delete)
20
+ }
21
+ end
22
+
23
+ private
24
+
25
+ # ファイルコピー(スタブ実装)
26
+ def copy
27
+ # 実装は将来追加
28
+ nil
29
+ end
30
+
31
+ # ファイル移動(スタブ実装)
32
+ def move
33
+ # 実装は将来追加
34
+ nil
35
+ end
36
+
37
+ # ファイル削除(スタブ実装)
38
+ def delete
39
+ # 実装は将来追加
40
+ nil
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rufio
4
+ # Manages selected items (files/directories) for bulk operations
5
+ class SelectionManager
6
+ def initialize
7
+ @selected_items = []
8
+ end
9
+
10
+ # Toggle selection for an entry
11
+ # @param entry [Hash] Entry with :name key
12
+ # @return [Boolean] true if now selected, false if unselected
13
+ def toggle_selection(entry)
14
+ return false unless entry
15
+
16
+ if @selected_items.include?(entry[:name])
17
+ @selected_items.delete(entry[:name])
18
+ false
19
+ else
20
+ @selected_items << entry[:name]
21
+ true
22
+ end
23
+ end
24
+
25
+ # Check if an entry is selected
26
+ # @param entry_name [String] Entry name
27
+ # @return [Boolean]
28
+ def selected?(entry_name)
29
+ @selected_items.include?(entry_name)
30
+ end
31
+
32
+ # Get all selected items
33
+ # @return [Array<String>] Copy of selected items
34
+ def selected_items
35
+ @selected_items.dup
36
+ end
37
+
38
+ # Clear all selections
39
+ def clear
40
+ @selected_items.clear
41
+ end
42
+
43
+ # Check if any items are selected
44
+ # @return [Boolean]
45
+ def any?
46
+ !@selected_items.empty?
47
+ end
48
+
49
+ # Get the count of selected items
50
+ # @return [Integer]
51
+ def count
52
+ @selected_items.length
53
+ end
54
+
55
+ # Add an item to selection
56
+ # @param item_name [String] Item name
57
+ def add(item_name)
58
+ @selected_items << item_name unless @selected_items.include?(item_name)
59
+ end
60
+
61
+ # Remove an item from selection
62
+ # @param item_name [String] Item name
63
+ def remove(item_name)
64
+ @selected_items.delete(item_name)
65
+ end
66
+
67
+ # Select multiple items
68
+ # @param item_names [Array<String>] Item names
69
+ def select_multiple(item_names)
70
+ item_names.each { |name| add(name) }
71
+ end
72
+
73
+ # Check if selection is empty
74
+ # @return [Boolean]
75
+ def empty?
76
+ @selected_items.empty?
77
+ end
78
+ end
79
+ end