cursorflow 1.2.0__py3-none-any.whl → 1.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cursorflow/cli.py +212 -0
- cursorflow/core/browser_controller.py +659 -7
- cursorflow/core/cursor_integration.py +788 -0
- cursorflow/core/cursorflow.py +151 -0
- cursorflow/core/mockup_comparator.py +1316 -0
- cursorflow-1.3.0.dist-info/METADATA +226 -0
- {cursorflow-1.2.0.dist-info → cursorflow-1.3.0.dist-info}/RECORD +11 -9
- cursorflow-1.3.0.dist-info/licenses/LICENSE +21 -0
- cursorflow-1.2.0.dist-info/METADATA +0 -444
- {cursorflow-1.2.0.dist-info → cursorflow-1.3.0.dist-info}/WHEEL +0 -0
- {cursorflow-1.2.0.dist-info → cursorflow-1.3.0.dist-info}/entry_points.txt +0 -0
- {cursorflow-1.2.0.dist-info → cursorflow-1.3.0.dist-info}/top_level.txt +0 -0
cursorflow/cli.py
CHANGED
@@ -124,6 +124,218 @@ def test(test_name, base_url, actions, logs, config, verbose):
|
|
124
124
|
console.print(traceback.format_exc())
|
125
125
|
raise
|
126
126
|
|
127
|
+
@main.command()
|
128
|
+
@click.argument('mockup_url', required=True)
|
129
|
+
@click.option('--base-url', '-u', default='http://localhost:3000',
|
130
|
+
help='Base URL of work-in-progress implementation')
|
131
|
+
@click.option('--mockup-actions', '-ma',
|
132
|
+
help='JSON file with actions to perform on mockup, or inline JSON string')
|
133
|
+
@click.option('--implementation-actions', '-ia',
|
134
|
+
help='JSON file with actions to perform on implementation, or inline JSON string')
|
135
|
+
@click.option('--viewports', '-v',
|
136
|
+
help='JSON array of viewports to test: [{"width": 1440, "height": 900, "name": "desktop"}]')
|
137
|
+
@click.option('--diff-threshold', '-t', type=float, default=0.1,
|
138
|
+
help='Visual difference threshold (0.0-1.0)')
|
139
|
+
@click.option('--output', '-o', default='mockup_comparison_results.json',
|
140
|
+
help='Output file for comparison results')
|
141
|
+
@click.option('--verbose', is_flag=True,
|
142
|
+
help='Verbose output')
|
143
|
+
def compare_mockup(mockup_url, base_url, mockup_actions, implementation_actions, viewports, diff_threshold, output, verbose):
|
144
|
+
"""Compare mockup design to work-in-progress implementation"""
|
145
|
+
|
146
|
+
console.print(f"🎨 Comparing mockup [blue]{mockup_url}[/blue] to implementation [blue]{base_url}[/blue]")
|
147
|
+
|
148
|
+
# Parse actions
|
149
|
+
def parse_actions(actions_input):
|
150
|
+
if not actions_input:
|
151
|
+
return None
|
152
|
+
|
153
|
+
if actions_input.startswith('[') or actions_input.startswith('{'):
|
154
|
+
return json.loads(actions_input)
|
155
|
+
else:
|
156
|
+
with open(actions_input, 'r') as f:
|
157
|
+
return json.load(f)
|
158
|
+
|
159
|
+
try:
|
160
|
+
mockup_actions_parsed = parse_actions(mockup_actions)
|
161
|
+
implementation_actions_parsed = parse_actions(implementation_actions)
|
162
|
+
|
163
|
+
# Parse viewports
|
164
|
+
viewports_parsed = None
|
165
|
+
if viewports:
|
166
|
+
if viewports.startswith('['):
|
167
|
+
viewports_parsed = json.loads(viewports)
|
168
|
+
else:
|
169
|
+
with open(viewports, 'r') as f:
|
170
|
+
viewports_parsed = json.load(f)
|
171
|
+
|
172
|
+
# Build comparison config
|
173
|
+
comparison_config = {
|
174
|
+
"diff_threshold": diff_threshold
|
175
|
+
}
|
176
|
+
if viewports_parsed:
|
177
|
+
comparison_config["viewports"] = viewports_parsed
|
178
|
+
|
179
|
+
except Exception as e:
|
180
|
+
console.print(f"[red]Error parsing input parameters: {e}[/red]")
|
181
|
+
return
|
182
|
+
|
183
|
+
# Initialize CursorFlow
|
184
|
+
try:
|
185
|
+
from .core.cursorflow import CursorFlow
|
186
|
+
flow = CursorFlow(
|
187
|
+
base_url=base_url,
|
188
|
+
log_config={'source': 'local', 'paths': ['logs/app.log']},
|
189
|
+
browser_config={'headless': True}
|
190
|
+
)
|
191
|
+
except Exception as e:
|
192
|
+
console.print(f"[red]Error initializing CursorFlow: {e}[/red]")
|
193
|
+
return
|
194
|
+
|
195
|
+
# Execute mockup comparison
|
196
|
+
try:
|
197
|
+
console.print("🚀 Starting mockup comparison...")
|
198
|
+
results = asyncio.run(flow.compare_mockup_to_implementation(
|
199
|
+
mockup_url=mockup_url,
|
200
|
+
mockup_actions=mockup_actions_parsed,
|
201
|
+
implementation_actions=implementation_actions_parsed,
|
202
|
+
comparison_config=comparison_config
|
203
|
+
))
|
204
|
+
|
205
|
+
if "error" in results:
|
206
|
+
console.print(f"[red]❌ Comparison failed: {results['error']}[/red]")
|
207
|
+
return
|
208
|
+
|
209
|
+
# Display results summary
|
210
|
+
summary = results.get('summary', {})
|
211
|
+
console.print(f"✅ Comparison completed: {results.get('comparison_id', 'unknown')}")
|
212
|
+
console.print(f"📊 Average similarity: [bold]{summary.get('average_similarity', 0)}%[/bold]")
|
213
|
+
console.print(f"📱 Viewports tested: {summary.get('viewports_tested', 0)}")
|
214
|
+
|
215
|
+
# Show recommendations
|
216
|
+
recommendations = results.get('recommendations', [])
|
217
|
+
if recommendations:
|
218
|
+
console.print(f"💡 Recommendations: {len(recommendations)} improvements suggested")
|
219
|
+
for i, rec in enumerate(recommendations[:3]): # Show first 3
|
220
|
+
console.print(f" {i+1}. {rec.get('description', 'No description')}")
|
221
|
+
|
222
|
+
# Save results
|
223
|
+
with open(output, 'w') as f:
|
224
|
+
json.dump(results, f, indent=2, default=str)
|
225
|
+
|
226
|
+
console.print(f"💾 Full results saved to: [cyan]{output}[/cyan]")
|
227
|
+
console.print(f"📁 Visual diffs stored in: [cyan].cursorflow/artifacts/[/cyan]")
|
228
|
+
|
229
|
+
except Exception as e:
|
230
|
+
console.print(f"[red]❌ Comparison failed: {e}[/red]")
|
231
|
+
if verbose:
|
232
|
+
import traceback
|
233
|
+
console.print(traceback.format_exc())
|
234
|
+
raise
|
235
|
+
|
236
|
+
@main.command()
|
237
|
+
@click.argument('mockup_url', required=True)
|
238
|
+
@click.option('--base-url', '-u', default='http://localhost:3000',
|
239
|
+
help='Base URL of work-in-progress implementation')
|
240
|
+
@click.option('--css-improvements', '-c', required=True,
|
241
|
+
help='JSON file with CSS improvements to test, or inline JSON string')
|
242
|
+
@click.option('--base-actions', '-a',
|
243
|
+
help='JSON file with base actions to perform before each test')
|
244
|
+
@click.option('--diff-threshold', '-t', type=float, default=0.1,
|
245
|
+
help='Visual difference threshold (0.0-1.0)')
|
246
|
+
@click.option('--output', '-o', default='mockup_iteration_results.json',
|
247
|
+
help='Output file for iteration results')
|
248
|
+
@click.option('--verbose', is_flag=True,
|
249
|
+
help='Verbose output')
|
250
|
+
def iterate_mockup(mockup_url, base_url, css_improvements, base_actions, diff_threshold, output, verbose):
|
251
|
+
"""Iteratively improve implementation to match mockup design"""
|
252
|
+
|
253
|
+
console.print(f"🔄 Iterating on [blue]{base_url}[/blue] to match [blue]{mockup_url}[/blue]")
|
254
|
+
|
255
|
+
# Parse CSS improvements
|
256
|
+
def parse_json_input(input_str):
|
257
|
+
if not input_str:
|
258
|
+
return None
|
259
|
+
|
260
|
+
if input_str.startswith('[') or input_str.startswith('{'):
|
261
|
+
return json.loads(input_str)
|
262
|
+
else:
|
263
|
+
with open(input_str, 'r') as f:
|
264
|
+
return json.load(f)
|
265
|
+
|
266
|
+
try:
|
267
|
+
css_improvements_parsed = parse_json_input(css_improvements)
|
268
|
+
base_actions_parsed = parse_json_input(base_actions)
|
269
|
+
|
270
|
+
if not css_improvements_parsed:
|
271
|
+
console.print("[red]Error: CSS improvements are required[/red]")
|
272
|
+
return
|
273
|
+
|
274
|
+
comparison_config = {"diff_threshold": diff_threshold}
|
275
|
+
|
276
|
+
except Exception as e:
|
277
|
+
console.print(f"[red]Error parsing input parameters: {e}[/red]")
|
278
|
+
return
|
279
|
+
|
280
|
+
# Initialize CursorFlow
|
281
|
+
try:
|
282
|
+
from .core.cursorflow import CursorFlow
|
283
|
+
flow = CursorFlow(
|
284
|
+
base_url=base_url,
|
285
|
+
log_config={'source': 'local', 'paths': ['logs/app.log']},
|
286
|
+
browser_config={'headless': True}
|
287
|
+
)
|
288
|
+
except Exception as e:
|
289
|
+
console.print(f"[red]Error initializing CursorFlow: {e}[/red]")
|
290
|
+
return
|
291
|
+
|
292
|
+
# Execute iterative mockup matching
|
293
|
+
try:
|
294
|
+
console.print(f"🚀 Starting iterative matching with {len(css_improvements_parsed)} CSS improvements...")
|
295
|
+
results = asyncio.run(flow.iterative_mockup_matching(
|
296
|
+
mockup_url=mockup_url,
|
297
|
+
css_improvements=css_improvements_parsed,
|
298
|
+
base_actions=base_actions_parsed,
|
299
|
+
comparison_config=comparison_config
|
300
|
+
))
|
301
|
+
|
302
|
+
if "error" in results:
|
303
|
+
console.print(f"[red]❌ Iteration failed: {results['error']}[/red]")
|
304
|
+
return
|
305
|
+
|
306
|
+
# Display results summary
|
307
|
+
summary = results.get('summary', {})
|
308
|
+
console.print(f"✅ Iteration completed: {results.get('session_id', 'unknown')}")
|
309
|
+
console.print(f"📊 Total improvement: [bold]{summary.get('total_improvement', 0)}%[/bold]")
|
310
|
+
console.print(f"🔄 Successful iterations: {summary.get('successful_iterations', 0)}/{summary.get('total_iterations', 0)}")
|
311
|
+
|
312
|
+
# Show best iteration
|
313
|
+
best_iteration = results.get('best_iteration')
|
314
|
+
if best_iteration:
|
315
|
+
console.print(f"🏆 Best iteration: {best_iteration.get('css_change', {}).get('name', 'unnamed')}")
|
316
|
+
console.print(f" Similarity achieved: {best_iteration.get('similarity_achieved', 0)}%")
|
317
|
+
|
318
|
+
# Show final recommendations
|
319
|
+
recommendations = results.get('final_recommendations', [])
|
320
|
+
if recommendations:
|
321
|
+
console.print(f"💡 Final recommendations: {len(recommendations)} actions suggested")
|
322
|
+
for i, rec in enumerate(recommendations[:3]):
|
323
|
+
console.print(f" {i+1}. {rec.get('description', 'No description')}")
|
324
|
+
|
325
|
+
# Save results
|
326
|
+
with open(output, 'w') as f:
|
327
|
+
json.dump(results, f, indent=2, default=str)
|
328
|
+
|
329
|
+
console.print(f"💾 Full results saved to: [cyan]{output}[/cyan]")
|
330
|
+
console.print(f"📁 Iteration progress stored in: [cyan].cursorflow/artifacts/[/cyan]")
|
331
|
+
|
332
|
+
except Exception as e:
|
333
|
+
console.print(f"[red]❌ Iteration failed: {e}[/red]")
|
334
|
+
if verbose:
|
335
|
+
import traceback
|
336
|
+
console.print(traceback.format_exc())
|
337
|
+
raise
|
338
|
+
|
127
339
|
@main.command()
|
128
340
|
@click.option('--project-path', '-p', default='.',
|
129
341
|
help='Project directory path')
|