cursorflow 1.2.0__py3-none-any.whl → 1.3.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.
- cursorflow/cli.py +245 -11
- cursorflow/core/browser_controller.py +659 -7
- cursorflow/core/cursor_integration.py +788 -0
- cursorflow/core/cursorflow.py +151 -0
- cursorflow/core/mockup_comparator.py +1316 -0
- cursorflow-1.3.1.dist-info/METADATA +247 -0
- {cursorflow-1.2.0.dist-info → cursorflow-1.3.1.dist-info}/RECORD +11 -9
- cursorflow-1.3.1.dist-info/licenses/LICENSE +21 -0
- cursorflow-1.2.0.dist-info/METADATA +0 -444
- {cursorflow-1.2.0.dist-info → cursorflow-1.3.1.dist-info}/WHEEL +0 -0
- {cursorflow-1.2.0.dist-info → cursorflow-1.3.1.dist-info}/entry_points.txt +0 -0
- {cursorflow-1.2.0.dist-info → cursorflow-1.3.1.dist-info}/top_level.txt +0 -0
cursorflow/core/cursorflow.py
CHANGED
@@ -20,6 +20,7 @@ from .auth_handler import AuthHandler
|
|
20
20
|
from .css_iterator import CSSIterator
|
21
21
|
from .cursor_integration import CursorIntegration
|
22
22
|
from .persistent_session import PersistentSession, get_session_manager
|
23
|
+
from .mockup_comparator import MockupComparator
|
23
24
|
|
24
25
|
|
25
26
|
class CursorFlow:
|
@@ -71,6 +72,7 @@ class CursorFlow:
|
|
71
72
|
self.auth_handler = AuthHandler(auth_config) if auth_config else None
|
72
73
|
self.css_iterator = CSSIterator()
|
73
74
|
self.cursor_integration = CursorIntegration()
|
75
|
+
self.mockup_comparator = MockupComparator()
|
74
76
|
|
75
77
|
# Session tracking
|
76
78
|
self.session_id = None
|
@@ -502,6 +504,155 @@ class CursorFlow:
|
|
502
504
|
await self.persistent_session.cleanup(save_state=save_state)
|
503
505
|
self.persistent_session = None
|
504
506
|
|
507
|
+
async def compare_mockup_to_implementation(
|
508
|
+
self,
|
509
|
+
mockup_url: str,
|
510
|
+
mockup_actions: Optional[List[Dict]] = None,
|
511
|
+
implementation_actions: Optional[List[Dict]] = None,
|
512
|
+
comparison_config: Optional[Dict] = None
|
513
|
+
) -> Dict[str, Any]:
|
514
|
+
"""
|
515
|
+
Compare mockup design to current implementation
|
516
|
+
|
517
|
+
Args:
|
518
|
+
mockup_url: URL of the design mockup/reference
|
519
|
+
mockup_actions: Optional actions to perform on mockup (clicks, scrolls, etc.)
|
520
|
+
implementation_actions: Optional actions to perform on implementation
|
521
|
+
comparison_config: {
|
522
|
+
"viewports": [{"width": 1440, "height": 900, "name": "desktop"}],
|
523
|
+
"diff_threshold": 0.1,
|
524
|
+
"ignore_regions": [{"x": 0, "y": 0, "width": 100, "height": 50}]
|
525
|
+
}
|
526
|
+
|
527
|
+
Returns:
|
528
|
+
{
|
529
|
+
"comparison_id": "mockup_comparison_123456",
|
530
|
+
"mockup_url": "https://mockup.example.com",
|
531
|
+
"implementation_url": "http://localhost:3000",
|
532
|
+
"results": [
|
533
|
+
{
|
534
|
+
"viewport": {"width": 1440, "height": 900, "name": "desktop"},
|
535
|
+
"visual_diff": {
|
536
|
+
"similarity_score": 85.2,
|
537
|
+
"diff_image": "path/to/diff.png",
|
538
|
+
"major_differences": [...]
|
539
|
+
},
|
540
|
+
"layout_analysis": {...},
|
541
|
+
"element_analysis": {...}
|
542
|
+
}
|
543
|
+
],
|
544
|
+
"summary": {
|
545
|
+
"average_similarity": 85.2,
|
546
|
+
"needs_improvement": false
|
547
|
+
},
|
548
|
+
"recommendations": [...]
|
549
|
+
}
|
550
|
+
"""
|
551
|
+
try:
|
552
|
+
# Execute raw comparison
|
553
|
+
raw_results = await self.mockup_comparator.compare_mockup_to_implementation(
|
554
|
+
mockup_url=mockup_url,
|
555
|
+
implementation_url=self.base_url,
|
556
|
+
mockup_actions=mockup_actions,
|
557
|
+
implementation_actions=implementation_actions,
|
558
|
+
comparison_config=comparison_config
|
559
|
+
)
|
560
|
+
|
561
|
+
if "error" in raw_results:
|
562
|
+
return raw_results
|
563
|
+
|
564
|
+
# Format results for Cursor analysis
|
565
|
+
session_id = f"mockup_comparison_{int(time.time())}"
|
566
|
+
cursor_results = self.cursor_integration.format_mockup_comparison_results(
|
567
|
+
raw_results=raw_results,
|
568
|
+
session_id=session_id,
|
569
|
+
project_context={
|
570
|
+
"framework": "auto-detected",
|
571
|
+
"base_url": self.base_url,
|
572
|
+
"test_type": "mockup_comparison"
|
573
|
+
}
|
574
|
+
)
|
575
|
+
|
576
|
+
return cursor_results
|
577
|
+
|
578
|
+
except Exception as e:
|
579
|
+
self.logger.error(f"Mockup comparison failed: {e}")
|
580
|
+
return {"error": str(e)}
|
581
|
+
|
582
|
+
async def iterative_mockup_matching(
|
583
|
+
self,
|
584
|
+
mockup_url: str,
|
585
|
+
css_improvements: List[Dict],
|
586
|
+
base_actions: Optional[List[Dict]] = None,
|
587
|
+
comparison_config: Optional[Dict] = None
|
588
|
+
) -> Dict[str, Any]:
|
589
|
+
"""
|
590
|
+
Iteratively improve implementation to match mockup design
|
591
|
+
|
592
|
+
Args:
|
593
|
+
mockup_url: Reference mockup URL
|
594
|
+
css_improvements: [
|
595
|
+
{
|
596
|
+
"name": "fix-header-spacing",
|
597
|
+
"css": ".header { padding: 2rem 0; }",
|
598
|
+
"rationale": "Match mockup header spacing"
|
599
|
+
}
|
600
|
+
]
|
601
|
+
base_actions: Actions to perform before each comparison
|
602
|
+
comparison_config: Configuration for comparison sensitivity
|
603
|
+
|
604
|
+
Returns:
|
605
|
+
{
|
606
|
+
"session_id": "ui_matching_123456",
|
607
|
+
"baseline_comparison": {...},
|
608
|
+
"iterations": [
|
609
|
+
{
|
610
|
+
"iteration_number": 1,
|
611
|
+
"css_change": {...},
|
612
|
+
"mockup_comparison": {...},
|
613
|
+
"improvement_metrics": {
|
614
|
+
"baseline_similarity": 75.0,
|
615
|
+
"improved_similarity": 82.5,
|
616
|
+
"improvement": 7.5,
|
617
|
+
"is_improvement": true
|
618
|
+
}
|
619
|
+
}
|
620
|
+
],
|
621
|
+
"best_iteration": {...},
|
622
|
+
"final_recommendations": [...]
|
623
|
+
}
|
624
|
+
"""
|
625
|
+
try:
|
626
|
+
# Execute raw iterative matching
|
627
|
+
raw_results = await self.mockup_comparator.iterative_ui_matching(
|
628
|
+
mockup_url=mockup_url,
|
629
|
+
implementation_url=self.base_url,
|
630
|
+
css_improvements=css_improvements,
|
631
|
+
base_actions=base_actions,
|
632
|
+
comparison_config=comparison_config
|
633
|
+
)
|
634
|
+
|
635
|
+
if "error" in raw_results:
|
636
|
+
return raw_results
|
637
|
+
|
638
|
+
# Format results for Cursor analysis
|
639
|
+
session_id = f"iterative_mockup_{int(time.time())}"
|
640
|
+
cursor_results = self.cursor_integration.format_iterative_mockup_results(
|
641
|
+
raw_results=raw_results,
|
642
|
+
session_id=session_id,
|
643
|
+
project_context={
|
644
|
+
"framework": "auto-detected",
|
645
|
+
"base_url": self.base_url,
|
646
|
+
"test_type": "iterative_mockup_matching"
|
647
|
+
}
|
648
|
+
)
|
649
|
+
|
650
|
+
return cursor_results
|
651
|
+
|
652
|
+
except Exception as e:
|
653
|
+
self.logger.error(f"Iterative mockup matching failed: {e}")
|
654
|
+
return {"error": str(e)}
|
655
|
+
|
505
656
|
async def _initialize_session(self, session_options: Dict):
|
506
657
|
"""Initialize browser and authentication session"""
|
507
658
|
self.session_id = f"session_{int(time.time())}"
|