mod3sec2.11TEST / app.py
ZENLLC's picture
Create app.py
306086a verified
import os, re, io, zipfile, tempfile, time
import gradio as gr
from anthropic import Anthropic
# -------------------- CONFIG --------------------
MODEL_ID = "claude-sonnet-4-5-20250929"
SYSTEM_PROMPT = (
"You are an expert full-stack developer. When the user asks for an app or site, "
"return complete production-ready code artifacts in Markdown code fences labeled "
"```html```, ```css```, and ```js```. Always include index.html, optionally styles.css and app.js. "
"Generate beautiful, functional, responsive designs using pure HTML, CSS, and JS."
)
# -------------------- HELPERS --------------------
def parse_artifacts(text: str):
files = {}
blocks = re.findall(r"```(html|css|js)\s+([\s\S]*?)```", text, re.I)
for lang, code in blocks:
name = {"html": "index.html", "css": "styles.css", "js": "app.js"}[lang.lower()]
if name in files:
base, ext = name.split(".")
n = 2
while f"{base}{n}.{ext}" in files:
n += 1
name = f"{base}{n}.{ext}"
files[name] = code.strip()
if not files:
esc = gr.utils.escape_html(text)
files["index.html"] = f"<!doctype html><meta charset='utf-8'><title>Artifact</title><pre>{esc}</pre>"
if "index.html" not in files:
files["index.html"] = "<!doctype html><meta charset='utf-8'><title>Artifact</title><h1>Artifact</h1>"
return files
def render_srcdoc(files: dict):
html = files.get("index.html", "")
css = files.get("styles.css", "")
js = files.get("app.js", "")
# Inline CSS + JS for sandbox preview
if "</head>" in html:
html = html.replace("</head>", f"<style>\n{css}\n</style></head>")
else:
html = f"<!doctype html><head><meta charset='utf-8'><style>{css}</style></head>{html}"
if "</body>" in html:
html = html.replace("</body>", f"<script>\n{js}\n</script></body>")
else:
html = f"{html}<script>{js}</script>"
return html
def make_zip_path(files: dict):
tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".zip")
with zipfile.ZipFile(tmp, "w", zipfile.ZIP_DEFLATED) as z:
for name, code in files.items():
z.writestr(name, code)
tmp.flush()
return tmp.name
def call_claude(api_key: str, prompt: str):
client = Anthropic(api_key=api_key)
t0 = time.time()
resp = client.messages.create(
model=MODEL_ID,
max_tokens=4000,
temperature=0.45,
system=SYSTEM_PROMPT,
messages=[{"role": "user", "content": prompt}],
timeout=120,
)
latency = int((time.time() - t0) * 1000)
content = "".join(getattr(c, "text", "") for c in resp.content)
files = parse_artifacts(content)
return files, content, latency
# -------------------- UI --------------------
with gr.Blocks(fill_height=True, theme=gr.themes.Soft()) as demo:
gr.Markdown(
"# 🌐 ZEN Artifact Builder — Claude Sonnet 4.5\n"
"Describe any app or website and see it appear live below."
)
with gr.Row():
api_key = gr.Textbox(
label="ANTHROPIC_API_KEY", type="password", placeholder="sk-ant-…"
)
prompt = gr.Textbox(
label="Describe your app/site",
lines=6,
placeholder="Example: responsive dark-mode portfolio with glass panels and smooth animations.",
)
generate_btn = gr.Button("✨ Generate", variant="primary")
with gr.Row():
with gr.Tab("Live Preview"):
preview = gr.HTML(
elem_id="preview-pane",
value="<div style='display:flex;align-items:center;justify-content:center;height:100%;color:#aaa;'>Awaiting generation…</div>",
)
with gr.Tab("Artifacts"):
file_select = gr.Dropdown(label="Select file", choices=[], interactive=True)
editor = gr.Code(language="html", label="Code Editor", value="")
save_btn = gr.Button("💾 Save")
with gr.Tab("Raw Output & Export"):
raw_output = gr.Textbox(label="Model Output (raw)", lines=12)
latency_box = gr.Number(label="Latency (ms)")
zip_file = gr.File(label="Download ZIP", interactive=False)
files_state = gr.State({})
demo.css = """
#preview-pane {
height: 85vh !important;
min-height: 550px;
border: 1px solid #ccc;
border-radius: 10px;
overflow: auto;
background: #fff;
}
"""
# -------------------- FUNCTIONS --------------------
def generate(api_key, prompt):
if not api_key:
raise gr.Error("Please enter your Anthropic API key.")
files, raw, latency = call_claude(api_key, prompt)
srcdoc = render_srcdoc(files)
zip_path = make_zip_path(files)
names = list(files.keys())
first = names[0] if names else ""
return (
files,
gr.update(value=srcdoc),
gr.update(choices=names, value=first),
gr.update(value=files.get(first, "")),
raw,
latency,
zip_path,
)
generate_btn.click(
generate,
inputs=[api_key, prompt],
outputs=[files_state, preview, file_select, editor, raw_output, latency_box, zip_file],
)
def load_editor(files, name):
return files.get(name, "")
file_select.change(load_editor, inputs=[files_state, file_select], outputs=editor)
def save_file(files, name, code):
files = dict(files)
if name:
files[name] = code
return files, gr.update(value=render_srcdoc(files))
save_btn.click(save_file, inputs=[files_state, file_select, editor], outputs=[files_state, preview])
demo.launch()