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,356 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Code Context Viewer Widget.
|
|
3
|
+
|
|
4
|
+
A SuperQode-original widget for displaying code with contextual
|
|
5
|
+
highlighting, inline diffs, and related issue information.
|
|
6
|
+
|
|
7
|
+
Design: Smart code display that emphasizes the specific lines
|
|
8
|
+
where issues were found, with collapsible related findings.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from enum import Enum
|
|
13
|
+
from typing import Dict, List, Optional, Tuple
|
|
14
|
+
|
|
15
|
+
from rich.console import RenderableType
|
|
16
|
+
from rich.panel import Panel
|
|
17
|
+
from rich.syntax import Syntax
|
|
18
|
+
from rich.text import Text
|
|
19
|
+
from textual.reactive import reactive
|
|
20
|
+
from textual.widgets import Static
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class LineType(Enum):
|
|
24
|
+
"""Type of line in the context view."""
|
|
25
|
+
|
|
26
|
+
CONTEXT = "context" # Normal context line
|
|
27
|
+
ADDED = "added" # Added line (green)
|
|
28
|
+
REMOVED = "removed" # Removed line (red)
|
|
29
|
+
ISSUE = "issue" # Line with issue (highlighted)
|
|
30
|
+
FOCUS = "focus" # The main focus line
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass
|
|
34
|
+
class CodeLine:
|
|
35
|
+
"""A single line of code with metadata."""
|
|
36
|
+
|
|
37
|
+
number: int
|
|
38
|
+
content: str
|
|
39
|
+
line_type: LineType = LineType.CONTEXT
|
|
40
|
+
annotation: str = "" # Inline annotation (e.g., "← HERE")
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def prefix(self) -> str:
|
|
44
|
+
"""Get the line prefix based on type."""
|
|
45
|
+
if self.line_type == LineType.ADDED:
|
|
46
|
+
return "+"
|
|
47
|
+
elif self.line_type == LineType.REMOVED:
|
|
48
|
+
return "-"
|
|
49
|
+
else:
|
|
50
|
+
return " "
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclass
|
|
54
|
+
class RelatedFinding:
|
|
55
|
+
"""A related finding in other parts of the codebase."""
|
|
56
|
+
|
|
57
|
+
file_path: str
|
|
58
|
+
line_number: int
|
|
59
|
+
description: str
|
|
60
|
+
similarity: float = 0.0 # 0.0 to 1.0
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@dataclass
|
|
64
|
+
class CodeContext:
|
|
65
|
+
"""Context for displaying code with an issue."""
|
|
66
|
+
|
|
67
|
+
file_path: str
|
|
68
|
+
language: str
|
|
69
|
+
issue_title: str
|
|
70
|
+
issue_description: str = ""
|
|
71
|
+
lines: List[CodeLine] = field(default_factory=list)
|
|
72
|
+
focus_line: Optional[int] = None
|
|
73
|
+
related_findings: List[RelatedFinding] = field(default_factory=list)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# Language detection from file extension
|
|
77
|
+
LANGUAGE_MAP = {
|
|
78
|
+
".py": "python",
|
|
79
|
+
".js": "javascript",
|
|
80
|
+
".ts": "typescript",
|
|
81
|
+
".tsx": "typescript",
|
|
82
|
+
".jsx": "javascript",
|
|
83
|
+
".java": "java",
|
|
84
|
+
".go": "go",
|
|
85
|
+
".rs": "rust",
|
|
86
|
+
".rb": "ruby",
|
|
87
|
+
".php": "php",
|
|
88
|
+
".c": "c",
|
|
89
|
+
".cpp": "cpp",
|
|
90
|
+
".h": "c",
|
|
91
|
+
".hpp": "cpp",
|
|
92
|
+
".cs": "csharp",
|
|
93
|
+
".swift": "swift",
|
|
94
|
+
".kt": "kotlin",
|
|
95
|
+
".scala": "scala",
|
|
96
|
+
".sh": "bash",
|
|
97
|
+
".bash": "bash",
|
|
98
|
+
".zsh": "zsh",
|
|
99
|
+
".yaml": "yaml",
|
|
100
|
+
".yml": "yaml",
|
|
101
|
+
".json": "json",
|
|
102
|
+
".xml": "xml",
|
|
103
|
+
".html": "html",
|
|
104
|
+
".css": "css",
|
|
105
|
+
".scss": "scss",
|
|
106
|
+
".sql": "sql",
|
|
107
|
+
".md": "markdown",
|
|
108
|
+
".toml": "toml",
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def detect_language(file_path: str) -> str:
|
|
113
|
+
"""Detect language from file path."""
|
|
114
|
+
for ext, lang in LANGUAGE_MAP.items():
|
|
115
|
+
if file_path.endswith(ext):
|
|
116
|
+
return lang
|
|
117
|
+
return "text"
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class CodeContextViewer(Static):
|
|
121
|
+
"""Code Context Viewer Widget.
|
|
122
|
+
|
|
123
|
+
Displays code with contextual highlighting around issues,
|
|
124
|
+
showing before/after diffs and related findings.
|
|
125
|
+
|
|
126
|
+
Usage:
|
|
127
|
+
viewer = CodeContextViewer()
|
|
128
|
+
viewer.set_context(CodeContext(
|
|
129
|
+
file_path="src/api/user.py",
|
|
130
|
+
language="python",
|
|
131
|
+
issue_title="Unhandled null reference",
|
|
132
|
+
lines=[
|
|
133
|
+
CodeLine(23, "def get_user(user_id: str):"),
|
|
134
|
+
CodeLine(24, " user = db.find(user_id)"),
|
|
135
|
+
CodeLine(25, " return user.name", LineType.ISSUE, "← HERE"),
|
|
136
|
+
],
|
|
137
|
+
focus_line=25,
|
|
138
|
+
))
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
DEFAULT_CSS = """
|
|
142
|
+
CodeContextViewer {
|
|
143
|
+
height: auto;
|
|
144
|
+
border: solid #3f3f46;
|
|
145
|
+
padding: 0 1;
|
|
146
|
+
margin: 0 0 1 0;
|
|
147
|
+
}
|
|
148
|
+
"""
|
|
149
|
+
|
|
150
|
+
# Reactive state
|
|
151
|
+
show_related: reactive[bool] = reactive(False)
|
|
152
|
+
|
|
153
|
+
def __init__(
|
|
154
|
+
self,
|
|
155
|
+
title: str = "Code Context",
|
|
156
|
+
show_line_numbers: bool = True,
|
|
157
|
+
context_lines: int = 3,
|
|
158
|
+
**kwargs,
|
|
159
|
+
):
|
|
160
|
+
super().__init__(**kwargs)
|
|
161
|
+
self.title = title
|
|
162
|
+
self.show_line_numbers = show_line_numbers
|
|
163
|
+
self.context_lines = context_lines
|
|
164
|
+
self._context: Optional[CodeContext] = None
|
|
165
|
+
|
|
166
|
+
def set_context(self, context: CodeContext) -> None:
|
|
167
|
+
"""Set the code context to display."""
|
|
168
|
+
self._context = context
|
|
169
|
+
self.refresh()
|
|
170
|
+
|
|
171
|
+
def set_from_diff(
|
|
172
|
+
self,
|
|
173
|
+
file_path: str,
|
|
174
|
+
issue_title: str,
|
|
175
|
+
before: str,
|
|
176
|
+
after: str,
|
|
177
|
+
focus_line: Optional[int] = None,
|
|
178
|
+
) -> None:
|
|
179
|
+
"""Set context from a before/after diff."""
|
|
180
|
+
# Simple diff - just show the change
|
|
181
|
+
before_lines = before.splitlines() if before else []
|
|
182
|
+
after_lines = after.splitlines() if after else []
|
|
183
|
+
|
|
184
|
+
lines = []
|
|
185
|
+
language = detect_language(file_path)
|
|
186
|
+
|
|
187
|
+
# Build a simple unified view
|
|
188
|
+
max_lines = max(len(before_lines), len(after_lines))
|
|
189
|
+
|
|
190
|
+
for i in range(max_lines):
|
|
191
|
+
line_num = i + 1
|
|
192
|
+
before_line = before_lines[i] if i < len(before_lines) else None
|
|
193
|
+
after_line = after_lines[i] if i < len(after_lines) else None
|
|
194
|
+
|
|
195
|
+
if before_line == after_line:
|
|
196
|
+
# Unchanged
|
|
197
|
+
lines.append(CodeLine(line_num, after_line or "", LineType.CONTEXT))
|
|
198
|
+
elif before_line is None:
|
|
199
|
+
# Added
|
|
200
|
+
lines.append(CodeLine(line_num, after_line, LineType.ADDED))
|
|
201
|
+
elif after_line is None:
|
|
202
|
+
# Removed
|
|
203
|
+
lines.append(CodeLine(line_num, before_line, LineType.REMOVED))
|
|
204
|
+
else:
|
|
205
|
+
# Changed - show both
|
|
206
|
+
lines.append(CodeLine(line_num, before_line, LineType.REMOVED))
|
|
207
|
+
lines.append(CodeLine(line_num, after_line, LineType.ADDED))
|
|
208
|
+
|
|
209
|
+
self._context = CodeContext(
|
|
210
|
+
file_path=file_path,
|
|
211
|
+
language=language,
|
|
212
|
+
issue_title=issue_title,
|
|
213
|
+
lines=lines,
|
|
214
|
+
focus_line=focus_line,
|
|
215
|
+
)
|
|
216
|
+
self.refresh()
|
|
217
|
+
|
|
218
|
+
def add_related_finding(self, finding: RelatedFinding) -> None:
|
|
219
|
+
"""Add a related finding."""
|
|
220
|
+
if self._context:
|
|
221
|
+
self._context.related_findings.append(finding)
|
|
222
|
+
self.refresh()
|
|
223
|
+
|
|
224
|
+
def toggle_related(self) -> None:
|
|
225
|
+
"""Toggle visibility of related findings."""
|
|
226
|
+
self.show_related = not self.show_related
|
|
227
|
+
|
|
228
|
+
def clear(self) -> None:
|
|
229
|
+
"""Clear the context."""
|
|
230
|
+
self._context = None
|
|
231
|
+
self.refresh()
|
|
232
|
+
|
|
233
|
+
def _get_line_style(self, line: CodeLine) -> Tuple[str, str]:
|
|
234
|
+
"""Get style and prefix for a line type."""
|
|
235
|
+
if line.line_type == LineType.ADDED:
|
|
236
|
+
return "#22c55e", "+"
|
|
237
|
+
elif line.line_type == LineType.REMOVED:
|
|
238
|
+
return "#ef4444", "-"
|
|
239
|
+
elif line.line_type == LineType.ISSUE:
|
|
240
|
+
return "#eab308", "!"
|
|
241
|
+
elif line.line_type == LineType.FOCUS:
|
|
242
|
+
return "#3b82f6", "▶"
|
|
243
|
+
else:
|
|
244
|
+
return "#a1a1aa", " "
|
|
245
|
+
|
|
246
|
+
def _render_line(self, line: CodeLine, max_num_width: int) -> Text:
|
|
247
|
+
"""Render a single code line."""
|
|
248
|
+
color, prefix = self._get_line_style(line)
|
|
249
|
+
|
|
250
|
+
result = Text()
|
|
251
|
+
|
|
252
|
+
# Line number (right-aligned)
|
|
253
|
+
if self.show_line_numbers:
|
|
254
|
+
num_str = str(line.number).rjust(max_num_width)
|
|
255
|
+
result.append(f"{num_str} │", style="#6b7280")
|
|
256
|
+
|
|
257
|
+
# Prefix (+/-/!)
|
|
258
|
+
result.append(prefix, style=f"bold {color}")
|
|
259
|
+
|
|
260
|
+
# Content with background for changes
|
|
261
|
+
if line.line_type == LineType.ADDED:
|
|
262
|
+
result.append(line.content, style=f"{color} on #1a2e1a")
|
|
263
|
+
elif line.line_type == LineType.REMOVED:
|
|
264
|
+
result.append(line.content, style=f"{color} on #2e1a1a")
|
|
265
|
+
elif line.line_type == LineType.ISSUE:
|
|
266
|
+
result.append(line.content, style=f"bold {color}")
|
|
267
|
+
else:
|
|
268
|
+
result.append(line.content, style="#e2e8f0")
|
|
269
|
+
|
|
270
|
+
# Annotation
|
|
271
|
+
if line.annotation:
|
|
272
|
+
padding = max(0, 50 - len(line.content))
|
|
273
|
+
result.append(" " * padding)
|
|
274
|
+
result.append(f" # {line.annotation}", style=f"italic {color}")
|
|
275
|
+
|
|
276
|
+
return result
|
|
277
|
+
|
|
278
|
+
def render(self) -> RenderableType:
|
|
279
|
+
"""Render the code context."""
|
|
280
|
+
content = Text()
|
|
281
|
+
|
|
282
|
+
if not self._context:
|
|
283
|
+
content.append("\n No code context set\n", style="#6b7280")
|
|
284
|
+
return Panel(
|
|
285
|
+
content,
|
|
286
|
+
title=f"[bold #3b82f6]{self.title}[/]",
|
|
287
|
+
border_style="#3f3f46",
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
ctx = self._context
|
|
291
|
+
|
|
292
|
+
# Issue header
|
|
293
|
+
content.append(" Issue: ", style="#6b7280")
|
|
294
|
+
content.append(ctx.issue_title, style="bold #eab308")
|
|
295
|
+
content.append("\n\n")
|
|
296
|
+
|
|
297
|
+
# Code lines
|
|
298
|
+
if ctx.lines:
|
|
299
|
+
max_num_width = len(str(max(line.number for line in ctx.lines)))
|
|
300
|
+
|
|
301
|
+
for line in ctx.lines:
|
|
302
|
+
content.append(self._render_line(line, max_num_width))
|
|
303
|
+
content.append("\n")
|
|
304
|
+
else:
|
|
305
|
+
content.append(" No code to display\n", style="#6b7280")
|
|
306
|
+
|
|
307
|
+
# Related findings section
|
|
308
|
+
if ctx.related_findings:
|
|
309
|
+
content.append("\n")
|
|
310
|
+
|
|
311
|
+
if self.show_related:
|
|
312
|
+
content.append(" ▼ Related: ", style="#6b7280")
|
|
313
|
+
content.append(
|
|
314
|
+
f"{len(ctx.related_findings)} similar patterns found\n", style="#a1a1aa"
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
for finding in ctx.related_findings[:5]: # Limit to 5
|
|
318
|
+
content.append(" • ", style="#3f3f46")
|
|
319
|
+
content.append(f"{finding.file_path}", style="#3b82f6")
|
|
320
|
+
content.append(f":{finding.line_number}", style="#6b7280")
|
|
321
|
+
content.append(f" - {finding.description}\n", style="#a1a1aa")
|
|
322
|
+
else:
|
|
323
|
+
content.append(" ▶ Related: ", style="#6b7280")
|
|
324
|
+
content.append(
|
|
325
|
+
f"{len(ctx.related_findings)} similar patterns found", style="#a1a1aa"
|
|
326
|
+
)
|
|
327
|
+
content.append(" (expand to view)\n", style="#52525b")
|
|
328
|
+
|
|
329
|
+
# Build title with file path
|
|
330
|
+
title_text = f"{self.title}: {ctx.file_path}"
|
|
331
|
+
if len(title_text) > 50:
|
|
332
|
+
title_text = f"{self.title}: ...{ctx.file_path[-40:]}"
|
|
333
|
+
|
|
334
|
+
return Panel(
|
|
335
|
+
content,
|
|
336
|
+
title=f"[bold #3b82f6]{title_text}[/]",
|
|
337
|
+
border_style="#3f3f46",
|
|
338
|
+
padding=(0, 0),
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
class CompactCodeContext(CodeContextViewer):
|
|
343
|
+
"""Compact version of CodeContextViewer for smaller displays."""
|
|
344
|
+
|
|
345
|
+
DEFAULT_CSS = """
|
|
346
|
+
CompactCodeContext {
|
|
347
|
+
height: auto;
|
|
348
|
+
max-height: 12;
|
|
349
|
+
border: solid #3f3f46;
|
|
350
|
+
padding: 0 1;
|
|
351
|
+
}
|
|
352
|
+
"""
|
|
353
|
+
|
|
354
|
+
def __init__(self, **kwargs):
|
|
355
|
+
kwargs.setdefault("context_lines", 2)
|
|
356
|
+
super().__init__(**kwargs)
|