rufio 0.91.0 → 1.0.1
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 +12 -0
- data/lib/rufio/keybind_handler.rb +0 -1
- data/lib/rufio/script_executor.rb +9 -4
- data/lib/rufio/terminal_ui.rb +147 -43
- data/lib/rufio/ui_renderer.rb +32 -3
- data/lib/rufio/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6e51cc4afc1f8340d7340288d7e20677fcd174d6dd32029714516827375d1d61
|
|
4
|
+
data.tar.gz: 3d777468ecee811d01cd303891d6c50aaae1b38e298e8ec2b48f818b6b24ad8c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4f9b4801d4d3f7bb0406e6f688d5e4de20ccf24d936924f714472d9841284804ebddc7d211a5c0b72cb72df09ea607ea47bd9bc02b0178550c86d9af791e8dfa
|
|
7
|
+
data.tar.gz: ffdb0429b787320d57fe28dd68bc67b7278dccf94723d30e52c619e51d60e954d3ed7fd4a1ca8eeaa1de0df914701784f08a74edafaa86a831bc928e3fad93ba
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,18 @@ 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
|
+
## [1.0.0] - 2026-04-28
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Job mode: View log with Space key**: Press Space on a selected job to display its full execution log. Footer switches to `[ESC] Close Log` while in log view; press ESC to return to the job list
|
|
12
|
+
- **Job mode: Disable Tab key**: Tab (bookmark cycling) is now suppressed in job mode to prevent accidental navigation. Removed `[Tab] Switch Mode` from the job mode footer
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
- **Windows: ESC key not working**: `STDIN.raw` + `getbyte` silently drops the ESC byte (0x1B) under Windows ConPTY. The blocking input thread now uses `getch` instead, pushing each received byte onto the queue
|
|
16
|
+
- **Windows: Preview flickering**: Redundant `print "\e[2J\e[H"` calls before `renderer.clear` caused the screen to blank momentarily on every redraw. Removed the duplicate clears in `refresh_display`, `set_job_mode`, and `exit_job_mode`. Also unified the async highlight-updated flag into `UIRenderer` (it was maintained separately in both `TerminalUI` and `UIRenderer`, causing missed redraws)
|
|
17
|
+
- **Windows: Cursor visible as blinking vertical bar in bottom-right corner**: `tput civis` is a no-op on Windows, leaving the cursor visible. Replaced `tput civis/cnorm/smcup/rmcup` with ANSI sequences `\e[?25l`/`\e[?25h`/`\e[?1049h`/`\e[?1049l` which work on Windows Terminal. Cursor is shown only during command mode input and hidden otherwise
|
|
18
|
+
- **Windows: `bundle install` fails with "cannot load git ls-files"**: `rufio.gemspec` now falls back to `Dir.glob` when `git` is not on PATH
|
|
19
|
+
|
|
8
20
|
## [0.91.0] - 2026-04-12
|
|
9
21
|
|
|
10
22
|
### Changed
|
|
@@ -118,7 +118,8 @@ module Rufio
|
|
|
118
118
|
|
|
119
119
|
begin
|
|
120
120
|
Timeout.timeout(timeout_sec) do
|
|
121
|
-
|
|
121
|
+
args = env.empty? ? [*command] : [env, *command]
|
|
122
|
+
stdin, stdout_io, stderr_io, wait_thread = Open3.popen3(*args, **options)
|
|
122
123
|
pid = wait_thread.pid
|
|
123
124
|
stdin.close
|
|
124
125
|
stdout = stdout_io.read
|
|
@@ -162,7 +163,9 @@ module Rufio
|
|
|
162
163
|
|
|
163
164
|
# タイムアウトなしで実行
|
|
164
165
|
def execute_without_timeout(command, env, options)
|
|
165
|
-
|
|
166
|
+
# 空の env ハッシュを渡すと Windows で SystemRoot 等が引き継がれないため省略する
|
|
167
|
+
args = env.empty? ? [*command] : [env, *command]
|
|
168
|
+
stdout, stderr, status = Open3.capture3(*args, **options)
|
|
166
169
|
|
|
167
170
|
{
|
|
168
171
|
success: status.success?,
|
|
@@ -183,7 +186,8 @@ module Rufio
|
|
|
183
186
|
|
|
184
187
|
begin
|
|
185
188
|
Timeout.timeout(timeout_sec) do
|
|
186
|
-
|
|
189
|
+
args = env.empty? ? [shell_command] : [env, shell_command]
|
|
190
|
+
stdin, stdout_io, stderr_io, wait_thread = Open3.popen3(*args, **options)
|
|
187
191
|
pid = wait_thread.pid
|
|
188
192
|
stdin.close
|
|
189
193
|
stdout = stdout_io.read
|
|
@@ -226,7 +230,8 @@ module Rufio
|
|
|
226
230
|
|
|
227
231
|
# シェルコマンドをタイムアウトなしで実行
|
|
228
232
|
def execute_shell_without_timeout(shell_command, env, options)
|
|
229
|
-
|
|
233
|
+
args = env.empty? ? [shell_command] : [env, shell_command]
|
|
234
|
+
stdout, stderr, status = Open3.capture3(*args, **options)
|
|
230
235
|
|
|
231
236
|
{
|
|
232
237
|
success: status.success?,
|
data/lib/rufio/terminal_ui.rb
CHANGED
|
@@ -48,6 +48,8 @@ module Rufio
|
|
|
48
48
|
@running = false
|
|
49
49
|
@test_mode = test_mode
|
|
50
50
|
@multibyte_reader = MultibyteInputReader.new(STDIN)
|
|
51
|
+
@input_queue = nil
|
|
52
|
+
@input_thread = nil
|
|
51
53
|
@command_mode_active = false
|
|
52
54
|
@command_input = ""
|
|
53
55
|
@command_mode = CommandMode.new
|
|
@@ -73,8 +75,6 @@ module Rufio
|
|
|
73
75
|
|
|
74
76
|
# シンタックスハイライター(bat が利用可能な場合のみ動作)
|
|
75
77
|
@syntax_highlighter = SyntaxHighlighter.new
|
|
76
|
-
# 非同期ハイライト完了フラグ(Thread → メインループへの通知)
|
|
77
|
-
@highlight_updated = false
|
|
78
78
|
|
|
79
79
|
|
|
80
80
|
# Tab mode manager
|
|
@@ -132,7 +132,6 @@ module Rufio
|
|
|
132
132
|
def refresh_display
|
|
133
133
|
# ウィンドウサイズを更新してから画面をクリアして再描画
|
|
134
134
|
update_screen_size
|
|
135
|
-
print "\e[2J\e[H" # clear screen, cursor to home
|
|
136
135
|
|
|
137
136
|
# プレビューキャッシュをクリア(ディレクトリ変更やリフレッシュ時)
|
|
138
137
|
@preview_cache.clear
|
|
@@ -176,16 +175,36 @@ module Rufio
|
|
|
176
175
|
# ブックマークハイライトが期限切れかどうか
|
|
177
176
|
# @return [Boolean] true=期限切れ or ハイライト中でない, false=ハイライト中
|
|
178
177
|
def setup_terminal
|
|
179
|
-
# terminal setup
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
print "\e[2J\e[H"
|
|
178
|
+
# terminal setup(ANSI エスケープで統一 — tput は Windows で動作しない)
|
|
179
|
+
print "\e[?1049h" # alternate screen buffer
|
|
180
|
+
print "\e[?25l" # cursor invisible
|
|
181
|
+
print "\e[2J\e[H" # clear screen, cursor to home (first time only)
|
|
183
182
|
|
|
184
183
|
# rawモードに設定(ゲームループのノンブロッキング入力用)
|
|
185
184
|
if STDIN.tty?
|
|
186
185
|
STDIN.raw!
|
|
187
186
|
end
|
|
188
187
|
|
|
188
|
+
# Windows: ブロッキング読み取りスレッドを起動。
|
|
189
|
+
# ConPTY パイプでは IO.select のシグナル遅延で ESC を取りこぼすため、
|
|
190
|
+
# 常にブロッキングで読み取りキューに積む。
|
|
191
|
+
# getbyte は raw モード下で ESC (0x1B) を取りこぼす ConPTY バグがあるため
|
|
192
|
+
# getch を使用し、受信した各バイトをキューに積む。
|
|
193
|
+
if windows?
|
|
194
|
+
@input_queue = Queue.new
|
|
195
|
+
@stop_input_thread = false
|
|
196
|
+
@input_thread = Thread.new do
|
|
197
|
+
loop do
|
|
198
|
+
break if @stop_input_thread
|
|
199
|
+
ch = STDIN.getch(min: 1)
|
|
200
|
+
break if ch.nil? || @stop_input_thread
|
|
201
|
+
ch.bytes.each { |b| @input_queue << b }
|
|
202
|
+
end
|
|
203
|
+
rescue
|
|
204
|
+
# スレッド終了
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
189
208
|
# SGR拡張マウスレポートを有効化
|
|
190
209
|
# Unix: \e[?1003h (any-event) + \e[?1006h (SGR座標) → クリック・スクロール・移動
|
|
191
210
|
# Windows: \e[?1003h は Windows Terminal / conhost 非対応。
|
|
@@ -215,35 +234,66 @@ module Rufio
|
|
|
215
234
|
end
|
|
216
235
|
|
|
217
236
|
# エスケープシーケンスの後続バイトを読み取る(矢印キー等の短いシーケンス用)。
|
|
218
|
-
# Windows: IO.select
|
|
237
|
+
# Windows: IO.select を使わずキューから取得(5ms タイムアウト)。
|
|
219
238
|
def read_next_input_byte
|
|
220
239
|
if windows?
|
|
221
|
-
|
|
222
|
-
|
|
240
|
+
b = windows_queue_pop_with_timeout(0.005)
|
|
241
|
+
b.nil? ? nil : b.chr(Encoding::BINARY)
|
|
223
242
|
else
|
|
224
243
|
STDIN.read_nonblock(1) rescue nil
|
|
225
244
|
end
|
|
226
245
|
end
|
|
227
246
|
|
|
228
247
|
# SGRマウスシーケンスの後続バイトを読み取る。
|
|
229
|
-
#
|
|
230
|
-
# Windows Console のイベントキューにバイトが積まれるまで 20ms 待つ。
|
|
248
|
+
# Windows: キューから取得(20ms タイムアウト)。
|
|
231
249
|
def read_next_mouse_byte
|
|
232
250
|
if windows?
|
|
233
|
-
|
|
234
|
-
|
|
251
|
+
b = windows_queue_pop_with_timeout(0.020)
|
|
252
|
+
b.nil? ? nil : b.chr(Encoding::BINARY)
|
|
235
253
|
else
|
|
236
254
|
STDIN.read_nonblock(1) rescue nil
|
|
237
255
|
end
|
|
238
256
|
end
|
|
239
257
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
258
|
+
# Windows 専用: キューから1文字(マルチバイト対応)を組み立てる。
|
|
259
|
+
# first_byte_int は getbyte が返す Integer。
|
|
260
|
+
def windows_build_char(first_byte_int)
|
|
261
|
+
remaining = case first_byte_int
|
|
262
|
+
when 0x00..0x7F then 0
|
|
263
|
+
when 0xC0..0xDF then 1
|
|
264
|
+
when 0xE0..0xEF then 2
|
|
265
|
+
when 0xF0..0xF7 then 3
|
|
266
|
+
else 0
|
|
267
|
+
end
|
|
268
|
+
if remaining == 0
|
|
269
|
+
first_byte_int.chr(Encoding::UTF_8)
|
|
270
|
+
else
|
|
271
|
+
buf = first_byte_int.chr(Encoding::BINARY)
|
|
272
|
+
remaining.times do
|
|
273
|
+
b = windows_queue_pop_with_timeout(0.005)
|
|
274
|
+
return nil if b.nil?
|
|
275
|
+
buf << b.chr(Encoding::BINARY)
|
|
276
|
+
end
|
|
277
|
+
result = buf.force_encoding(Encoding::UTF_8)
|
|
278
|
+
result.valid_encoding? ? result : nil
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
# Windows 専用: @input_queue からタイムアウト付きで Integer バイトを取り出す。
|
|
283
|
+
def windows_queue_pop_with_timeout(timeout_sec)
|
|
284
|
+
deadline = Time.now + timeout_sec
|
|
285
|
+
loop do
|
|
286
|
+
begin
|
|
287
|
+
return @input_queue.pop(true)
|
|
288
|
+
rescue ThreadError
|
|
289
|
+
return nil if Time.now >= deadline
|
|
290
|
+
sleep 0.001
|
|
291
|
+
end
|
|
244
292
|
end
|
|
293
|
+
end
|
|
245
294
|
|
|
246
|
-
|
|
295
|
+
def cleanup_terminal
|
|
296
|
+
# マウスレポートを先に無効化して新しいイベントの流入を止める
|
|
247
297
|
if windows?
|
|
248
298
|
print "\e[?1000l\e[?1006l"
|
|
249
299
|
else
|
|
@@ -251,11 +301,46 @@ module Rufio
|
|
|
251
301
|
end
|
|
252
302
|
STDOUT.flush
|
|
253
303
|
|
|
254
|
-
|
|
255
|
-
|
|
304
|
+
# Windows 入力スレッドを停止
|
|
305
|
+
if windows? && @input_thread
|
|
306
|
+
@stop_input_thread = true
|
|
307
|
+
@input_thread.kill rescue nil
|
|
308
|
+
@input_thread.join(0.5) rescue nil
|
|
309
|
+
@input_thread = nil
|
|
310
|
+
# コンソール入力バッファをフラッシュして残留イベントによる不正出力を防ぐ
|
|
311
|
+
windows_flush_console_input_buffer
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
# rawモードを解除
|
|
315
|
+
if STDIN.tty?
|
|
316
|
+
STDIN.cooked!
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
print "\e[?25h" # cursor visible
|
|
320
|
+
print "\e[?1049l" # restore normal screen buffer
|
|
321
|
+
STDOUT.flush
|
|
256
322
|
puts ConfigLoader.message('app.terminated')
|
|
257
323
|
end
|
|
258
324
|
|
|
325
|
+
def windows_flush_console_input_buffer
|
|
326
|
+
require 'fiddle'
|
|
327
|
+
kernel32 = Fiddle.dlopen('kernel32')
|
|
328
|
+
get_std_handle = Fiddle::Function.new(
|
|
329
|
+
kernel32['GetStdHandle'],
|
|
330
|
+
[Fiddle::TYPE_LONG],
|
|
331
|
+
Fiddle::TYPE_VOIDP
|
|
332
|
+
)
|
|
333
|
+
flush = Fiddle::Function.new(
|
|
334
|
+
kernel32['FlushConsoleInputBuffer'],
|
|
335
|
+
[Fiddle::TYPE_VOIDP],
|
|
336
|
+
Fiddle::TYPE_INT
|
|
337
|
+
)
|
|
338
|
+
handle = get_std_handle.call(-10) # STD_INPUT_HANDLE = -10
|
|
339
|
+
flush.call(handle)
|
|
340
|
+
rescue
|
|
341
|
+
# Windows API が使えない場合は無視して続行
|
|
342
|
+
end
|
|
343
|
+
|
|
259
344
|
# ゲームループパターンのmain_loop(CPU最適化版:フレームスキップ対応)
|
|
260
345
|
# UPDATE → DRAW → RENDER → SLEEP のサイクル
|
|
261
346
|
# 変更がない場合は描画をスキップしてCPU使用率を削減
|
|
@@ -369,9 +454,9 @@ module Rufio
|
|
|
369
454
|
needs_redraw = true
|
|
370
455
|
end
|
|
371
456
|
|
|
372
|
-
#
|
|
373
|
-
if @highlight_updated
|
|
374
|
-
@
|
|
457
|
+
# 非同期シンタックスハイライト完了チェック(UIRenderer側のフラグを参照)
|
|
458
|
+
if @ui_renderer.highlight_updated?
|
|
459
|
+
@ui_renderer.reset_highlight_updated
|
|
375
460
|
needs_redraw = true
|
|
376
461
|
end
|
|
377
462
|
|
|
@@ -432,25 +517,28 @@ module Rufio
|
|
|
432
517
|
private
|
|
433
518
|
|
|
434
519
|
# ノンブロッキング入力処理(ゲームループ用)
|
|
435
|
-
# Windows:
|
|
436
|
-
#
|
|
437
|
-
# 1ms の正のタイムアウトを使う。コンソールハンドルでも ConPTY パイプでも動作する。
|
|
520
|
+
# Windows: ブロッキング入力スレッドのキューからノンブロッキングで取得
|
|
521
|
+
# (IO.select の ConPTY シグナル遅延問題を回避)
|
|
438
522
|
# Unix: IO.select(timeout=0) で入力確認後 read_nonblock
|
|
439
523
|
def handle_input_nonblocking
|
|
440
|
-
# 入力バイトを1つ読み取る
|
|
441
524
|
if windows?
|
|
442
|
-
# Windows:
|
|
443
|
-
|
|
525
|
+
# Windows: ブロッキング入力スレッドが積んだキューから取得
|
|
526
|
+
raw_byte = begin
|
|
527
|
+
@input_queue.pop(true)
|
|
528
|
+
rescue ThreadError
|
|
529
|
+
return false
|
|
530
|
+
end
|
|
531
|
+
input = windows_build_char(raw_byte)
|
|
532
|
+
return false unless input
|
|
444
533
|
else
|
|
445
534
|
# Unix: 0msタイムアウトで即座にチェック(30FPS = 33.33ms/frame)
|
|
446
535
|
return false unless IO.select([STDIN], nil, nil, 0)
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
return false
|
|
536
|
+
begin
|
|
537
|
+
input = @multibyte_reader.read_char
|
|
538
|
+
return false if input.nil?
|
|
539
|
+
rescue Errno::ENOTTY, Errno::ENODEV
|
|
540
|
+
return false
|
|
541
|
+
end
|
|
454
542
|
end
|
|
455
543
|
|
|
456
544
|
# コマンドモードがアクティブな場合は、エスケープシーケンス処理をスキップ
|
|
@@ -492,15 +580,19 @@ module Rufio
|
|
|
492
580
|
when 'C' then 'l' # Right arrow
|
|
493
581
|
when 'D' then 'h' # Left arrow
|
|
494
582
|
when 'Z' then handle_shift_tab; return true # Shift+Tab
|
|
495
|
-
else
|
|
583
|
+
else
|
|
584
|
+
# 未知の CSI シーケンス(\e[27;1u 等): 残りバイトを読み捨てて
|
|
585
|
+
# パイプを汚染しないようにする
|
|
586
|
+
drain_csi_sequence(third_char)
|
|
587
|
+
"\e"
|
|
496
588
|
end
|
|
497
589
|
else
|
|
498
590
|
input = "\e" # ESCキー(そのまま保持)
|
|
499
591
|
end
|
|
500
592
|
end
|
|
501
593
|
|
|
502
|
-
# TabキーはFiles
|
|
503
|
-
if input == "\t" && @tab_mode_manager.current_mode == :files
|
|
594
|
+
# TabキーはFilesモードの時のみブックマーク循環移動(ジョブモード中は無効)
|
|
595
|
+
if input == "\t" && @tab_mode_manager.current_mode == :files && !@in_job_mode
|
|
504
596
|
handle_tab_key
|
|
505
597
|
return true
|
|
506
598
|
end
|
|
@@ -703,6 +795,8 @@ module Rufio
|
|
|
703
795
|
def activate_command_mode
|
|
704
796
|
@command_mode_active = true
|
|
705
797
|
@command_input = ""
|
|
798
|
+
print "\e[?25h" # カーソルを表示(テキスト入力中)
|
|
799
|
+
STDOUT.flush
|
|
706
800
|
# 閲覧中ディレクトリをコマンドモードに通知(ローカルスクリプト・Rakefileの検出用)
|
|
707
801
|
browsing_dir = @directory_listing&.current_path || Dir.pwd
|
|
708
802
|
@command_mode.update_browsing_directory(browsing_dir)
|
|
@@ -712,6 +806,8 @@ module Rufio
|
|
|
712
806
|
def deactivate_command_mode
|
|
713
807
|
@command_mode_active = false
|
|
714
808
|
@command_input = ""
|
|
809
|
+
print "\e[?25l" # カーソルを非表示(通常のファイラー表示に戻る)
|
|
810
|
+
STDOUT.flush
|
|
715
811
|
# オーバーレイをクリア
|
|
716
812
|
@screen&.clear_overlay if @screen&.overlay_enabled?
|
|
717
813
|
end
|
|
@@ -923,8 +1019,7 @@ module Rufio
|
|
|
923
1019
|
@job_manager = job_manager
|
|
924
1020
|
@notification_manager = notification_manager
|
|
925
1021
|
@in_job_mode = true
|
|
926
|
-
#
|
|
927
|
-
print "\e[2J\e[H"
|
|
1022
|
+
# レンダラーをリセット(print "\e[2J\e[H" は renderer.clear に含まれる)
|
|
928
1023
|
@renderer.clear if @renderer
|
|
929
1024
|
# 再描画フラグを立てる
|
|
930
1025
|
@job_mode_needs_redraw = true
|
|
@@ -937,7 +1032,6 @@ module Rufio
|
|
|
937
1032
|
@job_manager = nil
|
|
938
1033
|
# バッファベースの全画面再描画を使用
|
|
939
1034
|
update_screen_size
|
|
940
|
-
print "\e[2J\e[H"
|
|
941
1035
|
if @screen && @renderer
|
|
942
1036
|
@renderer.clear
|
|
943
1037
|
@screen.clear
|
|
@@ -1135,6 +1229,16 @@ module Rufio
|
|
|
1135
1229
|
})
|
|
1136
1230
|
end
|
|
1137
1231
|
|
|
1232
|
+
# 未知の CSI シーケンス(\e[X...)の残りバイトを終端アルファベットまで読み捨てる。
|
|
1233
|
+
# Windows Terminal が \e[27;1u 等を送った場合のパイプ汚染を防ぐ。
|
|
1234
|
+
def drain_csi_sequence(first_char)
|
|
1235
|
+
return if first_char.nil? || first_char =~ /[A-Za-z]/
|
|
1236
|
+
10.times do
|
|
1237
|
+
ch = read_next_input_byte
|
|
1238
|
+
break if ch.nil? || ch =~ /[A-Za-z]/
|
|
1239
|
+
end
|
|
1240
|
+
end
|
|
1241
|
+
|
|
1138
1242
|
end
|
|
1139
1243
|
end
|
|
1140
1244
|
|
data/lib/rufio/ui_renderer.rb
CHANGED
|
@@ -128,7 +128,8 @@ module Rufio
|
|
|
128
128
|
|
|
129
129
|
if in_job_mode
|
|
130
130
|
# ジョブモード: フッタ y=0(上部)、コンテンツ y=1〜h-2、統合行 y=h-1(下部)
|
|
131
|
-
|
|
131
|
+
log_mode = job_mode_instance&.log_mode? || false
|
|
132
|
+
draw_job_footer_to_buffer(screen, 0, job_manager, log_mode: log_mode)
|
|
132
133
|
draw_job_list_to_buffer(screen, content_height, job_manager, job_mode_instance)
|
|
133
134
|
draw_mode_tabs_to_buffer(screen, @screen_height - 1)
|
|
134
135
|
else
|
|
@@ -659,6 +660,12 @@ module Rufio
|
|
|
659
660
|
def draw_job_list_to_buffer(screen, height, job_manager, job_mode_instance)
|
|
660
661
|
return unless job_manager
|
|
661
662
|
|
|
663
|
+
# ログモード中は選択ジョブのログを表示
|
|
664
|
+
if job_mode_instance&.log_mode?
|
|
665
|
+
draw_job_log_to_buffer(screen, height, job_mode_instance.selected_job)
|
|
666
|
+
return
|
|
667
|
+
end
|
|
668
|
+
|
|
662
669
|
jobs = job_manager.jobs
|
|
663
670
|
selected_index = job_mode_instance&.selected_index || 0
|
|
664
671
|
|
|
@@ -674,6 +681,24 @@ module Rufio
|
|
|
674
681
|
end
|
|
675
682
|
end
|
|
676
683
|
|
|
684
|
+
def draw_job_log_to_buffer(screen, height, job)
|
|
685
|
+
unless job
|
|
686
|
+
screen.put_string(0, CONTENT_START_LINE, 'No job selected'.ljust(@screen_width), fg: "\e[90m")
|
|
687
|
+
return
|
|
688
|
+
end
|
|
689
|
+
|
|
690
|
+
log_lines = job.logs || []
|
|
691
|
+
title = "=== Log: #{job.name} ==="
|
|
692
|
+
screen.put_string(0, CONTENT_START_LINE, title.ljust(@screen_width), fg: "\e[1;36m")
|
|
693
|
+
|
|
694
|
+
(0...height - 1).each do |i|
|
|
695
|
+
line_num = i + CONTENT_START_LINE + 1
|
|
696
|
+
line = log_lines[i] || ''
|
|
697
|
+
line = line[0...@screen_width].ljust(@screen_width)
|
|
698
|
+
screen.put_string(0, line_num, line, fg: "\e[37m")
|
|
699
|
+
end
|
|
700
|
+
end
|
|
701
|
+
|
|
677
702
|
def draw_job_line_to_buffer(screen, job, is_selected, y)
|
|
678
703
|
icon = job.status_icon
|
|
679
704
|
name = job.name
|
|
@@ -712,9 +737,13 @@ module Rufio
|
|
|
712
737
|
end
|
|
713
738
|
end
|
|
714
739
|
|
|
715
|
-
def draw_job_footer_to_buffer(screen, y, job_manager)
|
|
740
|
+
def draw_job_footer_to_buffer(screen, y, job_manager, log_mode: false)
|
|
716
741
|
job_count = job_manager&.job_count || 0
|
|
717
|
-
help_text =
|
|
742
|
+
help_text = if log_mode
|
|
743
|
+
"[ESC] Close Log | Jobs: #{job_count}"
|
|
744
|
+
else
|
|
745
|
+
"[Space] View Log | [x] Cancel | Jobs: #{job_count}"
|
|
746
|
+
end
|
|
718
747
|
footer_content = help_text.center(@screen_width)[0...@screen_width]
|
|
719
748
|
|
|
720
749
|
footer_content.each_char.with_index do |char, x|
|
data/lib/rufio/version.rb
CHANGED
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.
|
|
4
|
+
version: 1.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- masisz
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-06-14 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: io-console
|