rufio 0.81.0 → 0.82.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 533e0a5b32ced89d9d65bfd1abaafee73bada752cc24848538abc05e878a3f41
4
- data.tar.gz: f614bb8ee10f320c3b8e3387f5374b5c1bc2c2b9299112f0640061dffcb47b05
3
+ metadata.gz: 12227b8ad934dc15230c756490aa8fff59aaec22a8d535a08bab0598533d52b0
4
+ data.tar.gz: 3fe89daae07e436eb27202a6dc3b0852daad7e45a5209a0c5178d139dfe27fc6
5
5
  SHA512:
6
- metadata.gz: a0f9f950cfa19fc467fe220614f298ac35473aa8e9112e7776b683b9414e39436f4cce7bf5f5d3cf3a481dbf48545e2e971a9773e3e68bc11bfae53c6b63fc13
7
- data.tar.gz: cff712ea8904a749d0d3f646bc0b9a016c085a043bf08bc6cf304d1f1bbbc5a4e94daf7e05464334f6da56c0bdcf32f6411e8d0787a7763b303428bde0993f65
6
+ metadata.gz: f614795b114ae1483947331a1f929e43cbf5b57b2e9f05923ccd0c143082836ee00740f43056279761fe36c462c5a40a9f9922de40c6ed72fc7910b332942b72
7
+ data.tar.gz: 85c223ae9f1a6c678e65871eafa17fe65ecdb8bf9926a09fc9fa003df6c287f78f45dee76c79fa8b14e51c96c3577969b843b9d9cb62bd5f35e55bf720ea0ef4
data/CHANGELOG.md CHANGED
@@ -5,10 +5,12 @@ All notable changes to rufio will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
- ## [Unreleased]
8
+ ## [0.82.0] - 2026-02-28
9
9
 
10
10
  ### Added
11
- - **Bookmark Jump Highlight**: When cycling bookmarks with `Tab`, the target bookmark in the top bar is highlighted in cyan for 500ms, then automatically fades back to gray
11
+ - **Bookmark Jump Highlight**: When cycling bookmarks with `Tab` or `Shift+Tab`, or jumping directly with number keys `1`–`9`, the target bookmark in the top bar is highlighted in cyan for 500ms, then automatically fades back to gray
12
+ - **`Shift+Tab` backward bookmark cycling**: In Files mode, `Shift+Tab` now cycles backwards through bookmarks (previously it switched tab modes)
13
+ - **Number key bookmark highlight**: Pressing `1`–`9` to jump to a bookmark now triggers the same 500ms cyan highlight as `Tab`; subsequent `Tab` presses continue forward from the jumped position
12
14
 
13
15
  ### Changed
14
16
  - **UI Layout**: Merged mode tabs and title bar into a single combined bottom row
@@ -18,13 +20,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
18
20
  - **`Tab` key**: Changed from mode cycling to **bookmark cycling** (Files mode only)
19
21
  - In Files mode: jump to next registered bookmark in sequence (wraps to first)
20
22
  - In other modes (Logs / Jobs / Help): no action
21
- - Mode switching is done via `J` / `L` / `?` keys and `Shift+Tab`
23
+ - Mode switching is done via `J` / `L` / `?` keys
24
+ - **Bookmark cycling state**: `BookmarkController` now tracks `@last_bookmark_idx` to maintain cycling position independently of the current directory — navigating into a subdirectory no longer resets Tab cycling to the first bookmark
25
+ - **UIRenderer bookmark source**: Footer display now reads bookmarks via `@keybind_handler.bookmark_list` (same `BookmarkManager` instance as navigation) instead of creating a separate `Bookmark.new` instance pointing to the legacy `bookmarks.json`
22
26
 
23
27
  ### Fixed
