superqode 0.1.5__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 (288) hide show
  1. superqode/__init__.py +33 -0
  2. superqode/acp/__init__.py +23 -0
  3. superqode/acp/client.py +913 -0
  4. superqode/acp/permission_screen.py +457 -0
  5. superqode/acp/types.py +480 -0
  6. superqode/acp_discovery.py +856 -0
  7. superqode/agent/__init__.py +22 -0
  8. superqode/agent/edit_strategies.py +334 -0
  9. superqode/agent/loop.py +892 -0
  10. superqode/agent/qe_report_templates.py +39 -0
  11. superqode/agent/system_prompts.py +353 -0
  12. superqode/agent_output.py +721 -0
  13. superqode/agent_stream.py +953 -0
  14. superqode/agents/__init__.py +59 -0
  15. superqode/agents/acp_registry.py +305 -0
  16. superqode/agents/client.py +249 -0
  17. superqode/agents/data/augmentcode.com.toml +51 -0
  18. superqode/agents/data/cagent.dev.toml +51 -0
  19. superqode/agents/data/claude.com.toml +60 -0
  20. superqode/agents/data/codeassistant.dev.toml +51 -0
  21. superqode/agents/data/codex.openai.com.toml +57 -0
  22. superqode/agents/data/fastagent.ai.toml +66 -0
  23. superqode/agents/data/geminicli.com.toml +77 -0
  24. superqode/agents/data/goose.block.xyz.toml +54 -0
  25. superqode/agents/data/junie.jetbrains.com.toml +56 -0
  26. superqode/agents/data/kimi.moonshot.cn.toml +57 -0
  27. superqode/agents/data/llmlingagent.dev.toml +51 -0
  28. superqode/agents/data/molt.bot.toml +49 -0
  29. superqode/agents/data/opencode.ai.toml +60 -0
  30. superqode/agents/data/stakpak.dev.toml +51 -0
  31. superqode/agents/data/vtcode.dev.toml +51 -0
  32. superqode/agents/discovery.py +266 -0
  33. superqode/agents/messaging.py +160 -0
  34. superqode/agents/persona.py +166 -0
  35. superqode/agents/registry.py +421 -0
  36. superqode/agents/schema.py +72 -0
  37. superqode/agents/unified.py +367 -0
  38. superqode/app/__init__.py +111 -0
  39. superqode/app/constants.py +314 -0
  40. superqode/app/css.py +366 -0
  41. superqode/app/models.py +118 -0
  42. superqode/app/suggester.py +125 -0
  43. superqode/app/widgets.py +1591 -0
  44. superqode/app_enhanced.py +399 -0
  45. superqode/app_main.py +17187 -0
  46. superqode/approval.py +312 -0
  47. superqode/atomic.py +296 -0
  48. superqode/commands/__init__.py +1 -0
  49. superqode/commands/acp.py +965 -0
  50. superqode/commands/agents.py +180 -0
  51. superqode/commands/auth.py +278 -0
  52. superqode/commands/config.py +374 -0
  53. superqode/commands/init.py +826 -0
  54. superqode/commands/providers.py +819 -0
  55. superqode/commands/qe.py +1145 -0
  56. superqode/commands/roles.py +380 -0
  57. superqode/commands/serve.py +172 -0
  58. superqode/commands/suggestions.py +127 -0
  59. superqode/commands/superqe.py +460 -0
  60. superqode/config/__init__.py +51 -0
  61. superqode/config/loader.py +812 -0
  62. superqode/config/schema.py +498 -0
  63. superqode/core/__init__.py +111 -0
  64. superqode/core/roles.py +281 -0
  65. superqode/danger.py +386 -0
  66. superqode/data/superqode-template.yaml +1522 -0
  67. superqode/design_system.py +1080 -0
  68. superqode/dialogs/__init__.py +6 -0
  69. superqode/dialogs/base.py +39 -0
  70. superqode/dialogs/model.py +130 -0
  71. superqode/dialogs/provider.py +870 -0
  72. superqode/diff_view.py +919 -0
  73. superqode/enterprise.py +21 -0
  74. superqode/evaluation/__init__.py +25 -0
  75. superqode/evaluation/adapters.py +93 -0
  76. superqode/evaluation/behaviors.py +89 -0
  77. superqode/evaluation/engine.py +209 -0
  78. superqode/evaluation/scenarios.py +96 -0
  79. superqode/execution/__init__.py +36 -0
  80. superqode/execution/linter.py +538 -0
  81. superqode/execution/modes.py +347 -0
  82. superqode/execution/resolver.py +283 -0
  83. superqode/execution/runner.py +642 -0
  84. superqode/file_explorer.py +811 -0
  85. superqode/file_viewer.py +471 -0
  86. superqode/flash.py +183 -0
  87. superqode/guidance/__init__.py +58 -0
  88. superqode/guidance/config.py +203 -0
  89. superqode/guidance/prompts.py +71 -0
  90. superqode/harness/__init__.py +54 -0
  91. superqode/harness/accelerator.py +291 -0
  92. superqode/harness/config.py +319 -0
  93. superqode/harness/validator.py +147 -0
  94. superqode/history.py +279 -0
  95. superqode/integrations/superopt_runner.py +124 -0
  96. superqode/logging/__init__.py +49 -0
  97. superqode/logging/adapters.py +219 -0
  98. superqode/logging/formatter.py +923 -0
  99. superqode/logging/integration.py +341 -0
  100. superqode/logging/sinks.py +170 -0
  101. superqode/logging/unified_log.py +417 -0
  102. superqode/lsp/__init__.py +26 -0
  103. superqode/lsp/client.py +544 -0
  104. superqode/main.py +1069 -0
  105. superqode/mcp/__init__.py +89 -0
  106. superqode/mcp/auth_storage.py +380 -0
  107. superqode/mcp/client.py +1236 -0
  108. superqode/mcp/config.py +319 -0
  109. superqode/mcp/integration.py +337 -0
  110. superqode/mcp/oauth.py +436 -0
  111. superqode/mcp/oauth_callback.py +385 -0
  112. superqode/mcp/types.py +290 -0
  113. superqode/memory/__init__.py +31 -0
  114. superqode/memory/feedback.py +342 -0
  115. superqode/memory/store.py +522 -0
  116. superqode/notifications.py +369 -0
  117. superqode/optimization/__init__.py +5 -0
  118. superqode/optimization/config.py +33 -0
  119. superqode/permissions/__init__.py +25 -0
  120. superqode/permissions/rules.py +488 -0
  121. superqode/plan.py +323 -0
  122. superqode/providers/__init__.py +33 -0
  123. superqode/providers/gateway/__init__.py +165 -0
  124. superqode/providers/gateway/base.py +228 -0
  125. superqode/providers/gateway/litellm_gateway.py +1170 -0
  126. superqode/providers/gateway/openresponses_gateway.py +436 -0
  127. superqode/providers/health.py +297 -0
  128. superqode/providers/huggingface/__init__.py +74 -0
  129. superqode/providers/huggingface/downloader.py +472 -0
  130. superqode/providers/huggingface/endpoints.py +442 -0
  131. superqode/providers/huggingface/hub.py +531 -0
  132. superqode/providers/huggingface/inference.py +394 -0
  133. superqode/providers/huggingface/transformers_runner.py +516 -0
  134. superqode/providers/local/__init__.py +100 -0
  135. superqode/providers/local/base.py +438 -0
  136. superqode/providers/local/discovery.py +418 -0
  137. superqode/providers/local/lmstudio.py +256 -0
  138. superqode/providers/local/mlx.py +457 -0
  139. superqode/providers/local/ollama.py +486 -0
  140. superqode/providers/local/sglang.py +268 -0
  141. superqode/providers/local/tgi.py +260 -0
  142. superqode/providers/local/tool_support.py +477 -0
  143. superqode/providers/local/vllm.py +258 -0
  144. superqode/providers/manager.py +1338 -0
  145. superqode/providers/models.py +1016 -0
  146. superqode/providers/models_dev.py +578 -0
  147. superqode/providers/openresponses/__init__.py +87 -0
  148. superqode/providers/openresponses/converters/__init__.py +17 -0
  149. superqode/providers/openresponses/converters/messages.py +343 -0
  150. superqode/providers/openresponses/converters/tools.py +268 -0
  151. superqode/providers/openresponses/schema/__init__.py +56 -0
  152. superqode/providers/openresponses/schema/models.py +585 -0
  153. superqode/providers/openresponses/streaming/__init__.py +5 -0
  154. superqode/providers/openresponses/streaming/parser.py +338 -0
  155. superqode/providers/openresponses/tools/__init__.py +21 -0
  156. superqode/providers/openresponses/tools/apply_patch.py +352 -0
  157. superqode/providers/openresponses/tools/code_interpreter.py +290 -0
  158. superqode/providers/openresponses/tools/file_search.py +333 -0
  159. superqode/providers/openresponses/tools/mcp_adapter.py +252 -0
  160. superqode/providers/registry.py +716 -0
  161. superqode/providers/usage.py +332 -0
  162. superqode/pure_mode.py +384 -0
  163. superqode/qr/__init__.py +23 -0
  164. superqode/qr/dashboard.py +781 -0
  165. superqode/qr/generator.py +1018 -0
  166. superqode/qr/templates.py +135 -0
  167. superqode/safety/__init__.py +41 -0
  168. superqode/safety/sandbox.py +413 -0
  169. superqode/safety/warnings.py +256 -0
  170. superqode/server/__init__.py +33 -0
  171. superqode/server/lsp_server.py +775 -0
  172. superqode/server/web.py +250 -0
  173. superqode/session/__init__.py +25 -0
  174. superqode/session/persistence.py +580 -0
  175. superqode/session/sharing.py +477 -0
  176. superqode/session.py +475 -0
  177. superqode/sidebar.py +2991 -0
  178. superqode/stream_view.py +648 -0
  179. superqode/styles/__init__.py +3 -0
  180. superqode/superqe/__init__.py +184 -0
  181. superqode/superqe/acp_runner.py +1064 -0
  182. superqode/superqe/constitution/__init__.py +62 -0
  183. superqode/superqe/constitution/evaluator.py +308 -0
  184. superqode/superqe/constitution/loader.py +432 -0
  185. superqode/superqe/constitution/schema.py +250 -0
  186. superqode/superqe/events.py +591 -0
  187. superqode/superqe/frameworks/__init__.py +65 -0
  188. superqode/superqe/frameworks/base.py +234 -0
  189. superqode/superqe/frameworks/e2e.py +263 -0
  190. superqode/superqe/frameworks/executor.py +237 -0
  191. superqode/superqe/frameworks/javascript.py +409 -0
  192. superqode/superqe/frameworks/python.py +373 -0
  193. superqode/superqe/frameworks/registry.py +92 -0
  194. superqode/superqe/mcp_tools/__init__.py +47 -0
  195. superqode/superqe/mcp_tools/core_tools.py +418 -0
  196. superqode/superqe/mcp_tools/registry.py +230 -0
  197. superqode/superqe/mcp_tools/testing_tools.py +167 -0
  198. superqode/superqe/noise.py +89 -0
  199. superqode/superqe/orchestrator.py +778 -0
  200. superqode/superqe/roles.py +609 -0
  201. superqode/superqe/session.py +713 -0
  202. superqode/superqe/skills/__init__.py +57 -0
  203. superqode/superqe/skills/base.py +106 -0
  204. superqode/superqe/skills/core_skills.py +899 -0
  205. superqode/superqe/skills/registry.py +90 -0
  206. superqode/superqe/verifier.py +101 -0
  207. superqode/superqe_cli.py +76 -0
  208. superqode/tool_call.py +358 -0
  209. superqode/tools/__init__.py +93 -0
  210. superqode/tools/agent_tools.py +496 -0
  211. superqode/tools/base.py +324 -0
  212. superqode/tools/batch_tool.py +133 -0
  213. superqode/tools/diagnostics.py +311 -0
  214. superqode/tools/edit_tools.py +653 -0
  215. superqode/tools/enhanced_base.py +515 -0
  216. superqode/tools/file_tools.py +269 -0
  217. superqode/tools/file_tracking.py +45 -0
  218. superqode/tools/lsp_tools.py +610 -0
  219. superqode/tools/network_tools.py +350 -0
  220. superqode/tools/permissions.py +400 -0
  221. superqode/tools/question_tool.py +324 -0
  222. superqode/tools/search_tools.py +598 -0
  223. superqode/tools/shell_tools.py +259 -0
  224. superqode/tools/todo_tools.py +121 -0
  225. superqode/tools/validation.py +80 -0
  226. superqode/tools/web_tools.py +639 -0
  227. superqode/tui.py +1152 -0
  228. superqode/tui_integration.py +875 -0
  229. superqode/tui_widgets/__init__.py +27 -0
  230. superqode/tui_widgets/widgets/__init__.py +18 -0
  231. superqode/tui_widgets/widgets/progress.py +185 -0
  232. superqode/tui_widgets/widgets/tool_display.py +188 -0
  233. superqode/undo_manager.py +574 -0
  234. superqode/utils/__init__.py +5 -0
  235. superqode/utils/error_handling.py +323 -0
  236. superqode/utils/fuzzy.py +257 -0
  237. superqode/widgets/__init__.py +477 -0
  238. superqode/widgets/agent_collab.py +390 -0
  239. superqode/widgets/agent_store.py +936 -0
  240. superqode/widgets/agent_switcher.py +395 -0
  241. superqode/widgets/animation_manager.py +284 -0
  242. superqode/widgets/code_context.py +356 -0
  243. superqode/widgets/command_palette.py +412 -0
  244. superqode/widgets/connection_status.py +537 -0
  245. superqode/widgets/conversation_history.py +470 -0
  246. superqode/widgets/diff_indicator.py +155 -0
  247. superqode/widgets/enhanced_status_bar.py +385 -0
  248. superqode/widgets/enhanced_toast.py +476 -0
  249. superqode/widgets/file_browser.py +809 -0
  250. superqode/widgets/file_reference.py +585 -0
  251. superqode/widgets/issue_timeline.py +340 -0
  252. superqode/widgets/leader_key.py +264 -0
  253. superqode/widgets/mode_switcher.py +445 -0
  254. superqode/widgets/model_picker.py +234 -0
  255. superqode/widgets/permission_preview.py +1205 -0
  256. superqode/widgets/prompt.py +358 -0
  257. superqode/widgets/provider_connect.py +725 -0
  258. superqode/widgets/pty_shell.py +587 -0
  259. superqode/widgets/qe_dashboard.py +321 -0
  260. superqode/widgets/resizable_sidebar.py +377 -0
  261. superqode/widgets/response_changes.py +218 -0
  262. superqode/widgets/response_display.py +528 -0
  263. superqode/widgets/rich_tool_display.py +613 -0
  264. superqode/widgets/sidebar_panels.py +1180 -0
  265. superqode/widgets/slash_complete.py +356 -0
  266. superqode/widgets/split_view.py +612 -0
  267. superqode/widgets/status_bar.py +273 -0
  268. superqode/widgets/superqode_display.py +786 -0
  269. superqode/widgets/thinking_display.py +815 -0
  270. superqode/widgets/throbber.py +87 -0
  271. superqode/widgets/toast.py +206 -0
  272. superqode/widgets/unified_output.py +1073 -0
  273. superqode/workspace/__init__.py +75 -0
  274. superqode/workspace/artifacts.py +472 -0
  275. superqode/workspace/coordinator.py +353 -0
  276. superqode/workspace/diff_tracker.py +429 -0
  277. superqode/workspace/git_guard.py +373 -0
  278. superqode/workspace/git_snapshot.py +526 -0
  279. superqode/workspace/manager.py +750 -0
  280. superqode/workspace/snapshot.py +357 -0
  281. superqode/workspace/watcher.py +535 -0
  282. superqode/workspace/worktree.py +440 -0
  283. superqode-0.1.5.dist-info/METADATA +204 -0
  284. superqode-0.1.5.dist-info/RECORD +288 -0
  285. superqode-0.1.5.dist-info/WHEEL +5 -0
  286. superqode-0.1.5.dist-info/entry_points.txt +3 -0
  287. superqode-0.1.5.dist-info/licenses/LICENSE +648 -0
  288. superqode-0.1.5.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1080 @@
