rufio 1.0.2 → 1.0.4
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 +12 -20
- data/README_ja.md +12 -22
- data/SECURITY.md +95 -0
- data/lib/rufio/command_logger.rb +2 -1
- data/lib/rufio/file_opener.rb +42 -26
- data/lib/rufio/help_menu.rb +155 -0
- data/lib/rufio/keybind_handler.rb +11 -1
- data/lib/rufio/search_controller.rb +2 -1
- data/lib/rufio/terminal_ui.rb +54 -64
- data/lib/rufio/ui_renderer.rb +5 -1
- data/lib/rufio/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 431baab2112b7382ee6b9aeb19a17f7cbc0133adc8d76e349ac614827908234b
|
|
4
|
+
data.tar.gz: ffe6479dc44a44869dbd56a67af086cfda84164cacb6b3b8334e0d691f24a706
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 92038826f2b9ba53ba7c2e329c835ba92f99e9678151805faadbdc23f32f7fe6b4bcb4cb8394855ef1ddb01a96ce1abc4ee12ffbf0f1b71672b2148ba7692daa
|
|
7
|
+
data.tar.gz: e7dfbeba635500c59b6fbd2c3b17efdd4b17c24c6bcac9af7d95bb6ebc7ef38b59ce55be6206dd42ceb49b2531728a21cd8c1876a44189371be6f3621ac7c2fc
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,32 @@ 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.4] - 2026-10-25
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- Replace directory-based Help with a floating menu containing Controls, Modes, and Scripts sections. Switch sections with 1/2/3 or Tab, scroll with j/k, and close with ESC while preserving the current mode, directory, and preview focus
|
|
12
|
+
- Show mode shortcuts as `[L] Logs`, `[J] Jobs`, and `[?] Help` in the bottom bar
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
- Show `ESC: Back to files` below the file name in Preview mode
|
|
16
|
+
- Preserve the original return directory when entering Logs repeatedly and prevent overlapping Help and Logs states
|
|
17
|
+
- Keep mouse interactions inside the floating Help window from affecting the underlying file list
|
|
18
|
+
|
|
19
|
+
## [1.0.3] - 2026-10-23
|
|
20
|
+
|
|
21
|
+
### Security
|
|
22
|
+
- **Fix shell command injection via file names**: `FileOpener` interpolated file paths into shell strings and only quoted them when they contained spaces or quotes, so opening a file named like `a;touch pwned` or containing `$(...)` / backticks could execute arbitrary commands. Commands are now executed in array form without a shell (`system(*argv)`), paths starting with `-` are prefixed with `./`, and the Windows default-app launch uses `rundll32 url.dll,FileProtocolHandler` instead of `cmd /c start` (avoids `%VAR%` / `&` expansion). Removed `quote_shell_argument`
|
|
23
|
+
- **rga option injection**: A `--` separator is now passed before the search query so queries such as `--pre=cmd` are not interpreted as rga options
|
|
24
|
+
- **Command logs are now written with `0600` permissions**, since command output may contain secrets
|
|
25
|
+
|
|
26
|
+
### Removed
|
|
27
|
+
- `TestUISnapshot` and its `test/snapshots/` fixtures (tests were failing independent of code changes)
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
- **Windows: Sluggish key handling**: The main loop processed only one input byte per frame and then slept ~33ms, so key repeat could not keep up. Now up to 32 queued inputs are processed per frame with a single redraw, and the Windows frame interval is ~8ms
|
|
31
|
+
- **Windows: Input queue wait**: Uses `Queue#pop(timeout:)` on Ruby 3.2+ instead of 1ms `sleep` polling (inaccurate under the ~15ms Windows timer resolution)
|
|
32
|
+
- **Windows: Arrow keys misread as ESC**: The wait for escape sequence follow-up bytes was extended from 5ms to 25ms to tolerate ConPTY byte delays
|
|
33
|
+
|
|
8
34
|
## [1.0.0] - 2026-04-28
|
|
9
35
|
|
|
10
36
|
### Added
|
data/README.md
CHANGED
|
@@ -2,34 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
**Runtime Unified Flow I/O Operator**
|
|
4
4
|
|
|
5
|
-
A
|
|
6
|
-
|
|
5
|
+
A terminal file manager that keeps your everyday script workflow close at hand.
|
|
6
|
+
Find files, run Ruby, Python, or PowerShell scripts in the current directory, and check jobs and logs.
|
|
7
|
+
Add your own actions in Ruby.
|
|
7
8
|
|
|
8
9
|
[日本語](./README_ja.md) | **English**
|
|
9
10
|
|
|
10
|
-
##
|
|
11
|
+
## What Makes rufio Different
|
|
11
12
|
|
|
12
|
-
rufio
|
|
13
|
+
rufio brings finding files, running scripts, and checking results together in one interface.
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
│ ↑ │ │
|
|
21
|
-
│ └───────────── Feedback ────────────┘ │
|
|
22
|
-
└─────────────────────────────────────────────────────────┘
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
- **File Operations**: Traditional file manager functionality
|
|
26
|
-
- **Script Execution**: Run Ruby/Python/PowerShell scripts in file context
|
|
27
|
-
- **Tool Integration**: Seamless integration with external tools (git, fzf, rga, etc.)
|
|
28
|
-
- **Unified I/O**: Manage all input/output in a single flow
|
|
15
|
+
- **Run scripts where your files are**: Register your script directories, then press `:`, type `@` + part of a script name, press `Tab`, and hit `Enter`. Run Ruby, Python, or PowerShell scripts in the directory you are browsing
|
|
16
|
+
- **Follow jobs and execution logs**: Keep browsing while scripts run in the background. Press `J` to check jobs and `L` to view execution logs
|
|
17
|
+
- **Add your own actions in Ruby**: Define custom commands with a small Ruby DSL in `~/.config/rufio/commands.rb`. `~/.rufio/commands.rb` is also supported
|
|
18
|
+
- **Use installed tools without extra configuration**: Search file names with fzf, search contents with rga, highlight previews with bat, and navigate directory history with zoxide
|
|
19
|
+
- **Windows support**: Run PowerShell scripts with keyboard input handling adapted to Windows terminals
|
|
20
|
+
- **Trace behavior in the source**: The implementation is primarily Ruby, so you can inspect and change the parts you need. Security design is documented in [SECURITY.md](./SECURITY.md)
|
|
29
21
|
|
|
30
22
|
## Features
|
|
31
23
|
|
|
32
|
-
###
|
|
24
|
+
### For Script Workflows
|
|
33
25
|
|
|
34
26
|
- **Multi-language Script Support**: Ruby, Python, PowerShell
|
|
35
27
|
- **Script Path Management**: Register and manage multiple script directories
|
data/README_ja.md
CHANGED
|
@@ -2,36 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
**Runtime Unified Flow I/O Operator**
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
Ruby
|
|
7
|
-
|
|
8
|
-
A TUI file manager as a unified runtime environment for tools and scripts.
|
|
5
|
+
日々のスクリプト作業を手元にまとめるターミナルファイラ。
|
|
6
|
+
ファイルを探し、その場所で Ruby・Python・PowerShell を実行し、ジョブとログを確認できます。
|
|
7
|
+
自分用の操作は Ruby で追加できます。
|
|
9
8
|
|
|
10
9
|
**日本語** | [English](./README.md)
|
|
11
10
|
|
|
12
|
-
##
|
|
13
|
-
|
|
14
|
-
rufioは単なるファイルマネージャーではありません。**ツールランタイム実行環境**です。
|
|
11
|
+
## rufioならではの点
|
|
15
12
|
|
|
16
|
-
|
|
17
|
-
┌─────────────────────────────────────────────────────────┐
|
|
18
|
-
│ rufio │
|
|
19
|
-
│ Runtime Unified Flow I/O Operator │
|
|
20
|
-
├─────────────────────────────────────────────────────────┤
|
|
21
|
-
│ Files ──→ Scripts ──→ Tools ──→ Output │
|
|
22
|
-
│ ↑ │ │
|
|
23
|
-
│ └───────────── Feedback ────────────┘ │
|
|
24
|
-
└─────────────────────────────────────────────────────────┘
|
|
25
|
-
```
|
|
13
|
+
rufio は、ファイルを探すところからスクリプトの実行、結果の確認までを一つの画面にまとめます。
|
|
26
14
|
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
-
|
|
15
|
+
- **ファイルのある場所からスクリプトを実行**: スクリプトのディレクトリを登録し、`:` → `@` + スクリプト名の一部 → `Tab` → `Enter` で呼び出せます。Ruby・Python・PowerShell を、いま開いているディレクトリで実行できます
|
|
16
|
+
- **ジョブとログで実行結果を追える**: バックグラウンドで実行しながらファイル操作を続け、`J` でジョブ、`L` で実行ログを確認できます
|
|
17
|
+
- **自分用の操作を Ruby で追加**: `~/.config/rufio/commands.rb` に小さな Ruby DSL で独自コマンドを定義できます。`~/.rufio/commands.rb` にも対応しています
|
|
18
|
+
- **インストール済みのツールと追加設定なしで連携**: fzf でファイル名検索、rga で内容検索、bat でプレビューのシンタックスハイライト、zoxide でディレクトリ履歴から移動できます
|
|
19
|
+
- **Windows にも対応**: PowerShell スクリプトの実行に対応し、Windows のターミナルに合わせたキー入力処理を備えています
|
|
20
|
+
- **挙動をソースで追いやすい**: Ruby を中心とした実装で、必要な処理を自分で確認・修正できます。セキュリティに関する設計は [SECURITY.md](./SECURITY.md) にまとめています
|
|
31
21
|
|
|
32
22
|
## 特徴
|
|
33
23
|
|
|
34
|
-
###
|
|
24
|
+
### スクリプト作業のために
|
|
35
25
|
|
|
36
26
|
- **マルチ言語スクリプト対応**: Ruby, Python, PowerShell
|
|
37
27
|
- **スクリプトパス管理**: 複数のスクリプトディレクトリを登録・管理
|
data/SECURITY.md
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Security Policy / セキュリティポリシー
|
|
2
|
+
|
|
3
|
+
[English](#english) | [日本語](#日本語)
|
|
4
|
+
|
|
5
|
+
## English
|
|
6
|
+
|
|
7
|
+
### Supported versions
|
|
8
|
+
|
|
9
|
+
Only the latest released version receives security fixes. Please upgrade with `gem update rufio` before reporting.
|
|
10
|
+
|
|
11
|
+
### Reporting a vulnerability
|
|
12
|
+
|
|
13
|
+
Please **do not open a public issue** for security problems.
|
|
14
|
+
|
|
15
|
+
- Use GitHub's private reporting: [Security → Report a vulnerability](https://github.com/masisz/rufio/security/advisories/new)
|
|
16
|
+
- Or email masisz.1567@gmail.com
|
|
17
|
+
|
|
18
|
+
Include the rufio version, OS, steps to reproduce, and the impact you expect. This is a personal project, so responses are best-effort, but reports are taken seriously and fixes are noted in [CHANGELOG.md](./CHANGELOG.md) under `Security`.
|
|
19
|
+
|
|
20
|
+
### Threat model
|
|
21
|
+
|
|
22
|
+
rufio is a local TUI tool run by you, on files you can already access.
|
|
23
|
+
|
|
24
|
+
**In scope** (treated as vulnerabilities):
|
|
25
|
+
- Names or contents of files, directories, archives, or tool output causing unintended command execution (e.g. shell metacharacters in file names)
|
|
26
|
+
- Escape sequences in file contents or names that manipulate the terminal in harmful ways
|
|
27
|
+
- Unsafe handling of paths that lets an operation affect files outside what the user selected
|
|
28
|
+
- Secrets exposed through logs or temporary files
|
|
29
|
+
|
|
30
|
+
**Out of scope** (by design):
|
|
31
|
+
- Commands you type yourself in command mode (`:`, `!`) — they are executed as shell commands
|
|
32
|
+
- Code in your own configuration: `~/.rufio/commands.rb` and `~/.config/rufio/commands.rb` are **evaluated as Ruby**. Keep them writable only by you
|
|
33
|
+
- Scripts you register under script paths — they run with your privileges
|
|
34
|
+
- Vulnerabilities in external tools (fzf, rga, bat, zoxide, editors)
|
|
35
|
+
|
|
36
|
+
### Design practices
|
|
37
|
+
|
|
38
|
+
- Files and scripts are launched in array form (`system(*argv)`), never through a shell string; paths beginning with `-` are prefixed with `./`
|
|
39
|
+
- Search queries are passed to rga after `--`
|
|
40
|
+
- YAML is loaded with `YAML.safe_load`
|
|
41
|
+
- Command execution logs are written with `0600` permissions
|
|
42
|
+
- Runtime dependencies are limited to `io-console`, `pastel`, `tty-cursor`, `tty-screen`
|
|
43
|
+
- Gems are published from tagged commits via RubyGems Trusted Publishing (no long-lived API key); CI runs the test suite before publishing
|
|
44
|
+
|
|
45
|
+
### Known limitations
|
|
46
|
+
|
|
47
|
+
- Text previews only detect binary files (by control-character ratio); terminal escape sequences inside otherwise-textual files are not explicitly stripped. Avoid previewing untrusted files in a terminal you do not trust
|
|
48
|
+
- The optional native extension under `lib_zig` is not shipped in the gem and has not been independently audited
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 日本語
|
|
53
|
+
|
|
54
|
+
### サポート対象バージョン
|
|
55
|
+
|
|
56
|
+
セキュリティ修正は最新のリリースにのみ行います。報告の前に `gem update rufio` で更新してください。
|
|
57
|
+
|
|
58
|
+
### 脆弱性の報告
|
|
59
|
+
|
|
60
|
+
セキュリティ上の問題は**公開Issueに書かない**でください。
|
|
61
|
+
|
|
62
|
+
- GitHubの非公開報告: [Security → Report a vulnerability](https://github.com/masisz/rufio/security/advisories/new)
|
|
63
|
+
- またはメール: masisz.1567@gmail.com
|
|
64
|
+
|
|
65
|
+
rufioのバージョン、OS、再現手順、想定される影響を添えてください。個人プロジェクトのため対応はベストエフォートですが、報告は真剣に扱い、修正は [CHANGELOG.md](./CHANGELOG.md) の `Security` に記載します。
|
|
66
|
+
|
|
67
|
+
### 脅威モデル
|
|
68
|
+
|
|
69
|
+
rufioは、あなた自身がアクセスできるファイルに対して、ローカルで実行するTUIツールです。
|
|
70
|
+
|
|
71
|
+
**対象(脆弱性として扱う)**:
|
|
72
|
+
- ファイル名・ディレクトリ名・アーカイブ・ツール出力の内容によって、意図しないコマンドが実行されること(例: ファイル名中のシェルメタ文字)
|
|
73
|
+
- ファイル内容や名前に含まれるエスケープシーケンスによる、有害なターミナル操作
|
|
74
|
+
- パスの扱いの不備により、ユーザーが選択した範囲外のファイルに操作が及ぶこと
|
|
75
|
+
- ログや一時ファイルを通じた機密情報の漏えい
|
|
76
|
+
|
|
77
|
+
**対象外(設計上の仕様)**:
|
|
78
|
+
- コマンドモード(`:`、`!`)で自分で入力したコマンド — シェルコマンドとして実行されます
|
|
79
|
+
- 自分の設定ファイル内のコード — `~/.rufio/commands.rb` と `~/.config/rufio/commands.rb` は**Rubyとして評価**されます。書き込み権限は自分だけにしてください
|
|
80
|
+
- スクリプトパスに登録したスクリプト — あなたの権限で実行されます
|
|
81
|
+
- 外部ツール(fzf、rga、bat、zoxide、エディタ)自体の脆弱性
|
|
82
|
+
|
|
83
|
+
### 設計上の方針
|
|
84
|
+
|
|
85
|
+
- ファイルやスクリプトは配列形式(`system(*argv)`)で起動し、シェル文字列を介しません。`-` で始まるパスには `./` を付与します
|
|
86
|
+
- rga には `--` を挟んで検索語を渡します
|
|
87
|
+
- YAML は `YAML.safe_load` で読み込みます
|
|
88
|
+
- コマンド実行ログは `0600` で保存します
|
|
89
|
+
- 実行時依存gemは `io-console`、`pastel`、`tty-cursor`、`tty-screen` のみです
|
|
90
|
+
- gemはタグ付きコミットからRubyGemsのTrusted Publishingで公開します(長期APIキーなし)。公開前にCIでテストを実行します
|
|
91
|
+
|
|
92
|
+
### 既知の制約
|
|
93
|
+
|
|
94
|
+
- テキストのプレビューはバイナリ判定(制御文字の比率)のみで、テキストとして扱われるファイル内のエスケープシーケンスは明示的に除去していません。信頼できないファイルを、信頼できないターミナルでプレビューするのは避けてください
|
|
95
|
+
- `lib_zig` のネイティブ拡張(任意機能)はgemに同梱しておらず、第三者による監査も受けていません
|
data/lib/rufio/command_logger.rb
CHANGED
|
@@ -30,7 +30,8 @@ module Rufio
|
|
|
30
30
|
# ディレクトリが存在しない場合は作成(バックグラウンドスレッドでの実行時の競合を防ぐ)
|
|
31
31
|
FileUtils.mkdir_p(@log_dir) unless Dir.exist?(@log_dir)
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
# コマンド出力に機密情報が含まれ得るため、所有者のみ読み書き可能にする
|
|
34
|
+
File.write(filepath, content, perm: 0o600)
|
|
34
35
|
end
|
|
35
36
|
|
|
36
37
|
# ログファイル一覧を取得(新しい順)
|
data/lib/rufio/file_opener.rb
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'shellwords'
|
|
4
|
+
|
|
3
5
|
module Rufio
|
|
4
6
|
class FileOpener
|
|
5
7
|
def initialize
|
|
@@ -43,26 +45,26 @@ module Rufio
|
|
|
43
45
|
end
|
|
44
46
|
|
|
45
47
|
def execute_command(application, file_path)
|
|
46
|
-
|
|
48
|
+
path = safe_path(file_path)
|
|
47
49
|
|
|
48
50
|
case RbConfig::CONFIG['host_os']
|
|
49
51
|
when /mswin|mingw|cygwin/
|
|
50
|
-
# Windows
|
|
51
|
-
|
|
52
|
+
# Windows: cmd.exe を経由せず(%VAR% や & の展開を避ける)既定アプリで開く
|
|
53
|
+
run_command(['rundll32', 'url.dll,FileProtocolHandler', path])
|
|
52
54
|
when /darwin/
|
|
53
55
|
# macOS
|
|
54
56
|
if application == 'open'
|
|
55
|
-
|
|
57
|
+
run_command(['open', path])
|
|
56
58
|
else
|
|
57
59
|
# VSCodeなど特定のアプリケーション
|
|
58
|
-
|
|
60
|
+
run_command([*application_argv(application), path])
|
|
59
61
|
end
|
|
60
62
|
else
|
|
61
63
|
# Linux/Unix
|
|
62
64
|
if application == 'open'
|
|
63
|
-
|
|
65
|
+
run_command(['xdg-open', path])
|
|
64
66
|
else
|
|
65
|
-
|
|
67
|
+
run_command([*application_argv(application), path])
|
|
66
68
|
end
|
|
67
69
|
end
|
|
68
70
|
rescue StandardError => e
|
|
@@ -71,37 +73,39 @@ module Rufio
|
|
|
71
73
|
end
|
|
72
74
|
|
|
73
75
|
def execute_command_with_line(application, file_path, line_number)
|
|
74
|
-
|
|
76
|
+
path = safe_path(file_path)
|
|
77
|
+
line = line_number.to_i
|
|
78
|
+
app = application_argv(application)
|
|
75
79
|
|
|
76
80
|
case RbConfig::CONFIG['host_os']
|
|
77
81
|
when /mswin|mingw|cygwin/
|
|
78
82
|
# Windows
|
|
79
83
|
if application.include?('code')
|
|
80
|
-
|
|
84
|
+
run_command([*app, '--goto', "#{path}:#{line}"])
|
|
81
85
|
else
|
|
82
|
-
|
|
86
|
+
run_command(['rundll32', 'url.dll,FileProtocolHandler', path])
|
|
83
87
|
end
|
|
84
88
|
when /darwin/
|
|
85
89
|
# macOS
|
|
86
90
|
if application == 'open'
|
|
87
|
-
|
|
91
|
+
run_command(['open', path])
|
|
88
92
|
elsif application.include?('code')
|
|
89
|
-
|
|
93
|
+
run_command([*app, '--goto', "#{path}:#{line}"])
|
|
90
94
|
elsif application.include?('vim') || application.include?('nvim')
|
|
91
|
-
|
|
95
|
+
run_command([*app, "+#{line}", path])
|
|
92
96
|
else
|
|
93
|
-
|
|
97
|
+
run_command([*app, path])
|
|
94
98
|
end
|
|
95
99
|
else
|
|
96
100
|
# Linux/Unix
|
|
97
101
|
if application == 'open'
|
|
98
|
-
|
|
102
|
+
run_command(['xdg-open', path])
|
|
99
103
|
elsif application.include?('code')
|
|
100
|
-
|
|
104
|
+
run_command([*app, '--goto', "#{path}:#{line}"])
|
|
101
105
|
elsif application.include?('vim') || application.include?('nvim')
|
|
102
|
-
|
|
106
|
+
run_command([*app, "+#{line}", path])
|
|
103
107
|
else
|
|
104
|
-
|
|
108
|
+
run_command([*app, path])
|
|
105
109
|
end
|
|
106
110
|
end
|
|
107
111
|
rescue StandardError => e
|
|
@@ -109,27 +113,39 @@ module Rufio
|
|
|
109
113
|
false
|
|
110
114
|
end
|
|
111
115
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
116
|
+
# シェルを介さず配列形式で実行する(ファイル名経由のコマンドインジェクション防止)
|
|
117
|
+
def run_command(argv)
|
|
118
|
+
system(*argv)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# 設定のアプリケーション指定(例: "code -r")を引数配列に分解する
|
|
122
|
+
def application_argv(application)
|
|
123
|
+
if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/
|
|
124
|
+
# Windows のパスはバックスラッシュを含むため POSIX 分割は使わない
|
|
125
|
+
File.exist?(application) ? [application] : application.split
|
|
115
126
|
else
|
|
116
|
-
|
|
127
|
+
Shellwords.split(application)
|
|
117
128
|
end
|
|
118
129
|
end
|
|
119
130
|
|
|
131
|
+
# '-' で始まるパスがオプションとして解釈されないようにする
|
|
132
|
+
def safe_path(path)
|
|
133
|
+
path.start_with?('-') ? File.join('.', path) : path
|
|
134
|
+
end
|
|
135
|
+
|
|
120
136
|
def execute_explorer_command(directory_path)
|
|
121
|
-
|
|
137
|
+
path = safe_path(directory_path)
|
|
122
138
|
|
|
123
139
|
case RbConfig::CONFIG['host_os']
|
|
124
140
|
when /mswin|mingw|cygwin/
|
|
125
141
|
# Windows
|
|
126
|
-
|
|
142
|
+
run_command(['explorer', path])
|
|
127
143
|
when /darwin/
|
|
128
144
|
# macOS
|
|
129
|
-
|
|
145
|
+
run_command(['open', path])
|
|
130
146
|
else
|
|
131
147
|
# Linux/Unix
|
|
132
|
-
|
|
148
|
+
run_command(['xdg-open', path])
|
|
133
149
|
end
|
|
134
150
|
rescue StandardError => e
|
|
135
151
|
warn "Failed to open directory: #{e.message}"
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'text_utils'
|
|
4
|
+
require_relative 'dialog_renderer'
|
|
5
|
+
require_relative 'config_loader'
|
|
6
|
+
|
|
7
|
+
module Rufio
|
|
8
|
+
# メインループを止めずに表示するヘルプメニュー。
|
|
9
|
+
class HelpMenu
|
|
10
|
+
SECTIONS = %w[Controls Modes Scripts].freeze
|
|
11
|
+
attr_reader :scroll_offset
|
|
12
|
+
|
|
13
|
+
def initialize
|
|
14
|
+
@active = false
|
|
15
|
+
@section_index = 0
|
|
16
|
+
@scroll_offset = 0
|
|
17
|
+
@max_scroll = 0
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def open
|
|
21
|
+
@active = true
|
|
22
|
+
@section_index = 0
|
|
23
|
+
@scroll_offset = 0
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def active?
|
|
27
|
+
@active
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def section
|
|
31
|
+
SECTIONS[@section_index]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def handle_key(key)
|
|
35
|
+
case key
|
|
36
|
+
when "\e", 'q', '?' then @active = false
|
|
37
|
+
when '1', '2', '3' then select_section(key.to_i - 1)
|
|
38
|
+
when "\t", 'l' then select_section((@section_index + 1) % SECTIONS.length)
|
|
39
|
+
when 'h', "\e[Z" then select_section((@section_index - 1) % SECTIONS.length)
|
|
40
|
+
when 'j' then @scroll_offset = [@scroll_offset + 1, @max_scroll].min
|
|
41
|
+
when 'k' then @scroll_offset = [@scroll_offset - 1, 0].max
|
|
42
|
+
when "\x04" then @scroll_offset = [@scroll_offset + @page_size.to_i, @max_scroll].min
|
|
43
|
+
when "\x15" then @scroll_offset = [@scroll_offset - @page_size.to_i, 0].max
|
|
44
|
+
when 'g' then @scroll_offset = 0
|
|
45
|
+
when 'G' then @scroll_offset = @max_scroll
|
|
46
|
+
end
|
|
47
|
+
true
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def content
|
|
51
|
+
case section
|
|
52
|
+
when 'Controls'
|
|
53
|
+
[
|
|
54
|
+
'Navigation',
|
|
55
|
+
"#{key(:move_down)}/#{key(:move_up)} or arrows: Move down/up",
|
|
56
|
+
"#{key(:navigate_parent)}/#{key(:navigate_enter)}: Parent / enter directory",
|
|
57
|
+
'Enter: Open file preview | ESC: Back to file list',
|
|
58
|
+
'Preview: j/k scroll, Ctrl+D/U page down/up',
|
|
59
|
+
"#{key(:top)}/#{key(:bottom)}: First / last entry",
|
|
60
|
+
"#{key(:open_file)}: Open in external editor",
|
|
61
|
+
"#{key(:refresh)}: Refresh directory",
|
|
62
|
+
'', 'Selection and file operations',
|
|
63
|
+
"#{key(:select)}: Select / unselect file",
|
|
64
|
+
"#{key(:copy_selected)}/#{key(:move_selected)}/#{key(:delete_selected)}: Copy / move / delete selected files",
|
|
65
|
+
"#{key(:rename)}/#{key(:delete)}: Rename / delete current file",
|
|
66
|
+
"#{key(:create_file)}/#{key(:create_dir)}: Create file / directory",
|
|
67
|
+
'', 'Search and bookmarks',
|
|
68
|
+
"#{key(:filter)}: Filter | Enter: Apply | ESC: Clear",
|
|
69
|
+
"#{key(:fzf_search)}: Filename search (fzf)",
|
|
70
|
+
"#{key(:rga_search)}: Content search (rga)",
|
|
71
|
+
"#{key(:add_bookmark)}/#{key(:bookmark_menu)}: Add / manage bookmarks",
|
|
72
|
+
'Tab / Shift+Tab: Next / previous bookmark',
|
|
73
|
+
"1-9: Jump to bookmark | #{key(:start_dir)}: Start directory",
|
|
74
|
+
"#{key(:zoxide)}: Directory history (zoxide)",
|
|
75
|
+
"#{key(:quit)}: Quit (with confirmation)"
|
|
76
|
+
]
|
|
77
|
+
when 'Modes'
|
|
78
|
+
[
|
|
79
|
+
'Files', 'Browse directories, select files and view previews.',
|
|
80
|
+
'', "[#{key(:log_viewer)}] Logs",
|
|
81
|
+
'Browse command logs. Enter opens a preview.',
|
|
82
|
+
'ESC: Leave preview; ESC again: Return to Files.',
|
|
83
|
+
'', "[#{key(:job_mode)}] Jobs",
|
|
84
|
+
'View background commands and their status.',
|
|
85
|
+
'Space: View log | x: Cancel job | ESC: Back',
|
|
86
|
+
'', "[#{key(:help)}] Help",
|
|
87
|
+
'This floating menu keeps your current mode and directory.',
|
|
88
|
+
'1/2/3 or Tab: Select section | ESC: Close',
|
|
89
|
+
'', "[#{key(:command_mode)}] Command",
|
|
90
|
+
'Type a command, then Enter to run it.',
|
|
91
|
+
'Tab: Complete | Up/Down: History | ESC: Cancel',
|
|
92
|
+
':!git status: Run a shell command',
|
|
93
|
+
':@hello.rb: Run a script in the current directory'
|
|
94
|
+
]
|
|
95
|
+
when 'Scripts'
|
|
96
|
+
[
|
|
97
|
+
'1. Personal scripts',
|
|
98
|
+
'Place scripts in ~/.config/rufio/scripts/',
|
|
99
|
+
'Example: ~/.config/rufio/scripts/hello.rb',
|
|
100
|
+
'Press : and type hello, then Enter (:hello).',
|
|
101
|
+
'', '2. Additional script folders',
|
|
102
|
+
'List folders in ~/.config/rufio/script_paths.yml:',
|
|
103
|
+
'- ~/.config/rufio/scripts', '- ~/my-scripts',
|
|
104
|
+
'Folders are searched in order. First matching name wins.',
|
|
105
|
+
'', '3. Project scripts',
|
|
106
|
+
'Place hello.rb or hello.sh in the directory you browse.',
|
|
107
|
+
'Run it with :@hello.rb or :@hello.sh.',
|
|
108
|
+
'Project search paths: ./rufio.yml, script_paths list.',
|
|
109
|
+
'', '4. Run and monitor',
|
|
110
|
+
'Tab completes script names. Up/Down recalls commands.',
|
|
111
|
+
"#{key(:job_mode)}: Background jobs | #{key(:log_viewer)}: Execution logs",
|
|
112
|
+
'Supported: Ruby, shell, Python and other script types.'
|
|
113
|
+
]
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def draw(screen)
|
|
118
|
+
width = [screen.width, 78].min
|
|
119
|
+
height = [screen.height, 26].min
|
|
120
|
+
return if width < 8 || height < 8
|
|
121
|
+
|
|
122
|
+
@page_size = height - 7
|
|
123
|
+
lines = content.flat_map { |line| line.empty? ? [''] : TextUtils.wrap_preview_lines([line], width - 4) }
|
|
124
|
+
@max_scroll = [lines.length - @page_size, 0].max
|
|
125
|
+
@scroll_offset = [@scroll_offset, @max_scroll].min
|
|
126
|
+
tabs = SECTIONS.each_with_index.map do |name, index|
|
|
127
|
+
index == @section_index ? "[#{index + 1}:#{name}]" : " #{index + 1}:#{name} "
|
|
128
|
+
end.join(' ')
|
|
129
|
+
visible = [TextUtils.truncate_to_width(tabs, width - 2), '']
|
|
130
|
+
visible.concat(lines.slice(@scroll_offset, @page_size) || [])
|
|
131
|
+
visible << '' while visible.length < height - 5
|
|
132
|
+
visible << TextUtils.truncate_to_width('ESC: Close | Tab: Menu | j/k: Scroll', width - 2)
|
|
133
|
+
screen.enable_overlay
|
|
134
|
+
screen.clear_overlay
|
|
135
|
+
title = "Help - #{section} (#{@scroll_offset + 1}/#{@max_scroll + 1})"
|
|
136
|
+
DialogRenderer.new.draw_floating_window_to_overlay(
|
|
137
|
+
screen, (screen.width - width) / 2, (screen.height - height) / 2,
|
|
138
|
+
width, height, TextUtils.truncate_to_width(title, width - 2), visible,
|
|
139
|
+
border_color: "\e[36m", title_color: "\e[1;36m"
|
|
140
|
+
)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
private
|
|
144
|
+
|
|
145
|
+
def select_section(index)
|
|
146
|
+
@section_index = index
|
|
147
|
+
@scroll_offset = 0
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def key(action)
|
|
151
|
+
value = ConfigLoader.keybinds[action]
|
|
152
|
+
value == ' ' ? 'Space' : value.to_s
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
@@ -130,6 +130,10 @@ module Rufio
|
|
|
130
130
|
def handle_key(key)
|
|
131
131
|
return false unless @directory_listing
|
|
132
132
|
|
|
133
|
+
if @key_map[key] == :help && !@filter_manager.filter_mode
|
|
134
|
+
return show_help_dialog
|
|
135
|
+
end
|
|
136
|
+
|
|
133
137
|
# ジョブモード中の特別処理
|
|
134
138
|
if @job_mode.active?
|
|
135
139
|
return handle_job_mode_key(key)
|
|
@@ -217,6 +221,9 @@ module Rufio
|
|
|
217
221
|
# ヘルプモードに入る
|
|
218
222
|
def enter_help_mode
|
|
219
223
|
return false unless @directory_listing
|
|
224
|
+
return true if @in_help_mode
|
|
225
|
+
|
|
226
|
+
exit_log_viewer_mode if @in_log_viewer_mode
|
|
220
227
|
|
|
221
228
|
# 現在のディレクトリを保存
|
|
222
229
|
@pre_help_directory = @directory_listing.current_path
|
|
@@ -264,6 +271,9 @@ module Rufio
|
|
|
264
271
|
# ログビューワモードに入る
|
|
265
272
|
def enter_log_viewer_mode
|
|
266
273
|
return false unless @directory_listing
|
|
274
|
+
return true if @in_log_viewer_mode
|
|
275
|
+
|
|
276
|
+
exit_help_mode if @in_help_mode
|
|
267
277
|
|
|
268
278
|
# 現在のディレクトリを保存
|
|
269
279
|
@pre_log_viewer_directory = @directory_listing.current_path
|
|
@@ -488,7 +498,7 @@ module Rufio
|
|
|
488
498
|
when :zoxide then show_zoxide_menu
|
|
489
499
|
when :start_dir then goto_start_directory
|
|
490
500
|
when :job_mode then enter_job_mode
|
|
491
|
-
when :help then
|
|
501
|
+
when :help then show_help_dialog
|
|
492
502
|
when :log_viewer then enter_log_viewer_mode
|
|
493
503
|
when :command_mode then activate_command_mode
|
|
494
504
|
when :quit then exit_request
|
|
@@ -57,7 +57,8 @@ module Rufio
|
|
|
57
57
|
search_results = nil
|
|
58
58
|
Dir.chdir(current_path) do
|
|
59
59
|
escaped_query = Shellwords.escape(search_query)
|
|
60
|
-
|
|
60
|
+
# "--" で検索語がオプション(--pre=コマンド 等)として解釈されるのを防ぐ
|
|
61
|
+
search_results = `rga --line-number --with-filename -- #{escaped_query} . 2>/dev/null`
|
|
61
62
|
end
|
|
62
63
|
|
|
63
64
|
if search_results.empty?
|
data/lib/rufio/terminal_ui.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'io/console'
|
|
4
4
|
require_relative 'text_utils'
|
|
5
|
+
require_relative 'help_menu'
|
|
5
6
|
|
|
6
7
|
module Rufio
|
|
7
8
|
class TerminalUI
|
|
@@ -51,6 +52,7 @@ module Rufio
|
|
|
51
52
|
@input_queue = nil
|
|
52
53
|
@input_thread = nil
|
|
53
54
|
@command_mode_active = false
|
|
55
|
+
@help_menu = HelpMenu.new
|
|
54
56
|
@command_input = ""
|
|
55
57
|
@command_mode = CommandMode.new
|
|
56
58
|
@dialog_renderer = DialogRenderer.new
|
|
@@ -237,7 +239,8 @@ module Rufio
|
|
|
237
239
|
# Windows: IO.select を使わずキューから取得(5ms タイムアウト)。
|
|
238
240
|
def read_next_input_byte
|
|
239
241
|
if windows?
|
|
240
|
-
|
|
242
|
+
# ConPTY ではバイト到着が遅れることがあるため 5ms では ESC 単体と誤判定する
|
|
243
|
+
b = windows_queue_pop_with_timeout(0.025)
|
|
241
244
|
b.nil? ? nil : b.chr(Encoding::BINARY)
|
|
242
245
|
else
|
|
243
246
|
STDIN.read_nonblock(1) rescue nil
|
|
@@ -280,7 +283,15 @@ module Rufio
|
|
|
280
283
|
end
|
|
281
284
|
|
|
282
285
|
# Windows 専用: @input_queue からタイムアウト付きで Integer バイトを取り出す。
|
|
286
|
+
# Ruby 3.2+ は Queue#pop(timeout:) で正確に待機する(sleep ポーリングは
|
|
287
|
+
# Windows のタイマー分解能 約15ms により遅延する)。それ以前はポーリング。
|
|
288
|
+
QUEUE_POP_TIMEOUT_SUPPORTED = Queue.instance_method(:pop).parameters.any? { |_, n| n == :timeout }
|
|
289
|
+
|
|
283
290
|
def windows_queue_pop_with_timeout(timeout_sec)
|
|
291
|
+
if QUEUE_POP_TIMEOUT_SUPPORTED
|
|
292
|
+
return @input_queue.pop(timeout: timeout_sec)
|
|
293
|
+
end
|
|
294
|
+
|
|
284
295
|
deadline = Time.now + timeout_sec
|
|
285
296
|
loop do
|
|
286
297
|
begin
|
|
@@ -344,10 +355,13 @@ module Rufio
|
|
|
344
355
|
# ゲームループパターンのmain_loop(CPU最適化版:フレームスキップ対応)
|
|
345
356
|
# UPDATE → DRAW → RENDER → SLEEP のサイクル
|
|
346
357
|
# 変更がない場合は描画をスキップしてCPU使用率を削減
|
|
358
|
+
MAX_INPUTS_PER_FRAME = 32
|
|
359
|
+
|
|
347
360
|
def main_loop
|
|
348
361
|
# CPU最適化: 固定FPSをやめて、イベントドリブンに変更
|
|
349
362
|
# 最小スリープ時間(入力チェック間隔)
|
|
350
|
-
|
|
363
|
+
# Windows は入力をキュー経由で受けるため、入力遅延を抑えるよう短めにする
|
|
364
|
+
min_sleep_interval = windows? ? 0.008 : 0.0333 # Windows: 約8ms / Unix: 30FPS
|
|
351
365
|
check_interval = 0.1 # バックグラウンドタスクのチェック間隔
|
|
352
366
|
|
|
353
367
|
# Phase 3: Screen/Rendererを初期化
|
|
@@ -402,7 +416,14 @@ module Rufio
|
|
|
402
416
|
|
|
403
417
|
# UPDATE phase - ノンブロッキング入力処理
|
|
404
418
|
# 入力があった場合は再描画が必要
|
|
405
|
-
|
|
419
|
+
# 1フレームで溜まった入力をまとめて処理し、再描画は1回だけにする
|
|
420
|
+
# (1フレーム1キーだとキーリピートに追いつけず操作がもたつく)
|
|
421
|
+
had_input = false
|
|
422
|
+
MAX_INPUTS_PER_FRAME.times do
|
|
423
|
+
break unless handle_input_nonblocking
|
|
424
|
+
had_input = true
|
|
425
|
+
break unless @running
|
|
426
|
+
end
|
|
406
427
|
needs_redraw = true if had_input
|
|
407
428
|
|
|
408
429
|
# バックグラウンドコマンドの完了チェック(0.1秒ごと)
|
|
@@ -475,8 +496,10 @@ module Rufio
|
|
|
475
496
|
draw_screen_to_buffer(@screen, nil, current_fps)
|
|
476
497
|
end
|
|
477
498
|
|
|
478
|
-
#
|
|
479
|
-
if
|
|
499
|
+
# ヘルプまたはコマンドモードのダイアログをオーバーレイに描画
|
|
500
|
+
if help_dialog_active?
|
|
501
|
+
@help_menu.draw(@screen)
|
|
502
|
+
elsif @command_mode_active
|
|
480
503
|
# 前回のオーバーレイ残留を防ぐためクリアしてから描画
|
|
481
504
|
@screen.clear_overlay if @screen.overlay_enabled?
|
|
482
505
|
draw_command_mode_to_overlay
|
|
@@ -579,7 +602,13 @@ module Rufio
|
|
|
579
602
|
when 'B' then 'j' # Down arrow
|
|
580
603
|
when 'C' then 'l' # Right arrow
|
|
581
604
|
when 'D' then 'h' # Left arrow
|
|
582
|
-
when 'Z'
|
|
605
|
+
when 'Z'
|
|
606
|
+
if help_dialog_active?
|
|
607
|
+
handle_help_input("\e[Z")
|
|
608
|
+
else
|
|
609
|
+
handle_shift_tab
|
|
610
|
+
end
|
|
611
|
+
return true # Shift+Tab
|
|
583
612
|
else
|
|
584
613
|
# 未知の CSI シーケンス(\e[27;1u 等): 残りバイトを読み捨てて
|
|
585
614
|
# パイプを汚染しないようにする
|
|
@@ -591,6 +620,8 @@ module Rufio
|
|
|
591
620
|
end
|
|
592
621
|
end
|
|
593
622
|
|
|
623
|
+
return handle_help_input(input) if help_dialog_active?
|
|
624
|
+
|
|
594
625
|
# TabキーはFilesモードの時のみブックマーク循環移動(ジョブモード中は無効)
|
|
595
626
|
if input == "\t" && @tab_mode_manager.current_mode == :files && !@in_job_mode
|
|
596
627
|
handle_tab_key
|
|
@@ -673,6 +704,12 @@ module Rufio
|
|
|
673
704
|
# @param row [Integer] クリック行(1-indexed)
|
|
674
705
|
# @param press [Boolean] true=押下, false=解放
|
|
675
706
|
def handle_mouse_event(btn, col, row, press)
|
|
707
|
+
if help_dialog_active?
|
|
708
|
+
handle_help_input('k') if btn == 64
|
|
709
|
+
handle_help_input('j') if btn == 65
|
|
710
|
+
return
|
|
711
|
+
end
|
|
712
|
+
|
|
676
713
|
case btn
|
|
677
714
|
when 0 # 左クリック(押下のみ処理)
|
|
678
715
|
handle_mouse_left_click(col, row) if press
|
|
@@ -765,12 +802,7 @@ module Rufio
|
|
|
765
802
|
@keybind_handler.send(:exit_job_mode)
|
|
766
803
|
end
|
|
767
804
|
when :help
|
|
768
|
-
|
|
769
|
-
unless @keybind_handler.help_mode?
|
|
770
|
-
@keybind_handler.send(:exit_log_viewer_mode) if @keybind_handler.log_viewer_mode?
|
|
771
|
-
@keybind_handler.send(:exit_job_mode) if @keybind_handler.in_job_mode?
|
|
772
|
-
@keybind_handler.send(:enter_help_mode)
|
|
773
|
-
end
|
|
805
|
+
show_help_dialog
|
|
774
806
|
when :logs
|
|
775
807
|
# ログビューワモードに入る
|
|
776
808
|
unless @keybind_handler.log_viewer_mode?
|
|
@@ -1172,61 +1204,19 @@ module Rufio
|
|
|
1172
1204
|
# Screen と Renderer のアクセサ(他のクラスから利用可能に)
|
|
1173
1205
|
attr_reader :screen, :renderer
|
|
1174
1206
|
|
|
1175
|
-
#
|
|
1207
|
+
# ヘルプは非同期オーバーレイとして表示し、元のモードを保持する。
|
|
1176
1208
|
def show_help_dialog
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
'',
|
|
1181
|
-
'Key Bindings:',
|
|
1182
|
-
'',
|
|
1183
|
-
'j/k - Move up/down',
|
|
1184
|
-
'h/l - Navigate back/enter',
|
|
1185
|
-
'g/G - Go to top/bottom',
|
|
1186
|
-
'o - Open file',
|
|
1187
|
-
'f - Filter files',
|
|
1188
|
-
's - Search with fzf',
|
|
1189
|
-
'F - Content search (rga)',
|
|
1190
|
-
'a/A - Create file/directory',
|
|
1191
|
-
'm/c/x - Move/Copy/Delete',
|
|
1192
|
-
'b - Add bookmark',
|
|
1193
|
-
'z - Zoxide navigation',
|
|
1194
|
-
'0 - Go to start directory',
|
|
1195
|
-
'1-9 - Go to bookmark',
|
|
1196
|
-
'J - Job mode',
|
|
1197
|
-
': - Command mode',
|
|
1198
|
-
'q - Quit',
|
|
1199
|
-
''
|
|
1200
|
-
]
|
|
1201
|
-
|
|
1202
|
-
# お知らせ情報を追加
|
|
1203
|
-
require_relative 'info_notice'
|
|
1204
|
-
info_notice = InfoNotice.new
|
|
1205
|
-
all_notices = Dir.glob(File.join(info_notice.info_dir, '*.txt'))
|
|
1206
|
-
|
|
1207
|
-
if !all_notices.empty?
|
|
1208
|
-
content_lines << 'Recent Updates:'
|
|
1209
|
-
content_lines << ''
|
|
1210
|
-
all_notices.take(3).each do |file|
|
|
1211
|
-
title = info_notice.extract_title(file)
|
|
1212
|
-
content_lines << " • #{title}"
|
|
1213
|
-
end
|
|
1214
|
-
content_lines << ''
|
|
1215
|
-
end
|
|
1216
|
-
|
|
1217
|
-
content_lines << 'Press any key to continue...'
|
|
1209
|
+
@help_menu.open
|
|
1210
|
+
true
|
|
1211
|
+
end
|
|
1218
1212
|
|
|
1219
|
-
|
|
1220
|
-
|
|
1213
|
+
def help_dialog_active?
|
|
1214
|
+
@help_menu.active?
|
|
1215
|
+
end
|
|
1221
1216
|
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
height: height,
|
|
1226
|
-
border_color: "\e[36m", # Cyan
|
|
1227
|
-
title_color: "\e[1;36m", # Bold cyan
|
|
1228
|
-
content_color: "\e[37m" # White
|
|
1229
|
-
})
|
|
1217
|
+
def handle_help_input(input)
|
|
1218
|
+
@help_menu.handle_key(input)
|
|
1219
|
+
true
|
|
1230
1220
|
end
|
|
1231
1221
|
|
|
1232
1222
|
# 未知の CSI シーケンス(\e[X...)の残りバイトを終端アルファベットまで読み捨てる。
|
data/lib/rufio/ui_renderer.rb
CHANGED
|
@@ -401,6 +401,10 @@ module Rufio
|
|
|
401
401
|
header += ' ' * remaining_space if remaining_space > 0
|
|
402
402
|
screen.put_string(content_x, line_num, header)
|
|
403
403
|
|
|
404
|
+
elsif i == 1 && @keybind_handler&.preview_focused?
|
|
405
|
+
hint = TextUtils.truncate_to_width(' ESC: Back to files', safe_width)
|
|
406
|
+
screen.put_string(content_x, line_num, hint + ' ' * (safe_width - TextUtils.display_width(hint)))
|
|
407
|
+
|
|
404
408
|
elsif i >= 2 && highlighted_wrapped_lines
|
|
405
409
|
# シンタックスハイライト付きコンテンツ
|
|
406
410
|
scroll_offset = @keybind_handler&.preview_scroll_offset || 0
|
|
@@ -583,7 +587,7 @@ module Rufio
|
|
|
583
587
|
|
|
584
588
|
modes.each_with_index do |mode, index|
|
|
585
589
|
key = keys[mode]
|
|
586
|
-
label = key ? " #{key}
|
|
590
|
+
label = key ? " [#{key}] #{labels[mode]} " : " #{labels[mode]} "
|
|
587
591
|
|
|
588
592
|
if mode == current_mode
|
|
589
593
|
label.each_char do |char|
|
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: 1.0.
|
|
4
|
+
version: 1.0.4
|
|
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-10-25 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: io-console
|
|
@@ -121,6 +121,7 @@ files:
|
|
|
121
121
|
- README.md
|
|
122
122
|
- README_ja.md
|
|
123
123
|
- Rakefile
|
|
124
|
+
- SECURITY.md
|
|
124
125
|
- bin/rufio
|
|
125
126
|
- docs/CHANGELOG_v0.10.0.md
|
|
126
127
|
- docs/CHANGELOG_v0.20.0.md
|
|
@@ -175,6 +176,7 @@ files:
|
|
|
175
176
|
- lib/rufio/file_preview.rb
|
|
176
177
|
- lib/rufio/filter_manager.rb
|
|
177
178
|
- lib/rufio/health_checker.rb
|
|
179
|
+
- lib/rufio/help_menu.rb
|
|
178
180
|
- lib/rufio/info_notice.rb
|
|
179
181
|
- lib/rufio/interpreter_resolver.rb
|
|
180
182
|
- lib/rufio/job_manager.rb
|