raggiecode 0.2.1__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.
- Agent/__init__.py +0 -0
- Agent/agent.py +891 -0
- Agent/chat_history_db.py +1500 -0
- Agent/command.py +49 -0
- Agent/config.py +46 -0
- Agent/effort_levels.py +33 -0
- Agent/git_manager.py +727 -0
- Agent/tools.py +35 -0
- Commands/__init__.py +18 -0
- Commands/effort.py +42 -0
- Commands/global_todo.py +23 -0
- Commands/help.py +22 -0
- Commands/reasoning.py +24 -0
- Commands/redo.py +11 -0
- Commands/reindex.py +27 -0
- Commands/shell.py +28 -0
- Commands/stream.py +24 -0
- Commands/undo.py +13 -0
- Commands/unlimited_effort.py +8 -0
- Commands/window_size.py +29 -0
- RAG/__init__.py +0 -0
- RAG/document.py +119 -0
- RAG/find.py +408 -0
- RAG/graph.py +231 -0
- Tools/GetFileCodeStructure.py +43 -0
- Tools/GetSymbolSourceCode.py +27 -0
- Tools/__init__.py +39 -0
- Tools/ask_user.py +102 -0
- Tools/dispatch_subagent.py +215 -0
- Tools/document.py +35 -0
- Tools/edit_symbol.py +250 -0
- Tools/fuzzy_search.py +119 -0
- Tools/list_dir.py +51 -0
- Tools/read.py +49 -0
- Tools/read_image.py +75 -0
- Tools/remove.py +75 -0
- Tools/replace.py +305 -0
- Tools/search.py +41 -0
- Tools/shell.py +149 -0
- Tools/shell_kill.py +87 -0
- Tools/temp_background_service.py +113 -0
- Tools/todo_list.py +481 -0
- Tools/utils.py +116 -0
- Tools/view_changes.py +179 -0
- Tools/walk_call_tree.py +30 -0
- Tools/web_fetch.py +175 -0
- Tools/web_search.py +69 -0
- Tools/write.py +48 -0
- cli.py +111 -0
- config/__init__.py +0 -0
- config/coder_system_prompt.md +119 -0
- config/roles.json +43 -0
- config/tools.json +709 -0
- indexing/__init__.py +0 -0
- indexing/cli.py +128 -0
- indexing/code_index_sdk.py +832 -0
- indexing/code_indexer.py +1763 -0
- indexing/db_schema.py +396 -0
- indexing/export_to_json.py +346 -0
- indexing/extractors.py +189 -0
- indexing/file_utils.py +97 -0
- indexing/frontend/__init__.py +0 -0
- indexing/frontend/css_extractor.py +195 -0
- indexing/frontend/css_parser.py +387 -0
- indexing/frontend/css_selector_utils.py +226 -0
- indexing/frontend/edit_safety.py +573 -0
- indexing/frontend/graph.py +838 -0
- indexing/frontend/html_extractor.py +496 -0
- indexing/frontend/html_parser.py +314 -0
- indexing/frontend/jsx_extractor.py +1204 -0
- indexing/frontend/location_lookup.py +247 -0
- indexing/frontend/resolver.py +485 -0
- indexing/frontend/runtime_resolver.py +862 -0
- indexing/frontend/semantic_output.py +705 -0
- indexing/frontend/source_location.py +69 -0
- indexing/frontend_config.py +72 -0
- indexing/frontend_models.py +347 -0
- indexing/language_config.py +360 -0
- indexing/models.py +284 -0
- indexing/node_utils.py +1112 -0
- indexing/parse_worker.py +1082 -0
- indexing/queries.py +1542 -0
- indexing/sdk_examples.py +426 -0
- interactive.py +248 -0
- raggie.py +673 -0
- raggiecode-0.2.1.dist-info/METADATA +944 -0
- raggiecode-0.2.1.dist-info/RECORD +93 -0
- raggiecode-0.2.1.dist-info/WHEEL +5 -0
- raggiecode-0.2.1.dist-info/entry_points.txt +2 -0
- raggiecode-0.2.1.dist-info/top_level.txt +10 -0
- skills/__init__.py +3 -0
- skills/manager.py +114 -0
- skills/tool.py +121 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Source location utilities for frontend extractors.
|
|
4
|
+
Converts tree-sitter byte offsets to 1-indexed line/column positions.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Dict, Optional
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SourceLocation:
|
|
11
|
+
"""Represents a source range with 1-indexed line/column positions."""
|
|
12
|
+
|
|
13
|
+
__slots__ = ("start_line", "start_column", "end_line", "end_column", "start_byte", "end_byte")
|
|
14
|
+
|
|
15
|
+
def __init__(self, start_line, start_column, end_line, end_column, start_byte=None, end_byte=None):
|
|
16
|
+
self.start_line = start_line
|
|
17
|
+
self.start_column = start_column
|
|
18
|
+
self.end_line = end_line
|
|
19
|
+
self.end_column = end_column
|
|
20
|
+
self.start_byte = start_byte
|
|
21
|
+
self.end_byte = end_byte
|
|
22
|
+
|
|
23
|
+
def to_dict(self) -> Dict:
|
|
24
|
+
d = {
|
|
25
|
+
"start_line": self.start_line,
|
|
26
|
+
"start_column": self.start_column,
|
|
27
|
+
"end_line": self.end_line,
|
|
28
|
+
"end_column": self.end_column,
|
|
29
|
+
}
|
|
30
|
+
if self.start_byte is not None:
|
|
31
|
+
d["start_byte"] = self.start_byte
|
|
32
|
+
if self.end_byte is not None:
|
|
33
|
+
d["end_byte"] = self.end_byte
|
|
34
|
+
return d
|
|
35
|
+
|
|
36
|
+
def __repr__(self):
|
|
37
|
+
return f"SourceLocation({self.start_line}:{self.start_column}-{self.end_line}:{self.end_column})"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def node_to_location(node) -> SourceLocation:
|
|
41
|
+
"""Convert a tree-sitter node to a SourceLocation with 1-indexed lines."""
|
|
42
|
+
start_line, start_col = node.start_point
|
|
43
|
+
end_line, end_col = node.end_point
|
|
44
|
+
return SourceLocation(
|
|
45
|
+
start_line=start_line + 1,
|
|
46
|
+
start_column=start_col,
|
|
47
|
+
end_line=end_line + 1,
|
|
48
|
+
end_column=end_col,
|
|
49
|
+
start_byte=node.start_byte,
|
|
50
|
+
end_byte=node.end_byte,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def byte_to_line_col(byte_offset: int, source_bytes: bytes) -> tuple:
|
|
55
|
+
"""Convert a byte offset to (line, column) with 1-indexed line."""
|
|
56
|
+
line = 1
|
|
57
|
+
col = 0
|
|
58
|
+
for i in range(min(byte_offset, len(source_bytes))):
|
|
59
|
+
if source_bytes[i] == 0x0A: # newline
|
|
60
|
+
line += 1
|
|
61
|
+
col = 0
|
|
62
|
+
else:
|
|
63
|
+
col += 1
|
|
64
|
+
return (line, col)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def extract_range(source_bytes: bytes, start_byte: int, end_byte: int) -> str:
|
|
68
|
+
"""Extract exact source text from byte range, handling UTF-8."""
|
|
69
|
+
return source_bytes[start_byte:end_byte].decode("utf-8", errors="replace")
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Frontend indexing configuration.
|
|
4
|
+
Loads defaults and optional overrides from .raggie/frontend_config.json.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import List, Optional
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# Default configuration
|
|
13
|
+
DEFAULT_CONFIG = {
|
|
14
|
+
"enabled_languages": ["html", "css", "javascript", "tsx"],
|
|
15
|
+
"generated_dir_exclusions": ["dist", "build", "node_modules", ".next", ".nuxt", "out"],
|
|
16
|
+
"parse_inline_scripts": True,
|
|
17
|
+
"parse_inline_styles": True,
|
|
18
|
+
"include_text_nodes": False,
|
|
19
|
+
"css_module_resolution": True,
|
|
20
|
+
"framework_detection_overrides": {},
|
|
21
|
+
"generated_css_threshold": 100_000,
|
|
22
|
+
"max_frontend_file_size": 500_000,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class FrontendConfig:
|
|
27
|
+
"""Configuration for frontend indexing behavior."""
|
|
28
|
+
|
|
29
|
+
def __init__(self, config_dict: Optional[dict] = None):
|
|
30
|
+
merged = dict(DEFAULT_CONFIG)
|
|
31
|
+
if config_dict:
|
|
32
|
+
merged.update(config_dict)
|
|
33
|
+
self.enabled_languages: List[str] = merged["enabled_languages"]
|
|
34
|
+
self.generated_dir_exclusions: List[str] = merged["generated_dir_exclusions"]
|
|
35
|
+
self.parse_inline_scripts: bool = merged["parse_inline_scripts"]
|
|
36
|
+
self.parse_inline_styles: bool = merged["parse_inline_styles"]
|
|
37
|
+
self.include_text_nodes: bool = merged["include_text_nodes"]
|
|
38
|
+
self.css_module_resolution: bool = merged["css_module_resolution"]
|
|
39
|
+
self.framework_detection_overrides: dict = merged["framework_detection_overrides"]
|
|
40
|
+
self.generated_css_threshold: int = merged["generated_css_threshold"]
|
|
41
|
+
self.max_frontend_file_size: int = merged["max_frontend_file_size"]
|
|
42
|
+
|
|
43
|
+
def is_language_enabled(self, language: str) -> bool:
|
|
44
|
+
return language in self.enabled_languages
|
|
45
|
+
|
|
46
|
+
def is_dir_excluded(self, dir_name: str) -> bool:
|
|
47
|
+
return dir_name in self.generated_dir_exclusions
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def load_frontend_config(root_dir) -> FrontendConfig:
|
|
51
|
+
"""Load frontend config from .raggie/frontend_config.json, or fall back to defaults.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
root_dir: Project root directory path.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
FrontendConfig instance.
|
|
58
|
+
"""
|
|
59
|
+
root_path = Path(root_dir)
|
|
60
|
+
config_path = root_path / ".raggie" / "frontend_config.json"
|
|
61
|
+
|
|
62
|
+
if config_path.exists():
|
|
63
|
+
try:
|
|
64
|
+
with open(config_path, "r", encoding="utf-8") as f:
|
|
65
|
+
config_dict = json.load(f)
|
|
66
|
+
return FrontendConfig(config_dict)
|
|
67
|
+
except json.JSONDecodeError as e:
|
|
68
|
+
print(f"Warning: Invalid JSON in {config_path}: {e}")
|
|
69
|
+
print("Falling back to default frontend config.")
|
|
70
|
+
return FrontendConfig()
|
|
71
|
+
|
|
72
|
+
return FrontendConfig()
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Data models for frontend semantic entities.
|
|
4
|
+
Dataclasses representing frontend entities stored in the SQLite index.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import sqlite3
|
|
9
|
+
from typing import List, Dict, Optional
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
|
|
12
|
+
from .models import Location
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class FrontendComponent:
|
|
17
|
+
"""Recognized UI component (React function/arrow/class/forwardRef/memo)."""
|
|
18
|
+
id: int
|
|
19
|
+
file_id: int
|
|
20
|
+
name: str
|
|
21
|
+
framework: str = "react"
|
|
22
|
+
source_range: Optional[Location] = None
|
|
23
|
+
is_exported: bool = False
|
|
24
|
+
impl_function_id: Optional[int] = None
|
|
25
|
+
impl_class_id: Optional[int] = None
|
|
26
|
+
|
|
27
|
+
@classmethod
|
|
28
|
+
def from_row(cls, row: sqlite3.Row) -> "FrontendComponent":
|
|
29
|
+
sr = None
|
|
30
|
+
if row["source_range"]:
|
|
31
|
+
sr = Location.from_dict(json.loads(row["source_range"]))
|
|
32
|
+
return cls(
|
|
33
|
+
id=row["id"],
|
|
34
|
+
file_id=row["file_id"],
|
|
35
|
+
name=row["name"],
|
|
36
|
+
framework=row["framework"],
|
|
37
|
+
source_range=sr,
|
|
38
|
+
is_exported=bool(row["is_exported"]),
|
|
39
|
+
impl_function_id=row["impl_function_id"],
|
|
40
|
+
impl_class_id=row["impl_class_id"],
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass
|
|
45
|
+
class MarkupElement:
|
|
46
|
+
"""Element in markup tree (HTML, JSX, fragment, template, expression, text)."""
|
|
47
|
+
id: int
|
|
48
|
+
file_id: int
|
|
49
|
+
tag_name: str
|
|
50
|
+
element_type: str
|
|
51
|
+
component_id: Optional[int] = None
|
|
52
|
+
parent_element_id: Optional[int] = None
|
|
53
|
+
source_range: Optional[Location] = None
|
|
54
|
+
element_id_attr: Optional[str] = None
|
|
55
|
+
static_classes: Optional[List[str]] = None
|
|
56
|
+
attributes: Optional[Dict] = None
|
|
57
|
+
is_conditional: bool = False
|
|
58
|
+
is_repeated: bool = False
|
|
59
|
+
conditional_expr: Optional[str] = None
|
|
60
|
+
repeated_expr: Optional[str] = None
|
|
61
|
+
|
|
62
|
+
@classmethod
|
|
63
|
+
def from_row(cls, row: sqlite3.Row) -> "MarkupElement":
|
|
64
|
+
sr = None
|
|
65
|
+
if row["source_range"]:
|
|
66
|
+
sr = Location.from_dict(json.loads(row["source_range"]))
|
|
67
|
+
classes = None
|
|
68
|
+
if row["static_classes"]:
|
|
69
|
+
classes = json.loads(row["static_classes"])
|
|
70
|
+
attrs = None
|
|
71
|
+
if row["attributes"]:
|
|
72
|
+
attrs = json.loads(row["attributes"])
|
|
73
|
+
return cls(
|
|
74
|
+
id=row["id"],
|
|
75
|
+
file_id=row["file_id"],
|
|
76
|
+
tag_name=row["tag_name"],
|
|
77
|
+
element_type=row["element_type"],
|
|
78
|
+
component_id=row["component_id"],
|
|
79
|
+
parent_element_id=row["parent_element_id"],
|
|
80
|
+
source_range=sr,
|
|
81
|
+
element_id_attr=row["element_id_attr"],
|
|
82
|
+
static_classes=classes,
|
|
83
|
+
attributes=attrs,
|
|
84
|
+
is_conditional=bool(row["is_conditional"]),
|
|
85
|
+
is_repeated=bool(row["is_repeated"]),
|
|
86
|
+
conditional_expr=row["conditional_expr"],
|
|
87
|
+
repeated_expr=row["repeated_expr"],
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@dataclass
|
|
92
|
+
class StyleSelector:
|
|
93
|
+
"""CSS selector definition."""
|
|
94
|
+
id: int
|
|
95
|
+
file_id: int
|
|
96
|
+
selector_text: str
|
|
97
|
+
selector_type: str
|
|
98
|
+
normalized_selector: Optional[str] = None
|
|
99
|
+
source_range: Optional[Location] = None
|
|
100
|
+
component_id: Optional[int] = None
|
|
101
|
+
is_scoped: bool = False
|
|
102
|
+
|
|
103
|
+
@classmethod
|
|
104
|
+
def from_row(cls, row: sqlite3.Row) -> "StyleSelector":
|
|
105
|
+
sr = None
|
|
106
|
+
if row["source_range"]:
|
|
107
|
+
sr = Location.from_dict(json.loads(row["source_range"]))
|
|
108
|
+
return cls(
|
|
109
|
+
id=row["id"],
|
|
110
|
+
file_id=row["file_id"],
|
|
111
|
+
selector_text=row["selector_text"],
|
|
112
|
+
selector_type=row["selector_type"],
|
|
113
|
+
normalized_selector=row["normalized_selector"],
|
|
114
|
+
source_range=sr,
|
|
115
|
+
component_id=row["component_id"],
|
|
116
|
+
is_scoped=bool(row["is_scoped"]),
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@dataclass
|
|
121
|
+
class StyleCustomProperty:
|
|
122
|
+
"""CSS custom property definition (--name: value)."""
|
|
123
|
+
id: int
|
|
124
|
+
file_id: int
|
|
125
|
+
name: str
|
|
126
|
+
value: Optional[str] = None
|
|
127
|
+
source_range: Optional[Location] = None
|
|
128
|
+
scope_selector: Optional[str] = None
|
|
129
|
+
|
|
130
|
+
@classmethod
|
|
131
|
+
def from_row(cls, row: sqlite3.Row) -> "StyleCustomProperty":
|
|
132
|
+
sr = None
|
|
133
|
+
if row["source_range"]:
|
|
134
|
+
sr = Location.from_dict(json.loads(row["source_range"]))
|
|
135
|
+
return cls(
|
|
136
|
+
id=row["id"],
|
|
137
|
+
file_id=row["file_id"],
|
|
138
|
+
name=row["name"],
|
|
139
|
+
value=row["value"],
|
|
140
|
+
source_range=sr,
|
|
141
|
+
scope_selector=row["scope_selector"],
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@dataclass
|
|
146
|
+
class StyleCustomPropertyUsage:
|
|
147
|
+
"""CSS custom property usage (var(--name))."""
|
|
148
|
+
id: int
|
|
149
|
+
file_id: int
|
|
150
|
+
property_name: str
|
|
151
|
+
source_range: Optional[Location] = None
|
|
152
|
+
selector_id: Optional[int] = None
|
|
153
|
+
resolved_property_id: Optional[int] = None
|
|
154
|
+
|
|
155
|
+
@classmethod
|
|
156
|
+
def from_row(cls, row: sqlite3.Row) -> "StyleCustomPropertyUsage":
|
|
157
|
+
sr = None
|
|
158
|
+
if row["source_range"]:
|
|
159
|
+
sr = Location.from_dict(json.loads(row["source_range"]))
|
|
160
|
+
return cls(
|
|
161
|
+
id=row["id"],
|
|
162
|
+
file_id=row["file_id"],
|
|
163
|
+
property_name=row["property_name"],
|
|
164
|
+
source_range=sr,
|
|
165
|
+
selector_id=row["selector_id"],
|
|
166
|
+
resolved_property_id=row["resolved_property_id"],
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
@dataclass
|
|
171
|
+
class StyleKeyframe:
|
|
172
|
+
"""Keyframe definition (@keyframes name)."""
|
|
173
|
+
id: int
|
|
174
|
+
file_id: int
|
|
175
|
+
name: str
|
|
176
|
+
source_range: Optional[Location] = None
|
|
177
|
+
|
|
178
|
+
@classmethod
|
|
179
|
+
def from_row(cls, row: sqlite3.Row) -> "StyleKeyframe":
|
|
180
|
+
sr = None
|
|
181
|
+
if row["source_range"]:
|
|
182
|
+
sr = Location.from_dict(json.loads(row["source_range"]))
|
|
183
|
+
return cls(
|
|
184
|
+
id=row["id"],
|
|
185
|
+
file_id=row["file_id"],
|
|
186
|
+
name=row["name"],
|
|
187
|
+
source_range=sr,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
@dataclass
|
|
192
|
+
class StyleImport:
|
|
193
|
+
"""CSS/stylesheet import (@import, <link rel="stylesheet">, CSS module import)."""
|
|
194
|
+
id: int
|
|
195
|
+
file_id: int
|
|
196
|
+
import_path: str
|
|
197
|
+
is_external: bool = False
|
|
198
|
+
resolved_file_id: Optional[int] = None
|
|
199
|
+
source_range: Optional[Location] = None
|
|
200
|
+
|
|
201
|
+
@classmethod
|
|
202
|
+
def from_row(cls, row: sqlite3.Row) -> "StyleImport":
|
|
203
|
+
sr = None
|
|
204
|
+
if row["source_range"]:
|
|
205
|
+
sr = Location.from_dict(json.loads(row["source_range"]))
|
|
206
|
+
return cls(
|
|
207
|
+
id=row["id"],
|
|
208
|
+
file_id=row["file_id"],
|
|
209
|
+
import_path=row["import_path"],
|
|
210
|
+
is_external=bool(row["is_external"]),
|
|
211
|
+
resolved_file_id=row["resolved_file_id"],
|
|
212
|
+
source_range=sr,
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
@dataclass
|
|
217
|
+
class FrontendEvent:
|
|
218
|
+
"""Event handler binding (onclick, onClick, etc.)."""
|
|
219
|
+
id: int
|
|
220
|
+
file_id: int
|
|
221
|
+
event_name: str
|
|
222
|
+
handler_type: str
|
|
223
|
+
element_id: Optional[int] = None
|
|
224
|
+
handler_expression: Optional[str] = None
|
|
225
|
+
handler_symbol_id: Optional[int] = None
|
|
226
|
+
resolution_status: str = "unresolved"
|
|
227
|
+
source_range: Optional[Location] = None
|
|
228
|
+
|
|
229
|
+
@classmethod
|
|
230
|
+
def from_row(cls, row: sqlite3.Row) -> "FrontendEvent":
|
|
231
|
+
sr = None
|
|
232
|
+
if row["source_range"]:
|
|
233
|
+
sr = Location.from_dict(json.loads(row["source_range"]))
|
|
234
|
+
return cls(
|
|
235
|
+
id=row["id"],
|
|
236
|
+
file_id=row["file_id"],
|
|
237
|
+
event_name=row["event_name"],
|
|
238
|
+
handler_type=row["handler_type"],
|
|
239
|
+
element_id=row["element_id"],
|
|
240
|
+
handler_expression=row["handler_expression"],
|
|
241
|
+
handler_symbol_id=row["handler_symbol_id"],
|
|
242
|
+
resolution_status=row["resolution_status"],
|
|
243
|
+
source_range=sr,
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
@dataclass
|
|
248
|
+
class FrontendBinding:
|
|
249
|
+
"""Property/state binding (disabled={x}, className={cn(...)}, {title}, etc.)."""
|
|
250
|
+
id: int
|
|
251
|
+
file_id: int
|
|
252
|
+
binding_type: str
|
|
253
|
+
element_id: Optional[int] = None
|
|
254
|
+
binding_name: Optional[str] = None
|
|
255
|
+
binding_expression: Optional[str] = None
|
|
256
|
+
resolution_status: str = "unresolved"
|
|
257
|
+
source_range: Optional[Location] = None
|
|
258
|
+
|
|
259
|
+
@classmethod
|
|
260
|
+
def from_row(cls, row: sqlite3.Row) -> "FrontendBinding":
|
|
261
|
+
sr = None
|
|
262
|
+
if row["source_range"]:
|
|
263
|
+
sr = Location.from_dict(json.loads(row["source_range"]))
|
|
264
|
+
return cls(
|
|
265
|
+
id=row["id"],
|
|
266
|
+
file_id=row["file_id"],
|
|
267
|
+
binding_type=row["binding_type"],
|
|
268
|
+
element_id=row["element_id"],
|
|
269
|
+
binding_name=row["binding_name"],
|
|
270
|
+
binding_expression=row["binding_expression"],
|
|
271
|
+
resolution_status=row["resolution_status"],
|
|
272
|
+
source_range=sr,
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
@dataclass
|
|
277
|
+
class RenderRelationship:
|
|
278
|
+
"""Component render graph edge (parent renders child)."""
|
|
279
|
+
id: int
|
|
280
|
+
parent_component_id: int
|
|
281
|
+
render_type: str
|
|
282
|
+
child_component_id: Optional[int] = None
|
|
283
|
+
child_component_name: Optional[str] = None
|
|
284
|
+
child_element_id: Optional[int] = None
|
|
285
|
+
controlling_expr: Optional[str] = None
|
|
286
|
+
|
|
287
|
+
@classmethod
|
|
288
|
+
def from_row(cls, row: sqlite3.Row) -> "RenderRelationship":
|
|
289
|
+
return cls(
|
|
290
|
+
id=row["id"],
|
|
291
|
+
parent_component_id=row["parent_component_id"],
|
|
292
|
+
child_component_id=row["child_component_id"],
|
|
293
|
+
child_component_name=row["child_component_name"],
|
|
294
|
+
child_element_id=row["child_element_id"],
|
|
295
|
+
render_type=row["render_type"],
|
|
296
|
+
controlling_expr=row["controlling_expr"],
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
@dataclass
|
|
301
|
+
class FrontendDiagnostic:
|
|
302
|
+
"""Extraction diagnostic for a frontend file."""
|
|
303
|
+
id: int
|
|
304
|
+
file_id: int
|
|
305
|
+
diagnostic_type: str
|
|
306
|
+
severity: str
|
|
307
|
+
message: Optional[str] = None
|
|
308
|
+
source_range: Optional[Location] = None
|
|
309
|
+
|
|
310
|
+
@classmethod
|
|
311
|
+
def from_row(cls, row: sqlite3.Row) -> "FrontendDiagnostic":
|
|
312
|
+
sr = None
|
|
313
|
+
if row["source_range"]:
|
|
314
|
+
sr = Location.from_dict(json.loads(row["source_range"]))
|
|
315
|
+
return cls(
|
|
316
|
+
id=row["id"],
|
|
317
|
+
file_id=row["file_id"],
|
|
318
|
+
diagnostic_type=row["diagnostic_type"],
|
|
319
|
+
severity=row["severity"],
|
|
320
|
+
message=row["message"],
|
|
321
|
+
source_range=sr,
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
@dataclass
|
|
326
|
+
class StyleSelectorMatch:
|
|
327
|
+
"""Pre-computed selector→element match for blast radius analysis."""
|
|
328
|
+
id: int
|
|
329
|
+
selector_id: int
|
|
330
|
+
element_id: int
|
|
331
|
+
match_type: str
|
|
332
|
+
confidence: str = "high"
|
|
333
|
+
source_range: Optional[Location] = None
|
|
334
|
+
|
|
335
|
+
@classmethod
|
|
336
|
+
def from_row(cls, row: sqlite3.Row) -> "StyleSelectorMatch":
|
|
337
|
+
sr = None
|
|
338
|
+
if row["source_range"]:
|
|
339
|
+
sr = Location.from_dict(json.loads(row["source_range"]))
|
|
340
|
+
return cls(
|
|
341
|
+
id=row["id"],
|
|
342
|
+
selector_id=row["selector_id"],
|
|
343
|
+
element_id=row["element_id"],
|
|
344
|
+
match_type=row["match_type"],
|
|
345
|
+
confidence=row["confidence"],
|
|
346
|
+
source_range=sr,
|
|
347
|
+
)
|