beniya 0.6.0 → 0.6.2

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.
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Beniya
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, '.beniya_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 "=== Beniya 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,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Beniya
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
@@ -4,14 +4,43 @@ require 'io/console'
4
4
 
5
5
  module Beniya
6
6
  class TerminalUI
7
+ # Layout constants
8
+ HEADER_HEIGHT = 2 # Header占有行数
9
+ FOOTER_HEIGHT = 1 # Footer占有行数
10
+ HEADER_FOOTER_MARGIN = 4 # Header + Footer分のマージン
11
+
12
+ # Panel layout ratios
13
+ LEFT_PANEL_RATIO = 0.5 # 左パネルの幅比率
14
+ RIGHT_PANEL_RATIO = 1.0 - LEFT_PANEL_RATIO
15
+
16
+ # Display constants
17
+ DEFAULT_SCREEN_WIDTH = 80 # デフォルト画面幅
18
+ DEFAULT_SCREEN_HEIGHT = 24 # デフォルト画面高さ
19
+ HEADER_PADDING = 2 # ヘッダーのパディング
20
+ BASE_INFO_RESERVED_WIDTH = 20 # ベースディレクトリ表示の予約幅
21
+ BASE_INFO_MIN_WIDTH = 10 # ベースディレクトリ表示の最小幅
22
+ FILTER_TEXT_RESERVED = 15 # フィルタテキスト表示の予約幅
23
+
24
+ # File display constants
25
+ ICON_SIZE_PADDING = 12 # アイコン、選択マーク、サイズ情報分
26
+ CURSOR_OFFSET = 1 # カーソル位置のオフセット
27
+
28
+ # Size display constants (bytes)
29
+ KILOBYTE = 1024
30
+ MEGABYTE = KILOBYTE * 1024
31
+ GIGABYTE = MEGABYTE * 1024
32
+
33
+ # Line offsets
34
+ CONTENT_START_LINE = 3 # コンテンツ開始行(ヘッダー2行スキップ)
35
+
7
36
  def initialize
8
37
  console = IO.console
9
38
  if console
10
39
  @screen_width, @screen_height = console.winsize.reverse
11
40
  else
12
41
  # fallback values (for test environments etc.)
13
- @screen_width = 80
14
- @screen_height = 24
42
+ @screen_width = DEFAULT_SCREEN_WIDTH
43
+ @screen_height = DEFAULT_SCREEN_HEIGHT
15
44
  end
16
45
  @running = false
17
46
  end
@@ -83,9 +112,9 @@ module Beniya
83
112
  entries = get_display_entries
84
113
  selected_entry = entries[@keybind_handler.current_index]
85
114
 
86
- # calculate height with header (2 lines) and footer margin
87
- content_height = @screen_height - 4 # ヘッダー(2行)とフッター分を除く
88
- left_width = @screen_width / 2
115
+ # calculate height with header and footer margin
116
+ content_height = @screen_height - HEADER_FOOTER_MARGIN
117
+ left_width = (@screen_width * LEFT_PANEL_RATIO).to_i
89
118
  right_width = @screen_width - left_width
90
119
 
91
120
  # adjust so right panel doesn't overflow into left panel
@@ -112,14 +141,14 @@ module Beniya
112
141
  end
113
142
 
114
143
  # abbreviate if path is too long
115
- if header.length > @screen_width - 2
144
+ if header.length > @screen_width - HEADER_PADDING
116
145
  if @keybind_handler.filter_active?
117
146
  # prioritize showing filter when active
118
147
  filter_text = " [Filter: #{@keybind_handler.filter_query}]"
119
- base_length = @screen_width - filter_text.length - 15
148
+ base_length = @screen_width - filter_text.length - FILTER_TEXT_RESERVED
120
149
  header = "📁 beniya - ...#{current_path[-base_length..-1]}#{filter_text}"
121
150
  else
122
- header = "📁 beniya - ...#{current_path[-(@screen_width - 15)..-1]}"
151
+ header = "📁 beniya - ...#{current_path[-(@screen_width - FILTER_TEXT_RESERVED)..-1]}"
123
152
  end
124
153
  end
125
154
 
@@ -143,15 +172,15 @@ module Beniya
143
172
  end
144
173
 
145
174
  # 長すぎる場合は省略
146
- if base_info.length > @screen_width - 2
175
+ if base_info.length > @screen_width - HEADER_PADDING
147
176
  if base_info.include?(" | Selected:")
