Skip to content

Commit 858f4e9

Browse files
author
ChidcGithub
committed
v0.7.0: Dependencies upgrade, 3D gesture control & light mode fixes
1 parent 0b0541a commit 858f4e9

18 files changed

Lines changed: 866 additions & 541 deletions

README.md

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@
1313

1414
# PyVizAST
1515

16-
[![Version](https://img.shields.io/badge/Version-0.7.0--rc3-blue.svg)](https://github.com/ChidcGithub/PyVizAST)
16+
[![Version](https://img.shields.io/badge/Version-0.7.0-blue.svg)](https://github.com/ChidcGithub/PyVizAST)
1717
[![Python](https://img.shields.io/badge/Python-3.8%2B-brightgreen.svg)](https://www.python.org/)
1818
[![License](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](LICENSE)
1919
[![Platform](https://img.shields.io/badge/Platform-Windows%20%7C%20Linux%20%7C%20macOS-lightgrey.svg)](https://github.com/ChidcGithub/PyVizAST)
20-
[![Status](https://img.shields.io/badge/Status-rc-green.svg)](https://github.com/ChidcGithub/PyVizAST)
20+
[![Status](https://img.shields.io/badge/Status-stable-brightgreen.svg)](https://github.com/ChidcGithub/PyVizAST)
2121

2222
A Python AST Visualizer & Static Analyzer that transforms code into interactive graphs. Detect complexity, performance bottlenecks, and code smells with actionable refactoring suggestions.
2323

@@ -212,6 +212,57 @@ Contributions are welcome. Please submit pull requests to the main repository.
212212

213213
<summary>Version History</summary>
214214

215+
<details>
216+
<summary>v0.7.0 (2026-03-14)</summary>
217+
218+
**Major Release - Dependencies Upgrade & 3D Gesture Control**
219+
220+
**Dependencies Upgrade:**
221+
- React 18 → React 19.2.4
222+
- @react-three/fiber 8 → 9.5.0
223+
- @react-three/drei 9 → 10.7.7
224+
- framer-motion 11 → 12.36.0
225+
- three 0.160 → 0.183.2
226+
- cytoscape-react 3 → 4.0.0
227+
- All other dependencies updated to latest versions
228+
229+
**3D Gesture Control:**
230+
- Implemented pointing gesture for 3D AST visualization
231+
- Virtual cursor with dot, ring, progress ring, and snap indicator
232+
- 3D-to-screen coordinate projection using Three.js camera
233+
- Node snapping with smooth animation
234+
- Hover progress for auto-selection (800ms dwell time)
235+
- Camera focus on selected nodes
236+
237+
**Virtual Cursor System:**
238+
- Unified state management with `cursorStateRef`
239+
- Smooth position interpolation (0.25 factor for cursor, 0.35 for snap)
240+
- GPU-accelerated transforms for better performance
241+
- Theme-aware CSS variables for cursor colors
242+
- Separate CSS classes for 2D (`cursor-snap`) and 3D (`cursor-snap-3d`)
243+
244+
**Light Mode Fixes:**
245+
- Fixed `.detail-tag.scope` and `.detail-tag.callable` using white transparency
246+
- Fixed `.relationship-tag.inheritance/derived/method` theme compatibility
247+
- Fixed `.highlighted-line` background color
248+
- Fixed `.warning-dismiss:hover` background
249+
- Fixed `@keyframes searchPulse` with theme-aware `--pulse-color` variable
250+
251+
**Bug Fixes:**
252+
- Fixed cursor dot/ring centering (offset calculations: 10px→-5, 44px→-22)
253+
- Fixed CSS animation conflicting with JS transform positioning
254+
- Fixed 3D node projection coordinate system issues
255+
- Fixed container reference binding for correct cursor positioning
256+
257+
**Files Modified:**
258+
- `frontend/package.json` - Dependencies upgrade
259+
- `frontend/src/components/ASTVisualizer3D.js` - 3D gesture control implementation
260+
- `frontend/src/components/components.css` - Cursor styles, light mode fixes
261+
- `frontend/src/App.js` - Gesture handling
262+
- `backend/main.py` - Version bump
263+
264+
</details>
265+
215266
<details>
216267
<summary>v0.7.0-rc3 (2026-03-13)</summary>
217268

backend/analyzers/security.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,14 @@ def _check_hardcoded_secrets(self, code: str, source_lines: List[str]):
225225
]
226226
dynamic_regex = re.compile('|'.join(dynamic_patterns))
227227

228+
# Maximum line length to prevent regex backtracking attacks
229+
MAX_LINE_LENGTH = 2000
230+
228231
for i, line in enumerate(source_lines, 1):
232+
# Skip very long lines to prevent regex performance issues
233+
if len(line) > MAX_LINE_LENGTH:
234+
continue
235+
229236
# Skip comment lines
230237
if comment_pattern.match(line):
231238
continue

backend/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ async def lifespan(app: FastAPI) -> AsyncIterator[None]:
5858
app = FastAPI(
5959
title="PyVizAST API",
6060
description="Python AST Visualization and Static Analysis API",
61-
version="0.7.0-rc3",
61+
version="0.7.0",
6262
docs_url="/docs",
6363
redoc_url="/redoc",
6464
lifespan=lifespan

backend/routers/analysis.py

Lines changed: 11 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,8 @@
1414
CodeInput, AnalysisResult, ComplexityMetrics, SeverityLevel
1515
)
1616
from ..exceptions import CodeParsingError, CodeTooLargeError, AnalysisError
17-
from ..ast_parser import ASTParser, NodeMapper
18-
from ..analyzers import ComplexityAnalyzer, PerformanceAnalyzer, CodeSmellDetector, SecurityScanner
19-
from ..optimizers import SuggestionEngine, PatchGenerator
17+
from ..ast_parser import ASTParser
18+
from ..utils import AnalyzerFactory, get_parser
2019

2120
logger = logging.getLogger(__name__)
2221

@@ -29,47 +28,6 @@ class PatchApplyRequest(BaseModel):
2928
patch: str
3029

3130

32-
def get_parser(options: dict = None) -> ASTParser:
33-
"""Get configured parser instance"""
34-
options = options or {}
35-
max_nodes = options.get('max_nodes', 2000)
36-
simplified = options.get('simplified', False)
37-
38-
return ASTParser(max_nodes=max_nodes, simplified=simplified)
39-
40-
41-
class AnalyzerFactory:
42-
"""Analyzer factory - creates new instances per request to avoid state pollution"""
43-
44-
@staticmethod
45-
def create_complexity_analyzer() -> ComplexityAnalyzer:
46-
return ComplexityAnalyzer()
47-
48-
@staticmethod
49-
def create_performance_analyzer() -> PerformanceAnalyzer:
50-
return PerformanceAnalyzer()
51-
52-
@staticmethod
53-
def create_code_smell_detector() -> CodeSmellDetector:
54-
return CodeSmellDetector()
55-
56-
@staticmethod
57-
def create_security_scanner() -> SecurityScanner:
58-
return SecurityScanner()
59-
60-
@staticmethod
61-
def create_suggestion_engine() -> SuggestionEngine:
62-
return SuggestionEngine()
63-
64-
@staticmethod
65-
def create_patch_generator() -> PatchGenerator:
66-
return PatchGenerator()
67-
68-
@staticmethod
69-
def create_node_mapper(theme: str = "default") -> NodeMapper:
70-
return NodeMapper(theme=theme)
71-
72-
7331
@router.post("/analyze", response_model=AnalysisResult)
7432
async def analyze_code(input_data: CodeInput):
7533
"""
@@ -155,6 +113,15 @@ async def analyze_code(input_data: CodeInput):
155113
f"3) Analyze only part of the code."
156114
)
157115

116+
# Safety check: ensure tree was successfully parsed
117+
if tree is None:
118+
raise CodeTooLargeError(
119+
f"Code too large ({code_lines} lines, {code_size} bytes), cannot parse after all optimization attempts. "
120+
f"Suggestions: 1) Split code into multiple files; "
121+
f"2) Use a more powerful machine; "
122+
f"3) Analyze only part of the code."
123+
)
124+
158125
parser = get_parser({'simplified': auto_simplified, **options})
159126
ast_graph = parser.parse(code, tree=tree) # Pass pre-parsed tree to avoid double parsing
160127

backend/routers/ast_routes.py

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,30 +11,13 @@
1111

1212
from ..models.schemas import CodeInput, NodeType
1313
from ..exceptions import CodeParsingError, CodeTooLargeError
14-
from ..ast_parser import ASTParser, NodeMapper
14+
from ..utils import AnalyzerFactory, get_parser
1515

1616
logger = logging.getLogger(__name__)
1717

1818
router = APIRouter(prefix="/api", tags=["ast"])
1919

2020

21-
def get_parser(options: dict = None) -> ASTParser:
22-
"""Get configured parser instance"""
23-
options = options or {}
24-
max_nodes = options.get('max_nodes', 2000)
25-
simplified = options.get('simplified', False)
26-
27-
return ASTParser(max_nodes=max_nodes, simplified=simplified)
28-
29-
30-
class AnalyzerFactory:
31-
"""Analyzer factory for node mapper"""
32-
33-
@staticmethod
34-
def create_node_mapper(theme: str = "default") -> NodeMapper:
35-
return NodeMapper(theme=theme)
36-
37-
3821
@router.post("/ast")
3922
async def get_ast(input_data: CodeInput):
4023
"""

backend/utils/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
11
# Utils module
2+
from .factory import AnalyzerFactory, get_parser
3+
4+
__all__ = ['AnalyzerFactory', 'get_parser']

backend/utils/factory.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
"""
2+
Analyzer Factory - Shared factory for creating analyzer instances
3+
4+
@author: Chidc
5+
@link: github.com/chidcGithub
6+
"""
7+
from ..ast_parser import ASTParser, NodeMapper
8+
from ..analyzers import (
9+
ComplexityAnalyzer,
10+
PerformanceAnalyzer,
11+
CodeSmellDetector,
12+
SecurityScanner,
13+
)
14+
from ..optimizers import SuggestionEngine, PatchGenerator
15+
16+
17+
def get_parser(options: dict = None) -> ASTParser:
18+
"""Get configured parser instance"""
19+
options = options or {}
20+
max_nodes = options.get('max_nodes', 2000)
21+
simplified = options.get('simplified', False)
22+
23+
return ASTParser(max_nodes=max_nodes, simplified=simplified)
24+
25+
26+
class AnalyzerFactory:
27+
"""Analyzer factory - creates new instances per request to avoid state pollution"""
28+
29+
@staticmethod
30+
def create_complexity_analyzer() -> ComplexityAnalyzer:
31+
return ComplexityAnalyzer()
32+
33+
@staticmethod
34+
def create_performance_analyzer() -> PerformanceAnalyzer:
35+
return PerformanceAnalyzer()
36+
37+
@staticmethod
38+
def create_code_smell_detector() -> CodeSmellDetector:
39+
return CodeSmellDetector()
40+
41+
@staticmethod
42+
def create_security_scanner() -> SecurityScanner:
43+
return SecurityScanner()
44+
45+
@staticmethod
46+
def create_suggestion_engine() -> SuggestionEngine:
47+
return SuggestionEngine()
48+
49+
@staticmethod
50+
def create_patch_generator() -> PatchGenerator:
51+
return PatchGenerator()
52+
53+
@staticmethod
54+
def create_node_mapper(theme: str = "default") -> NodeMapper:
55+
return NodeMapper(theme=theme)

0 commit comments

Comments
 (0)