kollabor 0.4.9__py3-none-any.whl
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.
- core/__init__.py +18 -0
- core/application.py +578 -0
- core/cli.py +193 -0
- core/commands/__init__.py +43 -0
- core/commands/executor.py +277 -0
- core/commands/menu_renderer.py +319 -0
- core/commands/parser.py +186 -0
- core/commands/registry.py +331 -0
- core/commands/system_commands.py +479 -0
- core/config/__init__.py +7 -0
- core/config/llm_task_config.py +110 -0
- core/config/loader.py +501 -0
- core/config/manager.py +112 -0
- core/config/plugin_config_manager.py +346 -0
- core/config/plugin_schema.py +424 -0
- core/config/service.py +399 -0
- core/effects/__init__.py +1 -0
- core/events/__init__.py +12 -0
- core/events/bus.py +129 -0
- core/events/executor.py +154 -0
- core/events/models.py +258 -0
- core/events/processor.py +176 -0
- core/events/registry.py +289 -0
- core/fullscreen/__init__.py +19 -0
- core/fullscreen/command_integration.py +290 -0
- core/fullscreen/components/__init__.py +12 -0
- core/fullscreen/components/animation.py +258 -0
- core/fullscreen/components/drawing.py +160 -0
- core/fullscreen/components/matrix_components.py +177 -0
- core/fullscreen/manager.py +302 -0
- core/fullscreen/plugin.py +204 -0
- core/fullscreen/renderer.py +282 -0
- core/fullscreen/session.py +324 -0
- core/io/__init__.py +52 -0
- core/io/buffer_manager.py +362 -0
- core/io/config_status_view.py +272 -0
- core/io/core_status_views.py +410 -0
- core/io/input_errors.py +313 -0
- core/io/input_handler.py +2655 -0
- core/io/input_mode_manager.py +402 -0
- core/io/key_parser.py +344 -0
- core/io/layout.py +587 -0
- core/io/message_coordinator.py +204 -0
- core/io/message_renderer.py +601 -0
- core/io/modal_interaction_handler.py +315 -0
- core/io/raw_input_processor.py +946 -0
- core/io/status_renderer.py +845 -0
- core/io/terminal_renderer.py +586 -0
- core/io/terminal_state.py +551 -0
- core/io/visual_effects.py +734 -0
- core/llm/__init__.py +26 -0
- core/llm/api_communication_service.py +863 -0
- core/llm/conversation_logger.py +473 -0
- core/llm/conversation_manager.py +414 -0
- core/llm/file_operations_executor.py +1401 -0
- core/llm/hook_system.py +402 -0
- core/llm/llm_service.py +1629 -0
- core/llm/mcp_integration.py +386 -0
- core/llm/message_display_service.py +450 -0
- core/llm/model_router.py +214 -0
- core/llm/plugin_sdk.py +396 -0
- core/llm/response_parser.py +848 -0
- core/llm/response_processor.py +364 -0
- core/llm/tool_executor.py +520 -0
- core/logging/__init__.py +19 -0
- core/logging/setup.py +208 -0
- core/models/__init__.py +5 -0
- core/models/base.py +23 -0
- core/plugins/__init__.py +13 -0
- core/plugins/collector.py +212 -0
- core/plugins/discovery.py +386 -0
- core/plugins/factory.py +263 -0
- core/plugins/registry.py +152 -0
- core/storage/__init__.py +5 -0
- core/storage/state_manager.py +84 -0
- core/ui/__init__.py +6 -0
- core/ui/config_merger.py +176 -0
- core/ui/config_widgets.py +369 -0
- core/ui/live_modal_renderer.py +276 -0
- core/ui/modal_actions.py +162 -0
- core/ui/modal_overlay_renderer.py +373 -0
- core/ui/modal_renderer.py +591 -0
- core/ui/modal_state_manager.py +443 -0
- core/ui/widget_integration.py +222 -0
- core/ui/widgets/__init__.py +27 -0
- core/ui/widgets/base_widget.py +136 -0
- core/ui/widgets/checkbox.py +85 -0
- core/ui/widgets/dropdown.py +140 -0
- core/ui/widgets/label.py +78 -0
- core/ui/widgets/slider.py +185 -0
- core/ui/widgets/text_input.py +224 -0
- core/utils/__init__.py +11 -0
- core/utils/config_utils.py +656 -0
- core/utils/dict_utils.py +212 -0
- core/utils/error_utils.py +275 -0
- core/utils/key_reader.py +171 -0
- core/utils/plugin_utils.py +267 -0
- core/utils/prompt_renderer.py +151 -0
- kollabor-0.4.9.dist-info/METADATA +298 -0
- kollabor-0.4.9.dist-info/RECORD +128 -0
- kollabor-0.4.9.dist-info/WHEEL +5 -0
- kollabor-0.4.9.dist-info/entry_points.txt +2 -0
- kollabor-0.4.9.dist-info/licenses/LICENSE +21 -0
- kollabor-0.4.9.dist-info/top_level.txt +4 -0
- kollabor_cli_main.py +20 -0
- plugins/__init__.py +1 -0
- plugins/enhanced_input/__init__.py +18 -0
- plugins/enhanced_input/box_renderer.py +103 -0
- plugins/enhanced_input/box_styles.py +142 -0
- plugins/enhanced_input/color_engine.py +165 -0
- plugins/enhanced_input/config.py +150 -0
- plugins/enhanced_input/cursor_manager.py +72 -0
- plugins/enhanced_input/geometry.py +81 -0
- plugins/enhanced_input/state.py +130 -0
- plugins/enhanced_input/text_processor.py +115 -0
- plugins/enhanced_input_plugin.py +385 -0
- plugins/fullscreen/__init__.py +9 -0
- plugins/fullscreen/example_plugin.py +327 -0
- plugins/fullscreen/matrix_plugin.py +132 -0
- plugins/hook_monitoring_plugin.py +1299 -0
- plugins/query_enhancer_plugin.py +350 -0
- plugins/save_conversation_plugin.py +502 -0
- plugins/system_commands_plugin.py +93 -0
- plugins/tmux_plugin.py +795 -0
- plugins/workflow_enforcement_plugin.py +629 -0
- system_prompt/default.md +1286 -0
- system_prompt/default_win.md +265 -0
- system_prompt/example_with_trender.md +47 -0
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Optional, Callable
|
|
3
|
+
|
|
4
|
+
from ..events import EventType
|
|
5
|
+
from ..events.models import CommandMode
|
|
6
|
+
from .key_parser import KeyPress
|
|
7
|
+
from .buffer_manager import BufferManager
|
|
8
|
+
from ..commands.menu_renderer import CommandMenuRenderer
|
|
9
|
+
from ..commands.parser import SlashCommandParser
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class InputModeManager:
|
|
15
|
+
"""Handles input mode management and command mode processing.
|
|
16
|
+
|
|
17
|
+
This component is responsible for:
|
|
18
|
+
- Mode state management (enter/exit command modes)
|
|
19
|
+
- Command mode keypress handling
|
|
20
|
+
- Menu navigation and command execution coordination
|
|
21
|
+
- Mode transitions and display updates
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(
|
|
25
|
+
self,
|
|
26
|
+
buffer_manager: BufferManager,
|
|
27
|
+
command_menu_renderer: CommandMenuRenderer,
|
|
28
|
+
slash_parser: SlashCommandParser,
|
|
29
|
+
event_bus,
|
|
30
|
+
renderer,
|
|
31
|
+
config,
|
|
32
|
+
) -> None:
|
|
33
|
+
"""Initialize the input mode manager.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
buffer_manager: Buffer manager for text operations.
|
|
37
|
+
command_menu_renderer: Command menu rendering system.
|
|
38
|
+
slash_parser: Slash command parser.
|
|
39
|
+
event_bus: Event bus for emitting input events.
|
|
40
|
+
renderer: Terminal renderer for updating input display.
|
|
41
|
+
config: Configuration manager for input settings.
|
|
42
|
+
"""
|
|
43
|
+
self.buffer_manager = buffer_manager
|
|
44
|
+
self.command_menu_renderer = command_menu_renderer
|
|
45
|
+
self.slash_parser = slash_parser
|
|
46
|
+
self.event_bus = event_bus
|
|
47
|
+
self.renderer = renderer
|
|
48
|
+
self.config = config
|
|
49
|
+
|
|
50
|
+
# Mode state
|
|
51
|
+
self.command_mode = CommandMode.NORMAL
|
|
52
|
+
self.command_menu_active = False
|
|
53
|
+
self.selected_command_index = 0
|
|
54
|
+
self.current_status_modal_config = None
|
|
55
|
+
|
|
56
|
+
# Callbacks for delegation back to InputHandler
|
|
57
|
+
self.on_mode_change: Optional[Callable] = None
|
|
58
|
+
self.on_command_execute: Optional[Callable] = None
|
|
59
|
+
self.on_display_update: Optional[Callable] = None
|
|
60
|
+
self.on_event_emit: Optional[Callable] = None
|
|
61
|
+
self.get_available_commands: Optional[Callable] = None
|
|
62
|
+
self.filter_commands: Optional[Callable] = None
|
|
63
|
+
self.execute_command: Optional[Callable] = None
|
|
64
|
+
|
|
65
|
+
logger.info("Input mode manager initialized")
|
|
66
|
+
|
|
67
|
+
def set_callbacks(
|
|
68
|
+
self,
|
|
69
|
+
on_mode_change: Callable,
|
|
70
|
+
on_command_execute: Callable,
|
|
71
|
+
on_display_update: Callable,
|
|
72
|
+
on_event_emit: Callable,
|
|
73
|
+
get_available_commands: Callable,
|
|
74
|
+
filter_commands: Callable,
|
|
75
|
+
execute_command: Callable,
|
|
76
|
+
) -> None:
|
|
77
|
+
"""Set callbacks for delegation back to InputHandler."""
|
|
78
|
+
self.on_mode_change = on_mode_change
|
|
79
|
+
self.on_command_execute = on_command_execute
|
|
80
|
+
self.on_display_update = on_display_update
|
|
81
|
+
self.on_event_emit = on_event_emit
|
|
82
|
+
self.get_available_commands = get_available_commands
|
|
83
|
+
self.filter_commands = filter_commands
|
|
84
|
+
self.execute_command = execute_command
|
|
85
|
+
|
|
86
|
+
# Mode State Management Methods (Phase 2A)
|
|
87
|
+
# =========================================
|
|
88
|
+
|
|
89
|
+
async def _enter_command_mode(self) -> None:
|
|
90
|
+
"""Enter slash command mode and show command menu."""
|
|
91
|
+
try:
|
|
92
|
+
logger.info("🎯 Entering slash command mode")
|
|
93
|
+
self.command_mode = CommandMode.MENU_POPUP
|
|
94
|
+
self.command_menu_active = True
|
|
95
|
+
|
|
96
|
+
# Reset selection to first command
|
|
97
|
+
self.selected_command_index = 0
|
|
98
|
+
|
|
99
|
+
# Add the '/' character to buffer for visual feedback
|
|
100
|
+
self.buffer_manager.insert_char("/")
|
|
101
|
+
|
|
102
|
+
# Show command menu via renderer
|
|
103
|
+
available_commands = self.get_available_commands()
|
|
104
|
+
self.command_menu_renderer.show_command_menu(available_commands, "")
|
|
105
|
+
|
|
106
|
+
# Emit command menu show event
|
|
107
|
+
await self.on_event_emit(
|
|
108
|
+
EventType.COMMAND_MENU_SHOW,
|
|
109
|
+
{"available_commands": available_commands, "filter_text": ""},
|
|
110
|
+
"commands",
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
# Update display to show command mode
|
|
114
|
+
await self.on_display_update(force_render=True)
|
|
115
|
+
|
|
116
|
+
logger.info("Command menu activated")
|
|
117
|
+
|
|
118
|
+
except Exception as e:
|
|
119
|
+
logger.error(f"Error entering command mode: {e}")
|
|
120
|
+
await self._exit_command_mode()
|
|
121
|
+
|
|
122
|
+
async def _exit_command_mode(self) -> None:
|
|
123
|
+
"""Exit command mode and restore normal input."""
|
|
124
|
+
try:
|
|
125
|
+
import traceback
|
|
126
|
+
|
|
127
|
+
logger.info("🚪 Exiting slash command mode")
|
|
128
|
+
logger.info(
|
|
129
|
+
f"🚪 Exit called from: {traceback.format_stack()[-2].strip()}"
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# Hide command menu via renderer
|
|
133
|
+
self.command_menu_renderer.hide_menu()
|
|
134
|
+
|
|
135
|
+
# Emit command menu hide event
|
|
136
|
+
if self.command_menu_active:
|
|
137
|
+
await self.on_event_emit(
|
|
138
|
+
EventType.COMMAND_MENU_HIDE,
|
|
139
|
+
{"reason": "manual_exit"},
|
|
140
|
+
"commands",
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
self.command_mode = CommandMode.NORMAL
|
|
144
|
+
self.command_menu_active = False
|
|
145
|
+
|
|
146
|
+
# Clear command buffer (remove the '/' and any partial command)
|
|
147
|
+
self.buffer_manager.clear()
|
|
148
|
+
|
|
149
|
+
# Update display
|
|
150
|
+
await self.on_display_update(force_render=True)
|
|
151
|
+
|
|
152
|
+
logger.info("Returned to normal input mode")
|
|
153
|
+
|
|
154
|
+
except Exception as e:
|
|
155
|
+
logger.error(f"Error exiting command mode: {e}")
|
|
156
|
+
|
|
157
|
+
async def _enter_status_modal_mode(self, ui_config) -> None:
|
|
158
|
+
"""Enter status modal mode - modal confined to status area.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
ui_config: Status modal configuration.
|
|
162
|
+
"""
|
|
163
|
+
try:
|
|
164
|
+
# Set status modal mode
|
|
165
|
+
self.command_mode = CommandMode.STATUS_MODAL
|
|
166
|
+
self.current_status_modal_config = ui_config
|
|
167
|
+
logger.info(f"Entered status modal mode: {ui_config.title}")
|
|
168
|
+
|
|
169
|
+
# Unlike full modals, status modals don't take over the screen
|
|
170
|
+
# They just appear in the status area via the renderer
|
|
171
|
+
await self.on_display_update(force_render=True)
|
|
172
|
+
|
|
173
|
+
except Exception as e:
|
|
174
|
+
logger.error(f"Error entering status modal mode: {e}")
|
|
175
|
+
await self._exit_command_mode()
|
|
176
|
+
|
|
177
|
+
async def _exit_status_modal_mode(self) -> None:
|
|
178
|
+
"""Exit status modal mode and return to normal input."""
|
|
179
|
+
try:
|
|
180
|
+
logger.info("Exiting status modal mode...")
|
|
181
|
+
self.command_mode = CommandMode.NORMAL
|
|
182
|
+
self.current_status_modal_config = None
|
|
183
|
+
logger.info("Status modal mode exited successfully")
|
|
184
|
+
|
|
185
|
+
# Refresh display to remove the status modal
|
|
186
|
+
await self.on_display_update(force_render=True)
|
|
187
|
+
logger.info("Display updated after status modal exit")
|
|
188
|
+
|
|
189
|
+
except Exception as e:
|
|
190
|
+
logger.error(f"Error exiting status modal mode: {e}")
|
|
191
|
+
self.command_mode = CommandMode.NORMAL
|
|
192
|
+
|
|
193
|
+
# Command Processing Methods (Phase 2B)
|
|
194
|
+
# =====================================
|
|
195
|
+
|
|
196
|
+
async def _handle_menu_popup_keypress(self, key_press: KeyPress) -> bool:
|
|
197
|
+
"""Handle KeyPress during menu popup mode with arrow key navigation.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
key_press: Parsed key press to process.
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
True if key was handled.
|
|
204
|
+
"""
|
|
205
|
+
try:
|
|
206
|
+
# Handle arrow key navigation
|
|
207
|
+
if key_press.name == "ArrowUp":
|
|
208
|
+
await self._navigate_menu("up")
|
|
209
|
+
return True
|
|
210
|
+
elif key_press.name == "ArrowDown":
|
|
211
|
+
await self._navigate_menu("down")
|
|
212
|
+
return True
|
|
213
|
+
elif key_press.name == "Enter":
|
|
214
|
+
await self._execute_selected_command()
|
|
215
|
+
return True
|
|
216
|
+
elif key_press.name == "Escape":
|
|
217
|
+
await self._exit_command_mode()
|
|
218
|
+
return True
|
|
219
|
+
|
|
220
|
+
# Handle printable characters (for filtering)
|
|
221
|
+
elif key_press.char and key_press.char.isprintable():
|
|
222
|
+
# Insert character for command filtering (routed from RawInputProcessor)
|
|
223
|
+
self.buffer_manager.insert_char(key_press.char)
|
|
224
|
+
await self._update_command_filter()
|
|
225
|
+
return True
|
|
226
|
+
|
|
227
|
+
# Handle backspace/delete
|
|
228
|
+
elif key_press.name in ["Backspace", "Delete"]:
|
|
229
|
+
# If buffer only has '/', exit command mode
|
|
230
|
+
if len(self.buffer_manager.content) <= 1:
|
|
231
|
+
await self._exit_command_mode()
|
|
232
|
+
return True
|
|
233
|
+
else:
|
|
234
|
+
# Remove character and update command filter
|
|
235
|
+
self.buffer_manager.delete_char()
|
|
236
|
+
await self._update_command_filter()
|
|
237
|
+
return True
|
|
238
|
+
|
|
239
|
+
# Other keys not handled
|
|
240
|
+
return False
|
|
241
|
+
|
|
242
|
+
except Exception as e:
|
|
243
|
+
logger.error(f"Error handling menu popup keypress: {e}")
|
|
244
|
+
await self._exit_command_mode()
|
|
245
|
+
return False
|
|
246
|
+
|
|
247
|
+
async def _update_command_filter(self) -> None:
|
|
248
|
+
"""Update command menu based on current buffer content."""
|
|
249
|
+
try:
|
|
250
|
+
# Get current input (minus the leading '/')
|
|
251
|
+
current_input = self.buffer_manager.content
|
|
252
|
+
filter_text = (
|
|
253
|
+
current_input[1:] if current_input.startswith("/") else current_input
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
# Update menu renderer with filtered commands
|
|
257
|
+
filtered_commands = self.filter_commands(filter_text)
|
|
258
|
+
|
|
259
|
+
# Reset selection when filtering
|
|
260
|
+
self.selected_command_index = 0
|
|
261
|
+
self.command_menu_renderer.set_selected_index(
|
|
262
|
+
self.selected_command_index
|
|
263
|
+
)
|
|
264
|
+
self.command_menu_renderer.filter_commands(
|
|
265
|
+
filtered_commands, filter_text
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
# Emit filter update event
|
|
269
|
+
await self.on_event_emit(
|
|
270
|
+
EventType.COMMAND_MENU_FILTER,
|
|
271
|
+
{
|
|
272
|
+
"filter_text": filter_text,
|
|
273
|
+
"available_commands": self.get_available_commands(),
|
|
274
|
+
"filtered_commands": filtered_commands,
|
|
275
|
+
},
|
|
276
|
+
"commands",
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
# Update display
|
|
280
|
+
await self.on_display_update(force_render=True)
|
|
281
|
+
|
|
282
|
+
except Exception as e:
|
|
283
|
+
logger.error(f"Error updating command filter: {e}")
|
|
284
|
+
|
|
285
|
+
async def _navigate_menu(self, direction: str) -> None:
|
|
286
|
+
"""Navigate the command menu up or down.
|
|
287
|
+
|
|
288
|
+
Args:
|
|
289
|
+
direction: "up" or "down"
|
|
290
|
+
"""
|
|
291
|
+
try:
|
|
292
|
+
# Get current filtered commands
|
|
293
|
+
current_input = self.buffer_manager.content
|
|
294
|
+
filter_text = (
|
|
295
|
+
current_input[1:] if current_input.startswith("/") else current_input
|
|
296
|
+
)
|
|
297
|
+
filtered_commands = self.filter_commands(filter_text)
|
|
298
|
+
|
|
299
|
+
if not filtered_commands:
|
|
300
|
+
return
|
|
301
|
+
|
|
302
|
+
# Update selection index
|
|
303
|
+
if direction == "up":
|
|
304
|
+
self.selected_command_index = max(0, self.selected_command_index - 1)
|
|
305
|
+
elif direction == "down":
|
|
306
|
+
self.selected_command_index = min(
|
|
307
|
+
len(filtered_commands) - 1, self.selected_command_index + 1
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
# Update menu renderer with new selection (don't reset selection during navigation)
|
|
311
|
+
self.command_menu_renderer.set_selected_index(
|
|
312
|
+
self.selected_command_index
|
|
313
|
+
)
|
|
314
|
+
self.command_menu_renderer.filter_commands(
|
|
315
|
+
filtered_commands, filter_text, reset_selection=False
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
# Note: No need to call _update_display - filter_commands already renders the menu
|
|
319
|
+
|
|
320
|
+
except Exception as e:
|
|
321
|
+
logger.error(f"Error navigating menu: {e}")
|
|
322
|
+
|
|
323
|
+
async def _execute_selected_command(self) -> None:
|
|
324
|
+
"""Execute the currently selected command."""
|
|
325
|
+
try:
|
|
326
|
+
# Get the selected command from the menu
|
|
327
|
+
selected_command = self.command_menu_renderer.get_selected_command()
|
|
328
|
+
if not selected_command:
|
|
329
|
+
logger.warning("No command selected")
|
|
330
|
+
await self._exit_command_mode()
|
|
331
|
+
return
|
|
332
|
+
|
|
333
|
+
# Create command string from selected command
|
|
334
|
+
command_string = f"/{selected_command['name']}"
|
|
335
|
+
|
|
336
|
+
# Parse the command
|
|
337
|
+
command = self.slash_parser.parse_command(command_string)
|
|
338
|
+
if command:
|
|
339
|
+
logger.info(f"🚀 Executing selected command: {command.name}")
|
|
340
|
+
|
|
341
|
+
# Exit command mode first
|
|
342
|
+
await self._exit_command_mode()
|
|
343
|
+
|
|
344
|
+
# Execute the command using delegation
|
|
345
|
+
result = await self.execute_command(command)
|
|
346
|
+
|
|
347
|
+
# Handle the result
|
|
348
|
+
if result.success:
|
|
349
|
+
logger.info(f"✅ Command {command.name} completed successfully")
|
|
350
|
+
|
|
351
|
+
# Modal display is handled by event bus trigger, not here
|
|
352
|
+
if result.message:
|
|
353
|
+
# Display success message in status area
|
|
354
|
+
logger.info(f"Command result: {result.message}")
|
|
355
|
+
# TODO: Display in status area
|
|
356
|
+
else:
|
|
357
|
+
logger.warning(
|
|
358
|
+
f"❌ Command {command.name} failed: {result.message}"
|
|
359
|
+
)
|
|
360
|
+
# TODO: Display error message in status area
|
|
361
|
+
else:
|
|
362
|
+
logger.warning("Failed to parse selected command")
|
|
363
|
+
await self._exit_command_mode()
|
|
364
|
+
|
|
365
|
+
except Exception as e:
|
|
366
|
+
logger.error(f"Error executing command: {e}")
|
|
367
|
+
await self._exit_command_mode()
|
|
368
|
+
|
|
369
|
+
# Public Interface Methods
|
|
370
|
+
# =======================
|
|
371
|
+
|
|
372
|
+
async def handle_mode_transition(self, new_mode: CommandMode, **kwargs) -> None:
|
|
373
|
+
"""Handle transition to a new command mode."""
|
|
374
|
+
if new_mode == CommandMode.MENU_POPUP:
|
|
375
|
+
await self._enter_command_mode()
|
|
376
|
+
elif new_mode == CommandMode.STATUS_MODAL:
|
|
377
|
+
ui_config = kwargs.get("ui_config")
|
|
378
|
+
await self._enter_status_modal_mode(ui_config)
|
|
379
|
+
elif new_mode == CommandMode.NORMAL:
|
|
380
|
+
if self.command_mode == CommandMode.MENU_POPUP:
|
|
381
|
+
await self._exit_command_mode()
|
|
382
|
+
elif self.command_mode == CommandMode.STATUS_MODAL:
|
|
383
|
+
await self._exit_status_modal_mode()
|
|
384
|
+
|
|
385
|
+
async def handle_command_mode_keypress(self, key_press: KeyPress) -> bool:
|
|
386
|
+
"""Handle keypress while in command mode."""
|
|
387
|
+
if self.command_mode == CommandMode.MENU_POPUP:
|
|
388
|
+
result = await self._handle_menu_popup_keypress(key_press)
|
|
389
|
+
# Notify mode change callback if mode changed
|
|
390
|
+
if hasattr(self, "on_mode_change") and self.on_mode_change:
|
|
391
|
+
await self.on_mode_change(self.command_mode)
|
|
392
|
+
return result
|
|
393
|
+
elif self.command_mode == CommandMode.STATUS_MODAL:
|
|
394
|
+
# For now, delegate status modal handling back to InputHandler
|
|
395
|
+
# This will be refined in future phases
|
|
396
|
+
return False
|
|
397
|
+
|
|
398
|
+
return False
|
|
399
|
+
|
|
400
|
+
def get_current_mode(self) -> CommandMode:
|
|
401
|
+
"""Get the current command mode."""
|
|
402
|
+
return self.command_mode
|