Spaces:
Running on Zero
Running on Zero
Redesign Space interface
Browse files
README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
---
|
| 2 |
title: Image Generator for HTML Games
|
| 3 |
emoji: 🎮
|
| 4 |
-
colorFrom:
|
| 5 |
colorTo: green
|
| 6 |
sdk: gradio
|
| 7 |
sdk_version: 6.14.0
|
|
|
|
| 1 |
---
|
| 2 |
title: Image Generator for HTML Games
|
| 3 |
emoji: 🎮
|
| 4 |
+
colorFrom: indigo
|
| 5 |
colorTo: green
|
| 6 |
sdk: gradio
|
| 7 |
sdk_version: 6.14.0
|
app.py
CHANGED
|
@@ -2385,84 +2385,253 @@ def check_hf_token() -> str:
|
|
| 2385 |
return f"HF_TOKEN check failed: {short_error(exc)}"
|
| 2386 |
|
| 2387 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2388 |
with gr.Blocks(title="Image Generator for HTML Games") as demo:
|
| 2389 |
-
gr.
|
| 2390 |
-
|
| 2391 |
-
|
| 2392 |
-
|
| 2393 |
-
|
| 2394 |
-
|
| 2395 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2396 |
|
| 2397 |
-
|
| 2398 |
|
| 2399 |
-
|
| 2400 |
-
|
| 2401 |
-
|
| 2402 |
-
|
| 2403 |
-
|
| 2404 |
-
|
| 2405 |
-
|
| 2406 |
-
|
| 2407 |
-
|
| 2408 |
-
|
| 2409 |
-
|
| 2410 |
-
|
| 2411 |
-
|
| 2412 |
-
|
| 2413 |
-
|
| 2414 |
-
"
|
| 2415 |
-
|
| 2416 |
-
|
| 2417 |
-
|
| 2418 |
-
|
| 2419 |
-
|
| 2420 |
-
|
| 2421 |
-
|
| 2422 |
-
|
| 2423 |
-
|
| 2424 |
-
|
| 2425 |
-
|
| 2426 |
-
|
| 2427 |
-
|
| 2428 |
-
|
| 2429 |
-
|
| 2430 |
-
|
| 2431 |
-
|
| 2432 |
-
|
| 2433 |
-
|
| 2434 |
-
|
| 2435 |
-
|
| 2436 |
-
|
| 2437 |
-
|
| 2438 |
-
|
| 2439 |
-
|
| 2440 |
-
|
| 2441 |
-
|
| 2442 |
-
|
| 2443 |
-
|
| 2444 |
-
|
| 2445 |
-
|
| 2446 |
-
|
| 2447 |
-
|
| 2448 |
-
|
| 2449 |
-
|
| 2450 |
-
|
| 2451 |
-
|
| 2452 |
-
|
|
|
|
|
|
|
|
|
|
| 2453 |
|
| 2454 |
-
|
| 2455 |
-
|
| 2456 |
-
|
| 2457 |
-
|
| 2458 |
-
|
| 2459 |
-
|
| 2460 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2461 |
output_code = gr.Code(
|
| 2462 |
label="Rewritten HTML with embedded images",
|
| 2463 |
language="html",
|
| 2464 |
lines=18,
|
| 2465 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2466 |
prompt_preview = gr.Textbox(
|
| 2467 |
label="Interpreted image prompts",
|
| 2468 |
lines=8,
|
|
@@ -2479,42 +2648,43 @@ with gr.Blocks(title="Image Generator for HTML Games") as demo:
|
|
| 2479 |
interactive=False,
|
| 2480 |
)
|
| 2481 |
|
| 2482 |
-
|
| 2483 |
-
|
| 2484 |
-
|
| 2485 |
-
|
| 2486 |
-
|
| 2487 |
-
|
| 2488 |
-
|
| 2489 |
-
|
| 2490 |
-
|
| 2491 |
-
|
| 2492 |
-
|
| 2493 |
-
|
| 2494 |
-
|
| 2495 |
-
|
| 2496 |
-
|
| 2497 |
-
|
| 2498 |
-
|
| 2499 |
-
|
| 2500 |
-
|
| 2501 |
-
|
| 2502 |
-
|
| 2503 |
-
|
| 2504 |
-
|
| 2505 |
-
|
| 2506 |
-
|
| 2507 |
-
|
| 2508 |
-
|
| 2509 |
-
|
| 2510 |
-
|
| 2511 |
-
|
| 2512 |
-
|
| 2513 |
-
|
| 2514 |
-
|
| 2515 |
-
|
| 2516 |
-
|
|
|
|
| 2517 |
|
| 2518 |
|
| 2519 |
if __name__ == "__main__":
|
| 2520 |
-
demo.launch()
|
|
|
|
| 2385 |
return f"HF_TOKEN check failed: {short_error(exc)}"
|
| 2386 |
|
| 2387 |
|
| 2388 |
+
APP_CSS = """
|
| 2389 |
+
:root {
|
| 2390 |
+
--canvas: #070a12;
|
| 2391 |
+
--surface: #0d1320;
|
| 2392 |
+
--surface-raised: #111a2b;
|
| 2393 |
+
--border: #25324a;
|
| 2394 |
+
--border-bright: #344766;
|
| 2395 |
+
--text: #f4f7fb;
|
| 2396 |
+
--muted: #9aa8bd;
|
| 2397 |
+
--accent: #72f2cf;
|
| 2398 |
+
--accent-strong: #42dbb5;
|
| 2399 |
+
--violet: #a89bff;
|
| 2400 |
+
}
|
| 2401 |
+
|
| 2402 |
+
.gradio-container {
|
| 2403 |
+
background:
|
| 2404 |
+
radial-gradient(circle at 8% 0%, rgba(82, 65, 170, .22), transparent 31rem),
|
| 2405 |
+
radial-gradient(circle at 92% 12%, rgba(26, 173, 145, .12), transparent 26rem),
|
| 2406 |
+
var(--canvas) !important;
|
| 2407 |
+
color: var(--text) !important;
|
| 2408 |
+
max-width: none !important;
|
| 2409 |
+
padding: 0 !important;
|
| 2410 |
+
}
|
| 2411 |
+
|
| 2412 |
+
.app-shell { max-width: 1440px; margin: 0 auto; padding: 28px 28px 64px; }
|
| 2413 |
+
|
| 2414 |
+
.hero {
|
| 2415 |
+
position: relative;
|
| 2416 |
+
overflow: hidden;
|
| 2417 |
+
padding: 34px 38px;
|
| 2418 |
+
margin-bottom: 22px;
|
| 2419 |
+
border: 1px solid var(--border-bright);
|
| 2420 |
+
border-radius: 24px;
|
| 2421 |
+
background: linear-gradient(135deg, rgba(20, 30, 51, .96), rgba(11, 17, 29, .95));
|
| 2422 |
+
box-shadow: 0 22px 60px rgba(0, 0, 0, .25);
|
| 2423 |
+
}
|
| 2424 |
+
|
| 2425 |
+
.hero::after {
|
| 2426 |
+
content: "";
|
| 2427 |
+
position: absolute;
|
| 2428 |
+
right: -70px;
|
| 2429 |
+
top: -100px;
|
| 2430 |
+
width: 320px;
|
| 2431 |
+
height: 320px;
|
| 2432 |
+
border-radius: 50%;
|
| 2433 |
+
background: radial-gradient(circle, rgba(114, 242, 207, .18), transparent 66%);
|
| 2434 |
+
}
|
| 2435 |
+
|
| 2436 |
+
.eyebrow {
|
| 2437 |
+
color: var(--accent);
|
| 2438 |
+
font-size: 12px;
|
| 2439 |
+
font-weight: 800;
|
| 2440 |
+
letter-spacing: .16em;
|
| 2441 |
+
text-transform: uppercase;
|
| 2442 |
+
}
|
| 2443 |
+
|
| 2444 |
+
.hero h1 {
|
| 2445 |
+
max-width: 820px;
|
| 2446 |
+
margin: 10px 0 12px;
|
| 2447 |
+
color: #fff;
|
| 2448 |
+
font-size: clamp(34px, 5vw, 62px);
|
| 2449 |
+
line-height: .98;
|
| 2450 |
+
letter-spacing: -.045em;
|
| 2451 |
+
}
|
| 2452 |
+
|
| 2453 |
+
.hero p { max-width: 760px; margin: 0; color: #b9c5d7; font-size: 17px; line-height: 1.6; }
|
| 2454 |
+
.hero-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 22px; }
|
| 2455 |
+
.hero-chips span {
|
| 2456 |
+
padding: 7px 11px;
|
| 2457 |
+
border: 1px solid #30415d;
|
| 2458 |
+
border-radius: 999px;
|
| 2459 |
+
background: rgba(8, 13, 23, .65);
|
| 2460 |
+
color: #cfdaea;
|
| 2461 |
+
font-size: 12px;
|
| 2462 |
+
font-weight: 700;
|
| 2463 |
+
}
|
| 2464 |
+
|
| 2465 |
+
.step-heading { margin: 28px 0 12px; }
|
| 2466 |
+
.step-heading span { color: var(--accent); font: 800 12px/1 sans-serif; letter-spacing: .14em; text-transform: uppercase; }
|
| 2467 |
+
.step-heading h2 { margin: 7px 0 3px; color: var(--text); font-size: 24px; letter-spacing: -.025em; }
|
| 2468 |
+
.step-heading p { margin: 0; color: var(--muted); font-size: 14px; }
|
| 2469 |
+
|
| 2470 |
+
.panel {
|
| 2471 |
+
border: 1px solid var(--border) !important;
|
| 2472 |
+
border-radius: 18px !important;
|
| 2473 |
+
background: linear-gradient(180deg, rgba(17, 26, 43, .92), rgba(12, 18, 31, .96)) !important;
|
| 2474 |
+
box-shadow: 0 12px 36px rgba(0, 0, 0, .18);
|
| 2475 |
+
padding: 18px !important;
|
| 2476 |
+
}
|
| 2477 |
+
|
| 2478 |
+
.gradio-container label, .gradio-container .label-wrap { color: #dce5f2 !important; font-weight: 700 !important; }
|
| 2479 |
+
.gradio-container .info { color: var(--muted) !important; }
|
| 2480 |
+
.gradio-container textarea,
|
| 2481 |
+
.gradio-container input,
|
| 2482 |
+
.gradio-container select,
|
| 2483 |
+
.gradio-container [role="listbox"] {
|
| 2484 |
+
color: var(--text) !important;
|
| 2485 |
+
background: #090f1b !important;
|
| 2486 |
+
border-color: var(--border) !important;
|
| 2487 |
+
}
|
| 2488 |
+
.gradio-container textarea:focus,
|
| 2489 |
+
.gradio-container input:focus { border-color: var(--accent-strong) !important; box-shadow: 0 0 0 2px rgba(66, 219, 181, .12) !important; }
|
| 2490 |
+
|
| 2491 |
+
#generate-button {
|
| 2492 |
+
min-height: 52px;
|
| 2493 |
+
border: 0 !important;
|
| 2494 |
+
border-radius: 12px !important;
|
| 2495 |
+
background: linear-gradient(135deg, var(--accent), #91f6a9) !important;
|
| 2496 |
+
color: #06110e !important;
|
| 2497 |
+
font-weight: 850 !important;
|
| 2498 |
+
box-shadow: 0 10px 28px rgba(66, 219, 181, .18);
|
| 2499 |
+
}
|
| 2500 |
+
#generate-button:hover { transform: translateY(-1px); filter: brightness(1.05); }
|
| 2501 |
+
.secondary-action { border-color: var(--border-bright) !important; background: #151f31 !important; color: #e7eef8 !important; }
|
| 2502 |
+
.status-card { margin-top: 10px; padding: 12px 14px; border-left: 3px solid var(--accent); border-radius: 8px; background: rgba(114, 242, 207, .06); }
|
| 2503 |
+
.status-card p { margin: 0; color: #c9d6e6; }
|
| 2504 |
+
.gallery-panel .grid-wrap { background: transparent !important; }
|
| 2505 |
+
.preview-panel iframe { border: 1px solid var(--border-bright) !important; border-radius: 12px !important; }
|
| 2506 |
+
.diagnostics { margin-top: 20px; border-color: var(--border) !important; background: var(--surface) !important; }
|
| 2507 |
+
.pipeline-note { color: var(--muted); font-size: 13px; }
|
| 2508 |
+
.footer-note { margin-top: 28px; color: #718096; text-align: center; font-size: 12px; }
|
| 2509 |
+
|
| 2510 |
+
@media (max-width: 780px) {
|
| 2511 |
+
.app-shell { padding: 14px 14px 42px; }
|
| 2512 |
+
.hero { padding: 26px 22px; border-radius: 18px; }
|
| 2513 |
+
.hero h1 { font-size: 38px; }
|
| 2514 |
+
.hero p { font-size: 15px; }
|
| 2515 |
+
.panel { padding: 13px !important; }
|
| 2516 |
+
}
|
| 2517 |
+
"""
|
| 2518 |
+
|
| 2519 |
+
|
| 2520 |
with gr.Blocks(title="Image Generator for HTML Games") as demo:
|
| 2521 |
+
with gr.Column(elem_classes=["app-shell"]):
|
| 2522 |
+
gr.HTML(
|
| 2523 |
+
"""
|
| 2524 |
+
<header class="hero">
|
| 2525 |
+
<div class="eyebrow">Game asset studio</div>
|
| 2526 |
+
<h1>Turn your game code into a visual world.</h1>
|
| 2527 |
+
<p>Describe the art direction, generate camera-aware assets, and embed them into deterministic image hooks—without rewriting your game logic.</p>
|
| 2528 |
+
<div class="hero-chips">
|
| 2529 |
+
<span>Camera-aware prompts</span><span>Game-ready PNGs</span><span>Safe code integration</span>
|
| 2530 |
+
</div>
|
| 2531 |
+
</header>
|
| 2532 |
+
"""
|
| 2533 |
+
)
|
| 2534 |
|
| 2535 |
+
generation_state = gr.State({})
|
| 2536 |
|
| 2537 |
+
gr.HTML(
|
| 2538 |
+
'<div class="step-heading"><span>Step 01</span><h2>Set up your generation</h2>'
|
| 2539 |
+
'<p>Paste the game first, then define the assets and shared art direction.</p></div>'
|
| 2540 |
+
)
|
| 2541 |
+
with gr.Row(equal_height=False):
|
| 2542 |
+
with gr.Column(scale=3, elem_classes=["panel"]):
|
| 2543 |
+
html_input = gr.Textbox(
|
| 2544 |
+
label="Original HTML game code",
|
| 2545 |
+
lines=22,
|
| 2546 |
+
placeholder="Paste your full HTML game code here.",
|
| 2547 |
+
value=STARTER_HTML,
|
| 2548 |
+
info="Use GAME_ASSETS.<role> or sprite_<role>.png for deterministic embedding.",
|
| 2549 |
+
)
|
| 2550 |
+
with gr.Column(scale=2, elem_classes=["panel"]):
|
| 2551 |
+
roles = gr.Textbox(
|
| 2552 |
+
label="Assets to create",
|
| 2553 |
+
lines=8,
|
| 2554 |
+
placeholder=ROLE_PLACEHOLDER,
|
| 2555 |
+
value=DEFAULT_ROLES,
|
| 2556 |
+
info="One per line: role: image description",
|
| 2557 |
+
)
|
| 2558 |
+
with gr.Row():
|
| 2559 |
+
game_type = gr.Dropdown(
|
| 2560 |
+
label="Game type",
|
| 2561 |
+
choices=[
|
| 2562 |
+
"Top-down action / RPG",
|
| 2563 |
+
"Platformer / side-scroller",
|
| 2564 |
+
"Isometric strategy / simulation",
|
| 2565 |
+
"First-person",
|
| 2566 |
+
"Card / board game",
|
| 2567 |
+
"Other / custom",
|
| 2568 |
+
],
|
| 2569 |
+
value="Top-down action / RPG",
|
| 2570 |
+
)
|
| 2571 |
+
perspective = gr.Dropdown(
|
| 2572 |
+
label="Camera perspective",
|
| 2573 |
+
choices=[
|
| 2574 |
+
"Top-down / overhead",
|
| 2575 |
+
"Side view",
|
| 2576 |
+
"Isometric 3/4 view",
|
| 2577 |
+
"First-person",
|
| 2578 |
+
"Front-facing",
|
| 2579 |
+
"Auto-detect from code",
|
| 2580 |
+
],
|
| 2581 |
+
value="Top-down / overhead",
|
| 2582 |
+
)
|
| 2583 |
+
theme = gr.Textbox(
|
| 2584 |
+
label="Theme and shared visual style",
|
| 2585 |
+
lines=3,
|
| 2586 |
+
placeholder="Example: moonlit forest, indigo and moss palette, crisp pixel art, readable silhouettes",
|
| 2587 |
+
)
|
| 2588 |
+
generate_btn = gr.Button(
|
| 2589 |
+
"Generate assets + build game",
|
| 2590 |
+
variant="primary",
|
| 2591 |
+
elem_id="generate-button",
|
| 2592 |
+
)
|
| 2593 |
+
status = gr.Markdown("Ready for your game code.", elem_classes=["status-card"])
|
| 2594 |
|
| 2595 |
+
gr.HTML(
|
| 2596 |
+
'<div class="step-heading"><span>Step 02</span><h2>Review generated assets</h2>'
|
| 2597 |
+
'<p>Check subject, perspective, style, and transparency before approving an asset.</p></div>'
|
| 2598 |
+
)
|
| 2599 |
+
with gr.Row(equal_height=False):
|
| 2600 |
+
with gr.Column(scale=3, elem_classes=["panel", "gallery-panel"]):
|
| 2601 |
+
gallery = gr.Gallery(label="Generated assets", columns=2, height=420)
|
| 2602 |
+
with gr.Column(scale=2, elem_classes=["panel"]):
|
| 2603 |
+
selected_role = gr.Dropdown(
|
| 2604 |
+
label="Asset to regenerate",
|
| 2605 |
+
choices=[],
|
| 2606 |
+
interactive=True,
|
| 2607 |
+
)
|
| 2608 |
+
regenerate_btn = gr.Button(
|
| 2609 |
+
"Regenerate selected asset",
|
| 2610 |
+
elem_classes=["secondary-action"],
|
| 2611 |
+
)
|
| 2612 |
+
approved_roles = gr.CheckboxGroup(
|
| 2613 |
+
label="Visually approved assets",
|
| 2614 |
+
choices=[],
|
| 2615 |
+
info="Approval is a manual review marker; automated checks cannot prove artistic correctness.",
|
| 2616 |
+
)
|
| 2617 |
+
|
| 2618 |
+
gr.HTML(
|
| 2619 |
+
'<div class="step-heading"><span>Step 03</span><h2>Play and export</h2>'
|
| 2620 |
+
'<p>Test the integrated game, then copy the rewritten HTML when it looks right.</p></div>'
|
| 2621 |
+
)
|
| 2622 |
+
with gr.Column(elem_classes=["panel", "preview-panel"]):
|
| 2623 |
+
preview = gr.HTML("")
|
| 2624 |
+
with gr.Column(elem_classes=["panel"]):
|
| 2625 |
output_code = gr.Code(
|
| 2626 |
label="Rewritten HTML with embedded images",
|
| 2627 |
language="html",
|
| 2628 |
lines=18,
|
| 2629 |
)
|
| 2630 |
+
|
| 2631 |
+
with gr.Accordion("Generation details and diagnostics", open=False, elem_classes=["diagnostics"]):
|
| 2632 |
+
gr.Markdown(model_configuration_summary(), elem_classes=["pipeline-note"])
|
| 2633 |
+
token_btn = gr.Button("Check HF token", elem_classes=["secondary-action"])
|
| 2634 |
+
token_status = gr.Markdown("")
|
| 2635 |
prompt_preview = gr.Textbox(
|
| 2636 |
label="Interpreted image prompts",
|
| 2637 |
lines=8,
|
|
|
|
| 2648 |
interactive=False,
|
| 2649 |
)
|
| 2650 |
|
| 2651 |
+
gr.HTML(
|
| 2652 |
+
'<div class="footer-note">Deterministic hooks only · Game logic remains untouched · Human visual review required</div>'
|
| 2653 |
+
)
|
| 2654 |
+
|
| 2655 |
+
generate_btn.click(
|
| 2656 |
+
fn=generate_images_and_game,
|
| 2657 |
+
inputs=[html_input, roles, game_type, perspective, theme],
|
| 2658 |
+
outputs=[
|
| 2659 |
+
output_code,
|
| 2660 |
+
status,
|
| 2661 |
+
gallery,
|
| 2662 |
+
prompt_preview,
|
| 2663 |
+
model_report,
|
| 2664 |
+
quality_report,
|
| 2665 |
+
preview,
|
| 2666 |
+
generation_state,
|
| 2667 |
+
selected_role,
|
| 2668 |
+
approved_roles,
|
| 2669 |
+
],
|
| 2670 |
+
)
|
| 2671 |
+
regenerate_btn.click(
|
| 2672 |
+
fn=regenerate_selected_asset,
|
| 2673 |
+
inputs=[generation_state, selected_role, approved_roles],
|
| 2674 |
+
outputs=[
|
| 2675 |
+
output_code,
|
| 2676 |
+
status,
|
| 2677 |
+
gallery,
|
| 2678 |
+
prompt_preview,
|
| 2679 |
+
model_report,
|
| 2680 |
+
quality_report,
|
| 2681 |
+
preview,
|
| 2682 |
+
generation_state,
|
| 2683 |
+
approved_roles,
|
| 2684 |
+
],
|
| 2685 |
+
)
|
| 2686 |
+
token_btn.click(fn=check_hf_token, inputs=None, outputs=token_status)
|
| 2687 |
|
| 2688 |
|
| 2689 |
if __name__ == "__main__":
|
| 2690 |
+
demo.launch(css=APP_CSS)
|