148
177
  selected_part = base_info.split(" | Selected:").last
149
- available_length = @screen_width - 20 - " | Selected:#{selected_part}".length
178
+ available_length = @screen_width - BASE_INFO_RESERVED_WIDTH - " | Selected:#{selected_part}".length
150
179
  else
151
- available_length = @screen_width - 20
180
+ available_length = @screen_width - BASE_INFO_RESERVED_WIDTH
152
181
  end
153
182
 
154
- if available_length > 10
183
+ if available_length > BASE_INFO_MIN_WIDTH
155
184
  # パスの最後の部分を表示
156
185
  dir_part = base_info.split(": ").last.split(" | ").first
157
186
  short_base_dir = "...#{dir_part[-available_length..-1]}"
@@ -170,7 +199,7 @@ module Beniya
170
199
 
171
200
  (0...height).each do |i|
172
201
  entry_index = start_index + i
173
- line_num = i + 3 # skip header (2 lines)
202
+ line_num = i + CONTENT_START_LINE
174
203
 
175
204
  print "\e[#{line_num};1H" # set cursor position
176
205
 
@@ -181,7 +210,7 @@ module Beniya
181
210
  draw_entry_line(entry, width, is_selected)
182
211
  else
183
212
  # 左ペイン専用の安全な幅で空行を出力
184
- safe_width = [width - 1, @screen_width / 2 - 1].min
213
+ safe_width = [width - CURSOR_OFFSET, (@screen_width * LEFT_PANEL_RATIO).to_i - CURSOR_OFFSET].min
185
214
  print ' ' * safe_width
186
215
  end
187
216
  end
@@ -192,14 +221,14 @@ module Beniya
192
221
  icon, color = get_entry_display_info(entry)
193
222
 
194
223
  # 左ペイン専用の安全な幅を計算(右ペインにはみ出さないよう)
195
- safe_width = [width - 1, @screen_width / 2 - 1].min
224
+ safe_width = [width - CURSOR_OFFSET, (@screen_width * LEFT_PANEL_RATIO).to_i - CURSOR_OFFSET].min
196
225
 
197
226
  # 選択マークの追加
198
227
  selection_mark = @keybind_handler.is_selected?(entry[:name]) ? "✓ " : " "
199
228
 
200
229
  # ファイル名(必要に応じて切り詰め)
201
230
  name = entry[:name]
202
- max_name_length = safe_width - 12 # アイコン、選択マーク、サイズ情報分を除く
231
+ max_name_length = safe_width - ICON_SIZE_PADDING
203
232
  name = name[0...max_name_length - 3] + '...' if max_name_length > 0 && name.length > max_name_length
204
233
 
205
234
  # サイズ情報
@@ -260,22 +289,22 @@ module Beniya
260
289
  def format_size(size)
261
290
  return ' ' if size == 0
262
291
 
263
- if size < 1024
292
+ if size < KILOBYTE
264
293
  "#{size}B".rjust(6)
265
- elsif size < 1024 * 1024
266
- "#{(size / 1024.0).round(1)}K".rjust(6)
267
- elsif size < 1024 * 1024 * 1024
268
- "#{(size / (1024.0 * 1024)).round(1)}M".rjust(6)
294
+ elsif size < MEGABYTE
295
+ "#{(size / KILOBYTE.to_f).round(1)}K".rjust(6)
296
+ elsif size < GIGABYTE
297
+ "#{(size / MEGABYTE.to_f).round(1)}M".rjust(6)
269
298
  else
270
- "#{(size / (1024.0 * 1024 * 1024)).round(1)}G".rjust(6)
299
+ "#{(size / GIGABYTE.to_f).round(1)}G".rjust(6)
271
300
  end
272
301
  end
273
302
 
274
303
  def draw_file_preview(selected_entry, width, height, left_offset)
275
304
  (0...height).each do |i|
276
- line_num = i + 3 # skip header (2 lines)
305
+ line_num = i + CONTENT_START_LINE
277
306
  # カーソル位置を左パネルの右端に設定
278
- cursor_position = left_offset + 1
307
+ cursor_position = left_offset + CURSOR_OFFSET
279
308
 
280
309
  # 画面の境界を厳密に計算
281
310
  max_chars_from_cursor = @screen_width - cursor_position
