rufio 0.60.0 → 0.61.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.
@@ -2,13 +2,15 @@
2
2
 
3
3
  require 'json'
4
4
  require 'fileutils'
5
+ require_relative 'bookmark_storage'
5
6
 
6
7
  module Rufio
7
8
  class Bookmark
8
9
  MAX_BOOKMARKS = 9
9
10
 
10
- def initialize(config_file = nil)
11
+ def initialize(config_file = nil, storage: nil)
11
12
  @config_file = config_file || default_config_file
13
+ @storage = storage || JsonBookmarkStorage.new(@config_file)
12
14
  @bookmarks = []
13
15
  ensure_config_directory
14
16
  load
@@ -70,38 +72,12 @@ module Rufio
70
72
  end
71
73
 
72
74
  def save
73
- begin
74
- File.write(@config_file, JSON.pretty_generate(@bookmarks))
75
- true
76
- rescue StandardError => e
77
- warn "Failed to save bookmarks: #{e.message}"
78
- false
79
- end
75
+ @storage.save(@bookmarks)
80
76
  end
81
77
 
82
78
  def load
83
- return true unless File.exist?(@config_file)
84
-
85
- begin
86
- content = File.read(@config_file)
87
- @bookmarks = JSON.parse(content, symbolize_names: true)
88
- @bookmarks = [] unless @bookmarks.is_a?(Array)
89
-
90
- # 無効なブックマークを除去
91
- @bookmarks = @bookmarks.select do |bookmark|
92
- bookmark.is_a?(Hash) &&
93
- bookmark.key?(:path) &&
94
- bookmark.key?(:name) &&
95
- bookmark[:path].is_a?(String) &&
96
- bookmark[:name].is_a?(String)
97
- end
98
-
99
- true
100
- rescue JSON::ParserError, StandardError => e
101
- warn "Failed to load bookmarks: #{e.message}"
102
- @bookmarks = []
103
- true
104
- end
79
+ @bookmarks = @storage.load
80
+ true
105
81
  end
106
82
 
107
83
  private
@@ -1,16 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'bookmark'
4
+ require_relative 'bookmark_storage'
4
5
  require_relative 'config_loader'
5
6
 
6
7
  module Rufio
7
8
  # Manages bookmark operations with interactive UI
8
9
  class BookmarkManager
9
10
  def initialize(bookmark = nil, dialog_renderer = nil)
10
- @bookmark = bookmark || Bookmark.new
11
+ @bookmark = bookmark || create_default_bookmark
11
12
  @dialog_renderer = dialog_renderer
12
13
  end
13
14
 
15
+ private
16
+
17
+ def create_default_bookmark
18
+ # 必要に応じてJSONからYAMLに移行
19
+ ConfigLoader.migrate_bookmarks_if_needed
20
+
21
+ # YAMLストレージを使用
22
+ storage = ConfigLoader.bookmark_storage
23
+ Bookmark.new(ConfigLoader::YAML_CONFIG_PATH, storage: storage)
24
+ end
25
+
26
+ public
27
+
14
28
  # Show bookmark menu and handle user selection
15
29
  # @param current_path [String] Current directory path
16
30
  # @return [Symbol, nil] Action to perform (:navigate, :add, :list, :remove, :cancel)