28
+ - **Tab cycling past 3rd bookmark**: Fixed a bug where Tab always returned to the first bookmark after navigating to a subdirectory (non-bookmark directory), because `find_index` returned `nil` for non-bookmark paths
29
+ - **Mismatched bookmark files**: UIRenderer was reading from `~/.config/rufio/bookmarks.json` while navigation used `~/.config/rufio/bookmarks.yml`, causing the footer to display different bookmarks than Tab actually navigated to
30
+ - **Navigation blocked by stale bookmarks**: `goto_next_bookmark` and `goto_prev_bookmark` now skip bookmarks whose paths no longer exist (`Dir.exist?`), preventing the cycling from getting stuck on deleted directories
31
+ - **Test contamination of real bookmark file**: `test_bookmark_controller.rb` used `Bookmark.new` without arguments, writing test entries into the real `~/.config/rufio/bookmarks.json`; fixed to use a temp file
32
+ - **Number key crash**: Pressing `1`–`9` caused a `NoMethodError` crash because `goto_bookmark` was defined after `private` in `KeybindHandler`, making it inaccessible from `TerminalUI`; moved to the public section
24
33
  - **zoxide 2-digit input**: Fixed a bug where pressing a single digit in the zoxide history dialog immediately navigated, making it impossible to enter 2-digit numbers (e.g., `12`)
25
34
  - **Jobs mode tab highlight**: Fixed active tab not highlighting when entering Jobs mode from Logs or Help mode
26
35
  - **Jobs mode navigation**: Fixed inability to switch away from Jobs mode using `L`, `?` keys
27
36
 
37
+ ### Removed
38
+ - Deleted 3 legacy hand-written test scripts that were not part of the Minitest suite and duplicated coverage already in proper test files:
39
+ - `test/test_bookmark_simple.rb` — covered by `test_bookmark.rb`
40
+ - `test/test_filter_reset.rb` — covered by `test_filter_manager.rb`
41
+ - `test/test_rga_search.rb` — obsolete manual script with monkey-patching
42
+
28
43
  ## [0.80.0] - 2026-02-21
29
44
 
30
45
  ### Added