@@ -450,7 +479,7 @@ module Beniya
450
479
 
451
480
  def draw_footer
452
481
  # 最下行から1行上に表示してスクロールを避ける
453
- footer_line = @screen_height - 1
482
+ footer_line = @screen_height - FOOTER_HEIGHT
454
483
  print "\e[#{footer_line};1H"
455
484
 
456
485
  if @keybind_handler.filter_active?
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Beniya
4
+ # Text utility methods for display width calculation and string manipulation
5
+ # Handles multi-byte characters (Japanese, etc.) correctly
6
+ module TextUtils
7
+ module_function
8
+
9
+ # Character width constants
10
+ FULLWIDTH_CHAR_WIDTH = 2
11
+ HALFWIDTH_CHAR_WIDTH = 1
12
+ MULTIBYTE_THRESHOLD = 1
13
+
14
+ # Truncation constants
15
+ ELLIPSIS_MIN_WIDTH = 3
16
+ ELLIPSIS = '...'
17
+
18
+ # Line break constants
19
+ BREAK_POINT_THRESHOLD = 0.5 # Break after 50% of max_width
20
+
21
+ # Calculate display width of a string
22
+ # Full-width characters (Japanese, etc.) count as 2, half-width as 1
23
+ def display_width(string)
24
+ string.each_char.map do |char|
25
+ case char
26
+ when /[\u3000-\u303F\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FAF\uFF00-\uFFEF]/
27
+ FULLWIDTH_CHAR_WIDTH # Japanese characters (hiragana, katakana, kanji, full-width symbols)
28
+ when /[\u0020-\u007E]/
29
+ HALFWIDTH_CHAR_WIDTH # ASCII characters
30
+ else
31
+ char.bytesize > MULTIBYTE_THRESHOLD ? FULLWIDTH_CHAR_WIDTH : HALFWIDTH_CHAR_WIDTH
32
+ end
33
+ end.sum
34
+ end
35
+
36
+ # Truncate string to fit within max_width
37
+ def truncate_to_width(string, max_width)
38
+ return string if display_width(string) <= max_width
39
+
40
+ result = ''
41
+ current_width = 0
42
+
43
+ string.each_char do |char|
44
+ char_width = display_width(char)
45
+ break if current_width + char_width > max_width
46
+
47
+ result += char
48
+ current_width += char_width
49
+ end
50
+
51
+ # Add ellipsis if there's room
52
+ result += ELLIPSIS if max_width >= ELLIPSIS_MIN_WIDTH && current_width <= max_width - ELLIPSIS_MIN_WIDTH
53
+ result
54
+ end
55
+
56
+ # Pad string to target_width with spaces
57
+ def pad_string_to_width(string, target_width)
58
+ current_width = display_width(string)
59
+ if current_width >= target_width
60
+ truncate_to_width(string, target_width)
61
+ else
62
+ string + ' ' * (target_width - current_width)
63
+ end
64
+ end
65
+
66
+ # Find the best break point for wrapping text within max_width
67
+ def find_break_point(line, max_width)
68
+ return line.length if display_width(line) <= max_width
69
+
70
+ current_width = 0
71
+ best_break_point = 0
72
+ space_break_point = nil
73
+ punct_break_point = nil
74
+
75
+ line.each_char.with_index do |char, index|
76
+ char_width = display_width(char)
77
+ break if current_width + char_width > max_width
78
+
79
+ current_width += char_width
80
+ best_break_point = index + 1
81
+
82
+ # Record break point at space
83
+ space_break_point = index + 1 if char == ' ' && current_width > max_width * BREAK_POINT_THRESHOLD
84
+
85
+ # Record break point at Japanese punctuation
86
+ punct_break_point = index + 1 if char.match?(/[、。,.!?]/) && current_width > max_width * BREAK_POINT_THRESHOLD
87
+ end
88
+
89
+ space_break_point || punct_break_point || best_break_point
90
+ end
91
+ end
92
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Beniya
4
- VERSION = '0.6.0'
4
+ VERSION = '0.6.2'
5
5
  end
