rufio 1.1.0 → 1.3.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.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +34 -0
  3. data/README.md +74 -3
  4. data/README_ja.md +71 -2
  5. data/Rakefile +14 -1
  6. data/bin/rufio +31 -2
  7. data/docs/fast_zip_design_ja.md +167 -0
  8. data/info/help.md +2 -0
  9. data/info/keybindings.md +3 -1
  10. data/info/welcome.md +1 -1
  11. data/lib/rufio/application.rb +12 -1
  12. data/lib/rufio/archive_controller.rb +211 -0
  13. data/lib/rufio/background_command_executor.rb +9 -0
  14. data/lib/rufio/background_status.rb +49 -0
  15. data/lib/rufio/bookmark_controller.rb +38 -121
  16. data/lib/rufio/bookmark_manager.rb +77 -331
  17. data/lib/rufio/command_completion.rb +6 -1
  18. data/lib/rufio/command_history.rb +5 -2
  19. data/lib/rufio/command_mode.rb +40 -134
  20. data/lib/rufio/command_mode_ui.rb +4 -42
  21. data/lib/rufio/config.rb +88 -136
  22. data/lib/rufio/config_loader.rb +3 -11
  23. data/lib/rufio/dialog_renderer.rb +64 -152
  24. data/lib/rufio/dialog_support.rb +69 -0
  25. data/lib/rufio/document_text_extractor.rb +167 -0
  26. data/lib/rufio/file_operation_controller.rb +49 -222
  27. data/lib/rufio/file_preview.rb +46 -2
  28. data/lib/rufio/global_search.rb +121 -9
  29. data/lib/rufio/help_menu.rb +8 -2
  30. data/lib/rufio/interpreter_resolver.rb +6 -11
  31. data/lib/rufio/job_manager.rb +2 -5
  32. data/lib/rufio/key_input.rb +77 -0
  33. data/lib/rufio/keybind_handler.rb +81 -115
  34. data/lib/rufio/local_script_scanner.rb +6 -18
  35. data/lib/rufio/native/aarch64-linux-gnu/librufio_zig.so +0 -0
  36. data/lib/rufio/native/aarch64-linux-musl/librufio_zig.so +0 -0
  37. data/lib/rufio/native/aarch64-macos/librufio_zig.dylib +0 -0
  38. data/lib/rufio/native/aarch64-windows/rufio_zig.dll +0 -0
  39. data/lib/rufio/native/x86_64-linux-gnu/librufio_zig.so +0 -0
  40. data/lib/rufio/native/x86_64-linux-musl/librufio_zig.so +0 -0
  41. data/lib/rufio/native/x86_64-macos/librufio_zig.dylib +0 -0
  42. data/lib/rufio/native/x86_64-windows/rufio_zig.dll +0 -0
  43. data/lib/rufio/native_content_search.rb +153 -0
  44. data/lib/rufio/native_scanner.rb +13 -68
  45. data/lib/rufio/native_scanner_zig.rb +106 -56
  46. data/lib/rufio/native_zip.rb +121 -0
  47. data/lib/rufio/navigation_controller.rb +18 -20
  48. data/lib/rufio/screen.rb +2 -12
  49. data/lib/rufio/script_executor.rb +6 -73
  50. data/lib/rufio/script_file.rb +44 -0
  51. data/lib/rufio/script_path_manager.rb +9 -43
  52. data/lib/rufio/script_runner.rb +13 -50
  53. data/lib/rufio/search_controller.rb +1 -1
  54. data/lib/rufio/shell_command_completion.rb +1 -5
  55. data/lib/rufio/shell_init.rb +90 -0
  56. data/lib/rufio/task_status.rb +5 -1
  57. data/lib/rufio/terminal_ui.rb +55 -294
  58. data/lib/rufio/text_utils.rb +26 -3
  59. data/lib/rufio/ui_renderer.rb +63 -40
  60. data/lib/rufio/version.rb +1 -1
  61. data/lib/rufio/zip_archive.rb +314 -0
  62. data/lib/rufio/zip_extractor.rb +346 -0
  63. data/lib/rufio/zip_writer.rb +268 -0
  64. data/lib/rufio/zoxide_integration.rb +5 -43
  65. data/lib/rufio.rb +5 -0
  66. data/lib_zig/rufio_native/Makefile +54 -17
  67. data/lib_zig/rufio_native/bench.sh +36 -0
  68. data/lib_zig/rufio_native/build.zig +32 -35
  69. data/lib_zig/rufio_native/src/cli.zig +99 -0
  70. data/lib_zig/rufio_native/src/main.zig +187 -83
  71. data/lib_zig/rufio_native/src/os.zig +889 -0
  72. data/lib_zig/rufio_native/src/search.zig +919 -0
  73. data/lib_zig/rufio_native/src/search_test.zig +88 -0
  74. data/lib_zig/rufio_native/src/zip.zig +482 -0
  75. metadata +125 -3
  76. data/lib/rufio/native/rufio_zig.bundle +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c22a65655be19fc13d4e565ea8f926186fc58c7acbff0e16daed2a0efd3362b1
4
- data.tar.gz: 654b40fe283c94abc1c438b77a2a8eeabc5e5a6321acd01e056d6ccaa5af874b
3
+ metadata.gz: 61f470cce894bacf7a88eaffbca98212689c7a74fe615c41cc90f75af4c6c8da
4
+ data.tar.gz: eb507f8b6ccfc365f1276512fd13d7959cffac7ae9b8208d8e3ae6a9716ab2d1
5
5
  SHA512:
