kollabor 0.4.9__py3-none-any.whl → 0.4.15__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 (192) hide show
  1. agents/__init__.py +2 -0
  2. agents/coder/__init__.py +0 -0
  3. agents/coder/agent.json +4 -0
  4. agents/coder/api-integration.md +2150 -0
  5. agents/coder/cli-pretty.md +765 -0
  6. agents/coder/code-review.md +1092 -0
  7. agents/coder/database-design.md +1525 -0
  8. agents/coder/debugging.md +1102 -0
  9. agents/coder/dependency-management.md +1397 -0
  10. agents/coder/git-workflow.md +1099 -0
  11. agents/coder/refactoring.md +1454 -0
  12. agents/coder/security-hardening.md +1732 -0
  13. agents/coder/system_prompt.md +1448 -0
  14. agents/coder/tdd.md +1367 -0
  15. agents/creative-writer/__init__.py +0 -0
  16. agents/creative-writer/agent.json +4 -0
  17. agents/creative-writer/character-development.md +1852 -0
  18. agents/creative-writer/dialogue-craft.md +1122 -0
  19. agents/creative-writer/plot-structure.md +1073 -0
  20. agents/creative-writer/revision-editing.md +1484 -0
  21. agents/creative-writer/system_prompt.md +690 -0
  22. agents/creative-writer/worldbuilding.md +2049 -0
  23. agents/data-analyst/__init__.py +30 -0
  24. agents/data-analyst/agent.json +4 -0
  25. agents/data-analyst/data-visualization.md +992 -0
  26. agents/data-analyst/exploratory-data-analysis.md +1110 -0
  27. agents/data-analyst/pandas-data-manipulation.md +1081 -0
  28. agents/data-analyst/sql-query-optimization.md +881 -0
  29. agents/data-analyst/statistical-analysis.md +1118 -0
  30. agents/data-analyst/system_prompt.md +928 -0
  31. agents/default/__init__.py +0 -0
  32. agents/default/agent.json +4 -0
  33. agents/default/dead-code.md +794 -0
  34. agents/default/explore-agent-system.md +585 -0
  35. agents/default/system_prompt.md +1448 -0
  36. agents/kollabor/__init__.py +0 -0
  37. agents/kollabor/analyze-plugin-lifecycle.md +175 -0
  38. agents/kollabor/analyze-terminal-rendering.md +388 -0
  39. agents/kollabor/code-review.md +1092 -0
  40. agents/kollabor/debug-mcp-integration.md +521 -0
  41. agents/kollabor/debug-plugin-hooks.md +547 -0
  42. agents/kollabor/debugging.md +1102 -0
  43. agents/kollabor/dependency-management.md +1397 -0
  44. agents/kollabor/git-workflow.md +1099 -0
  45. agents/kollabor/inspect-llm-conversation.md +148 -0
  46. agents/kollabor/monitor-event-bus.md +558 -0
  47. agents/kollabor/profile-performance.md +576 -0
  48. agents/kollabor/refactoring.md +1454 -0
  49. agents/kollabor/system_prompt copy.md +1448 -0
  50. agents/kollabor/system_prompt.md +757 -0
  51. agents/kollabor/trace-command-execution.md +178 -0
  52. agents/kollabor/validate-config.md +879 -0
  53. agents/research/__init__.py +0 -0
  54. agents/research/agent.json +4 -0
  55. agents/research/architecture-mapping.md +1099 -0
  56. agents/research/codebase-analysis.md +1077 -0
  57. agents/research/dependency-audit.md +1027 -0
  58. agents/research/performance-profiling.md +1047 -0
  59. agents/research/security-review.md +1359 -0
  60. agents/research/system_prompt.md +492 -0
  61. agents/technical-writer/__init__.py +0 -0
  62. agents/technical-writer/agent.json +4 -0
  63. agents/technical-writer/api-documentation.md +2328 -0
  64. agents/technical-writer/changelog-management.md +1181 -0
  65. agents/technical-writer/readme-writing.md +1360 -0
  66. agents/technical-writer/style-guide.md +1410 -0
  67. agents/technical-writer/system_prompt.md +653 -0
  68. agents/technical-writer/tutorial-creation.md +1448 -0
  69. core/__init__.py +0 -2
  70. core/application.py +343 -88
  71. core/cli.py +229 -10
  72. core/commands/menu_renderer.py +463 -59
  73. core/commands/registry.py +14 -9
  74. core/commands/system_commands.py +2461 -14
  75. core/config/loader.py +151 -37
  76. core/config/service.py +18 -6
  77. core/events/bus.py +29 -9
  78. core/events/executor.py +205 -75
  79. core/events/models.py +27 -8
  80. core/fullscreen/command_integration.py +20 -24
  81. core/fullscreen/components/__init__.py +10 -1
  82. core/fullscreen/components/matrix_components.py +1 -2
  83. core/fullscreen/components/space_shooter_components.py +654 -0
  84. core/fullscreen/plugin.py +5 -0
  85. core/fullscreen/renderer.py +52 -13
  86. core/fullscreen/session.py +52 -15
  87. core/io/__init__.py +29 -5
  88. core/io/buffer_manager.py +6 -1
  89. core/io/config_status_view.py +7 -29
  90. core/io/core_status_views.py +267 -347
  91. core/io/input/__init__.py +25 -0
  92. core/io/input/command_mode_handler.py +711 -0
  93. core/io/input/display_controller.py +128 -0
  94. core/io/input/hook_registrar.py +286 -0
  95. core/io/input/input_loop_manager.py +421 -0
  96. core/io/input/key_press_handler.py +502 -0
  97. core/io/input/modal_controller.py +1011 -0
  98. core/io/input/paste_processor.py +339 -0
  99. core/io/input/status_modal_renderer.py +184 -0
  100. core/io/input_errors.py +5 -1
  101. core/io/input_handler.py +211 -2452
  102. core/io/key_parser.py +7 -0
  103. core/io/layout.py +15 -3
  104. core/io/message_coordinator.py +111 -2
  105. core/io/message_renderer.py +129 -4
  106. core/io/status_renderer.py +147 -607
  107. core/io/terminal_renderer.py +97 -51
  108. core/io/terminal_state.py +21 -4
  109. core/io/visual_effects.py +816 -165
  110. core/llm/agent_manager.py +1063 -0
  111. core/llm/api_adapters/__init__.py +44 -0
  112. core/llm/api_adapters/anthropic_adapter.py +432 -0
  113. core/llm/api_adapters/base.py +241 -0
  114. core/llm/api_adapters/openai_adapter.py +326 -0
  115. core/llm/api_communication_service.py +167 -113
  116. core/llm/conversation_logger.py +322 -16
  117. core/llm/conversation_manager.py +556 -30
  118. core/llm/file_operations_executor.py +84 -32
  119. core/llm/llm_service.py +934 -103
  120. core/llm/mcp_integration.py +541 -57
  121. core/llm/message_display_service.py +135 -18
  122. core/llm/plugin_sdk.py +1 -2
  123. core/llm/profile_manager.py +1183 -0
  124. core/llm/response_parser.py +274 -56
  125. core/llm/response_processor.py +16 -3
  126. core/llm/tool_executor.py +6 -1
  127. core/logging/__init__.py +2 -0
  128. core/logging/setup.py +34 -6
  129. core/models/resume.py +54 -0
  130. core/plugins/__init__.py +4 -2
  131. core/plugins/base.py +127 -0
  132. core/plugins/collector.py +23 -161
  133. core/plugins/discovery.py +37 -3
  134. core/plugins/factory.py +6 -12
  135. core/plugins/registry.py +5 -17
  136. core/ui/config_widgets.py +128 -28
  137. core/ui/live_modal_renderer.py +2 -1
  138. core/ui/modal_actions.py +5 -0
  139. core/ui/modal_overlay_renderer.py +0 -60
  140. core/ui/modal_renderer.py +268 -7
  141. core/ui/modal_state_manager.py +29 -4
  142. core/ui/widgets/base_widget.py +7 -0
  143. core/updates/__init__.py +10 -0
  144. core/updates/version_check_service.py +348 -0
  145. core/updates/version_comparator.py +103 -0
  146. core/utils/config_utils.py +685 -526
  147. core/utils/plugin_utils.py +1 -1
  148. core/utils/session_naming.py +111 -0
  149. fonts/LICENSE +21 -0
  150. fonts/README.md +46 -0
  151. fonts/SymbolsNerdFont-Regular.ttf +0 -0
  152. fonts/SymbolsNerdFontMono-Regular.ttf +0 -0
  153. fonts/__init__.py +44 -0
  154. {kollabor-0.4.9.dist-info → kollabor-0.4.15.dist-info}/METADATA +54 -4
  155. kollabor-0.4.15.dist-info/RECORD +228 -0
  156. {kollabor-0.4.9.dist-info → kollabor-0.4.15.dist-info}/top_level.txt +2 -0
  157. plugins/agent_orchestrator/__init__.py +39 -0
  158. plugins/agent_orchestrator/activity_monitor.py +181 -0
  159. plugins/agent_orchestrator/file_attacher.py +77 -0
  160. plugins/agent_orchestrator/message_injector.py +135 -0
  161. plugins/agent_orchestrator/models.py +48 -0
  162. plugins/agent_orchestrator/orchestrator.py +403 -0
  163. plugins/agent_orchestrator/plugin.py +976 -0
  164. plugins/agent_orchestrator/xml_parser.py +191 -0
  165. plugins/agent_orchestrator_plugin.py +9 -0
  166. plugins/enhanced_input/box_styles.py +1 -0
  167. plugins/enhanced_input/color_engine.py +19 -4
  168. plugins/enhanced_input/config.py +2 -2
  169. plugins/enhanced_input_plugin.py +61 -11
  170. plugins/fullscreen/__init__.py +6 -2
  171. plugins/fullscreen/example_plugin.py +1035 -222
  172. plugins/fullscreen/setup_wizard_plugin.py +592 -0
  173. plugins/fullscreen/space_shooter_plugin.py +131 -0
  174. plugins/hook_monitoring_plugin.py +436 -78
  175. plugins/query_enhancer_plugin.py +66 -30
  176. plugins/resume_conversation_plugin.py +1494 -0
  177. plugins/save_conversation_plugin.py +98 -32
  178. plugins/system_commands_plugin.py +70 -56
  179. plugins/tmux_plugin.py +154 -78
  180. plugins/workflow_enforcement_plugin.py +94 -92
  181. system_prompt/default.md +952 -886
  182. core/io/input_mode_manager.py +0 -402
  183. core/io/modal_interaction_handler.py +0 -315
  184. core/io/raw_input_processor.py +0 -946
  185. core/storage/__init__.py +0 -5
  186. core/storage/state_manager.py +0 -84
  187. core/ui/widget_integration.py +0 -222
  188. core/utils/key_reader.py +0 -171
  189. kollabor-0.4.9.dist-info/RECORD +0 -128
  190. {kollabor-0.4.9.dist-info → kollabor-0.4.15.dist-info}/WHEEL +0 -0
  191. {kollabor-0.4.9.dist-info → kollabor-0.4.15.dist-info}/entry_points.txt +0 -0
  192. {kollabor-0.4.9.dist-info → kollabor-0.4.15.dist-info}/licenses/LICENSE +0 -0
