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,142 @@
|
|
|
1
|
+
"""Box drawing styles for enhanced input plugin."""
|
|
2
|
+
|
|
3
|
+
import random
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import Dict, List
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class BoxStyle:
|
|
10
|
+
"""Represents a box drawing style with all character components."""
|
|
11
|
+
|
|
12
|
+
top_left: str
|
|
13
|
+
top_right: str
|
|
14
|
+
bottom_left: str
|
|
15
|
+
bottom_right: str
|
|
16
|
+
horizontal: str
|
|
17
|
+
vertical: str
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class BoxStyleRegistry:
|
|
21
|
+
"""Registry for managing and providing box drawing styles."""
|
|
22
|
+
|
|
23
|
+
def __init__(self):
|
|
24
|
+
"""Initialize the registry with all available styles."""
|
|
25
|
+
self._styles = self._build_styles()
|
|
26
|
+
self._curated_random_styles = [
|
|
27
|
+
# User's confirmed favorites
|
|
28
|
+
"dots_only", "brackets", "dotted", "dashed", "square",
|
|
29
|
+
# Clean classics
|
|
30
|
+
"rounded", "double", "thick", "underline", "minimal",
|
|
31
|
+
# Sophisticated mixed-weight styles
|
|
32
|
+
"mixed_weight", "typography", "sophisticated", "editorial",
|
|
33
|
+
"clean_corners", "refined", "gradient_line",
|
|
34
|
+
# Clean minimal lines
|
|
35
|
+
"lines_only", "thick_lines", "double_lines"
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
def get_style(self, name: str) -> BoxStyle:
|
|
39
|
+
"""Get a style by name.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
name: Style name.
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
BoxStyle object.
|
|
46
|
+
|
|
47
|
+
Raises:
|
|
48
|
+
KeyError: If style doesn't exist.
|
|
49
|
+
"""
|
|
50
|
+
if name not in self._styles:
|
|
51
|
+
raise KeyError(f"Unknown box style: {name}")
|
|
52
|
+
return self._styles[name]
|
|
53
|
+
|
|
54
|
+
def get_random_style(self) -> BoxStyle:
|
|
55
|
+
"""Get a random style from curated collection.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
Random BoxStyle object.
|
|
59
|
+
"""
|
|
60
|
+
style_name = random.choice(self._curated_random_styles)
|
|
61
|
+
return self.get_style(style_name)
|
|
62
|
+
|
|
63
|
+
def get_random_style_name(self) -> str:
|
|
64
|
+
"""Get a random style name from curated collection.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
Random style name.
|
|
68
|
+
"""
|
|
69
|
+
return random.choice(self._curated_random_styles)
|
|
70
|
+
|
|
71
|
+
def list_styles(self) -> List[str]:
|
|
72
|
+
"""Get list of all available style names.
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
List of style names.
|
|
76
|
+
"""
|
|
77
|
+
return list(self._styles.keys())
|
|
78
|
+
|
|
79
|
+
def register_style(self, name: str, style: BoxStyle) -> None:
|
|
80
|
+
"""Register a new style.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
name: Style name.
|
|
84
|
+
style: BoxStyle object.
|
|
85
|
+
"""
|
|
86
|
+
self._styles[name] = style
|
|
87
|
+
|
|
88
|
+
def _build_styles(self) -> Dict[str, BoxStyle]:
|
|
89
|
+
"""Build the complete styles dictionary.
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
Dictionary of all box styles.
|
|
93
|
+
"""
|
|
94
|
+
return {
|
|
95
|
+
# Classic box styles
|
|
96
|
+
"rounded": BoxStyle("╭", "╮", "╰", "╯", "─", "│"),
|
|
97
|
+
"square": BoxStyle("┌", "┐", "└", "┘", "─", "│"),
|
|
98
|
+
"double": BoxStyle("╔", "╗", "╚", "╝", "═", "║"),
|
|
99
|
+
"thick": BoxStyle("┏", "┓", "┗", "┛", "━", "┃"),
|
|
100
|
+
"dotted": BoxStyle("┌", "┐", "└", "┘", "┄", "┆"),
|
|
101
|
+
"dashed": BoxStyle("┌", "┐", "└", "┘", "┅", "┇"),
|
|
102
|
+
|
|
103
|
+
# Minimal styles
|
|
104
|
+
"minimal": BoxStyle(" ", " ", " ", " ", "─", " "),
|
|
105
|
+
"brackets": BoxStyle("⌜", "⌝", "⌞", "⌟", " ", " "),
|
|
106
|
+
"underline": BoxStyle("", "", "", "", "_", ""),
|
|
107
|
+
|
|
108
|
+
# Line-only styles
|
|
109
|
+
"lines_only": BoxStyle("", "", "", "", "─", ""),
|
|
110
|
+
"thick_lines": BoxStyle("", "", "", "", "━", ""),
|
|
111
|
+
"double_lines": BoxStyle("", "", "", "", "═", ""),
|
|
112
|
+
"dots_only": BoxStyle("", "", "", "", "┄", ""),
|
|
113
|
+
"gradient_line": BoxStyle("", "", "", "", "▁", ""),
|
|
114
|
+
|
|
115
|
+
# Decorative styles
|
|
116
|
+
"stars": BoxStyle("★", "★", "★", "★", "✦", "✧"),
|
|
117
|
+
"arrows": BoxStyle("↖", "↗", "↙", "↘", "→", "↕"),
|
|
118
|
+
"diamonds": BoxStyle("◆", "◆", "◆", "◆", "◇", "◈"),
|
|
119
|
+
"circles": BoxStyle("●", "●", "●", "●", "○", "◐"),
|
|
120
|
+
"waves": BoxStyle("~", "~", "~", "~", "≈", "∿"),
|
|
121
|
+
|
|
122
|
+
# Mixed weight styles
|
|
123
|
+
"mixed_weight": BoxStyle("┏", "┓", "┗", "┛", "┄", "│"),
|
|
124
|
+
"typography": BoxStyle("┌", "┐", "└", "┘", "━", "┆"),
|
|
125
|
+
"sophisticated": BoxStyle("╭", "╮", "╰", "╯", "━", "┆"),
|
|
126
|
+
"editorial": BoxStyle("┌", "┐", "└", "┘", "─", ""),
|
|
127
|
+
"clean_corners": BoxStyle("┌", "┐", "└", "┘", "", ""),
|
|
128
|
+
"refined": BoxStyle("╭", "╮", "╰", "╯", "", ""),
|
|
129
|
+
|
|
130
|
+
# Futuristic styles
|
|
131
|
+
"neon": BoxStyle("▓", "▓", "▓", "▓", "▔", "▐"),
|
|
132
|
+
"cyber": BoxStyle("◢", "◣", "◥", "◤", "▬", "▌"),
|
|
133
|
+
"matrix": BoxStyle("╔", "╗", "╚", "╝", "▓", "▓"),
|
|
134
|
+
"holo": BoxStyle("◊", "◊", "◊", "◊", "◈", "◈"),
|
|
135
|
+
"quantum": BoxStyle("⟨", "⟩", "⟨", "⟩", "⟷", "⟁"),
|
|
136
|
+
"neural": BoxStyle("⊙", "⊙", "⊙", "⊙", "⊚", "⊜"),
|
|
137
|
+
"plasma": BoxStyle("◬", "◭", "◪", "◫", "◯", "◎"),
|
|
138
|
+
"circuit": BoxStyle("┫", "┣", "┳", "┻", "╋", "╂"),
|
|
139
|
+
"laser": BoxStyle("", "", "", "", "▇", ""),
|
|
140
|
+
"scan": BoxStyle("", "", "", "", "▓", ""),
|
|
141
|
+
"energy": BoxStyle("", "", "", "", "-", ""),
|
|
142
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"""Color and gradient engine for enhanced input plugin."""
|
|
2
|
+
|
|
3
|
+
from typing import List, Tuple
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ColorEngine:
|
|
7
|
+
"""Handles all color and gradient operations for enhanced input rendering."""
|
|
8
|
+
|
|
9
|
+
def __init__(self, config):
|
|
10
|
+
"""Initialize color engine.
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
config: InputConfig object with plugin configuration.
|
|
14
|
+
"""
|
|
15
|
+
self.config = config
|
|
16
|
+
|
|
17
|
+
def apply_color(self, text: str, color_type: str) -> str:
|
|
18
|
+
"""Apply color formatting to text.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
text: Text to color.
|
|
22
|
+
color_type: Color type ('border', 'text', 'placeholder').
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
Colored text with ANSI codes.
|
|
26
|
+
"""
|
|
27
|
+
# Check if gradient mode is enabled
|
|
28
|
+
gradient_mode = self.config.gradient_mode
|
|
29
|
+
|
|
30
|
+
if gradient_mode:
|
|
31
|
+
return self._apply_gradient_color(text, color_type)
|
|
32
|
+
else:
|
|
33
|
+
return self._apply_standard_color(text, color_type)
|
|
34
|
+
|
|
35
|
+
def _apply_gradient_color(self, text: str, color_type: str) -> str:
|
|
36
|
+
"""Apply gradient coloring to text.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
text: Text to color.
|
|
40
|
+
color_type: Color type.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
Text with gradient colors.
|
|
44
|
+
"""
|
|
45
|
+
gradient_colors = self.config.gradient_colors
|
|
46
|
+
|
|
47
|
+
# Apply gradient based on color type
|
|
48
|
+
if color_type == 'border' and self.config.border_gradient:
|
|
49
|
+
return self.apply_gradient(text, gradient_colors)
|
|
50
|
+
elif color_type == 'text' and self.config.text_gradient:
|
|
51
|
+
return self.apply_gradient(text, gradient_colors)
|
|
52
|
+
elif color_type == 'placeholder':
|
|
53
|
+
# Always apply dim to placeholder regardless of gradient
|
|
54
|
+
return f"\033[2m{text}\033[0m"
|
|
55
|
+
|
|
56
|
+
return text
|
|
57
|
+
|
|
58
|
+
def _apply_standard_color(self, text: str, color_type: str) -> str:
|
|
59
|
+
"""Apply standard color formatting.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
text: Text to color.
|
|
63
|
+
color_type: Color type.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
Colored text with ANSI codes.
|
|
67
|
+
"""
|
|
68
|
+
color_config = getattr(self.config, f'{color_type}_color', 'default')
|
|
69
|
+
|
|
70
|
+
if color_config == 'dim':
|
|
71
|
+
return f"\033[2m{text}\033[0m"
|
|
72
|
+
elif color_config == 'bright':
|
|
73
|
+
return f"\033[1m{text}\033[0m"
|
|
74
|
+
elif color_config == 'default':
|
|
75
|
+
return text
|
|
76
|
+
else:
|
|
77
|
+
return text
|
|
78
|
+
|
|
79
|
+
def apply_gradient(self, text: str, gradient_colors: List[str], is_background: bool = False) -> str:
|
|
80
|
+
"""Apply gradient colors to text.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
text: Text to apply gradient to.
|
|
84
|
+
gradient_colors: List of hex color strings.
|
|
85
|
+
is_background: Whether to apply as background colors.
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
Text with gradient ANSI codes.
|
|
89
|
+
"""
|
|
90
|
+
if len(gradient_colors) < 2 or len(text) == 0:
|
|
91
|
+
return text
|
|
92
|
+
|
|
93
|
+
# Convert hex colors to RGB
|
|
94
|
+
rgb_colors = [self._hex_to_rgb(color) for color in gradient_colors]
|
|
95
|
+
|
|
96
|
+
# Apply gradient character by character
|
|
97
|
+
result = ""
|
|
98
|
+
text_length = len(text)
|
|
99
|
+
|
|
100
|
+
for i, char in enumerate(text):
|
|
101
|
+
# Calculate position in gradient (0.0 to 1.0)
|
|
102
|
+
position = i / max(1, text_length - 1)
|
|
103
|
+
|
|
104
|
+
# Find which color segment we're in
|
|
105
|
+
segment_size = 1.0 / (len(rgb_colors) - 1)
|
|
106
|
+
segment_index = min(int(position / segment_size), len(rgb_colors) - 2)
|
|
107
|
+
local_position = (position - segment_index * segment_size) / segment_size
|
|
108
|
+
|
|
109
|
+
# Interpolate between colors
|
|
110
|
+
color1 = rgb_colors[segment_index]
|
|
111
|
+
color2 = rgb_colors[segment_index + 1]
|
|
112
|
+
interpolated = self._interpolate_color(color1, color2, local_position)
|
|
113
|
+
|
|
114
|
+
# Apply color to character
|
|
115
|
+
ansi_code = self._rgb_to_ansi(*interpolated, is_background)
|
|
116
|
+
result += f"{ansi_code}{char}"
|
|
117
|
+
|
|
118
|
+
# Reset color at the end
|
|
119
|
+
result += "\033[0m"
|
|
120
|
+
return result
|
|
121
|
+
|
|
122
|
+
def _hex_to_rgb(self, hex_color: str) -> Tuple[int, int, int]:
|
|
123
|
+
"""Convert hex color to RGB tuple.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
hex_color: Hex color string (e.g., '#1e3a8a').
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
RGB tuple (r, g, b).
|
|
130
|
+
"""
|
|
131
|
+
hex_color = hex_color.lstrip('#')
|
|
132
|
+
return tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))
|
|
133
|
+
|
|
134
|
+
def _rgb_to_ansi(self, r: int, g: int, b: int, is_background: bool = False) -> str:
|
|
135
|
+
"""Convert RGB to ANSI escape code.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
r: Red component (0-255).
|
|
139
|
+
g: Green component (0-255).
|
|
140
|
+
b: Blue component (0-255).
|
|
141
|
+
is_background: Whether this is a background color.
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
ANSI escape sequence.
|
|
145
|
+
"""
|
|
146
|
+
if is_background:
|
|
147
|
+
return f"\033[48;2;{r};{g};{b}m"
|
|
148
|
+
else:
|
|
149
|
+
return f"\033[38;2;{r};{g};{b}m"
|
|
150
|
+
|
|
151
|
+
def _interpolate_color(self, color1: Tuple[int, int, int], color2: Tuple[int, int, int], factor: float) -> Tuple[int, int, int]:
|
|
152
|
+
"""Interpolate between two RGB colors.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
color1: First RGB color tuple.
|
|
156
|
+
color2: Second RGB color tuple.
|
|
157
|
+
factor: Interpolation factor (0.0 to 1.0).
|
|
158
|
+
|
|
159
|
+
Returns:
|
|
160
|
+
Interpolated RGB tuple.
|
|
161
|
+
"""
|
|
162
|
+
r = int(color1[0] + (color2[0] - color1[0]) * factor)
|
|
163
|
+
g = int(color1[1] + (color2[1] - color1[1]) * factor)
|
|
164
|
+
b = int(color1[2] + (color2[2] - color1[2]) * factor)
|
|
165
|
+
return (r, g, b)
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"""Configuration management for Enhanced Input Plugin."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Any, Dict, List, TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from core.config import ConfigManager
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class InputConfig:
|
|
12
|
+
"""Type-safe configuration for Enhanced Input Plugin."""
|
|
13
|
+
|
|
14
|
+
# Core settings
|
|
15
|
+
enabled: bool = True
|
|
16
|
+
style: str = "rounded"
|
|
17
|
+
width_mode: str = "auto"
|
|
18
|
+
placeholder: str = "Type your message here..."
|
|
19
|
+
show_placeholder: bool = True
|
|
20
|
+
|
|
21
|
+
# Dimensions
|
|
22
|
+
min_width: int = 60
|
|
23
|
+
max_width: int = 120
|
|
24
|
+
min_height: int = 3
|
|
25
|
+
max_height: int = 10
|
|
26
|
+
|
|
27
|
+
# Dynamic behavior
|
|
28
|
+
dynamic_sizing: bool = True
|
|
29
|
+
wrap_text: bool = True
|
|
30
|
+
randomize_style: bool = False
|
|
31
|
+
randomize_interval: float = 5.0
|
|
32
|
+
|
|
33
|
+
# Cursor settings
|
|
34
|
+
cursor_blink_rate: float = 0.5
|
|
35
|
+
|
|
36
|
+
# Status display
|
|
37
|
+
show_status: bool = True
|
|
38
|
+
|
|
39
|
+
# Color settings
|
|
40
|
+
border_color: str = "dim"
|
|
41
|
+
text_color: str = "dim"
|
|
42
|
+
placeholder_color: str = "dim"
|
|
43
|
+
gradient_mode: bool = True
|
|
44
|
+
gradient_colors: List[str] = None
|
|
45
|
+
border_gradient: bool = True
|
|
46
|
+
text_gradient: bool = True
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def __post_init__(self):
|
|
50
|
+
"""Initialize default gradient colors if not provided."""
|
|
51
|
+
if self.gradient_colors is None:
|
|
52
|
+
self.gradient_colors = ["#333333", "#999999", "#222222"]
|
|
53
|
+
|
|
54
|
+
@classmethod
|
|
55
|
+
def from_config_manager(cls, config: 'ConfigManager') -> 'InputConfig':
|
|
56
|
+
"""Create InputConfig from ConfigManager.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
config: Configuration manager instance.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
InputConfig instance with values from config.
|
|
63
|
+
"""
|
|
64
|
+
colors_config = config.get("plugins.enhanced_input.colors", {})
|
|
65
|
+
|
|
66
|
+
# Ensure colors_config is a dictionary
|
|
67
|
+
if not isinstance(colors_config, dict):
|
|
68
|
+
colors_config = {} # Fallback to empty dict
|
|
69
|
+
|
|
70
|
+
return cls(
|
|
71
|
+
enabled=config.get("plugins.enhanced_input.enabled", True),
|
|
72
|
+
style=config.get("plugins.enhanced_input.style", "rounded"),
|
|
73
|
+
width_mode=config.get("plugins.enhanced_input.width", "auto"),
|
|
74
|
+
placeholder=config.get("plugins.enhanced_input.placeholder", "Type your message here..."),
|
|
75
|
+
show_placeholder=config.get("plugins.enhanced_input.show_placeholder", True),
|
|
76
|
+
|
|
77
|
+
min_width=config.get("plugins.enhanced_input.min_width", 60),
|
|
78
|
+
max_width=config.get("plugins.enhanced_input.max_width", 120),
|
|
79
|
+
min_height=config.get("plugins.enhanced_input.min_height", 3),
|
|
80
|
+
max_height=config.get("plugins.enhanced_input.max_height", 10),
|
|
81
|
+
|
|
82
|
+
dynamic_sizing=config.get("plugins.enhanced_input.dynamic_sizing", True),
|
|
83
|
+
wrap_text=config.get("plugins.enhanced_input.wrap_text", True),
|
|
84
|
+
randomize_style=config.get("plugins.enhanced_input.randomize_style", False),
|
|
85
|
+
randomize_interval=config.get("plugins.enhanced_input.randomize_interval", 5.0),
|
|
86
|
+
|
|
87
|
+
cursor_blink_rate=config.get("plugins.enhanced_input.cursor_blink_rate", 0.5),
|
|
88
|
+
show_status=config.get("plugins.enhanced_input.show_status", True),
|
|
89
|
+
|
|
90
|
+
border_color=colors_config.get("border", "dim"),
|
|
91
|
+
text_color=colors_config.get("text", "dim"),
|
|
92
|
+
placeholder_color=colors_config.get("placeholder", "dim"),
|
|
93
|
+
gradient_mode=colors_config.get("gradient_mode", True),
|
|
94
|
+
gradient_colors=colors_config.get("gradient_colors", ["#333333", "#999999", "#222222"]),
|
|
95
|
+
border_gradient=colors_config.get("border_gradient", True),
|
|
96
|
+
text_gradient=colors_config.get("text_gradient", True)
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
def get_color_config(self) -> Dict[str, Any]:
|
|
100
|
+
"""Get color configuration as dictionary for ColorEngine.
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
Dictionary with color configuration.
|
|
104
|
+
"""
|
|
105
|
+
return {
|
|
106
|
+
"border": self.border_color,
|
|
107
|
+
"text": self.text_color,
|
|
108
|
+
"placeholder": self.placeholder_color,
|
|
109
|
+
"gradient_mode": self.gradient_mode,
|
|
110
|
+
"gradient_colors": self.gradient_colors,
|
|
111
|
+
"border_gradient": self.border_gradient,
|
|
112
|
+
"text_gradient": self.text_gradient
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
def get_default_config_dict(self) -> Dict[str, Any]:
|
|
116
|
+
"""Get default configuration as dictionary for plugin registration.
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
Default configuration dictionary.
|
|
120
|
+
"""
|
|
121
|
+
return {
|
|
122
|
+
"plugins": {
|
|
123
|
+
"enhanced_input": {
|
|
124
|
+
"enabled": self.enabled,
|
|
125
|
+
"style": self.style,
|
|
126
|
+
"width": self.width_mode,
|
|
127
|
+
"placeholder": self.placeholder,
|
|
128
|
+
"show_placeholder": self.show_placeholder,
|
|
129
|
+
"min_width": self.min_width,
|
|
130
|
+
"max_width": self.max_width,
|
|
131
|
+
"randomize_style": self.randomize_style,
|
|
132
|
+
"randomize_interval": self.randomize_interval,
|
|
133
|
+
"dynamic_sizing": self.dynamic_sizing,
|
|
134
|
+
"min_height": self.min_height,
|
|
135
|
+
"max_height": self.max_height,
|
|
136
|
+
"wrap_text": self.wrap_text,
|
|
137
|
+
"colors": {
|
|
138
|
+
"border": self.border_color,
|
|
139
|
+
"text": self.text_color,
|
|
140
|
+
"placeholder": self.placeholder_color,
|
|
141
|
+
"gradient_mode": self.gradient_mode,
|
|
142
|
+
"gradient_colors": self.gradient_colors,
|
|
143
|
+
"border_gradient": self.border_gradient,
|
|
144
|
+
"text_gradient": self.text_gradient
|
|
145
|
+
},
|
|
146
|
+
"cursor_blink_rate": self.cursor_blink_rate,
|
|
147
|
+
"show_status": self.show_status
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""Cursor management for enhanced input plugin."""
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CursorManager:
|
|
7
|
+
"""Manages cursor positioning, blinking, and rendering."""
|
|
8
|
+
|
|
9
|
+
def __init__(self, config):
|
|
10
|
+
"""Initialize cursor manager.
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
config: InputConfig object with plugin configuration.
|
|
14
|
+
"""
|
|
15
|
+
self.config = config
|
|
16
|
+
self.cursor_visible = True
|
|
17
|
+
self.last_blink_time = 0
|
|
18
|
+
self.blink_interval = config.cursor_blink_rate
|
|
19
|
+
|
|
20
|
+
def insert_cursor(self, text: str, cursor_position: int, cursor_char: str = None) -> str:
|
|
21
|
+
"""Insert cursor character at the specified position.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
text: Text to insert cursor into.
|
|
25
|
+
cursor_position: Position to insert cursor (0-based).
|
|
26
|
+
cursor_char: Optional cursor character to use. If None, uses get_cursor_char().
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
Text with cursor inserted.
|
|
30
|
+
"""
|
|
31
|
+
if cursor_char is None:
|
|
32
|
+
cursor_char = self.get_cursor_char()
|
|
33
|
+
cursor_pos = max(0, min(cursor_position, len(text)))
|
|
34
|
+
return text[:cursor_pos] + cursor_char + text[cursor_pos:]
|
|
35
|
+
|
|
36
|
+
def get_cursor_char(self) -> str:
|
|
37
|
+
"""Get the current cursor character.
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
Cursor character ("▌" or " " based on blink state).
|
|
41
|
+
"""
|
|
42
|
+
return "▌" if self.cursor_visible else " "
|
|
43
|
+
|
|
44
|
+
def update_blink_state(self, input_is_active: bool) -> None:
|
|
45
|
+
"""Update cursor blinking state.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
input_is_active: Whether input is currently active.
|
|
49
|
+
"""
|
|
50
|
+
if not input_is_active:
|
|
51
|
+
self.cursor_visible = True
|
|
52
|
+
return
|
|
53
|
+
|
|
54
|
+
current_time = time.time()
|
|
55
|
+
if current_time - self.last_blink_time >= self.blink_interval:
|
|
56
|
+
self.cursor_visible = not self.cursor_visible
|
|
57
|
+
self.last_blink_time = current_time
|
|
58
|
+
|
|
59
|
+
def is_input_active(self, renderer) -> bool:
|
|
60
|
+
"""Determine if input is currently active.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
renderer: Terminal renderer object.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
True if input is active (not writing messages or thinking).
|
|
67
|
+
"""
|
|
68
|
+
writing_messages = getattr(renderer, 'writing_messages', False)
|
|
69
|
+
thinking_active = getattr(renderer, 'thinking_active', False)
|
|
70
|
+
conversation_active = getattr(renderer, 'conversation_active', True)
|
|
71
|
+
|
|
72
|
+
return not writing_messages and not thinking_active and conversation_active
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""Geometry calculations for enhanced input plugin."""
|
|
2
|
+
|
|
3
|
+
import shutil
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class GeometryCalculator:
|
|
8
|
+
"""Handles all layout and dimension calculations for enhanced input boxes."""
|
|
9
|
+
|
|
10
|
+
def __init__(self, config):
|
|
11
|
+
"""Initialize geometry calculator.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
config: InputConfig object with plugin configuration.
|
|
15
|
+
"""
|
|
16
|
+
self.config = config
|
|
17
|
+
|
|
18
|
+
def calculate_box_width(self) -> int:
|
|
19
|
+
"""Calculate the optimal box width.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
Box width in characters.
|
|
23
|
+
"""
|
|
24
|
+
width_mode = self.config.width_mode
|
|
25
|
+
|
|
26
|
+
if width_mode == "auto":
|
|
27
|
+
terminal_width = self._get_terminal_width()
|
|
28
|
+
# Leave some margin on both sides
|
|
29
|
+
proposed_width = terminal_width - 4
|
|
30
|
+
# Apply min/max constraints
|
|
31
|
+
min_width = self.config.min_width
|
|
32
|
+
max_width = self.config.max_width
|
|
33
|
+
return max(min_width, min(max_width, proposed_width))
|
|
34
|
+
else:
|
|
35
|
+
# Fixed width mode - use configured width or sensible default
|
|
36
|
+
return getattr(self.config, 'fixed_width', 80)
|
|
37
|
+
|
|
38
|
+
def calculate_box_height(self, content_lines: List[str]) -> int:
|
|
39
|
+
"""Calculate the box height based on content.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
content_lines: List of content lines.
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
Total box height including borders.
|
|
46
|
+
"""
|
|
47
|
+
dynamic_sizing = self.config.dynamic_sizing
|
|
48
|
+
|
|
49
|
+
if not dynamic_sizing:
|
|
50
|
+
return 3 # Fixed: top border + content + bottom border
|
|
51
|
+
|
|
52
|
+
content_height = len(content_lines)
|
|
53
|
+
total_height = content_height + 2 # Add top and bottom borders
|
|
54
|
+
|
|
55
|
+
# Apply min/max constraints
|
|
56
|
+
min_height = self.config.min_height
|
|
57
|
+
max_height = self.config.max_height
|
|
58
|
+
return max(min_height, min(max_height, total_height))
|
|
59
|
+
|
|
60
|
+
def calculate_content_width(self, box_width: int) -> int:
|
|
61
|
+
"""Calculate content area width.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
box_width: Total box width.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
Content width (excluding borders and spaces).
|
|
68
|
+
"""
|
|
69
|
+
# Width breakdown: │ + space + content + space + │ = box_width
|
|
70
|
+
return box_width - 4 # 2 border chars + 2 spaces
|
|
71
|
+
|
|
72
|
+
def _get_terminal_width(self) -> int:
|
|
73
|
+
"""Get the current terminal width.
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
Terminal width in columns.
|
|
77
|
+
"""
|
|
78
|
+
try:
|
|
79
|
+
return shutil.get_terminal_size().columns
|
|
80
|
+
except:
|
|
81
|
+
return 80 # Default fallback
|