cursorflow 2.4.3__tar.gz → 2.6.0__tar.gz

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.
Files changed (53) hide show
  1. {cursorflow-2.4.3 → cursorflow-2.6.0}/PKG-INFO +8 -6
  2. {cursorflow-2.4.3 → cursorflow-2.6.0}/README.md +7 -5
  3. {cursorflow-2.4.3 → cursorflow-2.6.0}/cursorflow/cli.py +11 -19
  4. {cursorflow-2.4.3 → cursorflow-2.6.0}/cursorflow/core/cursorflow.py +3 -13
  5. cursorflow-2.6.0/cursorflow/core/json_utils.py +53 -0
  6. {cursorflow-2.4.3 → cursorflow-2.6.0}/cursorflow/core/mockup_comparator.py +29 -58
  7. {cursorflow-2.4.3 → cursorflow-2.6.0}/cursorflow/rules/cursorflow-usage.mdc +25 -0
  8. {cursorflow-2.4.3 → cursorflow-2.6.0}/cursorflow.egg-info/SOURCES.txt +1 -0
  9. {cursorflow-2.4.3 → cursorflow-2.6.0}/docs/user/USAGE_GUIDE.md +198 -0
  10. cursorflow-2.6.0/examples/mockup_comparison_example.py +195 -0
  11. {cursorflow-2.4.3 → cursorflow-2.6.0}/pyproject.toml +2 -2
  12. cursorflow-2.4.3/examples/mockup_comparison_example.py +0 -316
  13. {cursorflow-2.4.3 → cursorflow-2.6.0}/LICENSE +0 -0
  14. {cursorflow-2.4.3 → cursorflow-2.6.0}/MANIFEST.in +0 -0
  15. {cursorflow-2.4.3 → cursorflow-2.6.0}/cursorflow/__init__.py +0 -0
  16. {cursorflow-2.4.3 → cursorflow-2.6.0}/cursorflow/auto_init.py +0 -0
  17. {cursorflow-2.4.3 → cursorflow-2.6.0}/cursorflow/auto_updater.py +0 -0
  18. {cursorflow-2.4.3 → cursorflow-2.6.0}/cursorflow/core/action_validator.py +0 -0
  19. {cursorflow-2.4.3 → cursorflow-2.6.0}/cursorflow/core/agent.py +0 -0
  20. {cursorflow-2.4.3 → cursorflow-2.6.0}/cursorflow/core/auth_handler.py +0 -0
  21. {cursorflow-2.4.3 → cursorflow-2.6.0}/cursorflow/core/browser_controller.py +0 -0
  22. {cursorflow-2.4.3 → cursorflow-2.6.0}/cursorflow/core/browser_engine.py +0 -0
  23. {cursorflow-2.4.3 → cursorflow-2.6.0}/cursorflow/core/config_validator.py +0 -0
  24. {cursorflow-2.4.3 → cursorflow-2.6.0}/cursorflow/core/css_iterator.py +0 -0
  25. {cursorflow-2.4.3 → cursorflow-2.6.0}/cursorflow/core/cursor_integration.py +0 -0
  26. {cursorflow-2.4.3 → cursorflow-2.6.0}/cursorflow/core/error_context_collector.py +0 -0
  27. {cursorflow-2.4.3 → cursorflow-2.6.0}/cursorflow/core/error_correlator.py +0 -0
  28. {cursorflow-2.4.3 → cursorflow-2.6.0}/cursorflow/core/event_correlator.py +0 -0
  29. {cursorflow-2.4.3 → cursorflow-2.6.0}/cursorflow/core/file_change_monitor.py +0 -0
  30. {cursorflow-2.4.3 → cursorflow-2.6.0}/cursorflow/core/hmr_detector.py +0 -0
  31. {cursorflow-2.4.3 → cursorflow-2.6.0}/cursorflow/core/log_collector.py +0 -0
  32. {cursorflow-2.4.3 → cursorflow-2.6.0}/cursorflow/core/log_monitor.py +0 -0
  33. {cursorflow-2.4.3 → cursorflow-2.6.0}/cursorflow/core/persistent_session.py +0 -0
  34. {cursorflow-2.4.3 → cursorflow-2.6.0}/cursorflow/core/report_generator.py +0 -0
  35. {cursorflow-2.4.3 → cursorflow-2.6.0}/cursorflow/core/trace_manager.py +0 -0
  36. {cursorflow-2.4.3 → cursorflow-2.6.0}/cursorflow/install_cursorflow_rules.py +0 -0
  37. {cursorflow-2.4.3 → cursorflow-2.6.0}/cursorflow/log_sources/local_file.py +0 -0
  38. {cursorflow-2.4.3 → cursorflow-2.6.0}/cursorflow/log_sources/ssh_remote.py +0 -0
  39. {cursorflow-2.4.3 → cursorflow-2.6.0}/cursorflow/post_install.py +0 -0
  40. {cursorflow-2.4.3 → cursorflow-2.6.0}/cursorflow/rules/__init__.py +0 -0
  41. {cursorflow-2.4.3 → cursorflow-2.6.0}/cursorflow/rules/cursorflow-installation.mdc +0 -0
  42. {cursorflow-2.4.3 → cursorflow-2.6.0}/cursorflow/updater.py +0 -0
  43. {cursorflow-2.4.3 → cursorflow-2.6.0}/examples/comprehensive_screenshot_example.py +0 -0
  44. {cursorflow-2.4.3 → cursorflow-2.6.0}/examples/element_inspection_example.py +0 -0
  45. {cursorflow-2.4.3 → cursorflow-2.6.0}/examples/element_measurement_example.py +0 -0
  46. {cursorflow-2.4.3 → cursorflow-2.6.0}/examples/enhanced_screenshot_example.py +0 -0
  47. {cursorflow-2.4.3 → cursorflow-2.6.0}/examples/hot_reload_css_iteration.py +0 -0
  48. {cursorflow-2.4.3 → cursorflow-2.6.0}/examples/opensas_example.py +0 -0
  49. {cursorflow-2.4.3 → cursorflow-2.6.0}/examples/react_example.py +0 -0
  50. {cursorflow-2.4.3 → cursorflow-2.6.0}/examples/responsive_testing_example.py +0 -0
  51. {cursorflow-2.4.3 → cursorflow-2.6.0}/examples/v2_comprehensive_demo.py +0 -0
  52. {cursorflow-2.4.3 → cursorflow-2.6.0}/setup.cfg +0 -0
  53. {cursorflow-2.4.3 → cursorflow-2.6.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cursorflow
3
- Version: 2.4.3
3
+ Version: 2.6.0
4
4
  Summary: 🔥 Complete page intelligence for AI-driven development with Hot Reload Intelligence - captures DOM, network, console, performance, HMR events, and comprehensive page analysis
5
5
  Author-email: GeekWarrior Development <rbush@cooltheory.com>
6
6
  License-Expression: MIT
@@ -370,20 +370,22 @@ cursorflow test --base-url http://localhost:3000 --responsive --actions '[
370
370
  cursorflow test --base-url http://localhost:3000 --path "/api" --output "api-test-results.json"
371
371
  ```
372
372
 
373
- ### **Design Comparison**
373
+ ### **Design Comparison** (Pure Measurement)
374
374
  ```bash
375
- # Compare mockup to implementation
375
+ # Compare mockup to implementation - get similarity metrics
376
376
  cursorflow compare-mockup https://mockup.com/design \
377
377
  --base-url http://localhost:3000 \
378
- --mockup-actions '[{"navigate": "/"}]' \
379
378
  --implementation-actions '[{"navigate": "/dashboard"}]'
379
+ # Output: 87.3% similarity, diff images, element measurements
380
380
 
381
- # CSS iteration with HMR intelligence
381
+ # Test CSS variations - observe real rendering
382
382
  cursorflow iterate-mockup https://mockup.com/design \
383
383
  --base-url http://localhost:5173 \
384
384
  --css-improvements '[
385
- {"name": "fix-spacing", "css": ".container { gap: 2rem; }"}
385
+ {"name": "spacing-fix", "css": ".container { gap: 2rem; }"},
386
+ {"name": "tighter-spacing", "css": ".container { gap: 1rem; }"}
386
387
  ]'