@@ -0,0 +1,439 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rufio
4
+ # ブックマーク・zoxide・スクリプトパス専用コントローラ
5
+ # KeybindHandler からブックマーク系・zoxide・スクリプトパス系メソッドを分離し、単一責任原則に準拠
6
+ class BookmarkController
7
+ def initialize(directory_listing, bookmark_manager, dialog_renderer, nav_controller,
8
+ script_path_manager, notification_manager, zoxide_integration)
9
+ @directory_listing = directory_listing
10
+ @bookmark_manager = bookmark_manager
11
+ @dialog_renderer = dialog_renderer
12
+ @nav_controller = nav_controller
13
+ @script_path_manager = script_path_manager
14
+ @notification_manager = notification_manager
15
+ @zoxide_integration = zoxide_integration
16
+ @terminal_ui = nil
17
+ @last_bookmark_idx = nil
18
+ end
19
+
20
+ def set_terminal_ui(terminal_ui)
21
+ @terminal_ui = terminal_ui
22
+ end
23
+
24
+ def set_directory_listing(directory_listing)
25
+ @directory_listing = directory_listing
26
+ end
27
+
28
+ # ============================
29
+ # ブックマーク操作
30
+ # ============================
31
+
32
+ def add_bookmark
33
+ current_path = @directory_listing&.current_path || Dir.pwd
34
+
35
+ bookmarks = @bookmark_manager.list
36
+ existing = bookmarks.find { |b| b[:path] == current_path }
37
+
38
+ if existing
39
+ content_lines = [
40
+ '',
41
+ 'This directory is already bookmarked',
42
+ "Name: #{existing[:name]}",
43
+ '',
44
+ 'Press any key to continue...'
45
+ ]
46
+
47
+ title = 'Bookmark Exists'
48
+ width = 50
49
+ height = content_lines.length + 4
50
+
51
+ show_overlay_dialog(title, content_lines, {
52
+ width: width,
53
+ height: height,
54
+ border_color: "\e[33m",
55
+ title_color: "\e[1;33m",
56
+ content_color: "\e[37m"
57
+ })
58
+
59
+ return false
60
+ end
61
+
62
+ dir_name = File.basename(current_path)
63
+
64
+ title = "Add Bookmark: #{dir_name}"
65
+ prompt = "Enter bookmark name:"
66
+ bookmark_name = @dialog_renderer.show_input_dialog(title, prompt, {
67
+ border_color: "\e[32m",
68
+ title_color: "\e[1;32m",
69
+ content_color: "\e[37m"
70
+ })
71
+
72
+ return false if bookmark_name.nil? || bookmark_name.empty?
73
+
74
+ result = @bookmark_manager.add(current_path, bookmark_name)
75
+
76
+ @terminal_ui&.refresh_display
77
+ result
78
+ end
79
+
80
+ def show_bookmark_menu
81
+ current_path = @directory_listing&.current_path || Dir.pwd
82
+
83
+ menu_items = [
84
+ '1. Add current dir to bookmarks',
85
+ '2. Add to script paths',
86
+ '3. Manage script paths',
87
+ '4. View bookmarks'
88
+ ]
89
+
90
+ content_lines = [''] + menu_items + ['', '[1-4] Select | [Esc] Cancel']
91
+
92
+ title = 'Bookmark Menu'
93
+ width = 45
94
+ height = content_lines.length + 4
95
+
96
+ key = show_overlay_dialog(title, content_lines, {
97
+ width: width,
98
+ height: height,
99
+ border_color: "\e[36m",
100
+ title_color: "\e[1;36m",
101
+ content_color: "\e[37m"
102
+ })
103
+
104
+ case key
105
+ when '1'
106
+ add_bookmark
107
+ when '2'
108
+ add_to_script_paths
109
+ when '3'
110
+ show_script_paths_manager
111
+ when '4'
112
+ selected_bookmark = @bookmark_manager.list_interactive
113
+ @terminal_ui&.refresh_display
114
+ if selected_bookmark
115
+ if @bookmark_manager.path_exists?(selected_bookmark)
116
+ navigate_to_directory(selected_bookmark[:path])
117
+ else
118
+ show_error_and_wait('bookmark.path_not_exist', selected_bookmark[:path])
119
+ end
120
+ end
121
+ else
122
+ @terminal_ui&.refresh_display
123
+ end
124
+
125
+ true
126
+ end
127
+
128
+ def goto_bookmark(number)
129
+ bookmark = @bookmark_manager.find_by_number(number)
130
+
131
+ return false unless bookmark
132
+ return false unless @bookmark_manager.path_exists?(bookmark)
133
+
134
+ # Tab循環位置を更新(数字キー後の Tab が次の番号から続くように)
135
+ @last_bookmark_idx = number - 1
136
+ navigate_to_directory(bookmark[:path])
137
+ end
138
+
139
+ # パスを指定してブックマーク移動(テスト用)
140
+ def goto_bookmark_by_path(path)
141
+ navigate_to_directory(path)
142
+ end
143
+
144
+ def goto_start_directory
145
+ start_dir = @directory_listing&.start_directory
146
+ return false unless start_dir
147
+
148
+ navigate_to_directory(start_dir)
149
+ end
150
+
151
+ # 次のブックマークに移動(Tabキー)
152
+ # 現在のディレクトリがブックマーク内なら current_path を基準に次へ、
153
+ # そうでない場合は @last_bookmark_idx から継続(ブックマーク外に出ても循環が止まらない)
154
+ # 存在しないパスのブックマークはスキップする
155
+ # @return [Integer, nil] 新しいブックマークインデックス、またはnil
156
+ def goto_next_bookmark
157
+ bookmarks = @bookmark_manager.list
158
+ return nil unless bookmarks&.any?
159
+
160
+ current_path = @directory_listing.current_path
161
+ current_position = bookmarks.find_index { |bm| bm[:path] == current_path }
162
+ start_idx = if current_position
163
+ (current_position + 1) % bookmarks.length
164
+ elsif @last_bookmark_idx
165
+ (@last_bookmark_idx + 1) % bookmarks.length
166
+ else
167
+ 0
168
+ end
169
+
170
+ bookmarks.length.times do |i|
171
+ idx = (start_idx + i) % bookmarks.length
172
+ next unless Dir.exist?(bookmarks[idx][:path])
173
+
174
+ @last_bookmark_idx = idx
175
+ navigate_to_directory(bookmarks[idx][:path])
176
+ return @last_bookmark_idx
177
+ end
178
+ nil
179
+ end
180
+
181
+ # 前のブックマークに移動(Shift+Tabキー)
182
+ # 存在しないパスのブックマークはスキップする
183
+ # @return [Integer, nil] 新しいブックマークインデックス、またはnil
184
+ def goto_prev_bookmark
185
+ bookmarks = @bookmark_manager.list
186
+ return nil unless bookmarks&.any?
187
+
188
+ current_path = @directory_listing.current_path
189
+ current_position = bookmarks.find_index { |bm| bm[:path] == current_path }
190
+ start_idx = if current_position
191
+ (current_position - 1 + bookmarks.length) % bookmarks.length
192
+ elsif @last_bookmark_idx
193
+ (@last_bookmark_idx - 1 + bookmarks.length) % bookmarks.length
194
+ else
195
+ bookmarks.length - 1
196
+ end
197
+
198
+ bookmarks.length.times do |i|
199
+ idx = (start_idx - i + bookmarks.length) % bookmarks.length
200
+ next unless Dir.exist?(bookmarks[idx][:path])
201
+
202
+ @last_bookmark_idx = idx
203
+ navigate_to_directory(bookmarks[idx][:path])
204
+ return @last_bookmark_idx
205
+ end
206
+ nil
207
+ end
208
+
209
+ # ============================
210
+ # zoxide 連携
211
+ # ============================
212
+
213
+ def show_zoxide_menu
214
+ selected_path = @zoxide_integration.show_menu
215
+
216
+ if selected_path && Dir.exist?(selected_path)
217
+ if navigate_to_directory(selected_path)
218
+ @zoxide_integration.add_to_history(selected_path)
219
+ true
220
+ else
221
+ false
222
+ end
223
+ else
224
+ @terminal_ui&.refresh_display
225
+ false
226
+ end
227
+ end
228
+
229
+ # ============================
230
+ # スクリプトパス管理
231
+ # ============================
232
+
233
+ def add_to_script_paths
234
+ current_path = @directory_listing&.current_path || Dir.pwd
235
+
236
+ @script_path_manager ||= ScriptPathManager.new(Config::SCRIPT_PATHS_YML)
237
+
238
+ if @script_path_manager.paths.include?(current_path)
239
+ show_notification('Already in paths', current_path, :info)
240
+ elsif @script_path_manager.add_path(current_path)
241
+ show_notification('Added to scripts', current_path, :success)
242
+ else
243
+ show_notification('Failed to add', current_path, :error)
244
+ end
245
+
246
+ @terminal_ui&.refresh_display
247
+ true
248
+ end
249
+
250
+ def show_script_paths_manager
251
+ unless @script_path_manager
252
+ show_notification('No script paths configured', '', :info)
253
+ @terminal_ui&.refresh_display
254
+ return false
255
+ end
256
+
257
+ paths = @script_path_manager.paths
258
+ if paths.empty?
259
+ show_notification('No script paths', 'Press B > 2 to add', :info)
260
+ @terminal_ui&.refresh_display
261
+ return false
262
+ end
263
+
264
+ selected_index = 0
265
+ screen = @terminal_ui&.screen
266
+ renderer = @terminal_ui&.renderer
267
+
268
+ loop do
269
+ menu_items = paths.each_with_index.map do |path, i|
270
+ prefix = i == selected_index ? '> ' : ' '
271
+ "#{prefix}#{i + 1}. #{truncate_path(path, 35)}"
272
+ end
273
+
274
+ content_lines = [''] + menu_items + ['', '[j/k] Move | [d] Delete | [Enter] Jump | [Esc] Back']
275
+
276
+ title = 'Script Paths'
277
+ width = 50
278
+ height = content_lines.length + 4
279
+
280
+ if screen && renderer
281
+ screen.enable_overlay
282
+ x, y = @dialog_renderer.calculate_center(width, height)
283
+ @dialog_renderer.draw_floating_window_to_overlay(screen, x, y, width, height, title, content_lines, {
284
+ border_color: "\e[35m",
285
+ title_color: "\e[1;35m",
286
+ content_color: "\e[37m"
287
+ })
288
+ renderer.render(screen)
289
+
290
+ key = STDIN.getch
291
+ screen.disable_overlay
292
+ renderer.render(screen)
293
+ else
294
+ x, y = @dialog_renderer.calculate_center(width, height)
295
+ @dialog_renderer.draw_floating_window(x, y, width, height, title, content_lines, {
296
+ border_color: "\e[35m",
297
+ title_color: "\e[1;35m",
298
+ content_color: "\e[37m"
299
+ })
300
+
301
+ key = STDIN.getch
302
+ @dialog_renderer.clear_area(x, y, width, height)
303
+ end
304
+
305
+ case key
306
+ when 'j'
307
+ selected_index = [selected_index + 1, paths.length - 1].min
308
+ when 'k'
309
+ selected_index = [selected_index - 1, 0].max
310
+ when 'd'
311
+ path_to_delete = paths[selected_index]
312
+ if confirm_delete_script_path(path_to_delete)
313
+ @script_path_manager.remove_path(path_to_delete)
314
+ paths = @script_path_manager.paths
315
+ selected_index = [selected_index, paths.length - 1].min
316
+ break if paths.empty?
317
+ end
318
+ when "\r", "\n"
319
+ path = paths[selected_index]
320
+ navigate_to_directory(path) if Dir.exist?(path)
321
+ break
322
+ when "\e"
323
+ break
324
+ end
325
+ end
326
+
327
+ @terminal_ui&.refresh_display
328
+ true
329
+ end
330
+
331
+ def confirm_delete_script_path(path)
332
+ content_lines = [
333
+ '',
334
+ 'Delete this script path?',
335
+ '',
336
+ truncate_path(path, 40),
337
+ '',
338
+ '[y] Yes | [n] No'
339
+ ]
340
+
341
+ title = 'Confirm Delete'
342
+ width = 50
343
+ height = content_lines.length + 4
344
+
345
+ key = show_overlay_dialog(title, content_lines, {
346
+ width: width,
347
+ height: height,
348
+ border_color: "\e[31m",
349
+ title_color: "\e[1;31m",
350
+ content_color: "\e[37m"
351
+ })
352
+
353
+ key.downcase == 'y'
354
+ end
355
+
356
+ # ============================
357
+ # ナビゲーション(委譲)
358
+ # ============================
359
+
360
+ def navigate_to_directory(path)
361
+ return false unless @directory_listing
362
+
363
+ result = @directory_listing.navigate_to_path(path)
364
+ if result
365
+ @nav_controller.select_index(0)
366
+ @nav_controller.clear_filter_mode
367
+ true
368
+ else
369
+ false
370
+ end
371
+ end
372
+
373
+ private
374
+
375
+ def show_overlay_dialog(title, content_lines, options = {}, &block)
376
+ use_overlay = @terminal_ui &&
377
+ @terminal_ui.respond_to?(:screen) &&
378
+ @terminal_ui.respond_to?(:renderer) &&
379
+ @terminal_ui.screen &&
380
+ @terminal_ui.renderer
381
+
382
+ if use_overlay
383
+ @terminal_ui.show_overlay_dialog(title, content_lines, options, &block)
384
+ else
385
+ width = options[:width]
386
+ height = options[:height]
387
+
388
+ unless width && height
389
+ width, height = @dialog_renderer.calculate_dimensions(content_lines, {
390
+ title: title,
391
+ min_width: options[:min_width] || 40,
392
+ max_width: options[:max_width] || 80
393
+ })
394
+ end
395
+
396
+ x, y = @dialog_renderer.calculate_center(width, height)
397
+
398
+ @dialog_renderer.draw_floating_window(x, y, width, height, title, content_lines, {
399
+ border_color: options[:border_color] || "\e[37m",
400
+ title_color: options[:title_color] || "\e[1;33m",
401
+ content_color: options[:content_color] || "\e[37m"
402
+ })
403
+
404
+ key = block_given? ? yield : STDIN.getch
405
+
406
+ @dialog_renderer.clear_area(x, y, width, height)
407
+ @terminal_ui&.refresh_display
408
+
409
+ key
410
+ end
411
+ end
412
+
413
+ def show_notification(title, message, type)
414
+ return unless @notification_manager
415
+
416
+ @notification_manager.add(title, type, duration: 3, exit_code: nil)
417
+ end
418
+
419
+ def show_error_and_wait(message_key, value)
420
+ puts "\n#{ConfigLoader.message(message_key) || message_key}: #{value}"
421
+ wait_for_keypress
422
+ false
423
+ end
424
+
425
+ def wait_for_keypress
426
+ print ConfigLoader.message('keybind.press_any_key') || 'Press any key to continue...'
427
+ STDIN.getch
428
+ end
429
+
430
+ def truncate_path(path, max_length)
431
+ return path if path.length <= max_length
432
+
433
+ display_path = path.sub(Dir.home, '~')
434
+ return display_path if display_path.length <= max_length
435
+
436
+ "...#{display_path[-(max_length - 3)..]}"
437
+ end
438
+ end
439
+ end
@@ -38,8 +38,66 @@ module Rufio
38
38
  load_config[:colors]
