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,224 @@
|
|
|
1
|
+
"""Text input widget for modal UI components."""
|
|
2
|
+
|
|
3
|
+
from typing import List
|
|
4
|
+
from .base_widget import BaseWidget
|
|
5
|
+
from ...io.key_parser import KeyPress
|
|
6
|
+
from ...io.visual_effects import ColorPalette
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TextInputWidget(BaseWidget):
|
|
10
|
+
"""Interactive text input widget with cursor ▌.
|
|
11
|
+
|
|
12
|
+
Displays as [text▌] with blinking cursor and accepts character input.
|
|
13
|
+
Uses ColorPalette.BRIGHT for focus highlighting.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, config: dict, config_path: str, config_service=None):
|
|
17
|
+
"""Initialize text input widget.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
config: Widget configuration with optional placeholder.
|
|
21
|
+
config_path: Dot-notation path to config value.
|
|
22
|
+
config_service: ConfigService instance for reading/writing config values.
|
|
23
|
+
"""
|
|
24
|
+
super().__init__(config, config_path, config_service)
|
|
25
|
+
self.cursor_position = 0
|
|
26
|
+
self._show_cursor = True
|
|
27
|
+
|
|
28
|
+
def render(self) -> List[str]:
|
|
29
|
+
"""Render text input with cursor indicator.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
List containing text input display line.
|
|
33
|
+
"""
|
|
34
|
+
# Get current text value
|
|
35
|
+
current_value = self.get_pending_value()
|
|
36
|
+
text = str(current_value) if current_value is not None else ""
|
|
37
|
+
label = self.get_label()
|
|
38
|
+
placeholder = self.config.get("placeholder", "")
|
|
39
|
+
|
|
40
|
+
# Display text or placeholder
|
|
41
|
+
if text:
|
|
42
|
+
display_text = text
|
|
43
|
+
elif placeholder and not self.focused:
|
|
44
|
+
display_text = placeholder
|
|
45
|
+
else:
|
|
46
|
+
display_text = ""
|
|
47
|
+
|
|
48
|
+
# Add cursor when focused
|
|
49
|
+
if self.focused and self._show_cursor:
|
|
50
|
+
# Insert cursor at current position
|
|
51
|
+
cursor_pos = min(self.cursor_position, len(display_text))
|
|
52
|
+
display_with_cursor = (
|
|
53
|
+
display_text[:cursor_pos] + "▌" + display_text[cursor_pos:]
|
|
54
|
+
)
|
|
55
|
+
else:
|
|
56
|
+
display_with_cursor = display_text
|
|
57
|
+
|
|
58
|
+
# Apply focus highlighting
|
|
59
|
+
if self.focused:
|
|
60
|
+
return [f"{ColorPalette.BRIGHT_WHITE} {label}: [{display_with_cursor}]{ColorPalette.RESET}"]
|
|
61
|
+
else:
|
|
62
|
+
if placeholder and not text:
|
|
63
|
+
# Show placeholder in dim text
|
|
64
|
+
return [f" {label}: [{ColorPalette.DIM}{placeholder}{ColorPalette.RESET}]"]
|
|
65
|
+
else:
|
|
66
|
+
return [f" {label}: [{display_with_cursor}]"]
|
|
67
|
+
|
|
68
|
+
def handle_input(self, key_press: KeyPress) -> bool:
|
|
69
|
+
"""Handle text input - character insertion and navigation.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
key_press: Key press event to handle.
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
True if key was handled.
|
|
76
|
+
"""
|
|
77
|
+
current_text = str(self.get_pending_value() or "")
|
|
78
|
+
|
|
79
|
+
# Handle special keys
|
|
80
|
+
if key_press.name == "Backspace":
|
|
81
|
+
if self.cursor_position > 0:
|
|
82
|
+
# Remove character before cursor
|
|
83
|
+
new_text = (
|
|
84
|
+
current_text[:self.cursor_position - 1] +
|
|
85
|
+
current_text[self.cursor_position:]
|
|
86
|
+
)
|
|
87
|
+
self.set_value(new_text)
|
|
88
|
+
self.cursor_position = max(0, self.cursor_position - 1)
|
|
89
|
+
return True
|
|
90
|
+
|
|
91
|
+
elif key_press.name == "Delete":
|
|
92
|
+
if self.cursor_position < len(current_text):
|
|
93
|
+
# Remove character at cursor
|
|
94
|
+
new_text = (
|
|
95
|
+
current_text[:self.cursor_position] +
|
|
96
|
+
current_text[self.cursor_position + 1:]
|
|
97
|
+
)
|
|
98
|
+
self.set_value(new_text)
|
|
99
|
+
return True
|
|
100
|
+
|
|
101
|
+
elif key_press.name == "ArrowLeft":
|
|
102
|
+
# Move cursor left
|
|
103
|
+
self.cursor_position = max(0, self.cursor_position - 1)
|
|
104
|
+
return True
|
|
105
|
+
|
|
106
|
+
elif key_press.name == "ArrowRight":
|
|
107
|
+
# Move cursor right
|
|
108
|
+
self.cursor_position = min(len(current_text), self.cursor_position + 1)
|
|
109
|
+
return True
|
|
110
|
+
|
|
111
|
+
elif key_press.name == "Home" or key_press.name == "Ctrl+A":
|
|
112
|
+
# Move cursor to beginning
|
|
113
|
+
self.cursor_position = 0
|
|
114
|
+
return True
|
|
115
|
+
|
|
116
|
+
elif key_press.name == "End" or key_press.name == "Ctrl+E":
|
|
117
|
+
# Move cursor to end
|
|
118
|
+
self.cursor_position = len(current_text)
|
|
119
|
+
return True
|
|
120
|
+
|
|
121
|
+
elif key_press.name == "Ctrl+U":
|
|
122
|
+
# Clear entire line
|
|
123
|
+
self.set_value("")
|
|
124
|
+
self.cursor_position = 0
|
|
125
|
+
return True
|
|
126
|
+
|
|
127
|
+
elif key_press.name == "Ctrl+K":
|
|
128
|
+
# Clear from cursor to end
|
|
129
|
+
new_text = current_text[:self.cursor_position]
|
|
130
|
+
self.set_value(new_text)
|
|
131
|
+
return True
|
|
132
|
+
|
|
133
|
+
# Handle printable characters
|
|
134
|
+
elif key_press.char and key_press.char.isprintable():
|
|
135
|
+
# CRITICAL FIX: Add input validation for specific field types
|
|
136
|
+
validation_type = self.config.get("validation", "text")
|
|
137
|
+
|
|
138
|
+
# For numeric fields, only allow digits and decimal points
|
|
139
|
+
if validation_type == "integer" and not key_press.char.isdigit():
|
|
140
|
+
return True # Reject non-numeric input
|
|
141
|
+
elif validation_type == "number" and not (key_press.char.isdigit() or key_press.char == "."):
|
|
142
|
+
return True # Reject non-numeric input
|
|
143
|
+
|
|
144
|
+
# Insert character at cursor position
|
|
145
|
+
new_text = (
|
|
146
|
+
current_text[:self.cursor_position] +
|
|
147
|
+
key_press.char +
|
|
148
|
+
current_text[self.cursor_position:]
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
# Validate the complete value
|
|
152
|
+
if self._validate_complete_value(new_text, validation_type):
|
|
153
|
+
self.set_value(new_text)
|
|
154
|
+
self.cursor_position += 1
|
|
155
|
+
return True
|
|
156
|
+
|
|
157
|
+
return False
|
|
158
|
+
|
|
159
|
+
def set_focus(self, focused: bool):
|
|
160
|
+
"""Set focus and reset cursor position.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
focused: Whether widget should be focused.
|
|
164
|
+
"""
|
|
165
|
+
super().set_focus(focused)
|
|
166
|
+
if focused:
|
|
167
|
+
# Move cursor to end when gaining focus
|
|
168
|
+
current_text = str(self.get_pending_value() or "")
|
|
169
|
+
self.cursor_position = len(current_text)
|
|
170
|
+
|
|
171
|
+
def set_value(self, value):
|
|
172
|
+
"""Set text value and adjust cursor position.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
value: New text value.
|
|
176
|
+
"""
|
|
177
|
+
super().set_value(str(value) if value is not None else "")
|
|
178
|
+
# Ensure cursor position is valid
|
|
179
|
+
new_text = str(value) if value is not None else ""
|
|
180
|
+
self.cursor_position = min(self.cursor_position, len(new_text))
|
|
181
|
+
|
|
182
|
+
def _validate_complete_value(self, value: str, validation_type: str) -> bool:
|
|
183
|
+
"""Validate complete input value based on validation type.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
value: String value to validate.
|
|
187
|
+
validation_type: Type of validation (text, integer, number).
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
True if value is valid for the field type.
|
|
191
|
+
"""
|
|
192
|
+
if validation_type == "integer":
|
|
193
|
+
if not value: # Empty is valid (will use default)
|
|
194
|
+
return True
|
|
195
|
+
try:
|
|
196
|
+
int(value)
|
|
197
|
+
return True
|
|
198
|
+
except ValueError:
|
|
199
|
+
return False
|
|
200
|
+
elif validation_type == "number":
|
|
201
|
+
if not value: # Empty is valid (will use default)
|
|
202
|
+
return True
|
|
203
|
+
try:
|
|
204
|
+
float(value)
|
|
205
|
+
return True
|
|
206
|
+
except ValueError:
|
|
207
|
+
return False
|
|
208
|
+
return True # Text validation always passes
|
|
209
|
+
|
|
210
|
+
def is_valid_value(self, value) -> bool:
|
|
211
|
+
"""Validate text input value - must be string-convertible.
|
|
212
|
+
|
|
213
|
+
Args:
|
|
214
|
+
value: Value to validate.
|
|
215
|
+
|
|
216
|
+
Returns:
|
|
217
|
+
True if value can be converted to string.
|
|
218
|
+
"""
|
|
219
|
+
try:
|
|
220
|
+
str(value)
|
|
221
|
+
validation_type = self.config.get("validation", "text")
|
|
222
|
+
return self._validate_complete_value(str(value), validation_type)
|
|
223
|
+
except (TypeError, ValueError):
|
|
224
|
+
return False
|
core/utils/__init__.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Shared utility modules for Kollabor CLI."""
|
|
2
|
+
|
|
3
|
+
from .dict_utils import deep_merge, safe_get, safe_set
|
|
4
|
+
from .plugin_utils import has_method, safe_call_method, get_plugin_metadata
|
|
5
|
+
from .error_utils import log_and_continue, safe_execute
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
'deep_merge', 'safe_get', 'safe_set',
|
|
9
|
+
'has_method', 'safe_call_method', 'get_plugin_metadata',
|
|
10
|
+
'log_and_continue', 'safe_execute'
|
|
11
|
+
]
|