@@ -1,327 +1,1140 @@
1
- """Example full-screen plugin demonstrating framework capabilities."""
1
+ """Enhanced example full-screen plugin demonstrating all framework capabilities.
2
+
3
+ This comprehensive plugin showcases:
4
+ - Table of contents navigation
5
+ - Framework statistics and info
6
+ - Drawing primitives (borders, shapes, progress, spinners)
7
+ - Color showcase (256 colors, gradients)
8
+ - Advanced animations (easing, particles, physics)
9
+ - Interactive components (text input, checkboxes, sliders)
10
+ - Calculator mini-app
11
+ - Todo list mini-app
12
+ - Conway's Game of Life
13
+ - Performance comparison
14
+ - Code examples with syntax highlighting
15
+ - Resources and documentation
16
+ """
2
17
 
3
18
  import asyncio
4
19
  import math
20
+ import random
21
+ import time
22
+ from typing import List, Tuple, Dict, Optional
23
+ from dataclasses import dataclass
24
+
5
25
  from core.fullscreen import FullScreenPlugin
6
26
  from core.fullscreen.plugin import PluginMetadata
7
27
  from core.fullscreen.components.drawing import DrawingPrimitives
8
28
  from core.fullscreen.components.animation import AnimationFramework, EasingFunctions
9
- from core.io.visual_effects import ColorPalette
29
+ from core.io.visual_effects import ColorPalette, GradientRenderer
10
30
  from core.io.key_parser import KeyPress
11
31
 
12
32
 
13
- class ExamplePlugin(FullScreenPlugin):
14
- """Example plugin showcasing full-screen framework features.
33
+ @dataclass
34
+ class TodoItem:
35
+ """Todo list item."""
36
+ text: str
37
+ completed: bool = False
38
+
39
+
40
+ @dataclass
41
+ class PageInfo:
42
+ """Page metadata for navigation."""
43
+ title: str
44
+ description: str
45
+ icon: str
46
+
47
+
48
+ class SimpleTextInput:
49
+ """Lightweight text input handler for fullscreen plugins."""
50
+
51
+ def __init__(self, initial_value: str = "", max_length: int = 50):
52
+ """Initialize text input.
53
+
54
+ Args:
55
+ initial_value: Starting text value
56
+ max_length: Maximum input length
57
+ """
58
+ self.value = initial_value
59
+ self.cursor_pos = len(initial_value)
60
+ self.max_length = max_length
61
+
62
+ def handle_key(self, key_press: KeyPress) -> bool:
63
+ """Handle keyboard input for text editing.
64
+
65
+ Args:
66
+ key_press: Key press event
67
+
68
+ Returns:
69
+ True if key was handled
70
+ """
71
+ # Backspace
72
+ if key_press.name == "Backspace" or key_press.char in ['\x7f', '\x08']:
73
+ if self.cursor_pos > 0:
74
+ self.value = self.value[:self.cursor_pos - 1] + self.value[self.cursor_pos:]
75
+ self.cursor_pos -= 1
76
+ return True
77
+
78
+ # Delete
79
+ elif key_press.name == "Delete":
80
+ if self.cursor_pos < len(self.value):
81
+ self.value = self.value[:self.cursor_pos] + self.value[self.cursor_pos + 1:]
82
+ return True
83
+
84
+ # Arrow left
85
+ elif key_press.name == "ArrowLeft":
86
+ self.cursor_pos = max(0, self.cursor_pos - 1)
87
+ return True
88
+
89
+ # Arrow right
90
+ elif key_press.name == "ArrowRight":
91
+ self.cursor_pos = min(len(self.value), self.cursor_pos + 1)
92
+ return True
93
+
94
+ # Home
95
+ elif key_press.name == "Home":
96
+ self.cursor_pos = 0
97
+ return True
98
+
99
+ # End
100
+ elif key_press.name == "End":
101
+ self.cursor_pos = len(self.value)
102
+ return True
103
+
104
+ # Printable character
105
+ elif key_press.char and key_press.char.isprintable() and len(self.value) < self.max_length:
106
+ self.value = self.value[:self.cursor_pos] + key_press.char + self.value[self.cursor_pos:]
107
+ self.cursor_pos += 1
108
+ return True
109
+
110
+ return False
111
+
112
+ def render_with_cursor(self) -> str:
113
+ """Render text with cursor indicator.
114
+
115
+ Returns:
116
+ Text with cursor shown as underscore
117
+ """
118
+ if self.cursor_pos < len(self.value):
119
+ return self.value[:self.cursor_pos] + "▌" + self.value[self.cursor_pos:]
120
+ else:
121
+ return self.value + "▌"
122
+
123
+
124
+ class EnhancedExamplePlugin(FullScreenPlugin):
125
+ """Comprehensive example plugin showcasing all framework features.
15
126
 
