beniya 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG_v0.8.0.md +267 -0
- data/README.md +75 -3
- data/bin/beniya +3 -0
- data/docs/PLUGIN_GUIDE.md +431 -0
- data/docs/plugin_example.rb +119 -0
- data/lib/beniya/command_mode.rb +72 -0
- data/lib/beniya/command_mode_ui.rb +122 -0
- data/lib/beniya/keybind_handler.rb +8 -0
- data/lib/beniya/terminal_ui.rb +105 -2
- data/lib/beniya/version.rb +1 -1
- data/lib/beniya.rb +2 -0
- metadata +7 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d90ab1b62a7576283298109004873e5add5aafc1ddf15d545c426ee9103a09ba
|
|
4
|
+
data.tar.gz: 425added333fc0722338e830f362a16d0e2e30c5f5228d7c84f837cdb0dc15d2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 56569d00c298f6ad30a22e216189013d9aad0a3a4fa9e6f850e037e09d5fec22ab47062209eef1f2f7b4c51ea6569bd818109ef2340416c2768401883e6e327c
|
|
7
|
+
data.tar.gz: d671303c256de4bf66383dd59b9bf8a20f7d2e0c0d480c20024245539dd9caaffe8ad4f8f2cd807220391c67ccf0a546d46cf4635629d5b4c8ffbc9739000d01
|
data/CHANGELOG_v0.8.0.md
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
# CHANGELOG - beniya v0.8.0
|
|
2
|
+
|
|
3
|
+
**Release Date**: 2024-12-06
|
|
4
|
+
|
|
5
|
+
## 🚀 New Features
|
|
6
|
+
|
|
7
|
+
### Command Mode UI Enhancements
|
|
8
|
+
|
|
9
|
+
- **Tab Completion for Commands**: Added intelligent tab completion in command mode
|
|
10
|
+
- Auto-completes available commands as you type
|
|
11
|
+
- Shows common prefix when multiple matches exist
|
|
12
|
+
- Complete match is filled automatically when only one option exists
|
|
13
|
+
- Activated by pressing `Tab` key during command input
|
|
14
|
+
|
|
15
|
+
- **Floating Window for Command Results**: Command execution results now display in an elegant floating window
|
|
16
|
+
- Reuses DialogRenderer from bookmark feature for consistent UI
|
|
17
|
+
- Color-coded results:
|
|
18
|
+
- **Green border**: Successful command execution
|
|
19
|
+
- **Red border**: Error or warning messages
|
|
20
|
+
- Dismissible with any key press
|
|
21
|
+
- Auto-sizes based on content length
|
|
22
|
+
- Centered on screen for optimal visibility
|
|
23
|
+
|
|
24
|
+
### CommandModeUI Class
|
|
25
|
+
|
|
26
|
+
- **New UI Handler Class** (`lib/beniya/command_mode_ui.rb`): Dedicated class for command mode user interactions
|
|
27
|
+
- `autocomplete(input)`: Returns list of matching command suggestions
|
|
28
|
+
- `complete_command(input)`: Completes command based on current input
|
|
29
|
+
- `show_result(result)`: Displays command results in floating window
|
|
30
|
+
- `find_common_prefix(strings)`: Finds common prefix among multiple strings (private helper)
|
|
31
|
+
|
|
32
|
+
## 🎨 UI/UX Improvements
|
|
33
|
+
|
|
34
|
+
### Enhanced Command Mode Experience
|
|
35
|
+
|
|
36
|
+
- **Intuitive Tab Completion**:
|
|
37
|
+
- Type partial command name (e.g., `he`)
|
|
38
|
+
- Press `Tab` to see all matches or complete automatically
|
|
39
|
+
- Works with all registered plugin commands
|
|
40
|
+
|
|
41
|
+
- **Visual Feedback**:
|
|
42
|
+
- Immediate visual feedback with floating windows
|
|
43
|
+
- Clear distinction between success and error states
|
|
44
|
+
- Professional-looking centered dialogs
|
|
45
|
+
|
|
46
|
+
- **Consistent Design**:
|
|
47
|
+
- Matches bookmark feature's floating window style
|
|
48
|
+
- Maintains beniya's cohesive visual language
|
|
49
|
+
- Uses existing color schemes and borders
|
|
50
|
+
|
|
51
|
+
## 🔧 Technical Improvements
|
|
52
|
+
|
|
53
|
+
### Architecture
|
|
54
|
+
|
|
55
|
+
- **CommandModeUI Integration**:
|
|
56
|
+
- Integrated into `TerminalUI` class initialization
|
|
57
|
+
- Uses existing `DialogRenderer` for window management
|
|
58
|
+
- Modular design allows for easy future enhancements
|
|
59
|
+
|
|
60
|
+
- **Input Handling**:
|
|
61
|
+
- Added `Tab` key handler in `handle_command_input`
|
|
62
|
+
- Improved command execution flow with visual feedback
|
|
63
|
+
- Screen refresh after command execution for clean state
|
|
64
|
+
|
|
65
|
+
### Code Organization
|
|
66
|
+
|
|
67
|
+
- **Clear Separation of Concerns**:
|
|
68
|
+
- `CommandMode`: Command execution logic
|
|
69
|
+
- `CommandModeUI`: User interface and interaction
|
|
70
|
+
- `DialogRenderer`: Reusable window rendering component
|
|
71
|
+
|
|
72
|
+
- **Dependency Injection**:
|
|
73
|
+
- CommandModeUI receives CommandMode and DialogRenderer via constructor
|
|
74
|
+
- Facilitates testing and maintainability
|
|
75
|
+
|
|
76
|
+
## 🧪 Testing
|
|
77
|
+
|
|
78
|
+
### Test-Driven Development
|
|
79
|
+
|
|
80
|
+
- **TDD Approach**: Features developed following strict TDD methodology
|
|
81
|
+
1. Wrote comprehensive tests first
|
|
82
|
+
2. Verified tests failed as expected
|
|
83
|
+
3. Implemented features to pass tests
|
|
84
|
+
4. Committed tests before implementation
|
|
85
|
+
|
|
86
|
+
### New Tests Added
|
|
87
|
+
|
|
88
|
+
- **CommandModeUI Tests** (`test/test_command_mode_ui.rb`):
|
|
89
|
+
- **Tab Completion Tests**:
|
|
90
|
+
- `test_autocomplete_no_input`: All commands when no input
|
|
91
|
+
- `test_autocomplete_partial_match`: Partial matching
|
|
92
|
+
- `test_autocomplete_exact_prefix`: Prefix filtering
|
|
93
|
+
- `test_autocomplete_single_match`: Single match completion
|
|
94
|
+
- `test_autocomplete_no_match`: No matches handling
|
|
95
|
+
- `test_complete_command_single_match`: Single match auto-completion
|
|
96
|
+
- `test_complete_command_multiple_matches`: Common prefix completion
|
|
97
|
+
- `test_complete_command_no_match`: Preserves input when no match
|
|
98
|
+
|
|
99
|
+
- **Floating Window Tests**:
|
|
100
|
+
- `test_show_result_success`: Success message display
|
|
101
|
+
- `test_show_result_error`: Error message display with red color
|
|
102
|
+
- `test_show_result_multiline`: Multi-line result handling
|
|
103
|
+
- `test_show_result_nil`: Nil result handling (no display)
|
|
104
|
+
- `test_show_result_empty_string`: Empty string handling (no display)
|
|
105
|
+
|
|
106
|
+
- **Integration Tests**:
|
|
107
|
+
- `test_command_mode_ui_class_exists`: Class existence verification
|
|
108
|
+
- `test_command_mode_ui_initialization`: Proper initialization
|
|
109
|
+
- `test_prompt_command_with_autocomplete`: End-to-end autocomplete flow
|
|
110
|
+
|
|
111
|
+
### Test Coverage
|
|
112
|
+
|
|
113
|
+
- **16 tests, 44 assertions**: Comprehensive coverage of all features
|
|
114
|
+
- **All tests passing**: 100% success rate
|
|
115
|
+
- **Mock-based testing**: Uses stubbing for UI components to avoid side effects
|
|
116
|
+
|
|
117
|
+
## 🐛 Bug Fixes
|
|
118
|
+
|
|
119
|
+
### Fixed Issues
|
|
120
|
+
|
|
121
|
+
- **Method Name Error**: Fixed incorrect method call `draw` → `draw_screen`
|
|
122
|
+
- Issue: `undefined local variable or method 'draw'` error in `execute_command`
|
|
123
|
+
- Fix: Changed to correct method name `draw_screen` for screen refresh
|
|
124
|
+
- Location: `lib/beniya/terminal_ui.rb:626`
|
|
125
|
+
|
|
126
|
+
## 📦 Dependencies
|
|
127
|
+
|
|
128
|
+
### No New Dependencies
|
|
129
|
+
|
|
130
|
+
- Uses existing Ruby standard library
|
|
131
|
+
- Leverages existing beniya components:
|
|
132
|
+
- `DialogRenderer`: For floating windows
|
|
133
|
+
- `CommandMode`: For command execution
|
|
134
|
+
- `TextUtils`: For text width calculations
|
|
135
|
+
|
|
136
|
+
## 🔄 Compatibility
|
|
137
|
+
|
|
138
|
+
### Backward Compatibility
|
|
139
|
+
|
|
140
|
+
- **No Breaking Changes**: All existing features work as before
|
|
141
|
+
- **Optional Enhancement**: New features enhance but don't replace existing functionality
|
|
142
|
+
- **Existing Keybindings Preserved**: `:` still activates command mode as before
|
|
143
|
+
|
|
144
|
+
### Platform Support
|
|
145
|
+
|
|
146
|
+
- **macOS**: Full support ✅
|
|
147
|
+
- **Linux**: Full support ✅
|
|
148
|
+
- **Windows**: Full support ✅
|
|
149
|
+
|
|
150
|
+
## ⚡ Performance
|
|
151
|
+
|
|
152
|
+
### Optimizations
|
|
153
|
+
|
|
154
|
+
- **Minimal Overhead**: Tab completion uses simple prefix matching (O(n) complexity)
|
|
155
|
+
- **Efficient Rendering**: Reuses existing DialogRenderer without new allocation
|
|
156
|
+
- **No Performance Impact**: Features only active when command mode is engaged
|
|
157
|
+
|
|
158
|
+
## 📝 Usage Examples
|
|
159
|
+
|
|
160
|
+
### Using Tab Completion
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
# Launch beniya
|
|
164
|
+
beniya
|
|
165
|
+
|
|
166
|
+
# Activate command mode
|
|
167
|
+
Press ':'
|
|
168
|
+
|
|
169
|
+
# Type partial command
|
|
170
|
+
:he
|
|
171
|
+
|
|
172
|
+
# Press Tab to see completions
|
|
173
|
+
Press 'Tab'
|
|
174
|
+
# Result: :he (if multiple matches like "hello", "help", "health")
|
|
175
|
+
|
|
176
|
+
# Type more specific prefix
|
|
177
|
+
:hel
|
|
178
|
+
|
|
179
|
+
# Press Tab again
|
|
180
|
+
Press 'Tab'
|
|
181
|
+
# Result: :hello or :help (completes to common prefix)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Viewing Command Results
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
# After activating command mode (:)
|
|
188
|
+
:hello
|
|
189
|
+
|
|
190
|
+
# Press Enter
|
|
191
|
+
# → Floating window appears with result:
|
|
192
|
+
# ┌────────────────────────────────┐
|
|
193
|
+
# │ コマンド実行結果 │
|
|
194
|
+
# ├────────────────────────────────┤
|
|
195
|
+
# │ │
|
|
196
|
+
# │ Hello from TestPlugin! │
|
|
197
|
+
# │ │
|
|
198
|
+
# │ Press any key to close │
|
|
199
|
+
# └────────────────────────────────┘
|
|
200
|
+
|
|
201
|
+
# Press any key to dismiss
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Error Handling
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
# Try non-existent command
|
|
208
|
+
:nonexistent
|
|
209
|
+
|
|
210
|
+
# Press Enter
|
|
211
|
+
# → Red-bordered floating window appears:
|
|
212
|
+
# ┌────────────────────────────────┐
|
|
213
|
+
# │ コマンド実行結果 │ (Red)
|
|
214
|
+
# ├────────────────────────────────┤
|
|
215
|
+
# │ │
|
|
216
|
+
# │ ⚠️ コマンドが見つかりません │
|
|
217
|
+
# │ │
|
|
218
|
+
# │ Press any key to close │
|
|
219
|
+
# └────────────────────────────────┘
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## 🔮 Future Plans
|
|
223
|
+
|
|
224
|
+
### Planned Enhancements
|
|
225
|
+
|
|
226
|
+
- **Command History**: Navigate through previously executed commands with ↑/↓ arrows
|
|
227
|
+
- **Command Arguments**: Support for commands with parameters
|
|
228
|
+
- **Auto-complete Menu**: Visual menu showing all available completions
|
|
229
|
+
- **Command Aliases**: User-defined shortcuts for frequently used commands
|
|
230
|
+
- **Command Help**: Inline help text showing command descriptions during completion
|
|
231
|
+
|
|
232
|
+
## 🙏 Acknowledgments
|
|
233
|
+
|
|
234
|
+
Main contributions in this version:
|
|
235
|
+
|
|
236
|
+
- **Test-Driven Development**: Strict TDD methodology ensuring code quality
|
|
237
|
+
- **Reusable Components**: Leveraged existing DialogRenderer for consistency
|
|
238
|
+
- **User Experience Focus**: Tab completion improves command discoverability
|
|
239
|
+
- **Visual Polish**: Floating windows provide professional command feedback
|
|
240
|
+
|
|
241
|
+
## 📋 Detailed Changes
|
|
242
|
+
|
|
243
|
+
### Files Modified
|
|
244
|
+
|
|
245
|
+
- `lib/beniya.rb`: Added `require_relative "beniya/command_mode_ui"`
|
|
246
|
+
- `lib/beniya/terminal_ui.rb`:
|
|
247
|
+
- Added `@dialog_renderer` and `@command_mode_ui` initialization
|
|
248
|
+
- Added Tab key handler for command completion
|
|
249
|
+
- Changed command result display to use floating windows
|
|
250
|
+
- Fixed `draw_screen` method call
|
|
251
|
+
- `test/test_command_mode_ui.rb`: Added comprehensive test suite (16 tests)
|
|
252
|
+
|
|
253
|
+
### Files Created
|
|
254
|
+
|
|
255
|
+
- `lib/beniya/command_mode_ui.rb`: New CommandModeUI class (130 lines)
|
|
256
|
+
|
|
257
|
+
### Lines of Code
|
|
258
|
+
|
|
259
|
+
- **Added**: ~260 lines (implementation + tests)
|
|
260
|
+
- **Modified**: ~15 lines
|
|
261
|
+
- **Removed**: ~3 lines
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
**Note**: This version significantly improves the command mode user experience with modern UI features. The Tab completion and floating window display make command execution more intuitive and visually appealing.
|
|
266
|
+
|
|
267
|
+
**GitHub Issues**: [https://github.com/masisz/beniya/issues](https://github.com/masisz/beniya/issues)
|
data/README.md
CHANGED
|
@@ -13,6 +13,7 @@ beniyaは、Yaziにインスパイアされたターミナル上で動作する
|
|
|
13
13
|
- **軽量でシンプル**: Rubyで書かれた軽量なファイルマネージャー
|
|
14
14
|
- **直感的な操作**: Vimライクなキーバインド
|
|
15
15
|
- **プラグインシステム**: 拡張可能なプラグインアーキテクチャ
|
|
16
|
+
- **コマンドモード**: Tab補完とフローティングウィンドウを備えた強力なコマンドシステム
|
|
16
17
|
- **ファイルプレビュー**: テキストファイルの内容をその場で確認
|
|
17
18
|
- **ファイル選択・操作**: 複数ファイルの選択、移動、コピー、削除が可能
|
|
18
19
|
- **ベースディレクトリ操作**: 起動ディレクトリへの一括ファイル移動・コピー
|
|
@@ -118,6 +119,15 @@ beniya --help # ヘルプメッセージを表示
|
|
|
118
119
|
| ---- | ---------------------------------- |
|
|
119
120
|
| `z` | zoxide履歴からディレクトリを選択移動 |
|
|
120
121
|
|
|
122
|
+
#### コマンドモード
|
|
123
|
+
|
|
124
|
+
| キー | 機能 |
|
|
125
|
+
| ------ | ---------------------------------------- |
|
|
126
|
+
| `:` | コマンドモードを起動 |
|
|
127
|
+
| `Tab` | コマンド補完(コマンドモード中) |
|
|
128
|
+
| `Enter`| コマンドを実行(コマンドモード中) |
|
|
129
|
+
| `ESC` | コマンドモードをキャンセル(コマンドモード中) |
|
|
130
|
+
|
|
121
131
|
#### システム操作
|
|
122
132
|
|
|
123
133
|
| キー | 機能 |
|
|
@@ -272,6 +282,46 @@ apt install zoxide
|
|
|
272
282
|
- zoxideが無効な場合は適切なメッセージが表示されます
|
|
273
283
|
- 履歴が空の場合も適切にハンドリングされます
|
|
274
284
|
|
|
285
|
+
### コマンドモードの詳細
|
|
286
|
+
|
|
287
|
+
#### コマンドモードの起動 (`:`)
|
|
288
|
+
|
|
289
|
+
- `:`キーを押してコマンドモードを起動
|
|
290
|
+
- 画面最下部にコマンド入力欄が表示される
|
|
291
|
+
- プラグインが提供するコマンドを実行できる
|
|
292
|
+
|
|
293
|
+
#### Tab補完機能
|
|
294
|
+
|
|
295
|
+
- **インテリジェントな補完**: コマンド名の一部を入力して`Tab`キーを押すと自動補完
|
|
296
|
+
- **複数候補の処理**: 複数のコマンドが一致する場合は共通プレフィックスまで補完
|
|
297
|
+
- **単一候補の自動完成**: 一つだけマッチする場合は完全に補完される
|
|
298
|
+
- **リアルタイム更新**: 入力内容に応じて候補が動的に変化
|
|
299
|
+
|
|
300
|
+
#### フローティングウィンドウでの結果表示
|
|
301
|
+
|
|
302
|
+
- **視覚的フィードバック**: コマンド実行結果をモダンなフローティングウィンドウで表示
|
|
303
|
+
- **色分けされた結果**:
|
|
304
|
+
- **緑色のボーダー**: コマンドが正常に実行された場合
|
|
305
|
+
- **赤色のボーダー**: エラーまたは警告が発生した場合
|
|
306
|
+
- **中央配置**: 画面中央に自動的に配置され視認性が高い
|
|
307
|
+
- **自動サイズ調整**: 結果の内容に応じてウィンドウサイズが自動調整
|
|
308
|
+
- **簡単に閉じる**: 任意のキーを押すとウィンドウを閉じて通常操作に戻る
|
|
309
|
+
|
|
310
|
+
#### 使用例
|
|
311
|
+
|
|
312
|
+
```
|
|
313
|
+
1. : → コマンドモードを起動
|
|
314
|
+
2. "he" → コマンド名の一部を入力
|
|
315
|
+
3. Tab → 補完("hello", "help", "health"などが候補)
|
|
316
|
+
4. Enter → コマンドを実行
|
|
317
|
+
5. フローティングウィンドウで結果を確認
|
|
318
|
+
6. 任意のキーを押してウィンドウを閉じる
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
#### 利用可能なコマンド
|
|
322
|
+
|
|
323
|
+
コマンドはプラグインによって提供されます。プラグインシステムの詳細については後述の「プラグインシステム」セクションを参照してください。
|
|
324
|
+
|
|
275
325
|
### 必要な外部ツール
|
|
276
326
|
|
|
277
327
|
検索機能・履歴機能を使用するには、以下のツールが必要です:
|
|
@@ -396,13 +446,21 @@ module Beniya
|
|
|
396
446
|
private
|
|
397
447
|
|
|
398
448
|
def say_hello
|
|
399
|
-
|
|
449
|
+
"Hello from beniya!"
|
|
400
450
|
end
|
|
401
451
|
end
|
|
402
452
|
end
|
|
403
453
|
end
|
|
404
454
|
```
|
|
405
455
|
|
|
456
|
+
**プラグインの使い方:**
|
|
457
|
+
|
|
458
|
+
1. beniyaを起動
|
|
459
|
+
2. `:`キーでコマンドモードを起動
|
|
460
|
+
3. `hello`と入力(または`he`と入力してTabキーで補完)
|
|
461
|
+
4. Enterキーで実行
|
|
462
|
+
5. フローティングウィンドウに"Hello from beniya!"が表示される
|
|
463
|
+
|
|
406
464
|
#### 外部gemに依存するプラグイン例
|
|
407
465
|
|
|
408
466
|
```ruby
|
|
@@ -435,14 +493,28 @@ module Beniya
|
|
|
435
493
|
|
|
436
494
|
private
|
|
437
495
|
|
|
438
|
-
def ask_ai
|
|
439
|
-
|
|
496
|
+
def ask_ai
|
|
497
|
+
response = @client.messages.create(
|
|
498
|
+
model: "claude-3-5-sonnet-20241022",
|
|
499
|
+
max_tokens: 1024,
|
|
500
|
+
messages: [{role: "user", content: "Hello, Claude!"}]
|
|
501
|
+
)
|
|
502
|
+
response.content.first.text
|
|
440
503
|
end
|
|
441
504
|
end
|
|
442
505
|
end
|
|
443
506
|
end
|
|
444
507
|
```
|
|
445
508
|
|
|
509
|
+
**プラグインの使い方:**
|
|
510
|
+
|
|
511
|
+
1. 依存gemをインストール: `gem install anthropic`
|
|
512
|
+
2. 環境変数を設定: `export ANTHROPIC_API_KEY=your_api_key`
|
|
513
|
+
3. beniyaを起動
|
|
514
|
+
4. `:`キーでコマンドモードを起動
|
|
515
|
+
5. `ai`と入力してEnterキーで実行
|
|
516
|
+
6. フローティングウィンドウにClaude APIからの応答が表示される
|
|
517
|
+
|
|
446
518
|
### プラグインの管理
|
|
447
519
|
|
|
448
520
|
#### プラグインの有効/無効設定
|
data/bin/beniya
CHANGED
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
# Beniya プラグインガイド
|
|
2
|
+
|
|
3
|
+
Beniyaのプラグインシステムを使って、機能を拡張する方法を説明します。
|
|
4
|
+
|
|
5
|
+
## 目次
|
|
6
|
+
|
|
7
|
+
1. [プラグインの基本](#プラグインの基本)
|
|
8
|
+
2. [プラグインの作成](#プラグインの作成)
|
|
9
|
+
3. [プラグインの設定](#プラグインの設定)
|
|
10
|
+
4. [高度な機能](#高度な機能)
|
|
11
|
+
5. [トラブルシューティング](#トラブルシューティング)
|
|
12
|
+
|
|
13
|
+
## プラグインの基本
|
|
14
|
+
|
|
15
|
+
### プラグインとは?
|
|
16
|
+
|
|
17
|
+
プラグインは、Beniyaに新しい機能を追加するためのRubyモジュールです。
|
|
18
|
+
|
|
19
|
+
### プラグインの種類
|
|
20
|
+
|
|
21
|
+
1. **組み込みプラグイン**: Beniya本体に含まれる
|
|
22
|
+
- 場所: `lib/beniya/plugins/`
|
|
23
|
+
- 例: `FileOperations`
|
|
24
|
+
|
|
25
|
+
2. **ユーザープラグイン**: ユーザーが作成する
|
|
26
|
+
- 場所: `~/.beniya/plugins/`
|
|
27
|
+
- 自由にカスタマイズ可能
|
|
28
|
+
|
|
29
|
+
## プラグインの作成
|
|
30
|
+
|
|
31
|
+
### ステップ1: ディレクトリの準備
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# プラグインディレクトリを作成
|
|
35
|
+
mkdir -p ~/.beniya/plugins
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### ステップ2: プラグインファイルの作成
|
|
39
|
+
|
|
40
|
+
`~/.beniya/plugins/my_plugin.rb`を作成:
|
|
41
|
+
|
|
42
|
+
```ruby
|
|
43
|
+
# frozen_string_literal: true
|
|
44
|
+
|
|
45
|
+
module Beniya
|
|
46
|
+
module Plugins
|
|
47
|
+
class MyPlugin < Plugin
|
|
48
|
+
# プラグイン名(必須)
|
|
49
|
+
def name
|
|
50
|
+
"MyPlugin"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# 説明(オプション)
|
|
54
|
+
def description
|
|
55
|
+
"私のカスタムプラグイン"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# バージョン(オプション)
|
|
59
|
+
def version
|
|
60
|
+
"1.0.0"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# コマンドの定義(オプション)
|
|
64
|
+
def commands
|
|
65
|
+
{
|
|
66
|
+
hello: method(:say_hello)
|
|
67
|
+
}
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def say_hello
|
|
73
|
+
puts "Hello from MyPlugin!"
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### ステップ3: プラグインの読み込み
|
|
81
|
+
|
|
82
|
+
Beniyaを起動すると、自動的に`~/.beniya/plugins/`内のプラグインが読み込まれます。
|
|
83
|
+
|
|
84
|
+
## プラグインの設定
|
|
85
|
+
|
|
86
|
+
### 設定ファイルの作成
|
|
87
|
+
|
|
88
|
+
`~/.beniya/config.yml`:
|
|
89
|
+
|
|
90
|
+
```yaml
|
|
91
|
+
plugins:
|
|
92
|
+
# プラグイン名を小文字で指定
|
|
93
|
+
myplugin:
|
|
94
|
+
enabled: true
|
|
95
|
+
|
|
96
|
+
# 無効化したいプラグイン
|
|
97
|
+
fileoperations:
|
|
98
|
+
enabled: false
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 設定のルール
|
|
102
|
+
|
|
103
|
+
- **プラグイン名**: 大文字小文字を区別せず、小文字に統一されます
|
|
104
|
+
- `MyPlugin`, `myplugin`, `MYPLUGIN` → すべて `myplugin` として扱われます
|
|
105
|
+
- **デフォルト**: 設定に記載のないプラグインは**有効**です
|
|
106
|
+
- **enabled**: `true`で有効、`false`で無効
|
|
107
|
+
|
|
108
|
+
## 高度な機能
|
|
109
|
+
|
|
110
|
+
### 外部gemへの依存
|
|
111
|
+
|
|
112
|
+
プラグインが外部gemに依存する場合、`requires`を使用:
|
|
113
|
+
|
|
114
|
+
```ruby
|
|
115
|
+
module Beniya
|
|
116
|
+
module Plugins
|
|
117
|
+
class AdvancedPlugin < Plugin
|
|
118
|
+
# 依存するgemを宣言
|
|
119
|
+
requires 'httparty', 'nokogiri'
|
|
120
|
+
|
|
121
|
+
def name
|
|
122
|
+
"AdvancedPlugin"
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def description
|
|
126
|
+
"HTTPartyとNokogiriを使用"
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
private
|
|
130
|
+
|
|
131
|
+
def fetch_and_parse
|
|
132
|
+
require 'httparty'
|
|
133
|
+
require 'nokogiri'
|
|
134
|
+
|
|
135
|
+
response = HTTParty.get('https://example.com')
|
|
136
|
+
doc = Nokogiri::HTML(response.body)
|
|
137
|
+
# 処理...
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
gemが不足している場合、以下のメッセージが表示されます:
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
⚠️ Plugin 'AdvancedPlugin' は以下のgemに依存していますが、インストールされていません:
|
|
148
|
+
- httparty
|
|
149
|
+
- nokogiri
|
|
150
|
+
|
|
151
|
+
以下のコマンドでインストールしてください:
|
|
152
|
+
gem install httparty nokogiri
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### コマンドの定義
|
|
156
|
+
|
|
157
|
+
プラグインは複数のコマンドを提供できます:
|
|
158
|
+
|
|
159
|
+
```ruby
|
|
160
|
+
def commands
|
|
161
|
+
{
|
|
162
|
+
search: method(:search_files),
|
|
163
|
+
count: method(:count_files),
|
|
164
|
+
list: method(:list_files)
|
|
165
|
+
}
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
private
|
|
169
|
+
|
|
170
|
+
def search_files
|
|
171
|
+
# 検索処理
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def count_files
|
|
175
|
+
# カウント処理
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def list_files
|
|
179
|
+
# 一覧表示
|
|
180
|
+
end
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## プラグインの例
|
|
184
|
+
|
|
185
|
+
### 1. ファイル検索プラグイン
|
|
186
|
+
|
|
187
|
+
```ruby
|
|
188
|
+
module Beniya
|
|
189
|
+
module Plugins
|
|
190
|
+
class FileSearchPlugin < Plugin
|
|
191
|
+
def name
|
|
192
|
+
"FileSearch"
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def description
|
|
196
|
+
"ファイル名で検索"
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def commands
|
|
200
|
+
{
|
|
201
|
+
search: method(:search_files),
|
|
202
|
+
find_ext: method(:find_by_extension)
|
|
203
|
+
}
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
private
|
|
207
|
+
|
|
208
|
+
def search_files(query = "*")
|
|
209
|
+
Dir.glob("**/*#{query}*").each do |file|
|
|
210
|
+
puts file if File.file?(file)
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def find_by_extension(ext)
|
|
215
|
+
Dir.glob("**/*.#{ext}").each do |file|
|
|
216
|
+
puts file
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### 2. Git統合プラグイン
|
|
225
|
+
|
|
226
|
+
```ruby
|
|
227
|
+
module Beniya
|
|
228
|
+
module Plugins
|
|
229
|
+
class GitPlugin < Plugin
|
|
230
|
+
def name
|
|
231
|
+
"Git"
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def description
|
|
235
|
+
"Git操作の統合"
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def commands
|
|
239
|
+
{
|
|
240
|
+
status: method(:git_status),
|
|
241
|
+
branch: method(:current_branch),
|
|
242
|
+
log: method(:git_log)
|
|
243
|
+
}
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
private
|
|
247
|
+
|
|
248
|
+
def git_status
|
|
249
|
+
system('git status') if git_available?
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def current_branch
|
|
253
|
+
if git_available?
|
|
254
|
+
branch = `git branch --show-current`.strip
|
|
255
|
+
puts "ブランチ: #{branch}"
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def git_log
|
|
260
|
+
system('git log --oneline -10') if git_available?
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def git_available?
|
|
264
|
+
if system('which git > /dev/null 2>&1')
|
|
265
|
+
true
|
|
266
|
+
else
|
|
267
|
+
puts "⚠️ gitがインストールされていません"
|
|
268
|
+
false
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### 3. システム情報プラグイン
|
|
277
|
+
|
|
278
|
+
```ruby
|
|
279
|
+
module Beniya
|
|
280
|
+
module Plugins
|
|
281
|
+
class SystemInfoPlugin < Plugin
|
|
282
|
+
def name
|
|
283
|
+
"SystemInfo"
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def description
|
|
287
|
+
"システム情報を表示"
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
def commands
|
|
291
|
+
{
|
|
292
|
+
info: method(:show_system_info),
|
|
293
|
+
disk: method(:show_disk_usage)
|
|
294
|
+
}
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
private
|
|
298
|
+
|
|
299
|
+
def show_system_info
|
|
300
|
+
puts "OS: #{RbConfig::CONFIG['host_os']}"
|
|
301
|
+
puts "Ruby: #{RUBY_VERSION}"
|
|
302
|
+
puts "ホームディレクトリ: #{ENV['HOME']}"
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
def show_disk_usage
|
|
306
|
+
puts "ディスク使用量:"
|
|
307
|
+
system('df -h .')
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
## プラグインの仕組み
|
|
315
|
+
|
|
316
|
+
### 自動登録
|
|
317
|
+
|
|
318
|
+
プラグインは`Beniya::Plugin`を継承すると、自動的に`PluginManager`に登録されます:
|
|
319
|
+
|
|
320
|
+
```ruby
|
|
321
|
+
class MyPlugin < Plugin
|
|
322
|
+
# 継承した時点で自動登録される
|
|
323
|
+
end
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### 初期化時の依存チェック
|
|
327
|
+
|
|
328
|
+
プラグインがインスタンス化されるとき、`requires`で宣言したgemがインストールされているかチェックされます:
|
|
329
|
+
|
|
330
|
+
```ruby
|
|
331
|
+
def initialize
|
|
332
|
+
check_dependencies! # 自動的に呼ばれる
|
|
333
|
+
end
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
## トラブルシューティング
|
|
337
|
+
|
|
338
|
+
### プラグインが読み込まれない
|
|
339
|
+
|
|
340
|
+
**症状**: プラグインを作成したのに動作しない
|
|
341
|
+
|
|
342
|
+
**確認事項**:
|
|
343
|
+
1. ファイルの場所: `~/.beniya/plugins/` に配置されているか
|
|
344
|
+
2. ファイル名: `.rb`拡張子がついているか
|
|
345
|
+
3. 構文エラー: Rubyの構文が正しいか
|
|
346
|
+
4. 設定ファイル: `config.yml`で無効化されていないか
|
|
347
|
+
|
|
348
|
+
### 依存gemが見つからない
|
|
349
|
+
|
|
350
|
+
**症状**: プラグイン起動時にエラーが出る
|
|
351
|
+
|
|
352
|
+
**解決方法**:
|
|
353
|
+
```bash
|
|
354
|
+
# エラーメッセージに表示されたgemをインストール
|
|
355
|
+
gem install <gem名>
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
### プラグインが無効化されている
|
|
359
|
+
|
|
360
|
+
**症状**: プラグインが読み込まれない(エラーなし)
|
|
361
|
+
|
|
362
|
+
**確認**: `~/.beniya/config.yml`を確認
|
|
363
|
+
|
|
364
|
+
```yaml
|
|
365
|
+
plugins:
|
|
366
|
+
myplugin:
|
|
367
|
+
enabled: false # ← これを true に変更
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
## ベストプラクティス
|
|
371
|
+
|
|
372
|
+
### 1. わかりやすい名前をつける
|
|
373
|
+
|
|
374
|
+
```ruby
|
|
375
|
+
def name
|
|
376
|
+
"MyAwesomePlugin" # 明確で分かりやすい名前
|
|
377
|
+
end
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### 2. 説明を書く
|
|
381
|
+
|
|
382
|
+
```ruby
|
|
383
|
+
def description
|
|
384
|
+
"このプラグインは〇〇の機能を提供します"
|
|
385
|
+
end
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### 3. エラーハンドリング
|
|
389
|
+
|
|
390
|
+
```ruby
|
|
391
|
+
def my_command
|
|
392
|
+
# エラーが起きる可能性のある処理
|
|
393
|
+
result = some_operation
|
|
394
|
+
rescue StandardError => e
|
|
395
|
+
puts "⚠️ エラーが発生しました: #{e.message}"
|
|
396
|
+
end
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
### 4. 外部コマンドの確認
|
|
400
|
+
|
|
401
|
+
```ruby
|
|
402
|
+
def command_available?(cmd)
|
|
403
|
+
system("which #{cmd} > /dev/null 2>&1")
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
def my_feature
|
|
407
|
+
unless command_available?('git')
|
|
408
|
+
puts "⚠️ gitがインストールされていません"
|
|
409
|
+
return
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
# 処理...
|
|
413
|
+
end
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
## 参考
|
|
417
|
+
|
|
418
|
+
- サンプルプラグイン: `docs/plugin_example.rb`
|
|
419
|
+
- 組み込みプラグイン: `lib/beniya/plugins/file_operations.rb`
|
|
420
|
+
- プラグイン基底クラス: `lib/beniya/plugin.rb`
|
|
421
|
+
- プラグインマネージャー: `lib/beniya/plugin_manager.rb`
|
|
422
|
+
|
|
423
|
+
## まとめ
|
|
424
|
+
|
|
425
|
+
1. `~/.beniya/plugins/` にRubyファイルを作成
|
|
426
|
+
2. `Beniya::Plugin`を継承したクラスを定義
|
|
427
|
+
3. 必須メソッド`name`を実装
|
|
428
|
+
4. オプションで`description`、`version`、`commands`を実装
|
|
429
|
+
5. Beniya起動時に自動読み込み
|
|
430
|
+
|
|
431
|
+
プラグインシステムを使って、Beniyaを自分好みにカスタマイズしてください!
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Beniya プラグイン実装例
|
|
4
|
+
# このファイルを ~/.beniya/plugins/ にコピーして使用してください
|
|
5
|
+
|
|
6
|
+
module Beniya
|
|
7
|
+
module Plugins
|
|
8
|
+
# ファイル検索プラグインの例
|
|
9
|
+
class FileSearchPlugin < Plugin
|
|
10
|
+
def name
|
|
11
|
+
"FileSearch"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def description
|
|
15
|
+
"ファイル名で検索する機能"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def version
|
|
19
|
+
"1.0.0"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def commands
|
|
23
|
+
{
|
|
24
|
+
search: method(:search_files),
|
|
25
|
+
find_by_ext: method(:find_by_extension)
|
|
26
|
+
}
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
# ファイル名で検索
|
|
32
|
+
def search_files(query)
|
|
33
|
+
Dir.glob("**/*#{query}*").each do |file|
|
|
34
|
+
puts file
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# 拡張子でファイルを検索
|
|
39
|
+
def find_by_extension(ext)
|
|
40
|
+
Dir.glob("**/*.#{ext}").each do |file|
|
|
41
|
+
puts file
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Git統合プラグインの例(外部gem依存)
|
|
47
|
+
class GitIntegrationPlugin < Plugin
|
|
48
|
+
# gitコマンドが必要
|
|
49
|
+
# requires 'git' # gemが必要な場合
|
|
50
|
+
|
|
51
|
+
def name
|
|
52
|
+
"GitIntegration"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def description
|
|
56
|
+
"Git操作を統合"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def commands
|
|
60
|
+
{
|
|
61
|
+
git_status: method(:show_git_status),
|
|
62
|
+
git_branch: method(:show_current_branch)
|
|
63
|
+
}
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
def show_git_status
|
|
69
|
+
if system('which git > /dev/null 2>&1')
|
|
70
|
+
system('git status')
|
|
71
|
+
else
|
|
72
|
+
puts "⚠️ gitがインストールされていません"
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def show_current_branch
|
|
77
|
+
if system('which git > /dev/null 2>&1')
|
|
78
|
+
branch = `git branch --show-current`.strip
|
|
79
|
+
puts "現在のブランチ: #{branch}"
|
|
80
|
+
else
|
|
81
|
+
puts "⚠️ gitがインストールされていません"
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# シンプルなユーティリティプラグイン
|
|
87
|
+
class UtilityPlugin < Plugin
|
|
88
|
+
def name
|
|
89
|
+
"Utility"
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def description
|
|
93
|
+
"便利なユーティリティ機能"
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def commands
|
|
97
|
+
{
|
|
98
|
+
disk_usage: method(:show_disk_usage),
|
|
99
|
+
count_files: method(:count_files_in_directory)
|
|
100
|
+
}
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
private
|
|
104
|
+
|
|
105
|
+
def show_disk_usage
|
|
106
|
+
puts "ディスク使用量:"
|
|
107
|
+
system('df -h .')
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def count_files_in_directory
|
|
111
|
+
files = Dir.glob('*').select { |f| File.file?(f) }
|
|
112
|
+
dirs = Dir.glob('*').select { |f| File.directory?(f) }
|
|
113
|
+
|
|
114
|
+
puts "ファイル数: #{files.count}"
|
|
115
|
+
puts "ディレクトリ数: #{dirs.count}"
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Beniya
|
|
4
|
+
# コマンドモード - プラグインコマンドを実行するためのインターフェース
|
|
5
|
+
class CommandMode
|
|
6
|
+
def initialize
|
|
7
|
+
@commands = {}
|
|
8
|
+
load_plugin_commands
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# コマンドを実行する
|
|
12
|
+
def execute(command_string)
|
|
13
|
+
# 空のコマンドは無視
|
|
14
|
+
return nil if command_string.nil? || command_string.strip.empty?
|
|
15
|
+
|
|
16
|
+
# コマンド名を取得 (前後の空白を削除)
|
|
17
|
+
command_name = command_string.strip.to_sym
|
|
18
|
+
|
|
19
|
+
# コマンドが存在するかチェック
|
|
20
|
+
unless @commands.key?(command_name)
|
|
21
|
+
return "⚠️ コマンドが見つかりません: #{command_name}"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# コマンドを実行
|
|
25
|
+
begin
|
|
26
|
+
command_method = @commands[command_name][:method]
|
|
27
|
+
command_method.call
|
|
28
|
+
rescue StandardError => e
|
|
29
|
+
"⚠️ コマンド実行エラー: #{e.message}"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# 利用可能なコマンドのリストを取得
|
|
34
|
+
def available_commands
|
|
35
|
+
@commands.keys
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# コマンドの情報を取得
|
|
39
|
+
def command_info(command_name)
|
|
40
|
+
return nil unless @commands.key?(command_name)
|
|
41
|
+
|
|
42
|
+
{
|
|
43
|
+
name: command_name,
|
|
44
|
+
plugin: @commands[command_name][:plugin],
|
|
45
|
+
description: @commands[command_name][:description]
|
|
46
|
+
}
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
# プラグインからコマンドを読み込む
|
|
52
|
+
def load_plugin_commands
|
|
53
|
+
# 有効なプラグインを取得
|
|
54
|
+
enabled_plugins = PluginManager.enabled_plugins
|
|
55
|
+
|
|
56
|
+
# 各プラグインからコマンドを取得
|
|
57
|
+
enabled_plugins.each do |plugin|
|
|
58
|
+
plugin_name = plugin.name
|
|
59
|
+
plugin_commands = plugin.commands
|
|
60
|
+
|
|
61
|
+
# 各コマンドを登録
|
|
62
|
+
plugin_commands.each do |command_name, command_method|
|
|
63
|
+
@commands[command_name] = {
|
|
64
|
+
method: command_method,
|
|
65
|
+
plugin: plugin_name,
|
|
66
|
+
description: plugin.description
|
|
67
|
+
}
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'io/console'
|
|
4
|
+
|
|
5
|
+
module Beniya
|
|
6
|
+
# コマンドモードのUI - Tab補完とフローティングウィンドウでの結果表示
|
|
7
|
+
class CommandModeUI
|
|
8
|
+
def initialize(command_mode, dialog_renderer)
|
|
9
|
+
@command_mode = command_mode
|
|
10
|
+
@dialog_renderer = dialog_renderer
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# 入力文字列に対する補完候補を取得
|
|
14
|
+
# @param input [String] 現在の入力文字列
|
|
15
|
+
# @return [Array<String>] 補完候補の配列
|
|
16
|
+
def autocomplete(input)
|
|
17
|
+
# 利用可能なコマンド一覧を取得
|
|
18
|
+
available = @command_mode.available_commands.map(&:to_s)
|
|
19
|
+
|
|
20
|
+
# 入力が空の場合は全てのコマンドを返す
|
|
21
|
+
return available if input.empty?
|
|
22
|
+
|
|
23
|
+
# 入力に一致するコマンドをフィルタリング
|
|
24
|
+
available.select { |cmd| cmd.start_with?(input) }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# コマンドを補完する
|
|
28
|
+
# @param input [String] 現在の入力文字列
|
|
29
|
+
# @return [String] 補完後の文字列
|
|
30
|
+
def complete_command(input)
|
|
31
|
+
suggestions = autocomplete(input)
|
|
32
|
+
|
|
33
|
+
# マッチするものがない場合は元の入力を返す
|
|
34
|
+
return input if suggestions.empty?
|
|
35
|
+
|
|
36
|
+
# 一つだけマッチする場合はそれを返す
|
|
37
|
+
return suggestions.first if suggestions.length == 1
|
|
38
|
+
|
|
39
|
+
# 複数マッチする場合は共通プレフィックスを返す
|
|
40
|
+
find_common_prefix(suggestions)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# コマンド実行結果をフローティングウィンドウで表示
|
|
44
|
+
# @param result [String, nil] コマンド実行結果
|
|
45
|
+
def show_result(result)
|
|
46
|
+
# nil または空文字列の場合は何も表示しない
|
|
47
|
+
return if result.nil? || result.empty?
|
|
48
|
+
|
|
49
|
+
# 結果を行に分割
|
|
50
|
+
result_lines = result.split("\n")
|
|
51
|
+
|
|
52
|
+
# エラーメッセージかどうかを判定
|
|
53
|
+
is_error = result.include?("⚠️") || result.include?("エラー")
|
|
54
|
+
|
|
55
|
+
# ウィンドウの色設定
|
|
56
|
+
if is_error
|
|
57
|
+
border_color = "\e[31m" # Red
|
|
58
|
+
title_color = "\e[1;31m" # Bold red
|
|
59
|
+
content_color = "\e[37m" # White
|
|
60
|
+
else
|
|
61
|
+
border_color = "\e[32m" # Green
|
|
62
|
+
title_color = "\e[1;32m" # Bold green
|
|
63
|
+
content_color = "\e[37m" # White
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# ウィンドウタイトル
|
|
67
|
+
title = "コマンド実行結果"
|
|
68
|
+
|
|
69
|
+
# コンテンツ行を構築
|
|
70
|
+
content_lines = [""] + result_lines + ["", "Press any key to close"]
|
|
71
|
+
|
|
72
|
+
# ウィンドウサイズを計算
|
|
73
|
+
width, height = @dialog_renderer.calculate_dimensions(content_lines, {
|
|
74
|
+
title: title,
|
|
75
|
+
min_width: 40,
|
|
76
|
+
max_width: 100
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
# 中央位置を計算
|
|
80
|
+
x, y = @dialog_renderer.calculate_center(width, height)
|
|
81
|
+
|
|
82
|
+
# フローティングウィンドウを描画
|
|
83
|
+
@dialog_renderer.draw_floating_window(x, y, width, height, title, content_lines, {
|
|
84
|
+
border_color: border_color,
|
|
85
|
+
title_color: title_color,
|
|
86
|
+
content_color: content_color
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
# キー入力を待つ
|
|
90
|
+
STDIN.getch
|
|
91
|
+
|
|
92
|
+
# ウィンドウをクリア
|
|
93
|
+
@dialog_renderer.clear_area(x, y, width, height)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
private
|
|
97
|
+
|
|
98
|
+
# 文字列配列の共通プレフィックスを見つける
|
|
99
|
+
# @param strings [Array<String>] 文字列配列
|
|
100
|
+
# @return [String] 共通プレフィックス
|
|
101
|
+
def find_common_prefix(strings)
|
|
102
|
+
return "" if strings.empty?
|
|
103
|
+
return strings.first if strings.length == 1
|
|
104
|
+
|
|
105
|
+
# 最短の文字列の長さを取得
|
|
106
|
+
min_length = strings.map(&:length).min
|
|
107
|
+
|
|
108
|
+
# 各文字位置で全ての文字列が同じ文字を持っているかチェック
|
|
109
|
+
common_length = 0
|
|
110
|
+
min_length.times do |i|
|
|
111
|
+
char = strings.first[i]
|
|
112
|
+
if strings.all? { |s| s[i] == char }
|
|
113
|
+
common_length = i + 1
|
|
114
|
+
else
|
|
115
|
+
break
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
strings.first[0...common_length]
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
@@ -136,6 +136,8 @@ module Beniya
|
|
|
136
136
|
show_zoxide_menu
|
|
137
137
|
when '1', '2', '3', '4', '5', '6', '7', '8', '9' # number keys - go to bookmark
|
|
138
138
|
goto_bookmark(key.to_i)
|
|
139
|
+
when ':' # : - command mode
|
|
140
|
+
activate_command_mode
|
|
139
141
|
else
|
|
140
142
|
false # #{ConfigLoader.message('keybind.invalid_key')}
|
|
141
143
|
end
|
|
@@ -769,6 +771,12 @@ module Beniya
|
|
|
769
771
|
end
|
|
770
772
|
end
|
|
771
773
|
|
|
774
|
+
# コマンドモードを起動
|
|
775
|
+
def activate_command_mode
|
|
776
|
+
@terminal_ui&.activate_command_mode
|
|
777
|
+
true
|
|
778
|
+
end
|
|
779
|
+
|
|
772
780
|
private
|
|
773
781
|
|
|
774
782
|
# カーソルを画面下部の入力行に移動
|
data/lib/beniya/terminal_ui.rb
CHANGED
|
@@ -43,6 +43,11 @@ module Beniya
|
|
|
43
43
|
@screen_height = DEFAULT_SCREEN_HEIGHT
|
|
44
44
|
end
|
|
45
45
|
@running = false
|
|
46
|
+
@command_mode_active = false
|
|
47
|
+
@command_input = ""
|
|
48
|
+
@command_mode = CommandMode.new
|
|
49
|
+
@dialog_renderer = DialogRenderer.new
|
|
50
|
+
@command_mode_ui = CommandModeUI.new(@command_mode, @dialog_renderer)
|
|
46
51
|
end
|
|
47
52
|
|
|
48
53
|
def start(directory_listing, keybind_handler, file_preview)
|
|
@@ -126,8 +131,16 @@ module Beniya
|
|
|
126
131
|
# footer
|
|
127
132
|
draw_footer
|
|
128
133
|
|
|
129
|
-
#
|
|
130
|
-
|
|
134
|
+
# コマンド実行結果を表示
|
|
135
|
+
draw_command_result
|
|
136
|
+
|
|
137
|
+
# コマンドモードがアクティブな場合はコマンド入力欄を表示
|
|
138
|
+
if @command_mode_active
|
|
139
|
+
draw_command_input
|
|
140
|
+
else
|
|
141
|
+
# move cursor to invisible position
|
|
142
|
+
print "\e[#{@screen_height};#{@screen_width}H"
|
|
143
|
+
end
|
|
131
144
|
end
|
|
132
145
|
|
|
133
146
|
def draw_header
|
|
@@ -543,6 +556,12 @@ module Beniya
|
|
|
543
556
|
end
|
|
544
557
|
end
|
|
545
558
|
|
|
559
|
+
# コマンドモードがアクティブな場合は、コマンド入力を処理
|
|
560
|
+
if @command_mode_active
|
|
561
|
+
handle_command_input(input)
|
|
562
|
+
return
|
|
563
|
+
end
|
|
564
|
+
|
|
546
565
|
# キーバインドハンドラーに処理を委譲
|
|
547
566
|
result = @keybind_handler.handle_key(input)
|
|
548
567
|
|
|
@@ -551,6 +570,90 @@ module Beniya
|
|
|
551
570
|
@running = false
|
|
552
571
|
end
|
|
553
572
|
end
|
|
573
|
+
|
|
574
|
+
# コマンドモード関連のメソッドは public にする
|
|
575
|
+
public
|
|
576
|
+
|
|
577
|
+
# コマンドモードを起動
|
|
578
|
+
def activate_command_mode
|
|
579
|
+
@command_mode_active = true
|
|
580
|
+
@command_input = ""
|
|
581
|
+
end
|
|
582
|
+
|
|
583
|
+
# コマンドモードを終了
|
|
584
|
+
def deactivate_command_mode
|
|
585
|
+
@command_mode_active = false
|
|
586
|
+
@command_input = ""
|
|
587
|
+
end
|
|
588
|
+
|
|
589
|
+
# コマンドモードがアクティブかどうか
|
|
590
|
+
def command_mode_active?
|
|
591
|
+
@command_mode_active
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
# コマンド入力を処理
|
|
595
|
+
def handle_command_input(input)
|
|
596
|
+
case input
|
|
597
|
+
when "\r", "\n"
|
|
598
|
+
# Enter キーでコマンドを実行
|
|
599
|
+
execute_command(@command_input)
|
|
600
|
+
deactivate_command_mode
|
|
601
|
+
when "\e"
|
|
602
|
+
# Escape キーでコマンドモードをキャンセル
|
|
603
|
+
deactivate_command_mode
|
|
604
|
+
when "\t"
|
|
605
|
+
# Tab キーで補完
|
|
606
|
+
@command_input = @command_mode_ui.complete_command(@command_input)
|
|
607
|
+
when "\u007F", "\b"
|
|
608
|
+
# Backspace
|
|
609
|
+
@command_input.chop! unless @command_input.empty?
|
|
610
|
+
else
|
|
611
|
+
# 通常の文字を追加
|
|
612
|
+
@command_input += input if input.length == 1
|
|
613
|
+
end
|
|
614
|
+
end
|
|
615
|
+
|
|
616
|
+
# コマンドを実行
|
|
617
|
+
def execute_command(command_string)
|
|
618
|
+
return if command_string.nil? || command_string.empty?
|
|
619
|
+
|
|
620
|
+
result = @command_mode.execute(command_string)
|
|
621
|
+
|
|
622
|
+
# コマンド実行結果をフローティングウィンドウで表示
|
|
623
|
+
@command_mode_ui.show_result(result) if result
|
|
624
|
+
|
|
625
|
+
# 画面を再描画
|
|
626
|
+
draw_screen
|
|
627
|
+
end
|
|
628
|
+
|
|
629
|
+
# コマンド入力欄を描画
|
|
630
|
+
def draw_command_input
|
|
631
|
+
# 画面最下部に描画
|
|
632
|
+
print "\e[#{@screen_height};1H"
|
|
633
|
+
print "\e[2K" # 行をクリア
|
|
634
|
+
|
|
635
|
+
# コマンドプロンプトと入力を表示
|
|
636
|
+
prompt = ":"
|
|
637
|
+
print "#{prompt}#{@command_input}"
|
|
638
|
+
|
|
639
|
+
# カーソルを表示
|
|
640
|
+
print "\e[?25h"
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
# コマンド実行結果を描画
|
|
644
|
+
def draw_command_result
|
|
645
|
+
return unless @command_result && @command_result_time
|
|
646
|
+
|
|
647
|
+
# 3秒間だけ表示
|
|
648
|
+
if Time.now - @command_result_time < 3
|
|
649
|
+
print "\e[#{@screen_height - 1};1H"
|
|
650
|
+
print "\e[2K" # 行をクリア
|
|
651
|
+
print @command_result
|
|
652
|
+
else
|
|
653
|
+
@command_result = nil
|
|
654
|
+
@command_result_time = nil
|
|
655
|
+
end
|
|
656
|
+
end
|
|
554
657
|
end
|
|
555
658
|
end
|
|
556
659
|
|
data/lib/beniya/version.rb
CHANGED
data/lib/beniya.rb
CHANGED
|
@@ -25,6 +25,8 @@ require_relative "beniya/health_checker"
|
|
|
25
25
|
require_relative "beniya/plugin_config"
|
|
26
26
|
require_relative "beniya/plugin"
|
|
27
27
|
require_relative "beniya/plugin_manager"
|
|
28
|
+
require_relative "beniya/command_mode"
|
|
29
|
+
require_relative "beniya/command_mode_ui"
|
|
28
30
|
|
|
29
31
|
module Beniya
|
|
30
32
|
class Error < StandardError; end
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: beniya
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- masisz
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2025-
|
|
10
|
+
date: 2025-12-06 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: io-console
|
|
@@ -121,17 +121,22 @@ files:
|
|
|
121
121
|
- CHANGELOG_v0.5.0.md
|
|
122
122
|
- CHANGELOG_v0.6.0.md
|
|
123
123
|
- CHANGELOG_v0.7.0.md
|
|
124
|
+
- CHANGELOG_v0.8.0.md
|
|
124
125
|
- README.md
|
|
125
126
|
- README_EN.md
|
|
126
127
|
- Rakefile
|
|
127
128
|
- beniya.gemspec
|
|
128
129
|
- bin/beniya
|
|
129
130
|
- config_example.rb
|
|
131
|
+
- docs/PLUGIN_GUIDE.md
|
|
132
|
+
- docs/plugin_example.rb
|
|
130
133
|
- lib/beniya.rb
|
|
131
134
|
- lib/beniya/application.rb
|
|
132
135
|
- lib/beniya/bookmark.rb
|
|
133
136
|
- lib/beniya/bookmark_manager.rb
|
|
134
137
|
- lib/beniya/color_helper.rb
|
|
138
|
+
- lib/beniya/command_mode.rb
|
|
139
|
+
- lib/beniya/command_mode_ui.rb
|
|
135
140
|
- lib/beniya/config.rb
|
|
136
141
|
- lib/beniya/config_loader.rb
|
|
137
142
|
- lib/beniya/dialog_renderer.rb
|