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.
Files changed (128) hide show
  1. core/__init__.py +18 -0
  2. core/application.py +578 -0
  3. core/cli.py +193 -0
  4. core/commands/__init__.py +43 -0
  5. core/commands/executor.py +277 -0
  6. core/commands/menu_renderer.py +319 -0
  7. core/commands/parser.py +186 -0
  8. core/commands/registry.py +331 -0
  9. core/commands/system_commands.py +479 -0
  10. core/config/__init__.py +7 -0
  11. core/config/llm_task_config.py +110 -0
  12. core/config/loader.py +501 -0
  13. core/config/manager.py +112 -0
  14. core/config/plugin_config_manager.py +346 -0
  15. core/config/plugin_schema.py +424 -0
  16. core/config/service.py +399 -0
  17. core/effects/__init__.py +1 -0
  18. core/events/__init__.py +12 -0
  19. core/events/bus.py +129 -0
  20. core/events/executor.py +154 -0
  21. core/events/models.py +258 -0
  22. core/events/processor.py +176 -0
  23. core/events/registry.py +289 -0
  24. core/fullscreen/__init__.py +19 -0
  25. core/fullscreen/command_integration.py +290 -0
  26. core/fullscreen/components/__init__.py +12 -0
  27. core/fullscreen/components/animation.py +258 -0
  28. core/fullscreen/components/drawing.py +160 -0
  29. core/fullscreen/components/matrix_components.py +177 -0
  30. core/fullscreen/manager.py +302 -0
  31. core/fullscreen/plugin.py +204 -0
  32. core/fullscreen/renderer.py +282 -0
  33. core/fullscreen/session.py +324 -0
  34. core/io/__init__.py +52 -0
  35. core/io/buffer_manager.py +362 -0
  36. core/io/config_status_view.py +272 -0
  37. core/io/core_status_views.py +410 -0
  38. core/io/input_errors.py +313 -0
  39. core/io/input_handler.py +2655 -0
  40. core/io/input_mode_manager.py +402 -0
  41. core/io/key_parser.py +344 -0
  42. core/io/layout.py +587 -0
  43. core/io/message_coordinator.py +204 -0
  44. core/io/message_renderer.py +601 -0
  45. core/io/modal_interaction_handler.py +315 -0
  46. core/io/raw_input_processor.py +946 -0
  47. core/io/status_renderer.py +845 -0
  48. core/io/terminal_renderer.py +586 -0
  49. core/io/terminal_state.py +551 -0
  50. core/io/visual_effects.py +734 -0
  51. core/llm/__init__.py +26 -0
  52. core/llm/api_communication_service.py +863 -0
  53. core/llm/conversation_logger.py +473 -0
  54. core/llm/conversation_manager.py +414 -0
  55. core/llm/file_operations_executor.py +1401 -0
  56. core/llm/hook_system.py +402 -0
  57. core/llm/llm_service.py +1629 -0
  58. core/llm/mcp_integration.py +386 -0
  59. core/llm/message_display_service.py +450 -0
  60. core/llm/model_router.py +214 -0
  61. core/llm/plugin_sdk.py +396 -0
  62. core/llm/response_parser.py +848 -0
  63. core/llm/response_processor.py +364 -0
  64. core/llm/tool_executor.py +520 -0
  65. core/logging/__init__.py +19 -0
  66. core/logging/setup.py +208 -0
  67. core/models/__init__.py +5 -0
  68. core/models/base.py +23 -0
  69. core/plugins/__init__.py +13 -0
  70. core/plugins/collector.py +212 -0
  71. core/plugins/discovery.py +386 -0
  72. core/plugins/factory.py +263 -0
  73. core/plugins/registry.py +152 -0
  74. core/storage/__init__.py +5 -0
  75. core/storage/state_manager.py +84 -0
  76. core/ui/__init__.py +6 -0
  77. core/ui/config_merger.py +176 -0
  78. core/ui/config_widgets.py +369 -0
  79. core/ui/live_modal_renderer.py +276 -0
  80. core/ui/modal_actions.py +162 -0
  81. core/ui/modal_overlay_renderer.py +373 -0
  82. core/ui/modal_renderer.py +591 -0
  83. core/ui/modal_state_manager.py +443 -0
  84. core/ui/widget_integration.py +222 -0
  85. core/ui/widgets/__init__.py +27 -0
  86. core/ui/widgets/base_widget.py +136 -0
  87. core/ui/widgets/checkbox.py +85 -0
  88. core/ui/widgets/dropdown.py +140 -0
  89. core/ui/widgets/label.py +78 -0
  90. core/ui/widgets/slider.py +185 -0
  91. core/ui/widgets/text_input.py +224 -0
  92. core/utils/__init__.py +11 -0
  93. core/utils/config_utils.py +656 -0
  94. core/utils/dict_utils.py +212 -0
  95. core/utils/error_utils.py +275 -0
  96. core/utils/key_reader.py +171 -0
  97. core/utils/plugin_utils.py +267 -0
  98. core/utils/prompt_renderer.py +151 -0
  99. kollabor-0.4.9.dist-info/METADATA +298 -0
  100. kollabor-0.4.9.dist-info/RECORD +128 -0
  101. kollabor-0.4.9.dist-info/WHEEL +5 -0
  102. kollabor-0.4.9.dist-info/entry_points.txt +2 -0
  103. kollabor-0.4.9.dist-info/licenses/LICENSE +21 -0
  104. kollabor-0.4.9.dist-info/top_level.txt +4 -0
  105. kollabor_cli_main.py +20 -0
  106. plugins/__init__.py +1 -0
  107. plugins/enhanced_input/__init__.py +18 -0
  108. plugins/enhanced_input/box_renderer.py +103 -0
  109. plugins/enhanced_input/box_styles.py +142 -0
  110. plugins/enhanced_input/color_engine.py +165 -0
  111. plugins/enhanced_input/config.py +150 -0
  112. plugins/enhanced_input/cursor_manager.py +72 -0
  113. plugins/enhanced_input/geometry.py +81 -0
  114. plugins/enhanced_input/state.py +130 -0
  115. plugins/enhanced_input/text_processor.py +115 -0
  116. plugins/enhanced_input_plugin.py +385 -0
  117. plugins/fullscreen/__init__.py +9 -0
  118. plugins/fullscreen/example_plugin.py +327 -0
  119. plugins/fullscreen/matrix_plugin.py +132 -0
  120. plugins/hook_monitoring_plugin.py +1299 -0
  121. plugins/query_enhancer_plugin.py +350 -0
  122. plugins/save_conversation_plugin.py +502 -0
  123. plugins/system_commands_plugin.py +93 -0
  124. plugins/tmux_plugin.py +795 -0
  125. plugins/workflow_enforcement_plugin.py +629 -0
  126. system_prompt/default.md +1286 -0
  127. system_prompt/default_win.md +265 -0
  128. system_prompt/example_with_trender.md +47 -0