@@ -0,0 +1,148 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'yaml'
5
+ require 'fileutils'
6
+
7
+ module Rufio
8
+ # ブックマークストレージの基底クラス
9
+ class BookmarkStorage
10
+ def initialize(file_path)
11
+ @file_path = file_path
12
+ end
13
+
14
+ def load
15
+ raise NotImplementedError, 'Subclasses must implement #load'
16
+ end
17
+
18
+ def save(_bookmarks)
19
+ raise NotImplementedError, 'Subclasses must implement #save'
20
+ end
21
+
22
+ protected
23
+
24
+ def ensure_directory
25
+ dir = File.dirname(@file_path)
26
+ FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
27
+ end
28
+
29
+ def valid_bookmark?(bookmark)
30
+ bookmark.is_a?(Hash) &&
31
+ bookmark.key?(:path) &&
32
+ bookmark.key?(:name) &&
33
+ bookmark[:path].is_a?(String) &&
34
+ bookmark[:name].is_a?(String)
35
+ end
36
+
37
+ def filter_valid_bookmarks(bookmarks)
38
+ return [] unless bookmarks.is_a?(Array)
39
+
40
+ bookmarks.select { |b| valid_bookmark?(b) }
41
+ end
42
+ end
43
+
44
+ # JSON形式のブックマークストレージ
45
+ class JsonBookmarkStorage < BookmarkStorage
46
+ def load
47
+ return [] unless File.exist?(@file_path)
48
+
49
+ content = File.read(@file_path)
50
+ bookmarks = JSON.parse(content, symbolize_names: true)
51
+ filter_valid_bookmarks(bookmarks)
52
+ rescue JSON::ParserError, StandardError
53
+ []
54
+ end
55
+
56
+ def save(bookmarks)
57
+ ensure_directory
58
+ File.write(@file_path, JSON.pretty_generate(bookmarks))
59
+ true
60
+ rescue StandardError => e
61
+ warn "Failed to save bookmarks to JSON: #{e.message}"
62
+ false
63
+ end
64
+ end
65
+
66
+ # YAML形式のブックマークストレージ(config.ymlに統合)
67
+ class YamlBookmarkStorage < BookmarkStorage
68
+ def load
69
+ return [] unless File.exist?(@file_path)
70
+
71
+ content = YAML.safe_load(File.read(@file_path), symbolize_names: true)
72
+ return [] unless content.is_a?(Hash) && content[:bookmarks]
73
+
74
+ filter_valid_bookmarks(content[:bookmarks])
75
+ rescue StandardError
76
+ []
77
+ end
78
+
79
+ def save(bookmarks)
80
+ ensure_directory
81
+
82
+ # 既存の設定を読み込み
83
+ existing_config = if File.exist?(@file_path)
84
+ YAML.safe_load(File.read(@file_path), symbolize_names: true) || {}
85
+ else
86
+ {}
87
+ end
88
+
89
+ # ブックマークを文字列キーのハッシュに変換(YAMLの可読性のため)
90
+ bookmarks_for_yaml = bookmarks.map do |b|
91
+ { 'path' => b[:path], 'name' => b[:name] }
92
+ end
93
+
94
+ # ブックマークセクションを更新
95
+ existing_config_string_keys = deep_stringify_keys(existing_config)
96
+ existing_config_string_keys['bookmarks'] = bookmarks_for_yaml
97
+
98
+ File.write(@file_path, YAML.dump(existing_config_string_keys))
99
+ true
100
+ rescue StandardError => e
101
+ warn "Failed to save bookmarks to YAML: #{e.message}"
102
+ false
103
+ end
104
+
105
+ private
106
+
107
+ def deep_stringify_keys(hash)
108
+ return hash unless hash.is_a?(Hash)
109
+
110
+ hash.transform_keys(&:to_s).transform_values do |v|
111
+ case v
112
+ when Hash
113
+ deep_stringify_keys(v)
114
+ when Array
115
+ v.map { |item| item.is_a?(Hash) ? deep_stringify_keys(item) : item }
116
+ else
117
+ v
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ # ブックマークのJSON→YAMLマイグレーター
124
+ class BookmarkMigrator
125
+ class << self
126
+ def migrate(json_path, yaml_path)
127
+ return false unless File.exist?(json_path)
128
+
129
+ # JSONからブックマークを読み込み
130
+ json_storage = JsonBookmarkStorage.new(json_path)
131
+ bookmarks = json_storage.load
132
+
133
+ # YAMLに保存
134
+ yaml_storage = YamlBookmarkStorage.new(yaml_path)
135
+ yaml_storage.save(bookmarks)
136
+
137
+ # JSONファイルをバックアップして削除
138
+ backup_path = "#{json_path}.bak"
139
+ FileUtils.mv(json_path, backup_path)
140
+
141
+ true
142
+ rescue StandardError => e
143
+ warn "Failed to migrate bookmarks: #{e.message}"
144
+ false
145
+ end
146
+ end
147
+ end
148
+ end
@@ -62,9 +62,9 @@ module Rufio
62
62
  # バックグラウンドエグゼキュータが利用可能な場合は非同期実行