388
+ # Output: Similarity data for each variation (Cursor decides which to apply)
387
389
  ```
388
390
 
389
391
  ### **Element Analysis & CSS Debugging**
@@ -325,20 +325,22 @@ cursorflow test --base-url http://localhost:3000 --responsive --actions '[
325
325
  cursorflow test --base-url http://localhost:3000 --path "/api" --output "api-test-results.json"
326
326
  ```
327
327
 
328
- ### **Design Comparison**
328
+ ### **Design Comparison** (Pure Measurement)
329
329
  ```bash
330
- # Compare mockup to implementation
330
+ # Compare mockup to implementation - get similarity metrics
331
331
  cursorflow compare-mockup https://mockup.com/design \
332
332
  --base-url http://localhost:3000 \
333
- --mockup-actions '[{"navigate": "/"}]' \
334
333
  --implementation-actions '[{"navigate": "/dashboard"}]'
334
+ # Output: 87.3% similarity, diff images, element measurements
335
335
 
336
- # CSS iteration with HMR intelligence
336
+ # Test CSS variations - observe real rendering
337
337
  cursorflow iterate-mockup https://mockup.com/design \
338
338
  --base-url http://localhost:5173 \
339
339
  --css-improvements '[
340
- {"name": "fix-spacing", "css": ".container { gap: 2rem; }"}
340
+ {"name": "spacing-fix", "css": ".container { gap: 2rem; }"},
341
+ {"name": "tighter-spacing", "css": ".container { gap: 1rem; }"}
341
342
  ]'
343
+ # Output: Similarity data for each variation (Cursor decides which to apply)
342
344
  ```
343
345
 
344
346
  ### **Element Analysis & CSS Debugging**
