Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -32,11 +32,11 @@ CODE_MODELS = [
|
|
| 32 |
"Mistral-7B-Instruct",
|
| 33 |
]
|
| 34 |
|
| 35 |
-
#
|
| 36 |
HTTP_TIMEOUTS = httpx.Timeout(connect=10.0, read=120.0, write=30.0, pool=60.0)
|
| 37 |
-
HTTP_RETRIES = 2
|
| 38 |
|
| 39 |
-
#
|
| 40 |
DEFAULT_NEBIUS_API_KEY = (
|
| 41 |
"eyJhbGciOiJIUzI1NiIsImtpZCI6IlV6SXJWd1h0dnprLVRvdzlLZWstc0M1akptWXBvX1VaVkxUZlpnMDRlOFUiLCJ0eXAiOiJKV1QifQ.eyJzdWIiOiJnb29nbGUtb2F1dGgyfDEwNTA1MTQzMDg2MDMwMzIxNDEwMiIsInNjb3BlIjoib3BlbmlkIG9mZmxpbmVfYWNjZXNzIiwiaXNzIjoiYXBpX2tleV9pc3N1ZXIiLCJhdWQiOlsiaHR0cHM6Ly9uZWJpdXMtaW5mZXJlbmNlLmV1LmF1dGgwLmNvbS9hcGkvdjIvIl0sImV4cCI6MTkwNjU5ODA0NCwidXVpZCI6ImNkOGFiMWZlLTIxN2QtNDJlMy04OWUwLWM1YTg4MjcwMGVhNyIsIm5hbWUiOiJodW5nZ2luZyIsImV4cGlyZXNfYXQiOiIyMDMwLTA2LTAyVDAyOjM0OjA0KzAwMDAifQ.MA52QuIiNruK7_lX688RXAEI2TkcCOjcf_02XrpnhI8"
|
| 42 |
)
|
|
@@ -123,6 +123,7 @@ def _split_assets(html_code: str) -> Tuple[str, str, str]:
|
|
| 123 |
|
| 124 |
# Collect and remove inline scripts (no src)
|
| 125 |
js_blocks = []
|
|
|
|
| 126 |
def _script_repl(m):
|
| 127 |
attrs = m.group("attrs") or ""
|
| 128 |
code = m.group("code") or ""
|
|
@@ -131,21 +132,36 @@ def _split_assets(html_code: str) -> Tuple[str, str, str]:
|
|
| 131 |
if code.strip():
|
| 132 |
js_blocks.append(code.strip())
|
| 133 |
return "" # remove inline script
|
| 134 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 135 |
js_text = "\n\n".join(js_blocks)
|
| 136 |
|
| 137 |
# If CSS collected, ensure link tag is added
|
| 138 |
if css_text:
|
| 139 |
if re.search(r"</head>", html, flags=re.IGNORECASE):
|
| 140 |
-
html = re.sub(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 141 |
else:
|
| 142 |
-
# add a minimal head if missing
|
| 143 |
html = f"<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"utf-8\"/>\n <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n{html}"
|
| 144 |
|
| 145 |
# If JS collected, ensure script tag before </body> or at end
|
| 146 |
if js_text:
|
| 147 |
if re.search(r"</body>", html, flags=re.IGNORECASE):
|
| 148 |
-
html = re.sub(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 149 |
else:
|
| 150 |
html = html.rstrip() + '\n <script src="script.js"></script>\n'
|
| 151 |
|
|
@@ -401,28 +417,116 @@ def export_html_to_file(html_code: str) -> Optional[str]:
|
|
| 401 |
|
| 402 |
|
| 403 |
# =========================
|
| 404 |
-
# Gradio UI (
|
| 405 |
# =========================
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 406 |
with gr.Blocks(
|
| 407 |
theme=gr.themes.Soft(),
|
| 408 |
title="AI Website Generator (Nebius)",
|
| 409 |
-
css="""
|
| 410 |
-
|
| 411 |
-
|
| 412 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 413 |
""",
|
| 414 |
) as app:
|
| 415 |
gr.Markdown(
|
| 416 |
"""
|
| 417 |
-
#
|
| 418 |
Turn website screenshots into functional HTML using Nebius-compatible models.
|
| 419 |
|
| 420 |
-
-
|
| 421 |
-
-
|
| 422 |
-
-
|
| 423 |
-
-
|
| 424 |
-
-
|
| 425 |
-
"""
|
|
|
|
| 426 |
)
|
| 427 |
|
| 428 |
with gr.Accordion("API & Models", open=True):
|
|
@@ -466,7 +570,7 @@ Turn website screenshots into functional HTML using Nebius-compatible models.
|
|
| 466 |
info="Higher is more creative; lower is more deterministic.",
|
| 467 |
)
|
| 468 |
|
| 469 |
-
with gr.Tab("
|
| 470 |
with gr.Row():
|
| 471 |
with gr.Column(scale=1):
|
| 472 |
gr.Markdown("### Step 1: Upload Screenshot", elem_classes=["section"])
|
|
@@ -476,7 +580,7 @@ Turn website screenshots into functional HTML using Nebius-compatible models.
|
|
| 476 |
sources=["upload", "clipboard"],
|
| 477 |
height=280,
|
| 478 |
)
|
| 479 |
-
generate_btn = gr.Button("
|
| 480 |
|
| 481 |
with gr.Column(scale=2):
|
| 482 |
gr.Markdown("### Step 2: Review Results", elem_classes=["section"])
|
|
@@ -492,8 +596,8 @@ Turn website screenshots into functional HTML using Nebius-compatible models.
|
|
| 492 |
)
|
| 493 |
|
| 494 |
with gr.Row():
|
| 495 |
-
codesandbox_btn = gr.Button("
|
| 496 |
-
download_btn = gr.Button("
|
| 497 |
|
| 498 |
codesandbox_links = gr.Markdown(value="")
|
| 499 |
download_file = gr.File(
|
|
@@ -502,18 +606,18 @@ Turn website screenshots into functional HTML using Nebius-compatible models.
|
|
| 502 |
visible=False,
|
| 503 |
)
|
| 504 |
|
| 505 |
-
with gr.Tab("
|
| 506 |
with gr.Row():
|
| 507 |
with gr.Column():
|
| 508 |
gr.Markdown("### Image Analysis Tool", elem_classes=["section"])
|
| 509 |
img_tool = gr.Image(type="pil", label="Image")
|
| 510 |
-
analyze_btn = gr.Button("Analyze Image")
|
| 511 |
analysis_result = gr.Textbox(label="Analysis Result", lines=6)
|
| 512 |
|
| 513 |
with gr.Column():
|
| 514 |
gr.Markdown("### Code Generation Tool", elem_classes=["section"])
|
| 515 |
desc_input = gr.Textbox(label="Description", lines=4, placeholder="Describe the page you want...")
|
| 516 |
-
code_btn = gr.Button("Generate Code")
|
| 517 |
code_result = gr.Code(label="Generated Code", language="html")
|
| 518 |
|
| 519 |
gr.Markdown("Made with Gradio • Nebius API compatible", elem_classes=["footer"])
|
|
@@ -529,7 +633,6 @@ Turn website screenshots into functional HTML using Nebius-compatible models.
|
|
| 529 |
url_block = create_codesandbox(html_code)
|
| 530 |
if url_block.startswith("Error"):
|
| 531 |
return f"**{url_block}**"
|
| 532 |
-
# Render as Markdown list
|
| 533 |
lines = ["### CodeSandbox Links", "", url_block]
|
| 534 |
return "\n".join(lines)
|
| 535 |
|
|
@@ -563,8 +666,5 @@ Turn website screenshots into functional HTML using Nebius-compatible models.
|
|
| 563 |
outputs=[code_result],
|
| 564 |
)
|
| 565 |
|
| 566 |
-
# Optional examples (uncomment and provide your own image file)
|
| 567 |
-
# gr.Examples(examples=[["1.jpg"]], inputs=[image_input], label="Example Screenshots")
|
| 568 |
-
|
| 569 |
if __name__ == "__main__":
|
| 570 |
app.launch(share=False)
|
|
|
|
| 32 |
"Mistral-7B-Instruct",
|
| 33 |
]
|
| 34 |
|
| 35 |
+
# Timeouts and simple retries for stability
|
| 36 |
HTTP_TIMEOUTS = httpx.Timeout(connect=10.0, read=120.0, write=30.0, pool=60.0)
|
| 37 |
+
HTTP_RETRIES = 2
|
| 38 |
|
| 39 |
+
# Keep the same default key you provided
|
| 40 |
DEFAULT_NEBIUS_API_KEY = (
|
| 41 |
"eyJhbGciOiJIUzI1NiIsImtpZCI6IlV6SXJWd1h0dnprLVRvdzlLZWstc0M1akptWXBvX1VaVkxUZlpnMDRlOFUiLCJ0eXAiOiJKV1QifQ.eyJzdWIiOiJnb29nbGUtb2F1dGgyfDEwNTA1MTQzMDg2MDMwMzIxNDEwMiIsInNjb3BlIjoib3BlbmlkIG9mZmxpbmVfYWNjZXNzIiwiaXNzIjoiYXBpX2tleV9pc3N1ZXIiLCJhdWQiOlsiaHR0cHM6Ly9uZWJpdXMtaW5mZXJlbmNlLmV1LmF1dGgwLmNvbS9hcGkvdjIvIl0sImV4cCI6MTkwNjU5ODA0NCwidXVpZCI6ImNkOGFiMWZlLTIxN2QtNDJlMy04OWUwLWM1YTg4MjcwMGVhNyIsIm5hbWUiOiJodW5nZ2luZyIsImV4cGlyZXNfYXQiOiIyMDMwLTA2LTAyVDAyOjM0OjA0KzAwMDAifQ.MA52QuIiNruK7_lX688RXAEI2TkcCOjcf_02XrpnhI8"
|
| 42 |
)
|
|
|
|
| 123 |
|
| 124 |
# Collect and remove inline scripts (no src)
|
| 125 |
js_blocks = []
|
| 126 |
+
|
| 127 |
def _script_repl(m):
|
| 128 |
attrs = m.group("attrs") or ""
|
| 129 |
code = m.group("code") or ""
|
|
|
|
| 132 |
if code.strip():
|
| 133 |
js_blocks.append(code.strip())
|
| 134 |
return "" # remove inline script
|
| 135 |
+
|
| 136 |
+
html = re.sub(
|
| 137 |
+
r"<script(?P<attrs>[^>]*)>(?P<code>.*?)</script>",
|
| 138 |
+
_script_repl,
|
| 139 |
+
html,
|
| 140 |
+
flags=re.IGNORECASE | re.DOTALL,
|
| 141 |
+
)
|
| 142 |
js_text = "\n\n".join(js_blocks)
|
| 143 |
|
| 144 |
# If CSS collected, ensure link tag is added
|
| 145 |
if css_text:
|
| 146 |
if re.search(r"</head>", html, flags=re.IGNORECASE):
|
| 147 |
+
html = re.sub(
|
| 148 |
+
r"</head>",
|
| 149 |
+
' <link rel="stylesheet" href="style.css">\n</head>',
|
| 150 |
+
html,
|
| 151 |
+
flags=re.IGNORECASE,
|
| 152 |
+
)
|
| 153 |
else:
|
|
|
|
| 154 |
html = f"<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"utf-8\"/>\n <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n{html}"
|
| 155 |
|
| 156 |
# If JS collected, ensure script tag before </body> or at end
|
| 157 |
if js_text:
|
| 158 |
if re.search(r"</body>", html, flags=re.IGNORECASE):
|
| 159 |
+
html = re.sub(
|
| 160 |
+
r"</body>",
|
| 161 |
+
' <script src="script.js"></script>\n</body>',
|
| 162 |
+
html,
|
| 163 |
+
flags=re.IGNORECASE,
|
| 164 |
+
)
|
| 165 |
else:
|
| 166 |
html = html.rstrip() + '\n <script src="script.js"></script>\n'
|
| 167 |
|
|
|
|
| 417 |
|
| 418 |
|
| 419 |
# =========================
|
| 420 |
+
# Gradio UI (Brizy-inspired palette, no emojis)
|
| 421 |
# =========================
|
| 422 |
+
BRIZY_PRIMARY = "#6C5CE7" # Indigo-like
|
| 423 |
+
BRIZY_SECONDARY = "#00C2FF" # Cyan accent
|
| 424 |
+
BRIZY_BG = "#F7F9FC" # Light background
|
| 425 |
+
BRIZY_SURFACE = "#FFFFFF" # Surface
|
| 426 |
+
BRIZY_TEXT = "#1F2937" # Dark text
|
| 427 |
+
BRIZY_MUTED = "#6B7280" # Muted text
|
| 428 |
+
BRIZY_BORDER = "#E5E7EB" # Soft border
|
| 429 |
+
BRIZY_GRADIENT = f"linear-gradient(135deg, {BRIZY_PRIMARY} 0%, {BRIZY_SECONDARY} 100%)"
|
| 430 |
+
|
| 431 |
with gr.Blocks(
|
| 432 |
theme=gr.themes.Soft(),
|
| 433 |
title="AI Website Generator (Nebius)",
|
| 434 |
+
css=f"""
|
| 435 |
+
:root {{
|
| 436 |
+
--app-primary: {BRIZY_PRIMARY};
|
| 437 |
+
--app-secondary: {BRIZY_SECONDARY};
|
| 438 |
+
--app-bg: {BRIZY_BG};
|
| 439 |
+
--app-surface: {BRIZY_SURFACE};
|
| 440 |
+
--app-text: {BRIZY_TEXT};
|
| 441 |
+
--app-muted: {BRIZY_MUTED};
|
| 442 |
+
--app-border: {BRIZY_BORDER};
|
| 443 |
+
}}
|
| 444 |
+
|
| 445 |
+
body {{
|
| 446 |
+
background: var(--app-bg);
|
| 447 |
+
color: var(--app-text);
|
| 448 |
+
}}
|
| 449 |
+
|
| 450 |
+
.section {{
|
| 451 |
+
border: 1px solid var(--app-border);
|
| 452 |
+
padding: 16px;
|
| 453 |
+
border-radius: 12px;
|
| 454 |
+
background: var(--app-surface);
|
| 455 |
+
box-shadow: 0 1px 2px rgba(0,0,0,0.03);
|
| 456 |
+
margin: 10px 0;
|
| 457 |
+
}}
|
| 458 |
+
|
| 459 |
+
.muted {{
|
| 460 |
+
color: var(--app-muted);
|
| 461 |
+
font-size: 0.92em;
|
| 462 |
+
}}
|
| 463 |
+
|
| 464 |
+
.footer {{
|
| 465 |
+
text-align: center;
|
| 466 |
+
color: var(--app-muted);
|
| 467 |
+
padding: 8px 0;
|
| 468 |
+
}}
|
| 469 |
+
|
| 470 |
+
.title h1 {{
|
| 471 |
+
background: {BRIZY_GRADIENT};
|
| 472 |
+
-webkit-background-clip: text;
|
| 473 |
+
background-clip: text;
|
| 474 |
+
color: transparent;
|
| 475 |
+
font-weight: 800;
|
| 476 |
+
letter-spacing: -0.02em;
|
| 477 |
+
}}
|
| 478 |
+
|
| 479 |
+
.primary-btn button {{
|
| 480 |
+
background: {BRIZY_GRADIENT} !important;
|
| 481 |
+
color: #fff !important;
|
| 482 |
+
border: none !important;
|
| 483 |
+
}}
|
| 484 |
+
.primary-btn button:hover {{
|
| 485 |
+
filter: brightness(0.98);
|
| 486 |
+
}}
|
| 487 |
+
|
| 488 |
+
.secondary-btn button {{
|
| 489 |
+
background: var(--app-surface) !important;
|
| 490 |
+
color: var(--app-text) !important;
|
| 491 |
+
border: 1px solid var(--app-border) !important;
|
| 492 |
+
}}
|
| 493 |
+
.secondary-btn button:hover {{
|
| 494 |
+
border-color: {BRIZY_PRIMARY} !important;
|
| 495 |
+
color: {BRIZY_PRIMARY} !important;
|
| 496 |
+
}}
|
| 497 |
+
|
| 498 |
+
/* Inputs focus */
|
| 499 |
+
input:focus, textarea:focus, select:focus {{
|
| 500 |
+
outline-color: {BRIZY_PRIMARY} !important;
|
| 501 |
+
border-color: {BRIZY_PRIMARY} !important;
|
| 502 |
+
box-shadow: 0 0 0 3px rgba(108,92,231,0.15) !important;
|
| 503 |
+
}}
|
| 504 |
+
|
| 505 |
+
/* Code block accents */
|
| 506 |
+
.gr-code .cm-editor, .gr-code textarea {{
|
| 507 |
+
border-radius: 10px !important;
|
| 508 |
+
border: 1px solid var(--app-border) !important;
|
| 509 |
+
}}
|
| 510 |
+
|
| 511 |
+
/* Tabs accent */
|
| 512 |
+
.gradio-container .tabs .tab-nav button[aria-selected="true"] {{
|
| 513 |
+
color: {BRIZY_PRIMARY} !important;
|
| 514 |
+
border-bottom: 2px solid {BRIZY_PRIMARY} !important;
|
| 515 |
+
}}
|
| 516 |
""",
|
| 517 |
) as app:
|
| 518 |
gr.Markdown(
|
| 519 |
"""
|
| 520 |
+
# AI Website Generator (Nebius)
|
| 521 |
Turn website screenshots into functional HTML using Nebius-compatible models.
|
| 522 |
|
| 523 |
+
- Image analysis (choose a vision model)
|
| 524 |
+
- Code generation (choose a code-capable model)
|
| 525 |
+
- One-click CodeSandbox deployment
|
| 526 |
+
- Editor links open index.html and style.css directly
|
| 527 |
+
- Your key is used at runtime only
|
| 528 |
+
""",
|
| 529 |
+
elem_classes=["title"],
|
| 530 |
)
|
| 531 |
|
| 532 |
with gr.Accordion("API & Models", open=True):
|
|
|
|
| 570 |
info="Higher is more creative; lower is more deterministic.",
|
| 571 |
)
|
| 572 |
|
| 573 |
+
with gr.Tab("Quick Generate"):
|
| 574 |
with gr.Row():
|
| 575 |
with gr.Column(scale=1):
|
| 576 |
gr.Markdown("### Step 1: Upload Screenshot", elem_classes=["section"])
|
|
|
|
| 580 |
sources=["upload", "clipboard"],
|
| 581 |
height=280,
|
| 582 |
)
|
| 583 |
+
generate_btn = gr.Button("Generate Website", elem_classes=["primary-btn"])
|
| 584 |
|
| 585 |
with gr.Column(scale=2):
|
| 586 |
gr.Markdown("### Step 2: Review Results", elem_classes=["section"])
|
|
|
|
| 596 |
)
|
| 597 |
|
| 598 |
with gr.Row():
|
| 599 |
+
codesandbox_btn = gr.Button("Deploy to CodeSandbox", elem_classes=["secondary-btn"])
|
| 600 |
+
download_btn = gr.Button("Download index.html", elem_classes=["secondary-btn"])
|
| 601 |
|
| 602 |
codesandbox_links = gr.Markdown(value="")
|
| 603 |
download_file = gr.File(
|
|
|
|
| 606 |
visible=False,
|
| 607 |
)
|
| 608 |
|
| 609 |
+
with gr.Tab("Individual Tools"):
|
| 610 |
with gr.Row():
|
| 611 |
with gr.Column():
|
| 612 |
gr.Markdown("### Image Analysis Tool", elem_classes=["section"])
|
| 613 |
img_tool = gr.Image(type="pil", label="Image")
|
| 614 |
+
analyze_btn = gr.Button("Analyze Image", elem_classes=["secondary-btn"])
|
| 615 |
analysis_result = gr.Textbox(label="Analysis Result", lines=6)
|
| 616 |
|
| 617 |
with gr.Column():
|
| 618 |
gr.Markdown("### Code Generation Tool", elem_classes=["section"])
|
| 619 |
desc_input = gr.Textbox(label="Description", lines=4, placeholder="Describe the page you want...")
|
| 620 |
+
code_btn = gr.Button("Generate Code", elem_classes=["secondary-btn"])
|
| 621 |
code_result = gr.Code(label="Generated Code", language="html")
|
| 622 |
|
| 623 |
gr.Markdown("Made with Gradio • Nebius API compatible", elem_classes=["footer"])
|
|
|
|
| 633 |
url_block = create_codesandbox(html_code)
|
| 634 |
if url_block.startswith("Error"):
|
| 635 |
return f"**{url_block}**"
|
|
|
|
| 636 |
lines = ["### CodeSandbox Links", "", url_block]
|
| 637 |
return "\n".join(lines)
|
| 638 |
|
|
|
|
| 666 |
outputs=[code_result],
|
| 667 |
)
|
| 668 |
|
|
|
|
|
|
|
|
|
|
| 669 |
if __name__ == "__main__":
|
| 670 |
app.launch(share=False)
|