63
63
  if @background_executor
64
64
  if @background_executor.execute_async(shell_command)
65
- return "🔄 バックグラウンドで実行中: #{shell_command.split.first}"
65
+ return "🔄 Running in background: #{shell_command.split.first}"
66
66
  else
67
- return "⚠️ 既にコマンドが実行中です"
67
+ return "⚠️ Command already running"
68
68
  end
69
69
  else
70
70
  # バックグラウンドエグゼキュータがない場合は同期実行
@@ -165,9 +165,9 @@ module Rufio
165
165
  if @background_executor.execute_ruby_async(command_display_name) do
166
166
  ScriptExecutor.execute_command(dsl_cmd)
167
167
  end
168
- return "🔄 バックグラウンドで実行中: #{command_display_name}"
168
+ return "🔄 Running in background: #{command_display_name}"
169
169
  else
170
- return "⚠️ 既にコマンドが実行中です"
170
+ return "⚠️ Command already running"
171
171
  end
172
172
  end
173
173
 
@@ -47,13 +47,13 @@ module Rufio
47
47
  # @param suggestions [Array<String>] 補完候補(オプション)
48
48
  def show_input_prompt(input, suggestions = [])
49
49
  # タイトル
50
- title = "コマンドモード"
50
+ title = "Command Mode"
51
51
 
52
- # コンテンツ行を構築
52
+ # Build content lines
53
53
  content_lines = [""]
54
- content_lines << "#{input}_" # カーソルを_で表現
54
+ content_lines << "#{input}_" # Show cursor as _
55
55
  content_lines << ""
56
- content_lines << "Tab: 補完 | Enter: 実行 | ESC: キャンセル"
56
+ content_lines << "Tab: Complete | Enter: Execute | ESC: Cancel"
57
57
 
58
58
  # ウィンドウの色設定(青)
59
59
  border_color = "\e[34m" # Blue
@@ -94,7 +94,7 @@ module Rufio
94
94
  else
95
95
  # 文字列形式の結果(従来の動作)
96
96
  result_text = result
97
- is_error = result.include?("⚠️") || result.include?("エラー")
97
+ is_error = result.include?("⚠️") || result.include?("Error")
98
98
  end
99
99
 
100
100
  # 結果を行に分割
@@ -112,7 +112,7 @@ module Rufio
112
112
  end
113
113
 
114
114
  # ウィンドウタイトル
115
- title = "コマンド実行結果"
115
+ title = "Command Result"
116
116
 
117
117
  # コンテンツ行を構築
118
118
  content_lines = [""] + result_lines + ["", "Press any key to close"]
@@ -207,9 +207,9 @@ module Rufio
207
207
  # 何も出力がない場合
208
208
  if lines.empty?
209
209
  if result[:success]
210
- lines << "コマンドが正常に実行されました"
210
+ lines << "Command executed successfully"
211
211
  else
212
- lines << "コマンドが失敗しました"
212
+ lines << "Command failed"
213
213
  end
214
214
  end
215
215
 
data/lib/rufio/config.rb CHANGED
@@ -79,71 +79,71 @@ module Rufio
79
79
 