@@ -428,29 +428,20 @@ def compare_mockup(mockup_url, base_url, mockup_actions, implementation_actions,
428
428
  console.print(f"[red]❌ Comparison failed: {results['error']}[/red]")
429
429
  return
430
430
 
431
- # Display results summary
431
+ # Display results summary (pure metrics only)
432
432
  summary = results.get('summary', {})
433
433
  console.print(f"✅ Comparison completed: {results.get('comparison_id', 'unknown')}")
434
434
  console.print(f"📊 Average similarity: [bold]{summary.get('average_similarity', 0)}%[/bold]")
435
435
  console.print(f"📱 Viewports tested: {summary.get('viewports_tested', 0)}")
436
436
 
437
- # Show recommendations
438
- recommendations = results.get('recommendations', [])
439
- if recommendations:
440
- console.print(f"💡 Recommendations: {len(recommendations)} improvements suggested")
441
- for i, rec in enumerate(recommendations[:3]): # Show first 3
442
- console.print(f" {i+1}. {rec.get('description', 'No description')}")
437
+ # Show similarity range
438
+ similarity_range = summary.get('similarity_range', {})
439
+ if similarity_range:
440
+ console.print(f"📈 Similarity range: {similarity_range.get('min', 0)}% - {similarity_range.get('max', 0)}%")
443
441
 
444
- # Save results with numpy type handling
445
- with open(output, 'w') as f:
446
- def json_serializer(obj):
447
- # Handle numpy types
448
- if hasattr(obj, 'item'): # numpy scalars (bool_, int_, float_)
449
- return obj.item()
450
- if hasattr(obj, 'tolist'): # numpy arrays
451
- return obj.tolist()
452
- return str(obj)
453
- json.dump(results, f, indent=2, default=json_serializer)
442
+ # Save results with safe serialization
443
+ from .core.json_utils import safe_json_dump
444
+ safe_json_dump(results, output)
454
445
 
455
446
  console.print(f"💾 Full results saved to: [cyan]{output}[/cyan]")
456
447
  console.print(f"📁 Visual diffs stored in: [cyan].cursorflow/artifacts/[/cyan]")
@@ -551,9 +542,10 @@ def iterate_mockup(mockup_url, base_url, css_improvements, base_actions, diff_th
551
542
  for i, rec in enumerate(recommendations[:3]):
552
543
  console.print(f" {i+1}. {rec.get('description', 'No description')}")
553
544
 
554
- # Save results
545
+ # Save results with numpy type handling
546
+ from cursorflow.core.json_utils import safe_json_serialize
555
547
  with open(output, 'w') as f:
556
- json.dump(results, f, indent=2, default=str)
548
+ json.dump(results, f, indent=2, default=safe_json_serialize)
557
549
 
558
550
  console.print(f"💾 Full results saved to: [cyan]{output}[/cyan]")
559
551
  console.print(f"📁 Iteration progress stored in: [cyan].cursorflow/artifacts/[/cyan]")
@@ -575,19 +575,9 @@ class CursorFlow:
575
575
  if "error" in raw_results:
576
576
  return raw_results
577
577
 
578
- # Format results for Cursor analysis
579
- session_id = f"mockup_comparison_{int(time.time())}"
580
- cursor_results = self.cursor_integration.format_mockup_comparison_results(
581
- raw_results=raw_results,
582
- session_id=session_id,
583
- project_context={
584
- "framework": "auto-detected",
585
- "base_url": self.base_url,
586
- "test_type": "mockup_comparison"
587
- }
588
- )
589
-
590
- return cursor_results
578
+ # Return raw measurement data directly (pure observation)
579
+ # No interpretation, no analysis - Cursor makes decisions based on data
580
+ return raw_results
591
581
 
592
582
  except Exception as e:
593
583
  self.logger.error(f"Mockup comparison failed: {e}")
@@ -0,0 +1,53 @@
1
+ """
2
+ JSON Serialization Utilities
3
+
4
+ Handles conversion of numpy and other non-standard types to JSON-safe Python types.
5
+ Essential for mockup comparison and any feature using PIL/numpy for image analysis.
6
+ """
7
+
8
+ import json
9
+ from typing import Any
10
+ from pathlib import Path
11
+
12
+
13
+ def safe_json_serialize(obj: Any) -> Any:
14
+ """
15
+ Convert any Python/numpy type to JSON-safe type
16
+
17
+ Handles:
18
+ - numpy scalars (bool_, int_, float_)
19
+ - numpy arrays (ndarray)
20
+ - Other non-serializable types (converts to string)
21
+
22
+ Args:
23
+ obj: Any object that might not be JSON serializable
24
+
25
+ Returns:
26
+ JSON-safe Python type
27
+ """
28
+ # numpy scalars (bool_, int64, float64, etc.)
29
+ if hasattr(obj, 'item'):
30
+ return obj.item()
31
+
32
+ # numpy arrays
33
+ if hasattr(obj, 'tolist'):
34
+ return obj.tolist()
35
+
36
+ # Fallback for anything else
37
+ return str(obj)
38
+
39
+
40
+ def safe_json_dump(data: Any, file_path: str, indent: int = 2) -> None:
41
+ """
42
+ Safely dump data to JSON file with numpy type handling
43
+
44
+ Args:
45
+ data: Data to serialize
46
+ file_path: Path to output file
47
+ indent: JSON indentation level
48
+ """
49
+ file_path = Path(file_path)
50
+ file_path.parent.mkdir(parents=True, exist_ok=True)
51
+
52
+ with open(file_path, 'w') as f:
53
+ json.dump(data, f, indent=indent, default=safe_json_serialize)
@@ -11,6 +11,8 @@ import json
11
11
  from typing import Dict, List, Optional, Any, Tuple
12
12
  from pathlib import Path
13
13
  import logging
14
+
15
+ from .json_utils import safe_json_dump, safe_json_serialize
14
16
  try:
15
17
  from PIL import Image, ImageDraw, ImageChops
16
18
  import numpy as np
@@ -107,9 +109,14 @@ class MockupComparator:
107
109
  await mockup_browser.set_viewport(viewport["width"], viewport["height"])
108
110
  await implementation_browser.set_viewport(viewport["width"], viewport["height"])
109
111
 
110
- # Navigate to initial pages
112
+ # Navigate to initial pages with stabilization
111
113
  await mockup_browser.navigate("/")
114
+ await asyncio.sleep(1)
115
+ await mockup_browser.page.wait_for_load_state("domcontentloaded")
116
+
112
117
  await implementation_browser.navigate("/")
118
+ await asyncio.sleep(1)
119
+ await implementation_browser.page.wait_for_load_state("domcontentloaded")
113
120
 
114
121
  # Execute any required actions on mockup
115
122
  if mockup_actions:
@@ -119,6 +126,9 @@ class MockupComparator:
119
126
  if implementation_actions:
120
127
  await self._execute_actions_on_browser(implementation_browser, implementation_actions)
121
128
 
129
+ # Final stabilization before capturing
130
+ await asyncio.sleep(0.5)
131
+
122
132
  # Capture screenshots
123
133
  mockup_screenshot = await self._capture_comparison_screenshot(
124
134
  mockup_browser, f"{comparison_name}_mockup_{viewport_name}"
@@ -163,19 +173,12 @@ class MockupComparator:
163
173
  "implementation_url": implementation_url,
164
174
  "viewports_tested": len(viewports),
165
175
  "results": comparison_results,
166
- "summary": self._create_comparison_summary(comparison_results),
167
- "recommendations": self._generate_improvement_recommendations(comparison_results)
176
+ "summary": self._create_comparison_summary(comparison_results)
168
177
  }
169
178
 
170
- # Save comparison report
179
+ # Save comparison report with safe serialization
171
180
  report_path = self.artifacts_base / "mockup_comparisons" / f"{comparison_name}.json"
172
- with open(report_path, 'w') as f:
173
- # Custom JSON encoder to handle numpy booleans/integers
174
- def json_serializer(obj):
175
- if hasattr(obj, 'item'): # numpy types
176
- return obj.item()
177
- return str(obj)
178
- json.dump(comparison_report, f, indent=2, default=json_serializer)
181
+ safe_json_dump(comparison_report, str(report_path))
179
182
 
180
183
  self.logger.info(f"Mockup comparison completed: {comparison_name}")
181
184
  return comparison_report
@@ -293,10 +296,9 @@ class MockupComparator:
293
296
  "final_recommendations": self._generate_final_recommendations(iteration_results)
294
297
  }
295
298
 
296
- # Save iteration report
299
+ # Save iteration report with safe JSON serialization
297
300
  report_path = self.artifacts_base / "iteration_progress" / f"{iteration_session_id}.json"
298
- with open(report_path, 'w') as f:
299
- json.dump(iteration_report, f, indent=2, default=str)
301
+ safe_json_dump(iteration_report, str(report_path))
300
302
 
301
303
  self.logger.info(f"UI matching iteration completed: {iteration_session_id}")
302
304
  return iteration_report
@@ -314,11 +316,15 @@ class MockupComparator:
314
316
  if "navigate" in action:
315
317
  path = action["navigate"]
316
318
  await browser.navigate(path)
319
+ # Wait for page to stabilize after navigation
320
+ await asyncio.sleep(1) # Brief pause for dynamic content rendering
321
+ await browser.page.wait_for_load_state("domcontentloaded")
317
322
  elif "click" in action:
318
323
  selector = action["click"]
319
324
  if isinstance(selector, dict):
320
325
  selector = selector["selector"]
321
326
  await browser.click(selector)
327
+ await asyncio.sleep(0.5) # Brief pause for UI response
322
328
  elif "wait_for" in action:
323
329
  selector = action["wait_for"]
324
330
  if isinstance(selector, dict):
@@ -329,6 +335,7 @@ class MockupComparator:
329
335
  elif "scroll" in action:
330
336
  scroll_config = action["scroll"]
331
337
  await browser.page.evaluate(f"window.scrollTo({scroll_config.get('x', 0)}, {scroll_config.get('y', 0)})")
338
+ await asyncio.sleep(0.3) # Brief pause for scroll rendering
332
339
 
333
340
  async def _capture_comparison_screenshot(self, browser: BrowserController, name: str) -> str:
334
341
  """Capture screenshot for comparison"""
@@ -1223,55 +1230,19 @@ class MockupComparator:
1223
1230
 
1224
1231
  avg_similarity = sum(similarities) / len(similarities) if similarities else 0
1225
1232
 
1233
+ # Pure data summary - no interpretation
1226
1234
  return {
1227
1235
  "average_similarity": round(float(avg_similarity), 2),
1228
1236
  "viewports_tested": len(comparison_results),
1229
- "best_viewport_match": max(comparison_results, key=lambda x: x.get("visual_diff", {}).get("similarity_score", 0)),
1230
- "needs_improvement": bool(avg_similarity < 80) # Convert numpy.bool_ to Python bool
1237
+ "similarity_by_viewport": [
1238
+ {
1239
+ "viewport": result.get("viewport", {}).get("name", "unknown"),
1240
+ "similarity": float(result.get("visual_diff", {}).get("similarity_score", 0))
1241
+ }
1242
+ for result in comparison_results
1243
+ ]
1231
1244
  }
1232
1245
 
1233
- def _generate_improvement_recommendations(self, comparison_results: List[Dict]) -> List[Dict]:
1234
- """Generate recommendations for improving implementation to match mockup"""
1235
- recommendations = []
1236
-
1237
- for result in comparison_results:
1238
- viewport = result.get("viewport", {})
1239
- layout_analysis = result.get("layout_analysis", {})
1240
- visual_diff = result.get("visual_diff", {})
1241
-
1242
- # Analyze layout differences for recommendations
1243
- differences = layout_analysis.get("differences", [])
1244
-
1245
- for diff in differences:
1246
- if diff["type"] == "missing_in_implementation":
1247
- recommendations.append({
1248
- "type": "add_element",
1249
- "priority": "high",
1250
- "description": f"Add missing element: {diff['selector']}",
1251
- "viewport": viewport.get("name", "unknown")
1252
- })
1253
- elif diff["type"] == "property_differences":
1254
- for prop, prop_diff in diff.get("differences", {}).items():
1255
- recommendations.append({
1256
- "type": "adjust_property",
1257
- "priority": "medium",
1258
- "description": f"Adjust {prop} for {diff['selector']}",
1259
- "current_value": prop_diff.get("implementation"),
1260
- "target_value": prop_diff.get("mockup"),
1261
- "viewport": viewport.get("name", "unknown")
1262
- })
1263
-
1264
- # Add visual similarity recommendations
1265
- similarity = visual_diff.get("similarity_score", 0)
1266
- if similarity < 70:
1267
- recommendations.append({
1268
- "type": "major_visual_changes",
1269
- "priority": "high",
1270
- "description": f"Significant visual differences detected (similarity: {similarity}%)",
1271
- "viewport": viewport.get("name", "unknown")
1272
- })
1273
-
1274
- return recommendations
1275
1246
 
1276
1247
  def _create_iteration_summary(self, baseline_comparison: Dict, iteration_results: List[Dict]) -> Dict[str, Any]:
1277
1248
  """Create summary of iteration session"""
@@ -425,6 +425,31 @@ cursorflow measure -u http://localhost:3000 -s "#panel"
425
425
  # Output: 532w × 900h ✅
426
426
  ```
427
427
 
428
+ ### Visual Comparison & Iteration
429
+ ```bash
430
+ # Compare mockup to implementation (pure measurement)
431
+ cursorflow compare-mockup MOCKUP_URL -u BASE_URL -ia '[{"navigate": "/page"}]'
432
+ # Output: Similarity percentage, diff images, element data
433
+
434
+ # Test CSS variations (observe real rendering)
435
+ cursorflow iterate-mockup MOCKUP_URL -u BASE_URL --css-improvements '[
436
+ {"name": "test1", "css": ".header { padding: 2rem; }"},
437
+ {"name": "test2", "css": ".header { padding: 1rem; }"}
438
+ ]'
439
+ # Output: Similarity for each variation - Cursor decides which to apply
440
+ ```
441
+
442
+ **Philosophy**:
443
+ - CursorFlow observes mockup and implementation (both are reality)
444
+ - Provides quantified measurements (similarity %, diff images, element data)
445
+ - Temporarily injects CSS to observe what reality WOULD look like
446
+ - Cursor analyzes the data and makes decisions
447
+
448
+ **When to use**:
449
+ - User has design mockup and needs to match it
450
+ - Testing multiple CSS approaches before applying
451
+ - Measuring progress toward design specifications
452
+
428
453
  ## Analyzing Results
429
454
 
430
455
  ### **Hot Reload CSS Iteration Results**
@@ -24,6 +24,7 @@ cursorflow/core/error_correlator.py
24
24
  cursorflow/core/event_correlator.py
25
25
  cursorflow/core/file_change_monitor.py
26
26
  cursorflow/core/hmr_detector.py
27
+ cursorflow/core/json_utils.py
27
28
  cursorflow/core/log_collector.py
28
29
  cursorflow/core/log_monitor.py
29
30
  cursorflow/core/mockup_comparator.py
@@ -322,6 +322,114 @@ cursorflow measure -u http://localhost:3000 -s "#panel"
322
322
  # Output: 532w × 900h ✅ Fixed!
323
323
  ```