6
- metadata.gz: afb532e1ae55826d8ffeb2607b31717bbae6a2c322493f9a5d81f308bad8c995c714e6638376fbba5ed3878689193917b29814e0ed657d02905a9d8ba8a873c5
7
- data.tar.gz: 97e60e1b5e180eccaf10fb2c5e501f52ec51dde50fc32f0fa5c5f11c2cbf89b367f970c693f5e2d533efea928d1ccd3a8a9e9aed72c4a1830e0975fb9033a980
6
+ metadata.gz: 43483f6db196732505620756a314556d7a2c07c709df960995e87e56b68d7aa348271344c215307859b92bcc4f90c5c0afd349cabf999eaf7b64845698c4da11
7
+ data.tar.gz: 9e0998e7819be5f14f4ad25a6beb8ddcd2de7da78858168e32b884d0b604347395f0b812b0fb3555e1773687d7cdbeaaf02893e09e1de9d2997e3a37b150f718
data/CHANGELOG.md CHANGED
@@ -5,6 +5,40 @@ All notable changes to rufio will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [Unreleased]
9
+
10
+ ## [1.3.0] - 2026-11-01
11
+
12
+ ### Added
13
+ - Preview ZIP archives as an entry list (size, method, modified time, name), including ZIP64, UTF-8 / CP932 file names, and the Info-ZIP Unicode Path extra field. The list is read from the central directory without extracting files
14
+ - Press `z` with items selected in Files mode to extract them when every selected item is a `.zip` file (each into `<name>/`, numbered if the name is taken) or otherwise compress them into one `.zip` in the current directory. The work runs as a background job with progress in Jobs mode, can be cancelled, removes a partially extracted folder on failure, and refreshes the file list when done. Encrypted entries are skipped. Without a selection, `z` still opens zoxide history
15
+ - The bundled Zig library adds a ZIP engine that decompresses entries and compresses files of 1 MiB or more in parallel outside the GVL, rebuilt for all eight bundled platforms. Ruby still validates archives, chooses paths, and renames files, so the safety checks are shared. On 8 × 64 MB text files with 4 CPUs it compressed about 4.6x and extracted about 3.2x faster than the Ruby implementation. `RUFIO_NATIVE_ZIP=0` disables it, and older libraries without the ZIP functions fall back to Ruby
16
+ - `Rufio::ZipExtractor` extracts Stored and Deflate entries after validating the whole archive first: it rejects path traversal, absolute paths, overlapping entries, and archives over size, entry count, or compression ratio limits, skips symlink and encrypted entries, never follows existing symlinks, verifies CRC-32, refuses to overwrite files unless asked, and leaves no partial files
17
+ - `Rufio::ZipWriter` creates ZIP archives with UTF-8 names, falls back to Stored for incompressible files, uses ZIP64 for large files and more than 65,535 entries, skips symlinks, and writes through a temporary file
18
+
19
+ ## [1.2.0] - 2026-10-31
20
+
21
+ ### Added
22
+ - G mode content search reads PDF (`pdf-reader`), Excel `.xlsx` (`roo`), and PowerPoint `.pptx` (Rubyzip and XML) directly from Ruby, without Python or external conversion commands
23
+ - Document results show the PDF page, Excel sheet and cell, or PowerPoint slide alongside matching text; Enter still locates the source file in Files mode
24
+ - A bounded in-memory document text cache avoids re-extracting unchanged documents when editing the query; file changes invalidate cached text, and corrupt or encrypted documents report errors without stopping other searches
25
+
26
+ - G mode content search runs on a bundled Zig engine: parallel directory walking, SIMD literal matching, and a revalidated in-memory file cache make the first search about 1.8x and repeated searches about 10x faster than `rg` on a 31k-file tree; queries with non-ASCII cased letters still use the Ruby search, and `RUFIO_NATIVE_SEARCH=0` disables the engine
27
+ - G mode highlights the matching text (case-insensitive, every occurrence) in file name results and content snippets
28
+ - `T` quits rufio and moves the shell to the current directory; `rufio --shell-init [bash|zsh|fish|powershell]` prints the `rf` shell function that does the move (PowerShell on Windows, the login shell elsewhere), and `--choosedir FILE` receives the directory
29
+ - The gem bundles the Zig library for macOS (arm64/x86_64), Linux (x86_64/aarch64, glibc 2.17+ and musl), and Windows (x64/arm64); unsupported platforms fall back to Ruby automatically
30
+
31
+ ### Changed
32
+ - Files mode shows the current path on its own top row above the bookmark list (full path with `~` for home, shortened from the left when too long) instead of on the bottom tab line; Jobs mode keeps the path on the tab line
33
+ - Rebuild the Zig library with Zig 0.17 (`make -C lib_zig/rufio_native dist`); bundled binaries live in `lib/rufio/native/<platform>/`
34
+ - Consolidate UI rendering, dialog drawing, file operations, bookmark flows, native scanner helpers, shell execution, and localized message definitions, removing dead code and runtime warnings
35
+
36
+ ### Fixed
37
+ - Windows: dialogs no longer compete with the input thread for keys, so bookmark names and other dialog input no longer need every key pressed twice
38
+ - Windows: wait longer for arrow key escape sequences in G mode so arrow keys no longer close G mode as a lone ESC
39
+ - Windows: DSL shell command timeouts stop the process with KILL instead of failing with `Invalid argument` on TERM and leaving it running
40
+ - Restore the scanner mode after native scanner tests so test order no longer affects normal directory listings
41
+
8
42
  ## [1.1.0] - 2026-10-25
9
43
 
10
44
  ### Added
data/README.md CHANGED
@@ -87,6 +87,7 @@ rufio /path/to # Launch in specified directory
87
87
  | `g/}` | Top/bottom |
88
88
  | `Enter` | Enter directory/open file |
89
89
  | `q` | Quit |
