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"Artifact
{esc}
" if "index.html" not in files: files["index.html"] = "Artifact

Artifact

" 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 "" in html: html = html.replace("", f"") else: html = f"{html}" if "" in html: html = html.replace("", f"") else: html = f"{html}" 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="
Awaiting generation…
", ) 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()