324
324
 
325
+ ### **Visual Comparison Commands**
326
+
327
+ CursorFlow provides visual comparison tools for iterating toward design specifications through pure measurement.
328
+
329
+ #### **`compare-mockup` - Visual Design Comparison**
330
+
331
+ Compare a design mockup against your work-in-progress implementation:
332
+
333
+ **What you get (pure data)**:
334
+ - **Screenshots** - Both mockup and implementation captured
335
+ - **Visual diff images** - Pixel-by-pixel difference highlighting
336
+ - **Similarity percentage** - Quantified visual match (0-100%)
337
+ - **Element position data** - X, Y coordinates for both versions
338
+ - **Size measurements** - Width, height comparisons
339
+ - **CSS property data** - Computed styles for matching elements
340
+
341
+ **Philosophy**: CursorFlow observes both realities (mockup + implementation) and provides measurements. Cursor analyzes the data and decides what changes to make.
342
+
343
+ **Basic usage**:
344
+ ```bash
345
+ cursorflow compare-mockup https://mockup.example.com/dashboard \
346
+ --base-url http://localhost:3000 \
347
+ --output comparison-results.json
348
+ ```
349
+
350
+ **With custom actions**:
351
+ ```bash
352
+ cursorflow compare-mockup https://mockup.example.com/dashboard \
353
+ --base-url http://localhost:3000 \
354
+ --mockup-actions '[{"navigate": "/dashboard"}]' \
355
+ --implementation-actions '[{"navigate": "/dashboard"}, {"wait_for": "#main-content"}]'
356
+ ```
357
+
358
+ **With multiple viewports**:
359
+ ```bash
360
+ cursorflow compare-mockup https://mockup.example.com \
361
+ --base-url http://localhost:3000 \
362
+ --viewports '[
363
+ {"width": 1440, "height": 900, "name": "desktop"},
364
+ {"width": 768, "height": 1024, "name": "tablet"}
365
+ ]'
366
+ ```
367
+
368
+ **Output structure**:
369
+ ```json
370
+ {
371
+ "comparison_id": "mockup_comparison_123456",
372
+ "mockup_url": "https://mockup.example.com",
373
+ "implementation_url": "http://localhost:3000",
374
+ "summary": {
375
+ "average_similarity": 87.73,
376
+ "viewports_tested": 2,
377
+ "similarity_by_viewport": [
378
+ {"viewport": "desktop", "similarity": 89.5},
379
+ {"viewport": "tablet", "similarity": 85.96}
380
+ ]
381
+ },
382
+ "results": [
383
+ {
384
+ "viewport": {"width": 1440, "height": 900, "name": "desktop"},
385
+ "mockup_screenshot": "path/to/mockup.png",
386
+ "implementation_screenshot": "path/to/impl.png",
387
+ "visual_diff": {
388
+ "similarity_score": 89.5,
389
+ "different_pixels": 45000,
390
+ "total_pixels": 1296000,
391
+ "diff_image": "path/to/diff.png",
392
+ "highlighted_diff": "path/to/highlighted.png"
393
+ },
394
+ "layout_analysis": {
395
+ "mockup_elements": 45,
396
+ "implementation_elements": 52,
397
+ "differences": [...]
398
+ }
399
+ }
400
+ ]
401
+ }
402
+ ```
403
+
404
+ **Use cases**:
405
+ - Compare implementation to Figma exports
406
+ - Verify design system component accuracy
407
+ - Measure progress toward design specifications
408
+ - Document visual differences for stakeholders
409
+
410
+ #### **`iterate-mockup` - CSS Iteration with Measurement**
411
+
412
+ Test multiple CSS changes and observe which gets closer to the mockup:
413
+
414
+ **Basic usage**:
415
+ ```bash
416
+ cursorflow iterate-mockup https://mockup.example.com/dashboard \
417
+ --base-url http://localhost:3000 \
418
+ --css-improvements '[
419
+ {"name": "spacing-fix", "css": ".header { padding: 2rem; }"},
420
+ {"name": "color-adjust", "css": ".btn { background: #007bff; }"}
421
+ ]'
422
+ ```
423
+
424
+ **What it does**:
425
+ 1. Captures baseline similarity
426
+ 2. Temporarily injects each CSS change
427
+ 3. Observes the REAL rendered result
428
+ 4. Captures similarity for each variation
429
+ 5. Provides measurements for Cursor to analyze
430
+
431
+ **Output**: Similarity data for each CSS variation (Cursor decides which to apply)
432
+
325
433
  ### **Artifact Management**