39
39
  end
40
40
 
41
+ # デフォルトキーバインド(全アクションの単一キー定義)
42
+ def default_keybinds
43
+ {
44
+ # ナビゲーション
45
+ move_up: 'k',
46
+ move_down: 'j',
47
+ navigate_parent: 'h',
48
+ navigate_enter: 'l',
49
+ top: 'g',
50
+ bottom: 'G',
51
+ refresh: 'R',
52
+ # ファイル操作
53
+ open_file: 'o',
54
+ rename: 'r',
55
+ delete: 'd',
56
+ create_file: 'a',
57
+ create_dir: 'A',
58
+ move_selected: 'm',
59
+ copy_selected: 'c',
60
+ delete_selected: 'x',
61
+ open_explorer: 'e',
62
+ # 選択・検索
63
+ select: ' ',
64
+ filter: 'f',
65
+ fzf_search: 's',
66
+ fzf_search_alt: '/',
67
+ rga_search: 'F',
68
+ # ブックマーク
69
+ add_bookmark: 'b',
70
+ bookmark_menu: 'B',
71
+ zoxide: 'z',
72
+ start_dir: '0',
73
+ # モード・ツール
74
+ job_mode: 'J',
75
+ help: '?',
76
+ log_viewer: 'L',
77
+ command_mode: ':',
78
+ quit: 'q'
79
+ }.freeze
80
+ end
81
+
82
+ # デフォルトUI設定
83
+ def default_ui_options
84
+ {
85
+ panel_ratio: 0.5, # 左パネル幅比率(0.3〜0.7)
86
+ preview_enabled: true # ファイルプレビューのON/OFF
87
+ }.freeze
88
+ end
89
+
90
+ # UI設定(デフォルト+ユーザー設定のマージ)
91
+ def ui_options
92
+ user_opts = load_config[:ui_options] || {}
93
+ default_ui_options.merge(user_opts)
94
+ end
95
+
41
96
  def keybinds
