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,734 @@
1
+ """Visual effects system for terminal rendering."""
2
+
3
+ import re
4
+ from dataclasses import dataclass, field
5
+ from enum import Enum
6
+ from typing import List, Tuple, Dict, Any
7
+
8
+
9
+ class EffectType(Enum):
10
+ """Types of visual effects."""
11
+
12
+ GRADIENT = "gradient"
13
+ SHIMMER = "shimmer"
14
+ DIM = "dim"
15
+ ANIMATION = "animation"
16
+ COLOR = "color"
17
+
18
+
19
+ @dataclass
20
+ class EffectConfig:
21
+ """Configuration for visual effects."""
22
+
23
+ effect_type: EffectType
24
+ enabled: bool = True
25
+ intensity: float = 1.0
26
+ speed: int = 3
27
+ width: int = 4
28
+ colors: List[str] = field(default_factory=list)
29
+
30
+
31
+ class ColorPalette:
32
+ """Color palette definitions for various effects - RGB True Color (24-bit)."""
33
+
34
+ # Standard colors
35
+ RESET = "\033[0m"
36
+ DIM = "\033[2m"
37
+ BRIGHT = "\033[1m"
38
+
39
+ # Basic colors - RGB True Color
40
+ WHITE = "\033[38;2;220;220;220m" # rgb(220, 220, 220)
41
+ BRIGHT_WHITE = "\033[1m\033[38;2;255;255;255m" # Bold + rgb(255, 255, 255)
42
+ BLACK = "\033[38;2;0;0;0m" # rgb(0, 0, 0)
43
+
44
+ # Red variants - RGB True Color
45
+ DIM_RED = "\033[2m\033[38;2;205;49;49m" # Dim + rgb(205, 49, 49)
46
+ RED = "\033[38;2;205;49;49m" # rgb(205, 49, 49)
47
+ BRIGHT_RED = "\033[1m\033[38;2;241;76;76m" # Bold + rgb(241, 76, 76)
48
+
49
+ # Green variants - RGB True Color
50
+ DIM_GREEN = "\033[2m\033[38;2;13;188;121m" # Dim + rgb(13, 188, 121)
51
+ GREEN = "\033[38;2;13;188;121m" # rgb(13, 188, 121)
52
+ BRIGHT_GREEN = "\033[1m\033[38;2;35;209;139m" # Bold + rgb(35, 209, 139)
53
+
54
+ # Yellow variants - RGB True Color
55
+ DIM_YELLOW = "\033[2m\033[38;2;229;192;123m" # Dim + rgb(229, 192, 123)
56
+ YELLOW = "\033[38;2;229;192;123m" # rgb(229, 192, 123)
57
+ BRIGHT_YELLOW = "\033[1m\033[38;2;245;223;77m" # Bold + rgb(245, 223, 77)
58
+
59
+ # Blue variants - RGB True Color
60
+ DIM_BLUE = "\033[2m\033[38;2;36;114;200m" # Dim + rgb(36, 114, 200)
61
+ BLUE = "\033[38;2;36;114;200m" # rgb(36, 114, 200)
62
+ BRIGHT_BLUE = "\033[1m\033[38;2;59;142;234m" # Bold + rgb(59, 142, 234)
63
+ NORMAL_BLUE = "\033[38;2;100;149;237m" # rgb(100, 149, 237) - cornflower blue
64
+
65
+ # Magenta variants - RGB True Color
66
+ DIM_MAGENTA = "\033[2m\033[38;2;188;63;188m" # Dim + rgb(188, 63, 188)
67
+ MAGENTA = "\033[38;2;188;63;188m" # rgb(188, 63, 188)
68
+ BRIGHT_MAGENTA = "\033[1m\033[38;2;214;112;214m" # Bold + rgb(214, 112, 214)
69
+
70
+ # Cyan variants - RGB True Color
71
+ DIM_CYAN = "\033[2m\033[38;2;17;168;205m" # Dim + rgb(17, 168, 205)
72
+ CYAN = "\033[38;2;17;168;205m" # rgb(17, 168, 205)
73
+ BRIGHT_CYAN = "\033[1m\033[38;2;41;184;219m" # Bold + rgb(41, 184, 219)
74
+
75
+ # Grey variants - RGB True Color
76
+ DIM_GREY = "\033[2m\033[38;2;128;128;128m" # Dim + rgb(128, 128, 128)
77
+ GREY = "\033[38;2;128;128;128m" # rgb(128, 128, 128)
78
+ BRIGHT_GREY = "\033[1m\033[38;2;169;169;169m" # Bold + rgb(169, 169, 169)
79
+
80
+ # Extended bright colors - RGB True Color (brighter versions)
81
+ BRIGHT_CYAN_256 = "\033[1m\033[38;2;0;255;255m" # Bold + rgb(0, 255, 255)
82
+ BRIGHT_BLUE_256 = "\033[1m\033[38;2;94;156;255m" # Bold + rgb(94, 156, 255)
83
+ BRIGHT_GREEN_256 = "\033[1m\033[38;2;90;247;142m" # Bold + rgb(90, 247, 142)
84
+ BRIGHT_YELLOW_256 = "\033[1m\033[38;2;255;231;76m" # Bold + rgb(255, 231, 76)
85
+ BRIGHT_MAGENTA_256 = "\033[1m\033[38;2;255;92;205m" # Bold + rgb(255, 92, 205)
86
+ BRIGHT_RED_256 = "\033[1m\033[38;2;255;85;85m" # Bold + rgb(255, 85, 85)
87
+
88
+ # Neon Minimal Palette - RGB True Color (24-bit)
89
+ # Primary: Lime Green #a3e635
90
+ LIME = "\033[38;2;163;230;53m"
91
+ LIME_LIGHT = "\033[38;2;190;242;100m"
92
+ LIME_DARK = "\033[38;2;132;204;22m"
93
+
94
+ # Info: Cyan #06b6d4
95
+ INFO_CYAN = "\033[38;2;6;182;212m"
96
+ INFO_CYAN_LIGHT = "\033[38;2;34;211;238m"
97
+ INFO_CYAN_DARK = "\033[38;2;8;145;178m"
98
+
99
+ # Warning: Gold #eab308
100
+ WARNING_GOLD = "\033[38;2;234;179;8m"
101
+ WARNING_GOLD_LIGHT = "\033[38;2;253;224;71m"
102
+ WARNING_GOLD_DARK = "\033[38;2;202;138;4m"
103
+
104
+ # Error: Bright Red #ef4444
105
+ ERROR_RED = "\033[38;2;239;68;68m"
106
+ ERROR_RED_LIGHT = "\033[38;2;248;113;113m"
107
+ ERROR_RED_DARK = "\033[38;2;220;38;38m"
108
+
109
+ # Muted: Steel #71717a
110
+ MUTED_STEEL = "\033[38;2;113;113;122m"
111
+ DIM_STEEL = "\033[2;38;2;113;113;122m"
112
+
113
+ # Grey gradient levels (256-color palette)
114
+ GREY_LEVELS = [255, 254, 253, 252, 251, 250]
115
+
116
+ # Dim white gradient levels (bright white to subtle dim white)
117
+ DIM_WHITE_LEVELS = [255, 254, 253, 252, 251, 250]
118
+
119
+ # Lime green gradient scheme RGB values for ultra-smooth gradients
120
+ DIM_SCHEME_COLORS = [
121
+ (190, 242, 100), # Bright lime (#bef264)
122
+ (175, 235, 80), # Light lime
123
+ (163, 230, 53), # Primary lime (#a3e635) - hero color!
124
+ (145, 210, 45), # Medium lime
125
+ (132, 204, 22), # Darker lime (#84cc16)
126
+ (115, 180, 18), # Deep lime
127
+ (100, 160, 15), # Strong lime
128
+ (115, 180, 18), # Deep lime (return)
129
+ (132, 204, 22), # Darker lime (return)
130
+ (163, 230, 53), # Primary lime (return)
131
+ (190, 242, 100), # Bright lime
132
+ ]
133
+
134
+
135
+ class GradientRenderer:
136
+ """Handles various gradient effects."""
137
+
138
+ @staticmethod
139
+ def apply_white_to_grey(text: str) -> str:
140
+ """Apply smooth white-to-grey gradient effect.
141
+
142
+ Args:
143
+ text: Text to apply gradient to.
144
+
145
+ Returns:
146
+ Text with gradient effect applied.
147
+ """
148
+ if not text or "\033[" in text:
149
+ return text
150
+
151
+ result = []
152
+ text_length = len(text)
153
+ grey_levels = ColorPalette.GREY_LEVELS
154
+
155
+ for i, char in enumerate(text):
156
+ # Calculate position in gradient (0.0 to 1.0)
157
+ position = i / max(1, text_length - 1)
158
+
159
+ # Map to grey level with smooth interpolation
160
+ level_index = position * (len(grey_levels) - 1)
161
+ level_index = min(int(level_index), len(grey_levels) - 1)
162
+
163
+ grey_level = grey_levels[level_index]
164
+ color_code = f"\033[38;5;{grey_level}m"
165
+ result.append(f"{color_code}{char}")
166
+
167
+ result.append(ColorPalette.RESET)
168
+ return "".join(result)
169
+
170
+ @staticmethod
171
+ def apply_dim_white_gradient(text: str) -> str:
172
+ """Apply subtle dim white to dimmer white gradient.
173
+
174
+ Args:
175
+ text: Text to apply gradient to.
176
+
177
+ Returns:
178
+ Text with dim white gradient applied.
179
+ """
180
+ if not text or "\033[" in text:
181
+ return text
182
+
183
+ result = []
184
+ text_length = len(text)
185
+ dim_levels = ColorPalette.DIM_WHITE_LEVELS
186
+
187
+ for i, char in enumerate(text):
188
+ # Calculate position in gradient (0.0 to 1.0)
189
+ position = i / max(1, text_length - 1)
190
+
191
+ # Map to dim white level with smooth interpolation
192
+ level_index = position * (len(dim_levels) - 1)
193
+ level_index = min(int(level_index), len(dim_levels) - 1)
194
+
195
+ dim_level = dim_levels[level_index]
196
+ color_code = f"\033[38;5;{dim_level}m"
197
+ result.append(f"{color_code}{char}")
198
+
199
+ result.append(ColorPalette.RESET)
200
+ return "".join(result)
201
+
202
+ @staticmethod
203
+ def apply_dim_scheme_gradient(text: str) -> str:
204
+ """Apply ultra-smooth gradient using dim color scheme.
205
+
206
+ Args:
207
+ text: Text to apply gradient to.
208
+
209
+ Returns:
210
+ Text with dim scheme gradient applied.
211
+ """
212
+ if not text:
213
+ return text
214
+
215
+ result = []
216
+ text_length = len(text)
217
+ color_rgb = ColorPalette.DIM_SCHEME_COLORS
218
+
219
+ for i, char in enumerate(text):
220
+ position = i / max(1, text_length - 1)
221
+ scaled_pos = position * (len(color_rgb) - 1)
222
+ color_index = int(scaled_pos)
223
+ t = scaled_pos - color_index
224
+
225
+ if color_index >= len(color_rgb) - 1:
226
+ r, g, b = color_rgb[-1]
227
+ else:
228
+ curr_rgb = color_rgb[color_index]
229
+ next_rgb = color_rgb[color_index + 1]
230
+
231
+ r = curr_rgb[0] + (next_rgb[0] - curr_rgb[0]) * t
232
+ g = curr_rgb[1] + (next_rgb[1] - curr_rgb[1]) * t
233
+ b = curr_rgb[2] + (next_rgb[2] - curr_rgb[2]) * t
234
+
235
+ r, g, b = int(r), int(g), int(b)
236
+ color_code = f"\033[38;2;{r};{g};{b}m"
237
+ result.append(f"{color_code}{char}")
238
+
239
+ result.append(ColorPalette.RESET)
240
+ return "".join(result)
241
+
242
+ @staticmethod
243
+ def apply_custom_gradient(text: str, colors: List[Tuple[int, int, int]]) -> str:
244
+ """Apply custom RGB gradient to text.
245
+
246
+ Args:
247
+ text: Text to apply gradient to.
248
+ colors: List of RGB color tuples for gradient stops.
249
+
250
+ Returns:
251
+ Text with custom gradient applied.
252
+ """
253
+ if not text or len(colors) < 2:
254
+ return text
255
+
256
+ result = []
257
+ text_length = len(text)
258
+
259
+ for i, char in enumerate(text):
260
+ position = i / max(1, text_length - 1)
261
+ scaled_pos = position * (len(colors) - 1)
262
+ color_index = int(scaled_pos)
263
+ t = scaled_pos - color_index
264
+
265
+ if color_index >= len(colors) - 1:
266
+ r, g, b = colors[-1]
267
+ else:
268
+ curr_rgb = colors[color_index]
269
+ next_rgb = colors[color_index + 1]
270
+
271
+ r = curr_rgb[0] + (next_rgb[0] - curr_rgb[0]) * t
272
+ g = curr_rgb[1] + (next_rgb[1] - curr_rgb[1]) * t
273
+ b = curr_rgb[2] + (next_rgb[2] - curr_rgb[2]) * t
274
+
275
+ r, g, b = int(r), int(g), int(b)
276
+ color_code = f"\033[38;2;{r};{g};{b}m"
277
+ result.append(f"{color_code}{char}")
278
+
279
+ result.append(ColorPalette.RESET)
280
+ return "".join(result)
281
+
282
+
283
+ class ShimmerEffect:
284
+ """Handles shimmer animation effects."""
285
+
286
+ def __init__(self, speed: int = 3, wave_width: int = 4):
287
+ """Initialize shimmer effect.
288
+
289
+ Args:
290
+ speed: Animation speed (frames between updates).
291
+ wave_width: Width of shimmer wave in characters.
292
+ """
293
+ self.speed = speed
294
+ self.wave_width = wave_width
295
+ self.frame_counter = 0
296
+ self.position = 0
297
+
298
+ def configure(self, speed: int, wave_width: int) -> None:
299
+ """Configure shimmer parameters.
300
+
301
+ Args:
302
+ speed: Animation speed.
303
+ wave_width: Wave width.
304
+ """
305
+ self.speed = speed
306
+ self.wave_width = wave_width
307
+
308
+ def apply_shimmer(self, text: str) -> str:
309
+ """Apply elegant wave shimmer effect to text.
310
+
311
+ Args:
312
+ text: Text to apply shimmer to.
313
+
314
+ Returns:
315
+ Text with shimmer effect applied.
316
+ """
317
+ if not text:
318
+ return text
319
+
320
+ # Update shimmer position
321
+ self.frame_counter = (self.frame_counter + 1) % self.speed
322
+ if self.frame_counter == 0:
323
+ self.position = (self.position + 1) % (len(text) + self.wave_width * 2)
324
+
325
+ result = []
326
+ for i, char in enumerate(text):
327
+ distance = abs(i - self.position)
328
+
329
+ if distance == 0:
330
+ # Center - bright cyan
331
+ result.append(
332
+ f"{ColorPalette.BRIGHT_CYAN}{char}{ColorPalette.RESET}"
333
+ )
334
+ elif distance == 1:
335
+ # Adjacent - bright blue
336
+ result.append(
337
+ f"{ColorPalette.BRIGHT_BLUE}{char}{ColorPalette.RESET}"
338
+ )
339
+ elif distance == 2:
340
+ # Second ring - normal blue
341
+ result.append(
342
+ f"{ColorPalette.NORMAL_BLUE}{char}{ColorPalette.RESET}"
343
+ )
344
+ elif distance <= self.wave_width:
345
+ # Edge - dim blue
346
+ result.append(f"{ColorPalette.DIM_BLUE}{char}{ColorPalette.RESET}")
347
+ else:
348
+ # Base - darker dim blue
349
+ result.append(f"\033[2;94m{char}{ColorPalette.RESET}")
350
+
351
+ return "".join(result)
352
+
353
+
354
+ class StatusColorizer:
355
+ """Handles semantic coloring of status text with ASCII icons."""
356
+
357
+ # ASCII icon mapping (no emojis)
358
+ ASCII_ICONS = {
359
+ "checkmark": "√",
360
+ "error": "×",
361
+ "processing": "*",
362
+ "active": "+",
363
+ "inactive": "-",
364
+ "ratio": "::",
365
+ "arrow_right": ">",
366
+ "separator": "|",
367
+ "loading": "...",
368
+ "count": "#",
369
+ "circle_filled": "●",
370
+ "circle_empty": "○",
371
+ "circle_dot": "•",
372
+ }
373
+
374
+ @staticmethod
375
+ def get_ascii_icon(icon_type: str) -> str:
376
+ """Get ASCII icon by type.
377
+
378
+ Args:
379
+ icon_type: Type of icon to retrieve.
380
+
381
+ Returns:
382
+ ASCII character for the icon.
383
+ """
384
+ return StatusColorizer.ASCII_ICONS.get(icon_type, "")
385
+
386
+ @staticmethod
387
+ def apply_status_colors(text: str) -> str:
388
+ """Apply semantic colors to status line text with ASCII icons.
389
+
390
+ Args:
391
+ text: Status text to colorize.
392
+
393
+ Returns:
394
+ Colorized text with ANSI codes and ASCII icons.
395
+ """
396
+ # Replace emoji-style indicators with ASCII equivalents
397
+ text = text.replace(
398
+ "🟢",
399
+ f"{ColorPalette.BRIGHT_GREEN}"
400
+ f"{StatusColorizer.ASCII_ICONS['circle_filled']}"
401
+ f"{ColorPalette.RESET}",
402
+ )
403
+ text = text.replace(
404
+ "🟡",
405
+ f"{ColorPalette.DIM_YELLOW}"
406
+ f"{StatusColorizer.ASCII_ICONS['circle_filled']}"
407
+ f"{ColorPalette.RESET}",
408
+ )
409
+ text = text.replace(
410
+ "🔴",
411
+ f"{ColorPalette.DIM_RED}"
412
+ f"{StatusColorizer.ASCII_ICONS['circle_filled']}"
413
+ f"{ColorPalette.RESET}",
414
+ )
415
+ text = text.replace(
416
+ "✅",
417
+ f"{ColorPalette.BRIGHT_GREEN}"
418
+ f"{StatusColorizer.ASCII_ICONS['checkmark']}"
419
+ f"{ColorPalette.RESET}",
420
+ )
421
+ text = text.replace(
422
+ "❌",
423
+ f"{ColorPalette.DIM_RED}"
424
+ f"{StatusColorizer.ASCII_ICONS['error']}"
425
+ f"{ColorPalette.RESET}",
426
+ )
427
+
428
+ # Number/count highlighting (dim cyan for metrics)
429
+ text = re.sub(
430
+ r"\b(\d{1,3}(?:,\d{3})*)\b",
431
+ f"{ColorPalette.DIM_CYAN}\\1{ColorPalette.RESET}",
432
+ text,
433
+ )
434
+
435
+ # ASCII icon patterns
436
+ text = re.sub(
437
+ r"\b(✓)\s*",
438
+ f"{ColorPalette.BRIGHT_GREEN}\\1{ColorPalette.RESET} ",
439
+ text,
440
+ ) # Checkmarks
441
+ text = re.sub(
442
+ r"\b(×)\s*",
443
+ f"{ColorPalette.DIM_RED}\\1{ColorPalette.RESET} ",
444
+ text,
445
+ ) # Errors
446
+ text = re.sub(
447
+ r"\b(\*)\s*",
448
+ f"{ColorPalette.DIM_YELLOW}\\1{ColorPalette.RESET} ",
449
+ text,
450
+ ) # Processing
451
+ text = re.sub(
452
+ r"\b(\+)\s*",
453
+ f"{ColorPalette.BRIGHT_GREEN}\\1{ColorPalette.RESET} ",
454
+ text,
455
+ ) # Active
456
+ text = re.sub(
457
+ r"(^|\s)(-)\s+",
458
+ f"\\1{ColorPalette.DIM_CYAN}\\2{ColorPalette.RESET} ",
459
+ text,
460
+ ) # Inactive (list markers only)
461
+
462
+ # Status indicators
463
+ text = re.sub(
464
+ r"\b(Processing: Yes)\b",
465
+ f"{ColorPalette.DIM_YELLOW}\\1{ColorPalette.RESET}",
466
+ text,
467
+ )
468
+ text = re.sub(
469
+ r"\b(Processing: No)\b",
470
+ f"{ColorPalette.BRIGHT_GREEN}\\1{ColorPalette.RESET}",
471
+ text,
472
+ )
473
+ text = re.sub(
474
+ r"\b(Ready)\b",
475
+ f"{ColorPalette.BRIGHT_GREEN}\\1{ColorPalette.RESET}",
476
+ text,
477
+ )
478
+ text = re.sub(
479
+ r"\b(Active)\b",
480
+ f"{ColorPalette.DIM_YELLOW}\\1{ColorPalette.RESET}",
481
+ text,
482
+ )
483
+ text = re.sub(
484
+ r"\b(On)\b",
485
+ f"{ColorPalette.DIM_YELLOW}\\1{ColorPalette.RESET}",
486
+ text,
487
+ )
488
+ text = re.sub(
489
+ r"\b(Off)\b",
490
+ f"{ColorPalette.DIM_CYAN}\\1{ColorPalette.RESET}",
491
+ text,
492
+ )
493
+
494
+ # Queue states
495
+ text = re.sub(
496
+ r"\b(Queue: 0)\b",
497
+ f"{ColorPalette.BRIGHT_GREEN}\\1{ColorPalette.RESET}",
498
+ text,
499
+ )
500
+ text = re.sub(
501
+ r"\b(Queue: [1-9][0-9]*)\b",
502
+ f"{ColorPalette.DIM_YELLOW}\\1{ColorPalette.RESET}",
503
+ text,
504
+ )
505
+
506
+ # Time measurements
507
+ text = re.sub(
508
+ r"\b(\d+\.\d+s)\b",
509
+ f"{ColorPalette.DIM_MAGENTA}\\1{ColorPalette.RESET}",
510
+ text,
511
+ )
512
+
513
+ # Ratio highlighting (with :: separator)
514
+ text = re.sub(
515
+ r"\b(\d+):(\d+)\b",
516
+ f"{ColorPalette.DIM_BLUE}\\1{ColorPalette.DIM_CYAN}::"
517
+ f"{ColorPalette.DIM_BLUE}\\2{ColorPalette.RESET}",
518
+ text,
519
+ )
520
+ text = re.sub(
521
+ r"\b(Enhanced: \d+/\d+)",
522
+ f"{ColorPalette.DIM_BLUE}\\1{ColorPalette.RESET}",
523
+ text,
524
+ )
525
+
526
+ # Percentage highlighting
527
+ text = re.sub(
528
+ r"\b(\d+\.\d+%)\b",
529
+ f"{ColorPalette.DIM_MAGENTA}\\1{ColorPalette.RESET}",
530
+ text,
531
+ )
532
+
533
+ # Token highlighting
534
+ text = re.sub(
535
+ r"\b(\d+\s*tok)\b",
536
+ f"{ColorPalette.DIM_CYAN}\\1{ColorPalette.RESET}",
537
+ text,
538
+ )
539
+ text = re.sub(
540
+ r"\b(\d+K\s*tok)\b",
541
+ f"{ColorPalette.DIM_CYAN}\\1{ColorPalette.RESET}",
542
+ text,
543
+ )
544
+
545
+ return text
546
+
547
+
548
+ class BannerRenderer:
549
+ """Handles ASCII banner creation and rendering."""
550
+
551
+ KOLLABOR_ASCII2 = [
552
+ "██╗ ██╗ ██████╗ ██╗ ██╗ █████╗ ██████╗ ██████╗ ██████╗ ",
553
+ "██║ ██╔╝██╔═══██╗██║ ██║ ██╔══██╗██╔══██╗██╔═══██╗██╔══██╗",
554
+ "█████╔╝ ██║ ██║██║ ██║ ███████║██████╔╝██║ ██║██████╔╝",
555
+ "██╔═██╗ ██║ ██║██║ ██║ ██╔══██║██╔══██╗██║ ██║██╔══██╗",
556
+ "██║ ██╗╚██████╔╝███████╗███████╗██║ ██║██████╔╝╚██████╔╝██║ ██║",
557
+ "╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚══════╝╚═╝ ╚═╝╚═════╝ ╚═════╝ ╚═╝ ╚═╝",
558
+ ]
559
+
560
+ KOLLABOR_ASCII_v1 = [
561
+ "▒█░▄▀ █▀▀█ █░░ █░░ █▀▀█ █▀▀▄ █▀▀█ █▀▀█ █▀▀█ ▀█▀",
562
+ "▒█▀▄░ █░░█ █░░ █░░ █▄▄█ █▀▀▄ █░░█ █▄▄▀ █▄▄█ ░█░",
563
+ "▒█░▒█ ▀▀▀▀ ▀▀▀ ▀▀▀ ▀░░▀ ▀▀▀░ ▀▀▀▀ ▀░▀▀ ▄ ▀░░▀ ▄█▄",
564
+ ]
565
+ KOLLABOR_ASCII_v2 = [
566
+ "\r ──────────────────────────────────────────────── ",
567
+ "\r █ ▄▀ █▀▀█ █ █ █▀▀█ █▀▀▄ █▀▀█ █▀▀█ █▀▀█ ▀█▀ ",
568
+ "\r █▀▄ █ █ █ █ █▄▄█ █▀▀▄ █ █ █▄▄▀ █▄▄█ █ ",
569
+ "\r ▀ ▀ ▀▀▀▀ ▀▀▀ ▀▀▀ ▀ ▀ ▀▀▀ ▀▀▀▀ ▀ ▀▀ ▀ ▀ ▀ ▀▀▀ ",
570
+ "\r ──────────────────────────────────────────────── ",
571
+ ]
572
+ KOLLABOR_ASCII = [
573
+ "\r ██╗ ██╔════════════════════════════════════════════╗",
574
+ "\r ██║ ██╔╝ ║",
575
+ "\r █████╔╝ █▀▀█ █ █ █▀▀█ █▀▀▄ █▀▀█ █▀▀█ █▀▀█ ▀█▀ ║",
576
+ "\r ██╔═██╗ █ █ █ █ █▄▄█ █▀▀▄ █ █ █▄▄▀ █▄▄█ █ ║",
577
+ "\r ██║ ██╗▀▀▀▀ ▀▀▀ ▀▀▀ ▀ ▀ ▀▀▀ ▀▀▀▀ ▀ ▀▀ ▀ ▀ ▀ ▀▀▀ ║",
578
+ "\r ╚═╝ ╚═╩════════════════════════════════════════════╝",
579
+ ]
580
+
581
+ @classmethod
582
+ def create_kollabor_banner(cls, version: str = "v1.0.0") -> str:
583
+ """Create beautiful Kollabor ASCII banner with gradient.
584
+
585
+ Args:
586
+ version: Version string to display.
587
+
588
+ Returns:
589
+ Formatted banner with gradient colors and version.
590
+ """
591
+ gradient_lines = []
592
+ for i, line in enumerate(cls.KOLLABOR_ASCII):
593
+ gradient_line = GradientRenderer.apply_dim_scheme_gradient(line)
594
+
595
+ # Add version to first line
596
+ if i == 0:
597
+ gradient_line += f" {ColorPalette.DIM}{version}{ColorPalette.RESET}"
598
+
599
+ gradient_lines.append(gradient_line)
600
+
601
+ return f"\n{chr(10).join(gradient_lines)}\n"
602
+
603
+
604
+ class VisualEffects:
605
+ """Main visual effects coordinator."""
606
+
607
+ def __init__(self):
608
+ """Initialize visual effects system."""
609
+ self.gradient_renderer = GradientRenderer()
610
+ self.shimmer_effect = ShimmerEffect()
611
+ self.status_colorizer = StatusColorizer()
612
+ self.banner_renderer = BannerRenderer()
613
+
614
+ # Effect configurations
615
+ self._effects_config: Dict[str, EffectConfig] = {
616
+ "thinking": EffectConfig(EffectType.SHIMMER, speed=3, width=4),
617
+ "gradient": EffectConfig(EffectType.GRADIENT),
618
+ "status": EffectConfig(EffectType.COLOR),
619
+ "banner": EffectConfig(EffectType.GRADIENT),
620
+ }
621
+
622
+ def configure_effect(self, effect_name: str, **kwargs) -> None:
623
+ """Configure a specific effect.
624
+
625
+ Args:
626
+ effect_name: Name of effect to configure.
627
+ **kwargs: Configuration parameters.
628
+ """
629
+ if effect_name in self._effects_config:
630
+ config = self._effects_config[effect_name]
631
+ for key, value in kwargs.items():
632
+ if hasattr(config, key):
633
+ setattr(config, key, value)
634
+
635
+ # Special handling for shimmer effect
636
+ if effect_name == "thinking":
637
+ self.shimmer_effect.configure(
638
+ kwargs.get("speed", 3), kwargs.get("width", 4)
639
+ )
640
+
641
+ def apply_thinking_effect(self, text: str, effect_type: str = "shimmer") -> str:
642
+ """Apply thinking visualization effect.
643
+
644
+ Args:
645
+ text: Text to apply effect to.
646
+ effect_type: Type of effect ("shimmer", "dim", "normal").
647
+
648
+ Returns:
649
+ Text with thinking effect applied.
650
+ """
651
+ config = self._effects_config.get("thinking")
652
+ if not config or not config.enabled:
653
+ return text
654
+
655
+ if effect_type == "shimmer":
656
+ return self.shimmer_effect.apply_shimmer(text)
657
+ elif effect_type == "dim":
658
+ return f"{ColorPalette.DIM}{text}{ColorPalette.RESET}"
659
+ else:
660
+ return text
661
+
662
+ def apply_message_gradient(
663
+ self, text: str, gradient_type: str = "dim_white"
664
+ ) -> str:
665
+ """Apply gradient effect to message text.
666
+
667
+ Args:
668
+ text: Text to apply gradient to.
669
+ gradient_type: Type of gradient to apply.
670
+
671
+ Returns:
672
+ Text with gradient applied.
673
+ """
674
+ config = self._effects_config.get("gradient")
675
+ if not config or not config.enabled:
676
+ return text
677
+
678
+ if gradient_type == "white_to_grey":
679
+ return self.gradient_renderer.apply_white_to_grey(text)
680
+ elif gradient_type == "dim_white":
681
+ return self.gradient_renderer.apply_dim_white_gradient(text)
682
+ elif gradient_type == "dim_scheme":
683
+ return self.gradient_renderer.apply_dim_scheme_gradient(text)
684
+ else:
685
+ return text
686
+
687
+ def apply_status_colors(self, text: str) -> str:
688
+ """Apply status colors to text.
689
+
690
+ Args:
691
+ text: Text to colorize.
692
+
693
+ Returns:
694
+ Colorized text.
695
+ """
696
+ config = self._effects_config.get("status")
697
+ if not config or not config.enabled:
698
+ return text
699
+
700
+ return self.status_colorizer.apply_status_colors(text)
701
+
702
+ def create_banner(self, version: str = "v1.0.0") -> str:
703
+ """Create application banner.
704
+
705
+ Args:
706
+ version: Version string.
707
+
708
+ Returns:
709
+ Formatted banner.
710
+ """
711
+ config = self._effects_config.get("banner")
712
+ if not config or not config.enabled:
713
+ return f"KOLLABOR {version}\n"
714
+
715
+ return self.banner_renderer.create_kollabor_banner(version)
716
+
717
+ def get_effect_stats(self) -> Dict[str, Any]:
718
+ """Get visual effects statistics.
719
+
720
+ Returns:
721
+ Dictionary with effect statistics.
722
+ """
723
+ return {
724
+ "shimmer_position": self.shimmer_effect.position,
725
+ "shimmer_frame_counter": self.shimmer_effect.frame_counter,
726
+ "effects_config": {
727
+ name: {
728
+ "enabled": config.enabled,
729
+ "type": getattr(config.effect_type, "value", config.effect_type),
730
+ "intensity": config.intensity,
731
+ }
732
+ for name, config in self._effects_config.items()
733
+ },
734
+ }