16
- This plugin demonstrates:
17
- - Drawing primitives (text, borders, shapes)
18
- - Animation framework (fade, slide, bounce)
19
- - Input handling
20
- - Multi-page layouts
127
+ Pages:
128
+ 0. Table of Contents
129
+ 1. Framework Stats & Info
130
+ 2. Drawing Primitives
131
+ 3. Color Showcase
132
+ 4. Animations
133
+ 5. Interactive Components
134
+ 6. Calculator
135
+ 7. Todo List
136
+ 8. Game of Life
137
+ 9. Performance Demo
138
+ 10. Code Examples
139
+ 11. Resources & Help
21
140
  """
22
141
 
23
142
  def __init__(self):
24
- """Initialize the example plugin."""
143
+ """Initialize the enhanced example plugin."""
25
144
  metadata = PluginMetadata(
26
145
  name="example",
27
- description="Example plugin showcasing framework features",
28
- version="1.0.0",
29
- author="Framework",
146
+ description="Comprehensive framework showcase with 12 interactive pages",
147
+ version="2.0.0",
148
+ author="Kollabor Framework",
30
149
  category="demo",
31
150
  icon="🎯",
32
- aliases=[]
151
+ aliases=["demo", "showcase"]
33
152
  )
34
153
  super().__init__(metadata)
35
154
 
36
- # Plugin state
155
+ # Moderate FPS for smooth animations without excessive CPU
156
+ self.target_fps = 30.0
157
+
158
+ # Navigation
37
159
  self.current_page = 0
38
- self.total_pages = 4
160
+ self.total_pages = 12
161
+ self.frame_count = 0
162
+
163
+ # Page definitions
164
+ self.pages = [
165
+ PageInfo("Table of Contents", "Navigate to any page", "📋"),
166
+ PageInfo("Framework Stats", "Live performance metrics", "📊"),
167
+ PageInfo("Drawing Primitives", "Borders, shapes, progress bars", "🎨"),
168
+ PageInfo("Color Showcase", "256 colors and gradients", "🌈"),
169
+ PageInfo("Animations", "Easing functions and particle effects", "✨"),
170
+ PageInfo("Interactive Components", "Text input, checkboxes, sliders", "🎮"),
171
+ PageInfo("Calculator", "Functional number pad calculator", "🔢"),
172
+ PageInfo("Todo List", "Task management with add/remove", "📝"),
173
+ PageInfo("Game of Life", "Conway's cellular automaton", "🧬"),
174
+ PageInfo("Performance Demo", "FPS comparison and optimization", "⚡"),
175
+ PageInfo("Code Examples", "Copy-ready plugin templates", "💻"),
176
+ PageInfo("Resources & Help", "Documentation and key bindings", "📚"),
177
+ ]
178
+
179
+ # Animation framework
39
180
  self.animation_framework = AnimationFramework()
40
- self.page_transition_id = None
41
181
  self.demo_animations = {}
42
- self.frame_count = 0
182
+
183
+ # Calculator state
184
+ self.calc_display = "0"
185
+ self.calc_operator = None
186
+ self.calc_operand = None
187
+ self.calc_new_number = True
188
+
189
+ # Todo list state
190
+ self.todos: List[TodoItem] = [
191
+ TodoItem("Try the calculator (page 6)", False),
192
+ TodoItem("Explore Game of Life (page 8)", False),
193
+ TodoItem("View code examples (page 10)", False),
194
+ ]
195
+ self.todo_input = SimpleTextInput()
196
+ self.todo_editing = False
197
+
198
+ # Game of Life state
199
+ self.life_grid: List[List[bool]] = []
200
+ self.life_running = False
201
+ self.life_generation = 0
202
+ self.life_last_update = 0.0
203
+ self.life_speed = 0.2 # seconds per generation
204
+
205
+ # Interactive components state
206
+ self.text_input = SimpleTextInput("Hello, World!")
207
+ self.checkbox_states = [True, False, True]
208
+ self.slider_value = 50
209
+ self.active_input = 0 # Which component is focused
210
+
211
+ # Performance demo state
212
+ self.perf_mode = 0 # 0=buffered, 1=unbuffered simulation
213
+ self.perf_fps_history: List[float] = []
43
214
 
44
215
  async def initialize(self, renderer) -> bool:
45
216
  """Initialize the example plugin."""
46
- print("🔍 CRITICAL: ExamplePlugin.initialize() called")
47
- try:
48
- if not await super().initialize(renderer):
49
- print("❌ CRITICAL: super().initialize() failed")
50
- return False
217
+ if not await super().initialize(renderer):
218
+ return False
51
219
 
52
- # Setup initial animations
53
- current_time = asyncio.get_event_loop().time()
54
- self.demo_animations['title_fade'] = self.animation_framework.fade_in(2.0, current_time)
55
- self.demo_animations['bounce'] = self.animation_framework.bounce_in(1.5, current_time + 0.5)
220
+ # Setup initial animations
221
+ current_time = asyncio.get_event_loop().time()
222
+ self.demo_animations['title_fade'] = self.animation_framework.fade_in(1.5, current_time)
223
+ self.demo_animations['bounce'] = self.animation_framework.bounce_in(1.0, current_time + 0.3)
56
224
 
57
- print("✅ CRITICAL: ExamplePlugin.initialize() completed successfully")
58
- return True
59
- except Exception as e:
60
- print(f"❌ CRITICAL: Exception in initialize(): {e}")
61
- import traceback
62
- traceback.print_exc()
63
- return False
225
+ # Initialize Game of Life grid
226
+ self._init_life_grid()
227
+
228
+ return True
64
229
 
65
230
  async def on_start(self):
66
- """Called when Example plugin starts."""
231
+ """Called when plugin starts."""
67
232
  await super().on_start()
233
+ self.perf_fps_history = []
68
234
 
69
235
  async def render_frame(self, delta_time: float) -> bool:
70
- """Render the example plugin frame."""
236
+ """Render the current page."""
71
237
  if not self.renderer:
72
238
  return False
73
239
 
74
- # Increment frame counter for animations
75
240
  self.frame_count += 1
76
241
 
77
- # Clear screen and show content
242
+ # Update FPS history for performance page
243
+ if len(self.perf_fps_history) > 100:
244
+ self.perf_fps_history.pop(0)
245
+ current_fps = 1.0 / delta_time if delta_time > 0 else 0
246
+ self.perf_fps_history.append(current_fps)
247
+
248
+ # Clear screen
78
249
  self.renderer.clear_screen()
79
250
  width, height = self.renderer.get_terminal_size()
80
251
 
81
- # Render current page
252
+ # Render appropriate page
82
253
  if self.current_page == 0:
83
- self._render_welcome_page(width, height)
254
+ self._render_toc_page(width, height)
84
255
  elif self.current_page == 1:
85
- self._render_drawing_demo(width, height)
256
+ self._render_stats_page(width, height)
86
257
  elif self.current_page == 2:
87
- self._render_animation_demo(width, height)
258
+ self._render_drawing_page(width, height)
88
259
  elif self.current_page == 3:
89
- self._render_final_page(width, height)
260
+ self._render_color_page(width, height)
261
+ elif self.current_page == 4:
262
+ self._render_animation_page(width, height)
263
+ elif self.current_page == 5:
264
+ self._render_interactive_page(width, height)
265
+ elif self.current_page == 6:
266
+ self._render_calculator_page(width, height)
267
+ elif self.current_page == 7:
268
+ self._render_todo_page(width, height)
269
+ elif self.current_page == 8:
270
+ await self._render_life_page(width, height, delta_time)
271
+ elif self.current_page == 9:
272
+ self._render_performance_page(width, height)
273
+ elif self.current_page == 10:
274
+ self._render_code_page(width, height)
275
+ elif self.current_page == 11:
276
+ self._render_resources_page(width, height)
90
277
 
91
- # Show navigation instructions
92
- nav_text = f"Page {self.current_page + 1}/{self.total_pages} • ←→ or h/l navigate • 1-4 direct • q/ESC exit"
93
- nav_x = (width - len(nav_text)) // 2
94
- self.renderer.write_at(nav_x, height - 1, nav_text, "\033[37m")
278
+ # Render common navigation footer
279
+ self._render_navigation(width, height)
95
280
 
96
- # Flush output
97
- self.renderer.flush()
98
281
  return True
99
282
 
100
- def _render_welcome_page(self, width: int, height: int):
101
- """Render the welcome page."""
102
- # Animated title
103
- title_alpha = self.animation_framework.get_value(self.demo_animations.get('title_fade', 0))
104
- if title_alpha > 0.5: # Show when fade is mostly complete
105
- DrawingPrimitives.draw_text_centered(
106
- self.renderer, height // 4,
107
- "🎯 Full-Screen Framework Demo",
108
- ColorPalette.BRIGHT_CYAN
109
- )
110
-
111
- # Bouncing subtitle
112
- bounce_offset = int(self.animation_framework.get_value(self.demo_animations.get('bounce', 0)) * 3)
113
- DrawingPrimitives.draw_text_centered(
114
- self.renderer, height // 2 - bounce_offset,
115
- "Welcome to the Plugin Framework!",
116
- ColorPalette.BRIGHT_GREEN
117
- )
283
+ def _render_toc_page(self, width: int, height: int):
284
+ """Render table of contents with grid layout."""
285
+ # Title
286
+ title = "🎯 FRAMEWORK SHOWCASE - TABLE OF CONTENTS"
287
+ DrawingPrimitives.draw_text_centered(self.renderer, 2, title, ColorPalette.BRIGHT_CYAN)
118
288
 
119
- # Static content
120
- DrawingPrimitives.draw_text_centered(
121
- self.renderer, height // 2 + 2,
122
- "This framework provides:",
123
- ColorPalette.WHITE
124
- )
289
+ # Subtitle
290
+ subtitle = "Press number key (0-9) or arrow keys to navigate"
291
+ DrawingPrimitives.draw_text_centered(self.renderer, 3, subtitle, ColorPalette.DIM_WHITE)
292
+
293
+ # Draw pages in grid (3 columns)
294
+ start_y = 6
295
+ col_width = width // 3
296
+
297
+ for idx, page in enumerate(self.pages):
298
+ row = idx // 3
299
+ col = idx % 3
300
+
301
+ x = col * col_width + 5
302
+ y = start_y + (row * 4)
303
+
304
+ # Page number and icon
305
+ num_str = f"[{idx}]" if idx < 10 else f"[{chr(65 + idx - 10)}]"
306
+ self.renderer.write_at(x, y, num_str, ColorPalette.BRIGHT_YELLOW)
307
+ self.renderer.write_at(x + 5, y, page.icon, ColorPalette.WHITE)
308
+
309
+ # Page title
310
+ self.renderer.write_at(x + 7, y, page.title[:25], ColorPalette.BRIGHT_GREEN)
311
+
312
+ # Page description
313
+ desc = page.description[:30]
314
+ self.renderer.write_at(x + 3, y + 1, desc, ColorPalette.DIM_WHITE)
315
+
316
+ def _render_stats_page(self, width: int, height: int):
317
+ """Render framework statistics and terminal info."""
318
+ DrawingPrimitives.draw_text_centered(self.renderer, 2, "📊 FRAMEWORK STATISTICS", ColorPalette.BRIGHT_MAGENTA)
319
+
320
+ # Calculate stats
321
+ runtime = asyncio.get_event_loop().time() - self.start_time if self.running else 0
322
+ avg_fps = sum(self.perf_fps_history[-30:]) / len(self.perf_fps_history[-30:]) if self.perf_fps_history else 0
323
+
324
+ # Draw stats in columns
325
+ left_x = 10
326
+ right_x = width // 2 + 5
327
+ y = 5
328
+
329
+ # Left column - Plugin stats
330
+ self.renderer.write_at(left_x, y, "PLUGIN INFORMATION", ColorPalette.BRIGHT_CYAN)
331
+ y += 2
332
+ self.renderer.write_at(left_x, y, f"Name: {self.metadata.name}", ColorPalette.WHITE)
333
+ y += 1
334
+ self.renderer.write_at(left_x, y, f"Version: {self.metadata.version}", ColorPalette.WHITE)
335
+ y += 1
336
+ self.renderer.write_at(left_x, y, f"Category: {self.metadata.category}", ColorPalette.WHITE)
337
+ y += 1
338
+ self.renderer.write_at(left_x, y, f"Target FPS: {self.target_fps:.1f}", ColorPalette.WHITE)
339
+ y += 2
340
+
341
+ self.renderer.write_at(left_x, y, "RUNTIME STATISTICS", ColorPalette.BRIGHT_GREEN)
342
+ y += 2
343
+ self.renderer.write_at(left_x, y, f"Uptime: {runtime:.1f}s", ColorPalette.WHITE)
344
+ y += 1
345
+ self.renderer.write_at(left_x, y, f"Frames: {self.frame_count:,}", ColorPalette.WHITE)
346
+ y += 1
347
+ self.renderer.write_at(left_x, y, f"Current FPS: {avg_fps:.1f}", ColorPalette.WHITE)
348
+ y += 1
349
+ self.renderer.write_at(left_x, y, f"Page: {self.current_page + 1}/{self.total_pages}", ColorPalette.WHITE)
125
350
 
351
+ # Right column - Terminal info
352
+ y = 5
353
+ self.renderer.write_at(right_x, y, "TERMINAL INFORMATION", ColorPalette.BRIGHT_YELLOW)
354
+ y += 2
355
+ self.renderer.write_at(right_x, y, f"Size: {width} x {height}", ColorPalette.WHITE)
356
+ y += 1
357
+ self.renderer.write_at(right_x, y, f"Colors: 256-color capable", ColorPalette.WHITE)
358
+ y += 1
359
+ self.renderer.write_at(right_x, y, f"Unicode: ✓ Supported", ColorPalette.WHITE)
360
+ y += 1
361
+ self.renderer.write_at(right_x, y, f"Buffering: ✓ Enabled", ColorPalette.WHITE)
362
+ y += 2
363
+
364
+ self.renderer.write_at(right_x, y, "FRAMEWORK FEATURES", ColorPalette.BRIGHT_CYAN)
365
+ y += 2
126
366
  features = [
127
- " Complete terminal takeover",
128
- " Modal system integration",
129
- " Drawing primitives & animations",
130
- " Plugin lifecycle management",
131
- " Input handling & routing"
367
+ " Frame buffering (flicker-free)",
368
+ " Adaptive FPS control",
369
+ " Modal system integration",
370
+ " Drawing primitives library",
371
+ " Animation framework",
372
+ "✓ Event-driven architecture",
132
373
  ]
374
+ for feature in features:
375
+ self.renderer.write_at(right_x, y, feature, ColorPalette.GREEN)
376
+ y += 1
377
+
378
+ # Live FPS graph
379
+ graph_y = height - 10
380
+ self.renderer.write_at(10, graph_y, "FPS GRAPH (last 50 frames)", ColorPalette.DIM_WHITE)
381
+ if len(self.perf_fps_history) > 1:
382
+ self._draw_fps_graph(15, graph_y + 1, 50, 5, self.perf_fps_history[-50:])
383
+
384
+ def _draw_fps_graph(self, x: int, y: int, width: int, height: int, data: List[float]):
385
+ """Draw a simple bar graph of FPS data."""
386
+ if not data:
387
+ return
388
+
389
+ max_fps = max(data) if data else 60
390
+ min_fps = min(data) if data else 0
133
391
 
134
- for i, feature in enumerate(features):
135
- DrawingPrimitives.draw_text_centered(
136
- self.renderer, height // 2 + 4 + i,
137
- feature,
138
- ColorPalette.YELLOW
139
- )
392
+ # Draw bars
393
+ for i, fps in enumerate(data[-width:]):
394
+ bar_x = x + i
395
+ normalized = (fps - min_fps) / (max_fps - min_fps) if max_fps > min_fps else 0.5
396
+ bar_height = int(normalized * height)
140
397
 
141
- def _render_drawing_demo(self, width: int, height: int):
398
+ for j in range(bar_height):
399
+ bar_y = y + height - 1 - j
400
+ if bar_y >= y:
401
+ char = "█" if j < bar_height - 1 else "▀"
402
+ color = ColorPalette.BRIGHT_GREEN if fps >= 25 else ColorPalette.YELLOW
403
+ self.renderer.write_at(bar_x, bar_y, char, color)
404
+
405
+ def _render_drawing_page(self, width: int, height: int):
142
406
  """Render drawing primitives demonstration."""
143
- DrawingPrimitives.draw_text_centered(
144
- self.renderer, 2,
145
- "Drawing Primitives Demo",
146
- ColorPalette.BRIGHT_MAGENTA
147
- )
407
+ DrawingPrimitives.draw_text_centered(self.renderer, 2, "🎨 DRAWING PRIMITIVES", ColorPalette.BRIGHT_MAGENTA)
148
408
 
149
- # Draw various shapes and elements
150
409
  center_x, center_y = width // 2, height // 2
151
410
 
152
- # Border around demo area
153
- DrawingPrimitives.draw_border(
154
- self.renderer, center_x - 20, center_y - 8, 40, 16,
155
- color=ColorPalette.CYAN
156
- )
411
+ # Border box
412
+ DrawingPrimitives.draw_border(self.renderer, center_x - 25, center_y - 8, 50, 16, color=ColorPalette.CYAN)
157
413
 
158
- # Progress bar
414
+ # Progress bar (animated)
159
415
  progress = (self.frame_count % 100) / 100.0
160
- DrawingPrimitives.draw_progress_bar(
161
- self.renderer, center_x - 15, center_y - 5, 30, progress,
162
- color=ColorPalette.GREEN
163
- )
164
- DrawingPrimitives.draw_text_centered(
165
- self.renderer, center_y - 6,
166
- f"Progress: {progress:.0%}",
167
- ColorPalette.WHITE
168
- )
416
+ DrawingPrimitives.draw_progress_bar(self.renderer, center_x - 20, center_y - 5, 40, progress, color=ColorPalette.GREEN)
417
+ self.renderer.write_at(center_x - 10, center_y - 6, f"Progress: {progress:.0%}", ColorPalette.WHITE)
169
418
 
170
419
  # Spinner
171
- DrawingPrimitives.draw_spinner(
172
- self.renderer, center_x - 2, center_y - 2, self.frame_count // 5,
173
- color=ColorPalette.BRIGHT_BLUE
174
- )
420
+ DrawingPrimitives.draw_spinner(self.renderer, center_x - 2, center_y - 2, self.frame_count // 3, color=ColorPalette.BRIGHT_BLUE)
175
421
  self.renderer.write_at(center_x + 2, center_y - 2, "Loading...", ColorPalette.WHITE)
176
422
 
177
- # Circle points
178
- radius = 8
179
- DrawingPrimitives.draw_circle_points(
180
- self.renderer, center_x, center_y + 3, radius,
181
- char="●", color=ColorPalette.RED
182
- )
423
+ # Circle
424
+ DrawingPrimitives.draw_circle_points(self.renderer, center_x, center_y + 3, 6, char="●", color=ColorPalette.RED)
183
425
 
184
- # Wave
426
+ # Wave animation
185
427
  wave_phase = self.frame_count * 0.1
186
- DrawingPrimitives.draw_wave(
187
- self.renderer, height - 5, 2, 0.3, wave_phase,
188
- char="~", color=ColorPalette.BLUE
189
- )
428
+ DrawingPrimitives.draw_wave(self.renderer, height - 6, 2, 0.3, wave_phase, char="~", color=ColorPalette.BLUE)
190
429
 
191
- def _render_animation_demo(self, width: int, height: int):
192
- """Render animation framework demonstration."""
193
- DrawingPrimitives.draw_text_centered(
194
- self.renderer, 2,
195
- "Animation Framework Demo",
196
- ColorPalette.BRIGHT_YELLOW
197
- )
430
+ # Text samples
431
+ self.renderer.write_at(5, 5, "Box drawing: ╔═╗ ║ ╚═╝", ColorPalette.WHITE)
432
+ self.renderer.write_at(5, 6, "Blocks: █ ▓ ▒ ░ ▀ ▄ ▌ ▐", ColorPalette.WHITE)
433
+ self.renderer.write_at(5, 7, "Arrows: ← ↑ → ↓ ↔ ↕", ColorPalette.WHITE)
434
+ self.renderer.write_at(5, 8, "Symbols: ✗ ★ ● ○ ◆ ◇", ColorPalette.WHITE)
198
435
 
199
- center_x, center_y = width // 2, height // 2
436
+ def _render_color_page(self, width: int, height: int):
437
+ """Render 256-color palette and gradients."""
438
+ DrawingPrimitives.draw_text_centered(self.renderer, 2, "🌈 COLOR SHOWCASE", ColorPalette.BRIGHT_MAGENTA)
439
+
440
+ # 256-color grid (basic 16 + 216 color cube + 24 grayscale)
441
+ y = 5
442
+ self.renderer.write_at(10, y, "256 COLOR PALETTE", ColorPalette.WHITE)
443
+ y += 2
444
+
445
+ # Draw color blocks in grid
446
+ for row in range(16):
447
+ x = 10
448
+ for col in range(16):
449
+ color_num = row * 16 + col
450
+ # ANSI 256-color escape code
451
+ color_code = f"\033[48;5;{color_num}m"
452
+ self.renderer.write_at(x + col * 2, y + row, f"{color_code} \033[0m", "")
453
+
454
+ # Gradient examples
455
+ grad_y = 5
456
+ grad_x = width // 2 + 10
457
+ self.renderer.write_at(grad_x, grad_y, "GRADIENT EXAMPLES", ColorPalette.WHITE)
458
+ grad_y += 2
459
+
460
+ # Horizontal gradient
461
+ gradient_text = "Horizontal Gradient Demo"
462
+ colored = GradientRenderer.apply_dim_scheme_gradient(gradient_text)
463
+ self.renderer.write_at(grad_x, grad_y, colored, "")
464
+ grad_y += 2
465
+
466
+ # Color names
467
+ colors = [
468
+ ("RED", ColorPalette.RED),
469
+ ("GREEN", ColorPalette.GREEN),
470
+ ("BLUE", ColorPalette.BLUE),
471
+ ("YELLOW", ColorPalette.YELLOW),
472
+ ("MAGENTA", ColorPalette.MAGENTA),
473
+ ("CYAN", ColorPalette.CYAN),
474
+ ("BRIGHT_WHITE", ColorPalette.BRIGHT_WHITE),
475
+ ("DIM_GREY", ColorPalette.DIM_GREY),
476
+ ]
477
+
478
+ for name, color in colors:
479
+ self.renderer.write_at(grad_x, grad_y, f"{name:15} ████████", color)
480
+ grad_y += 1
481
+
482
+ def _render_animation_page(self, width: int, height: int):
483
+ """Render advanced animation demonstrations."""
484
+ DrawingPrimitives.draw_text_centered(self.renderer, 2, "✨ ANIMATION SHOWCASE", ColorPalette.BRIGHT_YELLOW)
200
485
 
201
- # Create cycling animations
202
486
  current_time = asyncio.get_event_loop().time()
487
+ center_x, center_y = width // 2, height // 2
203
488
 
204
- # Pulsing circle
489
+ # Pulsing circle (sine wave)
205
490
  pulse_size = 5 + int(3 * math.sin(current_time * 2))
206
491
  for r in range(1, pulse_size):
207
- DrawingPrimitives.draw_circle_points(
208
- self.renderer, center_x, center_y, r,
209
- char="○", color=ColorPalette.GREEN
210
- )
211
-
212
- # Sliding text
213
- slide_x = int(20 * math.sin(current_time))
214
- self.renderer.write_at(center_x + slide_x, center_y - 5, "← Sliding Text →", ColorPalette.CYAN)
215
-
216
- # Fading elements
217
- fade_alpha = (math.sin(current_time * 1.5) + 1) / 2
218
- if fade_alpha > 0.3: # Only show when bright enough
219
- intensity = "▓" if fade_alpha > 0.7 else "░"
220
- fade_text = f"Fading {intensity}"
221
- DrawingPrimitives.draw_text_centered(
222
- self.renderer, center_y + 3,
223
- fade_text,
224
- ColorPalette.MAGENTA
225
- )
492
+ DrawingPrimitives.draw_circle_points(self.renderer, center_x, center_y, r, char="○", color=ColorPalette.GREEN)
493
+
494
+ # Sliding text with easing
495
+ slide_x = int(20 * math.sin(current_time * 1.5))
496
+ self.renderer.write_at(center_x + slide_x, center_y - 7, "← Sliding →", ColorPalette.CYAN)
497
+
498
+ # Particle effect (rising dots)
499
+ for i in range(20):
500
+ particle_age = (current_time * 2 + i * 0.5) % 3
501
+ particle_y = center_y + 10 - int(particle_age * 5)
502
+ particle_x = center_x - 10 + i * 2
503
+ if particle_y < height - 3 and particle_y > center_y - 10:
504
+ alpha = 1.0 - (particle_age / 3.0)
505
+ char = "●" if alpha > 0.5 else "○"
506
+ self.renderer.write_at(particle_x, particle_y, char, ColorPalette.BRIGHT_BLUE)
226
507
 
227
508
  # Bouncing ball
228
- bounce_y = center_y + 6 + int(3 * abs(math.sin(current_time * 3)))
229
- self.renderer.write_at(center_x, bounce_y, "●", ColorPalette.RED)
230
-
231
- def _render_final_page(self, width: int, height: int):
232
- """Render the final page with improved layout."""
233
- # Title at top
234
- DrawingPrimitives.draw_text_centered(
235
- self.renderer, 3,
236
- "🚀 Ready to Build Plugins!",
237
- ColorPalette.BRIGHT_GREEN
238
- )
509
+ bounce_y = center_y + 5 + int(3 * abs(math.sin(current_time * 3)))
510
+ self.renderer.write_at(center_x + 15, bounce_y, "●", ColorPalette.RED)
511
+
512
+ # Rotating spinner variations
513
+ spinners = ["⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏", "◐◓◑◒", "▁▃▄▅▆▇█▇▆▅▄▃", "←↖↑↗→↘↓↙"]
514
+ y = 5
515
+ for i, spinner in enumerate(spinners):
516
+ idx = (self.frame_count // 2) % len(spinner)
517
+ self.renderer.write_at(10, y + i * 2, f"Spinner {i+1}: {spinner[idx]}", ColorPalette.BRIGHT_GREEN)
518
+
519
+ # Easing function demo
520
+ easing_y = center_y - 10
521
+ easing_x = 10
522
+ self.renderer.write_at(easing_x, easing_y, "EASING FUNCTIONS:", ColorPalette.WHITE)
523
+ easing_y += 1
524
+
525
+ # Linear
526
+ t = (current_time % 2) / 2
527
+ pos = int(t * 30)
528
+ self.renderer.write_at(easing_x, easing_y, "Linear: ", ColorPalette.DIM_WHITE)
529
+ self.renderer.write_at(easing_x + 10 + pos, easing_y, "●", ColorPalette.YELLOW)
530
+ easing_y += 1
531
+
532
+ # Ease in/out
533
+ ease_t = -2 * t * t * t + 3 * t * t # Smooth step
534
+ ease_pos = int(ease_t * 30)
535
+ self.renderer.write_at(easing_x, easing_y, "Smooth: ", ColorPalette.DIM_WHITE)
536
+ self.renderer.write_at(easing_x + 10 + ease_pos, easing_y, "●", ColorPalette.GREEN)
537
+
538
+ def _render_interactive_page(self, width: int, height: int):
539
+ """Render interactive components demonstration."""
540
+ DrawingPrimitives.draw_text_centered(self.renderer, 2, "🎮 INTERACTIVE COMPONENTS", ColorPalette.BRIGHT_GREEN)
541
+
542
+ y = 6
543
+ x = 10
544
+
545
+ # Instructions
546
+ self.renderer.write_at(x, y, "Use Tab to switch between components, arrow keys to interact", ColorPalette.DIM_WHITE)
547
+ y += 3
548
+
549
+ # Text input
550
+ is_focused = self.active_input == 0
551
+ label_color = ColorPalette.BRIGHT_YELLOW if is_focused else ColorPalette.WHITE
552
+ self.renderer.write_at(x, y, "Text Input:", label_color)
553
+ input_display = self.text_input.render_with_cursor() if is_focused else self.text_input.value
554
+ input_color = ColorPalette.BRIGHT_WHITE if is_focused else ColorPalette.DIM_WHITE
555
+ self.renderer.write_at(x + 15, y, f"[{input_display}]", input_color)
556
+ y += 3
557
+
558
+ # Checkboxes
559
+ is_focused = self.active_input == 1
560
+ label_color = ColorPalette.BRIGHT_YELLOW if is_focused else ColorPalette.WHITE
561
+ self.renderer.write_at(x, y, "Checkboxes:", label_color)
562
+ y += 1
563
+ for i, (label, checked) in enumerate([("Option A", self.checkbox_states[0]),
564
+ ("Option B", self.checkbox_states[1]),
565
+ ("Option C", self.checkbox_states[2])]):
566
+ box = "[✓]" if checked else "[ ]"
567
+ color = ColorPalette.BRIGHT_GREEN if checked else ColorPalette.DIM_WHITE
568
+ if is_focused and i == 0:
569
+ color = ColorPalette.BRIGHT_YELLOW
570
+ self.renderer.write_at(x + 2, y, f"{box} {label}", color)
571
+ y += 1
572
+ y += 2
573
+
574
+ # Slider
575
+ is_focused = self.active_input == 2
576
+ label_color = ColorPalette.BRIGHT_YELLOW if is_focused else ColorPalette.WHITE
577
+ self.renderer.write_at(x, y, "Slider:", label_color)
578
+ slider_width = 40
579
+ filled = int((self.slider_value / 100.0) * slider_width)
580
+ slider_bar = "█" * filled + "░" * (slider_width - filled)
581
+ self.renderer.write_at(x + 10, y, f"[{slider_bar}] {self.slider_value}%", ColorPalette.CYAN)
582
+ y += 3
583
+
584
+ # Current value display
585
+ self.renderer.write_at(x, y, f"Active: Component {self.active_input + 1}/3", ColorPalette.DIM_WHITE)
586
+ y += 1
587
+ self.renderer.write_at(x, y, f"Text: '{self.text_input.value}'", ColorPalette.DIM_WHITE)
588
+ y += 1
589
+ self.renderer.write_at(x, y, f"Checks: {sum(self.checkbox_states)}/3 selected", ColorPalette.DIM_WHITE)
590
+ y += 1
591
+ self.renderer.write_at(x, y, f"Slider: {self.slider_value}%", ColorPalette.DIM_WHITE)
592
+
593
+ def _render_calculator_page(self, width: int, height: int):
594
+ """Render functional calculator interface."""
595
+ DrawingPrimitives.draw_text_centered(self.renderer, 2, "🔢 CALCULATOR", ColorPalette.BRIGHT_CYAN)
596
+
597
+ center_x, center_y = width // 2, height // 2 - 3
598
+
599
+ # Display
600
+ display_width = 24
601
+ display_x = center_x - display_width // 2
602
+ display_y = center_y - 8
603
+
604
+ DrawingPrimitives.draw_border(self.renderer, display_x - 2, display_y - 1, display_width + 4, 3, color=ColorPalette.CYAN)
605
+ display_text = self.calc_display[:display_width - 2].rjust(display_width - 2)
606
+ self.renderer.write_at(display_x, display_y, display_text, ColorPalette.BRIGHT_WHITE)
607
+
608
+ # Number pad
609
+ pad_x = center_x - 10
610
+ pad_y = center_y - 4
611
+
612
+ buttons = [
613
+ ["7", "8", "9", "/"],
614
+ ["4", "5", "6", "*"],
615
+ ["1", "2", "3", "-"],
616
+ ["C", "0", "=", "+"],
617
+ ]
618
+
619
+ for row_idx, row in enumerate(buttons):
620
+ for col_idx, btn in enumerate(row):
621
+ btn_x = pad_x + col_idx * 5
622
+ btn_y = pad_y + row_idx * 2
623
+
624
+ # Button styling
625
+ if btn in "0123456789":
626
+ color = ColorPalette.BRIGHT_WHITE
627
+ elif btn in "+-*/":
628
+ color = ColorPalette.BRIGHT_YELLOW
629
+ elif btn == "=":
630
+ color = ColorPalette.BRIGHT_GREEN
631
+ else:
632
+ color = ColorPalette.BRIGHT_RED
633
+
634
+ self.renderer.write_at(btn_x, btn_y, f"[{btn}]", color)
635
+
636
+ # Instructions
637
+ self.renderer.write_at(center_x - 15, height - 8, "Use number keys and operators: + - * /", ColorPalette.DIM_WHITE)
638
+ self.renderer.write_at(center_x - 15, height - 7, "Press = to calculate, C to clear", ColorPalette.DIM_WHITE)
639
+
640
+ def _render_todo_page(self, width: int, height: int):
641
+ """Render todo list mini-app."""
642
+ DrawingPrimitives.draw_text_centered(self.renderer, 2, "📝 TODO LIST", ColorPalette.BRIGHT_GREEN)
643
+
644
+ y = 5
645
+ x = 10
646
+
647
+ # Instructions
648
+ if not self.todo_editing:
649
+ self.renderer.write_at(x, y, "Press 'a' to add, Space to toggle, 'd' to delete selected", ColorPalette.DIM_WHITE)
650
+ else:
651
+ self.renderer.write_at(x, y, "Type task and press Enter to add (Esc to cancel)", ColorPalette.BRIGHT_YELLOW)
652
+ y += 3
653
+
654
+ # Add input (if editing)
655
+ if self.todo_editing:
656
+ input_text = self.todo_input.render_with_cursor()
657
+ self.renderer.write_at(x, y, f"New task: [{input_text}]", ColorPalette.BRIGHT_CYAN)
658
+ y += 2
659
+
660
+ # Todo items
661
+ self.renderer.write_at(x, y, "TASKS:", ColorPalette.WHITE)
662
+ y += 1
663
+
664
+ for idx, todo in enumerate(self.todos):
665
+ checkbox = "[✓]" if todo.completed else "[ ]"
666
+ text_color = ColorPalette.DIM_WHITE if todo.completed else ColorPalette.WHITE
667
+ checkbox_color = ColorPalette.GREEN if todo.completed else ColorPalette.YELLOW
668
+
669
+ self.renderer.write_at(x, y, f"{idx + 1}. {checkbox}", checkbox_color)
670
+
671
+ # Strikethrough for completed
672
+ if todo.completed:
673
+ display_text = "".join(c + "\u0336" for c in todo.text) # Strikethrough
674
+ else:
675
+ display_text = todo.text
676
+
677
+ self.renderer.write_at(x + 9, y, display_text, text_color)
678
+ y += 1
679
+
680
+ # Stats
681
+ y += 2
682
+ total = len(self.todos)
683
+ completed = sum(1 for t in self.todos if t.completed)
684
+ remaining = total - completed
685
+
686
+ self.renderer.write_at(x, y, f"Total: {total} | Completed: {completed} | Remaining: {remaining}", ColorPalette.DIM_WHITE)
687
+
688
+ async def _render_life_page(self, width: int, height: int, delta_time: float):
689
+ """Render Conway's Game of Life."""
690
+ DrawingPrimitives.draw_text_centered(self.renderer, 2, "🧬 GAME OF LIFE", ColorPalette.BRIGHT_MAGENTA)
691
+
692
+ # Update game if running
693
+ if self.life_running:
694
+ current_time = time.time()
695
+ if current_time - self.life_last_update >= self.life_speed:
696
+ self._update_life_generation()
697
+ self.life_last_update = current_time
698
+
699
+ # Instructions
700
+ instr_y = 4
701
+ self.renderer.write_at(10, instr_y, f"Space: Start/Stop | R: Random | C: Clear | +/-: Speed", ColorPalette.DIM_WHITE)
702
+ instr_y += 1
703
+ status = "RUNNING" if self.life_running else "PAUSED"
704
+ status_color = ColorPalette.BRIGHT_GREEN if self.life_running else ColorPalette.YELLOW
705
+ self.renderer.write_at(10, instr_y, f"Status: {status} | Gen: {self.life_generation} | Speed: {self.life_speed:.2f}s", status_color)
706
+
707
+ # Render grid
708
+ grid_start_x = (width - len(self.life_grid[0])) // 2
709
+ grid_start_y = 8
710
+
711
+ for row_idx, row in enumerate(self.life_grid):
712
+ for col_idx, cell in enumerate(row):
713
+ if cell:
714
+ self.renderer.write_at(grid_start_x + col_idx, grid_start_y + row_idx, "█", ColorPalette.BRIGHT_GREEN)
715
+ else:
716
+ self.renderer.write_at(grid_start_x + col_idx, grid_start_y + row_idx, "·", ColorPalette.DIM_GREY)
717
+
718
+ def _render_performance_page(self, width: int, height: int):
719
+ """Render performance comparison and metrics."""
720
+ DrawingPrimitives.draw_text_centered(self.renderer, 2, "⚡ PERFORMANCE METRICS", ColorPalette.BRIGHT_YELLOW)
721
+
722
+ y = 5
723
+ x = 10
724
+
725
+ # Current FPS
726
+ avg_fps = sum(self.perf_fps_history[-30:]) / len(self.perf_fps_history[-30:]) if self.perf_fps_history else 0
727
+ self.renderer.write_at(x, y, f"Current FPS: {avg_fps:.1f} / {self.target_fps:.1f} target", ColorPalette.BRIGHT_GREEN)
728
+ y += 2
729
+
730
+ # Frame buffer info
731
+ self.renderer.write_at(x, y, "OPTIMIZATIONS ENABLED:", ColorPalette.WHITE)
732
+ y += 1
733
+ self.renderer.write_at(x + 2, y, "✓ Frame buffering (single atomic write)", ColorPalette.GREEN)
734
+ y += 1
735
+ self.renderer.write_at(x + 2, y, "✓ State tracking (skip unchanged frames)", ColorPalette.GREEN)
736
+ y += 1
737
+ self.renderer.write_at(x + 2, y, f"✓ Adaptive FPS ({self.target_fps:.0f} fps for this page)", ColorPalette.GREEN)
738
+ y += 3
739
+
740
+ # Benefits
741
+ self.renderer.write_at(x, y, "BENEFITS:", ColorPalette.YELLOW)
742
+ y += 1
743
+ self.renderer.write_at(x + 2, y, "→ Zero flicker (no visible blank frames)", ColorPalette.WHITE)
744
+ y += 1
745
+ self.renderer.write_at(x + 2, y, "→ 60x fewer syscalls per frame", ColorPalette.WHITE)
746
+ y += 1
747
+ self.renderer.write_at(x + 2, y, "→ Minimal CPU usage when idle", ColorPalette.WHITE)
748
+ y += 1
749
+ self.renderer.write_at(x + 2, y, "→ Smooth animations with consistent timing", ColorPalette.WHITE)
750
+ y += 3
751
+
752
+ # FPS graph
753
+ self.renderer.write_at(x, y, "FPS HISTORY (last 60 frames):", ColorPalette.DIM_WHITE)
754
+ y += 1
755
+ if self.perf_fps_history:
756
+ self._draw_fps_graph(x, y, 60, 6, self.perf_fps_history[-60:])
757
+
758
+ def _render_code_page(self, width: int, height: int):
759
+ """Render code examples and templates."""
760
+ DrawingPrimitives.draw_text_centered(self.renderer, 2, "💻 CODE EXAMPLES", ColorPalette.BRIGHT_CYAN)
761
+
762
+ y = 5
763
+ x = 5
764
+
765
+ # Example 1: Basic plugin structure
766
+ self.renderer.write_at(x, y, "MINIMAL PLUGIN TEMPLATE:", ColorPalette.BRIGHT_YELLOW)
767
+ y += 2
239
768
 