@@ -0,0 +1,188 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'shellwords'
4
+
5
+ module Beniya
6
+ # Integrates with zoxide for directory history navigation
7
+ class ZoxideIntegration
8
+ # Dialog size constants
9
+ DIALOG_WIDTH = 45
10
+ DIALOG_BORDER_HEIGHT = 4
11
+
12
+ def initialize(dialog_renderer = nil)
13
+ @dialog_renderer = dialog_renderer
14
+ end
15
+
16
+ # Check if zoxide is available
17
+ # @return [Boolean]
18
+ def available?
19
+ system('which zoxide > /dev/null 2>&1')
20
+ end
21
+
22
+ # Get zoxide history
23
+ # @return [Array<Hash>] Array of { path: String, score: Float }
24
+ def get_history
25
+ return [] unless available?
26
+
27
+ begin
28
+ # Get zoxide history with scores
29
+ output = `zoxide query --list --score 2>/dev/null`.strip
30
+ return [] if output.empty?
31
+
32
+ # Parse each line into path and score
33
+ lines = output.split("\n")
34
+ history = lines.map do |line|
35
+ # zoxide output format: "score path"
36
+ if line.match(/^\s*(\d+(?:\.\d+)?)\s+(.+)$/)
37
+ score = ::Regexp.last_match(1).to_f
38
+ path = ::Regexp.last_match(2).strip
39
+ { path: path, score: score }
40
+ else
41
+ # No score (backward compatibility)
42
+ { path: line.strip, score: 0.0 }
43
+ end
44
+ end
45
+
46
+ # Filter to only existing directories
47
+ history.select { |entry| Dir.exist?(entry[:path]) }
48
+ rescue StandardError
49
+ []
50
+ end
51
+ end
52
+
53
+ # Show zoxide history menu and let user select
54
+ # @return [String, nil] Selected path or nil if cancelled
55
+ def show_menu
56
+ return nil unless @dialog_renderer
57
+
58
+ history = get_history
59
+
60
+ if history.empty?
61
+ show_no_history_message
62
+ return nil
63
+ end
64
+
65
+ select_from_history(history)
66
+ end
67
+
68
+ # Add directory to zoxide history
69
+ # @param path [String] Directory path
70
+ # @return [Boolean] Success status
71
+ def add_to_history(path)
72
+ return false unless available?
73
+ return false unless Dir.exist?(path)
74
+
75
+ begin
76
+ system("zoxide add #{Shellwords.escape(path)} > /dev/null 2>&1")
77
+ true
78
+ rescue StandardError
79
+ false
80
+ end
81
+ end
82
+
83
+ private
84
+
85
+ # Show message when no history is available
86
+ def show_no_history_message
87
+ return unless @dialog_renderer
88
+
89
+ title = 'Zoxide'
90
+ content_lines = [
91
+ '',
92
+ 'No zoxide history found.',
93
+ '',
94
+ 'Zoxide learns from your directory navigation.',
95
+ 'Use zoxide more to build up history.',
96
+ '',
97
+ 'Press any key to continue...'
98
+ ]
99
+
100
+ dialog_width = DIALOG_WIDTH
101
+ dialog_height = DIALOG_BORDER_HEIGHT + content_lines.length
102
+ x, y = @dialog_renderer.calculate_center(dialog_width, dialog_height)
103
+
104
+ @dialog_renderer.draw_floating_window(x, y, dialog_width, dialog_height, title, content_lines, {
105
+ border_color: "\e[33m", # Yellow
106
+ title_color: "\e[1;33m", # Bold yellow
107
+ content_color: "\e[37m" # White
108
+ })
109
+
110
+ STDIN.getch
111
+ @dialog_renderer.clear_area(x, y, dialog_width, dialog_height)
112
+ end
113
+
114
+ # Select from zoxide history
115
+ # @param history [Array<Hash>] History entries
116
+ # @return [String, nil] Selected path or nil
117
+ def select_from_history(history)
118
+ return nil unless @dialog_renderer
119
+
120
+ title = 'Zoxide History'
121
+
122
+ # Format history for display (max 20 items)
123
+ display_history = history.first(20)
124
+ content_lines = ['']
125
+
126
+ display_history.each_with_index do |entry, index|
127
+ # Shorten path display (replace home directory with ~)
128
+ display_path = entry[:path].gsub(ENV['HOME'], '~')
129
+ line = " #{index + 1}. #{display_path}"
130
+ # Truncate if too long
131
+ line = line[0...60] + '...' if line.length > 63
132
+ content_lines << line
133
+ end
134
+
135
+ content_lines << ''
136
+ content_lines << 'Enter number (1-' + display_history.length.to_s + ') or ESC to cancel'
137
+
138
+ dialog_width = 70
139
+ dialog_height = [4 + content_lines.length, 25].min
140
+ x, y = @dialog_renderer.calculate_center(dialog_width, dialog_height)
141
+
142
+ @dialog_renderer.draw_floating_window(x, y, dialog_width, dialog_height, title, content_lines, {
143
+ border_color: "\e[36m", # Cyan
144
+ title_color: "\e[1;36m", # Bold cyan
145
+ content_color: "\e[37m" # White
146
+ })
147
+
148
+ # Number input mode
149
+ input_buffer = ''
150
+
151
+ loop do
152
+ char = STDIN.getch
153
+
154
+ case char
155
+ when "\e", "\x03" # ESC, Ctrl+C
156
+ @dialog_renderer.clear_area(x, y, dialog_width, dialog_height)
157
+ return nil
158
+ when "\r", "\n" # Enter
159
+ unless input_buffer.empty?
160
+ number = input_buffer.to_i
161
+ if number > 0 && number <= display_history.length
162
+ selected_entry = display_history[number - 1]
163
+ @dialog_renderer.clear_area(x, y, dialog_width, dialog_height)
164
+ return selected_entry[:path]
165
+ end
166
+ end
167
+ # Invalid input, ask again
168
+ input_buffer = ''
169
+ when "\u007f", "\b" # Backspace
170
+ input_buffer = input_buffer[0...-1] unless input_buffer.empty?
171
+ when /[0-9]/
172
+ input_buffer += char
173
+ # Max 2 digits
174
+ input_buffer = input_buffer[-2..-1] if input_buffer.length > 2
175
+
176
+ # If number is within range, select immediately
177
+ number = input_buffer.to_i
178
+ if number > 0 && number <= display_history.length &&
179
+ (number >= 10 || input_buffer.length == 1)
180
+ selected_entry = display_history[number - 1]
181
+ @dialog_renderer.clear_area(x, y, dialog_width, dialog_height)
182
+ return selected_entry[:path]
183
+ end
184
+ end
185
+ end
186
+ end
187
+ end
188
+ end
data/lib/beniya.rb CHANGED
@@ -5,6 +5,15 @@ require_relative "beniya/config"
5
5
  require_relative "beniya/config_loader"