80
80
  'ja' => {
81
81
  # Application messages
82
- 'app.interrupted' => 'rufioを中断しました',
83
- 'app.error_occurred' => 'エラーが発生しました',
84
- 'app.terminated' => 'rufioを終了しました',
82
+ 'app.interrupted' => 'rufio interrupted',
83
+ 'app.error_occurred' => 'Error occurred',
84
+ 'app.terminated' => 'rufio terminated',
85
85
 
86
86
  # File operations
87
- 'file.not_found' => 'ファイルが見つかりません',
88
- 'file.not_readable' => 'ファイルを読み取れません',
89
- 'file.read_error' => 'ファイル読み込みエラー',
90
- 'file.binary_file' => 'バイナリファイル',
91
- 'file.cannot_preview' => 'プレビューできません',
92
- 'file.encoding_error' => '文字エンコーディングエラー - ファイルを読み取れません',
93
- 'file.preview_error' => 'プレビューエラー',
94
- 'file.error_prefix' => 'エラー',
87
+ 'file.not_found' => 'File not found',
88
+ 'file.not_readable' => 'File not readable',
89
+ 'file.read_error' => 'File read error',
90
+ 'file.binary_file' => 'Binary file',
91
+ 'file.cannot_preview' => 'Cannot preview',
92
+ 'file.encoding_error' => 'Encoding error - cannot read file',
93
+ 'file.preview_error' => 'Preview error',
94
+ 'file.error_prefix' => 'Error',
95
95
 
96
96
  # Keybind messages
97
- 'keybind.invalid_key' => '無効なキー',
98
- 'keybind.search_text' => '検索テキスト: ',
99
- 'keybind.no_matches' => 'マッチするものが見つかりません。',
100
- 'keybind.press_any_key' => '何かキーを押して続行...',
101
- 'keybind.input_filename' => 'ファイル名を入力: ',
102
- 'keybind.input_dirname' => 'ディレクトリ名を入力: ',
103
- 'keybind.invalid_filename' => '無効なファイル名(/や\\を含むことはできません)',
104
- 'keybind.invalid_dirname' => '無効なディレクトリ名(/や\\を含むことはできません)',
105
- 'keybind.file_exists' => 'ファイルが既に存在します',
106
- 'keybind.directory_exists' => 'ディレクトリが既に存在します',
107
- 'keybind.file_created' => 'ファイルを作成しました',
108
- 'keybind.directory_created' => 'ディレクトリを作成しました',
109
- 'keybind.creation_error' => '作成エラー',
97
+ 'keybind.invalid_key' => 'Invalid key',
98
+ 'keybind.search_text' => 'Search: ',
99
+ 'keybind.no_matches' => 'No matches found.',
100
+ 'keybind.press_any_key' => 'Press any key to continue...',
101
+ 'keybind.input_filename' => 'Enter filename: ',
102
+ 'keybind.input_dirname' => 'Enter directory name: ',
103
+ 'keybind.invalid_filename' => 'Invalid filename (cannot contain / or \\)',
104
+ 'keybind.invalid_dirname' => 'Invalid directory name (cannot contain / or \\)',
105
+ 'keybind.file_exists' => 'File already exists',
106
+ 'keybind.directory_exists' => 'Directory already exists',
107
+ 'keybind.file_created' => 'File created',
108
+ 'keybind.directory_created' => 'Directory created',
109
+ 'keybind.creation_error' => 'Creation error',
110
110
 
111
111
  # UI messages
112
- 'ui.operation_prompt' => '操作: ',
112
+ 'ui.operation_prompt' => 'Operation: ',
113
113
 
114
114
  # Help text
115
- 'help.full' => 'j/k:移動 h:戻る l:入る o:開く g/G:先頭/末尾 r:更新 f:絞込 s:検索 F:内容 a/A:作成 m/p/x:操作 b:ブックマーク z:zoxide 1-9:移動 q:終了',
116
- 'help.short' => 'j/k:移動 h:戻る l:入る o:開く f:絞込 s:検索 b:ブックマーク z:zoxide 1-9:移動 q:終了',
115
+ 'help.full' => 'j/k:move h:back l:enter o:open g/G:top/end r:refresh f:filter s:search F:content a/A:create m/c/x:ops b:bookmark z:zoxide 1-9:jump q:quit',
116
+ 'help.short' => 'j/k:move h:back l:enter o:open f:filter s:search b:bookmark z:zoxide 1-9:jump q:quit',
117
117
 
118
118
  # Health check messages
119
- 'health.title' => 'rufio ヘルスチェック',
120
- 'health.ruby_version' => 'Ruby バージョン',
121
- 'health.required_gems' => '必須 gem',
122
- 'health.fzf' => 'fzf (ファイル検索)',
123
- 'health.rga' => 'rga (内容検索)',
124
- 'health.zoxide' => 'zoxide (ディレクトリ履歴)',
125
- 'health.file_opener' => 'システムファイルオープナー',
126
- 'health.summary' => 'サマリー:',
119
+ 'health.title' => 'rufio Health Check',
120
+ 'health.ruby_version' => 'Ruby version',
121
+ 'health.required_gems' => 'Required gems',
122
+ 'health.fzf' => 'fzf (file search)',
123
+ 'health.rga' => 'rga (content search)',
124
+ 'health.zoxide' => 'zoxide (directory history)',
125
+ 'health.file_opener' => 'System file opener',
126
+ 'health.summary' => 'Summary:',
127
127
  'health.ok' => 'OK',
128
- 'health.warnings' => '警告',
129
- 'health.errors' => 'エラー',
130
- 'health.all_passed' => '全てのチェックが完了しました!rufioは使用可能です。',
131
- 'health.critical_missing' => '重要なコンポーネントが不足しています。rufioは正常に動作しない可能性があります。',
132
- 'health.optional_missing' => 'オプション機能が利用できません。基本機能は動作します。',
133
- 'health.all_gems_installed' => '全ての必須gemがインストールされています',
134
- 'health.missing_gems' => '不足しているgem',
135
- 'health.gem_install_instruction' => '実行: gem install',
136
- 'health.tool_not_found' => 'が見つかりません',
137
- 'health.unknown_platform' => '不明なプラットフォーム',
138
- 'health.file_open_may_not_work' => 'ファイルオープンが正常に動作しない可能性があります',
139
- 'health.macos_opener' => 'macOS ファイルオープナー',
140
- 'health.linux_opener' => 'Linux ファイルオープナー',
141
- 'health.windows_opener' => 'Windows ファイルオープナー',
142
- 'health.install_brew' => 'インストール: brew install',
143
- 'health.install_apt' => 'インストール: apt install',
144
- 'health.install_guide' => 'お使いのプラットフォーム向けのインストールガイドを確認してください',
145
- 'health.rga_releases' => 'インストール: https://github.com/phiresky/ripgrep-all/releases',
146
- 'health.ruby_upgrade_needed' => 'Rubyをバージョン2.7.0以上にアップグレードしてください'
128
+ 'health.warnings' => 'Warnings',
129
+ 'health.errors' => 'Errors',
130
+ 'health.all_passed' => 'All checks passed! rufio is ready to use.',
131
+ 'health.critical_missing' => 'Critical components missing. rufio may not work properly.',
132
+ 'health.optional_missing' => 'Optional features unavailable. Basic features will work.',
133
+ 'health.all_gems_installed' => 'All required gems are installed',
134
+ 'health.missing_gems' => 'Missing gems',
135
+ 'health.gem_install_instruction' => 'Run: gem install',
136
+ 'health.tool_not_found' => 'not found',
137
+ 'health.unknown_platform' => 'Unknown platform',
138
+ 'health.file_open_may_not_work' => 'File open may not work properly',
139
+ 'health.macos_opener' => 'macOS file opener',
140
+ 'health.linux_opener' => 'Linux file opener',
141
+ 'health.windows_opener' => 'Windows file opener',
142
+ 'health.install_brew' => 'Install: brew install',
143
+ 'health.install_apt' => 'Install: apt install',
144
+ 'health.install_guide' => 'Check installation guide for your platform',
145
+ 'health.rga_releases' => 'Install: https://github.com/phiresky/ripgrep-all/releases',
146
+ 'health.ruby_upgrade_needed' => 'Please upgrade Ruby to version 2.7.0 or higher'
147
147
  }
