Upload app.py
Browse files
app.py
CHANGED
|
@@ -360,6 +360,35 @@ async def extract_tokens(pages_data, progress=gr.Progress()):
|
|
| 360 |
except Exception as e:
|
| 361 |
state.log(f" β οΈ Firecrawl extraction skipped: {str(e)}")
|
| 362 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 363 |
progress(0.95, desc="π Preparing results...")
|
| 364 |
|
| 365 |
# Format results for Stage 1 UI
|
|
@@ -399,7 +428,10 @@ async def extract_tokens(pages_data, progress=gr.Progress()):
|
|
| 399 |
"value": c.value,
|
| 400 |
"frequency": c.frequency,
|
| 401 |
"contexts": c.contexts[:3] if c.contexts else [],
|
|
|
|
|
|
|
| 402 |
"contrast_white": c.contrast_white,
|
|
|
|
| 403 |
}
|
| 404 |
|
| 405 |
# Convert spacing tokens to dict format
|
|
@@ -449,10 +481,16 @@ async def extract_tokens(pages_data, progress=gr.Progress()):
|
|
| 449 |
|
| 450 |
state.log(" β
Typography preview generated")
|
| 451 |
state.log(" β
Colors AS-IS preview generated (no ramps)")
|
|
|
|
| 452 |
state.log(" β
Spacing AS-IS preview generated")
|
| 453 |
state.log(" β
Radius AS-IS preview generated")
|
| 454 |
state.log(" β
Shadows AS-IS preview generated")
|
| 455 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 456 |
state.log("")
|
| 457 |
state.log("=" * 50)
|
| 458 |
state.log("β
EXTRACTION COMPLETE!")
|
|
@@ -462,6 +500,10 @@ async def extract_tokens(pages_data, progress=gr.Progress()):
|
|
| 462 |
state.log(f" β’ {len(state.desktop_normalized.spacing)} spacing values")
|
| 463 |
state.log(f" β’ {len(state.desktop_normalized.radius)} radius values")
|
| 464 |
state.log(f" β’ {len(state.desktop_normalized.shadows)} shadow values")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 465 |
state.log("=" * 50)
|
| 466 |
|
| 467 |
progress(1.0, desc="β
Complete!")
|
|
@@ -475,12 +517,14 @@ async def extract_tokens(pages_data, progress=gr.Progress()):
|
|
| 475 |
|
| 476 |
**Primary Font:** {primary_font}
|
| 477 |
|
| 478 |
-
**
|
|
|
|
|
|
|
| 479 |
|
| 480 |
**Next:** Review the tokens below. Accept or reject, then proceed to Stage 2.
|
| 481 |
"""
|
| 482 |
|
| 483 |
-
# Return all AS-IS previews
|
| 484 |
return (
|
| 485 |
status,
|
| 486 |
state.get_logs(),
|
|
@@ -488,6 +532,7 @@ async def extract_tokens(pages_data, progress=gr.Progress()):
|
|
| 488 |
mobile_data,
|
| 489 |
typography_preview_html,
|
| 490 |
colors_asis_preview_html,
|
|
|
|
| 491 |
spacing_asis_preview_html,
|
| 492 |
radius_asis_preview_html,
|
| 493 |
shadows_asis_preview_html,
|
|
@@ -497,7 +542,7 @@ async def extract_tokens(pages_data, progress=gr.Progress()):
|
|
| 497 |
import traceback
|
| 498 |
state.log(f"β Error: {str(e)}")
|
| 499 |
state.log(traceback.format_exc())
|
| 500 |
-
return f"β Error: {str(e)}", state.get_logs(), None, None, "", "", "", "", ""
|
| 501 |
|
| 502 |
|
| 503 |
def format_tokens_for_display(normalized) -> dict:
|
|
@@ -600,7 +645,7 @@ async def run_stage2_analysis(competitors_str: str = "", progress=gr.Progress())
|
|
| 600 |
desktop_dict = normalized_to_dict(state.desktop_normalized)
|
| 601 |
mobile_dict = normalized_to_dict(state.mobile_normalized)
|
| 602 |
|
| 603 |
-
# Run multi-agent analysis
|
| 604 |
progress(0.1, desc="π Running parallel LLM analysis...")
|
| 605 |
|
| 606 |
result = await run_stage2_multi_agent(
|
|
@@ -608,6 +653,7 @@ async def run_stage2_analysis(competitors_str: str = "", progress=gr.Progress())
|
|
| 608 |
mobile_tokens=mobile_dict,
|
| 609 |
competitors=competitors,
|
| 610 |
log_callback=state.log,
|
|
|
|
| 611 |
)
|
| 612 |
|
| 613 |
progress(0.8, desc="π Processing results...")
|
|
@@ -677,10 +723,13 @@ async def run_stage2_analysis(competitors_str: str = "", progress=gr.Progress())
|
|
| 677 |
"letter_spacing": "0",
|
| 678 |
}
|
| 679 |
|
| 680 |
-
# Convert color tokens to dict format for preview
|
| 681 |
color_dict = {}
|
| 682 |
for name, c in state.desktop_normalized.colors.items():
|
| 683 |
-
color_dict[name] = {
|
|
|
|
|
|
|
|
|
|
| 684 |
|
| 685 |
typography_preview_html = generate_typography_preview_html(
|
| 686 |
typography_tokens=typo_dict,
|
|
@@ -1845,6 +1894,13 @@ def create_ui():
|
|
| 1845 |
label="Colors Preview"
|
| 1846 |
)
|
| 1847 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1848 |
with gr.Tab("π Spacing"):
|
| 1849 |
gr.Markdown("*All detected spacing values*")
|
| 1850 |
stage1_spacing_preview = gr.HTML(
|
|
@@ -2083,6 +2139,7 @@ def create_ui():
|
|
| 2083 |
inputs=[pages_table],
|
| 2084 |
outputs=[extraction_status, log_output, desktop_data, mobile_data,
|
| 2085 |
stage1_typography_preview, stage1_colors_preview,
|
|
|
|
| 2086 |
stage1_spacing_preview, stage1_radius_preview, stage1_shadows_preview],
|
| 2087 |
).then(
|
| 2088 |
fn=lambda d: (d.get("colors", []), d.get("typography", []), d.get("spacing", [])),
|
|
|
|
| 360 |
except Exception as e:
|
| 361 |
state.log(f" β οΈ Firecrawl extraction skipped: {str(e)}")
|
| 362 |
|
| 363 |
+
# === SEMANTIC COLOR ANALYSIS (Agent 1C) ===
|
| 364 |
+
progress(0.92, desc="π§ Semantic color analysis...")
|
| 365 |
+
|
| 366 |
+
semantic_result = {}
|
| 367 |
+
semantic_preview_html = ""
|
| 368 |
+
|
| 369 |
+
try:
|
| 370 |
+
from agents.semantic_analyzer import SemanticColorAnalyzer, generate_semantic_preview_html
|
| 371 |
+
|
| 372 |
+
# Create analyzer (using rule-based for now, can add LLM later)
|
| 373 |
+
semantic_analyzer = SemanticColorAnalyzer(llm_provider=None)
|
| 374 |
+
|
| 375 |
+
# Run analysis
|
| 376 |
+
semantic_result = semantic_analyzer.analyze_sync(
|
| 377 |
+
colors=state.desktop_normalized.colors,
|
| 378 |
+
log_callback=state.log
|
| 379 |
+
)
|
| 380 |
+
|
| 381 |
+
# Store in state for Stage 2
|
| 382 |
+
state.semantic_analysis = semantic_result
|
| 383 |
+
|
| 384 |
+
# Generate preview HTML
|
| 385 |
+
semantic_preview_html = generate_semantic_preview_html(semantic_result)
|
| 386 |
+
|
| 387 |
+
except Exception as e:
|
| 388 |
+
state.log(f" β οΈ Semantic analysis skipped: {str(e)}")
|
| 389 |
+
import traceback
|
| 390 |
+
state.log(traceback.format_exc())
|
| 391 |
+
|
| 392 |
progress(0.95, desc="π Preparing results...")
|
| 393 |
|
| 394 |
# Format results for Stage 1 UI
|
|
|
|
| 428 |
"value": c.value,
|
| 429 |
"frequency": c.frequency,
|
| 430 |
"contexts": c.contexts[:3] if c.contexts else [],
|
| 431 |
+
"elements": c.elements[:3] if c.elements else [],
|
| 432 |
+
"css_properties": c.css_properties[:3] if c.css_properties else [],
|
| 433 |
"contrast_white": c.contrast_white,
|
| 434 |
+
"contrast_black": getattr(c, 'contrast_black', 0),
|
| 435 |
}
|
| 436 |
|
| 437 |
# Convert spacing tokens to dict format
|
|
|
|
| 481 |
|
| 482 |
state.log(" β
Typography preview generated")
|
| 483 |
state.log(" β
Colors AS-IS preview generated (no ramps)")
|
| 484 |
+
state.log(" β
Semantic color analysis preview generated")
|
| 485 |
state.log(" β
Spacing AS-IS preview generated")
|
| 486 |
state.log(" β
Radius AS-IS preview generated")
|
| 487 |
state.log(" β
Shadows AS-IS preview generated")
|
| 488 |
|
| 489 |
+
# Get semantic summary for status
|
| 490 |
+
brand_count = len(semantic_result.get("brand", {}))
|
| 491 |
+
text_count = len(semantic_result.get("text", {}))
|
| 492 |
+
bg_count = len(semantic_result.get("background", {}))
|
| 493 |
+
|
| 494 |
state.log("")
|
| 495 |
state.log("=" * 50)
|
| 496 |
state.log("β
EXTRACTION COMPLETE!")
|
|
|
|
| 500 |
state.log(f" β’ {len(state.desktop_normalized.spacing)} spacing values")
|
| 501 |
state.log(f" β’ {len(state.desktop_normalized.radius)} radius values")
|
| 502 |
state.log(f" β’ {len(state.desktop_normalized.shadows)} shadow values")
|
| 503 |
+
state.log(f" Semantic Analysis:")
|
| 504 |
+
state.log(f" β’ {brand_count} brand colors identified")
|
| 505 |
+
state.log(f" β’ {text_count} text colors identified")
|
| 506 |
+
state.log(f" β’ {bg_count} background colors identified")
|
| 507 |
state.log("=" * 50)
|
| 508 |
|
| 509 |
progress(1.0, desc="β
Complete!")
|
|
|
|
| 517 |
|
| 518 |
**Primary Font:** {primary_font}
|
| 519 |
|
| 520 |
+
**Semantic Analysis:** {brand_count} brand, {text_count} text, {bg_count} background colors
|
| 521 |
+
|
| 522 |
+
**Enhanced Extraction:** DOM + CSS Variables + SVG + Inline + Stylesheets + Firecrawl
|
| 523 |
|
| 524 |
**Next:** Review the tokens below. Accept or reject, then proceed to Stage 2.
|
| 525 |
"""
|
| 526 |
|
| 527 |
+
# Return all AS-IS previews including semantic
|
| 528 |
return (
|
| 529 |
status,
|
| 530 |
state.get_logs(),
|
|
|
|
| 532 |
mobile_data,
|
| 533 |
typography_preview_html,
|
| 534 |
colors_asis_preview_html,
|
| 535 |
+
semantic_preview_html,
|
| 536 |
spacing_asis_preview_html,
|
| 537 |
radius_asis_preview_html,
|
| 538 |
shadows_asis_preview_html,
|
|
|
|
| 542 |
import traceback
|
| 543 |
state.log(f"β Error: {str(e)}")
|
| 544 |
state.log(traceback.format_exc())
|
| 545 |
+
return f"β Error: {str(e)}", state.get_logs(), None, None, "", "", "", "", "", ""
|
| 546 |
|
| 547 |
|
| 548 |
def format_tokens_for_display(normalized) -> dict:
|
|
|
|
| 645 |
desktop_dict = normalized_to_dict(state.desktop_normalized)
|
| 646 |
mobile_dict = normalized_to_dict(state.mobile_normalized)
|
| 647 |
|
| 648 |
+
# Run multi-agent analysis with semantic context
|
| 649 |
progress(0.1, desc="π Running parallel LLM analysis...")
|
| 650 |
|
| 651 |
result = await run_stage2_multi_agent(
|
|
|
|
| 653 |
mobile_tokens=mobile_dict,
|
| 654 |
competitors=competitors,
|
| 655 |
log_callback=state.log,
|
| 656 |
+
semantic_analysis=getattr(state, 'semantic_analysis', None), # Pass semantic context!
|
| 657 |
)
|
| 658 |
|
| 659 |
progress(0.8, desc="π Processing results...")
|
|
|
|
| 723 |
"letter_spacing": "0",
|
| 724 |
}
|
| 725 |
|
| 726 |
+
# Convert color tokens to dict format for preview (with frequency for sorting)
|
| 727 |
color_dict = {}
|
| 728 |
for name, c in state.desktop_normalized.colors.items():
|
| 729 |
+
color_dict[name] = {
|
| 730 |
+
"value": c.value,
|
| 731 |
+
"frequency": c.frequency,
|
| 732 |
+
}
|
| 733 |
|
| 734 |
typography_preview_html = generate_typography_preview_html(
|
| 735 |
typography_tokens=typo_dict,
|
|
|
|
| 1894 |
label="Colors Preview"
|
| 1895 |
)
|
| 1896 |
|
| 1897 |
+
with gr.Tab("π§ Semantic Colors"):
|
| 1898 |
+
gr.Markdown("*Colors categorized by usage: Brand, Text, Background, Border, Feedback*")
|
| 1899 |
+
stage1_semantic_preview = gr.HTML(
|
| 1900 |
+
value="<div style='padding: 20px; background: #f5f5f5; border-radius: 8px; color: #666;'>Semantic color analysis will appear after extraction...</div>",
|
| 1901 |
+
label="Semantic Colors Preview"
|
| 1902 |
+
)
|
| 1903 |
+
|
| 1904 |
with gr.Tab("π Spacing"):
|
| 1905 |
gr.Markdown("*All detected spacing values*")
|
| 1906 |
stage1_spacing_preview = gr.HTML(
|
|
|
|
| 2139 |
inputs=[pages_table],
|
| 2140 |
outputs=[extraction_status, log_output, desktop_data, mobile_data,
|
| 2141 |
stage1_typography_preview, stage1_colors_preview,
|
| 2142 |
+
stage1_semantic_preview,
|
| 2143 |
stage1_spacing_preview, stage1_radius_preview, stage1_shadows_preview],
|
| 2144 |
).then(
|
| 2145 |
fn=lambda d: (d.get("colors", []), d.get("typography", []), d.get("spacing", [])),
|