cursorflow 2.4.0__tar.gz → 2.4.2__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.
- {cursorflow-2.4.0 → cursorflow-2.4.2}/PKG-INFO +1 -1
- {cursorflow-2.4.0 → cursorflow-2.4.2}/cursorflow/__init__.py +13 -6
- {cursorflow-2.4.0 → cursorflow-2.4.2}/cursorflow/cli.py +56 -12
- {cursorflow-2.4.0 → cursorflow-2.4.2}/cursorflow/core/browser_controller.py +31 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/pyproject.toml +2 -2
- {cursorflow-2.4.0 → cursorflow-2.4.2}/LICENSE +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/MANIFEST.in +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/README.md +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/cursorflow/auto_init.py +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/cursorflow/auto_updater.py +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/cursorflow/core/action_validator.py +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/cursorflow/core/agent.py +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/cursorflow/core/auth_handler.py +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/cursorflow/core/browser_engine.py +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/cursorflow/core/config_validator.py +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/cursorflow/core/css_iterator.py +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/cursorflow/core/cursor_integration.py +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/cursorflow/core/cursorflow.py +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/cursorflow/core/error_context_collector.py +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/cursorflow/core/error_correlator.py +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/cursorflow/core/event_correlator.py +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/cursorflow/core/file_change_monitor.py +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/cursorflow/core/hmr_detector.py +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/cursorflow/core/log_collector.py +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/cursorflow/core/log_monitor.py +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/cursorflow/core/mockup_comparator.py +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/cursorflow/core/persistent_session.py +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/cursorflow/core/report_generator.py +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/cursorflow/core/trace_manager.py +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/cursorflow/install_cursorflow_rules.py +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/cursorflow/log_sources/local_file.py +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/cursorflow/log_sources/ssh_remote.py +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/cursorflow/post_install.py +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/cursorflow/rules/__init__.py +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/cursorflow/rules/cursorflow-installation.mdc +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/cursorflow/rules/cursorflow-usage.mdc +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/cursorflow/updater.py +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/cursorflow.egg-info/SOURCES.txt +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/docs/user/USAGE_GUIDE.md +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/examples/comprehensive_screenshot_example.py +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/examples/element_inspection_example.py +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/examples/element_measurement_example.py +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/examples/enhanced_screenshot_example.py +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/examples/hot_reload_css_iteration.py +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/examples/mockup_comparison_example.py +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/examples/opensas_example.py +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/examples/react_example.py +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/examples/responsive_testing_example.py +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/examples/v2_comprehensive_demo.py +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/setup.cfg +0 -0
- {cursorflow-2.4.0 → cursorflow-2.4.2}/setup.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: cursorflow
|
3
|
-
Version: 2.4.
|
3
|
+
Version: 2.4.2
|
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
|
@@ -18,9 +18,16 @@ from .core.log_monitor import LogMonitor
|
|
18
18
|
from .core.error_correlator import ErrorCorrelator
|
19
19
|
|
20
20
|
def _get_version():
|
21
|
-
"""Get version from
|
21
|
+
"""Get version from package metadata (works when installed via pip/pipx)"""
|
22
22
|
try:
|
23
|
-
#
|
23
|
+
# Standard approach: Use importlib.metadata (Python 3.8+)
|
24
|
+
from importlib.metadata import version
|
25
|
+
return version("cursorflow")
|
26
|
+
except Exception:
|
27
|
+
pass
|
28
|
+
|
29
|
+
try:
|
30
|
+
# Fallback for development: Read from pyproject.toml
|
24
31
|
import tomllib
|
25
32
|
pyproject_path = Path(__file__).parent.parent / "pyproject.toml"
|
26
33
|
if pyproject_path.exists():
|
@@ -31,7 +38,7 @@ def _get_version():
|
|
31
38
|
pass
|
32
39
|
|
33
40
|
try:
|
34
|
-
#
|
41
|
+
# Fallback for older Python: Try toml library
|
35
42
|
import toml
|
36
43
|
pyproject_path = Path(__file__).parent.parent / "pyproject.toml"
|
37
44
|
if pyproject_path.exists():
|
@@ -42,7 +49,7 @@ def _get_version():
|
|
42
49
|
pass
|
43
50
|
|
44
51
|
try:
|
45
|
-
#
|
52
|
+
# Final fallback: git tag (development only)
|
46
53
|
import subprocess
|
47
54
|
result = subprocess.run(
|
48
55
|
['git', 'describe', '--tags', '--exact-match'],
|
@@ -55,8 +62,8 @@ def _get_version():
|
|
55
62
|
except Exception:
|
56
63
|
pass
|
57
64
|
|
58
|
-
#
|
59
|
-
return "
|
65
|
+
# Last resort fallback - this should never be reached
|
66
|
+
return "0.0.0"
|
60
67
|
|
61
68
|
__version__ = _get_version()
|
62
69
|
__author__ = "GeekWarrior Development"
|
@@ -357,11 +357,13 @@ def test(base_url, path, actions, output, logs, config, verbose, headless, timeo
|
|
357
357
|
help='JSON array of viewports to test: [{"width": 1440, "height": 900, "name": "desktop"}]')
|
358
358
|
@click.option('--diff-threshold', '-t', type=float, default=0.1,
|
359
359
|
help='Visual difference threshold (0.0-1.0)')
|
360
|
+
@click.option('--block-tracking', is_flag=True,
|
361
|
+
help='Block tracking scripts (Google Analytics, Facebook Pixel, etc.) to prevent timeout')
|
360
362
|
@click.option('--output', '-o', default='mockup_comparison_results.json',
|
361
363
|
help='Output file for comparison results')
|
362
364
|
@click.option('--verbose', is_flag=True,
|
363
365
|
help='Verbose output')
|
364
|
-
def compare_mockup(mockup_url, base_url, mockup_actions, implementation_actions, viewports, diff_threshold, output, verbose):
|
366
|
+
def compare_mockup(mockup_url, base_url, mockup_actions, implementation_actions, viewports, diff_threshold, block_tracking, output, verbose):
|
365
367
|
"""Compare mockup design to work-in-progress implementation"""
|
366
368
|
|
367
369
|
console.print(f"🎨 Comparing mockup [blue]{mockup_url}[/blue] to implementation [blue]{base_url}[/blue]")
|
@@ -404,10 +406,17 @@ def compare_mockup(mockup_url, base_url, mockup_actions, implementation_actions,
|
|
404
406
|
# Initialize CursorFlow
|
405
407
|
try:
|
406
408
|
from .core.cursorflow import CursorFlow
|
409
|
+
|
410
|
+
# Configure browser with tracking blocking if requested
|
411
|
+
browser_config = {'headless': True}
|
412
|
+
if block_tracking:
|
413
|
+
browser_config['block_tracking'] = True
|
414
|
+
console.print("🚫 Blocking tracking scripts (Google Analytics, Facebook Pixel, etc.)")
|
415
|
+
|
407
416
|
flow = CursorFlow(
|
408
417
|
base_url=base_url,
|
409
418
|
log_config={'source': 'local', 'paths': ['logs/app.log']},
|
410
|
-
browser_config=
|
419
|
+
browser_config=browser_config
|
411
420
|
)
|
412
421
|
except Exception as e:
|
413
422
|
console.print(f"[red]Error initializing CursorFlow: {e}[/red]")
|
@@ -915,12 +924,25 @@ def inspect(base_url, path, selector, verbose):
|
|
915
924
|
visual = element.get('visual_context', {})
|
916
925
|
if visual:
|
917
926
|
console.print(f"\n👁️ Visual Context:")
|
918
|
-
|
927
|
+
# Check nested visibility structure
|
928
|
+
visibility = visual.get('visibility', {})
|
929
|
+
is_visible = visibility.get('is_visible', False)
|
930
|
+
is_in_viewport = visibility.get('is_in_viewport', False)
|
931
|
+
|
932
|
+
if is_visible:
|
919
933
|
console.print(f" Visibility: ✅ Visible")
|
934
|
+
if is_in_viewport:
|
935
|
+
console.print(f" In Viewport: ✅ Yes")
|
936
|
+
else:
|
937
|
+
console.print(f" In Viewport: ⬇️ Below fold")
|
920
938
|
else:
|
921
939
|
console.print(f" Visibility: ❌ Hidden")
|
922
|
-
|
923
|
-
|
940
|
+
|
941
|
+
# Z-index from layering info
|
942
|
+
layering = visual.get('layering', {})
|
943
|
+
z_index = layering.get('z_index')
|
944
|
+
if z_index and z_index != 'auto':
|
945
|
+
console.print(f" Z-index: {z_index}")
|
924
946
|
|
925
947
|
console.print("") # Spacing between elements
|
926
948
|
|
@@ -1067,14 +1089,19 @@ def measure(base_url, path, selector, verbose):
|
|
1067
1089
|
|
1068
1090
|
@main.command()
|
1069
1091
|
@click.option('--base-url', '-u', required=True)
|
1092
|
+
@click.option('--path', '-p', default='/', help='Path to navigate to')
|
1070
1093
|
@click.option('--selector', '-s', required=True)
|
1071
|
-
def count(base_url, selector):
|
1094
|
+
def count(base_url, path, selector):
|
1072
1095
|
"""
|
1073
1096
|
Quick element count without full test
|
1074
1097
|
|
1075
|
-
|
1098
|
+
Counts how many elements match the given selector.
|
1099
|
+
|
1100
|
+
Examples:
|
1101
|
+
cursorflow count --base-url http://localhost:3000 --selector ".message-item"
|
1102
|
+
cursorflow count -u http://localhost:3000 -p /dashboard -s "button"
|
1076
1103
|
"""
|
1077
|
-
console.print(f"🔢 Counting selector: [cyan]{selector}[/cyan]")
|
1104
|
+
console.print(f"🔢 Counting selector: [cyan]{selector}[/cyan] at [blue]{path}[/blue]\n")
|
1078
1105
|
|
1079
1106
|
try:
|
1080
1107
|
from .core.cursorflow import CursorFlow
|
@@ -1084,15 +1111,32 @@ def count(base_url, selector):
|
|
1084
1111
|
browser_config={'headless': True}
|
1085
1112
|
)
|
1086
1113
|
|
1087
|
-
#
|
1088
|
-
asyncio.run(flow.execute_and_collect([
|
1089
|
-
{"navigate":
|
1114
|
+
# Execute with screenshot to get comprehensive data
|
1115
|
+
results = asyncio.run(flow.execute_and_collect([
|
1116
|
+
{"navigate": path},
|
1117
|
+
{"wait_for_selector": "body"},
|
1118
|
+
{"screenshot": "count"}
|
1090
1119
|
]))
|
1091
1120
|
|
1092
|
-
|
1121
|
+
# Extract element data
|
1122
|
+
comprehensive_data = results.get('comprehensive_data', {})
|
1123
|
+
dom_analysis = comprehensive_data.get('dom_analysis', {})
|
1124
|
+
elements = dom_analysis.get('elements', [])
|
1125
|
+
|
1126
|
+
# Count matching elements
|
1127
|
+
matching_count = sum(1 for element in elements if _element_matches_selector(element, selector))
|
1128
|
+
|
1129
|
+
if matching_count == 0:
|
1130
|
+
console.print(f"[yellow]⚠️ No elements found matching: {selector}[/yellow]")
|
1131
|
+
console.print(f"💡 Total elements on page: {len(elements)}")
|
1132
|
+
else:
|
1133
|
+
console.print(f"[bold green]✅ Found {matching_count} element(s) matching: {selector}[/bold green]")
|
1134
|
+
console.print(f"💡 Total elements on page: {len(elements)}")
|
1093
1135
|
|
1094
1136
|
except Exception as e:
|
1095
1137
|
console.print(f"[red]❌ Count failed: {e}[/red]")
|
1138
|
+
import traceback
|
1139
|
+
console.print(traceback.format_exc())
|
1096
1140
|
|
1097
1141
|
@main.command()
|
1098
1142
|
@click.option('--click', '-c', multiple=True)
|
@@ -131,6 +131,10 @@ class BrowserController:
|
|
131
131
|
self.context = await self.browser.new_context(**context_config)
|
132
132
|
self.page = await self.context.new_page()
|
133
133
|
|
134
|
+
# Block tracking scripts if requested (prevents timeout on pages with analytics)
|
135
|
+
if self.config.get("block_tracking", False):
|
136
|
+
await self._setup_tracking_blocker()
|
137
|
+
|
134
138
|
# v2.0 Enhancement: Initialize Error Context Collector
|
135
139
|
self.error_context_collector = ErrorContextCollector(self.page, self.logger)
|
136
140
|
|
@@ -169,6 +173,33 @@ class BrowserController:
|
|
169
173
|
|
170
174
|
raise
|
171
175
|
|
176
|
+
async def _setup_tracking_blocker(self):
|
177
|
+
"""Block common tracking scripts to prevent networkidle timeout"""
|
178
|
+
tracking_patterns = [
|
179
|
+
"*google-analytics.com/*",
|
180
|
+
"*googletagmanager.com/*",
|
181
|
+
"*facebook.com/tr/*",
|
182
|
+
"*facebook.net/*",
|
183
|
+
"*doubleclick.net/*",
|
184
|
+
"*analytics.google.com/*",
|
185
|
+
"*hotjar.com/*",
|
186
|
+
"*mixpanel.com/*",
|
187
|
+
"*segment.com/*",
|
188
|
+
"*googleadservices.com/*",
|
189
|
+
"*connect.facebook.net/*",
|
190
|
+
"*/analytics/*",
|
191
|
+
"*/tracking/*"
|
192
|
+
]
|
193
|
+
|
194
|
+
async def block_tracking(route):
|
195
|
+
"""Block tracking request"""
|
196
|
+
await route.abort()
|
197
|
+
|
198
|
+
for pattern in tracking_patterns:
|
199
|
+
await self.page.route(pattern, block_tracking)
|
200
|
+
|
201
|
+
self.logger.info("🚫 Tracking scripts blocked")
|
202
|
+
|
172
203
|
async def _setup_event_listeners(self):
|
173
204
|
"""Set up universal event listeners for any framework"""
|
174
205
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "cursorflow"
|
7
|
-
version = "2.4.
|
7
|
+
version = "2.4.2"
|
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.
|
75
|
+
python_version = "2.4.2"
|
76
76
|
warn_return_any = true
|
77
77
|
warn_unused_configs = true
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|