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.
- superqode/__init__.py +33 -0
- superqode/acp/__init__.py +23 -0
- superqode/acp/client.py +913 -0
- superqode/acp/permission_screen.py +457 -0
- superqode/acp/types.py +480 -0
- superqode/acp_discovery.py +856 -0
- superqode/agent/__init__.py +22 -0
- superqode/agent/edit_strategies.py +334 -0
- superqode/agent/loop.py +892 -0
- superqode/agent/qe_report_templates.py +39 -0
- superqode/agent/system_prompts.py +353 -0
- superqode/agent_output.py +721 -0
- superqode/agent_stream.py +953 -0
- superqode/agents/__init__.py +59 -0
- superqode/agents/acp_registry.py +305 -0
- superqode/agents/client.py +249 -0
- superqode/agents/data/augmentcode.com.toml +51 -0
- superqode/agents/data/cagent.dev.toml +51 -0
- superqode/agents/data/claude.com.toml +60 -0
- superqode/agents/data/codeassistant.dev.toml +51 -0
- superqode/agents/data/codex.openai.com.toml +57 -0
- superqode/agents/data/fastagent.ai.toml +66 -0
- superqode/agents/data/geminicli.com.toml +77 -0
- superqode/agents/data/goose.block.xyz.toml +54 -0
- superqode/agents/data/junie.jetbrains.com.toml +56 -0
- superqode/agents/data/kimi.moonshot.cn.toml +57 -0
- superqode/agents/data/llmlingagent.dev.toml +51 -0
- superqode/agents/data/molt.bot.toml +49 -0
- superqode/agents/data/opencode.ai.toml +60 -0
- superqode/agents/data/stakpak.dev.toml +51 -0
- superqode/agents/data/vtcode.dev.toml +51 -0
- superqode/agents/discovery.py +266 -0
- superqode/agents/messaging.py +160 -0
- superqode/agents/persona.py +166 -0
- superqode/agents/registry.py +421 -0
- superqode/agents/schema.py +72 -0
- superqode/agents/unified.py +367 -0
- superqode/app/__init__.py +111 -0
- superqode/app/constants.py +314 -0
- superqode/app/css.py +366 -0
- superqode/app/models.py +118 -0
- superqode/app/suggester.py +125 -0
- superqode/app/widgets.py +1591 -0
- superqode/app_enhanced.py +399 -0
- superqode/app_main.py +17187 -0
- superqode/approval.py +312 -0
- superqode/atomic.py +296 -0
- superqode/commands/__init__.py +1 -0
- superqode/commands/acp.py +965 -0
- superqode/commands/agents.py +180 -0
- superqode/commands/auth.py +278 -0
- superqode/commands/config.py +374 -0
- superqode/commands/init.py +826 -0
- superqode/commands/providers.py +819 -0
- superqode/commands/qe.py +1145 -0
- superqode/commands/roles.py +380 -0
- superqode/commands/serve.py +172 -0
- superqode/commands/suggestions.py +127 -0
- superqode/commands/superqe.py +460 -0
- superqode/config/__init__.py +51 -0
- superqode/config/loader.py +812 -0
- superqode/config/schema.py +498 -0
- superqode/core/__init__.py +111 -0
- superqode/core/roles.py +281 -0
- superqode/danger.py +386 -0
- superqode/data/superqode-template.yaml +1522 -0
- superqode/design_system.py +1080 -0
- superqode/dialogs/__init__.py +6 -0
- superqode/dialogs/base.py +39 -0
- superqode/dialogs/model.py +130 -0
- superqode/dialogs/provider.py +870 -0
- superqode/diff_view.py +919 -0
- superqode/enterprise.py +21 -0
- superqode/evaluation/__init__.py +25 -0
- superqode/evaluation/adapters.py +93 -0
- superqode/evaluation/behaviors.py +89 -0
- superqode/evaluation/engine.py +209 -0
- superqode/evaluation/scenarios.py +96 -0
- superqode/execution/__init__.py +36 -0
- superqode/execution/linter.py +538 -0
- superqode/execution/modes.py +347 -0
- superqode/execution/resolver.py +283 -0
- superqode/execution/runner.py +642 -0
- superqode/file_explorer.py +811 -0
- superqode/file_viewer.py +471 -0
- superqode/flash.py +183 -0
- superqode/guidance/__init__.py +58 -0
- superqode/guidance/config.py +203 -0
- superqode/guidance/prompts.py +71 -0
- superqode/harness/__init__.py +54 -0
- superqode/harness/accelerator.py +291 -0
- superqode/harness/config.py +319 -0
- superqode/harness/validator.py +147 -0
- superqode/history.py +279 -0
- superqode/integrations/superopt_runner.py +124 -0
- superqode/logging/__init__.py +49 -0
- superqode/logging/adapters.py +219 -0
- superqode/logging/formatter.py +923 -0
- superqode/logging/integration.py +341 -0
- superqode/logging/sinks.py +170 -0
- superqode/logging/unified_log.py +417 -0
- superqode/lsp/__init__.py +26 -0
- superqode/lsp/client.py +544 -0
- superqode/main.py +1069 -0
- superqode/mcp/__init__.py +89 -0
- superqode/mcp/auth_storage.py +380 -0
- superqode/mcp/client.py +1236 -0
- superqode/mcp/config.py +319 -0
- superqode/mcp/integration.py +337 -0
- superqode/mcp/oauth.py +436 -0
- superqode/mcp/oauth_callback.py +385 -0
- superqode/mcp/types.py +290 -0
- superqode/memory/__init__.py +31 -0
- superqode/memory/feedback.py +342 -0
- superqode/memory/store.py +522 -0
- superqode/notifications.py +369 -0
- superqode/optimization/__init__.py +5 -0
- superqode/optimization/config.py +33 -0
- superqode/permissions/__init__.py +25 -0
- superqode/permissions/rules.py +488 -0
- superqode/plan.py +323 -0
- superqode/providers/__init__.py +33 -0
- superqode/providers/gateway/__init__.py +165 -0
- superqode/providers/gateway/base.py +228 -0
- superqode/providers/gateway/litellm_gateway.py +1170 -0
- superqode/providers/gateway/openresponses_gateway.py +436 -0
- superqode/providers/health.py +297 -0
- superqode/providers/huggingface/__init__.py +74 -0
- superqode/providers/huggingface/downloader.py +472 -0
- superqode/providers/huggingface/endpoints.py +442 -0
- superqode/providers/huggingface/hub.py +531 -0
- superqode/providers/huggingface/inference.py +394 -0
- superqode/providers/huggingface/transformers_runner.py +516 -0
- superqode/providers/local/__init__.py +100 -0
- superqode/providers/local/base.py +438 -0
- superqode/providers/local/discovery.py +418 -0
- superqode/providers/local/lmstudio.py +256 -0
- superqode/providers/local/mlx.py +457 -0
- superqode/providers/local/ollama.py +486 -0
- superqode/providers/local/sglang.py +268 -0
- superqode/providers/local/tgi.py +260 -0
- superqode/providers/local/tool_support.py +477 -0
- superqode/providers/local/vllm.py +258 -0
- superqode/providers/manager.py +1338 -0
- superqode/providers/models.py +1016 -0
- superqode/providers/models_dev.py +578 -0
- superqode/providers/openresponses/__init__.py +87 -0
- superqode/providers/openresponses/converters/__init__.py +17 -0
- superqode/providers/openresponses/converters/messages.py +343 -0
- superqode/providers/openresponses/converters/tools.py +268 -0
- superqode/providers/openresponses/schema/__init__.py +56 -0
- superqode/providers/openresponses/schema/models.py +585 -0
- superqode/providers/openresponses/streaming/__init__.py +5 -0
- superqode/providers/openresponses/streaming/parser.py +338 -0
- superqode/providers/openresponses/tools/__init__.py +21 -0
- superqode/providers/openresponses/tools/apply_patch.py +352 -0
- superqode/providers/openresponses/tools/code_interpreter.py +290 -0
- superqode/providers/openresponses/tools/file_search.py +333 -0
- superqode/providers/openresponses/tools/mcp_adapter.py +252 -0
- superqode/providers/registry.py +716 -0
- superqode/providers/usage.py +332 -0
- superqode/pure_mode.py +384 -0
- superqode/qr/__init__.py +23 -0
- superqode/qr/dashboard.py +781 -0
- superqode/qr/generator.py +1018 -0
- superqode/qr/templates.py +135 -0
- superqode/safety/__init__.py +41 -0
- superqode/safety/sandbox.py +413 -0
- superqode/safety/warnings.py +256 -0
- superqode/server/__init__.py +33 -0
- superqode/server/lsp_server.py +775 -0
- superqode/server/web.py +250 -0
- superqode/session/__init__.py +25 -0
- superqode/session/persistence.py +580 -0
- superqode/session/sharing.py +477 -0
- superqode/session.py +475 -0
- superqode/sidebar.py +2991 -0
- superqode/stream_view.py +648 -0
- superqode/styles/__init__.py +3 -0
- superqode/superqe/__init__.py +184 -0
- superqode/superqe/acp_runner.py +1064 -0
- superqode/superqe/constitution/__init__.py +62 -0
- superqode/superqe/constitution/evaluator.py +308 -0
- superqode/superqe/constitution/loader.py +432 -0
- superqode/superqe/constitution/schema.py +250 -0
- superqode/superqe/events.py +591 -0
- superqode/superqe/frameworks/__init__.py +65 -0
- superqode/superqe/frameworks/base.py +234 -0
- superqode/superqe/frameworks/e2e.py +263 -0
- superqode/superqe/frameworks/executor.py +237 -0
- superqode/superqe/frameworks/javascript.py +409 -0
- superqode/superqe/frameworks/python.py +373 -0
- superqode/superqe/frameworks/registry.py +92 -0
- superqode/superqe/mcp_tools/__init__.py +47 -0
- superqode/superqe/mcp_tools/core_tools.py +418 -0
- superqode/superqe/mcp_tools/registry.py +230 -0
- superqode/superqe/mcp_tools/testing_tools.py +167 -0
- superqode/superqe/noise.py +89 -0
- superqode/superqe/orchestrator.py +778 -0
- superqode/superqe/roles.py +609 -0
- superqode/superqe/session.py +713 -0
- superqode/superqe/skills/__init__.py +57 -0
- superqode/superqe/skills/base.py +106 -0
- superqode/superqe/skills/core_skills.py +899 -0
- superqode/superqe/skills/registry.py +90 -0
- superqode/superqe/verifier.py +101 -0
- superqode/superqe_cli.py +76 -0
- superqode/tool_call.py +358 -0
- superqode/tools/__init__.py +93 -0
- superqode/tools/agent_tools.py +496 -0
- superqode/tools/base.py +324 -0
- superqode/tools/batch_tool.py +133 -0
- superqode/tools/diagnostics.py +311 -0
- superqode/tools/edit_tools.py +653 -0
- superqode/tools/enhanced_base.py +515 -0
- superqode/tools/file_tools.py +269 -0
- superqode/tools/file_tracking.py +45 -0
- superqode/tools/lsp_tools.py +610 -0
- superqode/tools/network_tools.py +350 -0
- superqode/tools/permissions.py +400 -0
- superqode/tools/question_tool.py +324 -0
- superqode/tools/search_tools.py +598 -0
- superqode/tools/shell_tools.py +259 -0
- superqode/tools/todo_tools.py +121 -0
- superqode/tools/validation.py +80 -0
- superqode/tools/web_tools.py +639 -0
- superqode/tui.py +1152 -0
- superqode/tui_integration.py +875 -0
- superqode/tui_widgets/__init__.py +27 -0
- superqode/tui_widgets/widgets/__init__.py +18 -0
- superqode/tui_widgets/widgets/progress.py +185 -0
- superqode/tui_widgets/widgets/tool_display.py +188 -0
- superqode/undo_manager.py +574 -0
- superqode/utils/__init__.py +5 -0
- superqode/utils/error_handling.py +323 -0
- superqode/utils/fuzzy.py +257 -0
- superqode/widgets/__init__.py +477 -0
- superqode/widgets/agent_collab.py +390 -0
- superqode/widgets/agent_store.py +936 -0
- superqode/widgets/agent_switcher.py +395 -0
- superqode/widgets/animation_manager.py +284 -0
- superqode/widgets/code_context.py +356 -0
- superqode/widgets/command_palette.py +412 -0
- superqode/widgets/connection_status.py +537 -0
- superqode/widgets/conversation_history.py +470 -0
- superqode/widgets/diff_indicator.py +155 -0
- superqode/widgets/enhanced_status_bar.py +385 -0
- superqode/widgets/enhanced_toast.py +476 -0
- superqode/widgets/file_browser.py +809 -0
- superqode/widgets/file_reference.py +585 -0
- superqode/widgets/issue_timeline.py +340 -0
- superqode/widgets/leader_key.py +264 -0
- superqode/widgets/mode_switcher.py +445 -0
- superqode/widgets/model_picker.py +234 -0
- superqode/widgets/permission_preview.py +1205 -0
- superqode/widgets/prompt.py +358 -0
- superqode/widgets/provider_connect.py +725 -0
- superqode/widgets/pty_shell.py +587 -0
- superqode/widgets/qe_dashboard.py +321 -0
- superqode/widgets/resizable_sidebar.py +377 -0
- superqode/widgets/response_changes.py +218 -0
- superqode/widgets/response_display.py +528 -0
- superqode/widgets/rich_tool_display.py +613 -0
- superqode/widgets/sidebar_panels.py +1180 -0
- superqode/widgets/slash_complete.py +356 -0
- superqode/widgets/split_view.py +612 -0
- superqode/widgets/status_bar.py +273 -0
- superqode/widgets/superqode_display.py +786 -0
- superqode/widgets/thinking_display.py +815 -0
- superqode/widgets/throbber.py +87 -0
- superqode/widgets/toast.py +206 -0
- superqode/widgets/unified_output.py +1073 -0
- superqode/workspace/__init__.py +75 -0
- superqode/workspace/artifacts.py +472 -0
- superqode/workspace/coordinator.py +353 -0
- superqode/workspace/diff_tracker.py +429 -0
- superqode/workspace/git_guard.py +373 -0
- superqode/workspace/git_snapshot.py +526 -0
- superqode/workspace/manager.py +750 -0
- superqode/workspace/snapshot.py +357 -0
- superqode/workspace/watcher.py +535 -0
- superqode/workspace/worktree.py +440 -0
- superqode-0.1.5.dist-info/METADATA +204 -0
- superqode-0.1.5.dist-info/RECORD +288 -0
- superqode-0.1.5.dist-info/WHEEL +5 -0
- superqode-0.1.5.dist-info/entry_points.txt +3 -0
- superqode-0.1.5.dist-info/licenses/LICENSE +648 -0
- superqode-0.1.5.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,721 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SuperQode Agent Output Display - World's Most Beautiful CLI Agent Output
|
|
3
|
+
|
|
4
|
+
Features:
|
|
5
|
+
- Collapsible thinking/logs section with toggle (Ctrl+T)
|
|
6
|
+
- Beautiful final response with rich formatting
|
|
7
|
+
- Syntax highlighted code blocks
|
|
8
|
+
- Colorful emojis and gradients
|
|
9
|
+
- Clear visual separation
|
|
10
|
+
- Animated elements
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import re
|
|
16
|
+
from dataclasses import dataclass, field
|
|
17
|
+
from typing import List, Optional, Dict, Any
|
|
18
|
+
from time import monotonic
|
|
19
|
+
|
|
20
|
+
from textual.app import ComposeResult
|
|
21
|
+
from textual.containers import Container, Vertical, Horizontal, ScrollableContainer
|
|
22
|
+
from textual.widgets import Static, Collapsible, RichLog
|
|
23
|
+
from textual.reactive import reactive
|
|
24
|
+
from textual.binding import Binding
|
|
25
|
+
|
|
26
|
+
from rich.text import Text
|
|
27
|
+
from rich.panel import Panel
|
|
28
|
+
from rich.syntax import Syntax
|
|
29
|
+
from rich.markdown import Markdown
|
|
30
|
+
from rich.console import Group
|
|
31
|
+
from rich.box import ROUNDED, HEAVY, DOUBLE
|
|
32
|
+
from rich.table import Table
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# ============================================================================
|
|
36
|
+
# THEME - Vibrant SuperQode Colors
|
|
37
|
+
# ============================================================================
|
|
38
|
+
|
|
39
|
+
COLORS = {
|
|
40
|
+
# Primary gradient
|
|
41
|
+
"purple": "#a855f7",
|
|
42
|
+
"magenta": "#d946ef",
|
|
43
|
+
"pink": "#ec4899",
|
|
44
|
+
"rose": "#fb7185",
|
|
45
|
+
"orange": "#f97316",
|
|
46
|
+
"gold": "#fbbf24",
|
|
47
|
+
# Accent colors
|
|
48
|
+
"cyan": "#06b6d4",
|
|
49
|
+
"teal": "#14b8a6",
|
|
50
|
+
"green": "#22c55e",
|
|
51
|
+
"blue": "#3b82f6",
|
|
52
|
+
# Status
|
|
53
|
+
"success": "#22c55e",
|
|
54
|
+
"error": "#ef4444",
|
|
55
|
+
"warning": "#f59e0b",
|
|
56
|
+
"info": "#06b6d4",
|
|
57
|
+
# Backgrounds
|
|
58
|
+
"bg_dark": "#0a0a0a",
|
|
59
|
+
"bg_surface": "#111111",
|
|
60
|
+
"bg_elevated": "#1a1a1a",
|
|
61
|
+
"bg_thinking": "#0d1117",
|
|
62
|
+
"bg_response": "#0f0a1a",
|
|
63
|
+
# Text
|
|
64
|
+
"text": "#e4e4e7",
|
|
65
|
+
"text_muted": "#71717a",
|
|
66
|
+
"text_dim": "#52525b",
|
|
67
|
+
# Borders
|
|
68
|
+
"border": "#2a2a2a",
|
|
69
|
+
"border_active": "#a855f7",
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
# Rainbow gradient for special effects
|
|
73
|
+
RAINBOW = ["#ef4444", "#f97316", "#eab308", "#22c55e", "#06b6d4", "#3b82f6", "#8b5cf6", "#ec4899"]
|
|
74
|
+
|
|
75
|
+
# Gradient for response header
|
|
76
|
+
RESPONSE_GRADIENT = ["#a855f7", "#c026d3", "#d946ef", "#ec4899", "#f43f5e"]
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
# ============================================================================
|
|
80
|
+
# ICONS & EMOJIS
|
|
81
|
+
# ============================================================================
|
|
82
|
+
|
|
83
|
+
THINKING_ICONS = {
|
|
84
|
+
"read": "📖",
|
|
85
|
+
"write": "✏️",
|
|
86
|
+
"search": "🔍",
|
|
87
|
+
"run": "⚡",
|
|
88
|
+
"think": "🧠",
|
|
89
|
+
"analyze": "🔬",
|
|
90
|
+
"create": "🎨",
|
|
91
|
+
"delete": "🗑️",
|
|
92
|
+
"move": "📦",
|
|
93
|
+
"fetch": "🌐",
|
|
94
|
+
"test": "🧪",
|
|
95
|
+
"build": "🔨",
|
|
96
|
+
"deploy": "🚀",
|
|
97
|
+
"debug": "🐛",
|
|
98
|
+
"config": "⚙️",
|
|
99
|
+
"git": "📦",
|
|
100
|
+
"install": "📥",
|
|
101
|
+
"update": "🔄",
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
RESPONSE_DECORATIONS = {
|
|
105
|
+
"sparkle": "✨",
|
|
106
|
+
"star": "⭐",
|
|
107
|
+
"rocket": "🚀",
|
|
108
|
+
"magic": "🪄",
|
|
109
|
+
"gem": "💎",
|
|
110
|
+
"crown": "👑",
|
|
111
|
+
"fire": "🔥",
|
|
112
|
+
"lightning": "⚡",
|
|
113
|
+
"rainbow": "🌈",
|
|
114
|
+
"heart": "💜",
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def get_thinking_icon(line: str) -> str:
|
|
119
|
+
"""Get appropriate icon for a thinking/log line."""
|
|
120
|
+
line_lower = line.lower()
|
|
121
|
+
|
|
122
|
+
if any(x in line_lower for x in ["reading", "read", "loading", "fetching"]):
|
|
123
|
+
return THINKING_ICONS["read"]
|
|
124
|
+
elif any(x in line_lower for x in ["writing", "write", "creating", "saving"]):
|
|
125
|
+
return THINKING_ICONS["write"]
|
|
126
|
+
elif any(x in line_lower for x in ["searching", "search", "finding", "looking"]):
|
|
127
|
+
return THINKING_ICONS["search"]
|
|
128
|
+
elif any(x in line_lower for x in ["running", "executing", "command"]):
|
|
129
|
+
return THINKING_ICONS["run"]
|
|
130
|
+
elif any(x in line_lower for x in ["thinking", "analyzing", "processing"]):
|
|
131
|
+
return THINKING_ICONS["think"]
|
|
132
|
+
elif any(x in line_lower for x in ["test", "testing"]):
|
|
133
|
+
return THINKING_ICONS["test"]
|
|
134
|
+
elif any(x in line_lower for x in ["build", "compiling"]):
|
|
135
|
+
return THINKING_ICONS["build"]
|
|
136
|
+
elif any(x in line_lower for x in ["git", "commit", "push", "pull"]):
|
|
137
|
+
return THINKING_ICONS["git"]
|
|
138
|
+
elif any(x in line_lower for x in ["install", "npm", "pip", "yarn"]):
|
|
139
|
+
return THINKING_ICONS["install"]
|
|
140
|
+
elif any(x in line_lower for x in ["error", "fail", "bug"]):
|
|
141
|
+
return THINKING_ICONS["debug"]
|
|
142
|
+
else:
|
|
143
|
+
return "▸"
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def strip_markdown(text: str) -> str:
|
|
147
|
+
"""Strip markdown formatting from text for clean display."""
|
|
148
|
+
# Strip bold: **text** or __text__
|
|
149
|
+
text = re.sub(r"\*\*(.+?)\*\*", r"\1", text)
|
|
150
|
+
text = re.sub(r"__(.+?)__", r"\1", text)
|
|
151
|
+
|
|
152
|
+
# Strip italic: *text* or _text_
|
|
153
|
+
text = re.sub(r"(?<![*\w])\*([^*]+?)\*(?![*\w])", r"\1", text)
|
|
154
|
+
text = re.sub(r"(?<![_\w])_([^_]+?)_(?![_\w])", r"\1", text)
|
|
155
|
+
|
|
156
|
+
# Strip strikethrough: ~~text~~
|
|
157
|
+
text = re.sub(r"~~(.+?)~~", r"\1", text)
|
|
158
|
+
|
|
159
|
+
# Strip inline code: `code`
|
|
160
|
+
text = re.sub(r"`([^`]+?)`", r"\1", text)
|
|
161
|
+
|
|
162
|
+
# Strip links: [text](url) -> text
|
|
163
|
+
text = re.sub(r"\[([^\]]+?)\]\([^)]+?\)", r"\1", text)
|
|
164
|
+
|
|
165
|
+
# Strip images:  -> alt
|
|
166
|
+
text = re.sub(r"!\[([^\]]*?)\]\([^)]+?\)", r"\1", text)
|
|
167
|
+
|
|
168
|
+
# Strip blockquotes: > text -> text
|
|
169
|
+
text = re.sub(r"^>\s*", "", text, flags=re.MULTILINE)
|
|
170
|
+
|
|
171
|
+
# Strip headers: # text -> text (keep the text)
|
|
172
|
+
text = re.sub(r"^#{1,6}\s+", "", text, flags=re.MULTILINE)
|
|
173
|
+
|
|
174
|
+
return text
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
# ============================================================================
|
|
178
|
+
# DATA CLASSES
|
|
179
|
+
# ============================================================================
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
@dataclass
|
|
183
|
+
class ThinkingLine:
|
|
184
|
+
"""A single line of thinking/log output."""
|
|
185
|
+
|
|
186
|
+
text: str
|
|
187
|
+
icon: str = ""
|
|
188
|
+
timestamp: float = field(default_factory=monotonic)
|
|
189
|
+
|
|
190
|
+
def __post_init__(self):
|
|
191
|
+
if not self.icon:
|
|
192
|
+
self.icon = get_thinking_icon(self.text)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
@dataclass
|
|
196
|
+
class CodeBlock:
|
|
197
|
+
"""A code block in the response."""
|
|
198
|
+
|
|
199
|
+
code: str
|
|
200
|
+
language: str = "text"
|
|
201
|
+
filename: str = ""
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
@dataclass
|
|
205
|
+
class AgentResponse:
|
|
206
|
+
"""Parsed agent response with structured content."""
|
|
207
|
+
|
|
208
|
+
raw_text: str
|
|
209
|
+
paragraphs: List[str] = field(default_factory=list)
|
|
210
|
+
code_blocks: List[CodeBlock] = field(default_factory=list)
|
|
211
|
+
bullet_points: List[str] = field(default_factory=list)
|
|
212
|
+
|
|
213
|
+
def __post_init__(self):
|
|
214
|
+
self._parse_response()
|
|
215
|
+
|
|
216
|
+
def _parse_response(self):
|
|
217
|
+
"""Parse the raw response into structured parts."""
|
|
218
|
+
text = self.raw_text
|
|
219
|
+
|
|
220
|
+
# Extract code blocks
|
|
221
|
+
code_pattern = r"```(\w*)\n(.*?)```"
|
|
222
|
+
for match in re.finditer(code_pattern, text, re.DOTALL):
|
|
223
|
+
lang = match.group(1) or "text"
|
|
224
|
+
code = match.group(2).strip()
|
|
225
|
+
self.code_blocks.append(CodeBlock(code=code, language=lang))
|
|
226
|
+
|
|
227
|
+
# Remove code blocks from text for paragraph parsing
|
|
228
|
+
text_without_code = re.sub(code_pattern, "", text, flags=re.DOTALL)
|
|
229
|
+
|
|
230
|
+
# Extract bullet points
|
|
231
|
+
bullet_pattern = r"^[\s]*[-*•]\s+(.+)$"
|
|
232
|
+
for match in re.finditer(bullet_pattern, text_without_code, re.MULTILINE):
|
|
233
|
+
self.bullet_points.append(match.group(1).strip())
|
|
234
|
+
|
|
235
|
+
# Extract paragraphs (non-empty lines that aren't bullets)
|
|
236
|
+
text_without_bullets = re.sub(bullet_pattern, "", text_without_code, flags=re.MULTILINE)
|
|
237
|
+
for para in text_without_bullets.split("\n\n"):
|
|
238
|
+
para = para.strip()
|
|
239
|
+
if para and len(para) > 10:
|
|
240
|
+
self.paragraphs.append(para)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
# ============================================================================
|
|
244
|
+
# RENDERING FUNCTIONS
|
|
245
|
+
# ============================================================================
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def render_thinking_line(line: ThinkingLine, index: int) -> Text:
|
|
249
|
+
"""Render a single thinking line with icon and color."""
|
|
250
|
+
result = Text()
|
|
251
|
+
|
|
252
|
+
# Alternating subtle background effect via color
|
|
253
|
+
color = COLORS["text_dim"] if index % 2 == 0 else COLORS["text_muted"]
|
|
254
|
+
|
|
255
|
+
# Icon
|
|
256
|
+
result.append(f" {line.icon} ", style=COLORS["cyan"])
|
|
257
|
+
|
|
258
|
+
# Text (truncate if too long)
|
|
259
|
+
text = line.text[:100] + "..." if len(line.text) > 100 else line.text
|
|
260
|
+
result.append(text, style=color)
|
|
261
|
+
|
|
262
|
+
return result
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def render_thinking_section(lines: List[ThinkingLine], collapsed: bool = False) -> Panel:
|
|
266
|
+
"""Render the collapsible thinking section."""
|
|
267
|
+
if not lines:
|
|
268
|
+
return Panel(
|
|
269
|
+
Text("No thinking logs", style=COLORS["text_dim"]),
|
|
270
|
+
title="[bold]💭 Thinking[/]",
|
|
271
|
+
border_style=COLORS["border"],
|
|
272
|
+
box=ROUNDED,
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
content = Text()
|
|
276
|
+
|
|
277
|
+
# Show summary when collapsed
|
|
278
|
+
if collapsed:
|
|
279
|
+
content.append(f" 📊 {len(lines)} operations performed\n", style=COLORS["text_muted"])
|
|
280
|
+
content.append(f" ⏱️ Click to expand details", style=COLORS["text_dim"])
|
|
281
|
+
else:
|
|
282
|
+
# Show all lines
|
|
283
|
+
for i, line in enumerate(lines[-50:]): # Last 50 lines
|
|
284
|
+
content.append_text(render_thinking_line(line, i))
|
|
285
|
+
content.append("\n")
|
|
286
|
+
|
|
287
|
+
return Panel(
|
|
288
|
+
content,
|
|
289
|
+
title=f"[bold {COLORS['cyan']}]💭 Thinking ({len(lines)} steps)[/]",
|
|
290
|
+
subtitle="[dim]Ctrl+T to toggle[/]",
|
|
291
|
+
border_style=COLORS["border"],
|
|
292
|
+
box=ROUNDED,
|
|
293
|
+
padding=(0, 1),
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def render_code_block(block: CodeBlock) -> Panel:
|
|
298
|
+
"""Render a beautiful code block with syntax highlighting."""
|
|
299
|
+
# Create syntax highlighted code
|
|
300
|
+
syntax = Syntax(
|
|
301
|
+
block.code,
|
|
302
|
+
block.language,
|
|
303
|
+
theme="monokai",
|
|
304
|
+
line_numbers=True,
|
|
305
|
+
word_wrap=True,
|
|
306
|
+
background_color="#000000",
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
# Language badge
|
|
310
|
+
lang_icons = {
|
|
311
|
+
"python": "🐍",
|
|
312
|
+
"javascript": "📜",
|
|
313
|
+
"typescript": "💠",
|
|
314
|
+
"rust": "🦀",
|
|
315
|
+
"go": "🐹",
|
|
316
|
+
"java": "☕",
|
|
317
|
+
"ruby": "💎",
|
|
318
|
+
"bash": "🖥️",
|
|
319
|
+
"shell": "🖥️",
|
|
320
|
+
"sql": "🗄️",
|
|
321
|
+
"html": "🌐",
|
|
322
|
+
"css": "🎨",
|
|
323
|
+
"json": "📋",
|
|
324
|
+
"yaml": "📝",
|
|
325
|
+
"markdown": "📄",
|
|
326
|
+
}
|
|
327
|
+
icon = lang_icons.get(block.language.lower(), "📄")
|
|
328
|
+
|
|
329
|
+
title = f"[bold {COLORS['green']}]{icon} {block.language.upper()}[/]"
|
|
330
|
+
if block.filename:
|
|
331
|
+
title += f" [dim]({block.filename})[/]"
|
|
332
|
+
|
|
333
|
+
return Panel(
|
|
334
|
+
syntax,
|
|
335
|
+
title=title,
|
|
336
|
+
border_style=COLORS["green"],
|
|
337
|
+
box=ROUNDED,
|
|
338
|
+
padding=(0, 1),
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def render_response_header(agent_name: str = "Agent") -> Text:
|
|
343
|
+
"""Render a beautiful gradient header for the response."""
|
|
344
|
+
result = Text()
|
|
345
|
+
|
|
346
|
+
# Decorative line with gradient
|
|
347
|
+
line_chars = "━" * 50
|
|
348
|
+
for i, char in enumerate(line_chars):
|
|
349
|
+
color_idx = i % len(RESPONSE_GRADIENT)
|
|
350
|
+
result.append(char, style=RESPONSE_GRADIENT[color_idx])
|
|
351
|
+
|
|
352
|
+
result.append("\n")
|
|
353
|
+
|
|
354
|
+
# Agent name with sparkles
|
|
355
|
+
result.append(" 🤖 ", style=COLORS["gold"])
|
|
356
|
+
result.append(agent_name.upper(), style=f"bold {COLORS['purple']}")
|
|
357
|
+
result.append(" Response ", style=f"bold {COLORS['magenta']}")
|
|
358
|
+
result.append("🤖\n", style=COLORS["gold"])
|
|
359
|
+
|
|
360
|
+
# Another gradient line
|
|
361
|
+
for i, char in enumerate(line_chars):
|
|
362
|
+
color_idx = (i + 3) % len(RESPONSE_GRADIENT)
|
|
363
|
+
result.append(char, style=RESPONSE_GRADIENT[color_idx])
|
|
364
|
+
|
|
365
|
+
return result
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def render_paragraph(text: str, width: int = 80) -> Text:
|
|
369
|
+
"""Render a paragraph with nice formatting and word wrapping."""
|
|
370
|
+
import textwrap
|
|
371
|
+
import shutil
|
|
372
|
+
|
|
373
|
+
# Get terminal width if not specified
|
|
374
|
+
try:
|
|
375
|
+
term_width = shutil.get_terminal_size().columns
|
|
376
|
+
wrap_width = min(term_width - 10, width)
|
|
377
|
+
except Exception:
|
|
378
|
+
wrap_width = width
|
|
379
|
+
|
|
380
|
+
result = Text()
|
|
381
|
+
# Strip markdown and wrap text
|
|
382
|
+
clean_text = strip_markdown(text)
|
|
383
|
+
wrapped = textwrap.fill(clean_text, width=wrap_width - 4)
|
|
384
|
+
for line in wrapped.split("\n"):
|
|
385
|
+
result.append(" ", style="")
|
|
386
|
+
result.append(line, style=COLORS["text"])
|
|
387
|
+
result.append("\n", style="")
|
|
388
|
+
return result
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
def render_bullet_point(text: str, index: int, width: int = 80) -> Text:
|
|
392
|
+
"""Render a bullet point with colorful bullet and word wrapping."""
|
|
393
|
+
import textwrap
|
|
394
|
+
import shutil
|
|
395
|
+
|
|
396
|
+
# Get terminal width if not specified
|
|
397
|
+
try:
|
|
398
|
+
term_width = shutil.get_terminal_size().columns
|
|
399
|
+
wrap_width = min(term_width - 10, width)
|
|
400
|
+
except Exception:
|
|
401
|
+
wrap_width = width
|
|
402
|
+
|
|
403
|
+
result = Text()
|
|
404
|
+
|
|
405
|
+
# Rotating colors for bullets
|
|
406
|
+
bullet_colors = [
|
|
407
|
+
COLORS["purple"],
|
|
408
|
+
COLORS["pink"],
|
|
409
|
+
COLORS["cyan"],
|
|
410
|
+
COLORS["green"],
|
|
411
|
+
COLORS["orange"],
|
|
412
|
+
]
|
|
413
|
+
color = bullet_colors[index % len(bullet_colors)]
|
|
414
|
+
|
|
415
|
+
# Strip markdown and wrap the text
|
|
416
|
+
clean_text = strip_markdown(text)
|
|
417
|
+
wrapped = textwrap.fill(clean_text, width=wrap_width - 6)
|
|
418
|
+
lines = wrapped.split("\n")
|
|
419
|
+
|
|
420
|
+
for i, line in enumerate(lines):
|
|
421
|
+
if i == 0:
|
|
422
|
+
result.append(" ", style="")
|
|
423
|
+
result.append("◆ ", style=f"bold {color}")
|
|
424
|
+
result.append(line, style=COLORS["text"])
|
|
425
|
+
else:
|
|
426
|
+
result.append(" ", style="") # Indent continuation
|
|
427
|
+
result.append(line, style=COLORS["text"])
|
|
428
|
+
result.append("\n", style="")
|
|
429
|
+
|
|
430
|
+
return result
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
def render_response_footer(duration: float = 0) -> Text:
|
|
434
|
+
"""Render a beautiful footer for the response."""
|
|
435
|
+
result = Text()
|
|
436
|
+
|
|
437
|
+
# Gradient line
|
|
438
|
+
line_chars = "─" * 50
|
|
439
|
+
for i, char in enumerate(line_chars):
|
|
440
|
+
color_idx = i % len(RAINBOW)
|
|
441
|
+
result.append(char, style=RAINBOW[color_idx])
|
|
442
|
+
|
|
443
|
+
result.append("\n")
|
|
444
|
+
|
|
445
|
+
# Stats
|
|
446
|
+
result.append(" 🎯 ", style=COLORS["green"])
|
|
447
|
+
result.append("Response complete", style=COLORS["text_muted"])
|
|
448
|
+
|
|
449
|
+
if duration > 0:
|
|
450
|
+
result.append(f" ⏱️ {duration:.1f}s", style=COLORS["text_dim"])
|
|
451
|
+
|
|
452
|
+
result.append(" 💜", style=COLORS["purple"])
|
|
453
|
+
|
|
454
|
+
return result
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
def render_full_response(
|
|
458
|
+
response: AgentResponse, agent_name: str = "Agent", duration: float = 0
|
|
459
|
+
) -> Group:
|
|
460
|
+
"""Render the complete beautiful response."""
|
|
461
|
+
elements = []
|
|
462
|
+
|
|
463
|
+
# Header
|
|
464
|
+
elements.append(render_response_header(agent_name))
|
|
465
|
+
elements.append(Text("\n"))
|
|
466
|
+
|
|
467
|
+
# Main content - paragraphs
|
|
468
|
+
for para in response.paragraphs:
|
|
469
|
+
elements.append(render_paragraph(para))
|
|
470
|
+
elements.append(Text("\n\n"))
|
|
471
|
+
|
|
472
|
+
# Bullet points
|
|
473
|
+
if response.bullet_points:
|
|
474
|
+
elements.append(Text("\n"))
|
|
475
|
+
for i, bullet in enumerate(response.bullet_points):
|
|
476
|
+
elements.append(render_bullet_point(bullet, i))
|
|
477
|
+
elements.append(Text("\n"))
|
|
478
|
+
|
|
479
|
+
# Code blocks
|
|
480
|
+
for block in response.code_blocks:
|
|
481
|
+
elements.append(Text("\n"))
|
|
482
|
+
elements.append(render_code_block(block))
|
|
483
|
+
elements.append(Text("\n"))
|
|
484
|
+
|
|
485
|
+
# Footer
|
|
486
|
+
elements.append(Text("\n"))
|
|
487
|
+
elements.append(render_response_footer(duration))
|
|
488
|
+
|
|
489
|
+
return Group(*elements)
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
# ============================================================================
|
|
493
|
+
# TEXTUAL WIDGETS
|
|
494
|
+
# ============================================================================
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
class ThinkingLog(Static):
|
|
498
|
+
"""Collapsible thinking/log display widget."""
|
|
499
|
+
|
|
500
|
+
DEFAULT_CSS = """
|
|
501
|
+
ThinkingLog {
|
|
502
|
+
height: auto;
|
|
503
|
+
max-height: 15;
|
|
504
|
+
background: #0d1117;
|
|
505
|
+
border: round #2a2a2a;
|
|
506
|
+
margin: 0 0 1 0;
|
|
507
|
+
overflow-y: auto;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
ThinkingLog.collapsed {
|
|
511
|
+
max-height: 3;
|
|
512
|
+
}
|
|
513
|
+
"""
|
|
514
|
+
|
|
515
|
+
collapsed = reactive(False)
|
|
516
|
+
|
|
517
|
+
def __init__(self, **kwargs):
|
|
518
|
+
super().__init__(**kwargs)
|
|
519
|
+
self._lines: List[ThinkingLine] = []
|
|
520
|
+
|
|
521
|
+
def add_line(self, text: str):
|
|
522
|
+
"""Add a thinking line."""
|
|
523
|
+
self._lines.append(ThinkingLine(text=text))
|
|
524
|
+
self.refresh()
|
|
525
|
+
|
|
526
|
+
def toggle(self):
|
|
527
|
+
"""Toggle collapsed state."""
|
|
528
|
+
self.collapsed = not self.collapsed
|
|
529
|
+
self.set_class(self.collapsed, "collapsed")
|
|
530
|
+
|
|
531
|
+
def clear(self):
|
|
532
|
+
"""Clear all lines."""
|
|
533
|
+
self._lines.clear()
|
|
534
|
+
self.refresh()
|
|
535
|
+
|
|
536
|
+
def render(self) -> Panel:
|
|
537
|
+
return render_thinking_section(self._lines, self.collapsed)
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
class AgentResponseDisplay(Static):
|
|
541
|
+
"""Beautiful agent response display widget."""
|
|
542
|
+
|
|
543
|
+
DEFAULT_CSS = """
|
|
544
|
+
AgentResponseDisplay {
|
|
545
|
+
height: auto;
|
|
546
|
+
background: #0f0a1a;
|
|
547
|
+
border: round #a855f7;
|
|
548
|
+
padding: 1;
|
|
549
|
+
margin: 1 0;
|
|
550
|
+
}
|
|
551
|
+
"""
|
|
552
|
+
|
|
553
|
+
def __init__(
|
|
554
|
+
self, response_text: str = "", agent_name: str = "Agent", duration: float = 0, **kwargs
|
|
555
|
+
):
|
|
556
|
+
super().__init__(**kwargs)
|
|
557
|
+
self.response_text = response_text
|
|
558
|
+
self.agent_name = agent_name
|
|
559
|
+
self.duration = duration
|
|
560
|
+
self._response: Optional[AgentResponse] = None
|
|
561
|
+
|
|
562
|
+
def set_response(self, text: str, agent_name: str = "Agent", duration: float = 0):
|
|
563
|
+
"""Set the response content."""
|
|
564
|
+
self.response_text = text
|
|
565
|
+
self.agent_name = agent_name
|
|
566
|
+
self.duration = duration
|
|
567
|
+
self._response = AgentResponse(raw_text=text)
|
|
568
|
+
self.refresh()
|
|
569
|
+
|
|
570
|
+
def render(self) -> Group:
|
|
571
|
+
if not self.response_text:
|
|
572
|
+
return Group(Text("Waiting for response...", style=COLORS["text_dim"]))
|
|
573
|
+
|
|
574
|
+
if self._response is None:
|
|
575
|
+
self._response = AgentResponse(raw_text=self.response_text)
|
|
576
|
+
|
|
577
|
+
return render_full_response(self._response, self.agent_name, self.duration)
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
class AgentOutputContainer(Container):
|
|
581
|
+
"""
|
|
582
|
+
Complete agent output container with thinking logs and response.
|
|
583
|
+
|
|
584
|
+
Features:
|
|
585
|
+
- Collapsible thinking section (Ctrl+T)
|
|
586
|
+
- Beautiful response display
|
|
587
|
+
- Clear visual separation
|
|
588
|
+
"""
|
|
589
|
+
|
|
590
|
+
DEFAULT_CSS = """
|
|
591
|
+
AgentOutputContainer {
|
|
592
|
+
height: auto;
|
|
593
|
+
padding: 0 1;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
AgentOutputContainer .output-header {
|
|
597
|
+
height: 1;
|
|
598
|
+
text-align: center;
|
|
599
|
+
margin-bottom: 1;
|
|
600
|
+
}
|
|
601
|
+
"""
|
|
602
|
+
|
|
603
|
+
BINDINGS = [
|
|
604
|
+
Binding("ctrl+t", "toggle_thinking", "Toggle Thinking", show=True),
|
|
605
|
+
]
|
|
606
|
+
|
|
607
|
+
show_thinking = reactive(True)
|
|
608
|
+
|
|
609
|
+
def __init__(self, agent_name: str = "Agent", **kwargs):
|
|
610
|
+
super().__init__(**kwargs)
|
|
611
|
+
self.agent_name = agent_name
|
|
612
|
+
self._thinking_log: Optional[ThinkingLog] = None
|
|
613
|
+
self._response_display: Optional[AgentResponseDisplay] = None
|
|
614
|
+
self._start_time: float = 0
|
|
615
|
+
|
|
616
|
+
def compose(self) -> ComposeResult:
|
|
617
|
+
yield Static(self._render_header(), classes="output-header")
|
|
618
|
+
self._thinking_log = ThinkingLog()
|
|
619
|
+
yield self._thinking_log
|
|
620
|
+
self._response_display = AgentResponseDisplay(agent_name=self.agent_name)
|
|
621
|
+
yield self._response_display
|
|
622
|
+
|
|
623
|
+
def _render_header(self) -> Text:
|
|
624
|
+
result = Text()
|
|
625
|
+
result.append("🤖 ", style=COLORS["purple"])
|
|
626
|
+
result.append(self.agent_name.upper(), style=f"bold {COLORS['purple']}")
|
|
627
|
+
result.append(" OUTPUT", style=f"bold {COLORS['magenta']}")
|
|
628
|
+
return result
|
|
629
|
+
|
|
630
|
+
def start_session(self):
|
|
631
|
+
"""Start a new output session."""
|
|
632
|
+
self._start_time = monotonic()
|
|
633
|
+
if self._thinking_log:
|
|
634
|
+
self._thinking_log.clear()
|
|
635
|
+
|
|
636
|
+
def add_thinking(self, text: str):
|
|
637
|
+
"""Add a thinking/log line."""
|
|
638
|
+
if self._thinking_log:
|
|
639
|
+
self._thinking_log.add_line(text)
|
|
640
|
+
|
|
641
|
+
def set_response(self, text: str):
|
|
642
|
+
"""Set the final response."""
|
|
643
|
+
duration = monotonic() - self._start_time if self._start_time else 0
|
|
644
|
+
if self._response_display:
|
|
645
|
+
self._response_display.set_response(text, self.agent_name, duration)
|
|
646
|
+
|
|
647
|
+
def action_toggle_thinking(self):
|
|
648
|
+
"""Toggle thinking section visibility."""
|
|
649
|
+
if self._thinking_log:
|
|
650
|
+
self._thinking_log.toggle()
|
|
651
|
+
self.show_thinking = not self._thinking_log.collapsed
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
# ============================================================================
|
|
655
|
+
# HELPER FUNCTIONS FOR APP INTEGRATION
|
|
656
|
+
# ============================================================================
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
def format_agent_output_for_log(
|
|
660
|
+
thinking_lines: List[str],
|
|
661
|
+
response_text: str,
|
|
662
|
+
agent_name: str = "Agent",
|
|
663
|
+
duration: float = 0,
|
|
664
|
+
show_thinking: bool = True,
|
|
665
|
+
) -> Group:
|
|
666
|
+
"""
|
|
667
|
+
Format complete agent output for display in conversation log.
|
|
668
|
+
|
|
669
|
+
Args:
|
|
670
|
+
thinking_lines: List of thinking/log lines
|
|
671
|
+
response_text: Final response text
|
|
672
|
+
agent_name: Name of the agent
|
|
673
|
+
duration: Time taken in seconds
|
|
674
|
+
show_thinking: Whether to show thinking section
|
|
675
|
+
|
|
676
|
+
Returns:
|
|
677
|
+
Rich Group ready for display
|
|
678
|
+
"""
|
|
679
|
+
elements = []
|
|
680
|
+
|
|
681
|
+
# Thinking section (collapsible)
|
|
682
|
+
if thinking_lines and show_thinking:
|
|
683
|
+
lines = [ThinkingLine(text=t) for t in thinking_lines]
|
|
684
|
+
elements.append(render_thinking_section(lines, collapsed=False))
|
|
685
|
+
elements.append(Text("\n"))
|
|
686
|
+
elif thinking_lines:
|
|
687
|
+
# Show collapsed summary
|
|
688
|
+
elements.append(
|
|
689
|
+
Text(f" 💭 {len(thinking_lines)} thinking steps (hidden)\n", style=COLORS["text_dim"])
|
|
690
|
+
)
|
|
691
|
+
|
|
692
|
+
# Response
|
|
693
|
+
if response_text:
|
|
694
|
+
response = AgentResponse(raw_text=response_text)
|
|
695
|
+
elements.append(render_full_response(response, agent_name, duration))
|
|
696
|
+
|
|
697
|
+
return Group(*elements)
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
def create_simple_response_panel(text: str, agent_name: str = "Agent") -> Panel:
|
|
701
|
+
"""Create a simple but beautiful response panel."""
|
|
702
|
+
content = Text()
|
|
703
|
+
|
|
704
|
+
# Add sparkle decoration
|
|
705
|
+
content.append("✨ ", style=COLORS["gold"])
|
|
706
|
+
|
|
707
|
+
# Format the text nicely
|
|
708
|
+
lines = text.strip().split("\n")
|
|
709
|
+
for i, line in enumerate(lines):
|
|
710
|
+
if line.strip():
|
|
711
|
+
content.append(line, style=COLORS["text"])
|
|
712
|
+
if i < len(lines) - 1:
|
|
713
|
+
content.append("\n")
|
|
714
|
+
|
|
715
|
+
return Panel(
|
|
716
|
+
content,
|
|
717
|
+
title=f"[bold {COLORS['purple']}]🤖 {agent_name}[/]",
|
|
718
|
+
border_style=COLORS["purple"],
|
|
719
|
+
box=ROUNDED,
|
|
720
|
+
padding=(1, 2),
|
|
721
|
+
)
|