148
148
  }.freeze
149
149
 
@@ -2,11 +2,13 @@
2
2
 
3
3
  require 'yaml'
4
4
  require_relative 'config'
5
+ require_relative 'bookmark_storage'
5
6
 
6
7
  module Rufio
7
8
  class ConfigLoader
8
9
  CONFIG_PATH = File.expand_path('~/.config/rufio/config.rb').freeze
9
10
  YAML_CONFIG_PATH = File.expand_path('~/.config/rufio/config.yml').freeze
11
+ JSON_BOOKMARKS_PATH = File.expand_path('~/.config/rufio/bookmarks.json').freeze
10
12
 
11
13
  class << self
12
14
  def load_config
@@ -97,6 +99,20 @@ module Rufio
97
99
  {}
98
100
  end
99
101
 
102
+ # ブックマーク用のYAMLストレージを取得
103
+ # @return [YamlBookmarkStorage] ストレージインスタンス
104
+ def bookmark_storage
105
+ YamlBookmarkStorage.new(YAML_CONFIG_PATH)
106
+ end
107
+
108
+ # 必要に応じてブックマークをJSONからYAMLに移行
109
+ # @param json_path [String] JSONファイルパス
110
+ # @param yaml_path [String] YAMLファイルパス
111
+ # @return [Boolean] 移行が実行されたかどうか
112
+ def migrate_bookmarks_if_needed(json_path = JSON_BOOKMARKS_PATH, yaml_path = YAML_CONFIG_PATH)
113
+ BookmarkMigrator.migrate(json_path, yaml_path)
114
+ end
115
+
100
116
  private