42
- load_config[:keybinds]
97
+ raw_user_keybinds = load_config[:keybinds] || {}
98
+ # 新フォーマット(単一文字列値)のみ受け入れ、古いフォーマット(配列値)は無視
99
+ user_keybinds = raw_user_keybinds.select { |_, v| v.is_a?(String) }
100
+ default_keybinds.merge(user_keybinds)
43
101
  end
44
102
 
45
103
  def language
@@ -228,7 +286,7 @@ module Rufio
228
286
  config = {
229
287
  applications: safe_const_get(:APPLICATIONS, default_config[:applications]),
230
288
  colors: safe_const_get(:COLORS, default_config[:colors]),
231
- keybinds: safe_const_get(:KEYBINDS, default_config[:keybinds])
289
+ keybinds: safe_const_get(:KEYBINDS, {})
232
290
  }
233
291
 
234
292
  if Object.const_defined?(:LANGUAGE)
@@ -245,6 +303,10 @@ module Rufio
245
303
  config[:command_history_size] = Object.const_get(:COMMAND_HISTORY_SIZE)
246
304
  end
247
305
 
306
+ if Object.const_defined?(:UI_OPTIONS)
307
+ config[:ui_options] = safe_const_get(:UI_OPTIONS, {})
308
+ end
309
+
248
310
  config
249
311
  rescue StandardError => e
250
312
  warn "Failed to load config file: #{e.message}"
@@ -273,18 +335,8 @@ module Rufio
273
335
  selected: { hsl: [50, 90, 70] },
274
336
  preview: { hsl: [180, 60, 65] }
275
337
  },
276
- keybinds: {
277
- quit: %w[q ESC],
278
- up: %w[k UP],
279
- down: %w[j DOWN],
280
- left: %w[h LEFT],
281
- right: %w[l RIGHT ENTER],
282
- top: %w[g],
283
- bottom: %w[G],
284
- refresh: %w[r],
285
- search: %w[/],
286
- open_file: %w[o SPACE]
287
- }
338
+ keybinds: {}, # ユーザーの上書きのみ格納(デフォルトはdefault_keybindsを参照)
339
+ ui_options: {} # ユーザーの上書きのみ格納(デフォルトはdefault_ui_optionsを参照)
288
340
  }
289
341
  end
290
342
  end