@@ -0,0 +1,130 @@
1
+ """State management for Enhanced Input Plugin."""
2
+
3
+ import time
4
+ import random
5
+ from typing import TYPE_CHECKING
6
+
7
+ if TYPE_CHECKING:
8
+ from .config import InputConfig
9
+
10
+
11
+ class PluginState:
12
+ """Manages runtime state for Enhanced Input Plugin."""
13
+
14
+ def __init__(self, config: 'InputConfig'):
15
+ """Initialize plugin state.
16
+
17
+ Args:
18
+ config: Plugin configuration instance.
19
+ """
20
+ self.config = config
21
+
22
+ # Randomization state
23
+ self.last_randomize_time = 0
24
+ self.current_random_style = config.style
25
+
26
+ # Cursor blinking state
27
+ self.cursor_visible = True
28
+ self.last_blink_time = 0
29
+
30
+ # Curated list of sophisticated styles matching user preferences
31
+ self.random_styles = [
32
+ # User's confirmed favorites
33
+ "dots_only", "brackets", "dotted", "dashed", "square",
34
+ # Clean classics
35
+ "rounded", "double", "thick", "underline", "minimal",
36
+ # New sophisticated mixed-weight styles
37
+ "mixed_weight", "typography", "sophisticated", "editorial",
38
+ "clean_corners", "refined", "gradient_line",
39
+ # Clean minimal lines
40
+ "lines_only", "thick_lines", "double_lines"
41
+ ]
42
+
43
+ def get_current_style(self) -> str:
44
+ """Get the current style, handling randomization.
45
+
46
+ Returns:
47
+ Current active style name.
48
+ """
49
+ current_time = time.time()
50
+
51
+ # Handle randomization
52
+ if self.config.randomize_style or self.config.style == "random":
53
+ if (current_time - self.last_randomize_time) >= self.config.randomize_interval:
54
+ self.current_random_style = self._get_random_style()
55
+ self.last_randomize_time = current_time
56
+ return self.current_random_style
57
+
58
+ return self.config.style
59
+
60
+ def _get_random_style(self) -> str:
61
+ """Get a random style from curated good styles.
62
+
63
+ Returns:
64
+ Random style name.
65
+ """
66
+ return random.choice(self.random_styles)
67
+
68
+ def update_cursor_blink(self, input_is_active: bool) -> None:
69
+ """Update cursor blinking state.
70
+
71
+ Args:
72
+ input_is_active: Whether input is currently active.
73
+ """
74
+ if not input_is_active:
75
+ self.cursor_visible = True
76
+ return
77
+
78
+ current_time = time.time()
79
+ if current_time - self.last_blink_time >= self.config.cursor_blink_rate:
80
+ self.cursor_visible = not self.cursor_visible
81
+ self.last_blink_time = current_time
82
+
83
+ def get_cursor_char(self, input_is_active: bool) -> str:
84
+ """Get the current cursor character.
85
+
86
+ Args:
87
+ input_is_active: Whether input is currently active.
88
+
89
+ Returns:
90
+ Cursor character to display.
91
+ """
92
+ if input_is_active:
93
+ return "█" if self.cursor_visible else " "
94
+ else:
95
+ return "█"
96
+
97
+ def is_input_active(self, renderer) -> bool:
98
+ """Determine if input is currently active.
99
+
100
+ Args:
101
+ renderer: Terminal renderer instance.
102
+
103
+ Returns:
104
+ True if input should be considered active.
105
+ """
106
+ writing_messages = getattr(renderer, 'writing_messages', False)
107
+ thinking_active = getattr(renderer, 'thinking_active', False)
108
+ conversation_active = getattr(renderer, 'conversation_active', True)
109
+
110
+ return not writing_messages and not thinking_active and conversation_active
111
+
112
+ def get_status_display(self) -> str:
113
+ """Get status display string for current style.
114
+
115
+ Returns:
116
+ Status string showing current style.
117
+ """
118
+ current_style = self.get_current_style()
119
+ if self.config.randomize_style or self.config.style == "random":
120
+ return f"{current_style} (random)"
121
+ return current_style
122
+
123
+ def reset_randomization(self) -> None:
124
+ """Reset randomization timer."""
125
+ self.last_randomize_time = 0
126
+
127
+ def reset_cursor_blink(self) -> None:
128
+ """Reset cursor blink state."""
129
+ self.cursor_visible = True
130
+ self.last_blink_time = 0
@@ -0,0 +1,115 @@
1
+ """Text processing utilities for enhanced input plugin."""
2
+
3
+ import re
4
+ from typing import List
5
+
6
+
7
+ class TextProcessor:
8
+ """Handles text wrapping, truncation, and visual length calculations."""
9
+
10
+ def __init__(self, config):
11
+ """Initialize text processor.
12
+
13
+ Args:
14
+ config: InputConfig object with plugin configuration.
15
+ """
16
+ self.config = config
17
+
18
+ def wrap_text(self, text: str, width: int) -> List[str]:
19
+ """Wrap text to fit within the specified width.
20
+
21
+ Args:
22
+ text: Text to wrap.
23
+ width: Maximum width for each line.
24
+
25
+ Returns:
26
+ List of wrapped text lines.
27
+ """
28
+ wrap_text = self.config.wrap_text
29
+
30
+ if not wrap_text or not text:
31
+ return [text] if text else [""]
32
+
33
+ words = text.split()
34
+ lines = []
35
+ current_line = ""
36
+
37
+ for word in words:
38
+ # Check if adding this word would exceed the width
39
+ test_line = f"{current_line} {word}".strip()
40
+ if len(test_line) <= width:
41
+ current_line = test_line
42
+ else:
43
+ # Start a new line
44
+ if current_line:
45
+ lines.append(current_line)
46
+ current_line = word
47
+
48
+ # Add the last line
49
+ if current_line:
50
+ lines.append(current_line)
51
+
52
+ return lines if lines else [""]
53
+
54
+ def get_visual_length(self, text: str) -> int:
55
+ """Get the visual length of text, excluding ANSI escape codes.
56
+
57
+ Args:
58
+ text: Text that may contain ANSI codes.
59
+
60
+ Returns:
61
+ Visual length of the text.
62
+ """
63
+ # Remove ANSI escape codes
64
+ ansi_escape = re.compile(r'\x1b\[[0-9;]*m')
65
+ clean_text = ansi_escape.sub('', text)
66
+ return len(clean_text)
67
+
68
+ def truncate_with_ellipsis(self, text: str, max_width: int) -> str:
69
+ """Truncate text with ellipsis if it exceeds max width.
70
+
71
+ Args:
72
+ text: Text to potentially truncate.
73
+ max_width: Maximum allowed width.
74
+
75
+ Returns:
76
+ Truncated text with ellipsis if needed.
77
+ """
78
+ visual_length = self.get_visual_length(text)
79
+ if visual_length > max_width:
80
+ # Truncate content but be careful with ANSI codes
81
+ return text[:max_width-3] + "..."
82
+ return text
83
+
84
+ def pad_to_width(self, text: str, target_width: int) -> str:
85
+ """Pad text to target width with spaces.
86
+
87
+ Args:
88
+ text: Text to pad.
89
+ target_width: Target width.
90
+
91
+ Returns:
92
+ Padded text.
93
+ """
94
+ visual_length = self.get_visual_length(text)
95
+ if visual_length < target_width:
96
+ padding_needed = target_width - visual_length
97
+ return text + " " * padding_needed
98
+ return text
99
+
100
+ def fit_text_to_width(self, text: str, target_width: int) -> str:
101
+ """Fit text to target width by truncating or padding as needed.
102
+
103
+ Args:
104
+ text: Text to fit.
105
+ target_width: Target width.
106
+
107
+ Returns:
108
+ Text fitted to target width.
109
+ """
110
+ visual_length = self.get_visual_length(text)
111
+
112
+ if visual_length > target_width:
113
+ return self.truncate_with_ellipsis(text, target_width)
114
+ else:
115
+ return self.pad_to_width(text, target_width)
@@ -0,0 +1,385 @@
1
+ """Enhanced Input Plugin for Kollabor CLI - Refactored Version.
2
+
3
+ Provides enhanced input rendering with bordered boxes using Unicode box-drawing characters.
4
+ Now built with modular components following DRY principles.
5
+ """
6
+
7
+ import logging
8
+ import sys
9
+ from pathlib import Path
10
+ from typing import Any, Dict, List, TYPE_CHECKING
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+ # Add parent directory to path so we can import from chat_app
15
+ sys.path.insert(0, str(Path(__file__).parent.parent))
16
+
17
+ from core.events import Event, EventType, Hook, HookPriority
18
+
19
+ # Import our modular components
20
+ from plugins.enhanced_input.config import InputConfig
21
+ from plugins.enhanced_input.state import PluginState
22
+ from plugins.enhanced_input.box_styles import BoxStyleRegistry
23
+ from plugins.enhanced_input.color_engine import ColorEngine
24
+ from plugins.enhanced_input.geometry import GeometryCalculator
25
+ from plugins.enhanced_input.text_processor import TextProcessor
26
+ from plugins.enhanced_input.cursor_manager import CursorManager
27
+ from plugins.enhanced_input.box_renderer import BoxRenderer
28
+
29
+ # Type hints (only needed for type checking)
30
+ if TYPE_CHECKING:
31
+ from core.config import ConfigManager
32
+ from core.storage import StateManager
33
+ from core.events import EventBus
34
+ from core.io.terminal_renderer import TerminalRenderer
35
+
36
+
37
+ class EnhancedInputPlugin:
38
+ """Plugin that renders enhanced bordered input boxes using modular components."""
39
+
40
+ @staticmethod
41
+ def get_default_config() -> Dict[str, Any]:
42
+ """Get the default configuration for the enhanced input plugin.
43
+
44
+ Returns:
45
+ Default configuration dictionary for the enhanced input plugin.
46
+ """
47
+ # Use the default config from InputConfig
48
+ default_config = InputConfig()
49
+ return default_config.get_default_config_dict()
50
+
51
+ @staticmethod
52
+ def get_startup_info(config: 'ConfigManager') -> List[str]:
53
+ """Get startup information to display for this plugin.
54
+
55
+ Args:
56
+ config: Configuration manager instance.
57
+
58
+ Returns:
59
+ List of strings to display during startup.
60
+ """
61
+ enabled = config.get('plugins.enhanced_input.enabled', True)
62
+ style = config.get('plugins.enhanced_input.style', 'rounded')
63
+ return [
64
+ f"Enabled: {enabled}",
65
+ f"Style: {style}",
66
+ f"Unicode box drawing support"
67
+ ]
68
+
69
+ @staticmethod
70
+ def get_config_widgets() -> Dict[str, Any]:
71
+ """Get configuration widgets for this plugin.
72
+
73
+ Returns:
74
+ Widget section definition for the config modal.
75
+ """
76
+ return {
77
+ "title": "Enhanced Input Plugin",
78
+ "widgets": [
79
+ {
80
+ "type": "dropdown",
81
+ "label": "Box Style",
82
+ "config_path": "plugins.enhanced_input.style",
83
+ "options": ["rounded", "square", "double", "thick", "thin"],
84
+ "help": "Visual style of the input box border"
85
+ },
86
+ {
87
+ "type": "text_input",
88
+ "label": "Width",
89
+ "config_path": "plugins.enhanced_input.width",
90
+ "placeholder": "auto",
91
+ "help": "Box width (auto, pixels, or percentage)"
92
+ },
93
+ {
94
+ "type": "text_input",
95
+ "label": "Placeholder Text",
96
+ "config_path": "plugins.enhanced_input.placeholder",
97
+ "placeholder": "Type your message here...",
98
+ "help": "Text shown when input is empty"
99
+ },
100
+ {
101
+ "type": "checkbox",
102
+ "label": "Show Placeholder",
103
+ "config_path": "plugins.enhanced_input.show_placeholder",
104
+ "help": "Display placeholder text when input is empty"
105
+ },
106
+ {
107
+ "type": "slider",
108
+ "label": "Min Width",
109
+ "config_path": "plugins.enhanced_input.min_width",
110
+ "min_value": 20,
111
+ "max_value": 200,
112
+ "step": 10,
113
+ "help": "Minimum box width in characters"
114
+ },
115
+ {
116
+ "type": "slider",
117
+ "label": "Max Width",
118
+ "config_path": "plugins.enhanced_input.max_width",
119
+ "min_value": 40,
120
+ "max_value": 300,
121
+ "step": 10,
122
+ "help": "Maximum box width in characters"
123
+ },
124
+ {
125
+ "type": "checkbox",
126
+ "label": "Randomize Style",
127
+ "config_path": "plugins.enhanced_input.randomize_style",
128
+ "help": "Randomly change box style periodically"
129
+ },
130
+ {
131
+ "type": "slider",
132
+ "label": "Randomize Interval",
133
+ "config_path": "plugins.enhanced_input.randomize_interval",
134
+ "min_value": 1.0,
135
+ "max_value": 60.0,
136
+ "step": 1.0,
137
+ "help": "Seconds between style randomizations"
138
+ },
139
+ {
140
+ "type": "checkbox",
141
+ "label": "Dynamic Sizing",
142
+ "config_path": "plugins.enhanced_input.dynamic_sizing",
143
+ "help": "Automatically resize box based on content"
144
+ },
145
+ {
146
+ "type": "slider",
147
+ "label": "Min Height",
148
+ "config_path": "plugins.enhanced_input.min_height",
149
+ "min_value": 1,
150
+ "max_value": 20,
151
+ "step": 1,
152
+ "help": "Minimum box height in lines"
153
+ },
154
+ {
155
+ "type": "slider",
156
+ "label": "Max Height",
157
+ "config_path": "plugins.enhanced_input.max_height",
158
+ "min_value": 3,
159
+ "max_value": 50,
160
+ "step": 1,
161
+ "help": "Maximum box height in lines"
162
+ },
163
+ {
164
+ "type": "checkbox",
165
+ "label": "Wrap Text",
166
+ "config_path": "plugins.enhanced_input.wrap_text",
167
+ "help": "Enable text wrapping in input box"
168
+ },
169
+ {
170
+ "type": "slider",
171
+ "label": "Cursor Blink Rate",
172
+ "config_path": "plugins.enhanced_input.cursor_blink_rate",
173
+ "min_value": 0.1,
174
+ "max_value": 2.0,
175
+ "step": 0.1,
176
+ "help": "Cursor blink speed in seconds"
177
+ },
178
+ {
179
+ "type": "checkbox",
180
+ "label": "Show Status",
181
+ "config_path": "plugins.enhanced_input.show_status",
182
+ "help": "Display status information below input box"
183
+ }
184
+ ]
185
+ }
186
+
187
+ def __init__(self, name: str, state_manager: 'StateManager', event_bus: 'EventBus',
188
+ renderer: 'TerminalRenderer', config: 'ConfigManager') -> None:
189
+ """Initialize the enhanced input plugin.
190
+
191
+ Args:
192
+ name: Plugin name.
193
+ state_manager: State management system.
194
+ event_bus: Event bus for hook registration.
195
+ renderer: Terminal renderer.
196
+ config: Configuration manager.
197
+ """
198
+ self.name = name
199
+ self.state_manager = state_manager
200
+ self.event_bus = event_bus
201
+ self.renderer = renderer
202
+
203
+ # Initialize configuration
204
+ self.config = InputConfig.from_config_manager(config)
205
+
206
+ # Initialize state management
207
+ self.state = PluginState(self.config)
208
+
209
+ # Initialize modular components
210
+ self.box_styles = BoxStyleRegistry()
211
+ self.color_engine = ColorEngine(self.config)
212
+ self.geometry = GeometryCalculator(self.config)
213
+ self.text_processor = TextProcessor(self.config)
214
+ self.cursor_manager = CursorManager(self.config)
215
+ self.box_renderer = BoxRenderer(self.box_styles, self.color_engine, self.geometry, self.text_processor)
216
+
217
+ # Register hooks for input rendering
218
+ self.hooks = [
219
+ Hook(
220
+ name="render_fancy_input",
221
+ plugin_name=self.name,
222
+ event_type=EventType.INPUT_RENDER,
223
+ priority=HookPriority.DISPLAY.value,
224
+ callback=self._render_fancy_input
225
+ )
226
+ ]
227
+
228
+ def get_status_lines(self) -> Dict[str, List[str]]:
229
+ """Get status lines for the enhanced input plugin organized by area.
230
+
231
+ Returns:
232
+ Dictionary with status lines organized by area A, B, C.
233
+ """
234
+ # Check if status display is enabled for this plugin
235
+ if not self.config.show_status:
236
+ return {"A": [], "B": [], "C": []}
237
+
238
+ # UI-related status goes in area C
239
+ if not self.config.enabled:
240
+ return {"A": [], "B": [], "C": ["Enhanced Input: Off"]}
241
+
242
+ style_display = self.state.get_status_display()
243
+
244
+ return {
245
+ "A": [],
246
+ "B": [],
247
+ "C": [
248
+ f"Input: {style_display}",
249
+ f"Width: {self.config.width_mode}"
250
+ ]
251
+ }
252
+
253
+ async def _render_fancy_input(self, data: Dict[str, Any], event: Event) -> Dict[str, Any]:
254
+ """Render the fancy input box using modular components.
255
+
256
+ Args:
257
+ data: Event data containing input information.
258
+ event: The event object.
259
+
260
+ Returns:
261
+ Modified event data.
262
+ """
263
+ if not self.config.enabled:
264
+ return {"status": "disabled"}
265
+
266
+ # Get input buffer and cursor position from renderer
267
+ input_text = getattr(self.renderer, 'input_buffer', '')
268
+ cursor_position = getattr(self.renderer, 'cursor_position', len(input_text))
269
+
270
+ # Determine if input is active and update cursor state
271
+ input_is_active = self.state.is_input_active(self.renderer)
272
+ self.state.update_cursor_blink(input_is_active)
273
+
274
+ # Get cursor character
275
+ cursor_char = self.state.get_cursor_char(input_is_active)
276
+
277
+ # Insert cursor at correct position
278
+ text_with_cursor = self.cursor_manager.insert_cursor(
279
+ input_text, cursor_position, cursor_char
280
+ )
281
+
282
+ # Prepare content with prompt
283
+ if input_text:
284
+ content_with_cursor = f"> {text_with_cursor}"
285
+ elif self.config.show_placeholder:
286
+ colored_placeholder = self.color_engine.apply_color(
287
+ self.config.placeholder, 'placeholder'
288
+ )
289
+ content_with_cursor = f"> {cursor_char}{colored_placeholder}"
290
+ else:
291
+ content_with_cursor = f"> {cursor_char}"
292
+
293
+ # Calculate box dimensions
294
+ box_width = self.geometry.calculate_box_width()
295
+ content_width = self.geometry.calculate_content_width(box_width)
296
+
297
+ # Process text (wrapping if enabled)
298
+ content_lines = self.text_processor.wrap_text(content_with_cursor, content_width)
299
+
300
+ # Calculate dynamic height
301
+ box_height = self.geometry.calculate_box_height(content_lines)
302
+
303
+ # Get current style
304
+ current_style = self.state.get_current_style()
305
+
306
+ # Render the complete box
307
+ fancy_lines = self.box_renderer.render_box(
308
+ content_lines=content_lines,
309
+ box_width=box_width,
310
+ style_name=current_style
311
+ )
312
+
313
+ # Store the fancy input lines for the renderer to use
314
+ data["fancy_input_lines"] = fancy_lines
315
+
316
+ return {"status": "rendered", "lines": len(fancy_lines), "fancy_input_lines": fancy_lines}
317
+
318
+ async def initialize(self) -> None:
319
+ """Initialize the plugin."""
320
+ pass
321
+
322
+ async def register_hooks(self) -> None:
323
+ """Register all plugin hooks with the event bus."""
324
+ for hook in self.hooks:
325
+ await self.event_bus.register_hook(hook)
326
+
327
+ # Register status view
328
+ await self._register_status_view()
329
+
330
+ async def _register_status_view(self) -> None:
331
+ """Register enhanced input status view."""
332
+ try:
333
+ # Check if renderer has status registry
334
+ if (hasattr(self.renderer, 'status_renderer') and
335
+ self.renderer.status_renderer and
336
+ hasattr(self.renderer.status_renderer, 'status_registry') and
337
+ self.renderer.status_renderer.status_registry):
338
+
339
+ from core.io.status_renderer import StatusViewConfig, BlockConfig
340
+
341
+ # Create input UI view
342
+ input_view = StatusViewConfig(
343
+ name="Input UI",
344
+ plugin_source="enhanced_input",
345
+ priority=200, # Lower than core views
346
+ blocks=[
347
+ BlockConfig(
348
+ width_fraction=1.0,
349
+ content_provider=self._get_input_ui_content,
350
+ title="Input UI",
351
+ priority=100
352
+ )
353
+ ]
354
+ )
355
+
356
+ registry = self.renderer.status_renderer.status_registry
357
+ registry.register_status_view("enhanced_input", input_view)
358
+ logger.info(" Enhanced Input registered 'Input UI' status view")
359
+
360
+ else:
361
+ logger.debug("Status registry not available - cannot register status view")
362
+
363
+ except Exception as e:
364
+ logger.error(f"Failed to register enhanced input status view: {e}")
365
+
366
+ def _get_input_ui_content(self) -> List[str]:
367
+ """Get input UI content for status view."""
368
+ try:
369
+ if not self.config.enabled:
370
+ return ["Input: - Off"]
371
+
372
+ style_display = self.state.get_status_display()
373
+
374
+ return [
375
+ f"Input: {style_display} ··· Width: {self.config.width_mode}"
376
+ ]
377
+
378
+ except Exception as e:
379
+ logger.error(f"Error getting input UI content: {e}")
380
+ return ["Input UI: Error"]
381
+
382
+
383
+ async def shutdown(self) -> None:
384
+ """Shutdown the plugin."""
385
+ pass
@@ -0,0 +1,9 @@
1
+ """Full-screen plugins directory."""
2
+
3
+ from .matrix_plugin import MatrixRainPlugin
4
+ from .example_plugin import ExamplePlugin
5
+
6
+ __all__ = [
7
+ "MatrixRainPlugin",
8
+ "ExamplePlugin"
9
+ ]