rufio 0.50.0 → 0.60.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 +4 -4
- data/CHANGELOG.md +26 -0
- data/README.md +32 -36
- data/examples/config.yml +8 -0
- data/lib/rufio/command_completion.rb +20 -5
- data/lib/rufio/command_mode.rb +98 -5
- data/lib/rufio/config_loader.rb +37 -0
- data/lib/rufio/job_manager.rb +128 -0
- data/lib/rufio/job_mode.rb +146 -0
- data/lib/rufio/keybind_handler.rb +243 -232
- data/lib/rufio/notification_manager.rb +77 -0
- data/lib/rufio/script_config_loader.rb +101 -0
- data/lib/rufio/script_path_manager.rb +386 -0
- data/lib/rufio/script_runner.rb +216 -0
- data/lib/rufio/task_status.rb +118 -0
- data/lib/rufio/terminal_ui.rb +181 -495
- data/lib/rufio/version.rb +1 -1
- data/lib/rufio.rb +8 -4
- data/scripts/test_jobs/build_simulation.sh +29 -0
- data/scripts/test_jobs/deploy_simulation.sh +37 -0
- data/scripts/test_jobs/quick.sh +11 -0
- data/scripts/test_jobs/random_result.sh +23 -0
- data/scripts/test_jobs/slow_fail.sh +10 -0
- data/scripts/test_jobs/slow_success.sh +10 -0
- data/scripts/test_jobs/very_slow.sh +19 -0
- metadata +17 -7
- data/docs/file-preview-optimization-analysis.md +0 -759
- data/docs/file-preview-performance-issue-FIXED.md +0 -547
- data/lib/rufio/project_command.rb +0 -147
- data/lib/rufio/project_log.rb +0 -68
- data/lib/rufio/project_mode.rb +0 -58
|
@@ -44,14 +44,6 @@ module Rufio
|
|
|
44
44
|
@bookmark_manager = BookmarkManager.new(Bookmark.new, @dialog_renderer)
|
|
45
45
|
@zoxide_integration = ZoxideIntegration.new(@dialog_renderer)
|
|
46
46
|
|
|
47
|
-
# Project mode
|
|
48
|
-
log_dir = File.expand_path('~/.config/rufio/logs')
|
|
49
|
-
@project_mode = ProjectMode.new(@bookmark_manager.instance_variable_get(:@bookmark), log_dir)
|
|
50
|
-
@project_command = ProjectCommand.new(log_dir)
|
|
51
|
-
@project_log = ProjectLog.new(log_dir)
|
|
52
|
-
@in_project_mode = false
|
|
53
|
-
@in_log_mode = false
|
|
54
|
-
|
|
55
47
|
# Help mode
|
|
56
48
|
@in_help_mode = false
|
|
57
49
|
@pre_help_directory = nil
|
|
@@ -64,6 +56,15 @@ module Rufio
|
|
|
64
56
|
# Preview pane focus and scroll
|
|
65
57
|
@preview_focused = false
|
|
66
58
|
@preview_scroll_offset = 0
|
|
59
|
+
|
|
60
|
+
# Job mode
|
|
61
|
+
@notification_manager = NotificationManager.new
|
|
62
|
+
@job_manager = JobManager.new(notification_manager: @notification_manager)
|
|
63
|
+
@job_mode = JobMode.new(job_manager: @job_manager)
|
|
64
|
+
|
|
65
|
+
# Script path manager
|
|
66
|
+
config_file = File.expand_path('~/.config/rufio/config.yml')
|
|
67
|
+
@script_path_manager = File.exist?(config_file) ? ScriptPathManager.new(config_file) : nil
|
|
67
68
|
end
|
|
68
69
|
|
|
69
70
|
def set_directory_listing(directory_listing)
|
|
@@ -93,9 +94,9 @@ module Rufio
|
|
|
93
94
|
def handle_key(key)
|
|
94
95
|
return false unless @directory_listing
|
|
95
96
|
|
|
96
|
-
#
|
|
97
|
-
if @
|
|
98
|
-
return
|
|
97
|
+
# ジョブモード中の特別処理
|
|
98
|
+
if @job_mode.active?
|
|
99
|
+
return handle_job_mode_key(key)
|
|
99
100
|
end
|
|
100
101
|
|
|
101
102
|
# プレビューペインフォーカス中の特別処理
|
|
@@ -177,10 +178,12 @@ module Rufio
|
|
|
177
178
|
copy_selected_to_current
|
|
178
179
|
when 'x' # x - delete selected files
|
|
179
180
|
delete_selected_files
|
|
180
|
-
when '
|
|
181
|
-
|
|
181
|
+
when 'J' # J - job mode
|
|
182
|
+
enter_job_mode
|
|
182
183
|
when 'b' # b - add bookmark
|
|
183
184
|
add_bookmark
|
|
185
|
+
when 'B' # B - bookmark menu (with script paths)
|
|
186
|
+
show_bookmark_menu
|
|
184
187
|
when 'z' # z - zoxide history navigation
|
|
185
188
|
show_zoxide_menu
|
|
186
189
|
when '0' # 0 - go to start directory
|
|
@@ -1408,6 +1411,191 @@ module Rufio
|
|
|
1408
1411
|
result
|
|
1409
1412
|
end
|
|
1410
1413
|
|
|
1414
|
+
# ブックマークメニューを表示(スクリプトパス機能を含む)
|
|
1415
|
+
def show_bookmark_menu
|
|
1416
|
+
current_path = @directory_listing&.current_path || Dir.pwd
|
|
1417
|
+
|
|
1418
|
+
menu_items = [
|
|
1419
|
+
'1. Add current dir to bookmarks',
|
|
1420
|
+
'2. Add to script paths',
|
|
1421
|
+
'3. Manage script paths',
|
|
1422
|
+
'4. View bookmarks'
|
|
1423
|
+
]
|
|
1424
|
+
|
|
1425
|
+
content_lines = [''] + menu_items + ['', '[1-4] Select | [Esc] Cancel']
|
|
1426
|
+
|
|
1427
|
+
title = 'Bookmark Menu'
|
|
1428
|
+
width = 45
|
|
1429
|
+
height = content_lines.length + 4
|
|
1430
|
+
x, y = @dialog_renderer.calculate_center(width, height)
|
|
1431
|
+
|
|
1432
|
+
@dialog_renderer.draw_floating_window(x, y, width, height, title, content_lines, {
|
|
1433
|
+
border_color: "\e[36m", # Cyan
|
|
1434
|
+
title_color: "\e[1;36m", # Bold cyan
|
|
1435
|
+
content_color: "\e[37m" # White
|
|
1436
|
+
})
|
|
1437
|
+
|
|
1438
|
+
key = STDIN.getch
|
|
1439
|
+
@dialog_renderer.clear_area(x, y, width, height)
|
|
1440
|
+
|
|
1441
|
+
case key
|
|
1442
|
+
when '1'
|
|
1443
|
+
add_bookmark
|
|
1444
|
+
when '2'
|
|
1445
|
+
add_to_script_paths
|
|
1446
|
+
when '3'
|
|
1447
|
+
show_script_paths_manager
|
|
1448
|
+
when '4'
|
|
1449
|
+
# ブックマーク一覧表示(既存機能)
|
|
1450
|
+
@terminal_ui&.refresh_display
|
|
1451
|
+
else
|
|
1452
|
+
@terminal_ui&.refresh_display
|
|
1453
|
+
end
|
|
1454
|
+
|
|
1455
|
+
true
|
|
1456
|
+
end
|
|
1457
|
+
|
|
1458
|
+
# カレントディレクトリをスクリプトパスに追加
|
|
1459
|
+
def add_to_script_paths
|
|
1460
|
+
current_path = @directory_listing&.current_path || Dir.pwd
|
|
1461
|
+
|
|
1462
|
+
unless @script_path_manager
|
|
1463
|
+
# ScriptPathManagerがない場合は作成
|
|
1464
|
+
config_file = File.expand_path('~/.config/rufio/config.yml')
|
|
1465
|
+
FileUtils.mkdir_p(File.dirname(config_file))
|
|
1466
|
+
@script_path_manager = ScriptPathManager.new(config_file)
|
|
1467
|
+
end
|
|
1468
|
+
|
|
1469
|
+
if @script_path_manager.paths.include?(current_path)
|
|
1470
|
+
# 既に登録されている
|
|
1471
|
+
show_notification('Already in paths', current_path, :info)
|
|
1472
|
+
elsif @script_path_manager.add_path(current_path)
|
|
1473
|
+
show_notification('Added to scripts', current_path, :success)
|
|
1474
|
+
else
|
|
1475
|
+
show_notification('Failed to add', current_path, :error)
|
|
1476
|
+
end
|
|
1477
|
+
|
|
1478
|
+
@terminal_ui&.refresh_display
|
|
1479
|
+
true
|
|
1480
|
+
end
|
|
1481
|
+
|
|
1482
|
+
# スクリプトパス管理UIを表示
|
|
1483
|
+
def show_script_paths_manager
|
|
1484
|
+
unless @script_path_manager
|
|
1485
|
+
show_notification('No script paths configured', '', :info)
|
|
1486
|
+
@terminal_ui&.refresh_display
|
|
1487
|
+
return false
|
|
1488
|
+
end
|
|
1489
|
+
|
|
1490
|
+
paths = @script_path_manager.paths
|
|
1491
|
+
if paths.empty?
|
|
1492
|
+
show_notification('No script paths', 'Press B > 2 to add', :info)
|
|
1493
|
+
@terminal_ui&.refresh_display
|
|
1494
|
+
return false
|
|
1495
|
+
end
|
|
1496
|
+
|
|
1497
|
+
selected_index = 0
|
|
1498
|
+
|
|
1499
|
+
loop do
|
|
1500
|
+
# メニューを描画
|
|
1501
|
+
menu_items = paths.each_with_index.map do |path, i|
|
|
1502
|
+
prefix = i == selected_index ? '> ' : ' '
|
|
1503
|
+
"#{prefix}#{i + 1}. #{truncate_path(path, 35)}"
|
|
1504
|
+
end
|
|
1505
|
+
|
|
1506
|
+
content_lines = [''] + menu_items + ['', '[j/k] Move | [d] Delete | [Enter] Jump | [Esc] Back']
|
|
1507
|
+
|
|
1508
|
+
title = 'Script Paths'
|
|
1509
|
+
width = 50
|
|
1510
|
+
height = content_lines.length + 4
|
|
1511
|
+
x, y = @dialog_renderer.calculate_center(width, height)
|
|
1512
|
+
|
|
1513
|
+
@dialog_renderer.draw_floating_window(x, y, width, height, title, content_lines, {
|
|
1514
|
+
border_color: "\e[35m", # Magenta
|
|
1515
|
+
title_color: "\e[1;35m", # Bold magenta
|
|
1516
|
+
content_color: "\e[37m" # White
|
|
1517
|
+
})
|
|
1518
|
+
|
|
1519
|
+
key = STDIN.getch
|
|
1520
|
+
@dialog_renderer.clear_area(x, y, width, height)
|
|
1521
|
+
|
|
1522
|
+
case key
|
|
1523
|
+
when 'j'
|
|
1524
|
+
selected_index = [selected_index + 1, paths.length - 1].min
|
|
1525
|
+
when 'k'
|
|
1526
|
+
selected_index = [selected_index - 1, 0].max
|
|
1527
|
+
when 'd'
|
|
1528
|
+
# 削除確認
|
|
1529
|
+
path_to_delete = paths[selected_index]
|
|
1530
|
+
if confirm_delete_script_path(path_to_delete)
|
|
1531
|
+
@script_path_manager.remove_path(path_to_delete)
|
|
1532
|
+
paths = @script_path_manager.paths
|
|
1533
|
+
selected_index = [selected_index, paths.length - 1].min
|
|
1534
|
+
break if paths.empty?
|
|
1535
|
+
end
|
|
1536
|
+
when "\r", "\n"
|
|
1537
|
+
# ディレクトリにジャンプ
|
|
1538
|
+
path = paths[selected_index]
|
|
1539
|
+
if Dir.exist?(path)
|
|
1540
|
+
navigate_to_directory(path)
|
|
1541
|
+
end
|
|
1542
|
+
break
|
|
1543
|
+
when "\e"
|
|
1544
|
+
break
|
|
1545
|
+
end
|
|
1546
|
+
end
|
|
1547
|
+
|
|
1548
|
+
@terminal_ui&.refresh_display
|
|
1549
|
+
true
|
|
1550
|
+
end
|
|
1551
|
+
|
|
1552
|
+
# スクリプトパス削除の確認
|
|
1553
|
+
def confirm_delete_script_path(path)
|
|
1554
|
+
content_lines = [
|
|
1555
|
+
'',
|
|
1556
|
+
'Delete this script path?',
|
|
1557
|
+
'',
|
|
1558
|
+
truncate_path(path, 40),
|
|
1559
|
+
'',
|
|
1560
|
+
'[y] Yes | [n] No'
|
|
1561
|
+
]
|
|
1562
|
+
|
|
1563
|
+
title = 'Confirm Delete'
|
|
1564
|
+
width = 50
|
|
1565
|
+
height = content_lines.length + 4
|
|
1566
|
+
x, y = @dialog_renderer.calculate_center(width, height)
|
|
1567
|
+
|
|
1568
|
+
@dialog_renderer.draw_floating_window(x, y, width, height, title, content_lines, {
|
|
1569
|
+
border_color: "\e[31m", # Red
|
|
1570
|
+
title_color: "\e[1;31m", # Bold red
|
|
1571
|
+
content_color: "\e[37m" # White
|
|
1572
|
+
})
|
|
1573
|
+
|
|
1574
|
+
key = STDIN.getch
|
|
1575
|
+
@dialog_renderer.clear_area(x, y, width, height)
|
|
1576
|
+
|
|
1577
|
+
key.downcase == 'y'
|
|
1578
|
+
end
|
|
1579
|
+
|
|
1580
|
+
# 通知を表示
|
|
1581
|
+
def show_notification(title, message, type)
|
|
1582
|
+
return unless @notification_manager
|
|
1583
|
+
|
|
1584
|
+
@notification_manager.add(title, type, duration: 3, exit_code: nil)
|
|
1585
|
+
end
|
|
1586
|
+
|
|
1587
|
+
# パスを短縮表示
|
|
1588
|
+
def truncate_path(path, max_length)
|
|
1589
|
+
return path if path.length <= max_length
|
|
1590
|
+
|
|
1591
|
+
# ホームディレクトリを~に置換
|
|
1592
|
+
display_path = path.sub(Dir.home, '~')
|
|
1593
|
+
return display_path if display_path.length <= max_length
|
|
1594
|
+
|
|
1595
|
+
# 先頭と末尾を残して中間を...に
|
|
1596
|
+
"...#{display_path[-(max_length - 3)..]}"
|
|
1597
|
+
end
|
|
1598
|
+
|
|
1411
1599
|
# ヘルパーメソッド
|
|
1412
1600
|
def wait_for_keypress
|
|
1413
1601
|
print ConfigLoader.message('keybind.press_any_key') || 'Press any key to continue...'
|
|
@@ -1500,245 +1688,68 @@ module Rufio
|
|
|
1500
1688
|
end
|
|
1501
1689
|
end
|
|
1502
1690
|
|
|
1503
|
-
#
|
|
1504
|
-
def
|
|
1505
|
-
|
|
1506
|
-
when "\e" # ESC - ログモードならプロジェクトモードに戻る、そうでなければ終了
|
|
1507
|
-
if @in_log_mode
|
|
1508
|
-
exit_log_mode
|
|
1509
|
-
else
|
|
1510
|
-
exit_project_mode
|
|
1511
|
-
end
|
|
1512
|
-
when ' ' # Space - ブックマークを選択
|
|
1513
|
-
if @in_log_mode
|
|
1514
|
-
# ログモード中は何もしない
|
|
1515
|
-
false
|
|
1516
|
-
else
|
|
1517
|
-
select_bookmark_in_project_mode
|
|
1518
|
-
end
|
|
1519
|
-
when 'l' # l - ログディレクトリに移動
|
|
1520
|
-
if @in_log_mode
|
|
1521
|
-
# すでにログモードの場合は何もしない
|
|
1522
|
-
false
|
|
1523
|
-
else
|
|
1524
|
-
@in_log_mode = true
|
|
1525
|
-
@current_index = 0 # ログモードに入るときインデックスをリセット
|
|
1526
|
-
@terminal_ui&.enter_log_mode(@project_log) if @terminal_ui
|
|
1527
|
-
true
|
|
1528
|
-
end
|
|
1529
|
-
when 'j' # 下に移動
|
|
1530
|
-
move_down_in_project_mode
|
|
1531
|
-
when 'k' # 上に移動
|
|
1532
|
-
move_up_in_project_mode
|
|
1533
|
-
when 'g' # 先頭に移動
|
|
1534
|
-
@current_index = 0
|
|
1535
|
-
true
|
|
1536
|
-
when 'G' # 末尾に移動
|
|
1537
|
-
entries = get_project_mode_entries
|
|
1538
|
-
@current_index = [entries.length - 1, 0].max
|
|
1539
|
-
true
|
|
1540
|
-
when ':' # コマンドモード
|
|
1541
|
-
activate_project_command_mode
|
|
1542
|
-
when 'r' # r - ブックマークをリネーム
|
|
1543
|
-
if @in_log_mode
|
|
1544
|
-
false
|
|
1545
|
-
else
|
|
1546
|
-
rename_bookmark_in_project_mode
|
|
1547
|
-
end
|
|
1548
|
-
when 'd' # d - ブックマークを削除
|
|
1549
|
-
if @in_log_mode
|
|
1550
|
-
false
|
|
1551
|
-
else
|
|
1552
|
-
delete_bookmark_in_project_mode
|
|
1553
|
-
end
|
|
1554
|
-
else
|
|
1555
|
-
false
|
|
1556
|
-
end
|
|
1557
|
-
|
|
1558
|
-
# キー処理後、プロジェクトモードの再描画をトリガー
|
|
1559
|
-
@terminal_ui&.trigger_project_mode_redraw if result && @in_project_mode
|
|
1560
|
-
result
|
|
1561
|
-
end
|
|
1562
|
-
|
|
1563
|
-
# プロジェクトモード用のエントリ数取得
|
|
1564
|
-
def get_project_mode_entries
|
|
1565
|
-
if @in_log_mode
|
|
1566
|
-
@project_log.list_log_files
|
|
1567
|
-
else
|
|
1568
|
-
@project_mode.list_bookmarks
|
|
1569
|
-
end
|
|
1570
|
-
end
|
|
1571
|
-
|
|
1572
|
-
# プロジェクトモード用の下移動
|
|
1573
|
-
def move_down_in_project_mode
|
|
1574
|
-
entries = get_project_mode_entries
|
|
1575
|
-
@current_index = [@current_index + 1, entries.length - 1].min
|
|
1576
|
-
true
|
|
1577
|
-
end
|
|
1578
|
-
|
|
1579
|
-
# プロジェクトモード用の上移動
|
|
1580
|
-
def move_up_in_project_mode
|
|
1581
|
-
@current_index = [@current_index - 1, 0].max
|
|
1582
|
-
true
|
|
1583
|
-
end
|
|
1584
|
-
|
|
1585
|
-
# プロジェクトモードに入る
|
|
1586
|
-
def enter_project_mode
|
|
1587
|
-
@project_mode.activate
|
|
1588
|
-
@in_project_mode = true
|
|
1589
|
-
@current_index = 0 # プロジェクトモードに入るときインデックスをリセット
|
|
1590
|
-
@terminal_ui&.set_project_mode(@project_mode, @project_command, @project_log) if @terminal_ui
|
|
1691
|
+
# ヘルプダイアログを表示
|
|
1692
|
+
def show_help_dialog
|
|
1693
|
+
@terminal_ui&.show_help_dialog if @terminal_ui
|
|
1591
1694
|
true
|
|
1592
1695
|
end
|
|
1593
1696
|
|
|
1594
|
-
#
|
|
1595
|
-
|
|
1596
|
-
@project_mode.deactivate
|
|
1597
|
-
@in_project_mode = false
|
|
1598
|
-
@in_log_mode = false
|
|
1599
|
-
@current_index = 0 # 通常モードに戻るときインデックスをリセット
|
|
1600
|
-
@terminal_ui&.exit_project_mode if @terminal_ui
|
|
1601
|
-
refresh
|
|
1602
|
-
true
|
|
1603
|
-
end
|
|
1697
|
+
# ジョブモード関連メソッド(publicに戻す)
|
|
1698
|
+
public
|
|
1604
1699
|
|
|
1605
|
-
#
|
|
1606
|
-
|
|
1607
|
-
# 選択されたプロジェクトがあるかチェック
|
|
1608
|
-
if @project_mode.selected_path.nil?
|
|
1609
|
-
@terminal_ui&.show_project_not_selected_message if @terminal_ui
|
|
1610
|
-
return false
|
|
1611
|
-
end
|
|
1700
|
+
# ジョブマネージャを取得
|
|
1701
|
+
attr_reader :job_manager, :job_mode, :notification_manager
|
|
1612
1702
|
|
|
1613
|
-
|
|
1703
|
+
# ジョブモードに入る
|
|
1704
|
+
def enter_job_mode
|
|
1705
|
+
@job_mode.activate
|
|
1706
|
+
@terminal_ui&.set_job_mode(@job_mode, @job_manager, @notification_manager) if @terminal_ui
|
|
1614
1707
|
true
|
|
1615
1708
|
end
|
|
1616
1709
|
|
|
1617
|
-
#
|
|
1618
|
-
def
|
|
1619
|
-
@
|
|
1620
|
-
@
|
|
1621
|
-
|
|
1710
|
+
# ジョブモードを終了
|
|
1711
|
+
def exit_job_mode
|
|
1712
|
+
@job_mode.deactivate
|
|
1713
|
+
@terminal_ui&.exit_job_mode if @terminal_ui
|
|
1714
|
+
refresh
|
|
1622
1715
|
true
|
|
1623
1716
|
end
|
|
1624
1717
|
|
|
1625
|
-
#
|
|
1626
|
-
def
|
|
1627
|
-
|
|
1628
|
-
return false if bookmarks.empty? || @current_index >= bookmarks.length
|
|
1629
|
-
|
|
1630
|
-
current_bookmark = bookmarks[@current_index]
|
|
1631
|
-
old_name = current_bookmark[:name]
|
|
1632
|
-
|
|
1633
|
-
# ダイアログレンダラーを使用して入力ダイアログを表示
|
|
1634
|
-
new_name = @dialog_renderer.show_input_dialog("Rename: #{old_name}", "Enter new name:", {
|
|
1635
|
-
border_color: "\e[33m", # Yellow
|
|
1636
|
-
title_color: "\e[1;33m", # Bold yellow
|
|
1637
|
-
content_color: "\e[37m" # White
|
|
1638
|
-
})
|
|
1639
|
-
|
|
1640
|
-
return false if new_name.nil? || new_name.empty?
|
|
1641
|
-
|
|
1642
|
-
# Bookmarkを使用してリネーム
|
|
1643
|
-
result = @bookmark_manager.instance_variable_get(:@bookmark).rename(old_name, new_name)
|
|
1644
|
-
|
|
1645
|
-
@terminal_ui&.refresh_display if @terminal_ui
|
|
1646
|
-
result
|
|
1647
|
-
end
|
|
1648
|
-
|
|
1649
|
-
# プロジェクトモードでブックマークを削除
|
|
1650
|
-
def delete_bookmark_in_project_mode
|
|
1651
|
-
bookmarks = @bookmark_manager.list
|
|
1652
|
-
return false if bookmarks.empty? || @current_index >= bookmarks.length
|
|
1653
|
-
|
|
1654
|
-
current_bookmark = bookmarks[@current_index]
|
|
1655
|
-
bookmark_name = current_bookmark[:name]
|
|
1656
|
-
|
|
1657
|
-
# 確認ダイアログを表示
|
|
1658
|
-
content_lines = [
|
|
1659
|
-
'',
|
|
1660
|
-
"Delete this bookmark?",
|
|
1661
|
-
" #{bookmark_name}",
|
|
1662
|
-
'',
|
|
1663
|
-
' [Y]es - Delete',
|
|
1664
|
-
' [N]o - Cancel',
|
|
1665
|
-
''
|
|
1666
|
-
]
|
|
1667
|
-
|
|
1668
|
-
title = 'Confirm Delete'
|
|
1669
|
-
width = [50, bookmark_name.length + 10].max
|
|
1670
|
-
height = content_lines.length + 4
|
|
1671
|
-
x, y = @dialog_renderer.calculate_center(width, height)
|
|
1672
|
-
|
|
1673
|
-
@dialog_renderer.draw_floating_window(x, y, width, height, title, content_lines, {
|
|
1674
|
-
border_color: "\e[31m", # Red (warning)
|
|
1675
|
-
title_color: "\e[1;31m", # Bold red
|
|
1676
|
-
content_color: "\e[37m" # White
|
|
1677
|
-
})
|
|
1678
|
-
|
|
1679
|
-
# 確認を待つ
|
|
1680
|
-
confirmed = false
|
|
1681
|
-
loop do
|
|
1682
|
-
input = STDIN.getch.downcase
|
|
1683
|
-
|
|
1684
|
-
case input
|
|
1685
|
-
when 'y'
|
|
1686
|
-
confirmed = true
|
|
1687
|
-
break
|
|
1688
|
-
when 'n', "\e" # n or ESC
|
|
1689
|
-
confirmed = false
|
|
1690
|
-
break
|
|
1691
|
-
end
|
|
1692
|
-
end
|
|
1693
|
-
|
|
1694
|
-
@dialog_renderer.clear_area(x, y, width, height)
|
|
1695
|
-
@terminal_ui&.refresh_display
|
|
1696
|
-
|
|
1697
|
-
return false unless confirmed
|
|
1698
|
-
|
|
1699
|
-
# Bookmarkを使用して削除
|
|
1700
|
-
result = @bookmark_manager.instance_variable_get(:@bookmark).remove(bookmark_name)
|
|
1718
|
+
# ジョブモード中のキー処理
|
|
1719
|
+
def handle_job_mode_key(key)
|
|
1720
|
+
result = @job_mode.handle_key(key)
|
|
1701
1721
|
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
@
|
|
1722
|
+
case result
|
|
1723
|
+
when :exit
|
|
1724
|
+
exit_job_mode
|
|
1725
|
+
true
|
|
1726
|
+
when :show_log
|
|
1727
|
+
# ログ表示は将来実装
|
|
1728
|
+
@terminal_ui&.trigger_job_mode_redraw if @terminal_ui
|
|
1729
|
+
true
|
|
1730
|
+
when true, false
|
|
1731
|
+
@terminal_ui&.trigger_job_mode_redraw if @terminal_ui
|
|
1732
|
+
result
|
|
1733
|
+
else
|
|
1734
|
+
result
|
|
1712
1735
|
end
|
|
1713
|
-
|
|
1714
|
-
@terminal_ui&.refresh_display if @terminal_ui
|
|
1715
|
-
result
|
|
1716
1736
|
end
|
|
1717
1737
|
|
|
1718
|
-
#
|
|
1719
|
-
def
|
|
1720
|
-
|
|
1721
|
-
return false if bookmarks.empty? || @current_index >= bookmarks.length
|
|
1722
|
-
|
|
1723
|
-
# ブックマークをプロジェクトとして選択
|
|
1724
|
-
@project_mode.select_bookmark(@current_index + 1)
|
|
1725
|
-
true
|
|
1738
|
+
# ジョブモード中かどうか
|
|
1739
|
+
def in_job_mode?
|
|
1740
|
+
@job_mode.active?
|
|
1726
1741
|
end
|
|
1727
1742
|
|
|
1728
|
-
#
|
|
1729
|
-
def
|
|
1730
|
-
@
|
|
1743
|
+
# ジョブがあるかどうか
|
|
1744
|
+
def has_jobs?
|
|
1745
|
+
@job_manager.has_jobs?
|
|
1731
1746
|
end
|
|
1732
1747
|
|
|
1733
|
-
#
|
|
1734
|
-
def
|
|
1735
|
-
@
|
|
1736
|
-
end
|
|
1748
|
+
# ジョブのステータスバーテキストを取得
|
|
1749
|
+
def job_status_bar_text
|
|
1750
|
+
return nil unless @job_manager.has_jobs?
|
|
1737
1751
|
|
|
1738
|
-
|
|
1739
|
-
def show_help_dialog
|
|
1740
|
-
@terminal_ui&.show_help_dialog if @terminal_ui
|
|
1741
|
-
true
|
|
1752
|
+
@job_manager.status_bar_text
|
|
1742
1753
|
end
|
|
1743
1754
|
end
|
|
1744
1755
|
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rufio
|
|
4
|
+
# Noice風の通知を管理するクラス
|
|
5
|
+
# 画面右上に最大3個までの通知を表示
|
|
6
|
+
class NotificationManager
|
|
7
|
+
MAX_NOTIFICATIONS = 3
|
|
8
|
+
DEFAULT_DISPLAY_DURATION = 3 # 秒
|
|
9
|
+
|
|
10
|
+
attr_reader :notifications
|
|
11
|
+
|
|
12
|
+
def initialize
|
|
13
|
+
@notifications = []
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# 通知を追加
|
|
17
|
+
# @param name [String] タスク名
|
|
18
|
+
# @param type [Symbol] :success または :error
|
|
19
|
+
# @param duration [Float] タスクの実行時間
|
|
20
|
+
# @param exit_code [Integer, nil] 終了コード(エラー時のみ)
|
|
21
|
+
# @param display_duration [Integer] 通知の表示時間(秒)
|
|
22
|
+
def add(name, type, duration:, exit_code: nil, display_duration: DEFAULT_DISPLAY_DURATION)
|
|
23
|
+
notification = {
|
|
24
|
+
name: name,
|
|
25
|
+
type: type,
|
|
26
|
+
duration: duration,
|
|
27
|
+
exit_code: exit_code,
|
|
28
|
+
created_at: Time.now,
|
|
29
|
+
display_duration: display_duration,
|
|
30
|
+
border_color: type == :success ? :green : :red,
|
|
31
|
+
status_text: build_status_text(type, duration)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@notifications << notification
|
|
35
|
+
|
|
36
|
+
# 最大3個を超えた場合、最も古い通知を削除
|
|
37
|
+
@notifications.shift if @notifications.size > MAX_NOTIFICATIONS
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# 期限切れの通知を削除
|
|
41
|
+
def expire_old_notifications
|
|
42
|
+
now = Time.now
|
|
43
|
+
@notifications.reject! do |notification|
|
|
44
|
+
now - notification[:created_at] > notification[:display_duration]
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# 通知の数
|
|
49
|
+
# @return [Integer]
|
|
50
|
+
def count
|
|
51
|
+
@notifications.size
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# 全ての通知をクリア
|
|
55
|
+
def clear
|
|
56
|
+
@notifications.clear
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
# ステータステキストを生成
|
|
62
|
+
# @param type [Symbol] :success または :error
|
|
63
|
+
# @param duration [Float] 実行時間
|
|
64
|
+
# @return [String]
|
|
65
|
+
def build_status_text(type, duration)
|
|
66
|
+
formatted_duration = format('%.1fs', duration)
|
|
67
|
+
case type
|
|
68
|
+
when :success
|
|
69
|
+
"Done (#{formatted_duration})"
|
|
70
|
+
when :error
|
|
71
|
+
"Failed (#{formatted_duration})"
|
|
72
|
+
else
|
|
73
|
+
formatted_duration
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|