326
434
 
327
435
  CursorFlow generates screenshots, traces, and session data. Clean up regularly:
@@ -349,6 +457,96 @@ cursorflow cleanup --all --dry-run
349
457
 
350
458
  **Typical growth:** 50-100MB/day light usage, 500MB-1GB/day heavy usage
351
459
 
460
+ ### **Visual Comparison Commands**
461
+
462
+ CursorFlow provides mockup comparison for visual iteration - comparing your implementation to design specifications through pure data collection.
463
+
464
+ #### **compare-mockup - Visual Measurement**
465
+
466
+ Compare two URLs and get quantified similarity data:
467
+
468
+ ```bash
469
+ # Basic comparison
470
+ cursorflow compare-mockup "https://mockup.example.com" \
471
+ --base-url http://localhost:3000
472
+
473
+ # With custom actions
474
+ cursorflow compare-mockup "https://mockup.example.com" \
475
+ --base-url http://localhost:3000 \
476
+ --implementation-actions '[{"navigate": "/dashboard"}]'
477
+
478
+ # Multiple viewports
479
+ cursorflow compare-mockup "https://mockup.example.com" \
480
+ --base-url http://localhost:3000 \
481
+ --viewports '[{"width": 1440, "height": 900, "name": "desktop"}, {"width": 375, "height": 667, "name": "mobile"}]'
482
+ ```
483
+
484
+ **Output Data Structure**:
485
+ ```json
486
+ {
487
+ "comparison_id": "mockup_comparison_123456",
488
+ "mockup_url": "https://mockup.example.com",
489
+ "implementation_url": "http://localhost:3000",
490
+ "results": [
491
+ {
492
+ "viewport": {"width": 1440, "height": 900, "name": "desktop"},
493
+ "mockup_screenshot": "path/to/mockup.png",
494
+ "implementation_screenshot": "path/to/impl.png",
495
+ "visual_diff": {
496
+ "diff_image": "path/to/diff.png",
497
+ "highlighted_diff": "path/to/highlighted.png",
498
+ "similarity_score": 87.3,
499
+ "different_pixels": 45230,
500
+ "total_pixels": 1296000
501
+ }
502
+ }
503
+ ],
504
+ "summary": {
505
+ "average_similarity": 87.3,
506
+ "viewports_tested": 1,
507
+ "similarity_by_viewport": [...]
508
+ }
509
+ }
510
+ ```
511
+
512
+ **Philosophy**: Pure data collection - provides measurements, Cursor interprets them.
513
+
514
+ #### **iterate-mockup - CSS Experimentation**
515
+
516
+ Test multiple CSS variations and observe real outcomes:
517
+
518
+ ```bash
519
+ # Create CSS improvements JSON
520
+ cat > improvements.json << 'EOF'
521
+ [
522
+ {
523
+ "name": "fix-spacing",
524
+ "css": ".container { padding: 2rem; gap: 1.5rem; }"
525
+ },
526
+ {
527
+ "name": "adjust-colors",
528
+ "css": ".btn-primary { background: #007bff; }"
529
+ }
530
+ ]
531
+ EOF
532
+
533
+ # Run iteration
534
+ cursorflow iterate-mockup "https://mockup.example.com" \
535
+ --base-url http://localhost:3000 \
536
+ --css-improvements improvements.json
537
+ ```
538
+
539
+ **What it does**:
540
+ 1. Captures baseline comparison
541
+ 2. Temporarily injects each CSS variation
542
+ 3. Captures screenshot of REAL outcome
543
+ 4. Measures similarity for each variation
544
+ 5. Provides quantified data for each experiment
545
+
546
+ **Output**: Similarity percentages for each CSS variation, Cursor decides which to apply.
547
+
548
+ **Use case**: Rapid CSS experimentation with quantified feedback.
549
+
352
550
  ## ⚡ **Quick Usage Examples**
353
551
 
354
552
  ### **OpenSAS/Mod_Perl (Our Current Project)**