240
- # Plugin template example - more compact positioning
241
769
  code_lines = [
242
- "class MyPlugin(FullScreenPlugin):",
243
- " async def render_frame(self, delta_time):",
244
- " self.renderer.clear_screen()",
245
- " # Your awesome content here!",
246
- " return True",
247
- "",
248
- " async def handle_input(self, key_press):",
249
- " return key_press.char == 'q'"
770
+ ("class", "MyPlugin", "(", "FullScreenPlugin", "):"),
771
+ (" ", "async def", " ", "render_frame", "(", "self", ", ", "delta_time", "):"),
772
+ (" ", "self", ".", "renderer", ".", "clear_screen", "()"),
773
+ (" ", "self", ".", "renderer", ".", "write_at", "(", "10", ", ", "5", ", ", '"Hello!"', ")"),
774
+ (" ", "return", " ", "True"),
775
+ ("", "", "", "", ""),
776
+ (" ", "async def", " ", "handle_input", "(", "self", ", ", "key_press", "):"),
777
+ (" ", "return", " ", "key_press", ".", "char", " == ", "'q'"),
250
778
  ]
251
779
 
252
- # Position code in upper middle area
253
- start_y = 6
254
- for i, line in enumerate(code_lines):
255
- x = max(0, (width - len(line)) // 2)
256
- self.renderer.write_at(x, start_y + i, line, ColorPalette.YELLOW)
257
-
258
- # Features section positioned below code
259
- features_start_y = start_y + len(code_lines) + 2
260
- DrawingPrimitives.draw_text_centered(
261
- self.renderer, features_start_y,
262
- "Framework Features Available:",
263
- ColorPalette.WHITE
264
- )
780
+ for line_parts in code_lines:
781
+ line_x = x + 2
782
+ for part in line_parts:
783
+ if part in ["class", "async def", "return"]:
784
+ color = ColorPalette.BRIGHT_MAGENTA # Keywords
785
+ elif part in ["MyPlugin", "FullScreenPlugin", "render_frame", "handle_input"]:
786
+ color = ColorPalette.BRIGHT_CYAN # Classes/functions
787
+ elif part in ["self"]:
788
+ color = ColorPalette.BRIGHT_BLUE # Self
789
+ elif part.startswith('"'):
790
+ color = ColorPalette.BRIGHT_GREEN # Strings
791
+ elif part.isdigit():
792
+ color = ColorPalette.BRIGHT_YELLOW # Numbers
793
+ else:
794
+ color = ColorPalette.WHITE
265
795
 
266
- features = [
267
- "✓ Modal system integration",
268
- "✓ Terminal state management",
269
- "✓ Drawing primitives library",
270
- "✓ Animation framework",
271
- "✓ Input handling system"
796
+ self.renderer.write_at(line_x, y, part, color)
797
+ line_x += len(part)
798
+ y += 1
799
+
800
+ y += 2
801
+
802
+ # Key concepts
803
+ self.renderer.write_at(x, y, "KEY CONCEPTS:", ColorPalette.BRIGHT_GREEN)
804
+ y += 1
805
+ concepts = [
806
+ "→ render_frame(): Called every frame, return False to exit",
807
+ "→ handle_input(): Process key presses, return True to exit",
808
+ "→ target_fps: Set frame rate (15-60 fps recommended)",
809
+ "→ Use renderer.begin_frame() / end_frame() for buffering",
810
+ "→ Access width, height via renderer.get_terminal_size()",
811
+ ]
812
+ for concept in concepts:
813
+ self.renderer.write_at(x + 2, y, concept, ColorPalette.DIM_WHITE)
814
+ y += 1
815
+
816
+ def _render_resources_page(self, width: int, height: int):
817
+ """Render resources, documentation, and help."""
818
+ DrawingPrimitives.draw_text_centered(self.renderer, 2, "📚 RESOURCES & HELP", ColorPalette.BRIGHT_GREEN)
819
+
820
+ y = 5
821
+ x = 10
822
+
823
+ # Key bindings
824
+ self.renderer.write_at(x, y, "NAVIGATION:", ColorPalette.BRIGHT_YELLOW)
825
+ y += 1
826
+ bindings = [
827
+ ("←/→ or h/l", "Previous/Next page"),
828
+ ("0-9", "Jump to page by number"),
829
+ ("Home", "First page (Table of Contents)"),
830
+ ("End", "Last page (Resources)"),
831
+ ("q or ESC", "Exit plugin"),
832
+ ]
833
+ for key, desc in bindings:
834
+ self.renderer.write_at(x + 2, y, f"{key:12} - {desc}", ColorPalette.WHITE)
835
+ y += 1
836
+
837
+ y += 2
838
+
839
+ # Page-specific controls
840
+ self.renderer.write_at(x, y, "PAGE-SPECIFIC CONTROLS:", ColorPalette.BRIGHT_CYAN)
841
+ y += 1
842
+ controls = [
843
+ ("Calculator", "Number keys, +, -, *, /, =, C"),
844
+ ("Todo List", "a=add, Space=toggle, d=delete"),
845
+ ("Game of Life", "Space=start/stop, r=random, c=clear"),
846
+ ("Interactive", "Tab=switch component, arrows=adjust"),
272
847
  ]
848
+ for page, keys in controls:
849
+ self.renderer.write_at(x + 2, y, f"{page:15} → {keys}", ColorPalette.DIM_WHITE)
850
+ y += 1
273
851
 
274
- for i, feature in enumerate(features):
275
- DrawingPrimitives.draw_text_centered(
276
- self.renderer, features_start_y + 2 + i,
277
- feature,
278
- ColorPalette.BRIGHT_CYAN
279
- )
852
+ y += 2
853
+
854
+ # Documentation
855
+ self.renderer.write_at(x, y, "DOCUMENTATION:", ColorPalette.BRIGHT_MAGENTA)
856
+ y += 1
857
+ docs = [
858
+ "→ Plugin development guide in docs/",
859
+ "→ API reference in core/fullscreen/",
860
+ "→ More examples in plugins/fullscreen/",
861
+ "→ Framework source code is extensively commented",
862
+ ]
863
+ for doc in docs:
864
+ self.renderer.write_at(x + 2, y, doc, ColorPalette.WHITE)
865
+ y += 1
866
+
867
+ y += 2
868
+
869
+ # Credits
870
+ self.renderer.write_at(x, y, "Built with Kollabor Framework 🎯", ColorPalette.BRIGHT_WHITE)
280
871
 
281
872
  def _render_navigation(self, width: int, height: int):
282
- """Render navigation controls."""
283
- nav_text = f"Page {self.current_page + 1}/{self.total_pages} • ←→ Navigate • Q/ESC Exit"
284
- DrawingPrimitives.draw_text_centered(
285
- self.renderer, height - 2,
286
- nav_text,
287
- ColorPalette.DIM_WHITE
288
- )
873
+ """Render navigation footer."""
874
+ footer_y = height - 1
875
+
876
+ # Page info
877
+ page_info = f"Page {self.current_page + 1}/{self.total_pages}: {self.pages[self.current_page].title}"
878
+ DrawingPrimitives.draw_text_centered(self.renderer, footer_y, page_info, ColorPalette.DIM_WHITE)
879
+
880
+ # Navigation hints (left side)
881
+ nav_hint = "←→/h/l: Navigate | 0-9: Jump | q/ESC: Exit"
882
+ self.renderer.write_at(2, footer_y, nav_hint, ColorPalette.DIM_GREY)
883
+
884
+ def _init_life_grid(self):
885
+ """Initialize Game of Life grid."""
886
+ grid_width = 60
887
+ grid_height = 20
888
+
889
+ self.life_grid = [[False for _ in range(grid_width)] for _ in range(grid_height)]
890
+
891
+ # Add a glider
892
+ self.life_grid[5][5] = True
893
+ self.life_grid[6][6] = True
894
+ self.life_grid[7][4] = True
895
+ self.life_grid[7][5] = True
896
+ self.life_grid[7][6] = True
897
+
898
+ def _randomize_life_grid(self):
899
+ """Randomize Game of Life grid."""
900
+ for row in range(len(self.life_grid)):
901
+ for col in range(len(self.life_grid[0])):
902
+ self.life_grid[row][col] = random.random() < 0.3
903
+ self.life_generation = 0
904
+
905
+ def _clear_life_grid(self):
906
+ """Clear Game of Life grid."""
907
+ for row in range(len(self.life_grid)):
908
+ for col in range(len(self.life_grid[0])):
909
+ self.life_grid[row][col] = False
910
+ self.life_generation = 0
911
+
912
+ def _update_life_generation(self):
913
+ """Update Game of Life to next generation."""
914
+ rows = len(self.life_grid)
915
+ cols = len(self.life_grid[0])
916
+ new_grid = [[False for _ in range(cols)] for _ in range(rows)]
917
+
918
+ for row in range(rows):
919
+ for col in range(cols):
920
+ # Count neighbors
921
+ neighbors = 0
922
+ for dr in [-1, 0, 1]:
923
+ for dc in [-1, 0, 1]:
924
+ if dr == 0 and dc == 0:
925
+ continue
926
+ nr, nc = row + dr, col + dc
927
+ if 0 <= nr < rows and 0 <= nc < cols and self.life_grid[nr][nc]:
928
+ neighbors += 1
929
+
930
+ # Apply rules
931
+ if self.life_grid[row][col]:
932
+ # Live cell
933
+ new_grid[row][col] = neighbors in [2, 3]
934
+ else:
935
+ # Dead cell
936
+ new_grid[row][col] = neighbors == 3
937
+
938
+ self.life_grid = new_grid
939
+ self.life_generation += 1
940
+
941
+ def _handle_calculator_input(self, key_press: KeyPress) -> bool:
942
+ """Handle calculator key presses."""
943
+ if key_press.char and key_press.char in "0123456789":
944
+ # Number input
945
+ if self.calc_new_number:
946
+ self.calc_display = key_press.char
947
+ self.calc_new_number = False
948
+ else:
949
+ if len(self.calc_display) < 10:
950
+ self.calc_display += key_press.char
951
+ return False
952
+
953
+ elif key_press.char and key_press.char in "+-*/":
954
+ # Operator
955
+ if self.calc_operator and not self.calc_new_number:
956
+ # Calculate previous operation
957
+ self._calculate()
958
+ self.calc_operand = float(self.calc_display)
959
+ self.calc_operator = key_press.char
960
+ self.calc_new_number = True
961
+ return False
962
+
963
+ elif key_press.char and key_press.char in "=\r\n":
964
+ # Equals
965
+ self._calculate()
966
+ return False
967
+
968
+ elif key_press.char and key_press.char.lower() == 'c':
969
+ # Clear
970
+ self.calc_display = "0"
971
+ self.calc_operator = None
972
+ self.calc_operand = None
973
+ self.calc_new_number = True
974
+ return False
975
+
976
+ elif key_press.char == '.':
977
+ # Decimal point
978
+ if '.' not in self.calc_display:
979
+ self.calc_display += '.'
980
+ return False
981
+
982
+ return False
983
+
984
+ def _calculate(self):
985
+ """Perform calculator calculation."""
986
+ if self.calc_operator and self.calc_operand is not None:
987
+ try:
988
+ current = float(self.calc_display)
989
+
990
+ if self.calc_operator == '+':
991
+ result = self.calc_operand + current
992
+ elif self.calc_operator == '-':
993
+ result = self.calc_operand - current
994
+ elif self.calc_operator == '*':
995
+ result = self.calc_operand * current
996
+ elif self.calc_operator == '/':
997
+ if current != 0:
998
+ result = self.calc_operand / current
999
+ else:
1000
+ self.calc_display = "Error"
1001
+ self.calc_operator = None
1002
+ self.calc_operand = None
1003
+ self.calc_new_number = True
1004
+ return
1005
+ else:
1006
+ return
1007
+
1008
+ # Format result
1009
+ if result == int(result):
1010
+ self.calc_display = str(int(result))
1011
+ else:
1012
+ self.calc_display = f"{result:.6f}".rstrip('0').rstrip('.')
1013
+
1014
+ self.calc_operator = None
1015
+ self.calc_operand = None
1016
+ self.calc_new_number = True
1017
+ except:
1018
+ self.calc_display = "Error"
1019
+ self.calc_operator = None
1020
+ self.calc_operand = None
1021
+ self.calc_new_number = True
289
1022
 
290
1023
  async def handle_input(self, key_press: KeyPress) -> bool:
291
- """Handle input for the example plugin."""
292
- # Exit on 'q' or ESC
1024
+ """Handle user input for navigation and interactions."""
1025
+ # Global: Exit
293
1026
  if key_press.char in ['q', '\x1b'] or key_press.name == "Escape":
1027
+ # Cancel editing mode if active
1028
+ if self.current_page == 7 and self.todo_editing:
1029
+ self.todo_editing = False
1030
+ self.todo_input = SimpleTextInput()
1031
+ return False
294
1032
  return True
295
1033
 
296
- # Navigation with multiple options
1034
+ # Page-specific input handling
1035
+ if self.current_page == 5: # Interactive components
1036
+ # Tab to switch components
1037
+ if key_press.name == "Tab" or key_press.char == '\t':
1038
+ self.active_input = (self.active_input + 1) % 3
1039
+ return False
1040
+
1041
+ # Component-specific input
1042
+ if self.active_input == 0: # Text input
1043
+ self.text_input.handle_key(key_press)
1044
+ return False
1045
+ elif self.active_input == 1: # Checkboxes
1046
+ if key_press.char == ' ':
1047
+ self.checkbox_states[0] = not self.checkbox_states[0]
1048
+ elif key_press.name == "ArrowDown":
1049
+ # Rotate through checkboxes
1050
+ self.checkbox_states.append(self.checkbox_states.pop(0))
1051
+ return False
1052
+ elif self.active_input == 2: # Slider
1053
+ if key_press.name == "ArrowLeft":
1054
+ self.slider_value = max(0, self.slider_value - 5)
1055
+ elif key_press.name == "ArrowRight":
1056
+ self.slider_value = min(100, self.slider_value + 5)
1057
+ return False
1058
+
1059
+ elif self.current_page == 6: # Calculator
1060
+ return self._handle_calculator_input(key_press)
1061
+
1062
+ elif self.current_page == 7: # Todo list
1063
+ if self.todo_editing:
1064
+ # Handle todo input
1065
+ if key_press.name == "Enter" or key_press.char in ['\r', '\n']:
1066
+ if self.todo_input.value.strip():
1067
+ self.todos.append(TodoItem(self.todo_input.value.strip(), False))
1068
+ self.todo_editing = False
1069
+ self.todo_input = SimpleTextInput()
1070
+ return False
1071
+ else:
1072
+ self.todo_input.handle_key(key_press)
1073
+ return False
1074
+ else:
1075
+ # Todo list commands
1076
+ if key_press.char and key_press.char.lower() == 'a':
1077
+ self.todo_editing = True
1078
+ return False
1079
+ elif key_press.char == ' ' and self.todos:
1080
+ # Toggle first uncompleted todo
1081
+ for todo in self.todos:
1082
+ if not todo.completed:
1083
+ todo.completed = True
1084
+ break
1085
+ return False
1086
+ elif key_press.char and key_press.char.lower() == 'd' and self.todos:
1087
+ # Delete first completed todo
1088
+ for i, todo in enumerate(self.todos):
1089
+ if todo.completed:
1090
+ self.todos.pop(i)
1091
+ break
1092
+ return False
1093
+
1094
+ elif self.current_page == 8: # Game of Life
1095
+ if key_press.char == ' ':
1096
+ self.life_running = not self.life_running
1097
+ if self.life_running:
1098
+ self.life_last_update = time.time()
1099
+ return False
1100
+ elif key_press.char and key_press.char.lower() == 'r':
1101
+ self._randomize_life_grid()
1102
+ return False
1103
+ elif key_press.char and key_press.char.lower() == 'c':
1104
+ self._clear_life_grid()
1105
+ self.life_running = False
1106
+ return False
1107
+ elif key_press.char == '+':
1108
+ self.life_speed = max(0.05, self.life_speed - 0.05)
1109
+ return False
1110
+ elif key_press.char == '-':
1111
+ self.life_speed = min(2.0, self.life_speed + 0.05)
1112
+ return False
1113
+
1114
+ # Global navigation
297
1115
  if key_press.char == 'h' or key_press.char == 'a' or key_press.name == "ArrowLeft":
298
1116
  if self.current_page > 0:
299
1117
  self.current_page -= 1
300
- await self._start_page_transition()
301
1118
  elif key_press.char == 'l' or key_press.char == 'd' or key_press.name == "ArrowRight":
302
1119
  if self.current_page < self.total_pages - 1:
303
1120
  self.current_page += 1
304
- await self._start_page_transition()
305
- elif key_press.char in ['1', '2', '3', '4']: # Direct page navigation
306
- new_page = int(key_press.char) - 1
307
- if 0 <= new_page < self.total_pages:
308
- self.current_page = new_page
309
- await self._start_page_transition()
1121
+ elif key_press.name == "Home":
1122
+ self.current_page = 0
1123
+ elif key_press.name == "End":
1124
+ self.current_page = self.total_pages - 1
1125
+ elif key_press.char and key_press.char.isdigit():
1126
+ # Jump to page by number
1127
+ page_num = int(key_press.char)
1128
+ if 0 <= page_num < self.total_pages:
1129
+ self.current_page = page_num
310
1130
 
311
1131
  return False
312
1132
 
313
- async def _start_page_transition(self):
314
- """Start page transition animation."""
315
- current_time = asyncio.get_event_loop().time()
316
- # Could add page transition animations here
317
- # For now, just reset some demo animations
318
- self.demo_animations['bounce'] = self.animation_framework.bounce_in(0.5, current_time)
319
-
320
1133
  async def on_stop(self):
321
- """Called when Example plugin stops."""
1134
+ """Called when plugin stops."""
322
1135
  await super().on_stop()
323
1136
 
324
1137
  async def cleanup(self):
325
- """Clean up example plugin resources."""
1138
+ """Clean up plugin resources."""
326
1139
  self.animation_framework.clear_all()
327
- await super().cleanup()
1140
+ await super().cleanup()