90
+ | `T` | Quit and cd to the current directory ([Shell Integration](#shell-integration)) |
90
91
 
91
92
  ### File Operations
92
93
 
@@ -98,6 +99,7 @@ rufio /path/to # Launch in specified directory
98
99
  | `r` | Rename |
99
100
  | `d` | Delete |
100
101
  | `m/c/x` | Move/copy/delete (selected) |
102
+ | `z` | Extract selected `.zip` files / compress selected items (selected) |
101
103
 
102
104
  ### Search & Filter
103
105
 
@@ -116,7 +118,7 @@ rufio /path/to # Launch in specified directory
116
118
  | `0` | Return to startup directory |
117
119
  | `1-9` | Jump to bookmark |
118
120
  | `Tab` | Cycle to next bookmark (Files mode only) |
119
- | `z` | zoxide history |
121
+ | `z` | zoxide history (when nothing is selected) |
120
122
 
121
123
  ### Tool Runtime
122
124
 
@@ -135,8 +137,8 @@ rufio /path/to # Launch in specified directory
135
137
  Press `G` to search recursively across all bookmarked directories. `Tab`
136
138
  switches between **File names** and **Content**. Type a literal string to
137
139
  search (case insensitive); content results include the file, line number,
138
- and matching text. Text is read as UTF-8; files containing NUL bytes in the
139
- first 8 KB are skipped as binary. Use Up/Down or Ctrl+N/P to select a
140
+ and matching text. Plain text is read as UTF-8; other unsupported files containing
141
+ NUL bytes in the first 8 KB are skipped as binary. Use Up/Down or Ctrl+N/P to select a
140
142
  result, and press Enter to jump to that file in Files mode. `Ctrl+U` clears
141
143
  the query; `Esc` cancels and returns to your previous view. Scanning runs in
142
144
  the background and skips inaccessible directories and directory symlinks.
@@ -154,12 +156,62 @@ GLOBAL_SEARCH_EXCLUDED_EXTENSIONS = %w[.png .jpg .zip .tar.gz .log].freeze
154
156
  Extensions are case insensitive and the leading dot is optional. The default
155
157
  is `[]` (no extension exclusions). Directory names are unaffected.
156
158
 
159
+ #### PDF, Excel and PowerPoint content search (v1.2.0)
160
+
161
+ Content mode also extracts text from documents using Ruby gems; Python,
162
+ Microsoft Office and external conversion tools are not required.
163
+
164
+ | File | Searchable content | Result location |
165
+ |------|--------------------|-----------------|
166
+ | `.pdf` | Extractable text | Page number |
167
+ | `.xlsx` | Cell text and values | Sheet name and cell address |
168
+ | `.pptx` | Text in slide shapes and tables | Slide number |
169
+
170
+ Search remains literal and case insensitive. Split text runs in a PowerPoint
171
+ paragraph are joined, and slides follow the presentation's display order.
172
+ Excel formulas use the values saved in the workbook; Rufio does not recalculate them.
173
+ Enter locates the original document in Files mode; it does not open Office at a cell or slide.
174
+
175
+ Extracted text is cached in memory across searches within the running app.
176
+ The cache keeps up to 64 documents and approximately 16 MiB of text and metadata;
177
+ larger extracted documents remain searchable without being cached. Updated or
178
+ replaced files are re-extracted. No document text cache is written to disk.
179
+
180
+ Extension exclusions apply to documents too. Damaged, unsupported or encrypted
181
+ documents may report errors while other files continue to be searched.
182
+ Scanned images need OCR, which is not included. Legacy `.xls`/`.ppt`, slide notes,
183
+ and text embedded only in images or charts are not supported by this implementation.
184
+ PDF text extraction depends on the document's font and character mappings.
185
+
157
186
  ### Config Mode (C)
158
187
 
159
188
  Press `C` to open `~/.config/rufio/`, creating it if needed. Navigation, previews,
160
189
  and file operations work as in Files mode. Browse settings and scripts directly;
161
190
  leaving the config directory returns the mode display to Files.
162
191
 
192
+ ### Shell Integration
193
+
194
+ `rufio --shell-init` prints a shell function named `rf` for your OS: PowerShell on
195
+ Windows, and your login shell (`bash`, `zsh` or `fish`) elsewhere. Pass the shell name to
196
+ choose explicitly. Launch rufio with `rf` and press `T` to quit and move the shell to the
197
+ directory you were viewing; `q` quits without moving.
198
+
199
+ ```bash
200
+ # bash / zsh (~/.bashrc or ~/.zshrc)
201
+ eval "$(rufio --shell-init)"
202
+
203
+ # fish (~/.config/fish/config.fish)
204
+ rufio --shell-init fish | source
205
+ ```
206
+
207
+ ```powershell
208
+ # PowerShell ($PROFILE)
209
+ Invoke-Expression (& rufio --shell-init powershell | Out-String)
210
+ ```
211
+
212
+ To avoid starting Ruby with every shell, append the output once instead, for example
213
+ `rufio --shell-init >> ~/.bashrc`.
214
+
163
215
  ## Command Mode
164
216
 
165
217
  Press `:` to enter command mode and execute various commands.
@@ -354,6 +406,25 @@ apt install fzf bat zoxide
354
406
 
355
407
  ## Advanced Features
356
408
 
409
+ ### Native Content Search
410
+
411
+ G mode content search uses a bundled Zig engine on macOS (arm64/x86_64), Linux (x86_64/aarch64, glibc 2.17+ or musl), and Windows (x64/arm64). It searches bookmarks in parallel and caches file contents between queries, so retyping a query is about 10x faster than `rg`. Other platforms, and queries containing non-ASCII letters with case, use the Ruby search automatically. Set `RUFIO_NATIVE_SEARCH=0` to disable it.
412
+
413
+ ### ZIP Archives
414
+
415
+ Select items in Files mode with `Space` and press `z`: when every selected item is a `.zip` file, each is extracted into `<name>/` in the current directory; otherwise the selection is compressed into one `.zip`. The work runs as a job (progress and cancel in Jobs mode). `.zip` files also show their entry list in the preview.
416
+
417
+ Extraction validates the whole archive before writing anything: it rejects path traversal and absolute paths, overlapping entries, and archives over 16 GiB in total, 100,000 entries, or a 1000:1 compression ratio (for entries over 1 MiB); it skips symlink and encrypted entries, verifies CRC-32, and never overwrites existing files. Only Stored and Deflate entries are supported.
418
+
419
+ On the same platforms as content search, the bundled Zig engine decompresses entries and compresses files of 1 MiB or more in parallel (in our measurements on 8 × 64 MB text files with 4 CPUs, about 4.6x faster to compress and 3.2x faster to extract than the Ruby implementation). Validation stays in Ruby either way. Set `RUFIO_NATIVE_ZIP=0` to use the Ruby implementation only.
420
+
421
+ To rebuild the bundled binaries (requires Zig 0.17):
422
+
423
+ ```bash
424
+ make -C lib_zig/rufio_native dist # all platforms
425
+ bundle exec rake native:check # verify bundled binaries
426
+ ```
427
+
357
428
  ### Native Scanner (Experimental)
358
429
 
359
430
  Support for native implementation for fast directory scanning:
data/README_ja.md CHANGED
@@ -87,6 +87,7 @@ rufio /path/to # 指定したディレクトリで起動
87
87
  | `g/}` | 先頭/末尾 |
88
88
  | `Enter` | ディレクトリに入る/ファイルを開く |
89
89
  | `q` | 終了 |
90
+ | `T` | 終了して現在のディレクトリへ移動([シェル連携](#シェル連携)) |
90
91
 
91
92
  ### ファイル操作
92
93
 
@@ -98,6 +99,7 @@ rufio /path/to # 指定したディレクトリで起動
98
99
  | `r` | リネーム |
99
100
  | `d` | 削除 |
100
101
  | `m/c/x` | 移動/コピー/削除(選択済み) |
102
+ | `z` | 選択した `.zip` を展開 / 選択したアイテムを `.zip` に圧縮(選択済み) |
101
103
 
102
104
  ### 検索・フィルター
103
105
 
@@ -116,7 +118,7 @@ rufio /path/to # 指定したディレクトリで起動
116
118
  | `0` | 起動ディレクトリに戻る |
117
119
  | `1-9` | ブックマークにジャンプ |
118
120
  | `Tab` | 次のブックマークへ循環移動(Filesモード限定) |
119
- | `z` | zoxide履歴 |
121
+ | `z` | zoxide履歴(未選択時) |
120
122
 
121
123
  ### ツールランタイム
122
124
 
@@ -135,7 +137,7 @@ rufio /path/to # 指定したディレクトリで起動
135
137
  `G` を押すと、ブックマークした全ディレクトリの配下を再帰的に検索します。
136
138
  Tabで **File names(ファイル名)** と **Content(ファイル内容)** を切り替えます。
137
139
  大文字・小文字を区別しない文字列の部分一致で検索し、内容検索ではファイル名・行番号・一致した行を表示します。
138
- テキストはUTF-8として読み込み、先頭8 KBにNULバイトがあるファイルはバイナリとしてスキップします。
140
+ 通常のテキストはUTF-8として読み込み、文書抽出に対応していないファイルは先頭8 KBにNULバイトがあればバイナリとしてスキップします。
139
141
  上下矢印または `Ctrl+N/P` で選択、
140
142
  Enterで選んだファイルの場所へ移動します。`Ctrl+U` で検索語をクリアし、
141
143
  Escで元の表示に戻ります。検索はバックグラウンドで進みます。
@@ -154,12 +156,60 @@ GLOBAL_SEARCH_EXCLUDED_EXTENSIONS = %w[.png .jpg .zip .tar.gz .log].freeze
154
156
  大文字・小文字は区別せず、先頭のドットは省略できます。デフォルトは `[]`(拡張子の除外なし)です。
155
157
  ディレクトリ名には適用しません。
156
158
 
159
+ #### PDF・Excel・PowerPointの内容検索(v1.2.0)
160
+
161
+ Contentモードで文書内の文字列も検索できます。Rubyのgemで抽出するため、
162
+ Python・Microsoft Office・外部変換コマンドは不要です。
163
+
164
+ | ファイル | 検索する内容 | 結果の位置表示 |
165
+ |------------|--------------|----------------|
166
+ | `.pdf` | 抽出可能な文字列 | ページ番号 |
167
+ | `.xlsx` | セルの文字列・値 | シート名・セル番地 |
168
+ | `.pptx` | スライドの図形・表内の文字列 | スライド番号 |
169
+
170
+ 大文字・小文字を区別しない部分一致検索です。PowerPointの段落内で分割された文字列は連結し、
171
+ スライド番号はプレゼンテーションの表示順に対応します。
172
+ Excelの数式はファイルに保存済みの計算結果を検索し、再計算は行いません。
173
+ Enterでは元の文書の場所へ移動します。Officeでセルやスライドを直接開く機能ではありません。
174
+
175
+ 抽出した文字列は起動中のメモリにキャッシュし、検索語を変更しても再利用します。
176
+ 最大64文書、文字列とメタデータの概算で16 MiBまで保持し、超える文書もキャッシュせずに検索します。
177
+ ファイルを更新・置換した場合は再抽出します。キャッシュをディスクには保存しません。
178
+
179
+ 拡張子除外設定は文書にも適用します。破損・非対応・暗号化された文書でエラーが発生しても、他のファイルの検索は続行します。
180
+ 画像だけのPDFにはOCRが必要ですが、今回の実装には含みません。旧形式の`.xls`・`.ppt`、
181
+ スライドノート、画像やグラフだけに含まれる文字列も対象外です。
182
+ PDFの文字抽出精度は、文書のフォントや文字コードの対応情報に依存します。
183
+
157
184
  ### Configモード(C)
158
185
 
159
186
  `C` で `~/.config/rufio/` を開きます。ディレクトリがなければ作成します。
160
187
  移動・プレビュー・ファイル操作はFilesモードと共通で、設定やスクリプトを直接編集できます。
161
188
  設定ディレクトリの外へ移動すると、モード表示がFilesに戻ります。
162
189
 
190
+ ### シェル連携
191
+
192
+ `rufio --shell-init` は OS に合わせた `rf` というシェル関数を出力します。Windows では
193
+ PowerShell 用、それ以外ではログインシェル(`bash`・`zsh`・`fish`)用です。シェル名を
194
+ 指定して切り替えることもできます。`rf` で rufio を起動し `T` を押すと、終了してシェルが
195
+ 表示中のディレクトリへ移動します。`q` で終了した場合は移動しません。
196
+
197
+ ```bash
198
+ # bash / zsh(~/.bashrc または ~/.zshrc)
199
+ eval "$(rufio --shell-init)"
200
+
201
+ # fish(~/.config/fish/config.fish)
202
+ rufio --shell-init fish | source
203
+ ```
204
+
205
+ ```powershell
206
+ # PowerShell($PROFILE)
207
+ Invoke-Expression (& rufio --shell-init powershell | Out-String)
208
+ ```
209
+
210
+ シェル起動のたびに Ruby を起動したくない場合は、`rufio --shell-init >> ~/.bashrc` の
211
+ ように一度だけ出力を追記してください。
212
+
163
213
  ## コマンドモード
164
214
 
165
215
  `:` でコマンドモードを起動し、様々なコマンドを実行できます。
@@ -320,6 +370,25 @@ apt install fzf bat zoxide
320
370
 
321
371
  ## 高度な機能
322
372
 
373
+ ### ネイティブコンテンツ検索
374
+
375
+ G mode のコンテンツ検索は、macOS(arm64/x86_64)、Linux(x86_64/aarch64、glibc 2.17 以降または musl)、Windows(x64/arm64)では同梱の Zig エンジンで動作します。ブックマークを並列に検索し、検索の間でファイル内容をキャッシュするため、検索語を打ち直したときの再検索は `rg` の約 10 倍速くなります。その他のプラットフォームや、大文字小文字のある非 ASCII 文字を含む検索語では自動的に Ruby の検索を使います。`RUFIO_NATIVE_SEARCH=0` で無効化できます。
376
+
377
+ ### ZIP の圧縮・展開
378
+
379
+ Files モードで `Space` で選択して `z` を押すと、選択がすべて `.zip` ならそれぞれを現在のディレクトリの `<名前>/` に展開し、それ以外は 1 つの `.zip` に圧縮します。処理はジョブとして実行されます(Jobs モードで進捗表示・キャンセル)。`.zip` はプレビューでエントリ一覧も表示します。
380
+
381
+ 展開時は書き込み前にアーカイブ全体を検証します。パストラバーサル・絶対パス・データ範囲が重なるエントリ、合計 16GiB・10 万エントリ・圧縮率 1000:1(1MiB 超のエントリ)を超えるアーカイブを拒否し、シンボリックリンクと暗号化エントリはスキップし、CRC-32 を検証し、既存ファイルは上書きしません。対応する圧縮方式は Stored と Deflate です。
382
+
383
+ コンテンツ検索と同じプラットフォームでは、同梱の Zig エンジンがエントリの伸長と 1MiB 以上のファイルの圧縮を並列に行います(4 CPU で 64MB のテキスト 8 ファイルを計測したところ、Ruby 実装より圧縮が約 4.6 倍、展開が約 3.2 倍速くなりました)。検証はどちらの場合も Ruby 側で行います。`RUFIO_NATIVE_ZIP=0` で Ruby 実装のみを使います。
384
+
385
+ 同梱バイナリの再ビルド(Zig 0.17 が必要):
386
+
387
+ ```bash
388
+ make -C lib_zig/rufio_native dist # 全プラットフォーム
389
+ bundle exec rake native:check # 同梱バイナリの検証
390
+ ```
391
+
323
392
  ### ネイティブスキャナー(実験的)
324
393
 
325
394
  高速なディレクトリスキャンのためのネイティブ実装をサポート:
data/Rakefile CHANGED
@@ -14,10 +14,23 @@ end
14
14
  # Default task
15
15
  task default: :test
16
16
 
17
+ # Zig native library tasks
18
+ namespace :native do
19
+ desc 'Cross-compile the Zig library for every bundled platform (requires zig)'
20
+ task :build do
21
+ sh 'make -C lib_zig/rufio_native dist'
22
+ end
23
+
24
+ desc 'Verify bundled Zig binaries for macOS, Linux and Windows'
25
+ task :check do
26
+ ruby '-I test -I lib test/test_native_platform_binaries.rb'
27
+ end
28
+ end
29
+
17
30
  # Gem build and push tasks
18
31
  namespace :gem do
19
32
  desc 'Build the gem file'
20
- task :build do
33
+ task :build => 'native:check' do
21
34
  puts "Building rufio v#{Rufio::VERSION}..."
22
35
 
23
36
  # Clean old gem files
data/bin/rufio CHANGED
@@ -19,6 +19,8 @@ start_directory = nil
19
19
  test_mode = false
20
20
  yjit_mode = false
21
21
  zjit_mode = false
22
+ choosedir = nil
23
+ shell_init = nil
22
24
  skip_next = false
23
25
 
24
26
  ARGV.each_with_index do |arg, idx|
@@ -49,6 +51,21 @@ ARGV.each_with_index do |arg, idx|
49
51
  end
50
52
  when /^--native=(rust|go|auto|ruby)$/
51
53
  native_mode = $1
54
+ when '--choosedir'
55
+ choosedir = ARGV[idx + 1]
56
+ skip_next = true
57
+ when /^--choosedir=(.+)$/
58
+ choosedir = $1
59
+ when '--shell-init'
60
+ # 次の引数がシェル名なら使い、なければ OS から判定する
61
+ if Rufio::ShellInit::SHELLS.include?(ARGV[idx + 1]) || ARGV[idx + 1] == 'pwsh'
62
+ shell_init = ARGV[idx + 1]
63
+ skip_next = true
64
+ else
65
+ shell_init = Rufio::ShellInit.detect
66
+ end
67
+ when /^--shell-init=(.+)$/
68
+ shell_init = $1
52
69
  when '-c', '--check-health', '--help', '-h', '--yjit', '--zjit'
53
70
  # これらは後で処理
54
71
  when /^--/
@@ -70,7 +87,15 @@ if ENV['RUFIO_NATIVE'] && defined?(Rufio::NativeScanner)
70
87
  end
71
88
 
72
89
  # コマンド処理
73
- if ARGV.include?('--check-health') || ARGV.include?('-c')
90
+ if shell_init
91
+ begin
92
+ print Rufio::ShellInit.script(shell_init)
93
+ exit(0)
94
+ rescue ArgumentError => e
95
+ warn e.message
96
+ exit(1)
97
+ end
98
+ elsif ARGV.include?('--check-health') || ARGV.include?('-c')
74
99
  # ヘルスチェック実行
75
100
  health_checker = Rufio::HealthChecker.new
76
101
  success = health_checker.run_check
@@ -87,6 +112,9 @@ elsif ARGV.include?('--help') || ARGV.include?('-h')
87
112
  puts " --test Show FPS counter in footer (for performance testing)"
88
113
  puts " --yjit Enable YJIT JIT compiler (Ruby 3.1+)"
89
114
  puts " --zjit Enable ZJIT JIT compiler (Ruby 3.4+)"
115
+ puts " --shell-init [SHELL] Print the rf shell function (cd on T quit)"
116
+ puts " SHELL: bash|zsh|fish|powershell (default: by OS)"
117
+ puts " --choosedir FILE Write the directory to FILE when quitting with T"
90
118
  if defined?(Rufio::NativeScanner)
91
119
  puts " --native[=MODE] Enable native scanner (experimental)"
92
120
  puts " MODE: auto|rust|go (default: auto)"
@@ -117,11 +145,12 @@ elsif ARGV.include?('--help') || ARGV.include?('-h')
117
145
  puts " RUFIO_NATIVE=rust rufio # Use Rust via environment variable"
118
146
  end
119
147
  puts " rufio -c # Check dependencies"
148
+ puts " eval \"$(rufio --shell-init)\" # Add rf to bash/zsh (T quits and cd)"
120
149
  exit(0)
121
150
  else
122
151
  start_directory ||= Dir.pwd
123
152
 
124
153
  # アプリケーション開始
125
- app = Rufio::Application.new(start_directory)
154
+ app = Rufio::Application.new(start_directory, choosedir: choosedir)
126
155
  app.run(test_mode: test_mode)
127
156
  end
@@ -0,0 +1,167 @@
1
+ # FastZip 実装検討(rufio 1.3)
2
+
3
+ ブランチ: `v1.3.0`
4
+ ステータス: Phase 1〜5 実装済み(Pure Ruby の一覧・展開・作成、`z` キー、Zig エンジンの並列展開・圧縮、8 プラットフォームのバイナリ同梱)
5
+
6
+ 「FastZip 開発構想メモ(Ruby × Zig ネイティブ拡張アーカイバ)」を rufio 1.3 の機能として取り込む前提で、既存コードとの整合と実装方針を整理する。
7
+
8
+ ---
9
+
10
+ ## 1. 結論
11
+
12
+ | 項目 | 構想メモ | 本検討での方針 |
13
+ |------|----------|----------------|
14
+ | 置き場所 | 独立Gem `fast_zip` | まず rufio 内の機能として実装。ABIが Ruby 非依存なので、後から Gem に切り出せる |
15
+ | Ruby との接続 | C拡張(`Init_fast_zip` + `rb_thread_call_without_gvl`) | **既存の流儀に合わせて Fiddle + Ruby ABI 非依存の C 関数** |
16
+ | GVL 解放 | 自前で `rb_thread_call_without_gvl` | Fiddle が既定で解放する(下記 2.3)。長時間処理は既存スキャナと同じ「ハンドル+ポーリング」型にしてキャンセル可能にする |
17
+ | ネイティブ非搭載環境 | 記載なし | **Pure Ruby 実装を必須のフォールバック兼リファレンス**とする(Phase 1 で実装済み) |
18
+ | 最初のユーザー価値 | — | `.zip` をプレビューペインで一覧表示(`unzip -l` 相当)※実装済み |
19
+
20
+ ### 1.1 決定事項(2026-10-31)
21
+ - C 拡張にはしない。既存の Zig 連携(Fiddle + Ruby ABI 非依存の C 関数)で実装する
22
+ - Pure Ruby 版は残す(ネイティブ非搭載環境のフォールバック兼、互換性の基準)
23
+ - 展開時の安全対策を入れる(4.2)
24
+ - ネイティブバイナリはビルド済みのものを Gem に同梱して配る(1.2 の `make dist` の 8 プラットフォーム)
25
+ - 操作は Files モードで選択後に `z`(すべて .zip なら展開、それ以外は圧縮)。暗号化エントリはスキップのまま
26
+ - 展開上限は現在の値(合計 16GiB / 10 万エントリ / 1MiB 超で 1000:1)で確定。対応方式は Stored と Deflate のみ
27
+
28
+ ---
29
+
30
+ ## 2. 現状調査(事実)
31
+
32
+ ### 2.1 rufio のアーカイブ対応
33
+ - `FilePreview#determine_file_type` は `.zip` 等を `archive` と判定するが、実際はバイナリ判定が先に走り「Binary file / Cannot preview」と表示されていた。
34
+ - 圧縮・展開機能はない。
35
+
36
+ ### 2.2 既存の Zig ネイティブ拡張(1.2.0 時点)
37
+ - ソース: `lib_zig/rufio_native/src/`(`main.zig` がエクスポート、`search.zig` がコンテンツ検索、`os.zig` が OS 差分の吸収)。Ruby ヘッダは include せず、`core_async_*` / `core_search_*` / `core_cache_*` という **Ruby ABI 非依存の C 関数**をエクスポートしている。
38
+ - Ruby 側: `lib/rufio/native_scanner_zig.rb` が `Fiddle::Importer` でロード。関数グループごとに `search_available?` のような判定があり、古いバイナリでも Ruby 実装に切り替わる。
39
+ - ビルド: Zig 0.17。`make -C lib_zig/rufio_native dist` で macOS(arm64/x86_64)・Linux(x86_64/aarch64、glibc 2.17+ と musl)・Windows(x64/arm64)の 8 種類をクロスコンパイルし、`lib/rufio/native/<platform>/` に同梱する。
40
+ - `test/test_native_platform_binaries.rb` が各バイナリの形式・CPU・必須エクスポート(`REQUIRED_EXPORTS`)を検証している。
41
+ - 1.2.0 で `rubyzip`(`>= 2.4, < 3.0`)が G モードの `.pptx` 読み取り用に依存関係に入った。ただし rubyzip 2.4 はファイル名を ASCII-8BIT で保持し、UTF-8 文字列で引けない(このブランチのテストで確認)。プレビュー・展開・作成は本実装を使い、rubyzip は相互運用テストの相手として使う。
42
+
43
+ ### 2.3 Fiddle と GVL
44
+ - 本環境の fiddle 1.1.8 の `ext/fiddle/function.c` では、`need_gvl` の既定値が `Qfalse` で、その場合 `rb_thread_call_without_gvl` 経由で関数を呼ぶ。`Fiddle::Importer#extern` は `need_gvl` を指定しないので、**既存の Zig 関数呼び出しは GVL を解放して実行されている**。
45
+ - したがって構想メモの「GVL 解放のための C 拡張化」は、Fiddle 経由でも同じ効果が得られる。C 拡張にすると Ruby のマイナーバージョンごとにバイナリが必要になるデメリットの方が大きい。
46
+ - ただし Fiddle の GVL 解放ではキャンセル(unblock 関数)が指定できない。TUI から中断できるようにするため、展開・圧縮は既存スキャナと同じ非同期ハンドル方式を採る。
47
+
48
+ ### 2.4 Ruby 標準 zlib でできること
49
+ - 構想メモの「zlib は ZIP 構造を扱えない」は正しいが、ZIP のコンテナ構造(EOCD / Central Directory / Local Header)は数百行の Ruby で扱える(Phase 1 で実装)。
50
+ - `Zlib::Inflate.new(-Zlib::MAX_WBITS)` / `Zlib::Deflate.new(level, -Zlib::MAX_WBITS)` で raw DEFLATE を扱え、`Zlib.crc32` もある。つまり **Pure Ruby + zlib だけで Store / Deflate の展開・作成まで実装できる**。ネイティブ実装は「速度のための加速器」という位置づけになる。
51
+
52
+ ---
53
+
54
+ ## 3. 実装内容(このブランチ)
55
+
56
+ ### 3.1 `Rufio::ZipArchive`(`lib/rufio/zip_archive.rb`)
57
+ 読み取り専用。ファイル本体は読まず、末尾と Central Directory だけを読む。
58
+
59
+ - EOCD の後方探索(末尾 22 + 65535 バイト)。コメント長が整合し、かつ指す先に Central Directory シグネチャがある候補のみ採用(コメント内の偽シグネチャ対策)
60
+ - ZIP64(ZIP64 EOCD Locator / ZIP64 EOCD / ZIP64 拡張フィールド 0x0001)
61
+ - ファイル名のエンコーディング: 汎用フラグ bit 11 → UTF-8、Info-ZIP Unicode Path 拡張(0x7075、CRC 一致時のみ)、UTF-8 として不正なら CP932(Windows 日本語環境で作られた ZIP)として解釈、それも失敗したら置換文字
62
+ - DOS 日時の変換、ディレクトリ・暗号化フラグの判定
63
+ - `limit:` で先頭 N 件だけ読む(プレビュー用。総数は EOCD から得る)
64
+ - 範囲外オフセット・切り詰められたファイルは `ZipArchive::FormatError`
65
+ - マルチディスク(分割)ZIP は非対応
66
+
67
+ ### 3.2 プレビュー統合
68
+ - `FilePreview#preview_file` で拡張子 `.zip` または先頭が `PK\x03\x04` / `PK\x05\x06` のファイルを `type: "archive"` として一覧表示(最大 200 件、超過分は件数のみ)
69
+ - 壊れた ZIP は `Invalid ZIP archive: <理由>` をエラー表示
70
+ - `UIRenderer#extract_preview_lines` が `archive` を表示対象に追加
71
+
72
+ ### 3.3 テスト(`test/test_zip_archive.rb`)
73
+ フィクスチャはテスト内の最小 ZIP ライタで生成し、外部コマンドに依存しない。加えて `zip` / `unzip` がある環境では `unzip -Z1` の一覧と完全一致することを確認する(ない環境では skip)。
74
+
75
+ ### 3.4 計測(参考値)
76
+ 環境: このクラウドコンテナ(Linux x86_64, Ruby 3.3.6)、`zip -qr` で作成した 20,001 エントリ / 3.2MB の ZIP。各 5 回平均。
77
+
78
+ | 処理 | 時間 |
79
+ |------|------|
80
+ | `ZipArchive.list`(全 20,001 件) | 176 ms |
81
+ | `ZipArchive.list(limit: 200)` | 1.9 ms |
82
+ | `FilePreview#preview_file`(200 件表示) | 1.9 ms |
83
+ | 参考: `unzip -l`(全件を標準出力へ、`time` の real) | 55 ms |
84
+
85
+ プレビュー用途では Pure Ruby で十分速い。ネイティブ化の効果が出るのは「全件走査」と「展開・圧縮(データ本体の処理)」である。
86
+
87
+ ---
88
+
89
+ ### 3.5 Phase 2: 展開・作成(Pure Ruby)
90
+ - `Rufio::ZipExtractor`(`lib/rufio/zip_extractor.rb`): Stored / Deflate の展開。安全対策は 4.2 をすべて実装。書き込み前に全エントリを検証し、危険なエントリが 1 件でもあれば何も書かない。進捗コールバックとキャンセルに対応。
91
+ - `Rufio::ZipWriter`(`lib/rufio/zip_writer.rb`): ファイル・ディレクトリから ZIP を作成。UTF-8 名、縮まないファイルは Stored、ZIP64(4GiB 超・65535 件超)、シンボリックリンクと出力先自身はスキップ、一時ファイル経由で失敗時に残骸を残さない。
92
+ - テスト: `test/test_zip_extractor.rb` / `test/test_zip_writer.rb`。`zip` コマンド製(データディスクリプタ付きを含む)の展開、作成物の `unzip -t` 通過、rubyzip での読み取り、70,000 件での ZIP64 を確認。
93
+
94
+ ## 4. 計画
95
+
96
+ | Phase | 内容 | 実装 | 備考 |
97
+ |-------|------|------|------|
98
+ | 2 | Store / Deflate の展開・作成 | Pure Ruby + zlib | **実装済み**。ネイティブ版のテストオラクルにする |
99
+ | 3 | Zig コア(展開・圧縮のデータ処理) | Zig + Fiddle | **実装済み**。同じテストを両実装で回す |
100
+ | 4 | 並列展開・並列圧縮 | Zig(ワーカースレッド) | **実装済み**。最大 16 スレッド、既定は CPU 数 |
101
+ | 5 | 配布・ベンチマーク | — | **実装済み**。`make dist` で 8 プラットフォーム分を同梱、`REQUIRED_EXPORTS` に `core_zip_*` を追加 |
102
+
103
+ ### 4.1 TUI への組み込み(実装済み)
104
+ - Files モードで `Space` で選択してから `z`。選択がすべて `.zip` なら展開、それ以外は圧縮(`lib/rufio/archive_controller.rb`)
105
+ - 展開: 確認ダイアログの後、各 ZIP を現在のディレクトリの `<名前>/` に展開(既にあれば `<名前> 2/` …)。失敗・キャンセル時は作った展開先を削除する
106
+ - 圧縮: アーカイブ名を入力(空 Enter で 1 件なら `<名前>.zip`、複数なら `Archive.zip`)。同名ファイルがあればエラー通知
107
+ - `..` は対象外
108
+ - 選択がなければ `z` は従来どおり zoxide(キー設定 `zoxide` を共用)
109
+ - 処理は `JobManager` のジョブとしてバックグラウンドスレッドで実行。ジョブ名に 5% 刻みで進捗を表示し、Jobs モードのキャンセルで中断できる。完了時に通知し、表示中のディレクトリなら一覧を再読み込みする
110
+ - 暗号化エントリはスキップ(決定事項)
111
+
112
+ ### 4.2 安全性要件(実装済み。Ruby と Zig で共通)
113
+ - **Zip Slip**: `..`、絶対パス、Windows のドライブレター・`\` 区切りを含むエントリを拒否し、展開先ディレクトリ外に書かない
114
+ - **シンボリックリンク**: 外部属性で symlink を示すエントリは展開しない。展開先に既にあるシンボリックリンクも辿らない
115
+ - **ZIP 爆弾**: 展開後合計サイズ・圧縮率・エントリ数の上限。Central Directory 上のサイズを信用せず、実際に書いたバイト数で打ち切る。Local Header の範囲が重なるエントリ(重複参照型の爆弾)を拒否
116
+ - **CRC-32 検証**: 不一致なら展開したファイルを削除してエラー
117
+ - **既存ファイルの上書き**: 既定は拒否(`overwrite: true` で上書き)。UI では確認ダイアログを出す想定
118
+ - **ファイルモード**: 実行権限だけを引き継ぎ、setuid / setgid / sticky と他者書き込み権限は付けない
119
+ - 暗号化エントリ(ZipCrypto / AES)は初期は非対応としてスキップ
120
+
121
+ ### 4.3 Zig エンジン(実装済み)
122
+ `lib_zig/rufio_native/src/zip.zig`。役割分担は次のとおりで、安全性の検証は Ruby 実装と共通にした。
123
+
124
+ | 処理 | 担当 |
125
+ |------|------|
126
+ | Central Directory の解析、パス正規化、上限・重複範囲・シンボリックリンクの検証、ディレクトリ作成 | Ruby |
127
+ | ZIP 内のデータ範囲を読み、伸長し、宣言サイズ超過で打ち切り、CRC-32 を検証して一時ファイルに書く | Zig(エントリ単位で並列) |
128
+ | 一時ファイルの rename、権限・日時の設定、失敗時の一時ファイル削除 | Ruby |
129
+ | ファイルを raw DEFLATE で一時ファイルへ圧縮し、CRC-32 とサイズを返す(1MiB 以上のファイルのみ) | Zig(ファイル単位で並列) |
130
+ | ローカルヘッダ・Central Directory・ZIP64 を書いてアーカイブを組み立てる | Ruby |
131
+
132
+ - エクスポート: `core_zip_extract_create` / `core_zip_deflate_create` / `core_zip_state` / `core_zip_progress` / `core_zip_status` / `core_zip_deflate_result` / `core_zip_cancel` / `core_zip_destroy`
133
+ - Ruby 側は `NativeScannerZigFFI.zip_available?` で関数グループの有無を判定し、ない場合(古いバイナリ)や `RUFIO_NATIVE_ZIP=0` では Pure Ruby を使う
134
+ - DEFLATE は Zig 標準ライブラリの `std.compress.flate`、CRC-32 は `std.hash.Crc32`。libdeflate は同梱していない
135
+ - ファイル操作は `os.zig` に追加(Linux は直接システムコール、macOS は libc、Windows は Win32。位置指定読み込み・排他作成・書き込み)
136
+ - `flate.Compress` は約 230KB あり初期化時にスタックへ載るため、ワーカースレッドのスタックは 4MB
137
+ - 1MiB 未満のファイルの圧縮は Ruby の zlib で行う。小さいファイルを Zig に渡すと、一時ファイルの作成・コピー・削除の分だけ遅くなった(2 万ファイルで 0.98 秒 → 5.26 秒)ため
138
+ - テスト: `test/test_zip_extractor.rb` / `test/test_zip_writer.rb` の全テストを `TestZipExtractorNative` / `TestZipWriterNative` で Zig 実装でも実行。Debug ビルド(安全チェック有効)でも全件通過を確認
139
+
140
+ ### 4.4 計測(このクラウドコンテナ、4 CPU、Linux x86_64、ReleaseFast、各 3 回の最小値)
141
+
142
+ | データ | 処理 | Ruby | Zig | zip / unzip |
143
+ |--------|------|------|-----|-------------|
144
+ | 8 ファイル × 64MB のテキスト | 圧縮 | 15.36 秒 | 3.34 秒 | 20.20 秒 |
145
+ | 同上 | 展開 | 3.03 秒 | 0.94 秒 | 4.11 秒 |
146
+ | 2 万ファイル・計 79MB | 圧縮 | 0.93 秒 | 0.90 秒 | 0.30 秒 |
147
+ | 同上 | 展開 | 5.82 秒 | 5.00 秒 | 4.62 秒 |
148
+
149
+ - 圧縮後サイズは Zig の方がやや大きい(512MB → Ruby/zlib 67MB、Zig 71MB)。同じレベル指定でも実装が異なるため
150
+ - 小さいファイル大量の展開はファイル作成が律速で、計測ごとのばらつきが大きい(`sync` を挟まない計測では unzip 1.4〜1.8 秒、Ruby/Zig 3〜6 秒)。ファイルごとの Ruby 側処理(rename・chmod・utime)が残るため、unzip との差は縮め切れていない
151
+
152
+ ## 5. 技術的な注意点(構想メモへのコメント)
153
+
154
+ - **libdeflate はストリーミング API を持たない**(バッファ全体を一度に処理する設計)。巨大エントリを扱うにはエントリ全体をメモリに載せるか mmap が必要。メモリ上限の設計とセットで検討する。ライセンスは MIT。
155
+ - **CRC-32**: 自前の SIMD 実装より、libdeflate の `libdeflate_crc32`(CPU 機能に応じた高速版を内部で選択)を使う方が保守コストが低い。
156
+ - **mmap**: POSIX と Windows で API が異なる。Windows 対応を続けるなら抽象化が必要(rufio は Windows 対応の修正を継続的に入れている)。
157
+ - **Zig のバージョン固定**: 既存 `build.zig` は `addExecutable(.{ .root_source_file = ... })` の書式。Zig はリリースごとにビルド API・標準ライブラリ(`std.compress.flate` 含む)が変わっているため、採用前に対象バージョンを決めて動作確認する必要がある(本検討では未確認)。
158
+ - **圧縮率**: 構想メモの通り DEFLATE は 32KB 窓・エントリ単位圧縮なので 7z には勝てない。比較軸は速度と OS 標準互換性に絞る。
159
+
160
+ ---
161
+
162
+ ## 6. 要判断事項
163
+ 1. **独立 Gem 化のタイミング**: rufio 内で安定させてからか、`fast_zip` として切り出すか
164
+
165
+ ## 7. 未確認事項
166
+ - Zig 版は Linux x86_64(このコンテナ)でのみ実行・計測した。macOS / Windows / aarch64 向けはクロスコンパイルとバイナリ形式・エクスポートの検証(`rake native:check`)までで、実機では動かしていない。特に Windows のファイル操作(`CreateFileW` / 位置指定 `ReadFile`)は未実行
167
+ - 計測は 1 環境・1 種類のデータのみ。CPU 数やストレージによって倍率は変わる
data/info/help.md CHANGED
@@ -48,6 +48,7 @@
48
48
  | `m` | Move selected items to current directory |
49
49
  | `c` | Copy selected items to current directory |
50
50
  | `x` | Delete selected items |
51
+ | `z` | With selected items: extract when all are `.zip` files (each into `<name>/`), otherwise compress them into one `.zip` (runs as a job). Without a selection, `z` opens zoxide history |
51
52
 
52
53
  ### Filtering & Search
53
54
 
@@ -132,6 +133,7 @@ Press `B` then select script path management to:
132
133
  | `?` | Enter help mode (this mode) |
133
134
  | `ESC` | Exit current mode |
134
135
  | `q` | Quit rufio |
136
+ | `T` | Quit rufio and cd the shell to the current directory (requires `rufio --shell-init`) |
135
137
 
136
138
  ### Help Mode
137
139
 
data/info/keybindings.md CHANGED
@@ -33,6 +33,7 @@ Complete reference of all keyboard shortcuts in **rufio**.
33
33
  | `m` | Move selected items to current directory |
34
34
  | `c` | Copy selected items to current directory |
35
35
  | `x` | Delete selected items |
36
+ | `z` | Extract selected `.zip` files / compress selected items into a `.zip` |
36
37
 
37
38
  ## Filtering & Search
38
39
 
@@ -50,7 +51,7 @@ Complete reference of all keyboard shortcuts in **rufio**.
50
51
  | `B` | Bookmark menu (view/add/remove/script paths) |
51
52
  | `0` | Jump to startup directory |
52
53
  | `1-9` | Jump to bookmark 1-9 |
53
- | `z` | Navigate using zoxide history |
54
+ | `z` | Navigate using zoxide history (when nothing is selected) |
54
55
 
55
56
  ## Command Mode & Tool Runtime
56
57
 
@@ -69,6 +70,7 @@ Complete reference of all keyboard shortcuts in **rufio**.
69
70
  | `?` | Enter help mode |
70
71
  | `ESC` | Exit current mode (help/filter/command) |
71
72
  | `q` | Quit rufio |
73
+ | `T` | Quit rufio and cd the shell to the current directory (requires `rufio --shell-init`) |
72
74
 
73
75
  ---
74
76
 
data/info/welcome.md CHANGED
@@ -24,7 +24,7 @@ A TUI file manager that executes and coordinates tools and scripts from files.
24
24
  | `s` | Search with fzf |
25
25
  | `b` | Add bookmark |
26
26
  | `B` | Bookmark menu |
27
- | `z` | zoxide history |
27
+ | `z` | zoxide history / extract or compress selected items |
28
28
  | `:` | Command mode |
29
29
  | `J` | Job mode |
30
30
  | `?` | Help mode |