@@ -0,0 +1,195 @@
1
+ """
2
+ Mockup Comparison Example
3
+
4
+ Demonstrates pure observation approach to visual design matching.
5
+ CursorFlow provides measurements - Cursor makes decisions based on data.
6
+
7
+ Philosophy: We observe multiple realities (mockup, implementation, CSS variations)
8
+ and provide quantified similarity metrics. No interpretation, just data.
9
+ """
10
+
11
+ import asyncio
12
+ import json
13
+ from pathlib import Path
14
+ import sys
15
+
16
+ # Add parent directory to path for imports
17
+ sys.path.append(str(Path(__file__).parent.parent))
18
+
19
+ from cursorflow.core.cursorflow import CursorFlow
20
+
21
+
22
+ async def basic_mockup_comparison():
23
+ """
24
+ Basic example: Compare mockup to implementation with pure data collection
25
+ """
26
+ print("=" * 60)
27
+ print("🎨 Basic Mockup Comparison - Pure Observation")
28
+ print("=" * 60)
29
+
30
+ # Initialize CursorFlow
31
+ flow = CursorFlow(
32
+ base_url="http://localhost:3000", # Your work-in-progress
33
+ log_config={'source': 'local', 'paths': []},
34
+ browser_config={'headless': True}
35
+ )
36
+
37
+ # Compare mockup to implementation (observing both realities)
38
+ print("\n📸 Capturing both mockup and implementation...")
39
+ results = await flow.compare_mockup_to_implementation(
40
+ mockup_url="https://example.com", # Simulating mockup
41
+ implementation_actions=[
42
+ {"navigate": "/"},
43
+ {"wait_for": "body"}
44
+ ],
45
+ comparison_config={
46
+ "viewports": [
47
+ {"width": 1440, "height": 900, "name": "desktop"}
48
+ ],
49
+ "diff_threshold": 0.1
50
+ }
51
+ )
52
+
53
+ if "error" in results:
54
+ print(f"❌ Comparison failed: {results['error']}")
55
+ return
56
+
57
+ # Display pure metrics (no interpretation)
58
+ summary = results.get('summary', {})
59
+ print(f"\n✅ Comparison completed: {results.get('comparison_id')}")
60
+ print(f"\n📊 Measurement Data:")
61
+ print(f" Average similarity: {summary.get('average_similarity', 0)}%")
62
+ print(f" Viewports tested: {summary.get('viewports_tested', 0)}")
63
+
64
+ # Show per-viewport data
65
+ similarity_by_viewport = summary.get('similarity_by_viewport', [])
66
+ for viewport_data in similarity_by_viewport:
67
+ print(f" {viewport_data['viewport']}: {viewport_data['similarity']}%")
68
+
69
+ # Show what artifacts were created
70
+ print(f"\n📁 Artifacts created:")
71
+ for result in results.get('results', []):
72
+ print(f" Mockup screenshot: {Path(result['mockup_screenshot']).name}")
73
+ print(f" Implementation screenshot: {Path(result['implementation_screenshot']).name}")
74
+ print(f" Diff image: {Path(result['visual_diff']['diff_image']).name}")
75
+ print(f" Highlighted diff: {Path(result['visual_diff']['highlighted_diff']).name}")
76
+
77
+ print(f"\n💡 Cursor can now analyze this data to decide on CSS changes")
78
+ print(f" Data saved to: .cursorflow/artifacts/mockup_comparisons/")
79
+
80
+
81
+ async def css_iteration_with_measurements():
82
+ """
83
+ Example: Test multiple CSS variations and observe real outcomes
84
+ """
85
+ print("\n" + "=" * 60)
86
+ print("🔄 CSS Iteration - Observing Multiple Realities")
87
+ print("=" * 60)
88
+
89
+ flow = CursorFlow(
90
+ base_url="http://localhost:3000",
91
+ log_config={'source': 'local', 'paths': []},
92
+ browser_config={'headless': True}
93
+ )
94
+
95
+ # Define CSS variations to test
96
+ css_improvements = [
97
+ {
98
+ "name": "generous-spacing",
99
+ "css": ".container { padding: 2rem; gap: 2rem; }"
100
+ },
101
+ {
102
+ "name": "moderate-spacing",
103
+ "css": ".container { padding: 1.5rem; gap: 1.5rem; }"
104
+ },
105
+ {
106
+ "name": "tight-spacing",
107
+ "css": ".container { padding: 1rem; gap: 1rem; }"
108
+ }
109
+ ]
110
+
111
+ print(f"\n🧪 Testing {len(css_improvements)} CSS variations...")
112
+ print(" (Temporarily injecting CSS to observe real rendering)")
113
+
114
+ # Run iterative comparison
115
+ results = await flow.iterative_mockup_matching(
116
+ mockup_url="https://example.com",
117
+ css_improvements=css_improvements,
118
+ base_actions=[{"navigate": "/"}, {"wait_for": "body"}]
119
+ )
120
+
121
+ if "error" in results:
122
+ print(f"❌ Iteration failed: {results['error']}")
123
+ return
124
+
125
+ # Display measurements for each variation
126
+ print(f"\n📊 Similarity Measurements:")
127
+
128
+ baseline_sim = results.get('baseline', {}).get('visual_diff', {}).get('similarity_score', 0)
129
+ print(f" Baseline (no changes): {baseline_sim}%")
130
+
131
+ for iteration in results.get('iterations', []):
132
+ name = iteration.get('css_change', {}).get('name', 'unknown')
133
+ similarity = iteration.get('visual_diff', {}).get('similarity_score', 0)
134
+ diff = similarity - baseline_sim
135
+ print(f" {name}: {similarity}% ({diff:+.1f}%)")
136
+
137
+ print(f"\n💡 Pure data provided - Cursor analyzes and decides which CSS to apply")
138
+ print(f" Each variation measured against REAL rendering")
139
+
140
+
141
+ async def understanding_the_data():
142
+ """
143
+ Example: What data is available in comparison results
144
+ """
145
+ print("\n" + "=" * 60)
146
+ print("📋 Understanding Comparison Data Structure")
147
+ print("=" * 60)
148
+
149
+ print("""
150
+ CursorFlow provides these measurements:
151
+
152
+ 1. Visual Metrics:
153
+ - similarity_percentage: 0-100 (quantified match)
154
+ - different_pixels: Count of differing pixels
155
+ - total_pixels: Total canvas size
156
+ - major_difference_regions: [{x, y, width, height, area}]
157
+
158
+ 2. Screenshots:
159
+ - mockup_screenshot: Path to mockup capture
160
+ - implementation_screenshot: Path to implementation
161
+ - diff_image: Pixel-by-pixel difference
162
+ - highlighted_diff: Visual diff with red overlay
163
+
164
+ 3. Layout Data:
165
+ - mockup_elements: Count of elements in mockup
166
+ - implementation_elements: Count in implementation
167
+ - differences: Position/size/style variances
168
+
169
+ 4. Per-Viewport Data:
170
+ - Separate measurements for each breakpoint
171
+ - Responsive behavior captured
172
+
173
+ Cursor uses this data to:
174
+ - Decide if implementation is close enough
175
+ - Choose which CSS changes to make
176
+ - Prioritize layout vs styling fixes
177
+ - Determine when design match is acceptable
178
+ """)
179
+
180
+
181
+ if __name__ == '__main__':
182
+ print("=" * 60)
183
+ print("CursorFlow Mockup Comparison Examples")
184
+ print("Pure Observation - Cursor Makes Decisions")
185
+ print("=" * 60)
186
+
187
+ # Run examples
188
+ asyncio.run(basic_mockup_comparison())
189
+ asyncio.run(css_iteration_with_measurements())
190
+ asyncio.run(understanding_the_data())
191
+
192
+ print("\n" + "=" * 60)
193
+ print("✅ All examples completed!")
194
+ print("💡 CursorFlow observes reality - Cursor analyzes and decides")
195
+ print("=" * 60)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "cursorflow"
7
- version = "2.4.3"
7
+ version = "2.6.0"
8
8
  description = "🔥 Complete page intelligence for AI-driven development with Hot Reload Intelligence - captures DOM, network, console, performance, HMR events, and comprehensive page analysis"