6
6
  require_relative "beniya/color_helper"
7
7
  require_relative "beniya/directory_listing"
8
+ require_relative "beniya/filter_manager"
9
+ require_relative "beniya/selection_manager"
10
+ require_relative "beniya/file_operations"
11
+ require_relative "beniya/bookmark_manager"
12
+ require_relative "beniya/bookmark"
13
+ require_relative "beniya/zoxide_integration"
14
+ require_relative "beniya/dialog_renderer"
15
+ require_relative "beniya/text_utils"
16
+ require_relative "beniya/logger"
8
17
  require_relative "beniya/keybind_handler"
9
18
  require_relative "beniya/file_preview"
10
19
  require_relative "beniya/terminal_ui"
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: beniya
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - masisz
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-09-28 00:00:00.000000000 Z
10
+ date: 2025-10-27 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: io-console
@@ -129,16 +129,24 @@ files:
129
129
  - lib/beniya.rb
130
130
  - lib/beniya/application.rb
131
131
  - lib/beniya/bookmark.rb
132
+ - lib/beniya/bookmark_manager.rb
132
133
  - lib/beniya/color_helper.rb
133
134
  - lib/beniya/config.rb
134
135
  - lib/beniya/config_loader.rb
136
+ - lib/beniya/dialog_renderer.rb
135
137
  - lib/beniya/directory_listing.rb
136
138
  - lib/beniya/file_opener.rb
139
+ - lib/beniya/file_operations.rb
137
140
  - lib/beniya/file_preview.rb
141
+ - lib/beniya/filter_manager.rb
138
142
  - lib/beniya/health_checker.rb
139
143
  - lib/beniya/keybind_handler.rb
144
+ - lib/beniya/logger.rb
145
+ - lib/beniya/selection_manager.rb
140
146
  - lib/beniya/terminal_ui.rb
147
+ - lib/beniya/text_utils.rb
141
148
  - lib/beniya/version.rb
149
+ - lib/beniya/zoxide_integration.rb
142
150
  - publish_gem.zsh
143
151
  - test_delete/test1.txt
144
152
  - test_delete/test2.txt