1
+ """
2
+ SuperQode Design System - Unique Visual Identity.
3
+
4
+ Design Philosophy:
5
+ - QUANTUM-INSPIRED: Playing on the "Q" in SuperQode
6
+ - CRYSTALLINE: Sharp, precise, technical aesthetics
7
+ - PURPLE/MAGENTA: Signature gradient palette
8
+ - MINIMALIST: Clean, no unnecessary decorations
9
+ - PURPOSEFUL ANIMATION: Subtle, meaningful motion
10
+
11
+ Visual Motifs:
12
+ - Hexagonal patterns (quantum/molecular)
13
+ - Gradient lines (energy flow)
14
+ - Minimal borders (clean separation)
15
+ - Monospace precision (code-first)
16
+
17
+ Color System:
18
+ - Primary: Purple gradient (#7c3aed → #a855f7)
19
+ - Secondary: Magenta accent (#ec4899)
20
+ - Success: Emerald (#10b981)
21
+ - Warning: Amber (#f59e0b)
22
+ - Error: Rose (#f43f5e)
23
+ - Background: True black (#000000)
24
+ - Surface: Subtle gray (#0a0a0a)
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ from dataclasses import dataclass
30
+ from typing import Dict, List, Tuple
31
+
32
+
33
+ # ============================================================================
34
+ # COLOR PALETTE
35
+ # ============================================================================
36
+
37
+
38
+ @dataclass
39
+ class ColorPalette:
40
+ """SuperQode signature color palette."""
41
+
42
+ # Background layers
43
+ bg_void: str = "#000000" # True black - deepest
44
+ bg_surface: str = "#050505" # Almost black surface
45
+ bg_elevated: str = "#0a0a0a" # Elevated components
46
+ bg_hover: str = "#0f0f0f" # Hover state
47
+ bg_active: str = "#141414" # Active/pressed state
48
+
49
+ # Border colors
50
+ border_subtle: str = "#1a1a1a" # Subtle separation
51
+ border_default: str = "#27272a" # Default borders
52
+ border_strong: str = "#3f3f46" # Strong emphasis
53
+ border_focus: str = "#7c3aed" # Focus state (purple)
54
+
55
+ # Primary - Purple gradient
56
+ primary_dark: str = "#6d28d9" # Deep violet
57
+ primary: str = "#7c3aed" # Main purple
58
+ primary_light: str = "#8b5cf6" # Light purple
59
+ primary_bright: str = "#a855f7" # Bright purple
60
+ primary_glow: str = "#c084fc" # Glowing purple
61
+
62
+ # Secondary - Magenta accent
63
+ secondary_dark: str = "#be185d" # Deep magenta
64
+ secondary: str = "#ec4899" # Main magenta/pink
65
+ secondary_light: str = "#f472b6" # Light pink
66
+
67
+ # Semantic colors
68
+ success: str = "#10b981" # Emerald
69
+ success_light: str = "#34d399" # Light emerald
70
+ warning: str = "#f59e0b" # Amber
71
+ warning_light: str = "#fbbf24" # Light amber
72
+ error: str = "#f43f5e" # Rose red
73
+ error_light: str = "#fb7185" # Light rose
74
+ info: str = "#06b6d4" # Cyan
75
+ info_light: str = "#22d3ee" # Light cyan
76
+
77
+ # Text colors
78
+ text_primary: str = "#fafafa" # Brightest text
79
+ text_secondary: str = "#e4e4e7" # Normal text
80
+ text_muted: str = "#a1a1aa" # Muted text
81
+ text_dim: str = "#71717a" # Dimmed text
82
+ text_ghost: str = "#52525b" # Ghost text
83
+
84
+ # Special colors
85
+ code_bg: str = "#0c0c0c" # Code block background
86
+ diff_add: str = "#22c55e" # Diff additions
87
+ diff_remove: str = "#ef4444" # Diff deletions
88
+ diff_change: str = "#f59e0b" # Diff changes
89
+
90
+
91
+ COLORS = ColorPalette()
92
+
93
+
94
+ # ============================================================================
95
+ # GRADIENT DEFINITIONS
96
+ # ============================================================================
97
+
98
+ # Signature purple gradient (left to right)
99
+ GRADIENT_PURPLE = ["#6d28d9", "#7c3aed", "#8b5cf6", "#a855f7", "#c084fc"]
100
+
101
+ # Magenta accent gradient
102
+ GRADIENT_MAGENTA = ["#be185d", "#db2777", "#ec4899", "#f472b6"]
103
+
104
+ # Full spectrum (for special effects)
105
+ GRADIENT_SPECTRUM = [
106
+ "#7c3aed", # Purple
107
+ "#8b5cf6", # Light purple
108
+ "#a855f7", # Bright purple
109
+ "#c084fc", # Glow purple
110
+ "#ec4899", # Magenta
111
+ "#f472b6", # Pink
112
+ ]
113
+
114
+ # Quantum energy (for animations)
115
+ GRADIENT_QUANTUM = ["#7c3aed", "#06b6d4", "#22c55e", "#f59e0b", "#ec4899"]
116
+
117
+
118
+ # ============================================================================
119
+ # ICONS - SUPERQODE STYLE
120
+ # ============================================================================
121
+
122
+ SUPERQODE_ICONS = {
123
+ # Status indicators (quantum-inspired)
124
+ "idle": "◇", # Empty diamond - idle state
125
+ "active": "◆", # Filled diamond - active
126
+ "thinking": "◈", # Diamond with dot - processing
127
+ "streaming": "◇◆", # Alternating - streaming
128
+ "success": "✦", # Four-pointed star - success
129
+ "error": "✕", # X mark - error
130
+ "warning": "⚡", # Lightning - warning
131
+ # Connection states
132
+ "connected": "●", # Filled circle - connected
133
+ "disconnected": "○", # Empty circle - disconnected
134
+ "connecting": "◐", # Half circle - connecting
135
+ # Tool kinds (minimal, distinct)
136
+ "file_read": "↳", # Arrow in - reading
137
+ "file_write": "↲", # Arrow out - writing
138
+ "file_edit": "⟳", # Rotate - editing
139
+ "shell": "▸", # Play - shell command
140
+ "search": "⌕", # Magnifier - search
141
+ "glob": "⋮", # Vertical dots - glob
142
+ "browser": "◎", # Target - browser
143
+ "mcp": "⬡", # Hexagon - MCP (quantum)
144
+ "lsp": "λ", # Lambda - LSP/language
145
+ "other": "•", # Bullet - other
146
+ # UI elements
147
+ "expand": "▾", # Triangle down - expand
148
+ "collapse": "▸", # Triangle right - collapse
149
+ "menu": "☰", # Hamburger - menu
150
+ "close": "×", # X - close
151
+ "pin": "⊙", # Circle dot - pin
152
+ "unpin": "○", # Circle - unpin
153
+ # Navigation
154
+ "back": "←",
155
+ "forward": "→",
156
+ "up": "↑",
157
+ "down": "↓",
158
+ # Special
159
+ "quantum": "⬡", # Hexagon - quantum/SuperQode
160
+ "agent": "◈", # Diamond dot - agent
161
+ "model": "⬢", # Filled hexagon - model
162
+ "provider": "⬡", # Hexagon outline - provider
163
+ }
164
+
165
+
166
+ # ============================================================================
167
+ # BORDERS & FRAMES (Crystalline/Hexagonal)
168
+ # ============================================================================
169
+
170
+ # Clean minimal borders
171
+ BORDER_CHARS = {
172
+ "h": "─", # Horizontal
173
+ "v": "│", # Vertical
174
+ "tl": "┌", # Top left
175
+ "tr": "┐", # Top right
176
+ "bl": "└", # Bottom left
177
+ "br": "┘", # Bottom right
178
+ "t": "┬", # T down
179
+ "b": "┴", # T up
180
+ "l": "├", # T right
181
+ "r": "┤", # T left
182
+ "x": "┼", # Cross
183
+ }
184
+
185
+ # Heavy borders for emphasis
186
+ BORDER_HEAVY = {
187
+ "h": "━",
188
+ "v": "┃",
189
+ "tl": "┏",
190
+ "tr": "┓",
191
+ "bl": "┗",
192
+ "br": "┛",
193
+ }
194
+
195
+ # Double borders for panels
196
+ BORDER_DOUBLE = {
197
+ "h": "═",
198
+ "v": "║",
199
+ "tl": "╔",
200
+ "tr": "╗",
201
+ "bl": "╚",
202
+ "br": "╝",
203
+ }
204
+
205
+
206
+ def create_box(width: int, title: str = "", style: str = "default") -> Tuple[str, str, str]:
207
+ """Create top, middle divider, and bottom box lines."""
208
+ chars = (
209
+ BORDER_CHARS if style == "default" else BORDER_HEAVY if style == "heavy" else BORDER_DOUBLE
210
+ )
211
+
212
+ if title:
213
+ title_part = f" {title} "
214
+ remaining = width - len(title_part) - 2
215
+ left_pad = remaining // 2
216
+ right_pad = remaining - left_pad
217
+ top = (
218
+ f"{chars['tl']}{chars['h'] * left_pad}{title_part}{chars['h'] * right_pad}{chars['tr']}"
219
+ )
220
+ else:
221
+ top = f"{chars['tl']}{chars['h'] * (width - 2)}{chars['tr']}"
222
+
223
+ mid = f"{chars['l']}{chars['h'] * (width - 2)}{chars['r']}"
224
+ bottom = f"{chars['bl']}{chars['h'] * (width - 2)}{chars['br']}"
225
+
226
+ return top, mid, bottom
227
+
228
+
229
+ # ============================================================================
230
+ # CSS STYLING
231
+ # ============================================================================
232
+
233
+ SUPERQODE_CSS = """
234
+ /* ============================================================================
235
+ SUPERQODE DESIGN SYSTEM CSS
236
+ Quantum-inspired, crystalline aesthetics
237
+ ============================================================================ */
238
+
239
+ /* Base screen */
240
+ Screen {
241
+ background: #000000;
242
+ }
243
+
244
+ /* ============================================================================
245
+ LAYOUT: Main Grid
246
+ ============================================================================ */
247
+
248
+ #sq-main {
249
+ height: 100%;
250
+ layout: horizontal;
251
+ }
252
+
253
+ /* Sidebar - Minimal, precise */
254
+ #sq-sidebar {
255
+ width: 36;
256
+ background: #050505;
257
+ border-right: solid #1a1a1a;
258
+ }
259
+
260
+ #sq-sidebar.collapsed {
261
+ width: 0;
262
+ display: none;
263
+ }
264
+
265
+ #sq-sidebar-header {
266
+ height: 2;
267
+ background: #0a0a0a;
268
+ padding: 0 1;
269
+ border-bottom: solid #1a1a1a;
270
+ }
271
+
272
+ /* Content area */
273
+ #sq-content {
274
+ width: 1fr;
275
+ height: 100%;
276
+ layout: vertical;
277
+ }
278
+
279
+ /* ============================================================================
280
+ STATUS BAR: Top - Clean, informative
281
+ ============================================================================ */
282
+
283
+ #sq-status-bar {
284
+ height: 1;
285
+ background: #0a0a0a;
286
+ border-bottom: solid #1a1a1a;
287
+ padding: 0 1;
288
+ }
289
+
290
+ .sq-status-item {
291
+ margin-right: 2;
292
+ }
293
+
294
+ .sq-status-connected {
295
+ color: #10b981;
296
+ }
297
+
298
+ .sq-status-disconnected {
299
+ color: #71717a;
300
+ }
301
+
302
+ /* ============================================================================
303
+ TOOL PANEL: Collapsible, minimal
304
+ ============================================================================ */
305
+
306
+ #sq-tool-panel {
307
+ height: auto;
308
+ max-height: 25%;
309
+ background: #050505;
310
+ border-bottom: solid #1a1a1a;
311
+ padding: 0 1;
312
+ }
313
+
314
+ #sq-tool-panel.collapsed {
315
+ max-height: 1;
316
+ overflow: hidden;
317
+ }
318
+
319
+ #sq-tool-panel .tool-header {
320
+ height: 1;
321
+ color: #a1a1aa;
322
+ }
323
+
324
+ #sq-tool-panel .tool-item {
325
+ padding: 0 1;
326
+ border-left: solid #27272a;
327
+ margin-left: 1;
328
+ }
329
+
330
+ #sq-tool-panel .tool-item.running {
331
+ border-left: solid #7c3aed;
332
+ }
333
+
334
+ #sq-tool-panel .tool-item.success {
335
+ border-left: solid #10b981;
336
+ }
337
+
338
+ #sq-tool-panel .tool-item.error {
339
+ border-left: solid #f43f5e;
340
+ }
341
+
342
+ /* ============================================================================
343
+ THINKING INDICATOR: Subtle, informative
344
+ ============================================================================ */
345
+
346
+ #sq-thinking {
347
+ height: auto;
348
+ max-height: 15%;
349
+ background: #050505;
350
+ border-bottom: solid #7c3aed40;
351
+ padding: 0 1;
352
+ display: none;
353
+ }
354
+
355
+ #sq-thinking.visible {
356
+ display: block;
357
+ }
358
+
359
+ #sq-thinking .thinking-label {
360
+ color: #8b5cf6;
361
+ text-style: italic;
362
+ }
363
+
364
+ #sq-thinking .thinking-text {
365
+ color: #a1a1aa;
366
+ text-style: italic;
367
+ }
368
+
369
+ /* ============================================================================
370
+ CONVERSATION: Main content area
371
+ ============================================================================ */
372
+
373
+ #sq-conversation {
374
+ height: 1fr;
375
+ background: #000000;
376
+ padding: 1 2;
377
+ overflow-y: auto;
378
+ }
379
+
380
+ /* Message styling */
381
+ .sq-message {
382
+ margin-bottom: 1;
383
+ padding: 0 1;
384
+ }
385
+
386
+ .sq-message-user {
387
+ border-left: solid #7c3aed;
388
+ }
389
+
390
+ .sq-message-assistant {
391
+ border-left: solid #ec4899;
392
+ }
393
+
394
+ .sq-message-system {
395
+ border-left: solid #52525b;
396
+ color: #71717a;
397
+ }
398
+
399
+ /* Code blocks - Precise, minimal */
400
+ .sq-code-block {
401
+ background: #0c0c0c;
402
+ border: solid #1a1a1a;
403
+ padding: 1;
404
+ margin: 1 0;
405
+ }
406
+
407
+ .sq-code-header {
408
+ background: #0f0f0f;
409
+ padding: 0 1;
410
+ color: #71717a;
411
+ border-bottom: solid #1a1a1a;
412
+ }
413
+
414
+ /* ============================================================================
415
+ PROMPT AREA: Clean input
416
+ ============================================================================ */
417
+
418
+ #sq-prompt-area {
419
+ height: auto;
420
+ min-height: 3;
421
+ max-height: 8;
422
+ background: #0a0a0a;
423
+ border-top: solid #1a1a1a;
424
+ padding: 1;
425
+ }
426
+
427
+ #sq-prompt-area.hidden {
428
+ display: none;
429
+ }
430
+
431
+ #sq-prompt-input-box {
432
+ background: #050505;
433
+ border: solid #27272a;
434
+ padding: 0 1;
435
+ }
436
+
437
+ #sq-prompt-input-box:focus-within {
438
+ border: solid #7c3aed;
439
+ }
440
+
441
+ #sq-prompt-symbol {
442
+ width: 2;
443
+ color: #7c3aed;
444
+ }
445
+
446
+ #sq-prompt-input {
447
+ background: transparent;
448
+ border: none;
449
+ width: 1fr;
450
+ }
451
+
452
+ /* Mode indicator */
453
+ #sq-mode-indicator {
454
+ height: 1;
455
+ padding: 0 1;
456
+ }
457
+
458
+ .sq-mode-auto {
459
+ color: #10b981;
460
+ }
461
+
462
+ .sq-mode-ask {
463
+ color: #f59e0b;
464
+ }
465
+
466
+ .sq-mode-deny {
467
+ color: #f43f5e;
468
+ }
469
+
470
+ /* ============================================================================
471
+ SPLIT VIEW: Code + Chat
472
+ ============================================================================ */
473
+
474
+ #sq-split-container {
475
+ height: 100%;
476
+ layout: horizontal;
477
+ }
478
+
479
+ #sq-split-left {
480
+ width: 50%;
481
+ border-right: solid #1a1a1a;
482
+ }
483
+
484
+ #sq-split-right {
485
+ width: 50%;
486
+ }
487
+
488
+ #sq-split-divider {
489
+ width: 1;
490
+ background: #1a1a1a;
491
+ cursor: col-resize;
492
+ }
493
+
494
+ #sq-split-divider:hover {
495
+ background: #7c3aed;
496
+ }
497
+
498
+ /* ============================================================================
499
+ DIFF VIEW: Clean, precise
500
+ ============================================================================ */
501
+
502
+ .sq-diff {
503
+ background: #0c0c0c;
504
+ border: solid #1a1a1a;
505
+ padding: 1;
506
+ }
507
+
508
+ .sq-diff-add {
509
+ background: #22c55e15;
510
+ color: #22c55e;
511
+ }
512
+
513
+ .sq-diff-remove {
514
+ background: #ef444415;
515
+ color: #ef4444;
516
+ }
517
+
518
+ .sq-diff-context {
519
+ color: #71717a;
520
+ }
521
+
522
+ .sq-diff-line-number {
523
+ color: #52525b;
524
+ width: 4;
525
+ text-align: right;
526
+ padding-right: 1;
527
+ }
528
+
529
+ /* ============================================================================
530
+ CONNECTION PANEL: ACP/BYOK status
531
+ ============================================================================ */
532
+
533
+ #sq-connection-panel {
534
+ background: #050505;
535
+ border: solid #1a1a1a;
536
+ padding: 1;
537
+ }
538
+
539
+ .sq-connection-header {
540
+ color: #a1a1aa;
541
+ margin-bottom: 1;
542
+ }
543
+
544
+ .sq-connection-acp {
545
+ color: #10b981;
546
+ }
547
+
548
+ .sq-connection-byok {
549
+ color: #06b6d4;
550
+ }
551
+
552
+ .sq-connection-local {
553
+ color: #f59e0b;
554
+ }
555
+
556
+ /* ============================================================================
557
+ ANIMATIONS: Subtle, purposeful
558
+ ============================================================================ */
559
+
560
+ /* Quantum pulse - for active states */
561
+ @keyframes sq-pulse {
562
+ 0% { color: #7c3aed; }
563
+ 50% { color: #a855f7; }
564
+ 100% { color: #7c3aed; }
565
+ }
566
+
567
+ .sq-pulse {
568
+ animation: sq-pulse 2s infinite;
569
+ }
570
+
571
+ /* Stream indicator */
572
+ @keyframes sq-stream {
573
+ 0% { opacity: 0.3; }
574
+ 50% { opacity: 1; }
575
+ 100% { opacity: 0.3; }
576
+ }
577
+
578
+ .sq-streaming {
579
+ animation: sq-stream 1s infinite;
580
+ }
581
+
582
+ /* ============================================================================
583
+ FOOTER: Minimal
584
+ ============================================================================ */
585
+
586
+ Footer {
587
+ background: #0a0a0a;
588
+ color: #52525b;
589
+ height: 1;
590
+ }
591
+
592
+ Footer .footer-key {
593
+ color: #7c3aed;
594
+ }
595
+ """
596
+
597
+
598
+ # ============================================================================
599
+ # TEXT RENDERING HELPERS
600
+ # ============================================================================
601
+
602
+
603
+ def render_gradient_text(text: str, gradient: List[str] = None) -> "Text":
604
+ """Render text with a gradient effect."""
605
+ from rich.text import Text
606
+
607
+ if gradient is None:
608
+ gradient = GRADIENT_PURPLE
609
+
610
+ result = Text()
611
+ for i, char in enumerate(text):
612
+ color = gradient[i % len(gradient)]
613
+ result.append(char, style=f"bold {color}")
614
+
615
+ return result
616
+
617
+
618
+ def render_status_indicator(
619
+ connected: bool,
620
+ agent_name: str = "",
621
+ model_name: str = "",
622
+ connection_type: str = "",
623
+ ) -> "Text":
624
+ """Render a clean status indicator."""
625
+ from rich.text import Text
626
+
627
+ result = Text()
628
+
629
+ # Connection status
630
+ if connected:
631
+ result.append(SUPERQODE_ICONS["connected"], style=f"bold {COLORS.success}")
632
+ result.append(" ", style="")
633
+
634
+ if agent_name:
635
+ result.append(agent_name, style=f"bold {COLORS.text_secondary}")
636
+
637
+ if model_name:
638
+ if agent_name:
639
+ result.append(" → ", style=COLORS.text_dim)
640
+ result.append(model_name, style=COLORS.text_muted)
641
+
642
+ if connection_type:
643
+ conn_color = {
644
+ "acp": COLORS.success,
645
+ "byok": COLORS.info,
646
+ "local": COLORS.warning,
647
+ }.get(connection_type, COLORS.text_muted)
648
+ result.append(f" [{connection_type.upper()}]", style=conn_color)
649
+ else:
650
+ result.append(SUPERQODE_ICONS["disconnected"], style=COLORS.text_dim)
651
+ result.append(" Not connected", style=COLORS.text_dim)
652
+
653
+ return result
654
+
655
+
656
+ def render_tool_indicator(
657
+ name: str,
658
+ kind: str,
659
+ status: str = "running",
660
+ file_path: str = "",
661
+ ) -> "Text":
662
+ """Render a minimal tool indicator."""
663
+ from rich.text import Text
664
+
665
+ result = Text()
666
+
667
+ # Status icon
668
+ icon = SUPERQODE_ICONS.get(f"file_{kind}", SUPERQODE_ICONS.get(kind, SUPERQODE_ICONS["other"]))
669
+
670
+ status_styles = {
671
+ "pending": COLORS.text_dim,
672
+ "running": COLORS.primary,
673
+ "success": COLORS.success,
674
+ "error": COLORS.error,
675
+ }
676
+ style = status_styles.get(status, COLORS.text_muted)
677
+
678
+ result.append(f"{icon} ", style=f"bold {style}")
679
+ result.append(name, style=COLORS.text_secondary)
680
+
681
+ if file_path:
682
+ result.append(f" {file_path}", style=COLORS.text_dim)
683
+
684
+ return result
685
+
686
+
687
+ def render_thinking_line(text: str) -> "Text":
688
+ """Render a subtle thinking line."""
689
+ from rich.text import Text
690
+
691
+ result = Text()
692
+ result.append(f"{SUPERQODE_ICONS['thinking']} ", style=f"bold {COLORS.primary_light}")
693
+
694
+ # Truncate if too long
695
+ display = text[:80] + "…" if len(text) > 80 else text
696
+ result.append(display, style=f"italic {COLORS.text_muted}")
697
+
698
+ return result
699
+
700
+
701
+ def render_message_header(
702
+ role: str,
703
+ agent_name: str = "",
704
+ timestamp: str = "",
705
+ token_count: int = 0,
706
+ ) -> "Text":
707
+ """Render a clean message header."""
708
+ from rich.text import Text
709
+
710
+ result = Text()
711
+
712
+ if role == "user":
713
+ result.append("▸ ", style=f"bold {COLORS.primary}")
714
+ result.append("You", style=f"bold {COLORS.text_primary}")
715
+ elif role == "assistant":
716
+ result.append("◇ ", style=f"bold {COLORS.secondary}")
717
+ if agent_name:
718
+ result.append(agent_name, style=f"bold {COLORS.text_primary}")
719
+ else:
720
+ result.append("Assistant", style=f"bold {COLORS.text_primary}")
721
+ else:
722
+ result.append("• ", style=COLORS.text_dim)
723
+ result.append(role.title(), style=COLORS.text_muted)
724
+
725
+ if timestamp:
726
+ result.append(f" {timestamp}", style=COLORS.text_ghost)
727
+
728
+ if token_count > 0:
729
+ result.append(f" {token_count} tokens", style=COLORS.text_ghost)
730
+
731
+ return result
732
+
733
+
734
+ # ============================================================================
735
+ # QUANTUM ANIMATIONS (Character-based)
736
+ # ============================================================================
737
+
738
+ QUANTUM_FRAMES = [
739
+ "◇ ◇ ◇",
740
+ "◆ ◇ ◇",
741
+ "◇ ◆ ◇",
742
+ "◇ ◇ ◆",
743
+ "◇ ◆ ◇",
744
+ "◆ ◇ ◇",
745
+ ]
746
+
747
+ STREAM_FRAMES = [
748
+ "▸",
749
+ "▸▸",
750
+ "▸▸▸",
751
+ "▸▸",
752
+ "▸",
753
+ "",
754
+ ]
755
+
756
+ THINKING_FRAMES = [
757
+ "◈ ",
758
+ " ◈ ",
759
+ " ◈ ",
760
+ " ◈ ",
761
+ " ◈",
762
+ " ◈ ",
763
+ " ◈ ",
764
+ " ◈ ",
765
+ ]
766
+
767
+
768
+ def get_animation_frame(name: str, tick: int) -> str:
769
+ """Get animation frame by name and tick count."""
770
+ frames = {
771
+ "quantum": QUANTUM_FRAMES,
772
+ "stream": STREAM_FRAMES,
773
+ "thinking": THINKING_FRAMES,
774
+ }
775
+
776
+ frame_list = frames.get(name, QUANTUM_FRAMES)
777
+ return frame_list[tick % len(frame_list)]
778
+
779
+
780
+ # ============================================================================
781
+ # THEME SYSTEM
782
+ # ============================================================================
783
+
784
+
785
+ @dataclass
786
+ class Theme:
787
+ """A complete color theme."""
788
+
789
+ name: str
790
+ description: str
791
+ colors: ColorPalette
792
+
793
+
794
+ # Built-in themes
795
+ THEME_SUPERQODE = Theme(
796
+ name="superqode",
797
+ description="Default SuperQode theme - Purple quantum aesthetics",
798
+ colors=ColorPalette(), # Default colors
799
+ )
800
+
801
+ THEME_TOKYONIGHT = Theme(
802
+ name="tokyonight",
803
+ description="Tokyo Night - Deep blue with neon accents",
804
+ colors=ColorPalette(
805
+ # Background layers - deep blue
806
+ bg_void="#1a1b26",
807
+ bg_surface="#1f2335",
808
+ bg_elevated="#24283b",
809
+ bg_hover="#292e42",
810
+ bg_active="#3b4261",
811
+ # Border colors
812
+ border_subtle="#292e42",
813
+ border_default="#3b4261",
814
+ border_strong="#565f89",
815
+ border_focus="#7aa2f7",
816
+ # Primary - Blue
817
+ primary_dark="#3d59a1",
818
+ primary="#7aa2f7",
819
+ primary_light="#89b4fa",
820
+ primary_bright="#b4f9f8",
821
+ primary_glow="#c0caf5",
822
+ # Secondary - Magenta
823
+ secondary_dark="#ad8ee6",
824
+ secondary="#bb9af7",
825
+ secondary_light="#c0caf5",
826
+ # Semantic colors
827
+ success="#9ece6a",
828
+ success_light="#a9dc76",
829
+ warning="#e0af68",
830
+ warning_light="#f7c273",
831
+ error="#f7768e",
832
+ error_light="#ff9e64",
833
+ info="#7dcfff",
834
+ info_light="#89ddff",
835
+ # Text colors
836
+ text_primary="#c0caf5",
837
+ text_secondary="#a9b1d6",
838
+ text_muted="#737aa2",
839
+ text_dim="#565f89",
840
+ text_ghost="#414868",
841
+ # Special
842
+ code_bg="#1f2335",
843
+ diff_add="#9ece6a",
844
+ diff_remove="#f7768e",
845
+ diff_change="#e0af68",
846
+ ),
847
+ )
848
+
849
+ THEME_DRACULA = Theme(
850
+ name="dracula",
851
+ description="Dracula - Classic dark with vivid colors",
852
+ colors=ColorPalette(
853
+ # Background layers
854
+ bg_void="#282a36",
855
+ bg_surface="#1e1f29",
856
+ bg_elevated="#21222c",
857
+ bg_hover="#343746",
858
+ bg_active="#44475a",
859
+ # Border colors
860
+ border_subtle="#343746",
861
+ border_default="#44475a",
862
+ border_strong="#6272a4",
863
+ border_focus="#bd93f9",
864
+ # Primary - Purple
865
+ primary_dark="#6d28d9",
866
+ primary="#bd93f9",
867
+ primary_light="#caa9fa",
868
+ primary_bright="#d6bcfa",
869
+ primary_glow="#e2cffc",
870
+ # Secondary - Pink
871
+ secondary_dark="#e64faf",
872
+ secondary="#ff79c6",
873
+ secondary_light="#ff92d0",
874
+ # Semantic colors
875
+ success="#50fa7b",
876
+ success_light="#69ff94",
877
+ warning="#ffb86c",
878
+ warning_light="#ffc67d",
879
+ error="#ff5555",
880
+ error_light="#ff6e6e",
881
+ info="#8be9fd",
882
+ info_light="#9ff2ff",
883
+ # Text colors
884
+ text_primary="#f8f8f2",
885
+ text_secondary="#e4e4ef",
886
+ text_muted="#a9a9b5",
887
+ text_dim="#6272a4",
888
+ text_ghost="#44475a",
889
+ # Special
890
+ code_bg="#1e1f29",
891
+ diff_add="#50fa7b",
892
+ diff_remove="#ff5555",
893
+ diff_change="#ffb86c",
894
+ ),
895
+ )
896
+
897
+ THEME_NORD = Theme(
898
+ name="nord",
899
+ description="Nord - Arctic, calm blue palette",
900
+ colors=ColorPalette(
901
+ # Background layers - polar night
902
+ bg_void="#2e3440",
903
+ bg_surface="#3b4252",
904
+ bg_elevated="#434c5e",
905
+ bg_hover="#4c566a",
906
+ bg_active="#5e6779",
907
+ # Border colors
908
+ border_subtle="#3b4252",
909
+ border_default="#434c5e",
910
+ border_strong="#4c566a",
911
+ border_focus="#88c0d0",
912
+ # Primary - Frost
913
+ primary_dark="#5e81ac",
914
+ primary="#81a1c1",
915
+ primary_light="#88c0d0",
916
+ primary_bright="#8fbcbb",
917
+ primary_glow="#a3d3d2",
918
+ # Secondary - Aurora
919
+ secondary_dark="#b48ead",
920
+ secondary="#b48ead",
921
+ secondary_light="#c4a1b8",
922
+ # Semantic colors
923
+ success="#a3be8c",
924
+ success_light="#b4d89a",
925
+ warning="#ebcb8b",
926
+ warning_light="#f0d899",
927
+ error="#bf616a",
928
+ error_light="#d08770",
929
+ info="#88c0d0",
930
+ info_light="#8fbcbb",
931
+ # Text colors
932
+ text_primary="#eceff4",
933
+ text_secondary="#e5e9f0",
934
+ text_muted="#d8dee9",
935
+ text_dim="#a3adb8",
936
+ text_ghost="#4c566a",
937
+ # Special
938
+ code_bg="#3b4252",
939
+ diff_add="#a3be8c",
940
+ diff_remove="#bf616a",
941
+ diff_change="#ebcb8b",
942
+ ),
943
+ )
944
+
945
+ THEME_MONOKAI = Theme(
946
+ name="monokai",
947
+ description="Monokai Pro - Warm, vibrant colors",
948
+ colors=ColorPalette(
949
+ # Background layers
950
+ bg_void="#2d2a2e",
951
+ bg_surface="#221f22",
952
+ bg_elevated="#2d2a2e",
953
+ bg_hover="#363337",
954
+ bg_active="#403e41",
955
+ # Border colors
956
+ border_subtle="#363337",
957
+ border_default="#403e41",
958
+ border_strong="#5b595c",
959
+ border_focus="#ffd866",
960
+ # Primary - Yellow
961
+ primary_dark="#d9a23a",
962
+ primary="#ffd866",
963
+ primary_light="#ffe17d",
964
+ primary_bright="#ffe999",
965
+ primary_glow="#fff2b5",
966
+ # Secondary - Pink
967
+ secondary_dark="#cc6a7f",
968
+ secondary="#ff6188",
969
+ secondary_light="#ff7a9a",
970
+ # Semantic colors
971
+ success="#a9dc76",
972
+ success_light="#b8e38a",
973
+ warning="#ffd866",
974
+ warning_light="#ffe17d",
975
+ error="#ff6188",
976
+ error_light="#ff7a9a",
977
+ info="#78dce8",
978
+ info_light="#8ce4ed",
979
+ # Text colors
980
+ text_primary="#fcfcfa",
981
+ text_secondary="#f2f2f0",
982
+ text_muted="#939293",
983
+ text_dim="#727072",
984
+ text_ghost="#5b595c",
985
+ # Special
986
+ code_bg="#221f22",
987
+ diff_add="#a9dc76",
988
+ diff_remove="#ff6188",
989
+ diff_change="#ffd866",
990
+ ),
991
+ )
992
+
993
+ THEME_GRUVBOX = Theme(
994
+ name="gruvbox",
995
+ description="Gruvbox - Retro, earthy tones",
996
+ colors=ColorPalette(
997
+ # Background layers
998
+ bg_void="#282828",
999
+ bg_surface="#1d2021",
1000
+ bg_elevated="#282828",
1001
+ bg_hover="#32302f",
1002
+ bg_active="#3c3836",
1003
+ # Border colors
1004
+ border_subtle="#3c3836",
1005
+ border_default="#504945",
1006
+ border_strong="#665c54",
1007
+ border_focus="#fe8019",
1008
+ # Primary - Orange
1009
+ primary_dark="#d65d0e",
1010
+ primary="#fe8019",
1011
+ primary_light="#fe9932",
1012
+ primary_bright="#feb24c",
1013
+ primary_glow="#fec969",
1014
+ # Secondary - Aqua
1015
+ secondary_dark="#689d6a",
1016
+ secondary="#8ec07c",
1017
+ secondary_light="#a0cf8f",
1018
+ # Semantic colors
1019
+ success="#b8bb26",
1020
+ success_light="#c5c93a",
1021
+ warning="#fabd2f",
1022
+ warning_light="#fcca4b",
1023
+ error="#fb4934",
1024
+ error_light="#fc5f51",
1025
+ info="#83a598",
1026
+ info_light="#94b6a8",
1027
+ # Text colors
1028
+ text_primary="#ebdbb2",
1029
+ text_secondary="#d5c4a1",
1030
+ text_muted="#928374",
1031
+ text_dim="#7c6f64",
1032
+ text_ghost="#504945",
1033
+ # Special
1034
+ code_bg="#1d2021",
1035
+ diff_add="#b8bb26",
1036
+ diff_remove="#fb4934",
1037
+ diff_change="#fabd2f",
1038
+ ),
1039
+ )
1040
+
1041
+
1042
+ # Theme registry
1043
+ THEMES: Dict[str, Theme] = {
1044
+ "superqode": THEME_SUPERQODE,
1045
+ "tokyonight": THEME_TOKYONIGHT,
1046
+ "dracula": THEME_DRACULA,
1047
+ "nord": THEME_NORD,
1048
+ "monokai": THEME_MONOKAI,
1049
+ "gruvbox": THEME_GRUVBOX,
1050
+ }
1051
+
1052
+ # Active theme (mutable)
1053
+ _active_theme: str = "superqode"
1054
+
1055
+
1056
+ def get_theme(name: str = None) -> Theme:
1057
+ """Get a theme by name, or the active theme if no name given."""
1058
+ if name is None:
1059
+ name = _active_theme
1060
+ return THEMES.get(name, THEME_SUPERQODE)
1061
+
1062
+
1063
+ def set_theme(name: str) -> bool:
1064
+ """Set the active theme. Returns True if successful."""
1065
+ global _active_theme, COLORS
1066
+ if name in THEMES:
1067
+ _active_theme = name
1068
+ COLORS = THEMES[name].colors
1069
+ return True
1070
+ return False
1071
+
1072
+
1073
+ def get_active_theme_name() -> str:
1074
+ """Get the name of the active theme."""
1075
+ return _active_theme
1076
+
1077
+
1078
+ def list_themes() -> List[Tuple[str, str]]:
1079
+ """List all available themes as (name, description) tuples."""
1080
+ return [(name, theme.description) for name, theme in THEMES.items()]