9
9
  authors = [
10
10
  {name = "GeekWarrior Development", email = "rbush@cooltheory.com"}
@@ -72,6 +72,6 @@ line-length = 88
72
72
  target-version = ['py38']
73
73
 
74
74
  [tool.mypy]
75
- python_version = "2.4.3"
75
+ python_version = "2.6.0"
76
76
  warn_return_any = true
77
77
  warn_unused_configs = true
@@ -1,316 +0,0 @@
1
- """
2
- Mockup Comparison Example
3
-
4
- Demonstrates how to use CursorFlow to compare a design mockup with
5
- a work-in-progress implementation and iteratively improve the match.
6
- """
7
-
8
- import asyncio
9
- import json
10
- from pathlib import Path
11
- import sys
12
-
13
- # Add parent directory to path for imports
14
- sys.path.append(str(Path(__file__).parent.parent))
15
-
16
- from cursorflow.core.cursorflow import CursorFlow
17
-
18
-
19
- async def basic_mockup_comparison():
20
- """
21
- Basic example: Compare a mockup to current implementation
22
- """
23
- print("🎨 Basic Mockup Comparison Example")
24
- print("=" * 50)
25
-
26
- # Initialize CursorFlow
27
- flow = CursorFlow(
28
- base_url="http://localhost:3000", # Your work-in-progress implementation
29
- log_config={'source': 'local', 'paths': ['logs/app.log']},
30
- browser_config={'headless': True}
31
- )
32
-
33
- # Compare mockup to implementation
34
- results = await flow.compare_mockup_to_implementation(
35
- mockup_url="https://mockup.example.com/dashboard", # Replace with your mockup URL
36
- mockup_actions=[
37
- {"navigate": "/dashboard"},
38
- {"wait_for": "#main-content"},
39
- {"screenshot": "mockup_state"}
40
- ],
41
- implementation_actions=[
42
- {"navigate": "/dashboard"},
43
- {"wait_for": "#main-content"},
44
- {"screenshot": "implementation_state"}
45
- ],
46
- comparison_config={
47
- "viewports": [
48
- {"width": 1440, "height": 900, "name": "desktop"},
49
- {"width": 768, "height": 1024, "name": "tablet"},
50
- {"width": 375, "height": 667, "name": "mobile"}
51
- ],
52
- "diff_threshold": 0.1 # 10% difference threshold
53
- }
54
- )
55
-
56
- if "error" in results:
57
- print(f"❌ Comparison failed: {results['error']}")
58
- return
59
-
60
- # Display results
61
- summary = results.get('summary', {})
62
- print(f"✅ Comparison completed: {results.get('comparison_id')}")
63
- print(f"📊 Average similarity: {summary.get('average_similarity', 0)}%")
64
- print(f"📱 Viewports tested: {summary.get('viewports_tested', 0)}")
65
-
66
- # Show recommendations
67
- recommendations = results.get('recommendations', [])
68
- if recommendations:
69
- print(f"\n💡 Recommendations ({len(recommendations)} total):")
70
- for i, rec in enumerate(recommendations[:5]): # Show first 5
71
- print(f" {i+1}. {rec.get('description', 'No description')}")
72
-
73
- # Save results for analysis
74
- from pathlib import Path
75
- artifacts_dir = Path('.cursorflow/artifacts')
76
- artifacts_dir.mkdir(parents=True, exist_ok=True)
77
-
78
- results_file = artifacts_dir / 'basic_mockup_comparison_results.json'
79
- with open(results_file, 'w') as f:
80
- json.dump(results, f, indent=2, default=str)
81
-
82
- print(f"\n💾 Full results saved to: {results_file}")
83
- print(f"📁 Visual diffs available in: .cursorflow/artifacts/")
84
-
85
- return results
86
-
87
-
88
- async def iterative_mockup_matching():
89
- """
90
- Advanced example: Iteratively improve implementation to match mockup
91
- """
92
- print("\n🔄 Iterative Mockup Matching Example")
93
- print("=" * 50)
94
-
95
- # Initialize CursorFlow
96
- flow = CursorFlow(
97
- base_url="http://localhost:3000",
98
- log_config={'source': 'local', 'paths': ['logs/app.log']},
99
- browser_config={'headless': True}
100
- )
101
-
102
- # Define CSS improvements to test
103
- css_improvements = [
104
- {
105
- "name": "fix-header-spacing",
106
- "css": ".header { padding: 2rem 0; margin-bottom: 1rem; }",
107
- "rationale": "Match mockup header spacing and add bottom margin"
108
- },
109
- {
110
- "name": "adjust-button-styles",
111
- "css": ".btn-primary { background: #007bff; border-radius: 8px; padding: 12px 24px; }",
112
- "rationale": "Match mockup button styling with rounded corners"
113
- },
114
- {
115
- "name": "improve-card-layout",
116
- "css": ".card { box-shadow: 0 4px 6px rgba(0,0,0,0.1); border-radius: 12px; }",
117
- "rationale": "Add shadow and rounded corners to match mockup cards"
118
- },
119
- {
120
- "name": "fix-typography",
121
- "css": "h1 { font-size: 2.5rem; font-weight: 700; color: #1a1a1a; }",
122
- "rationale": "Match mockup heading typography"
123
- },
124
- {
125
- "name": "adjust-grid-spacing",
126
- "css": ".grid-container { gap: 2rem; padding: 1rem; }",
127
- "rationale": "Increase grid spacing to match mockup layout"
128
- }
129
- ]
130
-
131
- # Base actions to perform before each comparison
132
- base_actions = [
133
- {"navigate": "/dashboard"},
134
- {"wait_for": "#main-content"},
135
- {"wait": 1} # Let page fully load
136
- ]
137
-
138
- # Execute iterative matching
139
- results = await flow.iterative_mockup_matching(
140
- mockup_url="https://mockup.example.com/dashboard", # Replace with your mockup URL
141
- css_improvements=css_improvements,
142
- base_actions=base_actions,
143
- comparison_config={
144
- "diff_threshold": 0.08, # Slightly more sensitive
145
- "viewports": [
146
- {"width": 1440, "height": 900, "name": "desktop"}
147
- ]
148
- }
149
- )
150
-
151
- if "error" in results:
152
- print(f"❌ Iteration failed: {results['error']}")
153
- return
154
-
155
- # Display results
156
- summary = results.get('summary', {})
157
- print(f"✅ Iteration completed: {results.get('session_id')}")
158
- print(f"📊 Total improvement: {summary.get('total_improvement', 0)}%")
159
- print(f"🔄 Successful iterations: {summary.get('successful_iterations', 0)}/{summary.get('total_iterations', 0)}")
160
- print(f"📈 Average improvement per iteration: {summary.get('average_improvement_per_iteration', 0)}%")
161
-
162
- # Show best iteration
163
- best_iteration = results.get('best_iteration')
164
- if best_iteration:
165
- print(f"\n🏆 Best iteration:")
166
- print(f" Name: {best_iteration.get('css_change', {}).get('name', 'unnamed')}")
167
- print(f" Similarity achieved: {best_iteration.get('similarity_achieved', 0)}%")
168
- print(f" Improvement: +{best_iteration.get('improvement', 0)}%")
169
- print(f" CSS: {best_iteration.get('css_change', {}).get('css', 'N/A')}")
170
-
171
- # Show final recommendations
172
- recommendations = results.get('final_recommendations', [])
173
- if recommendations:
174
- print(f"\n💡 Final recommendations ({len(recommendations)} total):")
175
- for i, rec in enumerate(recommendations):
176
- print(f" {i+1}. [{rec.get('priority', 'medium').upper()}] {rec.get('description', 'No description')}")
177
-
178
- # Show successful CSS changes to apply
179
- successful_iterations = [
180
- iteration for iteration in results.get('iterations', [])
181
- if iteration.get('improvement_metrics', {}).get('is_improvement', False)
182
- ]
183
-
184
- if successful_iterations:
185
- print(f"\n🎯 CSS Changes to Apply ({len(successful_iterations)} successful):")
186
- for iteration in successful_iterations:
187
- css_change = iteration.get('css_change', {})
188
- improvement = iteration.get('improvement_metrics', {}).get('improvement', 0)
189
- print(f" ✅ {css_change.get('name', 'unnamed')} (+{improvement:.1f}%)")
190
- print(f" CSS: {css_change.get('css', 'N/A')}")
191
- print(f" Rationale: {css_change.get('rationale', 'N/A')}")
192
- print()
193
-
194
- # Save results for analysis
195
- from pathlib import Path
196
- artifacts_dir = Path('.cursorflow/artifacts')
197
- artifacts_dir.mkdir(parents=True, exist_ok=True)
198
-
199
- results_file = artifacts_dir / 'iterative_mockup_matching_results.json'
200
- with open(results_file, 'w') as f:
201
- json.dump(results, f, indent=2, default=str)
202
-
203
- print(f"💾 Full results saved to: {results_file}")
204
- print(f"📁 Iteration progress available in: .cursorflow/artifacts/")
205
-
206
- return results
207
-
208
-
209
- async def responsive_mockup_comparison():
210
- """
211
- Example: Compare mockup across multiple responsive breakpoints
212
- """
213
- print("\n📱 Responsive Mockup Comparison Example")
214
- print("=" * 50)
215
-
216
- # Initialize CursorFlow
217
- flow = CursorFlow(
218
- base_url="http://localhost:3000",
219
- log_config={'source': 'local', 'paths': ['logs/app.log']},
220
- browser_config={'headless': True}
221
- )
222
-
223
- # Test across multiple viewports
224
- results = await flow.compare_mockup_to_implementation(
225
- mockup_url="https://mockup.example.com/responsive-page",
226
- comparison_config={
227
- "viewports": [
228
- {"width": 1920, "height": 1080, "name": "large_desktop"},
229
- {"width": 1440, "height": 900, "name": "desktop"},
230
- {"width": 1024, "height": 768, "name": "tablet_landscape"},
231
- {"width": 768, "height": 1024, "name": "tablet_portrait"},
232
- {"width": 414, "height": 896, "name": "mobile_large"},
233
- {"width": 375, "height": 667, "name": "mobile_medium"},
234
- {"width": 320, "height": 568, "name": "mobile_small"}
235
- ],
236
- "diff_threshold": 0.12 # Slightly more tolerant for responsive
237
- }
238
- )
239
-
240
- if "error" in results:
241
- print(f"❌ Responsive comparison failed: {results['error']}")
242
- return
243
-
244
- # Analyze results by viewport
245
- print("📊 Responsive Comparison Results:")
246
- for result in results.get('results', []):
247
- viewport = result.get('viewport', {})
248
- visual_diff = result.get('visual_diff', {})
249
- similarity = visual_diff.get('similarity_score', 0)
250
-
251
- status = "✅" if similarity > 80 else "⚠️" if similarity > 60 else "❌"
252
- print(f" {status} {viewport.get('name', 'unknown')}: {similarity}% similarity")
253
-
254
- # Find best and worst performing viewports
255
- viewport_results = results.get('results', [])
256
- if viewport_results:
257
- best_viewport = max(viewport_results, key=lambda x: x.get('visual_diff', {}).get('similarity_score', 0))
258
- worst_viewport = min(viewport_results, key=lambda x: x.get('visual_diff', {}).get('similarity_score', 0))
259
-
260
- print(f"\n🏆 Best match: {best_viewport.get('viewport', {}).get('name', 'unknown')} "
261
- f"({best_viewport.get('visual_diff', {}).get('similarity_score', 0)}%)")
262
- print(f"🔧 Needs work: {worst_viewport.get('viewport', {}).get('name', 'unknown')} "
263
- f"({worst_viewport.get('visual_diff', {}).get('similarity_score', 0)}%)")
264
-
265
- # Save results
266
- from pathlib import Path
267
- artifacts_dir = Path('.cursorflow/artifacts')
268
- artifacts_dir.mkdir(parents=True, exist_ok=True)
269
-
270
- results_file = artifacts_dir / 'responsive_mockup_comparison_results.json'
271
- with open(results_file, 'w') as f:
272
- json.dump(results, f, indent=2, default=str)
273
-
274
- print(f"\n💾 Full results saved to: {results_file}")
275
-
276
- return results
277
-
278
-
279
- async def main():
280
- """
281
- Run all mockup comparison examples
282
- """
283
- print("🚀 CursorFlow Mockup Comparison Examples")
284
- print("=" * 60)
285
- print("This demonstrates how to compare mockups with implementations")
286
- print("and iteratively improve UI matching.\n")
287
-
288
- try:
289
- # Run basic comparison
290
- await basic_mockup_comparison()
291
-
292
- # Run iterative matching
293
- await iterative_mockup_matching()
294
-
295
- # Run responsive comparison
296
- await responsive_mockup_comparison()
297
-
298
- print("\n🎉 All examples completed successfully!")
299
- print("\nNext steps:")
300
- print("1. Review the generated JSON files for detailed analysis")
301
- print("2. Check .cursorflow/artifacts/ for visual diffs and screenshots")
302
- print("3. Apply the successful CSS changes to your actual codebase")
303
- print("4. Re-run comparisons to validate improvements")
304
-
305
- except Exception as e:
306
- print(f"\n❌ Example failed: {e}")
307
- import traceback
308
- traceback.print_exc()
309
-
310
-
311
- if __name__ == "__main__":
312
- # Note: Replace the mockup URLs with your actual mockup URLs
313
- print("⚠️ Remember to update the mockup URLs in this example!")
314
- print(" Replace 'https://mockup.example.com/...' with your actual mockup URLs\n")
315
-
316
- asyncio.run(main())
File without changes
File without changes
File without changes
File without changes