cursorflow 2.3.2__tar.gz → 2.4.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.
- {cursorflow-2.3.2 → cursorflow-2.4.0}/MANIFEST.in +2 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/PKG-INFO +1 -1
- {cursorflow-2.3.2 → cursorflow-2.4.0}/cursorflow/cli.py +2 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/cursorflow/core/browser_controller.py +39 -48
- {cursorflow-2.3.2 → cursorflow-2.4.0}/cursorflow/core/cursorflow.py +1 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/cursorflow.egg-info/SOURCES.txt +2 -0
- cursorflow-2.4.0/examples/element_inspection_example.py +183 -0
- cursorflow-2.4.0/examples/element_measurement_example.py +243 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/pyproject.toml +2 -2
- {cursorflow-2.3.2 → cursorflow-2.4.0}/LICENSE +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/README.md +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/cursorflow/__init__.py +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/cursorflow/auto_init.py +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/cursorflow/auto_updater.py +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/cursorflow/core/action_validator.py +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/cursorflow/core/agent.py +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/cursorflow/core/auth_handler.py +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/cursorflow/core/browser_engine.py +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/cursorflow/core/config_validator.py +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/cursorflow/core/css_iterator.py +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/cursorflow/core/cursor_integration.py +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/cursorflow/core/error_context_collector.py +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/cursorflow/core/error_correlator.py +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/cursorflow/core/event_correlator.py +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/cursorflow/core/file_change_monitor.py +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/cursorflow/core/hmr_detector.py +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/cursorflow/core/log_collector.py +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/cursorflow/core/log_monitor.py +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/cursorflow/core/mockup_comparator.py +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/cursorflow/core/persistent_session.py +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/cursorflow/core/report_generator.py +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/cursorflow/core/trace_manager.py +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/cursorflow/install_cursorflow_rules.py +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/cursorflow/log_sources/local_file.py +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/cursorflow/log_sources/ssh_remote.py +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/cursorflow/post_install.py +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/cursorflow/rules/__init__.py +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/cursorflow/rules/cursorflow-installation.mdc +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/cursorflow/rules/cursorflow-usage.mdc +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/cursorflow/updater.py +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/docs/user/USAGE_GUIDE.md +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/examples/comprehensive_screenshot_example.py +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/examples/enhanced_screenshot_example.py +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/examples/hot_reload_css_iteration.py +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/examples/mockup_comparison_example.py +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/examples/opensas_example.py +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/examples/react_example.py +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/examples/responsive_testing_example.py +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/examples/v2_comprehensive_demo.py +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/setup.cfg +0 -0
- {cursorflow-2.3.2 → cursorflow-2.4.0}/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
|
+
Version: 2.4.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
|
@@ -801,6 +801,7 @@ def inspect(base_url, path, selector, verbose):
|
|
801
801
|
)
|
802
802
|
|
803
803
|
# Comprehensive inspection with full data capture
|
804
|
+
# v2.1: Captures ALL visible elements automatically
|
804
805
|
console.print("📸 Capturing comprehensive page data...")
|
805
806
|
results = asyncio.run(flow.execute_and_collect([
|
806
807
|
{"navigate": path},
|
@@ -996,6 +997,7 @@ def measure(base_url, path, selector, verbose):
|
|
996
997
|
selectors_list = list(selector)
|
997
998
|
|
998
999
|
# Execute with screenshot to get comprehensive data
|
1000
|
+
# v2.1: Captures ALL visible elements automatically
|
999
1001
|
results = asyncio.run(flow.execute_and_collect([
|
1000
1002
|
{"navigate": path},
|
1001
1003
|
{"wait_for_selector": "body"},
|
@@ -550,7 +550,7 @@ class BrowserController:
|
|
550
550
|
}
|
551
551
|
|
552
552
|
if capture_comprehensive_data:
|
553
|
-
# Capture comprehensive page analysis
|
553
|
+
# Capture comprehensive page analysis (ALL visible elements)
|
554
554
|
comprehensive_data = await self._capture_comprehensive_page_analysis()
|
555
555
|
|
556
556
|
# Save comprehensive data alongside screenshot
|
@@ -1122,9 +1122,13 @@ class BrowserController:
|
|
1122
1122
|
return failed
|
1123
1123
|
|
1124
1124
|
async def _capture_comprehensive_page_analysis(self) -> Dict[str, Any]:
|
1125
|
-
"""
|
1125
|
+
"""
|
1126
|
+
Enhanced comprehensive page analysis with v2.0 features: fonts, animations, resources, storage
|
1127
|
+
|
1128
|
+
v2.1: Now captures ALL visible elements - truly comprehensive, no hardcoded limits.
|
1129
|
+
"""
|
1126
1130
|
try:
|
1127
|
-
# Capture DOM analysis
|
1131
|
+
# Capture DOM analysis (ALL visible elements)
|
1128
1132
|
dom_analysis = await self._capture_dom_analysis()
|
1129
1133
|
|
1130
1134
|
# Capture current network state
|
@@ -1684,7 +1688,12 @@ class BrowserController:
|
|
1684
1688
|
}
|
1685
1689
|
|
1686
1690
|
async def _capture_dom_analysis(self) -> Dict[str, Any]:
|
1687
|
-
"""
|
1691
|
+
"""
|
1692
|
+
Enhanced DOM analysis with multi-selector strategies and accessibility data (v2.0)
|
1693
|
+
|
1694
|
+
Captures ALL visible elements on the page - truly comprehensive analysis.
|
1695
|
+
No hardcoded selector limits, no artificial filtering.
|
1696
|
+
"""
|
1688
1697
|
try:
|
1689
1698
|
dom_analysis = await self.page.evaluate("""
|
1690
1699
|
() => {
|
@@ -2022,46 +2031,29 @@ class BrowserController:
|
|
2022
2031
|
};
|
2023
2032
|
}
|
2024
2033
|
|
2025
|
-
// Get
|
2034
|
+
// Get ALL visible elements - truly comprehensive analysis
|
2026
2035
|
const elements = [];
|
2027
|
-
const selectors = [
|
2028
|
-
// Structural elements
|
2029
|
-
'body', 'main', 'header', 'nav', 'aside', 'footer', 'section', 'article',
|
2030
|
-
// Common containers
|
2031
|
-
'.container', '.wrapper', '.content', '.sidebar', '.header', '.footer',
|
2032
|
-
'.navbar', '.nav', '.menu', '.main', '.page', '.app', '.layout',
|
2033
|
-
// Interactive elements
|
2034
|
-
'button', '.btn', '.button', 'a', '.link', 'input', 'form', '.form',
|
2035
|
-
'select', 'textarea', '.input', '.field',
|
2036
|
-
// Content elements
|
2037
|
-
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', '.title', '.heading',
|
2038
|
-
'.text', '.content', '.description',
|
2039
|
-
// Layout elements
|
2040
|
-
'.row', '.col', '.column', '.grid', '.flex', '.card', '.panel',
|
2041
|
-
'.box', '.item', '.component',
|
2042
|
-
// Common UI components
|
2043
|
-
'.modal', '.dropdown', '.tooltip', '.alert', '.badge', '.tab',
|
2044
|
-
'.table', '.list', '.menu-item'
|
2045
|
-
];
|
2046
2036
|
|
2047
|
-
|
2037
|
+
// Query EVERY element on the page (true comprehensive capture)
|
2038
|
+
const allElements = document.querySelectorAll('*');
|
2039
|
+
|
2040
|
+
allElements.forEach((element, globalIndex) => {
|
2048
2041
|
try {
|
2049
|
-
|
2050
|
-
|
2051
|
-
|
2042
|
+
const computedStyles = getComputedStylesDetailed(element);
|
2043
|
+
const visualContext = getVisualContext(element, computedStyles);
|
2044
|
+
|
2045
|
+
// Only include elements with meaningful size OR important semantic meaning
|
2046
|
+
// This filters out invisible helper elements but keeps everything visible
|
2047
|
+
if (visualContext.bounding_box.width > 0 && visualContext.bounding_box.height > 0 ||
|
2048
|
+
['HTML', 'BODY', 'HEAD', 'HEADER', 'NAV', 'MAIN', 'ASIDE', 'FOOTER'].includes(element.tagName)) {
|
2052
2049
|
|
2053
|
-
|
2054
|
-
|
2055
|
-
|
2056
|
-
|
2057
|
-
|
2058
|
-
|
2059
|
-
|
2060
|
-
index: index,
|
2061
|
-
tagName: element.tagName.toLowerCase(),
|
2062
|
-
id: element.id || null,
|
2063
|
-
className: element.className || null,
|
2064
|
-
textContent: element.textContent ? element.textContent.trim().substring(0, 200) : null,
|
2050
|
+
elements.push({
|
2051
|
+
// Basic element info
|
2052
|
+
index: globalIndex,
|
2053
|
+
tagName: element.tagName.toLowerCase(),
|
2054
|
+
id: element.id || null,
|
2055
|
+
className: element.className || null,
|
2056
|
+
textContent: element.textContent ? element.textContent.trim().substring(0, 200) : null,
|
2065
2057
|
|
2066
2058
|
// v2.0 Enhancement: Multiple selector strategies
|
2067
2059
|
selectors: generateMultipleSelectors(element),
|
@@ -2084,14 +2076,13 @@ class BrowserController:
|
|
2084
2076
|
return attrs;
|
2085
2077
|
}, {}),
|
2086
2078
|
|
2087
|
-
|
2088
|
-
|
2089
|
-
|
2090
|
-
|
2091
|
-
|
2092
|
-
});
|
2079
|
+
// Element hierarchy info
|
2080
|
+
childrenCount: element.children.length,
|
2081
|
+
parentTagName: element.parentElement ? element.parentElement.tagName.toLowerCase() : null
|
2082
|
+
});
|
2083
|
+
}
|
2093
2084
|
} catch (e) {
|
2094
|
-
|
2085
|
+
// Skip elements that fail to analyze (e.g., detached nodes, iframes)
|
2095
2086
|
}
|
2096
2087
|
});
|
2097
2088
|
|
@@ -2166,7 +2157,7 @@ class BrowserController:
|
|
2166
2157
|
elements: elements,
|
2167
2158
|
totalElements: elements.length,
|
2168
2159
|
captureTimestamp: Date.now(),
|
2169
|
-
analysisVersion: "2.
|
2160
|
+
analysisVersion: "2.1" // v2.1: Truly comprehensive (ALL elements)
|
2170
2161
|
};
|
2171
2162
|
}
|
2172
2163
|
""")
|
@@ -736,6 +736,7 @@ class CursorFlow:
|
|
736
736
|
name = "screenshot"
|
737
737
|
options = None
|
738
738
|
|
739
|
+
# v2.1: No need for additional_selectors - we capture ALL visible elements now
|
739
740
|
screenshot_data = await self.browser.screenshot(name, options)
|
740
741
|
self.artifacts["screenshots"].append(screenshot_data)
|
741
742
|
|
@@ -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.
|
7
|
+
version = "2.4.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.
|
75
|
+
python_version = "2.4.0"
|
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
|