rufio 0.63.0 → 0.65.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 +52 -16
- data/lib/rufio/bookmark_manager.rb +159 -103
- data/lib/rufio/command_mode_ui.rb +56 -15
- data/lib/rufio/dialog_renderer.rb +67 -0
- data/lib/rufio/keybind_handler.rb +215 -174
- data/lib/rufio/screen.rb +113 -5
- data/lib/rufio/script_path_manager.rb +3 -3
- data/lib/rufio/script_runner.rb +1 -1
- data/lib/rufio/terminal_ui.rb +145 -80
- data/lib/rufio/text_utils.rb +4 -2
- data/lib/rufio/version.rb +1 -1
- data/lib/rufio/zoxide_integration.rb +97 -48
- data/lib/rufio.rb +50 -49
- metadata +2 -2
|
@@ -73,6 +73,9 @@ module Rufio
|
|
|
73
73
|
|
|
74
74
|
def set_terminal_ui(terminal_ui)
|
|
75
75
|
@terminal_ui = terminal_ui
|
|
76
|
+
# terminal_ui が設定されたら、bookmark_manager と zoxide_integration にも渡す
|
|
77
|
+
@bookmark_manager.set_terminal_ui(terminal_ui)
|
|
78
|
+
@zoxide_integration.set_terminal_ui(terminal_ui)
|
|
76
79
|
end
|
|
77
80
|
|
|
78
81
|
def selected_items
|
|
@@ -430,6 +433,54 @@ module Rufio
|
|
|
430
433
|
|
|
431
434
|
private
|
|
432
435
|
|
|
436
|
+
# オーバーレイダイアログを表示してキー入力を待つヘルパーメソッド
|
|
437
|
+
# terminal_ui が利用可能な場合はオーバーレイを使用、そうでなければ従来の方法を使用
|
|
438
|
+
# @param title [String] ダイアログタイトル
|
|
439
|
+
# @param content_lines [Array<String>] コンテンツ行
|
|
440
|
+
# @param options [Hash] オプション
|
|
441
|
+
# @yield キー入力処理(ブロックが与えられた場合)
|
|
442
|
+
# @return [String] 入力されたキー
|
|
443
|
+
def show_overlay_dialog(title, content_lines, options = {}, &block)
|
|
444
|
+
# terminal_ui が利用可能で、screen と renderer が存在する場合のみオーバーレイを使用
|
|
445
|
+
use_overlay = @terminal_ui &&
|
|
446
|
+
@terminal_ui.respond_to?(:screen) &&
|
|
447
|
+
@terminal_ui.respond_to?(:renderer) &&
|
|
448
|
+
@terminal_ui.screen &&
|
|
449
|
+
@terminal_ui.renderer
|
|
450
|
+
|
|
451
|
+
if use_overlay
|
|
452
|
+
# オーバーレイを使用
|
|
453
|
+
@terminal_ui.show_overlay_dialog(title, content_lines, options, &block)
|
|
454
|
+
else
|
|
455
|
+
# フォールバック: 従来の方法
|
|
456
|
+
width = options[:width]
|
|
457
|
+
height = options[:height]
|
|
458
|
+
|
|
459
|
+
unless width && height
|
|
460
|
+
width, height = @dialog_renderer.calculate_dimensions(content_lines, {
|
|
461
|
+
title: title,
|
|
462
|
+
min_width: options[:min_width] || 40,
|
|
463
|
+
max_width: options[:max_width] || 80
|
|
464
|
+
})
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
x, y = @dialog_renderer.calculate_center(width, height)
|
|
468
|
+
|
|
469
|
+
@dialog_renderer.draw_floating_window(x, y, width, height, title, content_lines, {
|
|
470
|
+
border_color: options[:border_color] || "\e[37m",
|
|
471
|
+
title_color: options[:title_color] || "\e[1;33m",
|
|
472
|
+
content_color: options[:content_color] || "\e[37m"
|
|
473
|
+
})
|
|
474
|
+
|
|
475
|
+
key = block_given? ? yield : STDIN.getch
|
|
476
|
+
|
|
477
|
+
@dialog_renderer.clear_area(x, y, width, height)
|
|
478
|
+
@terminal_ui&.refresh_display
|
|
479
|
+
|
|
480
|
+
key
|
|
481
|
+
end
|
|
482
|
+
end
|
|
483
|
+
|
|
433
484
|
# Enterキーの処理:ファイルならプレビューフォーカス、ディレクトリならナビゲート
|
|
434
485
|
def handle_enter_key
|
|
435
486
|
entry = current_entry
|
|
@@ -537,7 +588,8 @@ module Rufio
|
|
|
537
588
|
|
|
538
589
|
if entry[:type] == 'file'
|
|
539
590
|
@file_opener.open_file(entry[:path])
|
|
540
|
-
|
|
591
|
+
# ターミナルアプリ(vim等)を起動した後は画面リフレッシュが必要
|
|
592
|
+
:needs_refresh
|
|
541
593
|
else
|
|
542
594
|
false
|
|
543
595
|
end
|
|
@@ -571,7 +623,8 @@ module Rufio
|
|
|
571
623
|
@file_opener.open_file(full_path) if File.exist?(full_path)
|
|
572
624
|
end
|
|
573
625
|
|
|
574
|
-
|
|
626
|
+
# fzfとvim等はターミナルを占有するので画面リフレッシュが必要
|
|
627
|
+
:needs_refresh
|
|
575
628
|
end
|
|
576
629
|
|
|
577
630
|
def fzf_available?
|
|
@@ -620,7 +673,8 @@ module Rufio
|
|
|
620
673
|
@file_opener.open_file_with_line(full_path, line_number) if File.exist?(full_path)
|
|
621
674
|
end
|
|
622
675
|
|
|
623
|
-
|
|
676
|
+
# fzfとvim等はターミナルを占有するので画面リフレッシュが必要
|
|
677
|
+
:needs_refresh
|
|
624
678
|
end
|
|
625
679
|
|
|
626
680
|
def rga_available?
|
|
@@ -804,32 +858,31 @@ module Rufio
|
|
|
804
858
|
title = 'Confirm Delete'
|
|
805
859
|
width = [50, current_name.length + 10].max
|
|
806
860
|
height = content_lines.length + 4
|
|
807
|
-
x, y = @dialog_renderer.calculate_center(width, height)
|
|
808
861
|
|
|
809
|
-
|
|
862
|
+
# 確認を待つ
|
|
863
|
+
confirmed = false
|
|
864
|
+
show_overlay_dialog(title, content_lines, {
|
|
865
|
+
width: width,
|
|
866
|
+
height: height,
|
|
810
867
|
border_color: "\e[31m", # Red (warning)
|
|
811
868
|
title_color: "\e[1;31m", # Bold red
|
|
812
869
|
content_color: "\e[37m" # White
|
|
813
|
-
})
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
confirmed = false
|
|
826
|
-
break
|
|
870
|
+
}) do
|
|
871
|
+
loop do
|
|
872
|
+
input = STDIN.getch.downcase
|
|
873
|
+
|
|
874
|
+
case input
|
|
875
|
+
when 'y'
|
|
876
|
+
confirmed = true
|
|
877
|
+
break
|
|
878
|
+
when 'n', "\e" # n or ESC
|
|
879
|
+
confirmed = false
|
|
880
|
+
break
|
|
881
|
+
end
|
|
827
882
|
end
|
|
883
|
+
nil
|
|
828
884
|
end
|
|
829
885
|
|
|
830
|
-
@dialog_renderer.clear_area(x, y, width, height)
|
|
831
|
-
@terminal_ui&.refresh_display
|
|
832
|
-
|
|
833
886
|
return false unless confirmed
|
|
834
887
|
|
|
835
888
|
# FileOperationsを使用して削除
|
|
@@ -969,41 +1022,35 @@ module Rufio
|
|
|
969
1022
|
# タイトルあり: 上枠1 + タイトル1 + 区切り1 + コンテンツ + 下枠1
|
|
970
1023
|
dialog_height = DIALOG_BORDER_HEIGHT + content_lines.length
|
|
971
1024
|
|
|
972
|
-
# ダイアログの位置を中央に設定
|
|
973
|
-
x, y = @dialog_renderer.calculate_center(dialog_width, dialog_height)
|
|
974
|
-
|
|
975
|
-
# ダイアログの描画
|
|
976
|
-
@dialog_renderer.draw_floating_window(x, y, dialog_width, dialog_height, title, content_lines, {
|
|
977
|
-
border_color: "\e[31m", # 赤色(警告)
|
|
978
|
-
title_color: "\e[1;31m", # 太字赤色
|
|
979
|
-
content_color: "\e[37m" # 白色
|
|
980
|
-
})
|
|
981
|
-
|
|
982
1025
|
# フラッシュしてユーザーの注意を引く
|
|
983
1026
|
print "\a" # ベル音
|
|
984
1027
|
|
|
985
1028
|
# キー入力待機
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1029
|
+
confirmed = false
|
|
1030
|
+
show_overlay_dialog(title, content_lines, {
|
|
1031
|
+
width: dialog_width,
|
|
1032
|
+
height: dialog_height,
|
|
1033
|
+
border_color: "\e[31m", # 赤色(警告)
|
|
1034
|
+
title_color: "\e[1;31m", # 太字赤色
|
|
1035
|
+
content_color: "\e[37m" # 白色
|
|
1036
|
+
}) do
|
|
1037
|
+
loop do
|
|
1038
|
+
input = STDIN.getch.downcase
|
|
1039
|
+
|
|
1040
|
+
case input
|
|
1041
|
+
when 'y'
|
|
1042
|
+
confirmed = true
|
|
1043
|
+
break
|
|
1044
|
+
when 'n', "\e", "\x03", 'q' # n, ESC, Ctrl+C, q
|
|
1045
|
+
confirmed = false
|
|
1046
|
+
break
|
|
1047
|
+
end
|
|
1048
|
+
# 無効なキー入力の場合は再度ループ
|
|
1004
1049
|
end
|
|
1005
|
-
|
|
1050
|
+
nil
|
|
1006
1051
|
end
|
|
1052
|
+
|
|
1053
|
+
confirmed
|
|
1007
1054
|
end
|
|
1008
1055
|
|
|
1009
1056
|
def show_floating_move_confirmation(count, source_path, dest_path)
|
|
@@ -1030,38 +1077,32 @@ module Rufio
|
|
|
1030
1077
|
dialog_width = CONFIRMATION_DIALOG_WIDTH
|
|
1031
1078
|
dialog_height = DIALOG_BORDER_HEIGHT + content_lines.length
|
|
1032
1079
|
|
|
1033
|
-
# ダイアログの位置を中央に設定
|
|
1034
|
-
x, y = @dialog_renderer.calculate_center(dialog_width, dialog_height)
|
|
1035
|
-
|
|
1036
|
-
# ダイアログの描画(移動は青色で表示)
|
|
1037
|
-
@dialog_renderer.draw_floating_window(x, y, dialog_width, dialog_height, title, content_lines, {
|
|
1038
|
-
border_color: "\e[34m", # 青色(情報)
|
|
1039
|
-
title_color: "\e[1;34m", # 太字青色
|
|
1040
|
-
content_color: "\e[37m" # 白色
|
|
1041
|
-
})
|
|
1042
|
-
|
|
1043
1080
|
# キー入力待機
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1081
|
+
confirmed = false
|
|
1082
|
+
show_overlay_dialog(title, content_lines, {
|
|
1083
|
+
width: dialog_width,
|
|
1084
|
+
height: dialog_height,
|
|
1085
|
+
border_color: "\e[34m", # 青色(情報)
|
|
1086
|
+
title_color: "\e[1;34m", # 太字青色
|
|
1087
|
+
content_color: "\e[37m" # 白色
|
|
1088
|
+
}) do
|
|
1089
|
+
loop do
|
|
1090
|
+
input = STDIN.getch.downcase
|
|
1091
|
+
|
|
1092
|
+
case input
|
|
1093
|
+
when 'y'
|
|
1094
|
+
confirmed = true
|
|
1095
|
+
break
|
|
1096
|
+
when 'n', "\e", "\x03", 'q' # n, ESC, Ctrl+C, q
|
|
1097
|
+
confirmed = false
|
|
1098
|
+
break
|
|
1099
|
+
end
|
|
1100
|
+
# 無効なキー入力の場合は再度ループ
|
|
1062
1101
|
end
|
|
1063
|
-
|
|
1102
|
+
nil
|
|
1064
1103
|
end
|
|
1104
|
+
|
|
1105
|
+
confirmed
|
|
1065
1106
|
end
|
|
1066
1107
|
|
|
1067
1108
|
def show_floating_copy_confirmation(count, source_path, dest_path)
|
|
@@ -1088,38 +1129,32 @@ module Rufio
|
|
|
1088
1129
|
dialog_width = CONFIRMATION_DIALOG_WIDTH
|
|
1089
1130
|
dialog_height = DIALOG_BORDER_HEIGHT + content_lines.length
|
|
1090
1131
|
|
|
1091
|
-
# ダイアログの位置を中央に設定
|
|
1092
|
-
x, y = @dialog_renderer.calculate_center(dialog_width, dialog_height)
|
|
1093
|
-
|
|
1094
|
-
# ダイアログの描画(コピーは緑色で表示)
|
|
1095
|
-
@dialog_renderer.draw_floating_window(x, y, dialog_width, dialog_height, title, content_lines, {
|
|
1096
|
-
border_color: "\e[32m", # 緑色(安全な操作)
|
|
1097
|
-
title_color: "\e[1;32m", # 太字緑色
|
|
1098
|
-
content_color: "\e[37m" # 白色
|
|
1099
|
-
})
|
|
1100
|
-
|
|
1101
1132
|
# キー入力待機
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1133
|
+
confirmed = false
|
|
1134
|
+
show_overlay_dialog(title, content_lines, {
|
|
1135
|
+
width: dialog_width,
|
|
1136
|
+
height: dialog_height,
|
|
1137
|
+
border_color: "\e[32m", # 緑色(安全な操作)
|
|
1138
|
+
title_color: "\e[1;32m", # 太字緑色
|
|
1139
|
+
content_color: "\e[37m" # 白色
|
|
1140
|
+
}) do
|
|
1141
|
+
loop do
|
|
1142
|
+
input = STDIN.getch.downcase
|
|
1143
|
+
|
|
1144
|
+
case input
|
|
1145
|
+
when 'y'
|
|
1146
|
+
confirmed = true
|
|
1147
|
+
break
|
|
1148
|
+
when 'n', "\e", "\x03", 'q' # n, ESC, Ctrl+C, q
|
|
1149
|
+
confirmed = false
|
|
1150
|
+
break
|
|
1151
|
+
end
|
|
1152
|
+
# 無効なキー入力の場合は再度ループ
|
|
1120
1153
|
end
|
|
1121
|
-
|
|
1154
|
+
nil
|
|
1122
1155
|
end
|
|
1156
|
+
|
|
1157
|
+
confirmed
|
|
1123
1158
|
end
|
|
1124
1159
|
|
|
1125
1160
|
def show_exit_confirmation
|
|
@@ -1139,34 +1174,32 @@ module Rufio
|
|
|
1139
1174
|
dialog_width = CONFIRMATION_DIALOG_WIDTH
|
|
1140
1175
|
dialog_height = DIALOG_BORDER_HEIGHT + content_lines.length
|
|
1141
1176
|
|
|
1142
|
-
# ダイアログの位置を中央に設定
|
|
1143
|
-
x, y = @dialog_renderer.calculate_center(dialog_width, dialog_height)
|
|
1144
|
-
|
|
1145
|
-
# ダイアログの描画(終了は黄色で表示)
|
|
1146
|
-
@dialog_renderer.draw_floating_window(x, y, dialog_width, dialog_height, title, content_lines, {
|
|
1147
|
-
border_color: "\e[33m", # 黄色(注意)
|
|
1148
|
-
title_color: "\e[1;33m", # 太字黄色
|
|
1149
|
-
content_color: "\e[37m" # 白色
|
|
1150
|
-
})
|
|
1151
|
-
|
|
1152
1177
|
# キー入力待機
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1178
|
+
confirmed = false
|
|
1179
|
+
show_overlay_dialog(title, content_lines, {
|
|
1180
|
+
width: dialog_width,
|
|
1181
|
+
height: dialog_height,
|
|
1182
|
+
border_color: "\e[33m", # 黄色(注意)
|
|
1183
|
+
title_color: "\e[1;33m", # 太字黄色
|
|
1184
|
+
content_color: "\e[37m" # 白色
|
|
1185
|
+
}) do
|
|
1186
|
+
loop do
|
|
1187
|
+
input = STDIN.getch.downcase
|
|
1188
|
+
|
|
1189
|
+
case input
|
|
1190
|
+
when 'y'
|
|
1191
|
+
confirmed = true
|
|
1192
|
+
break
|
|
1193
|
+
when 'n', "\e", "\x03" # n, ESC, Ctrl+C
|
|
1194
|
+
confirmed = false
|
|
1195
|
+
break
|
|
1196
|
+
end
|
|
1197
|
+
# 無効なキー入力の場合は再度ループ
|
|
1167
1198
|
end
|
|
1168
|
-
|
|
1199
|
+
nil
|
|
1169
1200
|
end
|
|
1201
|
+
|
|
1202
|
+
confirmed
|
|
1170
1203
|
end
|
|
1171
1204
|
|
|
1172
1205
|
# パスを指定した長さに短縮
|
|
@@ -1249,9 +1282,6 @@ module Rufio
|
|
|
1249
1282
|
dialog_width = has_errors ? 50 : 35
|
|
1250
1283
|
dialog_height = has_errors ? [8 + error_messages.length, 15].min : 6
|
|
1251
1284
|
|
|
1252
|
-
# ダイアログの位置を中央に設定
|
|
1253
|
-
x, y = @dialog_renderer.calculate_center(dialog_width, dialog_height)
|
|
1254
|
-
|
|
1255
1285
|
# 成功・失敗に応じた色設定
|
|
1256
1286
|
if success_count == total_count && !has_errors
|
|
1257
1287
|
border_color = "\e[32m" # 緑色(成功)
|
|
@@ -1284,19 +1314,14 @@ module Rufio
|
|
|
1284
1314
|
content_lines << ''
|
|
1285
1315
|
content_lines << 'Press any key to continue...'
|
|
1286
1316
|
|
|
1287
|
-
#
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
STDIN.getch
|
|
1296
|
-
|
|
1297
|
-
# ダイアログをクリア
|
|
1298
|
-
@dialog_renderer.clear_area(x, y, dialog_width, dialog_height)
|
|
1299
|
-
@terminal_ui&.refresh_display
|
|
1317
|
+
# オーバーレイダイアログを表示
|
|
1318
|
+
show_overlay_dialog(title, content_lines, {
|
|
1319
|
+
width: dialog_width,
|
|
1320
|
+
height: dialog_height,
|
|
1321
|
+
border_color: border_color,
|
|
1322
|
+
title_color: title_color,
|
|
1323
|
+
content_color: "\e[37m"
|
|
1324
|
+
})
|
|
1300
1325
|
end
|
|
1301
1326
|
|
|
1302
1327
|
|
|
@@ -1378,17 +1403,16 @@ module Rufio
|
|
|
1378
1403
|
title = 'Bookmark Exists'
|
|
1379
1404
|
width = 50
|
|
1380
1405
|
height = content_lines.length + 4
|
|
1381
|
-
x, y = @dialog_renderer.calculate_center(width, height)
|
|
1382
1406
|
|
|
1383
|
-
|
|
1407
|
+
# オーバーレイダイアログを表示
|
|
1408
|
+
show_overlay_dialog(title, content_lines, {
|
|
1409
|
+
width: width,
|
|
1410
|
+
height: height,
|
|
1384
1411
|
border_color: "\e[33m", # Yellow
|
|
1385
1412
|
title_color: "\e[1;33m", # Bold yellow
|
|
1386
1413
|
content_color: "\e[37m" # White
|
|
1387
1414
|
})
|
|
1388
1415
|
|
|
1389
|
-
STDIN.getch
|
|
1390
|
-
@dialog_renderer.clear_area(x, y, width, height)
|
|
1391
|
-
@terminal_ui&.refresh_display
|
|
1392
1416
|
return false
|
|
1393
1417
|
end
|
|
1394
1418
|
|
|
@@ -1429,17 +1453,16 @@ module Rufio
|
|
|
1429
1453
|
title = 'Bookmark Menu'
|
|
1430
1454
|
width = 45
|
|
1431
1455
|
height = content_lines.length + 4
|
|
1432
|
-
x, y = @dialog_renderer.calculate_center(width, height)
|
|
1433
1456
|
|
|
1434
|
-
|
|
1457
|
+
# オーバーレイダイアログを表示してキー入力を取得
|
|
1458
|
+
key = show_overlay_dialog(title, content_lines, {
|
|
1459
|
+
width: width,
|
|
1460
|
+
height: height,
|
|
1435
1461
|
border_color: "\e[36m", # Cyan
|
|
1436
1462
|
title_color: "\e[1;36m", # Bold cyan
|
|
1437
1463
|
content_color: "\e[37m" # White
|
|
1438
1464
|
})
|
|
1439
1465
|
|
|
1440
|
-
key = STDIN.getch
|
|
1441
|
-
@dialog_renderer.clear_area(x, y, width, height)
|
|
1442
|
-
|
|
1443
1466
|
case key
|
|
1444
1467
|
when '1'
|
|
1445
1468
|
add_bookmark
|
|
@@ -1503,6 +1526,8 @@ module Rufio
|
|
|
1503
1526
|
end
|
|
1504
1527
|
|
|
1505
1528
|
selected_index = 0
|
|
1529
|
+
screen = @terminal_ui&.screen
|
|
1530
|
+
renderer = @terminal_ui&.renderer
|
|
1506
1531
|
|
|
1507
1532
|
loop do
|
|
1508
1533
|
# メニューを描画
|
|
@@ -1516,16 +1541,33 @@ module Rufio
|
|
|
1516
1541
|
title = 'Script Paths'
|
|
1517
1542
|
width = 50
|
|
1518
1543
|
height = content_lines.length + 4
|
|
1519
|
-
x, y = @dialog_renderer.calculate_center(width, height)
|
|
1520
|
-
|
|
1521
|
-
@dialog_renderer.draw_floating_window(x, y, width, height, title, content_lines, {
|
|
1522
|
-
border_color: "\e[35m", # Magenta
|
|
1523
|
-
title_color: "\e[1;35m", # Bold magenta
|
|
1524
|
-
content_color: "\e[37m" # White
|
|
1525
|
-
})
|
|
1526
1544
|
|
|
1527
|
-
|
|
1528
|
-
|
|
1545
|
+
if screen && renderer
|
|
1546
|
+
# オーバーレイを使用
|
|
1547
|
+
screen.enable_overlay
|
|
1548
|
+
x, y = @dialog_renderer.calculate_center(width, height)
|
|
1549
|
+
@dialog_renderer.draw_floating_window_to_overlay(screen, x, y, width, height, title, content_lines, {
|
|
1550
|
+
border_color: "\e[35m", # Magenta
|
|
1551
|
+
title_color: "\e[1;35m", # Bold magenta
|
|
1552
|
+
content_color: "\e[37m" # White
|
|
1553
|
+
})
|
|
1554
|
+
renderer.render(screen)
|
|
1555
|
+
|
|
1556
|
+
key = STDIN.getch
|
|
1557
|
+
screen.disable_overlay
|
|
1558
|
+
renderer.render(screen)
|
|
1559
|
+
else
|
|
1560
|
+
# フォールバック: 従来の方法
|
|
1561
|
+
x, y = @dialog_renderer.calculate_center(width, height)
|
|
1562
|
+
@dialog_renderer.draw_floating_window(x, y, width, height, title, content_lines, {
|
|
1563
|
+
border_color: "\e[35m", # Magenta
|
|
1564
|
+
title_color: "\e[1;35m", # Bold magenta
|
|
1565
|
+
content_color: "\e[37m" # White
|
|
1566
|
+
})
|
|
1567
|
+
|
|
1568
|
+
key = STDIN.getch
|
|
1569
|
+
@dialog_renderer.clear_area(x, y, width, height)
|
|
1570
|
+
end
|
|
1529
1571
|
|
|
1530
1572
|
case key
|
|
1531
1573
|
when 'j'
|
|
@@ -1571,17 +1613,16 @@ module Rufio
|
|
|
1571
1613
|
title = 'Confirm Delete'
|
|
1572
1614
|
width = 50
|
|
1573
1615
|
height = content_lines.length + 4
|
|
1574
|
-
x, y = @dialog_renderer.calculate_center(width, height)
|
|
1575
1616
|
|
|
1576
|
-
|
|
1617
|
+
# オーバーレイダイアログを表示してキー入力を取得
|
|
1618
|
+
key = show_overlay_dialog(title, content_lines, {
|
|
1619
|
+
width: width,
|
|
1620
|
+
height: height,
|
|
1577
1621
|
border_color: "\e[31m", # Red
|
|
1578
1622
|
title_color: "\e[1;31m", # Bold red
|
|
1579
1623
|
content_color: "\e[37m" # White
|
|
1580
1624
|
})
|
|
1581
1625
|
|
|
1582
|
-
key = STDIN.getch
|
|
1583
|
-
@dialog_renderer.clear_area(x, y, width, height)
|
|
1584
|
-
|
|
1585
1626
|
key.downcase == 'y'
|
|
1586
1627
|
end
|
|
1587
1628
|
|
data/lib/rufio/screen.rb
CHANGED
|
@@ -30,6 +30,7 @@ module Rufio
|
|
|
30
30
|
@width = width
|
|
31
31
|
@height = height
|
|
32
32
|
@cells = Array.new(height) { Array.new(width) { default_cell } }
|
|
33
|
+
@overlay_cells = nil # オーバーレイレイヤー(ダイアログ用)
|
|
33
34
|
@dirty_rows = Set.new # Phase1: Dirty row tracking
|
|
34
35
|
end
|
|
35
36
|
|
|
@@ -116,12 +117,30 @@ module Rufio
|
|
|
116
117
|
result = String.new(capacity: @width * 20)
|
|
117
118
|
current_width = 0 # Phase1: Accumulate width from cells (no recalculation)
|
|
118
119
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
120
|
+
# オーバーレイがある場合はベース + オーバーレイを合成
|
|
121
|
+
if @overlay_cells
|
|
122
|
+
@width.times do |x|
|
|
123
|
+
overlay_cell = @overlay_cells[y][x]
|
|
124
|
+
base_cell = @cells[y][x]
|
|
122
125
|
|
|
123
|
-
|
|
124
|
-
|
|
126
|
+
# オーバーレイがあればオーバーレイを使用、なければベースを使用
|
|
127
|
+
cell = overlay_cell || base_cell
|
|
128
|
+
|
|
129
|
+
# マーカーセル(全角文字の2セル目)はスキップ
|
|
130
|
+
next if cell[:width] == 0
|
|
131
|
+
|
|
132
|
+
result << format_cell(cell)
|
|
133
|
+
current_width += cell[:width]
|
|
134
|
+
end
|
|
135
|
+
else
|
|
136
|
+
# オーバーレイなしの場合は従来の処理
|
|
137
|
+
@cells[y].each do |cell|
|
|
138
|
+
# Skip marker cells for full-width characters
|
|
139
|
+
next if cell[:width] == 0
|
|
140
|
+
|
|
141
|
+
result << format_cell(cell)
|
|
142
|
+
current_width += cell[:width] # Phase1: Use pre-calculated width
|
|
143
|
+
end
|
|
125
144
|
end
|
|
126
145
|
|
|
127
146
|
# Pad the row to full width
|
|
@@ -154,6 +173,95 @@ module Rufio
|
|
|
154
173
|
@dirty_rows.clear
|
|
155
174
|
end
|
|
156
175
|
|
|
176
|
+
# ===========================================
|
|
177
|
+
# オーバーレイレイヤー(ダイアログ用)
|
|
178
|
+
# ===========================================
|
|
179
|
+
|
|
180
|
+
# オーバーレイレイヤーを有効化
|
|
181
|
+
def enable_overlay
|
|
182
|
+
return if @overlay_cells
|
|
183
|
+
|
|
184
|
+
@overlay_cells = Array.new(@height) { Array.new(@width) { nil } }
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# オーバーレイレイヤーを無効化してクリア
|
|
188
|
+
def disable_overlay
|
|
189
|
+
return unless @overlay_cells
|
|
190
|
+
|
|
191
|
+
# オーバーレイが描画されていた行をdirtyにマーク
|
|
192
|
+
@height.times do |y|
|
|
193
|
+
if @overlay_cells[y].any? { |cell| cell }
|
|
194
|
+
@dirty_rows.add(y)
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
@overlay_cells = nil
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# オーバーレイレイヤーをクリア
|
|
202
|
+
def clear_overlay
|
|
203
|
+
return unless @overlay_cells
|
|
204
|
+
|
|
205
|
+
@height.times do |y|
|
|
206
|
+
if @overlay_cells[y].any? { |cell| cell }
|
|
207
|
+
@dirty_rows.add(y)
|
|
208
|
+
@overlay_cells[y] = Array.new(@width) { nil }
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# オーバーレイレイヤーが有効かどうか
|
|
214
|
+
def overlay_enabled?
|
|
215
|
+
!@overlay_cells.nil?
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# オーバーレイレイヤーに描画
|
|
219
|
+
def put_overlay(x, y, char, fg: nil, bg: nil, width: nil)
|
|
220
|
+
return unless @overlay_cells
|
|
221
|
+
return if out_of_bounds?(x, y)
|
|
222
|
+
|
|
223
|
+
char_width = width || TextUtils.display_width(char)
|
|
224
|
+
@overlay_cells[y][x] = {
|
|
225
|
+
char: char,
|
|
226
|
+
fg: fg,
|
|
227
|
+
bg: bg,
|
|
228
|
+
width: char_width
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
@dirty_rows.add(y)
|
|
232
|
+
|
|
233
|
+
# 全角文字の場合、次のセルをマーク
|
|
234
|
+
if char_width >= 2 && x + 1 < @width
|
|
235
|
+
(char_width - 1).times do |offset|
|
|
236
|
+
next_x = x + 1 + offset
|
|
237
|
+
break if next_x >= @width
|
|
238
|
+
@overlay_cells[y][next_x] = {
|
|
239
|
+
char: '',
|
|
240
|
+
fg: nil,
|
|
241
|
+
bg: nil,
|
|
242
|
+
width: 0
|
|
243
|
+
}
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# オーバーレイレイヤーに文字列を描画
|
|
249
|
+
def put_overlay_string(x, y, str, fg: nil, bg: nil)
|
|
250
|
+
return unless @overlay_cells
|
|
251
|
+
return if out_of_bounds?(x, y)
|
|
252
|
+
|
|
253
|
+
clean_str = str.include?("\e") ? ColorHelper.strip_ansi(str) : str
|
|
254
|
+
|
|
255
|
+
current_x = x
|
|
256
|
+
clean_str.each_char do |char|
|
|
257
|
+
break if current_x >= @width
|
|
258
|
+
|
|
259
|
+
char_width = TextUtils.display_width(char)
|
|
260
|
+
put_overlay(current_x, y, char, fg: fg, bg: bg, width: char_width)
|
|
261
|
+
current_x += char_width
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
157
265
|
private
|
|
158
266
|
|
|
159
267
|
def default_cell
|