101
117
 
102
118
  def load_config_file
@@ -1228,7 +1228,7 @@ module Rufio
1228
1228
 
1229
1229
  # バックグラウンドコマンドの場合は結果表示をスキップ
1230
1230
  # (完了通知は別途メインループで表示される)
1231
- if result && !result.to_s.include?("🔄 バックグラウンドで実行中")
1231
+ if result && !result.to_s.include?("🔄 Running in background")
1232
1232
  # コマンド実行結果をフローティングウィンドウで表示
1233
1233
  @command_mode_ui.show_result(result)
1234
1234
  end
@@ -1266,7 +1266,7 @@ module Rufio
1266
1266
 
1267
1267
  # 補完候補を一時的に表示
1268
1268
  def show_completion_candidates(candidates)
1269
- title = "補完候補 (#{candidates.size})"
1269
+ title = "Completions (#{candidates.size})"
1270
1270
 
1271
1271
  # 候補を表示用にフォーマット(最大20件)
1272
1272
  display_candidates = candidates.first(20)
data/lib/rufio/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rufio
4
- VERSION = '0.60.0'
4
+ VERSION = '0.61.0'
5
5
  end
data/lib/rufio.rb CHANGED
@@ -8,6 +8,7 @@ require_relative "rufio/directory_listing"
8
8
  require_relative "rufio/filter_manager"
9
9
  require_relative "rufio/selection_manager"
10
10
  require_relative "rufio/file_operations"
11
+ require_relative "rufio/bookmark_storage"
11
12
  require_relative "rufio/bookmark_manager"
12
13
  require_relative "rufio/bookmark"
13
14
  require_relative "rufio/zoxide_integration"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rufio
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.60.0
4
+ version: 0.61.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - masisz
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-01-24 00:00:00.000000000 Z
11
+ date: 2026-01-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: io-console
@@ -108,8 +108,8 @@ dependencies:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
110
  version: '1.21'
111
- description: A terminal-based file manager inspired by Yazi, written in Ruby with
112
- plugin support
111
+ description: Runtime Unified Flow I/O Operator - A TUI file manager as a unified runtime
112
+ environment for Ruby/Python/PowerShell scripts and external tools.
113
113
  email:
114
114
  - masisz.1567@gmail.com
115
115
  executables:
@@ -150,6 +150,7 @@ files:
150
150
  - lib/rufio/background_command_executor.rb
151
151
  - lib/rufio/bookmark.rb
152
152
  - lib/rufio/bookmark_manager.rb
153
+ - lib/rufio/bookmark_storage.rb
153
154
  - lib/rufio/builtin_commands.rb
154
155
  - lib/rufio/color_helper.rb
155
156
  - lib/rufio/command_completion.rb
@@ -232,5 +233,5 @@ requirements: []
232
233
  rubygems_version: 3.4.19
233
234
  signing_key:
234
235
  specification_version: 4
235
- summary: Ruby file manager
236
+ summary: Runtime Unified Flow I/O Operator - TUI file manager as a unified tool runtime
236
237
  test_files: []