cursorflow 2.3.1__tar.gz → 2.3.3__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.3.1 → cursorflow-2.3.3}/MANIFEST.in +2 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/PKG-INFO +1 -1
- {cursorflow-2.3.1 → cursorflow-2.3.3}/cursorflow/core/log_collector.py +14 -2
- {cursorflow-2.3.1 → cursorflow-2.3.3}/cursorflow.egg-info/SOURCES.txt +2 -0
- cursorflow-2.3.3/examples/element_inspection_example.py +183 -0
- cursorflow-2.3.3/examples/element_measurement_example.py +243 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/pyproject.toml +2 -2
- {cursorflow-2.3.1 → cursorflow-2.3.3}/LICENSE +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/README.md +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/cursorflow/__init__.py +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/cursorflow/auto_init.py +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/cursorflow/auto_updater.py +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/cursorflow/cli.py +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/cursorflow/core/action_validator.py +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/cursorflow/core/agent.py +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/cursorflow/core/auth_handler.py +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/cursorflow/core/browser_controller.py +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/cursorflow/core/browser_engine.py +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/cursorflow/core/config_validator.py +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/cursorflow/core/css_iterator.py +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/cursorflow/core/cursor_integration.py +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/cursorflow/core/cursorflow.py +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/cursorflow/core/error_context_collector.py +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/cursorflow/core/error_correlator.py +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/cursorflow/core/event_correlator.py +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/cursorflow/core/file_change_monitor.py +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/cursorflow/core/hmr_detector.py +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/cursorflow/core/log_monitor.py +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/cursorflow/core/mockup_comparator.py +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/cursorflow/core/persistent_session.py +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/cursorflow/core/report_generator.py +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/cursorflow/core/trace_manager.py +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/cursorflow/install_cursorflow_rules.py +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/cursorflow/log_sources/local_file.py +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/cursorflow/log_sources/ssh_remote.py +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/cursorflow/post_install.py +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/cursorflow/rules/__init__.py +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/cursorflow/rules/cursorflow-installation.mdc +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/cursorflow/rules/cursorflow-usage.mdc +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/cursorflow/updater.py +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/docs/user/USAGE_GUIDE.md +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/examples/comprehensive_screenshot_example.py +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/examples/enhanced_screenshot_example.py +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/examples/hot_reload_css_iteration.py +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/examples/mockup_comparison_example.py +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/examples/opensas_example.py +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/examples/react_example.py +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/examples/responsive_testing_example.py +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/examples/v2_comprehensive_demo.py +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/setup.cfg +0 -0
- {cursorflow-2.3.1 → cursorflow-2.3.3}/setup.py +0 -0
@@ -19,6 +19,8 @@ include examples/enhanced_screenshot_example.py
|
|
19
19
|
include examples/responsive_testing_example.py
|
20
20
|
include examples/hot_reload_css_iteration.py
|
21
21
|
include examples/v2_comprehensive_demo.py
|
22
|
+
include examples/element_inspection_example.py
|
23
|
+
include examples/element_measurement_example.py
|
22
24
|
|
23
25
|
# INCLUDE: Customer-facing documentation
|
24
26
|
include docs/user/USAGE_GUIDE.md
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: cursorflow
|
3
|
-
Version: 2.3.
|
3
|
+
Version: 2.3.3
|
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
|
@@ -87,12 +87,20 @@ class LogCollector:
|
|
87
87
|
"""Initialize local file log sources"""
|
88
88
|
paths = self.config.get("paths", ["logs/app.log"])
|
89
89
|
|
90
|
+
# Skip local log sources if we're clearly testing a remote URL
|
91
|
+
# (they won't work anyway and just cause confusing errors)
|
92
|
+
base_url = self.config.get("base_url", "")
|
93
|
+
if base_url and (base_url.startswith("http://") or base_url.startswith("https://")):
|
94
|
+
if not any("localhost" in base_url or "127.0.0.1" in base_url for url in [base_url]):
|
95
|
+
self.logger.debug("Skipping local log sources for remote URL testing")
|
96
|
+
return
|
97
|
+
|
90
98
|
for path in paths:
|
91
99
|
if Path(path).exists() or self.config.get("create_if_missing", True):
|
92
100
|
source = LocalFileLogSource(path)
|
93
101
|
self.log_sources.append(source)
|
94
102
|
else:
|
95
|
-
self.logger.
|
103
|
+
self.logger.debug(f"Log file not found: {path}")
|
96
104
|
|
97
105
|
def _init_docker_sources(self):
|
98
106
|
"""Initialize Docker container log sources"""
|
@@ -182,8 +190,12 @@ class LogCollector:
|
|
182
190
|
# Brief pause to avoid overwhelming
|
183
191
|
await asyncio.sleep(0.1)
|
184
192
|
|
193
|
+
except AttributeError as e:
|
194
|
+
# Log source doesn't support get_new_entries (incompatible source type)
|
195
|
+
self.logger.debug(f"Log source {source} is incompatible: {e}")
|
196
|
+
break # Stop monitoring this source
|
185
197
|
except Exception as e:
|
186
|
-
self.logger.
|
198
|
+
self.logger.debug(f"Log source error: {e}")
|
187
199
|
await asyncio.sleep(1) # Wait before retry
|
188
200
|
|
189
201
|
except Exception as e:
|
@@ -37,6 +37,8 @@ cursorflow/rules/cursorflow-installation.mdc
|
|
37
37
|
cursorflow/rules/cursorflow-usage.mdc
|
38
38
|
docs/user/USAGE_GUIDE.md
|
39
39
|
examples/comprehensive_screenshot_example.py
|
40
|
+
examples/element_inspection_example.py
|
41
|
+
examples/element_measurement_example.py
|
40
42
|
examples/enhanced_screenshot_example.py
|
41
43
|
examples/hot_reload_css_iteration.py
|
42
44
|
examples/mockup_comparison_example.py
|
@@ -0,0 +1,183 @@
|
|
1
|
+
"""
|
2
|
+
Element Inspection Example
|
3
|
+
|
4
|
+
This example shows how to use the inspect command for comprehensive CSS debugging
|
5
|
+
and element analysis. Perfect for understanding layout issues and computed styles.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import asyncio
|
9
|
+
import json
|
10
|
+
from cursorflow import CursorFlow
|
11
|
+
|
12
|
+
async def inspect_element_comprehensive():
|
13
|
+
"""Example: Comprehensive element inspection with full CSS analysis"""
|
14
|
+
|
15
|
+
print("🔍 Comprehensive Element Inspection Example\n")
|
16
|
+
|
17
|
+
# Initialize CursorFlow
|
18
|
+
flow = CursorFlow(
|
19
|
+
base_url="http://localhost:3000",
|
20
|
+
log_config={'source': 'local', 'paths': []},
|
21
|
+
browser_config={'headless': True}
|
22
|
+
)
|
23
|
+
|
24
|
+
# Execute inspection with screenshot
|
25
|
+
print("📸 Inspecting #messages-panel with comprehensive data capture...")
|
26
|
+
results = await flow.execute_and_collect([
|
27
|
+
{"navigate": "/console"},
|
28
|
+
{"wait_for_selector": "body"},
|
29
|
+
{"screenshot": "inspection"}
|
30
|
+
])
|
31
|
+
|
32
|
+
# Extract element data from comprehensive analysis
|
33
|
+
comprehensive_data = results.get('comprehensive_data', {})
|
34
|
+
dom_analysis = comprehensive_data.get('dom_analysis', {})
|
35
|
+
elements = dom_analysis.get('elements', [])
|
36
|
+
|
37
|
+
# Find the target element
|
38
|
+
target_selector = "#messages-panel"
|
39
|
+
matching_elements = [
|
40
|
+
el for el in elements
|
41
|
+
if el.get('id') == 'messages-panel'
|
42
|
+
]
|
43
|
+
|
44
|
+
if not matching_elements:
|
45
|
+
print(f"⚠️ Element {target_selector} not found")
|
46
|
+
return
|
47
|
+
|
48
|
+
element = matching_elements[0]
|
49
|
+
|
50
|
+
# Display comprehensive element information
|
51
|
+
print(f"\n═══ Element: {target_selector} ═══\n")
|
52
|
+
|
53
|
+
# Basic info
|
54
|
+
print(f"Tag: {element.get('tagName', 'unknown')}")
|
55
|
+
print(f"ID: #{element.get('id', 'N/A')}")
|
56
|
+
print(f"Classes: .{element.get('className', 'N/A')}")
|
57
|
+
|
58
|
+
# Dimensions
|
59
|
+
visual_context = element.get('visual_context', {})
|
60
|
+
bbox = visual_context.get('bounding_box', {})
|
61
|
+
if bbox:
|
62
|
+
print(f"\n📐 Dimensions:")
|
63
|
+
print(f" Position: x={bbox.get('x', 0):.0f}, y={bbox.get('y', 0):.0f}")
|
64
|
+
print(f" Size: {bbox.get('width', 0):.0f}w × {bbox.get('height', 0):.0f}h")
|
65
|
+
|
66
|
+
# Computed styles
|
67
|
+
computed = element.get('computedStyles', {})
|
68
|
+
if computed:
|
69
|
+
print(f"\n🎨 Key CSS Properties:")
|
70
|
+
print(f" display: {computed.get('display', 'N/A')}")
|
71
|
+
print(f" position: {computed.get('position', 'N/A')}")
|
72
|
+
print(f" flex: {computed.get('flex', 'N/A')}")
|
73
|
+
print(f" width: {computed.get('width', 'N/A')}")
|
74
|
+
print(f" height: {computed.get('height', 'N/A')}")
|
75
|
+
|
76
|
+
# Accessibility
|
77
|
+
accessibility = element.get('accessibility', {})
|
78
|
+
if accessibility:
|
79
|
+
print(f"\n♿ Accessibility:")
|
80
|
+
print(f" Role: {accessibility.get('role', 'N/A')}")
|
81
|
+
print(f" Interactive: {'✅' if accessibility.get('isInteractive') else '❌'}")
|
82
|
+
|
83
|
+
# Visual context
|
84
|
+
visibility = visual_context.get('visibility', {})
|
85
|
+
if visibility:
|
86
|
+
print(f"\n👁️ Visual Context:")
|
87
|
+
print(f" Visible: {'✅' if visibility.get('is_visible') else '❌'}")
|
88
|
+
print(f" In viewport: {'✅' if visibility.get('is_in_viewport') else '❌'}")
|
89
|
+
|
90
|
+
# Screenshot location
|
91
|
+
screenshots = results.get('artifacts', {}).get('screenshots', [])
|
92
|
+
if screenshots:
|
93
|
+
print(f"\n📸 Screenshot saved: {screenshots[0]}")
|
94
|
+
|
95
|
+
print("\n✅ Inspection complete!")
|
96
|
+
|
97
|
+
async def inspect_multiple_elements():
|
98
|
+
"""Example: Inspect multiple elements for comparison"""
|
99
|
+
|
100
|
+
print("\n🔍 Multiple Element Inspection Example\n")
|
101
|
+
|
102
|
+
flow = CursorFlow(
|
103
|
+
base_url="http://localhost:3000",
|
104
|
+
log_config={'source': 'local', 'paths': []},
|
105
|
+
browser_config={'headless': True}
|
106
|
+
)
|
107
|
+
|
108
|
+
# Execute inspection
|
109
|
+
results = await flow.execute_and_collect([
|
110
|
+
{"navigate": "/dashboard"},
|
111
|
+
{"wait_for_selector": "body"},
|
112
|
+
{"screenshot": "dashboard"}
|
113
|
+
])
|
114
|
+
|
115
|
+
# Extract element data
|
116
|
+
comprehensive_data = results.get('comprehensive_data', {})
|
117
|
+
elements = comprehensive_data.get('dom_analysis', {}).get('elements', [])
|
118
|
+
|
119
|
+
# Compare multiple panels
|
120
|
+
selectors = [".sidebar", ".main-content", ".right-panel"]
|
121
|
+
|
122
|
+
print("📊 Comparing panel widths:\n")
|
123
|
+
for selector in selectors:
|
124
|
+
matching = [
|
125
|
+
el for el in elements
|
126
|
+
if selector[1:] in el.get('className', '').split()
|
127
|
+
]
|
128
|
+
|
129
|
+
if matching:
|
130
|
+
element = matching[0]
|
131
|
+
bbox = element.get('visual_context', {}).get('bounding_box', {})
|
132
|
+
computed = element.get('computedStyles', {})
|
133
|
+
|
134
|
+
print(f"{selector}:")
|
135
|
+
print(f" Rendered: {bbox.get('width', 0):.0f}px")
|
136
|
+
print(f" CSS: {computed.get('width', 'N/A')}")
|
137
|
+
print(f" Flex: {computed.get('flex', 'N/A')}\n")
|
138
|
+
|
139
|
+
async def inspect_with_verbose_css():
|
140
|
+
"""Example: Get ALL computed CSS properties for deep debugging"""
|
141
|
+
|
142
|
+
print("\n🔍 Verbose CSS Inspection Example\n")
|
143
|
+
|
144
|
+
flow = CursorFlow(
|
145
|
+
base_url="http://localhost:3000",
|
146
|
+
log_config={'source': 'local', 'paths': []},
|
147
|
+
browser_config={'headless': True}
|
148
|
+
)
|
149
|
+
|
150
|
+
results = await flow.execute_and_collect([
|
151
|
+
{"navigate": "/component"},
|
152
|
+
{"wait_for_selector": "body"},
|
153
|
+
{"screenshot": "component"}
|
154
|
+
])
|
155
|
+
|
156
|
+
# Extract element
|
157
|
+
elements = results.get('comprehensive_data', {}).get('dom_analysis', {}).get('elements', [])
|
158
|
+
|
159
|
+
if elements:
|
160
|
+
element = elements[0] # First element
|
161
|
+
computed = element.get('computedStyles', {})
|
162
|
+
|
163
|
+
print(f"🎨 All {len(computed)} Computed CSS Properties:\n")
|
164
|
+
|
165
|
+
for prop, value in sorted(computed.items())[:20]: # Show first 20
|
166
|
+
print(f" {prop}: {value}")
|
167
|
+
|
168
|
+
print(f"\n ... and {len(computed) - 20} more properties")
|
169
|
+
|
170
|
+
if __name__ == '__main__':
|
171
|
+
print("=" * 60)
|
172
|
+
print("CursorFlow Element Inspection Examples")
|
173
|
+
print("=" * 60)
|
174
|
+
|
175
|
+
# Run examples
|
176
|
+
asyncio.run(inspect_element_comprehensive())
|
177
|
+
asyncio.run(inspect_multiple_elements())
|
178
|
+
asyncio.run(inspect_with_verbose_css())
|
179
|
+
|
180
|
+
print("\n" + "=" * 60)
|
181
|
+
print("✅ All examples completed!")
|
182
|
+
print("=" * 60)
|
183
|
+
|
@@ -0,0 +1,243 @@
|
|
1
|
+
"""
|
2
|
+
Element Measurement Example
|
3
|
+
|
4
|
+
This example shows how to use the measure command for surgical dimension checking
|
5
|
+
and quick CSS verification. Perfect for validating layout changes.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import asyncio
|
9
|
+
import json
|
10
|
+
from cursorflow import CursorFlow
|
11
|
+
|
12
|
+
async def measure_single_element():
|
13
|
+
"""Example: Quick dimension check for a single element"""
|
14
|
+
|
15
|
+
print("📏 Single Element Measurement Example\n")
|
16
|
+
|
17
|
+
flow = CursorFlow(
|
18
|
+
base_url="http://localhost:3000",
|
19
|
+
log_config={'source': 'local', 'paths': []},
|
20
|
+
browser_config={'headless': True}
|
21
|
+
)
|
22
|
+
|
23
|
+
# Execute measurement
|
24
|
+
print("📐 Measuring #main-panel dimensions...")
|
25
|
+
results = await flow.execute_and_collect([
|
26
|
+
{"navigate": "/dashboard"},
|
27
|
+
{"wait_for_selector": "body"},
|
28
|
+
{"screenshot": "measurement"}
|
29
|
+
])
|
30
|
+
|
31
|
+
# Extract element data
|
32
|
+
elements = results.get('comprehensive_data', {}).get('dom_analysis', {}).get('elements', [])
|
33
|
+
|
34
|
+
# Find target element
|
35
|
+
matching = [el for el in elements if el.get('id') == 'main-panel']
|
36
|
+
|
37
|
+
if not matching:
|
38
|
+
print("⚠️ Element #main-panel not found")
|
39
|
+
return
|
40
|
+
|
41
|
+
element = matching[0]
|
42
|
+
bbox = element.get('visual_context', {}).get('bounding_box', {})
|
43
|
+
computed = element.get('computedStyles', {})
|
44
|
+
|
45
|
+
# Display measurements (surgical precision)
|
46
|
+
print(f"\n#main-panel")
|
47
|
+
print(f" 📐 Rendered: {bbox.get('width', 0):.0f}w × {bbox.get('height', 0):.0f}h")
|
48
|
+
print(f" 📍 Position: x={bbox.get('x', 0):.0f}, y={bbox.get('y', 0):.0f}")
|
49
|
+
print(f" 🎨 Display: {computed.get('display', 'N/A')}")
|
50
|
+
print(f" 📦 CSS Width: {computed.get('width', 'N/A')}")
|
51
|
+
|
52
|
+
if computed.get('flex'):
|
53
|
+
print(f" 🔧 Flex: {computed.get('flex')}")
|
54
|
+
|
55
|
+
print("\n✅ Measurement complete!")
|
56
|
+
|
57
|
+
async def measure_multiple_elements():
|
58
|
+
"""Example: Measure multiple elements at once"""
|
59
|
+
|
60
|
+
print("\n📏 Multiple Element Measurement Example\n")
|
61
|
+
|
62
|
+
flow = CursorFlow(
|
63
|
+
base_url="http://localhost:3000",
|
64
|
+
log_config={'source': 'local', 'paths': []},
|
65
|
+
browser_config={'headless': True}
|
66
|
+
)
|
67
|
+
|
68
|
+
results = await flow.execute_and_collect([
|
69
|
+
{"navigate": "/layout"},
|
70
|
+
{"wait_for_selector": "body"},
|
71
|
+
{"screenshot": "layout"}
|
72
|
+
])
|
73
|
+
|
74
|
+
# Extract elements
|
75
|
+
elements = results.get('comprehensive_data', {}).get('dom_analysis', {}).get('elements', [])
|
76
|
+
|
77
|
+
# Measure multiple panels
|
78
|
+
selectors = ["#panel1", "#panel2", "#panel3"]
|
79
|
+
|
80
|
+
print("📊 Measuring layout panels:\n")
|
81
|
+
|
82
|
+
for selector in selectors:
|
83
|
+
matching = [el for el in elements if el.get('id') == selector[1:]]
|
84
|
+
|
85
|
+
if matching:
|
86
|
+
element = matching[0]
|
87
|
+
bbox = element.get('visual_context', {}).get('bounding_box', {})
|
88
|
+
computed = element.get('computedStyles', {})
|
89
|
+
|
90
|
+
print(f"{selector}")
|
91
|
+
print(f" 📐 Rendered: {bbox.get('width', 0):.0f}w × {bbox.get('height', 0):.0f}h")
|
92
|
+
print(f" 📦 CSS Width: {computed.get('width', 'N/A')}")
|
93
|
+
|
94
|
+
if computed.get('flex'):
|
95
|
+
print(f" 🔧 Flex: {computed.get('flex')}")
|
96
|
+
if computed.get('flexBasis') != 'auto':
|
97
|
+
print(f" 📏 Flex Base: {computed.get('flexBasis')}")
|
98
|
+
|
99
|
+
print()
|
100
|
+
|
101
|
+
async def measure_before_and_after():
|
102
|
+
"""Example: Measure before CSS changes, apply changes, measure after"""
|
103
|
+
|
104
|
+
print("\n📏 Before/After Measurement Example\n")
|
105
|
+
|
106
|
+
flow = CursorFlow(
|
107
|
+
base_url="http://localhost:3000",
|
108
|
+
log_config={'source': 'local', 'paths': []},
|
109
|
+
browser_config={'headless': True}
|
110
|
+
)
|
111
|
+
|
112
|
+
# BEFORE measurement
|
113
|
+
print("📐 BEFORE CSS changes:")
|
114
|
+
before_results = await flow.execute_and_collect([
|
115
|
+
{"navigate": "/component"},
|
116
|
+
{"wait_for_selector": "body"},
|
117
|
+
{"screenshot": "before"}
|
118
|
+
])
|
119
|
+
|
120
|
+
before_elements = before_results.get('comprehensive_data', {}).get('dom_analysis', {}).get('elements', [])
|
121
|
+
before_element = [el for el in before_elements if el.get('id') == 'container'][0]
|
122
|
+
before_bbox = before_element.get('visual_context', {}).get('bounding_box', {})
|
123
|
+
|
124
|
+
print(f" Container: {before_bbox.get('width', 0):.0f}w × {before_bbox.get('height', 0):.0f}h")
|
125
|
+
|
126
|
+
# --- User would make CSS changes here in actual workflow ---
|
127
|
+
print("\n🔧 (Apply CSS changes: .container { padding: 2rem; gap: 1.5rem; })")
|
128
|
+
print(" (In real workflow, you'd modify CSS files here)")
|
129
|
+
|
130
|
+
# AFTER measurement (simulated with new test)
|
131
|
+
print("\n📐 AFTER CSS changes:")
|
132
|
+
after_results = await flow.execute_and_collect([
|
133
|
+
{"navigate": "/component"},
|
134
|
+
{"wait_for_selector": "body"},
|
135
|
+
{"screenshot": "after"}
|
136
|
+
])
|
137
|
+
|
138
|
+
after_elements = after_results.get('comprehensive_data', {}).get('dom_analysis', {}).get('elements', [])
|
139
|
+
after_element = [el for el in after_elements if el.get('id') == 'container'][0]
|
140
|
+
after_bbox = after_element.get('visual_context', {}).get('bounding_box', {})
|
141
|
+
|
142
|
+
print(f" Container: {after_bbox.get('width', 0):.0f}w × {after_bbox.get('height', 0):.0f}h")
|
143
|
+
|
144
|
+
# Compare
|
145
|
+
width_diff = after_bbox.get('width', 0) - before_bbox.get('width', 0)
|
146
|
+
height_diff = after_bbox.get('height', 0) - before_bbox.get('height', 0)
|
147
|
+
|
148
|
+
print(f"\n📊 Changes:")
|
149
|
+
print(f" Width: {'+' if width_diff > 0 else ''}{width_diff:.0f}px")
|
150
|
+
print(f" Height: {'+' if height_diff > 0 else ''}{height_diff:.0f}px")
|
151
|
+
print("\n✅ CSS changes verified!")
|
152
|
+
|
153
|
+
async def measure_responsive_breakpoints():
|
154
|
+
"""Example: Measure element dimensions across responsive breakpoints"""
|
155
|
+
|
156
|
+
print("\n📏 Responsive Breakpoint Measurement Example\n")
|
157
|
+
|
158
|
+
viewports = [
|
159
|
+
{"width": 375, "height": 667, "name": "mobile"},
|
160
|
+
{"width": 768, "height": 1024, "name": "tablet"},
|
161
|
+
{"width": 1440, "height": 900, "name": "desktop"}
|
162
|
+
]
|
163
|
+
|
164
|
+
flow = CursorFlow(
|
165
|
+
base_url="http://localhost:3000",
|
166
|
+
log_config={'source': 'local', 'paths': []},
|
167
|
+
browser_config={'headless': True}
|
168
|
+
)
|
169
|
+
|
170
|
+
print("📱 Measuring across breakpoints:\n")
|
171
|
+
|
172
|
+
for viewport in viewports:
|
173
|
+
# Set viewport size
|
174
|
+
flow.browser_config['viewport'] = {"width": viewport['width'], "height": viewport['height']}
|
175
|
+
|
176
|
+
results = await flow.execute_and_collect([
|
177
|
+
{"navigate": "/responsive"},
|
178
|
+
{"wait_for_selector": "body"},
|
179
|
+
{"screenshot": f"responsive-{viewport['name']}"}
|
180
|
+
])
|
181
|
+
|
182
|
+
elements = results.get('comprehensive_data', {}).get('dom_analysis', {}).get('elements', [])
|
183
|
+
matching = [el for el in elements if el.get('id') == 'main-content']
|
184
|
+
|
185
|
+
if matching:
|
186
|
+
element = matching[0]
|
187
|
+
bbox = element.get('visual_context', {}).get('bounding_box', {})
|
188
|
+
computed = element.get('computedStyles', {})
|
189
|
+
|
190
|
+
print(f"{viewport['name'].upper()} ({viewport['width']}x{viewport['height']}):")
|
191
|
+
print(f" #main-content: {bbox.get('width', 0):.0f}w × {bbox.get('height', 0):.0f}h")
|
192
|
+
print(f" Display: {computed.get('display', 'N/A')}")
|
193
|
+
print()
|
194
|
+
|
195
|
+
async def measure_with_verbose_css():
|
196
|
+
"""Example: Get all computed CSS for detailed analysis"""
|
197
|
+
|
198
|
+
print("\n📏 Verbose Measurement (All CSS Properties)\n")
|
199
|
+
|
200
|
+
flow = CursorFlow(
|
201
|
+
base_url="http://localhost:3000",
|
202
|
+
log_config={'source': 'local', 'paths': []},
|
203
|
+
browser_config={'headless': True}
|
204
|
+
)
|
205
|
+
|
206
|
+
results = await flow.execute_and_collect([
|
207
|
+
{"navigate": "/element"},
|
208
|
+
{"wait_for_selector": "body"},
|
209
|
+
{"screenshot": "element"}
|
210
|
+
])
|
211
|
+
|
212
|
+
elements = results.get('comprehensive_data', {}).get('dom_analysis', {}).get('elements', [])
|
213
|
+
|
214
|
+
if elements:
|
215
|
+
element = elements[0]
|
216
|
+
bbox = element.get('visual_context', {}).get('bounding_box', {})
|
217
|
+
computed = element.get('computedStyles', {})
|
218
|
+
|
219
|
+
print(f"Element: {element.get('tagName', 'unknown')}")
|
220
|
+
print(f" 📐 Rendered: {bbox.get('width', 0):.0f}w × {bbox.get('height', 0):.0f}h\n")
|
221
|
+
print(f" 🎨 All {len(computed)} Computed CSS Properties:")
|
222
|
+
|
223
|
+
for prop, value in sorted(computed.items())[:15]:
|
224
|
+
print(f" {prop}: {value}")
|
225
|
+
|
226
|
+
print(f"\n 💡 {len(computed) - 15} more properties available")
|
227
|
+
|
228
|
+
if __name__ == '__main__':
|
229
|
+
print("=" * 60)
|
230
|
+
print("CursorFlow Element Measurement Examples")
|
231
|
+
print("=" * 60)
|
232
|
+
|
233
|
+
# Run examples
|
234
|
+
asyncio.run(measure_single_element())
|
235
|
+
asyncio.run(measure_multiple_elements())
|
236
|
+
asyncio.run(measure_before_and_after())
|
237
|
+
asyncio.run(measure_responsive_breakpoints())
|
238
|
+
asyncio.run(measure_with_verbose_css())
|
239
|
+
|
240
|
+
print("\n" + "=" * 60)
|
241
|
+
print("✅ All examples completed!")
|
242
|
+
print("=" * 60)
|
243
|
+
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "cursorflow"
|
7
|
-
version = "2.3.
|
7
|
+
version = "2.3.3"
|
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.3.
|
75
|
+
python_version = "2.3.3"
|
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
|